bug 2509: fix file permissions
[koha.git] / koha-tmpl / intranet-tmpl / prog / en / lib / greybox / GreyBox_v5_5 / greybox_source / base / AJS.js
1 /*
2 Last Modified: 29/01/07 14:16:09
3
4 AJS JavaScript library
5     A very small library with a lot of functionality
6 AUTHOR
7     4mir Salihefendic (http://amix.dk) - amix@amix.dk
8 LICENSE
9     Copyright (c) 2006 Amir Salihefendic. All rights reserved.
10     Copyright (c) 2005 Bob Ippolito. All rights reserved.
11     http://www.opensource.org/licenses/mit-license.php
12 VERSION
13     3.7
14 SITE
15     http://orangoo.com/AmiNation/AJS
16 **/
17 if(!AJS) {
18 var AJS = {
19     BASE_URL: "",
20
21     drag_obj: null,
22     drag_elm: null,
23     _drop_zones: [],
24     _drag_zones: [],
25     _cur_pos: null,
26
27     ajaxErrorHandler: null,
28
29 ////
30 // General accessor functions
31 ////
32     getQueryArgument: function(var_name) {
33         var query = window.location.search.substring(1);
34         var vars = query.split("&");
35         for (var i=0;i<vars.length;i++) {
36             var pair = vars[i].split("=");
37             if (pair[0] == var_name) {
38                 return pair[1];
39             }
40         }
41         return null;
42     },
43
44     isIe: function() {
45         return (navigator.userAgent.toLowerCase().indexOf("msie") != -1 && navigator.userAgent.toLowerCase().indexOf("opera") == -1);
46     },
47     isNetscape7: function() {
48         return (navigator.userAgent.toLowerCase().indexOf("netscape") != -1 && navigator.userAgent.toLowerCase().indexOf("7.") != -1);
49     },
50     isSafari: function() {
51         return (navigator.userAgent.toLowerCase().indexOf("khtml") != -1);
52     },
53     isOpera: function() {
54         return (navigator.userAgent.toLowerCase().indexOf("opera") != -1);
55     },
56     isMozilla: function() {
57         return (navigator.userAgent.toLowerCase().indexOf("gecko") != -1 && navigator.productSub >= 20030210);
58     },
59
60
61 ////
62 // Array functions
63 ////
64     //Shortcut: AJS.$A
65     createArray: function(v) {
66         if(AJS.isArray(v) && !AJS.isString(v))
67             return v;
68         else if(!v)
69             return [];
70         else
71             return [v];
72     },
73
74     forceArray: function(args) {
75         var r = [];
76         AJS.map(args, function(elm) {
77             r.push(elm);
78         });
79         return r;
80     },
81
82     join: function(delim, list) {
83         try {
84             return list.join(delim);
85         }
86         catch(e) {
87             var r = list[0] || '';
88             AJS.map(list, function(elm) {
89                 r += delim + elm;
90             }, 1);
91             return r + '';
92         }
93     },
94
95     isIn: function(elm, list) {
96         var i = AJS.getIndex(elm, list);
97         if(i != -1)
98             return true;
99         else
100             return false;
101     },
102
103     getIndex: function(elm, list/*optional*/, eval_fn) {
104         for(var i=0; i < list.length; i++)
105             if(eval_fn && eval_fn(list[i]) || elm == list[i])
106                 return i;
107         return -1;
108     },
109
110     getFirst: function(list) {
111         if(list.length > 0)
112             return list[0];
113         else
114             return null;
115     },
116
117     getLast: function(list) {
118         if(list.length > 0)
119             return list[list.length-1];
120         else
121             return null;
122     },
123
124     update: function(l1, l2) {
125         for(var i in l2)
126             l1[i] = l2[i];
127         return l1;
128     },
129
130     flattenList: function(list) {
131         var r = [];
132         var _flatten = function(r, l) {
133             AJS.map(l, function(o) {
134                 if(o == null) {}
135                 else if (AJS.isArray(o))
136                     _flatten(r, o);
137                 else
138                     r.push(o);
139             });
140         }
141         _flatten(r, list);
142         return r;
143     },
144
145
146 ////
147 // Functional programming
148 ////
149     map: function(list, fn,/*optional*/ start_index, end_index) {
150         var i = 0, l = list.length;
151         if(start_index)
152              i = start_index;
153         if(end_index)
154              l = end_index;
155         for(i; i < l; i++)
156             fn.apply(null, [list[i], i]);
157     },
158
159     rmap: function(list, fn) {
160         var i = list.length-1, l = 0;
161         for(i; i >= l; i--)
162             fn.apply(null, [list[i], i]);
163     },
164
165     filter: function(list, fn, /*optional*/ start_index, end_index) {
166         var r = [];
167         AJS.map(list, function(elm) {
168             if(fn(elm))
169                 r.push(elm);
170         }, start_index, end_index);
171         return r;
172     },
173
174     partial: function(fn) {
175         var args = AJS.forceArray(arguments);
176         return AJS.$b(fn, null, args.slice(1, args.length).reverse(), false, true);
177     },
178
179
180 ////
181 // DOM functions
182 ////
183     //Shortcut: AJS.$
184     getElement: function(id) {
185         if(AJS.isString(id) || AJS.isNumber(id))
186             return document.getElementById(id);
187         else
188             return id;
189     },
190
191     //Shortcut: AJS.$$
192     getElements: function(/*id1, id2, id3*/) {
193         var args = AJS.forceArray(arguments);
194         var elements = new Array();
195             for (var i = 0; i < args.length; i++) {
196                 var element = AJS.getElement(args[i]);
197                 elements.push(element);
198             }
199             return elements;
200     },
201
202     //Shortcut: AJS.$bytc
203     getElementsByTagAndClassName: function(tag_name, class_name, /*optional*/ parent) {
204         var class_elements = [];
205         if(!AJS.isDefined(parent))
206             parent = document;
207         if(!AJS.isDefined(tag_name))
208             tag_name = '*';
209
210         var els = parent.getElementsByTagName(tag_name);
211         var els_len = els.length;
212         var pattern = new RegExp("(^|\\s)" + class_name + "(\\s|$)");
213
214         for (i = 0, j = 0; i < els_len; i++) {
215             if ( pattern.test(els[i].className) || class_name == null ) {
216                 class_elements[j] = els[i];
217                 j++;
218             }
219         }
220         return class_elements;
221     },
222
223     _nodeWalk: function(elm, tag_name, class_name, fn_next_elm) {
224         var p = fn_next_elm(elm);
225
226         var checkFn;
227         if(tag_name && class_name) {
228             checkFn = function(p) {
229                 return AJS.nodeName(p) == tag_name && AJS.hasClass(p, class_name);
230             }
231         }
232         else if(tag_name) {
233             checkFn = function(p) { return AJS.nodeName(p) == tag_name; }
234         }
235         else {
236             checkFn = function(p) { return AJS.hasClass(p, class_name); }
237         }
238
239         while(p) {
240             if(checkFn(p))
241                 return p;
242             p = fn_next_elm(p);
243         }
244         return null;
245     },
246
247     getParentBytc: function(elm, tag_name, class_name) {
248         return AJS._nodeWalk(elm, tag_name, class_name, function(m) { return m.parentNode; });
249     },
250
251     getPreviousSiblingBytc: function(elm, tag_name, class_name) {
252         return AJS._nodeWalk(elm, tag_name, class_name, function(m) { return m.previousSibling; });
253     },
254
255     getNextSiblingBytc: function(elm, tag_name, class_name) {
256         return AJS._nodeWalk(elm, tag_name, class_name, function(m) { return m.nextSibling; });
257     },
258
259     //Shortcut: AJS.$f
260     getFormElement: function(form, name) {
261         form = AJS.$(form);
262         var r = null;
263         AJS.map(form.elements, function(elm) {
264             if(elm.name && elm.name == name)
265                 r = elm;
266         });
267         return r;
268     },
269
270     formContents: function(form) {
271         var form = AJS.$(form);
272         var r = {};
273         var fn = function(elms) {
274             AJS.map(elms, function(e) {
275                 if(e.name)
276                     r[e.name] = e.value || '';
277             });
278         }
279         fn(AJS.$bytc('input', null, form));
280         fn(AJS.$bytc('textarea', null, form));
281         return r;
282     },
283
284     getBody: function() {
285         return AJS.$bytc('body')[0]
286     },
287
288     nodeName: function(elm) {
289         return elm.nodeName.toLowerCase();
290     },
291
292     hasParent: function(elm, parent_to_consider, max_look_up) {
293         if(elm == parent_to_consider)
294             return true;
295         if(max_look_up == 0)
296             return false;
297         return AJS.hasParent(elm.parentNode, parent_to_consider, max_look_up-1);
298     },
299
300     isElementHidden: function(elm) {
301         return elm.style.visibility == "hidden";
302     },
303
304     //Shortcut: AJS.DI
305     documentInsert: function(elm) {
306         if(typeof(elm) == 'string')
307             elm = AJS.HTML2DOM(elm);
308         document.write('<span id="dummy_holder"></span>');
309         AJS.swapDOM(AJS.$('dummy_holder'), elm);
310     },
311
312     cloner: function(element) {
313         return function() {
314             return element.cloneNode(true);
315         }
316     },
317
318     appendToTop: function(elm/*, elms...*/) {
319         var args = AJS.forceArray(arguments).slice(1);
320         if(args.length >= 1) {
321             var first_child = elm.firstChild;
322             if(first_child) {
323                 while(true) {
324                     var t_elm = args.shift();
325                     if(t_elm)
326                         AJS.insertBefore(t_elm, first_child);
327                     else
328                         break;
329                 }
330             }
331             else {
332                 AJS.ACN.apply(null, arguments);
333             }
334         }
335         return elm;
336     },
337
338     //Shortcut: AJS.ACN
339     appendChildNodes: function(elm/*, elms...*/) {
340         if(arguments.length >= 2) {
341             AJS.map(arguments, function(n) {
342                 if(AJS.isString(n))
343                     n = AJS.TN(n);
344                 if(AJS.isDefined(n))
345                     elm.appendChild(n);
346             }, 1);
347         }
348         return elm;
349     },
350
351     //Shortcut: AJS.RCN
352     replaceChildNodes: function(elm/*, elms...*/) {
353         var child;
354         while ((child = elm.firstChild))
355             elm.removeChild(child);
356         if (arguments.length < 2)
357             return elm;
358         else
359             return AJS.appendChildNodes.apply(null, arguments);
360         return elm;
361     },
362
363     insertAfter: function(elm, reference_elm) {
364         reference_elm.parentNode.insertBefore(elm, reference_elm.nextSibling);
365         return elm;
366     },
367
368     insertBefore: function(elm, reference_elm) {
369         reference_elm.parentNode.insertBefore(elm, reference_elm);
370         return elm;
371     },
372
373     showElement: function(/*elms...*/) {
374         var args = AJS.forceArray(arguments);
375         AJS.map(args, function(elm) { elm.style.display = ''});
376     },
377
378     hideElement: function(elm) {
379         var args = AJS.forceArray(arguments);
380         AJS.map(args, function(elm) { elm.style.display = 'none'});
381     },
382
383     swapDOM: function(dest, src) {
384         dest = AJS.getElement(dest);
385         var parent = dest.parentNode;
386         if (src) {
387             src = AJS.getElement(src);
388             parent.replaceChild(src, dest);
389         } else {
390             parent.removeChild(dest);
391         }
392         return src;
393     },
394
395     removeElement: function(/*elm1, elm2...*/) {
396         var args = AJS.forceArray(arguments);
397         AJS.map(args, function(elm) { AJS.swapDOM(elm, null); });
398     },
399
400     createDOM: function(name, attrs) {
401         var i=0, attr;
402         elm = document.createElement(name);
403
404         if(AJS.isDict(attrs[i])) {
405             for(k in attrs[0]) {
406                 attr = attrs[0][k];
407                 if(k == "style")
408                     elm.style.cssText = attr;
409                 else if(k == "class" || k == 'className')
410                     elm.className = attr;
411                 else {
412                     elm.setAttribute(k, attr);
413                 }
414             }
415             i++;
416         }
417
418         if(attrs[0] == null)
419             i = 1;
420
421         AJS.map(attrs, function(n) {
422             if(n) {
423                 if(AJS.isString(n) || AJS.isNumber(n))
424                     n = AJS.TN(n);
425                 elm.appendChild(n);
426             }
427         }, i);
428         return elm;
429     },
430
431     _createDomShortcuts: function() {
432         var elms = [
433                 "ul", "li", "td", "tr", "th",
434                 "tbody", "table", "input", "span", "b",
435                 "a", "div", "img", "button", "h1",
436                 "h2", "h3", "br", "textarea", "form",
437                 "p", "select", "option", "iframe", "script",
438                 "center", "dl", "dt", "dd", "small",
439                 "pre"
440         ];
441         var extends_ajs = function(elm) {
442             var c_dom = "return AJS.createDOM.apply(null, ['" + elm + "', arguments]);";
443             var c_fun_dom = 'function() { ' + c_dom + '    }';
444             eval("AJS." + elm.toUpperCase() + "=" + c_fun_dom);
445         }
446         AJS.map(elms, extends_ajs);
447         AJS.TN = function(text) { return document.createTextNode(text) };
448     },
449
450     getCssDim: function(dim) {
451         if(AJS.isString(dim))
452             return dim;
453         else
454             return dim + "px";
455     },
456     getCssProperty: function(elm, prop) {
457         elm = AJS.$(elm);
458         var y;
459         if(elm.currentStyle)
460             y = elm.currentStyle[prop];
461         else if (window.getComputedStyle)
462             y = document.defaultView.getComputedStyle(elm,null).getPropertyValue(prop);
463         return y;
464     },
465
466     setStyle: function(/*elm1, elm2..., property, new_value*/) {
467         var args = AJS.forceArray(arguments);
468         var new_val = args.pop();
469         var property = args.pop();
470         AJS.map(args, function(elm) { 
471             elm.style[property] = AJS.getCssDim(new_val);
472         });
473     },
474
475     setWidth: function(/*elm1, elm2..., width*/) {
476         var args = AJS.forceArray(arguments);
477         args.splice(args.length-1, 0, 'width');
478         AJS.setStyle.apply(null, args);
479     },
480     setHeight: function(/*elm1, elm2..., height*/) {
481         var args = AJS.forceArray(arguments);
482         args.splice(args.length-1, 0, 'height');
483         AJS.setStyle.apply(null, args);
484     },
485     setLeft: function(/*elm1, elm2..., left*/) {
486         var args = AJS.forceArray(arguments);
487         args.splice(args.length-1, 0, 'left');
488         AJS.setStyle.apply(null, args);
489     },
490     setTop: function(/*elm1, elm2..., top*/) {
491         var args = AJS.forceArray(arguments);
492         args.splice(args.length-1, 0, 'top');
493         AJS.setStyle.apply(null, args);
494     },
495     setClass: function(/*elm1, elm2..., className*/) {
496         var args = AJS.forceArray(arguments);
497         var c = args.pop();
498         AJS.map(args, function(elm) { elm.className = c});
499     },
500     addClass: function(/*elm1, elm2..., className*/) {
501         var args = AJS.forceArray(arguments);
502         var cls = args.pop();
503         var add_class = function(o) {
504             if(!new RegExp("(^|\\s)" + cls + "(\\s|$)").test(o.className))
505                 o.className += (o.className ? " " : "") + cls;
506         };
507         AJS.map(args, function(elm) { add_class(elm); });
508     },
509     hasClass: function(elm, cls) {
510         if(!elm.className)
511             return false;
512         return elm.className == cls || 
513                elm.className.search(new RegExp(" " + cls)) != -1
514     },
515     removeClass: function(/*elm1, elm2..., className*/) {
516         var args = AJS.forceArray(arguments);
517         var cls = args.pop();
518         var rm_class = function(o) {
519             o.className = o.className.replace(new RegExp("\\s?" + cls, 'g'), "");
520         };
521         AJS.map(args, function(elm) { rm_class(elm); });
522     },
523
524     setHTML: function(elm, html) {
525         elm.innerHTML = html;
526         return elm;
527     },
528
529     RND: function(tmpl, ns, scope) {
530         scope = scope || window;
531         var fn = function(w, g) {
532             g = g.split("|");
533             var cnt = ns[g[0]];
534             for(var i=1; i < g.length; i++)
535                 cnt = scope[g[i]](cnt);
536             if(cnt == '')
537                 return '';
538             if(cnt == 0 || cnt == -1)
539                 cnt += '';
540             return cnt || w;
541         };
542         return tmpl.replace(/%\(([A-Za-z0-9_|.]*)\)/g, fn);
543     },
544
545     HTML2DOM: function(html,/*optional*/ first_child) {
546         var d = AJS.DIV();
547         d.innerHTML = html;
548         if(first_child)
549             return d.childNodes[0];
550         else
551             return d;
552     },
553
554     preloadImages: function(/*img_src1, ..., img_srcN*/) {
555         AJS.AEV(window, 'load', AJS.$p(function(args) {
556             AJS.map(args, function(src) {
557                 var pic = new Image();
558                 pic.src = src;
559             });
560         }, arguments));
561     },
562
563
564 ////
565 // Effects
566 ////
567     setOpacity: function(elm, p) {
568         elm.style.opacity = p;
569         elm.style.filter = "alpha(opacity="+ p*100 +")";
570     },
571
572
573 ////
574 // Ajax functions
575 ////
576     getXMLHttpRequest: function() {
577         var try_these = [
578             function () { return new XMLHttpRequest(); },
579             function () { return new ActiveXObject('Msxml2.XMLHTTP'); },
580             function () { return new ActiveXObject('Microsoft.XMLHTTP'); },
581             function () { return new ActiveXObject('Msxml2.XMLHTTP.4.0'); },
582             function () { throw "Browser does not support XMLHttpRequest"; }
583         ];
584         for (var i = 0; i < try_these.length; i++) {
585             var func = try_these[i];
586             try {
587                 return func();
588             } catch (e) {
589             }
590         }
591     },
592
593     getRequest: function(url, data, type) {
594         if(!type)
595             type = "POST";
596         var req = AJS.getXMLHttpRequest();
597
598         if(url.indexOf("http://") == -1) {
599             if(AJS.BASE_URL != '') {
600                 if(AJS.BASE_URL.lastIndexOf('/') != AJS.BASE_URL.length-1)
601                     AJS.BASE_URL += '/';
602                 url = AJS.BASE_URL + url;
603             }
604             else
605                 url = window.location + url;
606         }
607
608         req.open(type, url, true);
609         if(type == "POST")
610             req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
611         return AJS._sendXMLHttpRequest(req);
612     },
613
614     _sendXMLHttpRequest: function(req, data) {
615         var d = new AJSDeferred(req);
616
617         var onreadystatechange = function () {
618             if (req.readyState == 4) {
619                 var status = '';
620                 try {
621                     status = req.status;
622                 }
623                 catch(e) {};
624                 if(status == 200 || status == 304 || req.responseText == null) {
625                     d.callback();
626                 }
627                 else {
628                     if(AJS.ajaxErrorHandler)
629                         AJS.ajaxErrorHandler(req.responseText, req);
630                     else 
631                         d.errback();
632                 }
633             }
634         }
635         req.onreadystatechange = onreadystatechange;
636         return d;
637     },
638
639     _reprString: function(o) {
640         return ('"' + o.replace(/(["\\])/g, '\\$1') + '"'
641         ).replace(/[\f]/g, "\\f"
642         ).replace(/[\b]/g, "\\b"
643         ).replace(/[\n]/g, "\\n"
644         ).replace(/[\t]/g, "\\t"
645         ).replace(/[\r]/g, "\\r");
646     },
647
648     serializeJSON: function(o) {
649         var objtype = typeof(o);
650         if (objtype == "undefined") {
651             return "undefined";
652         } else if (objtype == "number" || objtype == "boolean") {
653             return o + "";
654         } else if (o === null) {
655             return "null";
656         }
657         if (objtype == "string") {
658             return AJS._reprString(o);
659         }
660         var me = arguments.callee;
661         if (objtype != "function" && typeof(o.length) == "number") {
662             var res = [];
663             for (var i = 0; i < o.length; i++) {
664                 var val = me(o[i]);
665                 if (typeof(val) != "string") {
666                     val = "undefined";
667                 }
668                 res.push(val);
669             }
670             return "[" + res.join(",") + "]";
671         }
672         // it's a function with no adapter, bad
673         if (objtype == "function")
674             return null;
675         res = [];
676         for (var k in o) {
677             var useKey;
678             if (typeof(k) == "number") {
679                 useKey = '"' + k + '"';
680             } else if (typeof(k) == "string") {
681                 useKey = AJS._reprString(k);
682             } else {
683                 // skip non-string or number keys
684                 continue;
685             }
686             val = me(o[k]);
687             if (typeof(val) != "string") {
688                 // skip non-serializable values
689                 continue;
690             }
691             res.push(useKey + ":" + val);
692         }
693         return "{" + res.join(",") + "}";
694     },
695
696     loadJSONDoc: function(url) {
697         var d = AJS.getRequest(url);
698         var eval_req = function(data, req) {
699             var text = req.responseText;
700             if(text == "Error")
701                 d.errback(req);
702             else
703                 return AJS.evalTxt(text);
704         };
705         d.addCallback(eval_req);
706         return d;
707     },
708
709     evalTxt: function(txt) {
710         try {
711             return eval('('+ txt + ')');
712         }
713         catch(e) {
714             return eval(txt);
715         }
716     },
717
718     evalScriptTags: function(html) {
719         var script_data = html.match(/<script.*?>((\n|\r|.)*?)<\/script>/g);
720         if(script_data != null) {
721             for(var i=0; i < script_data.length; i++) {
722                 var script_only = script_data[i].replace(/<script.*?>/g, "");
723                 script_only = script_only.replace(/<\/script>/g, "");
724                 eval(script_only);
725             }
726         }
727     },
728
729     queryArguments: function(data) {
730         var post_data = [];
731         for(k in data)
732             post_data.push(k + "=" + AJS.urlencode(data[k]));
733         return post_data.join("&");
734     },
735
736
737 ////
738 // Position and size
739 ////
740     getMousePos: function(e) {
741         var posx = 0;
742         var posy = 0;
743         if (!e) var e = window.event;
744         if (e.pageX || e.pageY)
745         {
746             posx = e.pageX;
747             posy = e.pageY;
748         }
749         else if (e.clientX || e.clientY)
750         {
751             posx = e.clientX + document.body.scrollLeft;
752             posy = e.clientY + document.body.scrollTop;
753         }
754         return {x: posx, y: posy};
755     },
756
757     getScrollTop: function() {
758         //From: http://www.quirksmode.org/js/doctypes.html
759         var t;
760         if (document.documentElement && document.documentElement.scrollTop)
761                 t = document.documentElement.scrollTop;
762         else if (document.body)
763                 t = document.body.scrollTop;
764         return t;
765     },
766
767     absolutePosition: function(elm) {
768         var posObj = {'x': elm.offsetLeft, 'y': elm.offsetTop};
769         if(elm.offsetParent) {
770             var temp_pos = AJS.absolutePosition(elm.offsetParent);
771             posObj.x += temp_pos.x;
772             posObj.y += temp_pos.y;
773         }
774         // safari bug
775         if (AJS.isSafari() && elm.style.position == 'absolute' ) {
776             posObj.x -= document.body.offsetLeft;
777             posObj.y -= document.body.offsetTop;
778         }
779         return posObj;
780     },
781
782     getWindowSize: function(doc) {
783         doc = doc || document;
784         var win_w, win_h;
785         if (self.innerHeight) {
786             win_w = self.innerWidth;
787             win_h = self.innerHeight;
788         } else if (doc.documentElement && doc.documentElement.clientHeight) {
789             win_w = doc.documentElement.clientWidth;
790             win_h = doc.documentElement.clientHeight;
791         } else if (doc.body) {
792             win_w = doc.body.clientWidth;
793             win_h = doc.body.clientHeight;
794         }
795         return {'w': win_w, 'h': win_h};
796     },
797
798     isOverlapping: function(elm1, elm2) {
799         var pos_elm1 = AJS.absolutePosition(elm1);
800         var pos_elm2 = AJS.absolutePosition(elm2);
801
802         var top1 = pos_elm1.y;
803         var left1 = pos_elm1.x;
804         var right1 = left1 + elm1.offsetWidth;
805         var bottom1 = top1 + elm1.offsetHeight;
806         var top2 = pos_elm2.y;
807         var left2 = pos_elm2.x;
808         var right2 = left2 + elm2.offsetWidth;
809         var bottom2 = top2 + elm2.offsetHeight;
810         var getSign = function(v) {
811             if(v > 0) return "+";
812             else if(v < 0) return "-";
813             else return 0;
814         }
815
816         if ((getSign(top1 - bottom2) != getSign(bottom1 - top2)) &&
817                 (getSign(left1 - right2) != getSign(right1 - left2)))
818             return true;
819         return false;
820     },
821
822
823 ////
824 // Events
825 ////
826     getEventElm: function(e) {
827         if(e && !e.type && !e.keyCode)
828             return e
829         var targ;
830         if (!e) var e = window.event;
831         if (e.target) targ = e.target;
832         else if (e.srcElement) targ = e.srcElement;
833         if (targ.nodeType == 3) // defeat Safari bug
834             targ = targ.parentNode;
835         return targ;
836     },
837
838     _getRealScope: function(fn, /*optional*/ extra_args, dont_send_event, rev_extra_args) {
839         var scope = window;
840         extra_args = AJS.$A(extra_args);
841         if(fn._cscope)
842             scope = fn._cscope;
843
844         return function() {
845             //Append all the orginal arguments + extra_args
846             var args = [];
847             var i = 0;
848             if(dont_send_event)
849                 i = 1;
850
851             AJS.map(arguments, function(arg) { args.push(arg) }, i);
852             args = args.concat(extra_args);
853             if(rev_extra_args)
854                 args = args.reverse();
855             return fn.apply(scope, args);
856         };
857     },
858
859     _unloadListeners: function() {
860         if(AJS.listeners)
861             AJS.map(AJS.listeners, function(elm, type, fn) { AJS.REV(elm, type, fn) });
862         AJS.listeners = [];
863     },
864
865     setEventKey: function(e) {
866         e.key = e.keyCode ? e.keyCode : e.charCode;
867
868         if(window.event) {
869             e.ctrl = window.event.ctrlKey;
870             e.shift = window.event.shiftKey;
871         }
872         else {
873             e.ctrl = e.ctrlKey;
874             e.shift = e.shiftKey;
875         }
876         switch(e.key) {
877             case 63232:
878                 e.key = 38;
879                 break;
880             case 63233:
881                 e.key = 40;
882                 break;
883             case 63235:
884                 e.key = 39;
885                 break;
886             case 63234:
887                 e.key = 37;
888                 break;
889         }
890     },
891
892     //Shortcut: AJS.AEV
893     addEventListener: function(elm, type, fn, /*optional*/listen_once, cancle_bubble) {
894         if(!cancle_bubble)
895             cancle_bubble = false;
896
897         var elms = AJS.$A(elm);
898         AJS.map(elms, function(elmz) {
899             if(listen_once)
900                 fn = AJS._listenOnce(elmz, type, fn);
901             
902             //Hack since it does not work in all browsers
903             if(AJS.isIn(type, ['submit', 'load', 'scroll', 'resize'])) {
904                 var old = elm['on' + type];
905                 elm['on' + type] = function() {
906                     if(old) {
907                         fn(arguments);
908                         return old(arguments);
909                     }
910                     else
911                         return fn(arguments);
912                 };
913                 return;
914             }
915
916             //Fix keyCode
917             if(AJS.isIn(type, ['keypress', 'keydown', 'keyup', 'click'])) {
918                 var old_fn = fn;
919                 fn = function(e) {
920                     AJS.setEventKey(e);
921                     return old_fn.apply(null, arguments);
922                 }
923             }
924
925             if (elmz.attachEvent) {
926                 //FIXME: We ignore cancle_bubble for IE... could be a problem?
927                 elmz.attachEvent("on" + type, fn);
928             }
929             else if(elmz.addEventListener)
930                 elmz.addEventListener(type, fn, cancle_bubble);
931
932             AJS.listeners = AJS.$A(AJS.listeners);
933             AJS.listeners.push([elmz, type, fn]);
934         });
935     },
936
937     //Shortcut: AJS.REV
938     removeEventListener: function(elm, type, fn, /*optional*/cancle_bubble) {
939         if(!cancle_bubble)
940             cancle_bubble = false;
941         if(elm.removeEventListener) {
942             elm.removeEventListener(type, fn, cancle_bubble);
943             if(AJS.isOpera())
944                 elm.removeEventListener(type, fn, !cancle_bubble);
945         }
946         else if(elm.detachEvent)
947             elm.detachEvent("on" + type, fn);
948     },
949
950     //Shortcut: AJS.$b
951     bind: function(fn, scope, /*optional*/ extra_args, dont_send_event, rev_extra_args) {
952         fn._cscope = scope;
953         return AJS._getRealScope(fn, extra_args, dont_send_event, rev_extra_args);
954     },
955
956     bindMethods: function(self) {
957         for (var k in self) {
958             var func = self[k];
959             if (typeof(func) == 'function') {
960                 self[k] = AJS.$b(func, self);
961             }
962         }
963     },
964
965     _listenOnce: function(elm, type, fn) {
966         var r_fn = function() {
967             AJS.removeEventListener(elm, type, r_fn);
968             fn(arguments);
969         }
970         return r_fn;
971     },
972
973     callLater: function(fn, interval) {
974         var fn_no_send = function() {
975             fn();
976         };
977         window.setTimeout(fn_no_send, interval);
978     },
979
980     preventDefault: function(e) {
981         if(AJS.isIe()) 
982             window.event.returnValue = false;
983         else 
984             e.preventDefault();
985     },
986
987
988 ////
989 // Drag and drop
990 ////
991     dragAble: function(elm, /*optional*/ handler, args) {
992         if(!args)
993             args = {};
994         if(!AJS.isDefined(args['move_x']))
995             args['move_x'] = true;
996         if(!AJS.isDefined(args['move_y']))
997             args['move_y'] = true;
998         if(!AJS.isDefined(args['moveable']))
999             args['moveable'] = false;
1000         if(!AJS.isDefined(args['hide_on_move']))
1001             args['hide_on_move'] = true;
1002         if(!AJS.isDefined(args['on_mouse_up']))
1003             args['on_mouse_up'] = null;
1004         if(!AJS.isDefined(args['cursor']))
1005             args['cursor'] = 'move';
1006         if(!AJS.isDefined(args['max_move']))
1007             args['max_move'] = {'top': null, 'left': null};
1008
1009         elm = AJS.$(elm);
1010
1011         if(!handler)
1012             handler = elm;
1013
1014         handler = AJS.$(handler);
1015         var old_cursor = handler.style.cursor;
1016         handler.style.cursor = args['cursor'];
1017         elm.style.position = 'relative';
1018
1019         AJS.addClass(handler, '_ajs_handler');
1020         handler._args = args;
1021         handler._elm = elm;
1022         AJS.AEV(handler, 'mousedown', AJS._dragStart);
1023     },
1024
1025     _dragStart: function(e) {
1026         var handler = AJS.getEventElm(e);
1027         if(!AJS.hasClass(handler, '_ajs_handler')) {
1028             handler = AJS.getParentBytc(handler, null, '_ajs_handler');
1029         }
1030         if(handler)
1031             AJS._dragInit(e, handler._elm, handler._args);
1032     },
1033
1034     dropZone: function(elm, args) {
1035         elm = AJS.$(elm);
1036         var item = {elm: elm};
1037         AJS.update(item, args);
1038         AJS._drop_zones.push(item);
1039     },
1040
1041     removeDragAble: function(elm) {
1042         AJS.REV(elm, 'mousedown', AJS._dragStart);
1043         elm.style.cursor = '';
1044     },
1045
1046     removeDropZone: function(elm) {
1047         var i = AJS.getIndex(elm, AJS._drop_zones, function(item) {
1048             if(item.elm == elm) return true;
1049         });
1050         if(i != -1) {
1051             AJS._drop_zones.splice(i, 1);
1052         }
1053     },
1054
1055     _dragInit: function(e, click_elm, args) {
1056         AJS.drag_obj = new Object();
1057         AJS.drag_obj.args = args;
1058
1059         AJS.drag_obj.click_elm = click_elm;
1060         AJS.drag_obj.mouse_pos = AJS.getMousePos(e);
1061         AJS.drag_obj.click_elm_pos = AJS.absolutePosition(click_elm);
1062
1063         AJS.AEV(document, 'mousemove', AJS._dragMove, false, true);
1064         AJS.AEV(document, 'mouseup', AJS._dragStop, false, true);
1065
1066         if (AJS.isIe())
1067             window.event.cancelBubble = true;
1068         AJS.preventDefault(e);
1069     },
1070
1071     _initDragElm: function(elm) {
1072         if(AJS.drag_elm && AJS.drag_elm.style.display == 'none')
1073             AJS.removeElement(AJS.drag_elm);
1074
1075         if(!AJS.drag_elm) {
1076             AJS.drag_elm = AJS.DIV();
1077             var d = AJS.drag_elm;
1078             AJS.insertBefore(d, AJS.getBody().firstChild);
1079             AJS.setHTML(d, elm.innerHTML);
1080
1081             d.className = elm.className;
1082             d.style.cssText = elm.style.cssText;
1083
1084             d.style.position = 'absolute';
1085             d.style.zIndex = 10000;
1086
1087             var t = AJS.absolutePosition(elm);
1088             AJS.setTop(d, t.y);
1089             AJS.setLeft(d, t.x);
1090
1091             if(AJS.drag_obj.args.on_init) {
1092                 AJS.drag_obj.args.on_init(elm);
1093             }
1094         }
1095     },
1096
1097     _dragMove: function(e) {
1098         var drag_obj = AJS.drag_obj;
1099         var click_elm = drag_obj.click_elm;
1100
1101         AJS._initDragElm(click_elm);
1102         var drag_elm = AJS.drag_elm;
1103
1104         if(drag_obj.args['hide_on_move'])
1105             click_elm.style.visibility = 'hidden';
1106
1107         var cur_pos = AJS.getMousePos(e);
1108
1109         var mouse_pos = drag_obj.mouse_pos;
1110
1111         var click_elm_pos = drag_obj.click_elm_pos;
1112
1113         AJS.map(AJS._drop_zones, function(d_z) {
1114             if(AJS.isOverlapping(d_z['elm'], drag_elm)) {
1115                 if(d_z['elm'] != drag_elm) {
1116                     var on_hover = d_z['on_hover'];
1117                     if(on_hover)
1118                         on_hover(d_z['elm'], click_elm, drag_elm);
1119                 }
1120             }
1121         });
1122
1123         if(drag_obj.args['on_drag'])
1124             drag_obj.args['on_drag'](click_elm, e);
1125
1126         var max_move_top = drag_obj.args['max_move']['top'];
1127         var max_move_left = drag_obj.args['max_move']['left'];
1128         var p;
1129         if(drag_obj.args['move_x']) {
1130             p = cur_pos.x - (mouse_pos.x - click_elm_pos.x);
1131             if(max_move_left == null || max_move_left <= p)
1132                 AJS.setLeft(elm, p);
1133         }
1134
1135         if(drag_obj.args['move_y']) {
1136             p = cur_pos.y - (mouse_pos.y - click_elm_pos.y);
1137             if(max_move_top == null || max_move_top <= p)
1138                 AJS.setTop(elm, p);
1139         }
1140         if(AJS.isIe()) {
1141             window.event.cancelBubble = true;
1142             window.event.returnValue = false;
1143         }
1144         else
1145             e.preventDefault();
1146     },
1147
1148     _dragStop: function(e) {
1149         var drag_obj = AJS.drag_obj;
1150         var drag_elm = AJS.drag_elm;
1151         var click_elm = drag_obj.click_elm;
1152
1153         AJS.REV(document, "mousemove", AJS._dragMove, true);
1154         AJS.REV(document, "mouseup", AJS._dragStop, true);
1155
1156         var dropped = false;
1157         AJS.map(AJS._drop_zones, function(d_z) {
1158             if(AJS.isOverlapping(d_z['elm'], click_elm)) {
1159                 if(d_z['elm'] != click_elm) {
1160                     var on_drop = d_z['on_drop'];
1161                     if(on_drop) {
1162                         dropped = true;
1163                         on_drop(d_z['elm'], click_elm);
1164                     }
1165                 }
1166             }
1167         });
1168
1169         if(drag_obj.args['moveable']) {
1170             var t = parseInt(click_elm.style.top) || 0;
1171             var l = parseInt(click_elm.style.left) || 0;
1172             var drag_elm_xy = AJS.absolutePosition(drag_elm);
1173             var click_elm_xy = AJS.absolutePosition(click_elm);
1174             AJS.setTop(click_elm, t + drag_elm_xy.y - click_elm_xy.y);
1175             AJS.setLeft(click_elm, l + drag_elm_xy.x - click_elm_xy.x);
1176         }
1177
1178         if(!dropped && drag_obj.args['on_mouse_up'])
1179             drag_obj.args['on_mouse_up'](click_elm, e);
1180
1181         if(drag_obj.args['hide_on_move'])
1182             drag_obj.click_elm.style.visibility = 'visible';
1183
1184         if(drag_obj.args.on_end) {
1185             drag_obj.args.on_end(click_elm);
1186         }
1187
1188         AJS._dragObj = null;
1189         if(drag_elm)
1190             AJS.hideElement(drag_elm);
1191         AJS.drag_elm = null;
1192     },
1193
1194
1195 ////
1196 // Misc.
1197 ////
1198     keys: function(obj) {
1199         var rval = [];
1200         for (var prop in obj) {
1201             rval.push(prop);
1202         }
1203         return rval;
1204     },
1205
1206     values: function(obj) {
1207         var rval = [];
1208         for (var prop in obj) {
1209             rval.push(obj[prop]);
1210         }
1211         return rval;
1212     },
1213
1214     urlencode: function(str) {
1215         return encodeURIComponent(str.toString());
1216     },
1217
1218     isDefined: function(o) {
1219         return (o != "undefined" && o != null)
1220     },
1221
1222     isArray: function(obj) {
1223         return obj instanceof Array;
1224     },
1225
1226     isString: function(obj) {
1227         return (typeof obj == 'string');
1228     },
1229
1230     isNumber: function(obj) {
1231         return (typeof obj == 'number');
1232     },
1233
1234     isObject: function(obj) {
1235         return (typeof obj == 'object');
1236     },
1237
1238     isFunction: function(obj) {
1239         return (typeof obj == 'function');
1240     },
1241
1242     isDict: function(o) {
1243         var str_repr = String(o);
1244         return str_repr.indexOf(" Object") != -1;
1245     },
1246
1247     exportToGlobalScope: function() {
1248         for(e in AJS)
1249             eval(e + " = AJS." + e);
1250     },
1251
1252     log: function(o) {
1253         if(AJS.isMozilla())
1254             console.log(o);
1255         else {
1256             var div = AJS.DIV({'style': 'color: green'});
1257             AJS.ACN(AJS.getBody(), AJS.setHTML(div, ''+o));
1258         }
1259     }
1260
1261 }
1262
1263 AJS.Class = function(members) {
1264     var fn = function() {
1265         if(arguments[0] != 'no_init') {
1266             return this.init.apply(this, arguments);
1267         }
1268     }
1269     fn.prototype = members;
1270     AJS.update(fn, AJS.Class.prototype);
1271     return fn;
1272 }
1273 AJS.Class.prototype = {
1274     extend: function(members) {
1275         var parent = new this('no_init');
1276         for(k in members) {
1277             var prev = parent[k];
1278             var cur = members[k];
1279             if (prev && prev != cur && typeof cur == 'function') {
1280                 cur = this._parentize(cur, prev);
1281             }
1282             parent[k] = cur;
1283         }
1284         return new AJS.Class(parent);
1285     },
1286
1287     implement: function(members) {
1288         AJS.update(this.prototype, members);
1289     },
1290
1291     _parentize: function(cur, prev) {
1292         return function(){
1293             this.parent = prev;
1294             return cur.apply(this, arguments);
1295         }
1296     }
1297 };
1298
1299 //Shortcuts
1300 AJS.$ = AJS.getElement;
1301 AJS.$$ = AJS.getElements;
1302 AJS.$f = AJS.getFormElement;
1303 AJS.$b = AJS.bind;
1304 AJS.$p = AJS.partial;
1305 AJS.$FA = AJS.forceArray;
1306 AJS.$A = AJS.createArray;
1307 AJS.DI = AJS.documentInsert;
1308 AJS.ACN = AJS.appendChildNodes;
1309 AJS.RCN = AJS.replaceChildNodes;
1310 AJS.AEV = AJS.addEventListener;
1311 AJS.REV = AJS.removeEventListener;
1312 AJS.$bytc = AJS.getElementsByTagAndClassName;
1313
1314 AJSDeferred = function(req) {
1315     this.callbacks = [];
1316     this.errbacks = [];
1317     this.req = req;
1318 }
1319 AJSDeferred.prototype = {
1320     excCallbackSeq: function(req, list) {
1321         var data = req.responseText;
1322         while (list.length > 0) {
1323             var fn = list.pop();
1324             var new_data = fn(data, req);
1325             if(new_data)
1326                 data = new_data;
1327         }
1328     },
1329
1330     callback: function () {
1331         this.excCallbackSeq(this.req, this.callbacks);
1332     },
1333
1334     errback: function() {
1335         if(this.errbacks.length == 0)
1336             alert("Error encountered:\n" + this.req.responseText);
1337
1338         this.excCallbackSeq(this.req, this.errbacks);
1339     },
1340
1341     addErrback: function(fn) {
1342         this.errbacks.unshift(fn);
1343     },
1344
1345     addCallback: function(fn) {
1346         this.callbacks.unshift(fn);
1347     },
1348
1349     addCallbacks: function(fn1, fn2) {
1350         this.addCallback(fn1);
1351         this.addErrback(fn2);
1352     },
1353
1354     sendReq: function(data) {
1355         if(AJS.isObject(data)) {
1356             this.req.send(AJS.queryArguments(data));
1357         }
1358         else if(AJS.isDefined(data))
1359             this.req.send(data);
1360         else {
1361             this.req.send("");
1362         }
1363     }
1364 };
1365
1366 //Prevent memory-leaks
1367 AJS.addEventListener(window, 'unload', AJS._unloadListeners);
1368 AJS._createDomShortcuts()
1369 }
1370
1371 script_loaded = true;