source: Dev/branches/rest-dojo-ui/client/dojox/jq.js @ 274

Last change on this file since 274 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

File size: 50.7 KB
Line 
1//For jQuery 1.3.2
2
3dojo.provide("dojox.jq");
4dojo.require("dojo.NodeList-traverse");
5dojo.require("dojo.NodeList-manipulate");
6dojo.require("dojo.io.script");
7
8/*
9To get jquery tests to pass:
10- add spaces between span>form selectors, other ones like one,two
11- .last() instead of :last
12- $("<div>").find("#foo") does not work unless the div is attached to the body.
13
14- trigger .test not work
15- No jquery.Event thing.
16
17- jQuery.ajax() modifies incoming arguments?
18- test framework not good for our io methods, async, poll.
19- Dojo ajax is async: we fire ajaxStop more than jquery.
20
21- jquery makes assumptions of a value to set for an element
22by inserting an element and checking display. Does not seem to
23account for nested styles, only captures generic tag name style off
24of body. Why can't we just set display to empty?
25
26
27
28
29
30OK for failures:
31- test("jQuery.ajax - beforeSend, cancel request (#2688)"
32  We cancel the deferred which triggers error and complete callbacks.
33
34
35Looked at jquery code for:
36- how it handled text(): did not use textContent/innerText, but use recursive look over childNodes and nodeValue,
37so it may have impact on how <br> is serialized, but it has uniform behavior across browsers.
38- Looked at trigger: how it triggered actions on dom nodes. This seemed unsafe.
39*/
40
41/*
42dojo.query differences that cause some tests to fail:
43- does not support XML queries
44- $("#sap>form") does not find a match but $("#sap > form") does. Similar issue with comma instead of > (see is() tests)
45- "$("form:last") should return the last form object, not if that particular form is that last related
46  to its siblings? Same issue with :first?
47- $("p").filter("#ap, #sndp"): filter does not work.
48- dojo.NodeList uses d.NodeList a few places in the code. Would be nice to use a ctor that can be configured.
49  That would make the filter function operate better.
50- filterQueryResult, cannot handle queries like "p, div"? See andSelf test with parents().
51- adjacent "p + p" not supported?
52= a:only-child not supported?
53- nth(1)
54- even/odd
55- eq/gt/lt
56- #form :radio:checked does not run the first :radio psuedo selector? Seems to be a general issue where only the last pseud
57  is run. For example, "#form :checked:radio" does only the radio pseudo.
58*/
59
60(function(){
61        //Enable io topic publishing
62        dojo.config.ioPublish = true;
63
64        //Support stuff for toDom
65        var selfClosedTags = "|img|meta|hr|br|input|";
66
67        function toDom(/*String*/html, /*Document?*/doc){
68                //summary converts HTML string into DOM nodes.
69                //Make sure html is a string.
70                html += "";
71
72                //Convert <tag/> into <tag></tag>
73                html = html.replace(/<\s*(\w+)([^\/\>]*)\/\s*>/g, function(tag, name, contents){
74                        if(selfClosedTags.indexOf("|" + name + "|") == -1){
75                                return "<" + name + contents + "></" + name + ">";
76                        }else{
77                                return tag;
78                        }
79                });
80
81                return dojo._toDom(html, doc);
82        }
83
84        function cssNameToJs(name){
85                var index = name.indexOf("-");
86                if(index != -1){
87                        //Strip off beginning dash
88                        if(index == 0){
89                                name = name.substring(1);
90                        }
91                        name = name.replace(/-(\w)/g, function(match, match1){
92                                return match1.toUpperCase();
93                        });
94                }
95                return name;
96        }
97
98        var _old$ = dojo.global.$;
99        var _oldJQuery = dojo.global.jQuery;
100
101        var $ = dojo.global.$ = dojo.global.jQuery = function(){
102                var arg = arguments[0];
103                if(!arg){
104                        return $._wrap([], null, $);
105                }else if(dojo.isString(arg)){
106                        if(arg.charAt(0) == "<"){
107                                //String of html that needs nodes created.
108                                arg = toDom(arg);
109                                //If a DocumentFragment, convert to use its children
110                                //since we want to treat all top level nodes as elements
111                                //in the NodeList array.
112                                if(arg.nodeType == 11){
113                                        arg = arg.childNodes;
114                                }else{
115                                        return $._wrap([arg], null, $);
116                                }
117                                //Use end case of nodelist to finish work.
118                        }else{
119                                //Normal dojo.query selector.
120                                //Switch out query's NodeList constructor to be our specialized
121                                //NodeList constructor.
122                                var listCtor = dojo._NodeListCtor;
123                                dojo._NodeListCtor = $;
124
125                                //If the second arg is one of our fake NodeLists then
126                                //use the first parent for the call.
127                                var arg2 = arguments[1];
128                                if(arg2 && arg2._is$){
129                                        arg2 = arg2[0];
130                                }else if(dojo.isString(arg2)){
131                                        arg2 = dojo.query(arg2)[0];
132                                }
133
134                                var nl = dojo.query.call(this, arg, arg2);
135                                dojo._NodeListCtor = listCtor;
136                                return nl;
137                        }
138                }else if(dojo.isFunction(arg)){
139                        //The addOnLoad case
140                        $.ready(arg);
141                        return $;
142                }else if(arg == document || arg == window){
143                        //If the arg is the document or window,
144                        //then just use it directly.
145                        return $._wrap([arg], null, $);
146                }else if(dojo.isArray(arg)){
147                        //Input is a plain array.
148                        //Filter out duplicates.
149                        var ary = [];
150                        for(var i = 0; i < arg.length; i++){
151                                if(dojo.indexOf(ary, arg[i]) == -1){
152                                        ary.push(arg[i]);
153                                }
154                        }
155                        return $._wrap(arg, null, $);
156                }else if("nodeType" in arg){
157                        //A DOM Node
158                        return $._wrap([arg], null, $);
159                }
160
161                //A native NodeList that does not conform to dojo.isArray().
162                //Convert it to a workable array and create new NodeList.
163                return $._wrap(dojo._toArray(arg), null, $);
164
165        };
166
167        //Set up plugin extension point.
168        var nlProto = dojo.NodeList.prototype;
169
170        //Need a delegate, because at least one method conflicts with jquery
171        //API: attr(name) in jQuery only returns a single, first value, where
172        //dojo.attr will return an array.
173        var f = $.fn = $.prototype = dojo.delegate(nlProto);
174
175        //_wrap is required for proper use in dojo.query, but the _adaptAs* methods
176        //do not have to placed on $ -- they can be used directly off dojo.NodeList.
177        $._wrap = dojo.NodeList._wrap;
178
179        //Add in some pseudos selectors
180        var headerRegExp = /^H\d/i;
181        var pseudos = dojo.query.pseudos;
182        dojo.mixin(pseudos, {
183                has: function(name, condition){
184                        return function(elem){
185                                return $(condition, elem).length;
186                        }
187                },
188                visible: function(name, condition){
189                        return function(elem){
190                                return dojo.style(elem, "visible") != "hidden" && dojo.style(elem, "display") != "none";
191                        }
192                },
193                hidden: function(name, condition){
194                        return function(elem){
195                                return elem.type == "hidden" || dojo.style(elem, "visible") == "hidden" || dojo.style(elem, "display") == "none";
196                        }
197                },
198                selected: function(name, condition){
199                        return function(elem){
200                                return elem.selected;
201                        }
202                },
203                checked: function(name, condition){
204                        return function(elem){
205                                return elem.nodeName.toUpperCase() == "INPUT" && elem.checked;
206                        }
207                },
208                disabled: function(name, condition){
209                        return function(elem){
210                                return elem.getAttribute("disabled");
211                        }
212                },
213                enabled: function(name, condition){
214                        return function(elem){
215                                return !elem.getAttribute("disabled");
216                        }
217                },
218                input: function(name, condition){
219                        return function(elem){
220                                var n = elem.nodeName.toUpperCase();
221                                return n == "INPUT" || n == "SELECT" || n == "TEXTAREA" || n == "BUTTON";
222                        }
223                },
224                button: function(name, condition){
225                        return function(elem){
226                                return (elem.nodeName.toUpperCase() == "INPUT" && elem.type == "button") || elem.nodeName.toUpperCase() == "BUTTON";
227                        }
228                },
229                header: function(name, condition){
230                        return function(elem){
231                                return elem.nodeName.match(headerRegExp);
232                        }
233                }
234                //TODO: implement :animated
235        });
236
237
238        //Add the input type selectors to pseudos
239        var inputPseudos = {};
240        dojo.forEach([
241                "text", "password", "radio", "checkbox", "submit", "image", "reset", "file"
242        ], function(type) {
243                inputPseudos[type] = function(name, condition){
244                        return function(elem){
245                                return elem.nodeName.toUpperCase() == "INPUT" && elem.type == type;
246                        }
247                };
248        });
249        dojo.mixin(pseudos, inputPseudos);
250
251        //Set up browser sniff.
252        $.browser = {
253                mozilla: dojo.isMoz,
254                msie: dojo.isIE,
255                opera: dojo.isOpera,
256                safari: dojo.isSafari
257        };
258        $.browser.version = dojo.isIE || dojo.isMoz || dojo.isOpera || dojo.isSafari || dojo.isWebKit;
259       
260        //Map back into dojo
261        //Hmm maybe this is not so good. Dojo
262        //modules may still be holding on to old
263        //dojo (example: the d._NodeListCtor in query.js)
264        //dojo = dojo.mixin($, dojo);
265
266        // Add $.ready
267        $.ready = $.fn.ready = function(callback){
268                dojo.addOnLoad(dojo.hitch(null, callback, $));
269                return this;
270        }
271
272        //START jquery Core API methods
273        //http://docs.jquery.com/Core
274        f._is$ = true;
275        f.size = function(){return this.length; };
276
277        $.prop = function(node, propCheck){
278                //TODO: not sure about this one, could not find the docs?
279                if(dojo.isFunction(propCheck)){
280                        return propCheck.call(node);
281                }else{
282                        return propCheck;
283                }
284        }
285
286        $.className = {
287                add: dojo.addClass,
288                remove: dojo.removeClass,
289                has: dojo.hasClass
290        };
291
292        $.makeArray = function(thing){
293                if(typeof thing == "undefined"){
294                        return [];
295                }else if(thing.length && !dojo.isString(thing) && !("location" in thing)){
296                        //Location check was for excluding window objects that have a length.
297                        return dojo._toArray(thing);
298                }else{
299                        return [thing];
300                }
301        }
302       
303        $.merge = function(ary1, ary2){
304                //Alters first array by adding in the element.
305                var args = [ary1.length, 0];
306                args = args.concat(ary2);
307                ary1.splice.apply(ary1, args);
308                return ary1;
309        }
310
311        $.each = function(/*Array||ArrayLike*/list, /*Function*/cb){
312                //each differs from dojo.NodeList.forEach in that
313                //"this" is the current cycled node. Breaking
314                //the loop is also possible. Also, index is first arg
315                //to the callback.
316                if(dojo.isArrayLike(list)){
317                        for(var i = 0; i < list.length; i++){
318                                if(cb.call(list[i], i, list[i]) === false){
319                                        break;
320                                }
321                        }
322                }else if(dojo.isObject(list)){
323                        for(var param in list){
324                                if(cb.call(list[param], param, list[param]) === false){
325                                        break;
326                                }
327                        }
328                }
329                return this;
330        };
331        f.each = function(/*Function*/cb){
332                return $.each.call(this, this, cb);
333        };
334        //f.length already implemented by NodeList
335        f.eq = function(){
336                //Direct copy of dojo.NodeList.at, but want
337                //to use our NodeList class.
338                var nl = $();
339                dojo.forEach(arguments, function(i) { if(this[i]) { nl.push(this[i]); } }, this);
340                return nl; // dojo.NodeList
341        };
342        f.get = function(/*Number*/index){
343                if(index || index == 0){
344                        return this[index];
345                }
346                return this;
347        };
348        f.index = function(arg){
349                //Hmm, allows passing in a $ nodelist. Apparently in that
350                //case take the first item in that array and match
351                if(arg._is$){
352                        arg = arg[0];
353                }
354                return this.indexOf(arg);
355        }
356
357        //.data implementation
358        var dataStore = [];
359        var dataId = 0;
360        var dataAttr = dojo._scopeName + "DataId";
361       
362        var getDataId = function(node){
363                var id = node.getAttribute(dataAttr);
364                if(!id){
365                        id = dataId++;
366                        node.setAttribute(dataAttr, id);
367                }
368        }
369       
370        var getData = function(node){
371                var data = {};
372                if(node.nodeType == 1){
373                        var id = getDataId(node);
374                        data = dataStore[id];
375                        if(!data){
376                                data = dataStore[id] = {};
377                        }
378                }
379                return data;
380        }
381
382        $.data = function(/*DOMNode*/node, /*String*/name, /*String*/value){
383                var result = null;
384                if(name == "events"){
385                        //Special case "events", since jquery tests seem to use it to
386                        //get the event handler storage for jquery. So for jquery apps
387                        //"events" is probably a reserved word anyway.
388                        result = listeners[node.getAttribute(eventAttr)];
389                        var isEmpty = true;
390                        if(result){
391                                for(var param in result){
392                                        isEmpty = false;
393                                        break;
394                                }
395                        }
396                        return isEmpty ? null : result;
397                }
398
399                var data = getData(node);
400                if(typeof value != "undefined"){
401                        data[name] = value;
402                }else{
403                        result = data[name];
404                }
405                return value ? this : result;
406        }
407
408        $.removeData = function(/*DOMNode*/node, /*String*/name){
409                var data = getData(node);
410                delete data[name];
411                if(node.nodeType == 1){
412                        var isEmpty = true;
413                        for(var param in data){
414                                isEmpty = false;
415                                break;
416                        }
417                        if(isEmpty){
418                                node.removeAttribute(dataAttr);
419                        }
420                }
421                return this;
422        }
423
424        f.data = function(/*String*/name, /*String*/value){
425                var result = null;
426                this.forEach(function(node){
427                        result = $.data(node, name, value);
428                });
429
430                return value ? this : result;
431        }
432
433        f.removeData = function(/*String*/name){
434                this.forEach(function(node){
435                        $.removeData(node, name);
436                });
437                return this;
438        }
439       
440        function jqMix(obj, props){
441                //summary: an attempt at a mixin that follows
442                //jquery's .extend rules. Seems odd. Not sure how
443                //to resolve this with dojo.mixin and what the use
444                //cases are for the jquery version.
445                //Copying some code from dojo._mixin.
446                if(obj == props){
447                        return obj;
448                }
449                var tobj = {};
450                for(var x in props){
451                        // the "tobj" condition avoid copying properties in "props"
452                        // inherited from Object.prototype.  For example, if obj has a custom
453                        // toString() method, don't overwrite it with the toString() method
454                        // that props inherited from Object.prototype
455                        if((tobj[x] === undefined || tobj[x] != props[x]) && props[x] !== undefined && obj != props[x]){
456                                if(dojo.isObject(obj[x]) && dojo.isObject(props[x])){
457                                        if(dojo.isArray(props[x])){
458                                                obj[x] = props[x];
459                                        }else{
460                                                obj[x] = jqMix(obj[x], props[x]);
461                                        }
462                                }else{
463                                        obj[x] = props[x];
464                                }
465                        }
466                }
467                // IE doesn't recognize custom toStrings in for..in
468                if(dojo.isIE && props){
469                        var p = props.toString;
470                        if(typeof p == "function" && p != obj.toString && p != tobj.toString &&
471                                p != "\nfunction toString() {\n    [native code]\n}\n"){
472                                        obj.toString = props.toString;
473                        }
474                }
475                return obj; // Object
476        }
477
478        f.extend = function(){
479                var args = [this];
480                args = args.concat(arguments);
481                return $.extend.apply($, args);
482        }
483
484        $.extend = function(){
485                //Could have multiple args to mix in. Similar to dojo.mixin,
486                //but has some different rules, and the mixins all get applied
487                //to the first arg.
488                var args = arguments, finalObj;
489                for(var i = 0; i < args.length; i++){
490                        var obj = args[i];
491                        if(obj && dojo.isObject(obj)){
492                                if(!finalObj){
493                                        finalObj = obj;
494                                }else{
495                                        jqMix(finalObj, obj);
496                                }
497                        }
498                }
499                return finalObj;
500        }
501
502        $.noConflict = function(/*Boolean*/extreme){
503                var me = $;
504                dojo.global.$ = _old$;
505                if(extreme){
506                        dojo.global.jQuery = _oldJQuery;
507                }
508                return me;
509        }
510        //END jquery Core API methods
511       
512        //START jquery Attribute API methods
513        //http://docs.jquery.com/Attributes
514        f.attr = function(name, value){
515                //The isObject tests below are to weed out where something
516                //like a form node has an input called "action" but we really
517                //want to get the attribute "action". But in general, favor
518                //a property value over a DOM attribute value.
519                if(arguments.length == 1 && dojo.isString(arguments[0])){
520                        //The get case, return first match.
521                        var first = this[0];
522                       
523                        //Weed out empty nodes
524                        if(!first){
525                                return null;
526                        }
527
528                        var arg = arguments[0];
529                        //favor properties over attributes.
530                        var attr = dojo.attr(first, arg);
531                        var prop = first[arg];
532                        if((arg in first) && !dojo.isObject(prop) && name != "href"){
533                                return prop;
534                        }else{
535                                return attr || prop;
536                        }
537                }else if(dojo.isObject(name)){
538                        //A setter, using an object.
539                        for(var param in name){
540                                this.attr(param, name[param]);
541                        }
542                        return this;
543                }else{
544                        //The setter case. Figure out if value is a function.
545                        var isFunc = dojo.isFunction(value);
546                        this.forEach(function(node, index){
547                                var prop = node[name];
548                                if((name in node) && !dojo.isObject(prop) && name != "href"){
549                                        node[name] = (isFunc ? value.call(node, index) : value);
550                                }else if(node.nodeType == 1){
551                                        dojo.attr(node, name, (isFunc ? value.call(node, index) : value));
552                                }
553                        });
554                        return this;
555                }
556        }
557
558        f.removeAttr = function(name){
559                this.forEach(function(node, index){
560                        var prop = node[name];
561                        if((name in node) && !dojo.isObject(prop) && name != "href"){
562                                delete node[name];
563                        }else if(node.nodeType == 1){
564                                if(name == "class"){
565                                        //TODO: push this fix into dojo.removeAttr
566                                        node.removeAttribute(name);
567                                }else{
568                                        dojo.removeAttr(node, name);
569                                }
570                        }
571                });
572                return this;
573        }
574
575        //addClass, removeClass exist in dojo.NodeList. toggleClass in jQuery case
576        //just means add/remove the classname if it missing/exists. So need custom override.
577        f.toggleClass = function(/*String*/name, /*Expression?*/condition){
578                var hasCondition = arguments.length > 1;
579                this.forEach(function(node){
580                        dojo.toggleClass(node, name,  hasCondition ? condition : !dojo.hasClass(node, name));
581                });
582                return this;
583        }
584
585        //Action depends on arguments: if an array of functions do one thing,
586        //If no args, do a display toggle,
587        //If an expression, something that evaluates to true or false,
588        //then toggle display accordingly.
589        //If first arg is a String/Number, then do animation. Second arg
590        //is an optional callback.
591        f.toggle = function(){
592                //If more than two args and we have a function as first arg, then
593                //probably the onclick toggle variant: takes variable args that are
594                //functions and cycles through them on click actions.
595                var args = arguments;
596                if(arguments.length > 1 && dojo.isFunction(arguments[0])){
597                        var index = 0;
598                        var func = function(){
599                                var result = args[index].apply(this, arguments);
600                                index += 1;
601                                if(index > args.length - 1){
602                                        index = 0;
603                                }
604                        };
605                        return this.bind("click", func);
606                }else{
607                        //The display/hide/show case.
608                        var condition = arguments.length == 1 ? arguments[0] : undefined;
609                        this.forEach(function(node){
610                                var result = typeof condition == "undefined" ? dojo.style(node, "display") == "none" : condition;
611                                var action = (result ? "show" : "hide");
612                                var nl = $(node);
613                                nl[action].apply(nl, args);
614                        });
615                        return this;
616                }
617        }
618
619        //hasClass just returns true if any of the nodes has the class.
620        f.hasClass = function(/*String*/name){
621                return this.some(function(node){
622                        return dojo.hasClass(node, name);
623                });
624        }
625
626        //use the html method from dojo.NodeList-manipulate.
627        f.html = f.innerHTML;
628
629        //END jquery Attribute API methods
630
631       
632        //START jquery Traversing API methods
633        //http://docs.jquery.com/Traversing
634        dojo.forEach(["filter", "slice"], function(item){
635                f[item] = function(){
636                        //Convert the "this" value for functions passed in:
637                        var nl;
638                        if(dojo.isFunction(arguments[0])){
639                                var origFunc = arguments[0];
640                                arguments[0] = function(item, index){
641                                        return origFunc.call(item, item, index);
642                                }
643                        }
644                       
645                        if(item == "filter" && dojo.isString(arguments[0])){
646                                var nl = this._filterQueryResult(this, arguments[0]);
647                        }else{
648                                var oldCtor = dojo._NodeListCtor;
649                                dojo._NodeListCtor = f;
650                                //Need to wrap in a $() call since internally some
651                                //dojo.NodeList functions reference dojo.NodeList directly.
652                                //Need to get a configurable constructor for dojo.NodeList.
653                                nl = $(nlProto[item].apply(this, arguments));
654                                dojo._NodeListCtor = oldCtor;
655                        }
656
657                        return nl._stash(this);
658                }
659        });
660
661        f.map = function(/*Function*/callback){
662                //Hmm, this is not like array map/dojo.map where you get one item back for
663                //each input.
664                return this._buildArrayFromCallback(callback);
665        }
666        $.map = function(/*Array*/ary, /*Function*/callback){
667                //Hmm, this is not like array map/dojo.map where you get one item back for
668                //each input.
669                return f._buildArrayFromCallback.call(ary, callback);
670        }
671
672        $.inArray = function(value, /*Array*/ary){
673                return dojo.indexOf(ary, value);
674        }
675
676        f.is = function(query){
677                return (query ? !!this.filter(query).length : false);
678        }
679
680        //TODO: probably a better way to do this.
681        f.not = function(){
682                var notList = $.apply($, arguments);
683                //TODO: another place where if dojo.NodeList can configure a constructor,
684                //then we could avoid the $() wrapper below.
685                var nl = $(nlProto.filter.call(this, function(node){
686                        return notList.indexOf(node) == -1;
687                }));
688                return nl._stash(this);
689        }
690
691        f.add = function(){
692                return this.concat.apply(this, arguments);
693        }
694
695        function iframeDoc(/*DOMNode*/iframeNode){
696                //summary: Returns the document object associated with the iframe DOM Node argument.
697                //Taken from dojo.io.iframe.doc(). Needed for contents() function below.
698                var doc = iframeNode.contentDocument || // W3
699                        (
700                                (
701                                        (iframeNode.name) && (iframeNode.document) &&
702                                        (document.getElementsByTagName("iframe")[iframeNode.name].contentWindow) &&
703                                        (document.getElementsByTagName("iframe")[iframeNode.name].contentWindow.document)
704                                )
705                        ) ||  // IE
706                        (
707                                (iframeNode.name)&&(document.frames[iframeNode.name])&&
708                                (document.frames[iframeNode.name].document)
709                        ) || null;
710                return doc;
711        }
712
713        f.contents = function(){
714                var ary = [];
715                this.forEach(function(node){
716                        if(node.nodeName.toUpperCase() == "IFRAME"){
717                                var doc = iframeDoc(node);
718                                if(doc){
719                                        ary.push(doc);
720                                }
721                        }else{
722                                //TODO: code similar to children() function. Refactor?
723                                var children = node.childNodes;
724                                //Using for loop for better speed.
725                                for(var i = 0; i < children.length; i++){
726                                        ary.push(children[i]);
727                                }
728                        }
729                });
730                return this._wrap(ary)._stash(this);
731        }
732
733        f.find = function(/*String*/query){
734                var ary = [];
735                this.forEach(function(node){
736                        if(node.nodeType == 1){
737                                ary = ary.concat(dojo._toArray($(query, node)));
738                        }
739                });
740                return this._getUniqueAsNodeList(ary)._stash(this);
741        }
742
743        f.andSelf = function(){
744                return this.add(this._parent);
745        }
746
747        //END jquery Traversing API methods
748
749        //START jquery Manipulation API methods
750        //http://docs.jquery.com/Manipulation
751
752        f.remove = function(/*String?*/query){
753                //Override NodeList-manipulate's remove so we can remove data.
754                var nl = (query ? this._filterQueryResult(this, query) : this);
755               
756                //Remove data
757                nl.removeData();
758               
759                //Remove event listeners.
760                //TODO! do this, once event stuff is built out.
761               
762                //Remove the items from the DOM, but keep them in this
763                //node list.
764                nl.forEach(function(node){
765                        node.parentNode.removeChild(node);
766                });
767               
768                return this;
769        }
770
771        //START jquery CSS API methods
772        //http://docs.jquery.com/CSS
773        $.css = function(/*DOMNode*/node, /*String|Object*/name, /*String|Number?*/value){
774                name = cssNameToJs(name);
775               
776                //Hmm, dojo.style does an arguments. length check.
777                var result = (value ? dojo.style(node, name, value) : dojo.style(node, name));
778                return result;
779        }
780
781        f.css = function(/*String|Object*/name, /*String|Number?*/value){
782                if(dojo.isString(name)){
783                        //Convert name to JS name if needed.
784                        name = cssNameToJs(name);
785                        if(arguments.length == 2){
786                                //set the value. Cannot directly delegate to
787                                //this.style, since non-element nodes may be in the mix?
788                                //this.contents() in particular will return some funky stuff.
789                               
790                                //Need to be sure to add "px" if appropriate.
791                                if(!dojo.isString(value) && name != "zIndex"){
792                                        value = value + "px";
793                                }
794
795                                this.forEach(function(node){
796                                        if(node.nodeType == 1){
797                                                dojo.style(node, name, value);
798                                        }
799                                });
800                                return this;
801                        }else{
802                                //return the value
803                                value = dojo.style(this[0], name);
804                                //Need to be sure to add "px" if appropriate.
805                                if(!dojo.isString(value) && name != "zIndex"){
806                                        value = value + "px";
807                                }
808                                return value;
809                        }
810                }else{
811                        for(var param in name){
812                                this.css(param, name[param]);
813                        }
814                        return this;
815                }
816        }
817       
818        function doBox(/*NodeList*/nl, /*String*/boxType, /*String*/prop, /*String||Number*/value){;
819                if(value){
820                        //Set height for all elements.
821                        var mod = {};
822                        mod[prop] = value;
823                        nl.forEach(function(node){
824                                dojo[boxType](node, mod);
825                        });
826                        return nl;
827                }else{
828                        //Just get first node's height.
829                        //Hmm. width is negative when element is display none in FF3?
830                        return Math.abs(Math.round(dojo[boxType](nl[0])[prop]));
831                }
832        }
833
834        f.height = function(value){
835                return doBox(this, "contentBox", "h", value);
836        }
837
838        f.width = function(value){
839                return doBox(this, "contentBox", "w", value);
840        }
841
842        function getDimensions(/*DOMNode*/node, /*String*/type, /*Boolean*/usePadding, /*Boolean*/useBorder, /*Boolean*/useMargin){
843                //summary: sums up the different parts of the width/height based on arguments.
844                //If hidden, temporarily show it, do measurements then close.
845                var rehide = false;
846                if((rehide = node.style.display == "none")){
847                        node.style.display = "block";
848                }
849
850                var cs = dojo.getComputedStyle(node);
851                var content = Math.abs(Math.round(dojo._getContentBox(node, cs)[type]));
852                var pad = usePadding ? Math.abs(Math.round(dojo._getPadExtents(node, cs)[type])) : 0;
853                var border = useBorder ? Math.abs(Math.round(dojo._getBorderExtents(node, cs)[type])) : 0;
854                var margin = useMargin ? Math.abs(Math.round(dojo._getMarginExtents(node, cs)[type])) : 0;
855               
856                if(rehide){
857                        node.style.display = "none";
858                }
859
860                return pad + content + border + margin;
861        }
862
863        f.innerHeight = function(){
864                return getDimensions(this[0], "h", true);
865        }
866
867        f.innerWidth = function(){
868                return getDimensions(this[0], "w", true);
869        }
870
871        f.outerHeight = function(useMargin){
872                return getDimensions(this[0], "h", true, true, useMargin);
873        }
874
875        f.outerWidth = function(useMargin){
876                return getDimensions(this[0], "w", true, true, useMargin);
877        }
878
879        //END jquery CSS API methods
880
881
882        //START jquery Events API methods
883        //http://docs.jquery.com/Events
884       
885        //ready() already defined above.
886
887        //Event plumbing.
888        var listeners = [];
889        var listenId = 1;
890        var eventAttr = dojo._scopeName + "eventid";
891        var currentEvtData;
892
893        function getNonNamespacedName(/*String*/evtName){
894                //summary: gets name of the event before the first ".".
895                //The $$ stuff is special ids used to create unique names
896                //for bound functions that did not have a unique namespace name.
897                evtName = evtName.split("$$")[0];
898                var dotIndex = evtName.indexOf(".");
899                if(dotIndex != -1){
900                        evtName = evtName.substring(0, dotIndex);
901                }
902                return evtName;
903        }
904
905        function domConnect(/*DOMNode*/node, /*String*/evtName){
906                //summary: handles creating the connection with a real DOM event.
907                //This work should only be done one time per evName type.
908                //If the event if an ajax event, use dojo.subscribe instead.
909                if(evtName.indexOf("ajax") == 0){
910                        return dojo.subscribe(topics[evtName], function(dfd, res){
911                                var fakeEvt = new $.Event(evtName);
912                                if("ajaxComplete|ajaxSend|ajaxSuccess".indexOf(evtName) != -1){
913                                        triggerHandlers(node, [fakeEvt, dfd.ioArgs.xhr, dfd.ioArgs.args]);
914                                }else if(evtName == "ajaxError"){
915                                        triggerHandlers(node, [fakeEvt, dfd.ioArgs.xhr, dfd.ioArgs.args, res]);
916                                }else{
917                                        //ajaxStart|ajaxStop
918                                        triggerHandlers(node, [fakeEvt]);
919                                }
920                        });
921                }else{
922                        return dojo.connect(node, "on" + evtName, function(e){
923                                triggerHandlers(node, arguments);
924                        }); //Object
925                }
926        }
927
928        //Event object for compatibility for some tests.
929        $.Event = function(/*String*/type){
930                //Allow for calling function without "new"
931                if(this == $){
932                        return new $.Event(type);
933                }
934                if(typeof type == "string"){
935                        this.type = type.replace(/!/, "");
936                }else{
937                        dojo.mixin(this, type);
938                }
939                this.timeStamp = (new Date()).getTime();
940                this._isFake = true;
941                this._isStrict = (this.type.indexOf("!") != -1);
942               
943        }
944       
945        var ep = $.Event.prototype = {
946                preventDefault: function(){
947                        this.isDefaultPrevented = this._true;
948                },
949                stopPropagation: function(){
950                        this.isPropagationStopped = this._true;
951                },
952                stopImmediatePropagation: function(){
953                        this.isPropagationStopped = this._true;
954                        this.isImmediatePropagationStopped = this._true;
955                },
956                _true: function(){ return true; },
957                _false: function(){ return false; }
958        }
959        dojo.mixin(ep, {
960                isPropagationStopped: ep._false,
961                isImmediatePropagationStopped: ep._false,
962                isDefaultPrevented: ep._false
963        });
964
965        function makeTriggerData(data, type){
966                //summary: makes sure that the data array is copied
967                //and has an event as the first arg. If this function generates
968                //a fake event (known by the data[0]._isFake property being true)
969                //then the data[0].target needs to be set by the consumer of this function.
970               
971                data = data || [];
972                data = [].concat(data);
973
974                //If first data item is not an event, make one up.
975                //Need to set up target: prop in the consumers of this
976                //function.
977                var evt = data[0];
978                if(!evt || !evt.preventDefault){
979                        evt = type && type.preventDefault ? type : new $.Event(type);
980                        data.unshift(evt);
981                }
982                return data;
983        }
984       
985        var triggerHandlersCalled = false;
986
987        function triggerHandlers(/*DOMNode*/node, /*Array*/data, /*Function?*/extraFunc){
988                //summary: handles the actual callbacks to the handlers.
989               
990                //Indicate triggerHandlers was called.
991                triggerHandlersCalled = true;
992               
993                //Uses currentEvtData if this is a simulated event.
994                data = data || currentEvtData;
995                extraFunc = extraFunc;
996
997                //Normalize on a real element if dealing with a document.
998                if(node.nodeType == 9){
999                        node = node.documentElement;
1000                }
1001
1002                var nodeId = node.getAttribute(eventAttr);
1003                if(!nodeId){
1004                        return;
1005                }
1006
1007                var evt = data[0];
1008                var evtFullName = evt.type;
1009                var evtName = getNonNamespacedName(evtFullName);
1010
1011                var cbs = listeners[nodeId][evtName];
1012
1013                var result;
1014                //Apply the extra function. What is that about? Not mentioned in the
1015                //public APIs?
1016                if(extraFunc){
1017                        result = extraFunc.apply(node, data);
1018                }
1019
1020                if (result !== false){
1021                        for(var param in cbs){
1022                                if(param != "_connectId" && (!evt._isStrict && (param.indexOf(evtFullName) == 0) || (evt._isStrict && param == evtFullName))){
1023                                        //Store the callback ID in case unbind is called with this event
1024                                        //so we can only unbind that one callback.
1025                                        evt[dojo._scopeName + "callbackId"] = param;
1026
1027                                        var cb = cbs[param];
1028                                        if(typeof cb.data != "undefined"){
1029                                                evt.data = cb.data;
1030                                        }else{
1031                                                evt.data = null;
1032                                        }
1033       
1034                                        //Do the actual callback.
1035                                        if ((result = cb.fn.apply(evt.target, data)) === false && !evt._isFake){
1036                                                dojo.stopEvent(evt);
1037                                        }
1038                                        evt.result = result;
1039                                }
1040                        }
1041                }
1042
1043                return result;
1044        }
1045
1046        f.triggerHandler = function(/*String*/type, /*Array?*/data, /*Function?*/extraFunc){
1047                //Only triggers handlers on the first node. Huh.
1048                var node = this[0];
1049                if(node && node.nodeType != 3 && node.nodeType != 8){
1050                        data = makeTriggerData(data, type);
1051                        return triggerHandlers(node, data, extraFunc);
1052                }else{
1053                        return undefined;
1054                }
1055        }
1056
1057        f.trigger = function(/*String*/type, /*Array?*/data, /*Function?*/extraFunc){
1058                //Copy data since we may need to modify by adding a
1059                data = makeTriggerData(data, type);
1060                var evt = data[0];
1061                var type = getNonNamespacedName(evt.type);
1062               
1063                //Store the current event data in case handlers need
1064                //to reference it because of a simulated event.
1065                currentEvtData = data;
1066                currentExtraFunc = extraFunc;
1067
1068                var result = null;
1069                var needTarget = !evt.target;
1070                this.forEach(function(node){
1071                        //Only handle non text/comment nodes.
1072                        if(node.nodeType != 3 && node.nodeType != 8){
1073
1074                                //Normalize on a real element if dealing with a document.
1075                                if(node.nodeType == 9){
1076                                        node = node.documentElement;
1077                                }
1078
1079                                //Set the node target appropriately for fake events.
1080                                if(evt._isFake){
1081                                        evt.currentTarget = node;
1082                                        if(needTarget){
1083                                                evt.target = node;
1084                                        }
1085                                }
1086
1087                                //Bizarre extra function thing. Not really demonstrated in public
1088                                //API docs.
1089                                if(extraFunc){
1090                                        var funcData = data.slice(1);
1091                                        result = extraFunc.apply(node, (result = null ? funcData : funcData.concat(result)));
1092                                }
1093
1094                                if(result !== false){
1095                                        //Trigger DOM event. onclick is handled differently than
1096                                        //others.
1097                                        /*
1098                                        if(type == 'click' && node.onclick && node.nodeName.toUpperCase() == "A"){
1099                                                result = node.onclick.apply(node, data);
1100                                        }
1101                                        */
1102                                       
1103                                        //Set the "global" flag that indicates if triggerHandlers was called.
1104                                        //If the direct node.event/onevent does not trigger the handlers, do so
1105                                        //manually at the end.
1106                                        triggerHandlersCalled = false;
1107                                       
1108                                        //Trigger functions registered directly on the DOM node.
1109                                        if(node[type]){
1110                                                try{
1111                                                        result = node[type]();
1112                                                }catch(e){
1113                                                        //Apparently IE throws on some hidden elements. Just eat it.
1114                                                }
1115                                        }else if(node["on" + type]){
1116                                                try{
1117                                                        result = node["on" + type]();
1118                                                }catch(e){
1119                                                        //Apparently IE throws on some hidden elements. Just eat it.
1120                                                }
1121                                        }
1122                                       
1123                                        if(!triggerHandlersCalled){
1124                                                //Finally triggerHandlers directly if the above code did not trigger it yet.
1125                                                result = triggerHandlers(node, data);
1126                                        }
1127
1128                                        //Bubble the event up.
1129                                        //TODO: optimize this path so we don't have to do forEach and NodeList work.
1130                                        var parentNode = node.parentNode;
1131                                        if(result !== false && !evt.isImmediatePropagationStopped() && !evt.isPropagationStopped() && parentNode && parentNode.nodeType == 1){
1132                                                $(parentNode).trigger(type, data, extraFunc);
1133                                        }
1134                                }
1135                        }
1136                });
1137
1138                //Clear current event data.
1139                currentEvtData = null;
1140                currentExtraFunc = null;
1141
1142                return this;
1143        }
1144
1145        var bindIdCounter = 0;
1146
1147        f.bind = function(/*String*/type, /*Array||Function?*/data, /*Function*/fn){
1148                //Type can be space separated values.
1149                type = type.split(" ");
1150               
1151                //May not have data argument.
1152                if(!fn){
1153                        fn = data;
1154                        data = null;
1155                }
1156
1157                this.forEach(function(node){
1158                        //Only handle non text/comment nodes.
1159                        if(node.nodeType != 3 && node.nodeType != 8){
1160                       
1161                                //If document, bind to documentElement
1162                                if(node.nodeType == 9){
1163                                        node = node.documentElement;
1164                                }
1165
1166                                //If no nodeId, then create one and attach it to the DOM node.
1167                                var nodeId = node.getAttribute(eventAttr);
1168                                if(!nodeId){
1169                                        nodeId = listenId++;
1170                                        node.setAttribute(eventAttr, nodeId);
1171                                        listeners[nodeId] = {};
1172                                }
1173       
1174                                //Process each event type.
1175                                for(var i = 0; i < type.length; i++){
1176                                        //Get event name, if have a dot on it, it is namespaced,
1177                                        //be sure to get the core event name.
1178                                        var evtFullName = type[i];
1179                                        var evtName = getNonNamespacedName(evtFullName);
1180                                        if(evtName == evtFullName){
1181                                                //Generate a unique ID for this function binding
1182                                                evtFullName = evtName + "$$" + (bindIdCounter++);
1183                                        }
1184       
1185                                        //Get the event listeners for the event name, the complete name.
1186                                        var lls = listeners[nodeId];
1187                                        if(!lls[evtName]){
1188                                                lls[evtName] = {
1189                                                        _connectId: domConnect(node, evtName)
1190                                                };
1191                                        }
1192       
1193                                        //Add the callback to the list of listeners.
1194                                        lls[evtName][evtFullName] = {
1195                                                fn: fn,
1196                                                data: data
1197                                        };
1198                                }
1199                        }
1200                });
1201               
1202                return this;
1203        }
1204
1205        function copyEventHandlers(/*DOMNode*/ src, /*DOMNode*/ target){
1206                // summary:
1207                //              copies the event handlers from onne src *element* node to
1208                //              another target *element* node. Assumes that target had
1209                //              no previous events on it, and is a clone of the src node.
1210
1211                //Get src listeners.
1212                var srcNodeId = target.getAttribute(eventAttr);
1213                var sls = listeners[srcNodeId];
1214                if(!sls){
1215                        return;
1216                }
1217
1218                //Generate listeners area for target.
1219                var nodeId = nodeId = listenId++;
1220                target.setAttribute(eventAttr, nodeId);
1221                var tls = listeners[nodeId] = {};
1222
1223                //Loope through events in source. Protect against bad
1224                //code modifying Object.prototype.
1225                var empty = {};
1226                for (var evtName in sls){
1227                        var tEvtData = tls[evtName] = {
1228                                _connectId: domConnect(target, evtName)
1229                        };
1230                        var sEvtData = sls[evtName];
1231
1232                        for (var evtFullName in sEvtData){
1233                                tEvtData[evtFullName] = {
1234                                        fn: sEvtData[evtFullName].fn,
1235                                        data: sEvtData[evtFullName].data
1236                                };
1237                        }
1238                }
1239        }
1240
1241        function listenerUnbind(lls, evtName, evtFullName, callbackId, fn){
1242                //Handles the real remove of an event and dojo.disconnects DOM handler if necessary.
1243                //This has to be broken out of the main unbind function because we have to support
1244                //things like unbind(".test") that go across major event names. Yuck.
1245                var handles = lls[evtName];
1246                if(handles){
1247                        var hasDot = evtFullName.indexOf(".") != -1;
1248                        var forceDelete = false;
1249
1250                        if(callbackId){
1251                                //Only need to unbind that one callback
1252                                delete handles[callbackId];
1253                        }else if(!hasDot && !fn){
1254                                forceDelete = true;
1255                        }else if(hasDot){
1256                                //A namespaced event.
1257                                //Problem is the namespaced event could be something like
1258                                //".test" which means remove all that end in .test. Yuck.
1259                                if(evtFullName.charAt(0) == "."){
1260                                        for(var param in handles){
1261                                                if(param.indexOf(evtFullName) == param.length - evtFullName.length){
1262                                                        delete handles[param];
1263                                                }
1264                                        }
1265                                }else{
1266                                        delete handles[evtFullName];
1267                                }
1268                        }else{
1269                                //Not a namespaced event. Cycle through the $$ names
1270                                //to find a function match.
1271                                for(var param in handles){
1272                                        if(param.indexOf("$$") != -1 && handles[param].fn == fn){
1273                                                delete handles[param];
1274                                                break;
1275                                        }
1276                                }
1277                        }
1278
1279                        //Remove handles/disconnect dom if no other params.
1280                        var allDone = true;
1281                        for(var param in handles){
1282                                if(param != "_connectId"){
1283                                        allDone = false;
1284                                        break;
1285                                }
1286                        }
1287                        if(forceDelete || allDone){
1288                                if(evtName.indexOf("ajax") != -1){
1289                                        dojo.unsubscribe(handles._connectId);
1290                                }else{
1291                                        dojo.disconnect(handles._connectId);
1292                                }
1293                                delete lls[evtName];
1294                        }
1295                }
1296        }
1297
1298        f.unbind = function(/*String*/type, /*Function*/fn){
1299               
1300                //See if event has a callbackId, if so, then we only unbind
1301                //that one callback.
1302                var callbackId = type ? type[dojo._scopeName + "callbackId"] : null;
1303
1304                //Type can be space separated values.
1305                type = type && type.type ? type.type : type;
1306                type = type ? type.split(" ") : type;
1307
1308                this.forEach(function(node){
1309                        //Only handle non text/comment nodes.
1310                        if(node.nodeType != 3 && node.nodeType != 8){
1311                                //If document, bind to documentElement
1312                                if(node.nodeType == 9){
1313                                        node = node.documentElement;
1314                                }
1315
1316                                //If no nodeId, then create one and attach it to the DOM node.
1317                                var nodeId = node.getAttribute(eventAttr);
1318                               
1319                                if(nodeId){
1320                                        //Get the event listeners for the event name, the complete name.
1321                                        var lls = listeners[nodeId];
1322                                        if(lls){
1323                                                //If no type, then it means do all bound types. Make a list of them.
1324                                                var etypes = type;
1325                                                if(!etypes){
1326                                                        etypes = [];
1327                                                        for(var param in lls){
1328                                                                etypes.push(param);
1329                                                        }
1330                                                }
1331
1332                                                //Process each event type.
1333                                                for(var i = 0; i < etypes.length; i++){
1334                                                        //Get event name, if have a dot on it, it is namespaced,
1335                                                        //be sure to get the core event name.
1336                                                        var evtFullName = etypes[i];
1337                                                        var evtName = getNonNamespacedName(evtFullName);
1338                       
1339                                                        //Problem is the namespaced event could be something like
1340                                                        //".test" which means remove all that end in .test. Yuck.
1341                                                        if(evtFullName.charAt(0) == "."){
1342                                                                for(var param in lls) {
1343                                                                        listenerUnbind(lls, param, evtFullName, callbackId, fn);
1344                                                                }
1345                                                        }else{
1346                                                                listenerUnbind(lls, evtName, evtFullName, callbackId, fn);
1347                                                        }
1348                                                }
1349                                        }
1350                                }
1351                        }
1352                });
1353
1354                return this;
1355        }
1356
1357        f.one = function(/*String*/evtName, /*Function*/func){
1358                var oneFunc = function(){
1359                        $(this).unbind(evtName, arguments.callee);
1360                        return func.apply(this, arguments);
1361                }
1362
1363                return this.bind(evtName, oneFunc);
1364        };
1365
1366        f._cloneNode = function(/*DOMNode*/ src){
1367                // summary:
1368                //              private utiltity to clone a node. Copies event handlers too.
1369                var target = src.cloneNode(true);
1370
1371                if(src.nodeType == 1){
1372                        //Look for event handlers in target.
1373                        var evNodes = dojo.query("[" + eventAttr + "]", target);
1374                        for(var i = 0, newNode; newNode = evNodes[i]; i++){
1375                                var oldNode = dojo.query('[' + eventAttr + '="' + newNode.getAttribute(eventAttr) + '"]', src)[0];
1376                                if(oldNode){
1377                                        copyEventHandlers(oldNode, newNode);
1378                                }
1379                        }
1380                }
1381                return target;
1382        };
1383
1384        //Temporary testing shim to get past jquery test setup errors.
1385        dojo.getObject("$.event.global", true);
1386
1387        //Set up event handlers
1388        dojo.forEach([
1389                "blur", "focus", "dblclick", "click", "error", "keydown", "keypress", "keyup", "load", "mousedown",
1390                "mouseenter", "mouseleave", "mousemove", "mouseout", "mouseover", "mouseup", "submit",
1391                "ajaxStart", "ajaxSend", "ajaxSuccess", "ajaxError", "ajaxComplete", "ajaxStop"
1392                ], function(evt){
1393                        f[evt] = function(callback){
1394                                if(callback){
1395                                        this.bind(evt, callback);
1396                                }else{
1397                                        this.trigger(evt);
1398                                }
1399                                return this;
1400                        }
1401                }
1402        );
1403
1404        //END jquery Events API methods
1405
1406
1407        //START jquery Effects API methods
1408        //http://docs.jquery.com/Effects
1409        function speedInt(speed){
1410                //Fix speed setting, translate string values to numbers.
1411                if(dojo.isString(speed)){
1412                        if(speed == "slow"){
1413                                speed = 700;
1414                        }else if(speed = "fast"){
1415                                speed = 300;
1416                        }else{
1417                                //Everything else is considered normal speed.
1418                                speed = 500;
1419                        }
1420                }
1421                return speed;
1422        }
1423       
1424        f.hide = function(/*String||Number*/speed, /*Function?*/callback){
1425                //Fix speed setting, translate string values to numbers.
1426                speed = speedInt(speed);
1427
1428                this.forEach(function(node){
1429                        var style = node.style;
1430                       
1431                        //Skip if already hidden
1432                        var cs = dojo.getComputedStyle(node);
1433                        if(cs.display == "none"){
1434                                return;
1435                        }
1436
1437                        style.overflow = "hidden";
1438                        style.display = "block";
1439                       
1440                        if(speed){
1441                                //It is alive!
1442                                dojo.anim(
1443                                        node,
1444                                        {
1445                                                width: 0,
1446                                                height: 0,
1447                                                opacity: 0
1448                                        },
1449                                        speed,
1450                                        null,
1451                                        function(){
1452                                                style.width = "";
1453                                                style.height = "";
1454                                                style.display = "none";
1455                                                return callback && callback.call(node);
1456                                        }
1457                                );
1458                        }else{
1459                                //No need for animation, fast path it.
1460                                dojo.style(node, "display", "none");
1461                                if(callback){
1462                                        callback.call(node);
1463                                }
1464                        }
1465                });
1466                return this;
1467        }
1468
1469        f.show = function(/*String||Number*/speed, /*Function?*/callback){
1470                //Fix speed setting, translate string values to numbers.
1471                speed = speedInt(speed);
1472
1473                this.forEach(function(node){
1474                        var style = node.style;
1475                        //Skip if the node is already showing.
1476                        var cs = dojo.getComputedStyle(node);
1477                        if(cs.display != "none"){
1478                                return;
1479                        }
1480
1481                        if(speed){
1482                                //Figure out size of element
1483                                //so we know when to stop animation.
1484                                //Try the easy path first.
1485                                var width = parseFloat(style.width);
1486                                var height = parseFloat(style.height);
1487                                if(!width || !height){
1488                                        //temporarily show the element to get
1489                                        //dimensions
1490                                        style.display = "block";
1491                                        var box = dojo.marginBox(node);
1492                                        width = box.w;
1493                                        height = box.h;
1494                                }
1495
1496                                //Make sure values are set to hidden state.
1497                                style.width = 0;
1498                                style.height = 0;
1499                                style.overflow = "hidden";
1500                                dojo.attr(node, "opacity", 0);
1501                                style.display = "block";
1502
1503                                //It is alive!
1504                                dojo.anim(
1505                                        node,
1506                                        {
1507                                                width: width,
1508                                                height: height,
1509                                                opacity: 1
1510                                        },
1511                                        speed,
1512                                        null,
1513                                        callback ? dojo.hitch(node, callback) : undefined
1514                                );
1515                        }else{
1516                                dojo.style(node, "display", "block");
1517                                if(callback){
1518                                        callback.call(node);
1519                                }
1520                        }
1521                });
1522                return this;
1523        }
1524
1525
1526        //END jquery Effects API methods
1527
1528
1529        //START jquery Ajax API methods
1530        //http://docs.jquery.com/Ajax
1531       
1532        $.ajaxSettings = {
1533        };
1534
1535        $.ajaxSetup = function(/*Object*/args){
1536                dojo.mixin($.ajaxSettings, args);
1537        }
1538
1539        var topics = {
1540                "ajaxStart": "/dojo/io/start",
1541                "ajaxSend": "/dojo/io/send",
1542                "ajaxSuccess": "/dojo/io/load",
1543                "ajaxError": "/dojo/io/error",
1544                "ajaxComplete": "/dojo/io/done",
1545                "ajaxStop": "/dojo/io/stop"
1546        };
1547
1548        for(var fnName in topics){
1549                //Make sure we are dealing with properties
1550                //we care about and not something another toolkit added.
1551                if(fnName.indexOf("ajax") == 0){
1552                        ;(function(fnName){
1553                                f[fnName] = function(callback){
1554                                        this.forEach(function(node){
1555                                                dojo.subscribe(topics[fnName], function(){
1556                                                        var fakeEvt = new $.Event(fnName);
1557                                                        var ioArgs = arguments[0] && arguments[0].ioArgs;
1558                                                        var xhr = ioArgs && ioArgs.xhr;
1559                                                        var args = ioArgs && ioArgs.args;
1560                                                        var res = arguments[1];
1561                                                        if("ajaxComplete|ajaxSend|ajaxSuccess".indexOf(fnName) != -1){
1562                                                                return callback.call(node, fakeEvt, xhr, args);
1563                                                        }else if(fnName == "ajaxError"){
1564                                                                return callback.call(node, fakeEvt, xhr, args, res);
1565                                                        }else{
1566                                                                //ajaxStart|ajaxStop
1567                                                                return callback.call(node, fakeEvt);
1568                                                        }
1569                                                });
1570                                        });
1571                                        return this;
1572                                }
1573                        })(fnName);
1574                }
1575        };
1576
1577        //Override dojo._xhrObj(dfd.ioArgs.args) to support beforeSend
1578        //Do not understand the reason for beforeSend, particularly
1579        //returning false stops the request.
1580        //WARNING: even with this code, the error and complete callbacks
1581        //will be fired because the deferred is cancelled. I feel this is
1582        //correct behavior for dojo, and not sure why beforeSend is needed.
1583        var _oldXhrObj = dojo._xhrObj;
1584        dojo._xhrObj = function(args){
1585                var xhr = _oldXhrObj.apply(dojo, arguments);
1586                if(args && args.beforeSend){
1587                        if(args.beforeSend(xhr) === false){
1588                                return false;
1589                        }
1590                }
1591                return xhr;
1592        }
1593
1594        $.ajax = function(/*Object*/args){
1595                //Not sure if the args are considered mutable.
1596                //Copy them to be safe.
1597                var temp = dojo.delegate($.ajaxSettings);
1598                for(var param in args){
1599                        //For data objects merge the data do not overwrite.
1600                        if(param == "data" && dojo.isObject(args[param]) && dojo.isObject(temp.data)){
1601                                for(var prop in args[param]){
1602                                        temp.data[prop] = args[param][prop];
1603                                }
1604                        }else{
1605                                temp[param] = args[param];
1606                        }
1607                }
1608                args = temp;
1609                var url = args.url;
1610
1611                if("async" in args){
1612                        args.sync = !args.async;
1613                }
1614
1615                //Turn off topic publications
1616                if(args.global === false){
1617                        args.ioPublish = false;
1618                }
1619
1620                if(args.data){
1621                        var data = args.data;
1622                        if(dojo.isString(data)){
1623                                //convert to an object.
1624                                args.content = dojo.queryToObject(data);
1625                        }else{
1626                                //data property values could be a function, be sure to call them if so.
1627                                //Hmm, this seems to be of dubious value.
1628                                for(var param in data){
1629                                        if(dojo.isFunction(data[param])){
1630                                                data[param] = data[param]();
1631                                        }
1632                                }
1633                                args.content = data;
1634                        }
1635                }
1636
1637                //dataType
1638                var dataType = args.dataType;
1639                if("dataType" in args){
1640                        if(dataType == "script"){
1641                                dataType = "javascript";
1642                        }else if(dataType == "html"){
1643                                dataType = "text";
1644                        }
1645                        args.handleAs = dataType;
1646                }else{
1647                        //Make a guess based on the URL.
1648                        dataType = args.handleAs = "text";
1649                        args.guessedType = true;
1650                }
1651
1652                //cache:
1653                if("cache" in args){
1654                        args.preventCache = !args.cache;
1655                }else{
1656                        if(args.dataType == "script" || args.dataType == "jsonp"){
1657                                args.preventCache = true;
1658                        }
1659                }
1660
1661                //Hide error since dojo treats it different.
1662                if(args.error){
1663                        args._jqueryError = args.error;
1664                        delete args.error;
1665                }
1666               
1667                //TODO: dataFilter
1668
1669                //Set up callbacks.
1670                args.handle = function(result, ioArgs){
1671                        var textStatus = "success";
1672                        if(result instanceof Error){
1673                                textStatus = (result.dojoType == "timeout" ? "timeout" : "error");
1674                                if(args._jqueryError){
1675                                        args._jqueryError(ioArgs.xhr, textStatus, result);
1676                                }
1677                        }else{
1678                                //If we guessed the type, see if it should be XML.
1679                                var xml = (ioArgs.args.guessedType && ioArgs.xhr && ioArgs.xhr.responseXML);
1680                                if(xml){
1681                                        result = xml;
1682                                }
1683
1684                                if(args.success){
1685                                        args.success(result, textStatus, ioArgs.xhr);
1686                                }
1687                        }
1688                        if(args.complete){
1689                                args.complete(result, textStatus, ioArgs.xhr);
1690                        }
1691                       
1692                        return result;
1693                };
1694               
1695                //Use a script tag if the request is xdomain or a jsonp thing.
1696                var useScript = (dataType == "jsonp");
1697                if(dataType == "javascript"){
1698                        //Get protocol and domain.
1699                        var colonIndex = url.indexOf(":");
1700                        var slashIndex = url.indexOf("/");
1701                        if(colonIndex > 0 && colonIndex < slashIndex){
1702                                //Possibly xdomain. Peel off protocol and hostname to find out.
1703                                var lastSlash = url.indexOf("/", slashIndex + 2);
1704                                if(lastSlash == -1){
1705                                        lastSlash = url.length;
1706                                }
1707                                if(location.protocol != url.substring(0, colonIndex + 1) ||
1708                                        location.hostname != url.substring(slashIndex + 2, lastSlash)){
1709                                        useScript = true;
1710                                }
1711                        }
1712                }
1713
1714                if(useScript){
1715                        if(dataType == "jsonp"){
1716                                //Look for callback param
1717                                var cb = args.jsonp;
1718                                if(!cb){
1719                                        //Look in the URL
1720                                        var params = args.url.split("?")[1];
1721                                        if(params && (params = dojo.queryToObject(params))){
1722                                                cb = findJsonpCallback(params);
1723                                                if(cb){
1724                                                        //Remove the cb from the url.
1725                                                        var regex = new RegExp("([&\\?])?" + cb + "=?");
1726                                                        args.url = args.url.replace(regex + "=?");
1727                                                }
1728                                        }
1729                                        //Look in the content.
1730                                        if(!cb){
1731                                                cb = findJsonpCallback(args.content);
1732                                                if(cb){
1733                                                        delete args.content[cb];
1734                                                }
1735                                        }
1736                                }
1737                                args.jsonp = cb || "callback";
1738                        }
1739                        var dfd = dojo.io.script.get(args);
1740                        return dfd;
1741                }else{
1742                        var dfd = dojo.xhr(args.type || "GET", args);
1743                        //If the XHR object is false, it means beforeSend canceled the request.
1744                        return dfd.ioArgs.xhr === false ? false : dfd.ioArgs.xhr;
1745                }
1746        }
1747
1748        function findJsonpCallback(obj){
1749                for(var prop in obj){
1750                        if(prop.indexOf("callback") == prop.length - 8){
1751                                return prop;
1752                        }
1753                }
1754                return null;
1755        }
1756       
1757        $.getpost = function(httpType, url, data, callback, dataType){
1758                var args = {
1759                        url: url,
1760                        type: httpType
1761                };
1762
1763                //Normalize data, considering it may be the real
1764                //callback.
1765                if(data){
1766                        if(dojo.isFunction(data) && !callback){
1767                                args.complete = data;
1768                        }else{
1769                                args.data = data;
1770                        }
1771                }
1772
1773                //Normalize callback, considering it may be
1774                //the datatype.
1775                if(callback){
1776                        if(dojo.isString(callback) && !dataType){
1777                                dataType = callback;
1778                        }else{
1779                                args.complete = callback;
1780                        }
1781                }
1782
1783                if(dataType){
1784                        args.dataType = dataType;
1785                }
1786
1787                return $.ajax(args);
1788        };
1789
1790        $.get = dojo.hitch($, "getpost", "GET");
1791        $.post = dojo.hitch($, "getpost", "POST");
1792        $.getJSON = function(url, data, callback){
1793                return $.getpost("GET", url, data, callback, "json");
1794        }
1795        $.getScript = function(url, callback){
1796                return $.ajax({
1797                        url: url,
1798                        success: callback,
1799                        dataType: "script"
1800                });
1801        }
1802
1803        f.load = function(url, data, callback){
1804               
1805                //See if this is a window or document. If so, then want to
1806                //register onload handler.
1807                var node = this[0];
1808                if(!node || !node.nodeType || node.nodeType == 9){
1809                        dojo.addOnLoad(url);
1810                        return this;
1811                }
1812
1813                //The rest of this function is the ajax HTML load case.
1814                //Pull off selector if it is on the url.
1815                var parts = url.split(/\s+/);
1816                url = parts[0];
1817                var query = parts[1];
1818               
1819                var finalCb = callback || data;
1820
1821                var cb = dojo.hitch(this, function(result, textStatus, xhr){
1822                        //Try to find all the body content.
1823                        var match = result.match(/\<\s*body[^>]+>.*<\/body\s*>/i);
1824                        if(match){
1825                                result = match;
1826                        }
1827
1828                        //Convert to DOM nodes.
1829                        var nodes = dojo._toDom(result);
1830
1831                        //Apply query, using a temp div to do the filtering.
1832                        if(query){
1833                                var temp = $(dojo.create("div"));
1834                                temp.append(nodes);
1835                                nodes = temp.find(query);
1836                        }else{
1837                                nodes = $(nodes.nodeType == 11 ? nodes.childNodes : nodes);
1838                        }
1839
1840                        //Add the HTML to all nodes in this node list.
1841                        this.html(nodes);
1842
1843                        //Call the user's callback.
1844                        //Use a timeout to allow any embedded scripts that
1845                        //were just inserted to run.
1846                        if(finalCb){
1847                                setTimeout(dojo.hitch(this, function(){
1848                                        this.forEach(function(node){
1849                                                finalCb.call(node, result, textStatus, xhr);
1850                                        });
1851                                }), 10);
1852                        }
1853                });
1854
1855                //Adjust parameters since they are variable.
1856                if(!callback){
1857                        data = cb;
1858                }else{
1859                        callback = cb;
1860                }
1861
1862                //Set HTTP method. If the data is a string, use get, if it is an object,
1863                //use post.
1864                var method = "GET";
1865                if(data && dojo.isObject(data)){
1866                        method = "POST";
1867                }
1868
1869                $.getpost(method, url, data, callback, "html");
1870                return this;
1871        }
1872
1873        var serializeExclude = "file|submit|image|reset|button|";
1874        f.serialize = function(){
1875                var ret = "";
1876                var strs = this.map(function(node){
1877                        if(node.nodeName.toUpperCase() == "FORM"){
1878                                return dojo.formToQuery(node);
1879                        }else{
1880                                var type = (node.type||"").toLowerCase();
1881                                if(serializeExclude.indexOf(type) == -1){
1882                                        var val = dojo.fieldToObject(node);
1883                                        if(node.name && val != null){
1884                                                var q = {};
1885                                                q[node.name] = val;
1886                                                return dojo.objectToQuery(q);
1887                                        }
1888                                }
1889                        }
1890                });
1891                return ret + strs.join("&");
1892        }
1893
1894        $.param = function(obj){
1895                if(obj._is$ && obj.serialize){
1896                        return obj.serialize();
1897                }else if(dojo.isArray(obj)){
1898                        return dojo.map(obj, function(item){
1899                                return $.param(item);
1900                        }).join("&");
1901                }else{
1902                        return dojo.objectToQuery(obj);
1903                }
1904        }
1905       
1906        //END jquery Ajax API methods
1907
1908        //START jquery Utilities API methods
1909        //http://docs.jquery.com/Utilities
1910        //TODO:
1911       
1912        $.isFunction = function(){
1913                var result = dojo.isFunction.apply(dojo, arguments);
1914                //Make sure Object does not return true
1915                if(result){
1916                        result = (typeof(arguments[0]) != "object");
1917                }
1918                return result;
1919        }
1920
1921        //END jquery Utilities API methods
1922
1923       
1924})();
1925
Note: See TracBrowser for help on using the repository browser.