diff options
Diffstat (limited to 'lib/dojo/_base')
24 files changed, 13234 insertions, 5829 deletions
diff --git a/lib/dojo/_base/Color.js b/lib/dojo/_base/Color.js index 0ee5888d1..5f5c5af9b 100644 --- a/lib/dojo/_base/Color.js +++ b/lib/dojo/_base/Color.js @@ -5,98 +5,223 @@ */ -if(!dojo._hasResource["dojo._base.Color"]){ -dojo._hasResource["dojo._base.Color"]=true; +if(!dojo._hasResource["dojo._base.Color"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo._base.Color"] = true; dojo.provide("dojo._base.Color"); dojo.require("dojo._base.array"); dojo.require("dojo._base.lang"); + (function(){ -var d=dojo; -dojo.Color=function(_1){ -if(_1){ -this.setColor(_1); -} -}; -dojo.Color.named={black:[0,0,0],silver:[192,192,192],gray:[128,128,128],white:[255,255,255],maroon:[128,0,0],red:[255,0,0],purple:[128,0,128],fuchsia:[255,0,255],green:[0,128,0],lime:[0,255,0],olive:[128,128,0],yellow:[255,255,0],navy:[0,0,128],blue:[0,0,255],teal:[0,128,128],aqua:[0,255,255],transparent:d.config.transparentColor||[255,255,255]}; -dojo.extend(dojo.Color,{r:255,g:255,b:255,a:1,_set:function(r,g,b,a){ -var t=this; -t.r=r; -t.g=g; -t.b=b; -t.a=a; -},setColor:function(_2){ -if(d.isString(_2)){ -d.colorFromString(_2,this); -}else{ -if(d.isArray(_2)){ -d.colorFromArray(_2,this); -}else{ -this._set(_2.r,_2.g,_2.b,_2.a); -if(!(_2 instanceof d.Color)){ -this.sanitize(); -} -} -} -return this; -},sanitize:function(){ -return this; -},toRgb:function(){ -var t=this; -return [t.r,t.g,t.b]; -},toRgba:function(){ -var t=this; -return [t.r,t.g,t.b,t.a]; -},toHex:function(){ -var _3=d.map(["r","g","b"],function(x){ -var s=this[x].toString(16); -return s.length<2?"0"+s:s; -},this); -return "#"+_3.join(""); -},toCss:function(_4){ -var t=this,_5=t.r+", "+t.g+", "+t.b; -return (_4?"rgba("+_5+", "+t.a:"rgb("+_5)+")"; -},toString:function(){ -return this.toCss(true); -}}); -dojo.blendColors=function(_6,_7,_8,_9){ -var t=_9||new d.Color(); -d.forEach(["r","g","b","a"],function(x){ -t[x]=_6[x]+(_7[x]-_6[x])*_8; -if(x!="a"){ -t[x]=Math.round(t[x]); -} -}); -return t.sanitize(); -}; -dojo.colorFromRgb=function(_a,_b){ -var m=_a.toLowerCase().match(/^rgba?\(([\s\.,0-9]+)\)/); -return m&&dojo.colorFromArray(m[1].split(/\s*,\s*/),_b); -}; -dojo.colorFromHex=function(_c,_d){ -var t=_d||new d.Color(),_e=(_c.length==4)?4:8,_f=(1<<_e)-1; -_c=Number("0x"+_c.substr(1)); -if(isNaN(_c)){ -return null; -} -d.forEach(["b","g","r"],function(x){ -var c=_c&_f; -_c>>=_e; -t[x]=_e==4?17*c:c; -}); -t.a=1; -return t; -}; -dojo.colorFromArray=function(a,obj){ -var t=obj||new d.Color(); -t._set(Number(a[0]),Number(a[1]),Number(a[2]),Number(a[3])); -if(isNaN(t.a)){ -t.a=1; -} -return t.sanitize(); -}; -dojo.colorFromString=function(str,obj){ -var a=d.Color.named[str]; -return a&&d.colorFromArray(a,obj)||d.colorFromRgb(str,obj)||d.colorFromHex(str,obj); -}; + + var d = dojo; + + dojo.Color = function(/*Array|String|Object*/ color){ + // summary: + // Takes a named string, hex string, array of rgb or rgba values, + // an object with r, g, b, and a properties, or another `dojo.Color` object + // and creates a new Color instance to work from. + // + // example: + // Work with a Color instance: + // | var c = new dojo.Color(); + // | c.setColor([0,0,0]); // black + // | var hex = c.toHex(); // #000000 + // + // example: + // Work with a node's color: + // | var color = dojo.style("someNode", "backgroundColor"); + // | var n = new dojo.Color(color); + // | // adjust the color some + // | n.r *= .5; + // | console.log(n.toString()); // rgb(128, 255, 255); + if(color){ this.setColor(color); } + }; + + // FIXME: + // there's got to be a more space-efficient way to encode or discover + // these!! Use hex? + dojo.Color.named = { + black: [0,0,0], + silver: [192,192,192], + gray: [128,128,128], + white: [255,255,255], + maroon: [128,0,0], + red: [255,0,0], + purple: [128,0,128], + fuchsia: [255,0,255], + green: [0,128,0], + lime: [0,255,0], + olive: [128,128,0], + yellow: [255,255,0], + navy: [0,0,128], + blue: [0,0,255], + teal: [0,128,128], + aqua: [0,255,255], + transparent: d.config.transparentColor || [255,255,255] + }; + + dojo.extend(dojo.Color, { + r: 255, g: 255, b: 255, a: 1, + _set: function(r, g, b, a){ + var t = this; t.r = r; t.g = g; t.b = b; t.a = a; + }, + setColor: function(/*Array|String|Object*/ color){ + // summary: + // Takes a named string, hex string, array of rgb or rgba values, + // an object with r, g, b, and a properties, or another `dojo.Color` object + // and sets this color instance to that value. + // + // example: + // | var c = new dojo.Color(); // no color + // | c.setColor("#ededed"); // greyish + if(d.isString(color)){ + d.colorFromString(color, this); + }else if(d.isArray(color)){ + d.colorFromArray(color, this); + }else{ + this._set(color.r, color.g, color.b, color.a); + if(!(color instanceof d.Color)){ this.sanitize(); } + } + return this; // dojo.Color + }, + sanitize: function(){ + // summary: + // Ensures the object has correct attributes + // description: + // the default implementation does nothing, include dojo.colors to + // augment it with real checks + return this; // dojo.Color + }, + toRgb: function(){ + // summary: + // Returns 3 component array of rgb values + // example: + // | var c = new dojo.Color("#000000"); + // | console.log(c.toRgb()); // [0,0,0] + var t = this; + return [t.r, t.g, t.b]; // Array + }, + toRgba: function(){ + // summary: + // Returns a 4 component array of rgba values from the color + // represented by this object. + var t = this; + return [t.r, t.g, t.b, t.a]; // Array + }, + toHex: function(){ + // summary: + // Returns a CSS color string in hexadecimal representation + // example: + // | console.log(new dojo.Color([0,0,0]).toHex()); // #000000 + var arr = d.map(["r", "g", "b"], function(x){ + var s = this[x].toString(16); + return s.length < 2 ? "0" + s : s; + }, this); + return "#" + arr.join(""); // String + }, + toCss: function(/*Boolean?*/ includeAlpha){ + // summary: + // Returns a css color string in rgb(a) representation + // example: + // | var c = new dojo.Color("#FFF").toCss(); + // | console.log(c); // rgb('255','255','255') + var t = this, rgb = t.r + ", " + t.g + ", " + t.b; + return (includeAlpha ? "rgba(" + rgb + ", " + t.a : "rgb(" + rgb) + ")"; // String + }, + toString: function(){ + // summary: + // Returns a visual representation of the color + return this.toCss(true); // String + } + }); + + dojo.blendColors = function( + /*dojo.Color*/ start, + /*dojo.Color*/ end, + /*Number*/ weight, + /*dojo.Color?*/ obj + ){ + // summary: + // Blend colors end and start with weight from 0 to 1, 0.5 being a 50/50 blend, + // can reuse a previously allocated dojo.Color object for the result + var t = obj || new d.Color(); + d.forEach(["r", "g", "b", "a"], function(x){ + t[x] = start[x] + (end[x] - start[x]) * weight; + if(x != "a"){ t[x] = Math.round(t[x]); } + }); + return t.sanitize(); // dojo.Color + }; + + dojo.colorFromRgb = function(/*String*/ color, /*dojo.Color?*/ obj){ + // summary: + // Returns a `dojo.Color` instance from a string of the form + // "rgb(...)" or "rgba(...)". Optionally accepts a `dojo.Color` + // object to update with the parsed value and return instead of + // creating a new object. + // returns: + // A dojo.Color object. If obj is passed, it will be the return value. + var m = color.toLowerCase().match(/^rgba?\(([\s\.,0-9]+)\)/); + return m && dojo.colorFromArray(m[1].split(/\s*,\s*/), obj); // dojo.Color + }; + + dojo.colorFromHex = function(/*String*/ color, /*dojo.Color?*/ obj){ + // summary: + // Converts a hex string with a '#' prefix to a color object. + // Supports 12-bit #rgb shorthand. Optionally accepts a + // `dojo.Color` object to update with the parsed value. + // + // returns: + // A dojo.Color object. If obj is passed, it will be the return value. + // + // example: + // | var thing = dojo.colorFromHex("#ededed"); // grey, longhand + // + // example: + // | var thing = dojo.colorFromHex("#000"); // black, shorthand + var t = obj || new d.Color(), + bits = (color.length == 4) ? 4 : 8, + mask = (1 << bits) - 1; + color = Number("0x" + color.substr(1)); + if(isNaN(color)){ + return null; // dojo.Color + } + d.forEach(["b", "g", "r"], function(x){ + var c = color & mask; + color >>= bits; + t[x] = bits == 4 ? 17 * c : c; + }); + t.a = 1; + return t; // dojo.Color + }; + + dojo.colorFromArray = function(/*Array*/ a, /*dojo.Color?*/ obj){ + // summary: + // Builds a `dojo.Color` from a 3 or 4 element array, mapping each + // element in sequence to the rgb(a) values of the color. + // example: + // | var myColor = dojo.colorFromArray([237,237,237,0.5]); // grey, 50% alpha + // returns: + // A dojo.Color object. If obj is passed, it will be the return value. + var t = obj || new d.Color(); + t._set(Number(a[0]), Number(a[1]), Number(a[2]), Number(a[3])); + if(isNaN(t.a)){ t.a = 1; } + return t.sanitize(); // dojo.Color + }; + + dojo.colorFromString = function(/*String*/ str, /*dojo.Color?*/ obj){ + // summary: + // Parses `str` for a color value. Accepts hex, rgb, and rgba + // style color values. + // description: + // Acceptable input values for str may include arrays of any form + // accepted by dojo.colorFromArray, hex strings such as "#aaaaaa", or + // rgb or rgba strings such as "rgb(133, 200, 16)" or "rgba(10, 10, + // 10, 50)" + // returns: + // A dojo.Color object. If obj is passed, it will be the return value. + var a = d.Color.named[str]; + return a && d.colorFromArray(a, obj) || d.colorFromRgb(str, obj) || d.colorFromHex(str, obj); + }; })(); + } diff --git a/lib/dojo/_base/Deferred.js b/lib/dojo/_base/Deferred.js index dfbabc4c0..3193024ab 100644 --- a/lib/dojo/_base/Deferred.js +++ b/lib/dojo/_base/Deferred.js @@ -5,126 +5,338 @@ */ -if(!dojo._hasResource["dojo._base.Deferred"]){ -dojo._hasResource["dojo._base.Deferred"]=true; +if(!dojo._hasResource["dojo._base.Deferred"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo._base.Deferred"] = true; dojo.provide("dojo._base.Deferred"); dojo.require("dojo._base.lang"); + (function(){ -var _1=function(){ -}; -var _2=Object.freeze||function(){ -}; -dojo.Deferred=function(_3){ -var _4,_5,_6,_7,_8; -var _9=this.promise={}; -function _a(_b){ -if(_5){ -throw new Error("This deferred has already been resolved"); -} -_4=_b; -_5=true; -_c(); -}; -function _c(){ -var _d; -while(!_d&&_8){ -var _e=_8; -_8=_8.next; -if(_d=(_e.progress==_1)){ -_5=false; -} -var _f=(_6?_e.error:_e.resolved); -if(_f){ -try{ -var _10=_f(_4); -if(_10&&typeof _10.then==="function"){ -_10.then(dojo.hitch(_e.deferred,"resolve"),dojo.hitch(_e.deferred,"reject")); -continue; -} -var _11=_d&&_10===undefined; -_e.deferred[_11&&_6?"reject":"resolve"](_11?_4:_10); -} -catch(e){ -_e.deferred.reject(e); -} -}else{ -if(_6){ -_e.deferred.reject(_4); -}else{ -_e.deferred.resolve(_4); -} -} -} -}; -this.resolve=this.callback=function(_12){ -this.fired=0; -this.results=[_12,null]; -_a(_12); -}; -this.reject=this.errback=function(_13){ -_6=true; -this.fired=1; -_a(_13); -this.results=[null,_13]; -if(!_13||_13.log!==false){ -(dojo.config.deferredOnError||function(x){ -console.error(x); -})(_13); -} -}; -this.progress=function(_14){ -var _15=_8; -while(_15){ -var _16=_15.progress; -_16&&_16(_14); -_15=_15.next; -} -}; -this.addCallbacks=function(_17,_18){ -this.then(_17,_18,_1); -return this; -}; -this.then=_9.then=function(_19,_1a,_1b){ -var _1c=_1b==_1?this:new dojo.Deferred(_9.cancel); -var _1d={resolved:_19,error:_1a,progress:_1b,deferred:_1c}; -if(_8){ -_7=_7.next=_1d; -}else{ -_8=_7=_1d; -} -if(_5){ -_c(); -} -return _1c.promise; -}; -var _1e=this; -this.cancel=_9.cancel=function(){ -if(!_5){ -var _1f=_3&&_3(_1e); -if(!_5){ -if(!(_1f instanceof Error)){ -_1f=new Error(_1f); -} -_1f.log=false; -_1e.reject(_1f); -} -} -}; -_2(_9); -}; -dojo.extend(dojo.Deferred,{addCallback:function(_20){ -return this.addCallbacks(dojo.hitch.apply(dojo,arguments)); -},addErrback:function(_21){ -return this.addCallbacks(null,dojo.hitch.apply(dojo,arguments)); -},addBoth:function(_22){ -var _23=dojo.hitch.apply(dojo,arguments); -return this.addCallbacks(_23,_23); -},fired:-1}); + var mutator = function(){}; + var freeze = Object.freeze || function(){}; + // A deferred provides an API for creating and resolving a promise. + dojo.Deferred = function(/*Function?*/canceller){ + // summary: + // Deferreds provide a generic means for encapsulating an asynchronous + // operation and notifying users of the completion and result of the operation. + // description: + // The dojo.Deferred API is based on the concept of promises that provide a + // generic interface into the eventual completion of an asynchronous action. + // The motivation for promises fundamentally is about creating a + // separation of concerns that allows one to achieve the same type of + // call patterns and logical data flow in asynchronous code as can be + // achieved in synchronous code. Promises allows one + // to be able to call a function purely with arguments needed for + // execution, without conflating the call with concerns of whether it is + // sync or async. One shouldn't need to alter a call's arguments if the + // implementation switches from sync to async (or vice versa). By having + // async functions return promises, the concerns of making the call are + // separated from the concerns of asynchronous interaction (which are + // handled by the promise). + // + // The dojo.Deferred is a type of promise that provides methods for fulfilling the + // promise with a successful result or an error. The most important method for + // working with Dojo's promises is the then() method, which follows the + // CommonJS proposed promise API. An example of using a Dojo promise: + // + // | var resultingPromise = someAsyncOperation.then(function(result){ + // | ... handle result ... + // | }, + // | function(error){ + // | ... handle error ... + // | }); + // + // The .then() call returns a new promise that represents the result of the + // execution of the callback. The callbacks will never affect the original promises value. + // + // The dojo.Deferred instances also provide the following functions for backwards compatibility: + // + // * addCallback(handler) + // * addErrback(handler) + // * callback(result) + // * errback(result) + // + // Callbacks are allowed to return promisesthemselves, so + // you can build complicated sequences of events with ease. + // + // The creator of the Deferred may specify a canceller. The canceller + // is a function that will be called if Deferred.cancel is called + // before the Deferred fires. You can use this to implement clean + // aborting of an XMLHttpRequest, etc. Note that cancel will fire the + // deferred with a CancelledError (unless your canceller returns + // another kind of error), so the errbacks should be prepared to + // handle that error for cancellable Deferreds. + // example: + // | var deferred = new dojo.Deferred(); + // | setTimeout(function(){ deferred.callback({success: true}); }, 1000); + // | return deferred; + // example: + // Deferred objects are often used when making code asynchronous. It + // may be easiest to write functions in a synchronous manner and then + // split code using a deferred to trigger a response to a long-lived + // operation. For example, instead of register a callback function to + // denote when a rendering operation completes, the function can + // simply return a deferred: + // + // | // callback style: + // | function renderLotsOfData(data, callback){ + // | var success = false + // | try{ + // | for(var x in data){ + // | renderDataitem(data[x]); + // | } + // | success = true; + // | }catch(e){ } + // | if(callback){ + // | callback(success); + // | } + // | } + // + // | // using callback style + // | renderLotsOfData(someDataObj, function(success){ + // | // handles success or failure + // | if(!success){ + // | promptUserToRecover(); + // | } + // | }); + // | // NOTE: no way to add another callback here!! + // example: + // Using a Deferred doesn't simplify the sending code any, but it + // provides a standard interface for callers and senders alike, + // providing both with a simple way to service multiple callbacks for + // an operation and freeing both sides from worrying about details + // such as "did this get called already?". With Deferreds, new + // callbacks can be added at any time. + // + // | // Deferred style: + // | function renderLotsOfData(data){ + // | var d = new dojo.Deferred(); + // | try{ + // | for(var x in data){ + // | renderDataitem(data[x]); + // | } + // | d.callback(true); + // | }catch(e){ + // | d.errback(new Error("rendering failed")); + // | } + // | return d; + // | } + // + // | // using Deferred style + // | renderLotsOfData(someDataObj).then(null, function(){ + // | promptUserToRecover(); + // | }); + // | // NOTE: addErrback and addCallback both return the Deferred + // | // again, so we could chain adding callbacks or save the + // | // deferred for later should we need to be notified again. + // example: + // In this example, renderLotsOfData is syncrhonous and so both + // versions are pretty artificial. Putting the data display on a + // timeout helps show why Deferreds rock: + // + // | // Deferred style and async func + // | function renderLotsOfData(data){ + // | var d = new dojo.Deferred(); + // | setTimeout(function(){ + // | try{ + // | for(var x in data){ + // | renderDataitem(data[x]); + // | } + // | d.callback(true); + // | }catch(e){ + // | d.errback(new Error("rendering failed")); + // | } + // | }, 100); + // | return d; + // | } + // + // | // using Deferred style + // | renderLotsOfData(someDataObj).then(null, function(){ + // | promptUserToRecover(); + // | }); + // + // Note that the caller doesn't have to change his code at all to + // handle the asynchronous case. + var result, finished, isError, head, nextListener; + var promise = this.promise = {}; + + function complete(value){ + if(finished){ + throw new Error("This deferred has already been resolved"); + } + result = value; + finished = true; + notify(); + } + function notify(){ + var mutated; + while(!mutated && nextListener){ + var listener = nextListener; + nextListener = nextListener.next; + if(mutated = (listener.progress == mutator)){ // assignment and check + finished = false; + } + var func = (isError ? listener.error : listener.resolved); + if (func) { + try { + var newResult = func(result); + if (newResult && typeof newResult.then === "function") { + newResult.then(dojo.hitch(listener.deferred, "resolve"), dojo.hitch(listener.deferred, "reject")); + continue; + } + var unchanged = mutated && newResult === undefined; + listener.deferred[unchanged && isError ? "reject" : "resolve"](unchanged ? result : newResult); + } + catch (e) { + listener.deferred.reject(e); + } + }else { + if(isError){ + listener.deferred.reject(result); + }else{ + listener.deferred.resolve(result); + } + } + } + } + // calling resolve will resolve the promise + this.resolve = this.callback = function(value){ + // summary: + // Fulfills the Deferred instance successfully with the provide value + this.fired = 0; + this.results = [value, null]; + complete(value); + }; + + + // calling error will indicate that the promise failed + this.reject = this.errback = function(error){ + // summary: + // Fulfills the Deferred instance as an error with the provided error + isError = true; + this.fired = 1; + complete(error); + this.results = [null, error]; + if(!error || error.log !== false){ + (dojo.config.deferredOnError || function(x){ console.error(x); })(error); + } + }; + // call progress to provide updates on the progress on the completion of the promise + this.progress = function(update){ + // summary + // Send progress events to all listeners + var listener = nextListener; + while(listener){ + var progress = listener.progress; + progress && progress(update); + listener = listener.next; + } + }; + this.addCallbacks = function(/*Function?*/callback, /*Function?*/errback){ + this.then(callback, errback, mutator); + return this; + }; + // provide the implementation of the promise + this.then = promise.then = function(/*Function?*/resolvedCallback, /*Function?*/errorCallback, /*Function?*/progressCallback){ + // summary + // Adds a fulfilledHandler, errorHandler, and progressHandler to be called for + // completion of a promise. The fulfilledHandler is called when the promise + // is fulfilled. The errorHandler is called when a promise fails. The + // progressHandler is called for progress events. All arguments are optional + // and non-function values are ignored. The progressHandler is not only an + // optional argument, but progress events are purely optional. Promise + // providers are not required to ever create progress events. + // + // This function will return a new promise that is fulfilled when the given + // fulfilledHandler or errorHandler callback is finished. This allows promise + // operations to be chained together. The value returned from the callback + // handler is the fulfillment value for the returned promise. If the callback + // throws an error, the returned promise will be moved to failed state. + // + // example: + // An example of using a CommonJS compliant promise: + // | asyncComputeTheAnswerToEverything(). + // | then(addTwo). + // | then(printResult, onError); + // | >44 + // + var returnDeferred = progressCallback == mutator ? this : new dojo.Deferred(promise.cancel); + var listener = { + resolved: resolvedCallback, + error: errorCallback, + progress: progressCallback, + deferred: returnDeferred + }; + if(nextListener){ + head = head.next = listener; + } + else{ + nextListener = head = listener; + } + if(finished){ + notify(); + } + return returnDeferred.promise; + }; + var deferred = this; + this.cancel = promise.cancel = function () { + // summary: + // Cancels the asynchronous operation + if(!finished){ + var error = canceller && canceller(deferred); + if(!finished){ + if (!(error instanceof Error)) { + error = new Error(error); + } + error.log = false; + deferred.reject(error); + } + } + } + freeze(promise); + }; + dojo.extend(dojo.Deferred, { + addCallback: function (/*Function*/callback) { + return this.addCallbacks(dojo.hitch.apply(dojo, arguments)); + }, + + addErrback: function (/*Function*/errback) { + return this.addCallbacks(null, dojo.hitch.apply(dojo, arguments)); + }, + + addBoth: function (/*Function*/callback) { + var enclosed = dojo.hitch.apply(dojo, arguments); + return this.addCallbacks(enclosed, enclosed); + }, + fired: -1 + }); })(); -dojo.when=function(_24,_25,_26,_27){ -if(_24&&typeof _24.then==="function"){ -return _24.then(_25,_26,_27); -} -return _25(_24); +dojo.when = function(promiseOrValue, /*Function?*/callback, /*Function?*/errback, /*Function?*/progressHandler){ + // summary: + // This provides normalization between normal synchronous values and + // asynchronous promises, so you can interact with them in a common way + // example: + // | function printFirstAndList(items){ + // | dojo.when(findFirst(items), console.log); + // | dojo.when(findLast(items), console.log); + // | } + // | function findFirst(items){ + // | return dojo.when(items, function(items){ + // | return items[0]; + // | }); + // | } + // | function findLast(items){ + // | return dojo.when(items, function(items){ + // | return items[items.length]; + // | }); + // | } + // And now all three of his functions can be used sync or async. + // | printFirstAndLast([1,2,3,4]) will work just as well as + // | printFirstAndLast(dojo.xhrGet(...)); + + if(promiseOrValue && typeof promiseOrValue.then === "function"){ + return promiseOrValue.then(callback, errback, progressHandler); + } + return callback(promiseOrValue); }; + } diff --git a/lib/dojo/_base/NodeList.js b/lib/dojo/_base/NodeList.js index 6df6db976..12b631a4b 100644 --- a/lib/dojo/_base/NodeList.js +++ b/lib/dojo/_base/NodeList.js @@ -5,228 +5,1012 @@ */ -if(!dojo._hasResource["dojo._base.NodeList"]){ -dojo._hasResource["dojo._base.NodeList"]=true; +if(!dojo._hasResource["dojo._base.NodeList"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo._base.NodeList"] = true; dojo.provide("dojo._base.NodeList"); dojo.require("dojo._base.lang"); dojo.require("dojo._base.array"); + (function(){ -var d=dojo; -var ap=Array.prototype,_1=ap.slice,_2=ap.concat; -var _3=function(a,_4,_5){ -if(!a.sort){ -a=_1.call(a,0); -} -var _6=_5||this._NodeListCtor||d._NodeListCtor; -a.constructor=_6; -dojo._mixin(a,_6.prototype); -a._NodeListCtor=_6; -return _4?a._stash(_4):a; -}; -var _7=function(f,a,o){ -a=[0].concat(_1.call(a,0)); -o=o||d.global; -return function(_8){ -a[0]=_8; -return f.apply(o,a); -}; -}; -var _9=function(f,o){ -return function(){ -this.forEach(_7(f,arguments,o)); -return this; -}; -}; -var _a=function(f,o){ -return function(){ -return this.map(_7(f,arguments,o)); -}; -}; -var _b=function(f,o){ -return function(){ -return this.filter(_7(f,arguments,o)); -}; -}; -var _c=function(f,g,o){ -return function(){ -var a=arguments,_d=_7(f,a,o); -if(g.call(o||d.global,a)){ -return this.map(_d); -} -this.forEach(_d); -return this; -}; -}; -var _e=function(a){ -return a.length==1&&(typeof a[0]=="string"); -}; -var _f=function(_10){ -var p=_10.parentNode; -if(p){ -p.removeChild(_10); -} -}; -dojo.NodeList=function(){ -return _3(Array.apply(null,arguments)); -}; -d._NodeListCtor=d.NodeList; -var nl=d.NodeList,nlp=nl.prototype; -nl._wrap=nlp._wrap=_3; -nl._adaptAsMap=_a; -nl._adaptAsForEach=_9; -nl._adaptAsFilter=_b; -nl._adaptWithCondition=_c; -d.forEach(["slice","splice"],function(_11){ -var f=ap[_11]; -nlp[_11]=function(){ -return this._wrap(f.apply(this,arguments),_11=="slice"?this:null); -}; -}); -d.forEach(["indexOf","lastIndexOf","every","some"],function(_12){ -var f=d[_12]; -nlp[_12]=function(){ -return f.apply(d,[this].concat(_1.call(arguments,0))); -}; -}); -d.forEach(["attr","style"],function(_13){ -nlp[_13]=_c(d[_13],_e); -}); -d.forEach(["connect","addClass","removeClass","toggleClass","empty","removeAttr"],function(_14){ -nlp[_14]=_9(d[_14]); -}); -dojo.extend(dojo.NodeList,{_normalize:function(_15,_16){ -var _17=_15.parse===true?true:false; -if(typeof _15.template=="string"){ -var _18=_15.templateFunc||(dojo.string&&dojo.string.substitute); -_15=_18?_18(_15.template,_15):_15; -} -var _19=(typeof _15); -if(_19=="string"||_19=="number"){ -_15=dojo._toDom(_15,(_16&&_16.ownerDocument)); -if(_15.nodeType==11){ -_15=dojo._toArray(_15.childNodes); -}else{ -_15=[_15]; -} -}else{ -if(!dojo.isArrayLike(_15)){ -_15=[_15]; -}else{ -if(!dojo.isArray(_15)){ -_15=dojo._toArray(_15); -} -} -} -if(_17){ -_15._runParse=true; -} -return _15; -},_cloneNode:function(_1a){ -return _1a.cloneNode(true); -},_place:function(ary,_1b,_1c,_1d){ -if(_1b.nodeType!=1&&_1c=="only"){ -return; -} -var _1e=_1b,_1f; -var _20=ary.length; -for(var i=_20-1;i>=0;i--){ -var _21=(_1d?this._cloneNode(ary[i]):ary[i]); -if(ary._runParse&&dojo.parser&&dojo.parser.parse){ -if(!_1f){ -_1f=_1e.ownerDocument.createElement("div"); -} -_1f.appendChild(_21); -dojo.parser.parse(_1f); -_21=_1f.firstChild; -while(_1f.firstChild){ -_1f.removeChild(_1f.firstChild); -} -} -if(i==_20-1){ -dojo.place(_21,_1e,_1c); -}else{ -_1e.parentNode.insertBefore(_21,_1e); -} -_1e=_21; -} -},_stash:function(_22){ -this._parent=_22; -return this; -},end:function(){ -if(this._parent){ -return this._parent; -}else{ -return new this._NodeListCtor(); -} -},concat:function(_23){ -var t=d.isArray(this)?this:_1.call(this,0),m=d.map(arguments,function(a){ -return a&&!d.isArray(a)&&(typeof NodeList!="undefined"&&a.constructor===NodeList||a.constructor===this._NodeListCtor)?_1.call(a,0):a; -}); -return this._wrap(_2.apply(t,m),this); -},map:function(_24,obj){ -return this._wrap(d.map(this,_24,obj),this); -},forEach:function(_25,_26){ -d.forEach(this,_25,_26); -return this; -},coords:_a(d.coords),position:_a(d.position),place:function(_27,_28){ -var _29=d.query(_27)[0]; -return this.forEach(function(_2a){ -d.place(_2a,_29,_28); -}); -},orphan:function(_2b){ -return (_2b?d._filterQueryResult(this,_2b):this).forEach(_f); -},adopt:function(_2c,_2d){ -return d.query(_2c).place(this[0],_2d)._stash(this); -},query:function(_2e){ -if(!_2e){ -return this; -} -var ret=this.map(function(_2f){ -return d.query(_2e,_2f).filter(function(_30){ -return _30!==undefined; -}); -}); -return this._wrap(_2.apply([],ret),this); -},filter:function(_31){ -var a=arguments,_32=this,_33=0; -if(typeof _31=="string"){ -_32=d._filterQueryResult(this,a[0]); -if(a.length==1){ -return _32._stash(this); -} -_33=1; -} -return this._wrap(d.filter(_32,a[_33],a[_33+1]),this); -},addContent:function(_34,_35){ -_34=this._normalize(_34,this[0]); -for(var i=0,_36;_36=this[i];i++){ -this._place(_34,_36,_35,i>0); -} -return this; -},instantiate:function(_37,_38){ -var c=d.isFunction(_37)?_37:d.getObject(_37); -_38=_38||{}; -return this.forEach(function(_39){ -new c(_38,_39); -}); -},at:function(){ -var t=new this._NodeListCtor(); -d.forEach(arguments,function(i){ -if(i<0){ -i=this.length+i; -} -if(this[i]){ -t.push(this[i]); -} -},this); -return t._stash(this); -}}); -nl.events=["blur","focus","change","click","error","keydown","keypress","keyup","load","mousedown","mouseenter","mouseleave","mousemove","mouseout","mouseover","mouseup","submit"]; -d.forEach(nl.events,function(evt){ -var _3a="on"+evt; -nlp[_3a]=function(a,b){ -return this.connect(_3a,a,b); -}; -}); + + var d = dojo; + + var ap = Array.prototype, aps = ap.slice, apc = ap.concat; + + var tnl = function(/*Array*/ a, /*dojo.NodeList?*/ parent, /*Function?*/ NodeListCtor){ + // summary: + // decorate an array to make it look like a `dojo.NodeList`. + // a: + // Array of nodes to decorate. + // parent: + // An optional parent NodeList that generated the current + // list of nodes. Used to call _stash() so the parent NodeList + // can be accessed via end() later. + // NodeListCtor: + // An optional constructor function to use for any + // new NodeList calls. This allows a certain chain of + // NodeList calls to use a different object than dojo.NodeList. + if(!a.sort){ + // make sure it's a real array before we pass it on to be wrapped + a = aps.call(a, 0); + } + var ctor = NodeListCtor || this._NodeListCtor || d._NodeListCtor; + a.constructor = ctor; + dojo._mixin(a, ctor.prototype); + a._NodeListCtor = ctor; + return parent ? a._stash(parent) : a; + }; + + var loopBody = function(f, a, o){ + a = [0].concat(aps.call(a, 0)); + o = o || d.global; + return function(node){ + a[0] = node; + return f.apply(o, a); + }; + }; + + // adapters + + var adaptAsForEach = function(f, o){ + // summary: + // adapts a single node function to be used in the forEach-type + // actions. The initial object is returned from the specialized + // function. + // f: Function + // a function to adapt + // o: Object? + // an optional context for f + return function(){ + this.forEach(loopBody(f, arguments, o)); + return this; // Object + }; + }; + + var adaptAsMap = function(f, o){ + // summary: + // adapts a single node function to be used in the map-type + // actions. The return is a new array of values, as via `dojo.map` + // f: Function + // a function to adapt + // o: Object? + // an optional context for f + return function(){ + return this.map(loopBody(f, arguments, o)); + }; + }; + + var adaptAsFilter = function(f, o){ + // summary: + // adapts a single node function to be used in the filter-type actions + // f: Function + // a function to adapt + // o: Object? + // an optional context for f + return function(){ + return this.filter(loopBody(f, arguments, o)); + }; + }; + + var adaptWithCondition = function(f, g, o){ + // summary: + // adapts a single node function to be used in the map-type + // actions, behaves like forEach() or map() depending on arguments + // f: Function + // a function to adapt + // g: Function + // a condition function, if true runs as map(), otherwise runs as forEach() + // o: Object? + // an optional context for f and g + return function(){ + var a = arguments, body = loopBody(f, a, o); + if(g.call(o || d.global, a)){ + return this.map(body); // self + } + this.forEach(body); + return this; // self + }; + }; + + var magicGuard = function(a){ + // summary: + // the guard function for dojo.attr() and dojo.style() + return a.length == 1 && (typeof a[0] == "string"); // inline'd type check + }; + + var orphan = function(node){ + // summary: + // function to orphan nodes + var p = node.parentNode; + if(p){ + p.removeChild(node); + } + }; + // FIXME: should we move orphan() to dojo.html? + + dojo.NodeList = function(){ + // summary: + // dojo.NodeList is an of Array subclass which adds syntactic + // sugar for chaining, common iteration operations, animation, and + // node manipulation. NodeLists are most often returned as the + // result of dojo.query() calls. + // description: + // dojo.NodeList instances provide many utilities that reflect + // core Dojo APIs for Array iteration and manipulation, DOM + // manipulation, and event handling. Instead of needing to dig up + // functions in the dojo.* namespace, NodeLists generally make the + // full power of Dojo available for DOM manipulation tasks in a + // simple, chainable way. + // example: + // create a node list from a node + // | new dojo.NodeList(dojo.byId("foo")); + // example: + // get a NodeList from a CSS query and iterate on it + // | var l = dojo.query(".thinger"); + // | l.forEach(function(node, index, nodeList){ + // | console.log(index, node.innerHTML); + // | }); + // example: + // use native and Dojo-provided array methods to manipulate a + // NodeList without needing to use dojo.* functions explicitly: + // | var l = dojo.query(".thinger"); + // | // since NodeLists are real arrays, they have a length + // | // property that is both readable and writable and + // | // push/pop/shift/unshift methods + // | console.log(l.length); + // | l.push(dojo.create("span")); + // | + // | // dojo's normalized array methods work too: + // | console.log( l.indexOf(dojo.byId("foo")) ); + // | // ...including the special "function as string" shorthand + // | console.log( l.every("item.nodeType == 1") ); + // | + // | // NodeLists can be [..] indexed, or you can use the at() + // | // function to get specific items wrapped in a new NodeList: + // | var node = l[3]; // the 4th element + // | var newList = l.at(1, 3); // the 2nd and 4th elements + // example: + // the style functions you expect are all there too: + // | // style() as a getter... + // | var borders = dojo.query(".thinger").style("border"); + // | // ...and as a setter: + // | dojo.query(".thinger").style("border", "1px solid black"); + // | // class manipulation + // | dojo.query("li:nth-child(even)").addClass("even"); + // | // even getting the coordinates of all the items + // | var coords = dojo.query(".thinger").coords(); + // example: + // DOM manipulation functions from the dojo.* namespace area also + // available: + // | // remove all of the elements in the list from their + // | // parents (akin to "deleting" them from the document) + // | dojo.query(".thinger").orphan(); + // | // place all elements in the list at the front of #foo + // | dojo.query(".thinger").place("foo", "first"); + // example: + // Event handling couldn't be easier. `dojo.connect` is mapped in, + // and shortcut handlers are provided for most DOM events: + // | // like dojo.connect(), but with implicit scope + // | dojo.query("li").connect("onclick", console, "log"); + // | + // | // many common event handlers are already available directly: + // | dojo.query("li").onclick(console, "log"); + // | var toggleHovered = dojo.hitch(dojo, "toggleClass", "hovered"); + // | dojo.query("p") + // | .onmouseenter(toggleHovered) + // | .onmouseleave(toggleHovered); + // example: + // chainability is a key advantage of NodeLists: + // | dojo.query(".thinger") + // | .onclick(function(e){ /* ... */ }) + // | .at(1, 3, 8) // get a subset + // | .style("padding", "5px") + // | .forEach(console.log); + + return tnl(Array.apply(null, arguments)); + }; + + //Allow things that new up a NodeList to use a delegated or alternate NodeList implementation. + d._NodeListCtor = d.NodeList; + + var nl = d.NodeList, nlp = nl.prototype; + + // expose adapters and the wrapper as private functions + + nl._wrap = nlp._wrap = tnl; + nl._adaptAsMap = adaptAsMap; + nl._adaptAsForEach = adaptAsForEach; + nl._adaptAsFilter = adaptAsFilter; + nl._adaptWithCondition = adaptWithCondition; + + // mass assignment + + // add array redirectors + d.forEach(["slice", "splice"], function(name){ + var f = ap[name]; + //Use a copy of the this array via this.slice() to allow .end() to work right in the splice case. + // CANNOT apply ._stash()/end() to splice since it currently modifies + // the existing this array -- it would break backward compatibility if we copy the array before + // the splice so that we can use .end(). So only doing the stash option to this._wrap for slice. + nlp[name] = function(){ return this._wrap(f.apply(this, arguments), name == "slice" ? this : null); }; + }); + // concat should be here but some browsers with native NodeList have problems with it + + // add array.js redirectors + d.forEach(["indexOf", "lastIndexOf", "every", "some"], function(name){ + var f = d[name]; + nlp[name] = function(){ return f.apply(d, [this].concat(aps.call(arguments, 0))); }; + }); + + // add conditional methods + d.forEach(["attr", "style"], function(name){ + nlp[name] = adaptWithCondition(d[name], magicGuard); + }); + + // add forEach actions + d.forEach(["connect", "addClass", "removeClass", "toggleClass", "empty", "removeAttr"], function(name){ + nlp[name] = adaptAsForEach(d[name]); + }); + + dojo.extend(dojo.NodeList, { + _normalize: function(/*String||Element||Object||NodeList*/content, /*DOMNode?*/refNode){ + // summary: + // normalizes data to an array of items to insert. + // description: + // If content is an object, it can have special properties "template" and + // "parse". If "template" is defined, then the template value is run through + // dojo.string.substitute (if dojo.string.substitute has been dojo.required elsewhere), + // or if templateFunc is a function on the content, that function will be used to + // transform the template into a final string to be used for for passing to dojo._toDom. + // If content.parse is true, then it is remembered for later, for when the content + // nodes are inserted into the DOM. At that point, the nodes will be parsed for widgets + // (if dojo.parser has been dojo.required elsewhere). + + //Wanted to just use a DocumentFragment, but for the array/NodeList + //case that meant using cloneNode, but we may not want that. + //Cloning should only happen if the node operations span + //multiple refNodes. Also, need a real array, not a NodeList from the + //DOM since the node movements could change those NodeLists. + + var parse = content.parse === true ? true : false; + + //Do we have an object that needs to be run through a template? + if(typeof content.template == "string"){ + var templateFunc = content.templateFunc || (dojo.string && dojo.string.substitute); + content = templateFunc ? templateFunc(content.template, content) : content; + } + + var type = (typeof content); + if(type == "string" || type == "number"){ + content = dojo._toDom(content, (refNode && refNode.ownerDocument)); + if(content.nodeType == 11){ + //DocumentFragment. It cannot handle cloneNode calls, so pull out the children. + content = dojo._toArray(content.childNodes); + }else{ + content = [content]; + } + }else if(!dojo.isArrayLike(content)){ + content = [content]; + }else if(!dojo.isArray(content)){ + //To get to this point, content is array-like, but + //not an array, which likely means a DOM NodeList. Convert it now. + content = dojo._toArray(content); + } + + //Pass around the parse info + if(parse){ + content._runParse = true; + } + return content; //Array + }, + + _cloneNode: function(/*DOMNode*/ node){ + // summary: + // private utiltity to clone a node. Not very interesting in the vanilla + // dojo.NodeList case, but delegates could do interesting things like + // clone event handlers if that is derivable from the node. + return node.cloneNode(true); + }, + + _place: function(/*Array*/ary, /*DOMNode*/refNode, /*String*/position, /*Boolean*/useClone){ + // summary: + // private utility to handle placing an array of nodes relative to another node. + // description: + // Allows for cloning the nodes in the array, and for + // optionally parsing widgets, if ary._runParse is true. + + //Avoid a disallowed operation if trying to do an innerHTML on a non-element node. + if(refNode.nodeType != 1 && position == "only"){ + return; + } + var rNode = refNode, tempNode; + + //Always cycle backwards in case the array is really a + //DOM NodeList and the DOM operations take it out of the live collection. + var length = ary.length; + for(var i = length - 1; i >= 0; i--){ + var node = (useClone ? this._cloneNode(ary[i]) : ary[i]); + + //If need widget parsing, use a temp node, instead of waiting after inserting into + //real DOM because we need to start widget parsing at one node up from current node, + //which could cause some already parsed widgets to be parsed again. + if(ary._runParse && dojo.parser && dojo.parser.parse){ + if(!tempNode){ + tempNode = rNode.ownerDocument.createElement("div"); + } + tempNode.appendChild(node); + dojo.parser.parse(tempNode); + node = tempNode.firstChild; + while(tempNode.firstChild){ + tempNode.removeChild(tempNode.firstChild); + } + } + + if(i == length - 1){ + dojo.place(node, rNode, position); + }else{ + rNode.parentNode.insertBefore(node, rNode); + } + rNode = node; + } + }, + + _stash: function(parent){ + // summary: + // private function to hold to a parent NodeList. end() to return the parent NodeList. + // + // example: + // How to make a `dojo.NodeList` method that only returns the third node in + // the dojo.NodeList but allows access to the original NodeList by using this._stash: + // | dojo.extend(dojo.NodeList, { + // | third: function(){ + // | var newNodeList = dojo.NodeList(this[2]); + // | return newNodeList._stash(this); + // | } + // | }); + // | // then see how _stash applies a sub-list, to be .end()'ed out of + // | dojo.query(".foo") + // | .third() + // | .addClass("thirdFoo") + // | .end() + // | // access to the orig .foo list + // | .removeClass("foo") + // | + // + this._parent = parent; + return this; //dojo.NodeList + }, + + end: function(){ + // summary: + // Ends use of the current `dojo.NodeList` by returning the previous dojo.NodeList + // that generated the current dojo.NodeList. + // description: + // Returns the `dojo.NodeList` that generated the current `dojo.NodeList`. If there + // is no parent dojo.NodeList, an empty dojo.NodeList is returned. + // example: + // | dojo.query("a") + // | .filter(".disabled") + // | // operate on the anchors that only have a disabled class + // | .style("color", "grey") + // | .end() + // | // jump back to the list of anchors + // | .style(...) + // + if(this._parent){ + return this._parent; + }else{ + //Just return empy list. + return new this._NodeListCtor(); + } + }, + + // http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array#Methods + + // FIXME: handle return values for #3244 + // http://trac.dojotoolkit.org/ticket/3244 + + // FIXME: + // need to wrap or implement: + // join (perhaps w/ innerHTML/outerHTML overload for toString() of items?) + // reduce + // reduceRight + + /*===== + slice: function(begin, end){ + // summary: + // Returns a new NodeList, maintaining this one in place + // description: + // This method behaves exactly like the Array.slice method + // with the caveat that it returns a dojo.NodeList and not a + // raw Array. For more details, see Mozilla's (slice + // documentation)[http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:slice] + // begin: Integer + // Can be a positive or negative integer, with positive + // integers noting the offset to begin at, and negative + // integers denoting an offset from the end (i.e., to the left + // of the end) + // end: Integer? + // Optional parameter to describe what position relative to + // the NodeList's zero index to end the slice at. Like begin, + // can be positive or negative. + return this._wrap(a.slice.apply(this, arguments)); + }, + + splice: function(index, howmany, item){ + // summary: + // Returns a new NodeList, manipulating this NodeList based on + // the arguments passed, potentially splicing in new elements + // at an offset, optionally deleting elements + // description: + // This method behaves exactly like the Array.splice method + // with the caveat that it returns a dojo.NodeList and not a + // raw Array. For more details, see Mozilla's (splice + // documentation)[http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:splice] + // For backwards compatibility, calling .end() on the spliced NodeList + // does not return the original NodeList -- splice alters the NodeList in place. + // index: Integer + // begin can be a positive or negative integer, with positive + // integers noting the offset to begin at, and negative + // integers denoting an offset from the end (i.e., to the left + // of the end) + // howmany: Integer? + // Optional parameter to describe what position relative to + // the NodeList's zero index to end the slice at. Like begin, + // can be positive or negative. + // item: Object...? + // Any number of optional parameters may be passed in to be + // spliced into the NodeList + // returns: + // dojo.NodeList + return this._wrap(a.splice.apply(this, arguments)); + }, + + indexOf: function(value, fromIndex){ + // summary: + // see dojo.indexOf(). The primary difference is that the acted-on + // array is implicitly this NodeList + // value: Object: + // The value to search for. + // fromIndex: Integer?: + // The loction to start searching from. Optional. Defaults to 0. + // description: + // For more details on the behavior of indexOf, see Mozilla's + // (indexOf + // docs)[http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:indexOf] + // returns: + // Positive Integer or 0 for a match, -1 of not found. + return d.indexOf(this, value, fromIndex); // Integer + }, + + lastIndexOf: function(value, fromIndex){ + // summary: + // see dojo.lastIndexOf(). The primary difference is that the + // acted-on array is implicitly this NodeList + // description: + // For more details on the behavior of lastIndexOf, see + // Mozilla's (lastIndexOf + // docs)[http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:lastIndexOf] + // value: Object + // The value to search for. + // fromIndex: Integer? + // The loction to start searching from. Optional. Defaults to 0. + // returns: + // Positive Integer or 0 for a match, -1 of not found. + return d.lastIndexOf(this, value, fromIndex); // Integer + }, + + every: function(callback, thisObject){ + // summary: + // see `dojo.every()` and the (Array.every + // docs)[http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:every]. + // Takes the same structure of arguments and returns as + // dojo.every() with the caveat that the passed array is + // implicitly this NodeList + // callback: Function: the callback + // thisObject: Object?: the context + return d.every(this, callback, thisObject); // Boolean + }, + + some: function(callback, thisObject){ + // summary: + // Takes the same structure of arguments and returns as + // `dojo.some()` with the caveat that the passed array is + // implicitly this NodeList. See `dojo.some()` and Mozilla's + // (Array.some + // documentation)[http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:some]. + // callback: Function: the callback + // thisObject: Object?: the context + return d.some(this, callback, thisObject); // Boolean + }, + =====*/ + + concat: function(item){ + // summary: + // Returns a new NodeList comprised of items in this NodeList + // as well as items passed in as parameters + // description: + // This method behaves exactly like the Array.concat method + // with the caveat that it returns a `dojo.NodeList` and not a + // raw Array. For more details, see the (Array.concat + // docs)[http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:concat] + // item: Object? + // Any number of optional parameters may be passed in to be + // spliced into the NodeList + // returns: + // dojo.NodeList + + //return this._wrap(apc.apply(this, arguments)); + // the line above won't work for the native NodeList :-( + + // implementation notes: + // 1) Native NodeList is not an array, and cannot be used directly + // in concat() --- the latter doesn't recognize it as an array, and + // does not inline it, but append as a single entity. + // 2) On some browsers (e.g., Safari) the "constructor" property is + // read-only and cannot be changed. So we have to test for both + // native NodeList and dojo.NodeList in this property to recognize + // the node list. + + var t = d.isArray(this) ? this : aps.call(this, 0), + m = d.map(arguments, function(a){ + return a && !d.isArray(a) && + (typeof NodeList != "undefined" && a.constructor === NodeList || a.constructor === this._NodeListCtor) ? + aps.call(a, 0) : a; + }); + return this._wrap(apc.apply(t, m), this); // dojo.NodeList + }, + + map: function(/*Function*/ func, /*Function?*/ obj){ + // summary: + // see dojo.map(). The primary difference is that the acted-on + // array is implicitly this NodeList and the return is a + // dojo.NodeList (a subclass of Array) + ///return d.map(this, func, obj, d.NodeList); // dojo.NodeList + return this._wrap(d.map(this, func, obj), this); // dojo.NodeList + }, + + forEach: function(callback, thisObj){ + // summary: + // see `dojo.forEach()`. The primary difference is that the acted-on + // array is implicitly this NodeList. If you want the option to break out + // of the forEach loop, use every() or some() instead. + d.forEach(this, callback, thisObj); + // non-standard return to allow easier chaining + return this; // dojo.NodeList + }, + + /*===== + coords: function(){ + // summary: + // Returns the box objects of all elements in a node list as + // an Array (*not* a NodeList). Acts like `dojo.coords`, though assumes + // the node passed is each node in this list. + + return d.map(this, d.coords); // Array + }, + + position: function(){ + // summary: + // Returns border-box objects (x/y/w/h) of all elements in a node list + // as an Array (*not* a NodeList). Acts like `dojo.position`, though + // assumes the node passed is each node in this list. + + return d.map(this, d.position); // Array + }, + + attr: function(property, value){ + // summary: + // gets or sets the DOM attribute for every element in the + // NodeList. See also `dojo.attr` + // property: String + // the attribute to get/set + // value: String? + // optional. The value to set the property to + // returns: + // if no value is passed, the result is an array of attribute values + // If a value is passed, the return is this NodeList + // example: + // Make all nodes with a particular class focusable: + // | dojo.query(".focusable").attr("tabIndex", -1); + // example: + // Disable a group of buttons: + // | dojo.query("button.group").attr("disabled", true); + // example: + // innerHTML can be assigned or retreived as well: + // | // get the innerHTML (as an array) for each list item + // | var ih = dojo.query("li.replaceable").attr("innerHTML"); + return; // dojo.NodeList + return; // Array + }, + + style: function(property, value){ + // summary: + // gets or sets the CSS property for every element in the NodeList + // property: String + // the CSS property to get/set, in JavaScript notation + // ("lineHieght" instead of "line-height") + // value: String? + // optional. The value to set the property to + // returns: + // if no value is passed, the result is an array of strings. + // If a value is passed, the return is this NodeList + return; // dojo.NodeList + return; // Array + }, + + addClass: function(className){ + // summary: + // adds the specified class to every node in the list + // className: String|Array + // A String class name to add, or several space-separated class names, + // or an array of class names. + return; // dojo.NodeList + }, + + removeClass: function(className){ + // summary: + // removes the specified class from every node in the list + // className: String|Array? + // An optional String class name to remove, or several space-separated + // class names, or an array of class names. If omitted, all class names + // will be deleted. + // returns: + // dojo.NodeList, this list + return; // dojo.NodeList + }, + + toggleClass: function(className, condition){ + // summary: + // Adds a class to node if not present, or removes if present. + // Pass a boolean condition if you want to explicitly add or remove. + // condition: Boolean? + // If passed, true means to add the class, false means to remove. + // className: String + // the CSS class to add + return; // dojo.NodeList + }, + + connect: function(methodName, objOrFunc, funcName){ + // summary: + // attach event handlers to every item of the NodeList. Uses dojo.connect() + // so event properties are normalized + // methodName: String + // the name of the method to attach to. For DOM events, this should be + // the lower-case name of the event + // objOrFunc: Object|Function|String + // if 2 arguments are passed (methodName, objOrFunc), objOrFunc should + // reference a function or be the name of the function in the global + // namespace to attach. If 3 arguments are provided + // (methodName, objOrFunc, funcName), objOrFunc must be the scope to + // locate the bound function in + // funcName: String? + // optional. A string naming the function in objOrFunc to bind to the + // event. May also be a function reference. + // example: + // add an onclick handler to every button on the page + // | dojo.query("div:nth-child(odd)").connect("onclick", function(e){ + // | console.log("clicked!"); + // | }); + // example: + // attach foo.bar() to every odd div's onmouseover + // | dojo.query("div:nth-child(odd)").connect("onmouseover", foo, "bar"); + }, + + empty: function(){ + // summary: + // clears all content from each node in the list. Effectively + // equivalent to removing all child nodes from every item in + // the list. + return this.forEach("item.innerHTML='';"); // dojo.NodeList + // FIXME: should we be checking for and/or disposing of widgets below these nodes? + }, + =====*/ + + // useful html methods + coords: adaptAsMap(d.coords), + position: adaptAsMap(d.position), + + // FIXME: connectPublisher()? connectRunOnce()? + + /* + destroy: function(){ + // summary: + // destroys every item in the list. + this.forEach(d.destroy); + // FIXME: should we be checking for and/or disposing of widgets below these nodes? + }, + */ + + place: function(/*String||Node*/ queryOrNode, /*String*/ position){ + // summary: + // places elements of this node list relative to the first element matched + // by queryOrNode. Returns the original NodeList. See: `dojo.place` + // queryOrNode: + // may be a string representing any valid CSS3 selector or a DOM node. + // In the selector case, only the first matching element will be used + // for relative positioning. + // position: + // can be one of: + // | "last" (default) + // | "first" + // | "before" + // | "after" + // | "only" + // | "replace" + // or an offset in the childNodes property + var item = d.query(queryOrNode)[0]; + return this.forEach(function(node){ d.place(node, item, position); }); // dojo.NodeList + }, + + orphan: function(/*String?*/ simpleFilter){ + // summary: + // removes elements in this list that match the simple filter + // from their parents and returns them as a new NodeList. + // simpleFilter: + // single-expression CSS rule. For example, ".thinger" or + // "#someId[attrName='value']" but not "div > span". In short, + // anything which does not invoke a descent to evaluate but + // can instead be used to test a single node is acceptable. + // returns: + // `dojo.NodeList` containing the orpahned elements + return (simpleFilter ? d._filterQueryResult(this, simpleFilter) : this).forEach(orphan); // dojo.NodeList + }, + + adopt: function(/*String||Array||DomNode*/ queryOrListOrNode, /*String?*/ position){ + // summary: + // places any/all elements in queryOrListOrNode at a + // position relative to the first element in this list. + // Returns a dojo.NodeList of the adopted elements. + // queryOrListOrNode: + // a DOM node or a query string or a query result. + // Represents the nodes to be adopted relative to the + // first element of this NodeList. + // position: + // can be one of: + // | "last" (default) + // | "first" + // | "before" + // | "after" + // | "only" + // | "replace" + // or an offset in the childNodes property + return d.query(queryOrListOrNode).place(this[0], position)._stash(this); // dojo.NodeList + }, + + // FIXME: do we need this? + query: function(/*String*/ queryStr){ + // summary: + // Returns a new list whose memebers match the passed query, + // assuming elements of the current NodeList as the root for + // each search. + // example: + // assume a DOM created by this markup: + // | <div id="foo"> + // | <p> + // | bacon is tasty, <span>dontcha think?</span> + // | </p> + // | </div> + // | <div id="bar"> + // | <p>great commedians may not be funny <span>in person</span></p> + // | </div> + // If we are presented with the following defintion for a NodeList: + // | var l = new dojo.NodeList(dojo.byId("foo"), dojo.byId("bar")); + // it's possible to find all span elements under paragraphs + // contained by these elements with this sub-query: + // | var spans = l.query("p span"); + + // FIXME: probably slow + if(!queryStr){ return this; } + var ret = this.map(function(node){ + // FIXME: why would we ever get undefined here? + return d.query(queryStr, node).filter(function(subNode){ return subNode !== undefined; }); + }); + return this._wrap(apc.apply([], ret), this); // dojo.NodeList + }, + + filter: function(/*String|Function*/ simpleFilter){ + // summary: + // "masks" the built-in javascript filter() method (supported + // in Dojo via `dojo.filter`) to support passing a simple + // string filter in addition to supporting filtering function + // objects. + // simpleFilter: + // If a string, a single-expression CSS rule. For example, + // ".thinger" or "#someId[attrName='value']" but not "div > + // span". In short, anything which does not invoke a descent + // to evaluate but can instead be used to test a single node + // is acceptable. + // example: + // "regular" JS filter syntax as exposed in dojo.filter: + // | dojo.query("*").filter(function(item){ + // | // highlight every paragraph + // | return (item.nodeName == "p"); + // | }).style("backgroundColor", "yellow"); + // example: + // the same filtering using a CSS selector + // | dojo.query("*").filter("p").styles("backgroundColor", "yellow"); + + var a = arguments, items = this, start = 0; + if(typeof simpleFilter == "string"){ // inline'd type check + items = d._filterQueryResult(this, a[0]); + if(a.length == 1){ + // if we only got a string query, pass back the filtered results + return items._stash(this); // dojo.NodeList + } + // if we got a callback, run it over the filtered items + start = 1; + } + return this._wrap(d.filter(items, a[start], a[start + 1]), this); // dojo.NodeList + }, + + /* + // FIXME: should this be "copyTo" and include parenting info? + clone: function(){ + // summary: + // creates node clones of each element of this list + // and returns a new list containing the clones + }, + */ + + addContent: function(/*String||DomNode||Object||dojo.NodeList*/ content, /*String||Integer?*/ position){ + // summary: + // add a node, NodeList or some HTML as a string to every item in the + // list. Returns the original list. + // description: + // a copy of the HTML content is added to each item in the + // list, with an optional position argument. If no position + // argument is provided, the content is appended to the end of + // each item. + // content: + // DOM node, HTML in string format, a NodeList or an Object. If a DOM node or + // NodeList, the content will be cloned if the current NodeList has more than one + // element. Only the DOM nodes are cloned, no event handlers. If it is an Object, + // it should be an object with at "template" String property that has the HTML string + // to insert. If dojo.string has already been dojo.required, then dojo.string.substitute + // will be used on the "template" to generate the final HTML string. Other allowed + // properties on the object are: "parse" if the HTML + // string should be parsed for widgets (dojo.require("dojo.parser") to get that + // option to work), and "templateFunc" if a template function besides dojo.string.substitute + // should be used to transform the "template". + // position: + // can be one of: + // | "last"||"end" (default) + // | "first||"start" + // | "before" + // | "after" + // | "replace" (replaces nodes in this NodeList with new content) + // | "only" (removes other children of the nodes so new content is hte only child) + // or an offset in the childNodes property + // example: + // appends content to the end if the position is ommitted + // | dojo.query("h3 > p").addContent("hey there!"); + // example: + // add something to the front of each element that has a + // "thinger" property: + // | dojo.query("[thinger]").addContent("...", "first"); + // example: + // adds a header before each element of the list + // | dojo.query(".note").addContent("<h4>NOTE:</h4>", "before"); + // example: + // add a clone of a DOM node to the end of every element in + // the list, removing it from its existing parent. + // | dojo.query(".note").addContent(dojo.byId("foo")); + // example: + // Append nodes from a templatized string. + // dojo.require("dojo.string"); + // dojo.query(".note").addContent({ + // template: '<b>${id}: </b><span>${name}</span>', + // id: "user332", + // name: "Mr. Anderson" + // }); + // example: + // Append nodes from a templatized string that also has widgets parsed. + // dojo.require("dojo.string"); + // dojo.require("dojo.parser"); + // var notes = dojo.query(".note").addContent({ + // template: '<button dojoType="dijit.form.Button">${text}</button>', + // parse: true, + // text: "Send" + // }); + content = this._normalize(content, this[0]); + for(var i = 0, node; node = this[i]; i++){ + this._place(content, node, position, i > 0); + } + return this; //dojo.NodeList + }, + + instantiate: function(/*String|Object*/ declaredClass, /*Object?*/ properties){ + // summary: + // Create a new instance of a specified class, using the + // specified properties and each node in the nodeList as a + // srcNodeRef. + // example: + // Grabs all buttons in the page and converts them to diji.form.Buttons. + // | var buttons = dojo.query("button").instantiate("dijit.form.Button", {showLabel: true}); + var c = d.isFunction(declaredClass) ? declaredClass : d.getObject(declaredClass); + properties = properties || {}; + return this.forEach(function(node){ + new c(properties, node); + }); // dojo.NodeList + }, + + at: function(/*===== index =====*/){ + // summary: + // Returns a new NodeList comprised of items in this NodeList + // at the given index or indices. + // + // index: Integer... + // One or more 0-based indices of items in the current + // NodeList. A negative index will start at the end of the + // list and go backwards. + // + // example: + // Shorten the list to the first, second, and third elements + // | dojo.query("a").at(0, 1, 2).forEach(fn); + // + // example: + // Retrieve the first and last elements of a unordered list: + // | dojo.query("ul > li").at(0, -1).forEach(cb); + // + // example: + // Do something for the first element only, but end() out back to + // the original list and continue chaining: + // | dojo.query("a").at(0).onclick(fn).end().forEach(function(n){ + // | console.log(n); // all anchors on the page. + // | }) + // + // returns: + // dojo.NodeList + var t = new this._NodeListCtor(); + d.forEach(arguments, function(i){ + if(i < 0){ i = this.length + i } + if(this[i]){ t.push(this[i]); } + }, this); + return t._stash(this); // dojo.NodeList + } + + }); + + nl.events = [ + // summary: list of all DOM events used in NodeList + "blur", "focus", "change", "click", "error", "keydown", "keypress", + "keyup", "load", "mousedown", "mouseenter", "mouseleave", "mousemove", + "mouseout", "mouseover", "mouseup", "submit" + ]; + + // FIXME: pseudo-doc the above automatically generated on-event functions + + // syntactic sugar for DOM events + d.forEach(nl.events, function(evt){ + var _oe = "on" + evt; + nlp[_oe] = function(a, b){ + return this.connect(_oe, a, b); + } + // FIXME: should these events trigger publishes? + /* + return (a ? this.connect(_oe, a, b) : + this.forEach(function(n){ + // FIXME: + // listeners get buried by + // addEventListener and can't be dug back + // out to be triggered externally. + // see: + // http://developer.mozilla.org/en/docs/DOM:element + + console.log(n, evt, _oe); + + // FIXME: need synthetic event support! + var _e = { target: n, faux: true, type: evt }; + // dojo._event_listener._synthesizeEvent({}, { target: n, faux: true, type: evt }); + try{ n[evt](_e); }catch(e){ console.log(e); } + try{ n[_oe](_e); }catch(e){ console.log(e); } + }) + ); + */ + } + ); + })(); + } diff --git a/lib/dojo/_base/_loader/bootstrap.js b/lib/dojo/_base/_loader/bootstrap.js index 7cc168e5d..3ef3012a2 100644 --- a/lib/dojo/_base/_loader/bootstrap.js +++ b/lib/dojo/_base/_loader/bootstrap.js @@ -5,116 +5,500 @@ */ -(function(){ -if(typeof this["loadFirebugConsole"]=="function"){ -this["loadFirebugConsole"](); -}else{ -this.console=this.console||{}; -var cn=["assert","count","debug","dir","dirxml","error","group","groupEnd","info","profile","profileEnd","time","timeEnd","trace","warn","log"]; -var i=0,tn; -while((tn=cn[i++])){ -if(!console[tn]){ -(function(){ -var _1=tn+""; -console[_1]=("log" in console)?function(){ -var a=Array.apply({},arguments); -a.unshift(_1+":"); -console["log"](a.join(" ")); -}:function(){ -}; -console[_1]._fake=true; -})(); -} -} -} -if(typeof dojo=="undefined"){ -dojo={_scopeName:"dojo",_scopePrefix:"",_scopePrefixArgs:"",_scopeSuffix:"",_scopeMap:{},_scopeMapRev:{}}; -} -var d=dojo; -if(typeof dijit=="undefined"){ -dijit={_scopeName:"dijit"}; -} -if(typeof dojox=="undefined"){ -dojox={_scopeName:"dojox"}; -} -if(!d._scopeArgs){ -d._scopeArgs=[dojo,dijit,dojox]; -} -d.global=this; -d.config={isDebug:false,debugAtAllCosts:false}; -if(typeof djConfig!="undefined"){ -for(var _2 in djConfig){ -d.config[_2]=djConfig[_2]; -} -} -dojo.locale=d.config.locale; -var _3="$Rev: 22487 $".match(/\d+/); -dojo.version={major:1,minor:5,patch:0,flag:"",revision:_3?+_3[0]:NaN,toString:function(){ -with(d.version){ -return major+"."+minor+"."+patch+flag+" ("+revision+")"; -} -}}; -if(typeof OpenAjax!="undefined"){ -OpenAjax.hub.registerLibrary(dojo._scopeName,"http://dojotoolkit.org",d.version.toString()); -} -var _4,_5,_6={}; -for(var i in {toString:1}){ -_4=[]; -break; -} -dojo._extraNames=_4=_4||["hasOwnProperty","valueOf","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","constructor"]; -_5=_4.length; -dojo._mixin=function(_7,_8){ -var _9,s,i; -for(_9 in _8){ -s=_8[_9]; -if(!(_9 in _7)||(_7[_9]!==s&&(!(_9 in _6)||_6[_9]!==s))){ -_7[_9]=s; -} -} -if(_5&&_8){ -for(i=0;i<_5;++i){ -_9=_4[i]; -s=_8[_9]; -if(!(_9 in _7)||(_7[_9]!==s&&(!(_9 in _6)||_6[_9]!==s))){ -_7[_9]=s; -} -} -} -return _7; -}; -dojo.mixin=function(_a,_b){ -if(!_a){ -_a={}; -} -for(var i=1,l=arguments.length;i<l;i++){ -d._mixin(_a,arguments[i]); -} -return _a; -}; -dojo._getProp=function(_c,_d,_e){ -var _f=_e||d.global; -for(var i=0,p;_f&&(p=_c[i]);i++){ -if(i==0&&d._scopeMap[p]){ -p=d._scopeMap[p]; +/*===== +// note: +// 'djConfig' does not exist under 'dojo.*' so that it can be set before the +// 'dojo' variable exists. +// note: +// Setting any of these variables *after* the library has loaded does +// nothing at all. + +djConfig = { + // summary: + // Application code can set the global 'djConfig' prior to loading + // the library to override certain global settings for how dojo works. + // + // isDebug: Boolean + // Defaults to `false`. If set to `true`, ensures that Dojo provides + // extended debugging feedback via Firebug. If Firebug is not available + // on your platform, setting `isDebug` to `true` will force Dojo to + // pull in (and display) the version of Firebug Lite which is + // integrated into the Dojo distribution, thereby always providing a + // debugging/logging console when `isDebug` is enabled. Note that + // Firebug's `console.*` methods are ALWAYS defined by Dojo. If + // `isDebug` is false and you are on a platform without Firebug, these + // methods will be defined as no-ops. + isDebug: false, + // debugAtAllCosts: Boolean + // Defaults to `false`. If set to `true`, this triggers an alternate + // mode of the package system in which dependencies are detected and + // only then are resources evaluated in dependency order via + // `<script>` tag inclusion. This may double-request resources and + // cause problems with scripts which expect `dojo.require()` to + // preform synchronously. `debugAtAllCosts` can be an invaluable + // debugging aid, but when using it, ensure that all code which + // depends on Dojo modules is wrapped in `dojo.addOnLoad()` handlers. + // Due to the somewhat unpredictable side-effects of using + // `debugAtAllCosts`, it is strongly recommended that you enable this + // flag as a last resort. `debugAtAllCosts` has no effect when loading + // resources across domains. For usage information, see the + // [Dojo Book](http://dojotoolkit.org/book/book-dojo/part-4-meta-dojo-making-your-dojo-code-run-faster-and-better/debugging-facilities/deb) + debugAtAllCosts: false, + // locale: String + // The locale to assume for loading localized resources in this page, + // specified according to [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt). + // Must be specified entirely in lowercase, e.g. `en-us` and `zh-cn`. + // See the documentation for `dojo.i18n` and `dojo.requireLocalization` + // for details on loading localized resources. If no locale is specified, + // Dojo assumes the locale of the user agent, according to `navigator.userLanguage` + // or `navigator.language` properties. + locale: undefined, + // extraLocale: Array + // No default value. Specifies additional locales whose + // resources should also be loaded alongside the default locale when + // calls to `dojo.requireLocalization()` are processed. + extraLocale: undefined, + // baseUrl: String + // The directory in which `dojo.js` is located. Under normal + // conditions, Dojo auto-detects the correct location from which it + // was loaded. You may need to manually configure `baseUrl` in cases + // where you have renamed `dojo.js` or in which `<base>` tags confuse + // some browsers (e.g. IE 6). The variable `dojo.baseUrl` is assigned + // either the value of `djConfig.baseUrl` if one is provided or the + // auto-detected root if not. Other modules are located relative to + // this path. The path should end in a slash. + baseUrl: undefined, + // modulePaths: Object + // A map of module names to paths relative to `dojo.baseUrl`. The + // key/value pairs correspond directly to the arguments which + // `dojo.registerModulePath` accepts. Specifiying + // `djConfig.modulePaths = { "foo": "../../bar" }` is the equivalent + // of calling `dojo.registerModulePath("foo", "../../bar");`. Multiple + // modules may be configured via `djConfig.modulePaths`. + modulePaths: {}, + // afterOnLoad: Boolean + // Indicates Dojo was added to the page after the page load. In this case + // Dojo will not wait for the page DOMContentLoad/load events and fire + // its dojo.addOnLoad callbacks after making sure all outstanding + // dojo.required modules have loaded. Only works with a built dojo.js, + // it does not work the dojo.js directly from source control. + afterOnLoad: false, + // addOnLoad: Function or Array + // Adds a callback via dojo.addOnLoad. Useful when Dojo is added after + // the page loads and djConfig.afterOnLoad is true. Supports the same + // arguments as dojo.addOnLoad. When using a function reference, use + // `djConfig.addOnLoad = function(){};`. For object with function name use + // `djConfig.addOnLoad = [myObject, "functionName"];` and for object with + // function reference use + // `djConfig.addOnLoad = [myObject, function(){}];` + addOnLoad: null, + // require: Array + // An array of module names to be loaded immediately after dojo.js has been included + // in a page. + require: [], + // defaultDuration: Array + // Default duration, in milliseconds, for wipe and fade animations within dijits. + // Assigned to dijit.defaultDuration. + defaultDuration: 200, + // dojoBlankHtmlUrl: String + // Used by some modules to configure an empty iframe. Used by dojo.io.iframe and + // dojo.back, and dijit popup support in IE where an iframe is needed to make sure native + // controls do not bleed through the popups. Normally this configuration variable + // does not need to be set, except when using cross-domain/CDN Dojo builds. + // Save dojo/resources/blank.html to your domain and set `djConfig.dojoBlankHtmlUrl` + // to the path on your domain your copy of blank.html. + dojoBlankHtmlUrl: undefined, + // ioPublish: Boolean? + // Set this to true to enable publishing of topics for the different phases of + // IO operations. Publishing is done via dojo.publish. See dojo.__IoPublish for a list + // of topics that are published. + ioPublish: false, + // useCustomLogger: Anything? + // If set to a value that evaluates to true such as a string or array and + // isDebug is true and Firebug is not available or running, then it bypasses + // the creation of Firebug Lite allowing you to define your own console object. + useCustomLogger: undefined, + // transparentColor: Array + // Array containing the r, g, b components used as transparent color in dojo.Color; + // if undefined, [255,255,255] (white) will be used. + transparentColor: undefined, + // skipIeDomLoaded: Boolean + // For IE only, skip the DOMContentLoaded hack used. Sometimes it can cause an Operation + // Aborted error if the rest of the page triggers script defers before the DOM is ready. + // If this is config value is set to true, then dojo.addOnLoad callbacks will not be + // triggered until the page load event, which is after images and iframes load. If you + // want to trigger the callbacks sooner, you can put a script block in the bottom of + // your HTML that calls dojo._loadInit();. If you are using multiversion support, change + // "dojo." to the appropriate scope name for dojo. + skipIeDomLoaded: false } -_f=(p in _f?_f[p]:(_d?_f[p]={}:undefined)); +=====*/ + +(function(){ + // firebug stubs + + if(typeof this["loadFirebugConsole"] == "function"){ + // for Firebug 1.2 + this["loadFirebugConsole"](); + }else{ + this.console = this.console || {}; + + // Be careful to leave 'log' always at the end + var cn = [ + "assert", "count", "debug", "dir", "dirxml", "error", "group", + "groupEnd", "info", "profile", "profileEnd", "time", "timeEnd", + "trace", "warn", "log" + ]; + var i=0, tn; + while((tn=cn[i++])){ + if(!console[tn]){ + (function(){ + var tcn = tn+""; + console[tcn] = ('log' in console) ? function(){ + var a = Array.apply({}, arguments); + a.unshift(tcn+":"); + console["log"](a.join(" ")); + } : function(){} + console[tcn]._fake = true; + })(); + } + } + } + + //TODOC: HOW TO DOC THIS? + // dojo is the root variable of (almost all) our public symbols -- make sure it is defined. + if(typeof dojo == "undefined"){ + dojo = { + _scopeName: "dojo", + _scopePrefix: "", + _scopePrefixArgs: "", + _scopeSuffix: "", + _scopeMap: {}, + _scopeMapRev: {} + }; + } + + var d = dojo; + + //Need placeholders for dijit and dojox for scoping code. + if(typeof dijit == "undefined"){ + dijit = {_scopeName: "dijit"}; + } + if(typeof dojox == "undefined"){ + dojox = {_scopeName: "dojox"}; + } + + if(!d._scopeArgs){ + d._scopeArgs = [dojo, dijit, dojox]; + } + +/*===== +dojo.global = { + // summary: + // Alias for the global scope + // (e.g. the window object in a browser). + // description: + // Refer to 'dojo.global' rather than referring to window to ensure your + // code runs correctly in contexts other than web browsers (e.g. Rhino on a server). } -return _f; -}; -dojo.setObject=function(_10,_11,_12){ -var _13=_10.split("."),p=_13.pop(),obj=d._getProp(_13,true,_12); -return obj&&p?(obj[p]=_11):undefined; -}; -dojo.getObject=function(_14,_15,_16){ -return d._getProp(_14.split("."),_15,_16); -}; -dojo.exists=function(_17,obj){ -return !!d.getObject(_17,false,obj); -}; -dojo["eval"]=function(_18){ -return d.global.eval?d.global.eval(_18):eval(_18); -}; -d.deprecated=d.experimental=function(){ -}; +=====*/ + d.global = this; + + d.config =/*===== djConfig = =====*/{ + isDebug: false, + debugAtAllCosts: false + }; + + if(typeof djConfig != "undefined"){ + for(var opt in djConfig){ + d.config[opt] = djConfig[opt]; + } + } + +/*===== + // Override locale setting, if specified + dojo.locale = { + // summary: the locale as defined by Dojo (read-only) + }; +=====*/ + dojo.locale = d.config.locale; + + var rev = "$Rev: 22487 $".match(/\d+/); + +/*===== + dojo.version = function(){ + // summary: + // Version number of the Dojo Toolkit + // major: Integer + // Major version. If total version is "1.2.0beta1", will be 1 + // minor: Integer + // Minor version. If total version is "1.2.0beta1", will be 2 + // patch: Integer + // Patch version. If total version is "1.2.0beta1", will be 0 + // flag: String + // Descriptor flag. If total version is "1.2.0beta1", will be "beta1" + // revision: Number + // The SVN rev from which dojo was pulled + this.major = 0; + this.minor = 0; + this.patch = 0; + this.flag = ""; + this.revision = 0; + } +=====*/ + dojo.version = { + major: 1, minor: 5, patch: 0, flag: "", + revision: rev ? +rev[0] : NaN, + toString: function(){ + with(d.version){ + return major + "." + minor + "." + patch + flag + " (" + revision + ")"; // String + } + } + } + + // Register with the OpenAjax hub + if(typeof OpenAjax != "undefined"){ + OpenAjax.hub.registerLibrary(dojo._scopeName, "http://dojotoolkit.org", d.version.toString()); + } + + var extraNames, extraLen, empty = {}; + for(var i in {toString: 1}){ extraNames = []; break; } + dojo._extraNames = extraNames = extraNames || ["hasOwnProperty", "valueOf", "isPrototypeOf", + "propertyIsEnumerable", "toLocaleString", "toString", "constructor"]; + extraLen = extraNames.length; + + dojo._mixin = function(/*Object*/ target, /*Object*/ source){ + // summary: + // Adds all properties and methods of source to target. This addition + // is "prototype extension safe", so that instances of objects + // will not pass along prototype defaults. + var name, s, i; + for(name in source){ + // the "tobj" condition avoid copying properties in "source" + // inherited from Object.prototype. For example, if target has a custom + // toString() method, don't overwrite it with the toString() method + // that source inherited from Object.prototype + s = source[name]; + if(!(name in target) || (target[name] !== s && (!(name in empty) || empty[name] !== s))){ + target[name] = s; + } + } + // IE doesn't recognize some custom functions in for..in + if(extraLen && source){ + for(i = 0; i < extraLen; ++i){ + name = extraNames[i]; + s = source[name]; + if(!(name in target) || (target[name] !== s && (!(name in empty) || empty[name] !== s))){ + target[name] = s; + } + } + } + return target; // Object + } + + dojo.mixin = function(/*Object*/obj, /*Object...*/props){ + // summary: + // Adds all properties and methods of props to obj and returns the + // (now modified) obj. + // description: + // `dojo.mixin` can mix multiple source objects into a + // destination object which is then returned. Unlike regular + // `for...in` iteration, `dojo.mixin` is also smart about avoiding + // extensions which other toolkits may unwisely add to the root + // object prototype + // obj: + // The object to mix properties into. Also the return value. + // props: + // One or more objects whose values are successively copied into + // obj. If more than one of these objects contain the same value, + // the one specified last in the function call will "win". + // example: + // make a shallow copy of an object + // | var copy = dojo.mixin({}, source); + // example: + // many class constructors often take an object which specifies + // values to be configured on the object. In this case, it is + // often simplest to call `dojo.mixin` on the `this` object: + // | dojo.declare("acme.Base", null, { + // | constructor: function(properties){ + // | // property configuration: + // | dojo.mixin(this, properties); + // | + // | console.log(this.quip); + // | // ... + // | }, + // | quip: "I wasn't born yesterday, you know - I've seen movies.", + // | // ... + // | }); + // | + // | // create an instance of the class and configure it + // | var b = new acme.Base({quip: "That's what it does!" }); + // example: + // copy in properties from multiple objects + // | var flattened = dojo.mixin( + // | { + // | name: "Frylock", + // | braces: true + // | }, + // | { + // | name: "Carl Brutanananadilewski" + // | } + // | ); + // | + // | // will print "Carl Brutanananadilewski" + // | console.log(flattened.name); + // | // will print "true" + // | console.log(flattened.braces); + if(!obj){ obj = {}; } + for(var i=1, l=arguments.length; i<l; i++){ + d._mixin(obj, arguments[i]); + } + return obj; // Object + } + + dojo._getProp = function(/*Array*/parts, /*Boolean*/create, /*Object*/context){ + var obj=context || d.global; + for(var i=0, p; obj && (p=parts[i]); i++){ + if(i == 0 && d._scopeMap[p]){ + p = d._scopeMap[p]; + } + obj = (p in obj ? obj[p] : (create ? obj[p]={} : undefined)); + } + return obj; // mixed + } + + dojo.setObject = function(/*String*/name, /*Object*/value, /*Object?*/context){ + // summary: + // Set a property from a dot-separated string, such as "A.B.C" + // description: + // Useful for longer api chains where you have to test each object in + // the chain, or when you have an object reference in string format. + // Objects are created as needed along `path`. Returns the passed + // value if setting is successful or `undefined` if not. + // name: + // Path to a property, in the form "A.B.C". + // context: + // Optional. Object to use as root of path. Defaults to + // `dojo.global`. + // example: + // set the value of `foo.bar.baz`, regardless of whether + // intermediate objects already exist: + // | dojo.setObject("foo.bar.baz", value); + // example: + // without `dojo.setObject`, we often see code like this: + // | // ensure that intermediate objects are available + // | if(!obj["parent"]){ obj.parent = {}; } + // | if(!obj.parent["child"]){ obj.parent.child= {}; } + // | // now we can safely set the property + // | obj.parent.child.prop = "some value"; + // wheras with `dojo.setObject`, we can shorten that to: + // | dojo.setObject("parent.child.prop", "some value", obj); + var parts=name.split("."), p=parts.pop(), obj=d._getProp(parts, true, context); + return obj && p ? (obj[p]=value) : undefined; // Object + } + + dojo.getObject = function(/*String*/name, /*Boolean?*/create, /*Object?*/context){ + // summary: + // Get a property from a dot-separated string, such as "A.B.C" + // description: + // Useful for longer api chains where you have to test each object in + // the chain, or when you have an object reference in string format. + // name: + // Path to an property, in the form "A.B.C". + // create: + // Optional. Defaults to `false`. If `true`, Objects will be + // created at any point along the 'path' that is undefined. + // context: + // Optional. Object to use as root of path. Defaults to + // 'dojo.global'. Null may be passed. + return d._getProp(name.split("."), create, context); // Object + } + + dojo.exists = function(/*String*/name, /*Object?*/obj){ + // summary: + // determine if an object supports a given method + // description: + // useful for longer api chains where you have to test each object in + // the chain. Useful only for object and method detection. + // Not useful for testing generic properties on an object. + // In particular, dojo.exists("foo.bar") when foo.bar = "" + // will return false. Use ("bar" in foo) to test for those cases. + // name: + // Path to an object, in the form "A.B.C". + // obj: + // Object to use as root of path. Defaults to + // 'dojo.global'. Null may be passed. + // example: + // | // define an object + // | var foo = { + // | bar: { } + // | }; + // | + // | // search the global scope + // | dojo.exists("foo.bar"); // true + // | dojo.exists("foo.bar.baz"); // false + // | + // | // search from a particular scope + // | dojo.exists("bar", foo); // true + // | dojo.exists("bar.baz", foo); // false + return !!d.getObject(name, false, obj); // Boolean + } + + dojo["eval"] = function(/*String*/ scriptFragment){ + // summary: + // A legacy method created for use exclusively by internal Dojo methods. Do not use + // this method directly, the behavior of this eval will differ from the normal + // browser eval. + // description: + // Placed in a separate function to minimize size of trapped + // exceptions. Calling eval() directly from some other scope may + // complicate tracebacks on some platforms. + // returns: + // The result of the evaluation. Often `undefined` + return d.global.eval ? d.global.eval(scriptFragment) : eval(scriptFragment); // Object + } + + /*===== + dojo.deprecated = function(behaviour, extra, removal){ + // summary: + // Log a debug message to indicate that a behavior has been + // deprecated. + // behaviour: String + // The API or behavior being deprecated. Usually in the form + // of "myApp.someFunction()". + // extra: String? + // Text to append to the message. Often provides advice on a + // new function or facility to achieve the same goal during + // the deprecation period. + // removal: String? + // Text to indicate when in the future the behavior will be + // removed. Usually a version number. + // example: + // | dojo.deprecated("myApp.getTemp()", "use myApp.getLocaleTemp() instead", "1.0"); + } + + dojo.experimental = function(moduleName, extra){ + // summary: Marks code as experimental. + // description: + // This can be used to mark a function, file, or module as + // experimental. Experimental code is not ready to be used, and the + // APIs are subject to change without notice. Experimental code may be + // completed deleted without going through the normal deprecation + // process. + // moduleName: String + // The name of a module, or the name of a module file or a specific + // function + // extra: String? + // some additional message for the user + // example: + // | dojo.experimental("dojo.data.Result"); + // example: + // | dojo.experimental("dojo.weather.toKelvin()", "PENDING approval from NOAA"); + } + =====*/ + + //Real functions declared in dojo._firebug.firebug. + d.deprecated = d.experimental = function(){}; + })(); +// vim:ai:ts=4:noet diff --git a/lib/dojo/_base/_loader/hostenv_browser.js b/lib/dojo/_base/_loader/hostenv_browser.js index 0d746833e..7d92d70e3 100644 --- a/lib/dojo/_base/_loader/hostenv_browser.js +++ b/lib/dojo/_base/_loader/hostenv_browser.js @@ -5,240 +5,470 @@ */ -if(typeof window!="undefined"){ -dojo.isBrowser=true; -dojo._name="browser"; -(function(){ -var d=dojo; -if(document&&document.getElementsByTagName){ -var _1=document.getElementsByTagName("script"); -var _2=/dojo(\.xd)?\.js(\W|$)/i; -for(var i=0;i<_1.length;i++){ -var _3=_1[i].getAttribute("src"); -if(!_3){ -continue; -} -var m=_3.match(_2); -if(m){ -if(!d.config.baseUrl){ -d.config.baseUrl=_3.substring(0,m.index); -} -var _4=_1[i].getAttribute("djConfig"); -if(_4){ -var _5=eval("({ "+_4+" })"); -for(var x in _5){ -dojo.config[x]=_5[x]; -} -} -break; -} -} -} -d.baseUrl=d.config.baseUrl; -var n=navigator; -var _6=n.userAgent,_7=n.appVersion,tv=parseFloat(_7); -if(_6.indexOf("Opera")>=0){ -d.isOpera=tv; -} -if(_6.indexOf("AdobeAIR")>=0){ -d.isAIR=1; -} -d.isKhtml=(_7.indexOf("Konqueror")>=0)?tv:0; -d.isWebKit=parseFloat(_6.split("WebKit/")[1])||undefined; -d.isChrome=parseFloat(_6.split("Chrome/")[1])||undefined; -d.isMac=_7.indexOf("Macintosh")>=0; -var _8=Math.max(_7.indexOf("WebKit"),_7.indexOf("Safari"),0); -if(_8&&!dojo.isChrome){ -d.isSafari=parseFloat(_7.split("Version/")[1]); -if(!d.isSafari||parseFloat(_7.substr(_8+7))<=419.3){ -d.isSafari=2; -} -} -if(_6.indexOf("Gecko")>=0&&!d.isKhtml&&!d.isWebKit){ -d.isMozilla=d.isMoz=tv; -} -if(d.isMoz){ -d.isFF=parseFloat(_6.split("Firefox/")[1]||_6.split("Minefield/")[1])||undefined; -} -if(document.all&&!d.isOpera){ -d.isIE=parseFloat(_7.split("MSIE ")[1])||undefined; -var _9=document.documentMode; -if(_9&&_9!=5&&Math.floor(d.isIE)!=_9){ -d.isIE=_9; -} -} -if(dojo.isIE&&window.location.protocol==="file:"){ -dojo.config.ieForceActiveXXhr=true; -} -d.isQuirks=document.compatMode=="BackCompat"; -d.locale=dojo.config.locale||(d.isIE?n.userLanguage:n.language).toLowerCase(); -d._XMLHTTP_PROGIDS=["Msxml2.XMLHTTP","Microsoft.XMLHTTP","Msxml2.XMLHTTP.4.0"]; -d._xhrObj=function(){ -var _a,_b; -if(!dojo.isIE||!dojo.config.ieForceActiveXXhr){ -try{ -_a=new XMLHttpRequest(); -} -catch(e){ -} -} -if(!_a){ -for(var i=0;i<3;++i){ -var _c=d._XMLHTTP_PROGIDS[i]; -try{ -_a=new ActiveXObject(_c); -} -catch(e){ -_b=e; -} -if(_a){ -d._XMLHTTP_PROGIDS=[_c]; -break; -} -} -} -if(!_a){ -throw new Error("XMLHTTP not available: "+_b); -} -return _a; -}; -d._isDocumentOk=function(_d){ -var _e=_d.status||0,lp=location.protocol; -return (_e>=200&&_e<300)||_e==304||_e==1223||(!_e&&(lp=="file:"||lp=="chrome:"||lp=="chrome-extension:"||lp=="app:")); +/*===== +dojo.isBrowser = { + // example: + // | if(dojo.isBrowser){ ... } }; -var _f=window.location+""; -var _10=document.getElementsByTagName("base"); -var _11=(_10&&_10.length>0); -d._getText=function(uri,_12){ -var _13=d._xhrObj(); -if(!_11&&dojo._Url){ -uri=(new dojo._Url(_f,uri)).toString(); -} -if(d.config.cacheBust){ -uri+=""; -uri+=(uri.indexOf("?")==-1?"?":"&")+String(d.config.cacheBust).replace(/\W+/g,""); -} -_13.open("GET",uri,false); -try{ -_13.send(null); -if(!d._isDocumentOk(_13)){ -var err=Error("Unable to load "+uri+" status:"+_13.status); -err.status=_13.status; -err.responseText=_13.responseText; -throw err; -} -} -catch(e){ -if(_12){ -return null; -} -throw e; -} -return _13.responseText; -}; -var _14=window; -var _15=function(_16,fp){ -var _17=_14.attachEvent||_14.addEventListener; -_16=_14.attachEvent?_16:_16.substring(2); -_17(_16,function(){ -fp.apply(_14,arguments); -},false); -}; -d._windowUnloaders=[]; -d.windowUnloaded=function(){ -var mll=d._windowUnloaders; -while(mll.length){ -(mll.pop())(); -} -d=null; -}; -var _18=0; -d.addOnWindowUnload=function(obj,_19){ -d._onto(d._windowUnloaders,obj,_19); -if(!_18){ -_18=1; -_15("onunload",d.windowUnloaded); -} + +dojo.isFF = { + // example: + // | if(dojo.isFF > 1){ ... } }; -var _1a=0; -d.addOnUnload=function(obj,_1b){ -d._onto(d._unloaders,obj,_1b); -if(!_1a){ -_1a=1; -_15("onbeforeunload",dojo.unloaded); -} + +dojo.isIE = { + // example: + // | if(dojo.isIE > 6){ + // | // we are IE7 + // | } }; -})(); -dojo._initFired=false; -dojo._loadInit=function(e){ -if(dojo._scrollIntervalId){ -clearInterval(dojo._scrollIntervalId); -dojo._scrollIntervalId=0; -} -if(!dojo._initFired){ -dojo._initFired=true; -if(!dojo.config.afterOnLoad&&window.detachEvent){ -window.detachEvent("onload",dojo._loadInit); -} -if(dojo._inFlightCount==0){ -dojo._modulesLoaded(); -} -} + +dojo.isSafari = { + // example: + // | if(dojo.isSafari){ ... } + // example: + // Detect iPhone: + // | if(dojo.isSafari && navigator.userAgent.indexOf("iPhone") != -1){ + // | // we are iPhone. Note, iPod touch reports "iPod" above and fails this test. + // | } }; -if(!dojo.config.afterOnLoad){ -if(document.addEventListener){ -document.addEventListener("DOMContentLoaded",dojo._loadInit,false); -window.addEventListener("load",dojo._loadInit,false); -}else{ -if(window.attachEvent){ -window.attachEvent("onload",dojo._loadInit); -if(!dojo.config.skipIeDomLoaded&&self===self.top){ -dojo._scrollIntervalId=setInterval(function(){ -try{ -if(document.body){ -document.documentElement.doScroll("left"); -dojo._loadInit(); -} -} -catch(e){ -} -},30); -} -} -} -} -if(dojo.isIE){ -try{ -(function(){ -document.namespaces.add("v","urn:schemas-microsoft-com:vml"); -var _1c=["*","group","roundrect","oval","shape","rect","imagedata","path","textpath","text"],i=0,l=1,s=document.createStyleSheet(); -if(dojo.isIE>=8){ -i=1; -l=_1c.length; -} -for(;i<l;++i){ -s.addRule("v\\:"+_1c[i],"behavior:url(#default#VML); display:inline-block"); -} -})(); -} -catch(e){ -} -} -} + +dojo = { + // isBrowser: Boolean + // True if the client is a web-browser + isBrowser: true, + // isFF: Number | undefined + // Version as a Number if client is FireFox. undefined otherwise. Corresponds to + // major detected FireFox version (1.5, 2, 3, etc.) + isFF: 2, + // isIE: Number | undefined + // Version as a Number if client is MSIE(PC). undefined otherwise. Corresponds to + // major detected IE version (6, 7, 8, etc.) + isIE: 6, + // isKhtml: Number | undefined + // Version as a Number if client is a KHTML browser. undefined otherwise. Corresponds to major + // detected version. + isKhtml: 0, + // isWebKit: Number | undefined + // Version as a Number if client is a WebKit-derived browser (Konqueror, + // Safari, Chrome, etc.). undefined otherwise. + isWebKit: 0, + // isMozilla: Number | undefined + // Version as a Number if client is a Mozilla-based browser (Firefox, + // SeaMonkey). undefined otherwise. Corresponds to major detected version. + isMozilla: 0, + // isOpera: Number | undefined + // Version as a Number if client is Opera. undefined otherwise. Corresponds to + // major detected version. + isOpera: 0, + // isSafari: Number | undefined + // Version as a Number if client is Safari or iPhone. undefined otherwise. + isSafari: 0, + // isChrome: Number | undefined + // Version as a Number if client is Chrome browser. undefined otherwise. + isChrome: 0 + // isMac: Boolean + // True if the client runs on Mac +} +=====*/ + +if(typeof window != 'undefined'){ + dojo.isBrowser = true; + dojo._name = "browser"; + + + // attempt to figure out the path to dojo if it isn't set in the config + (function(){ + var d = dojo; + + // this is a scope protection closure. We set browser versions and grab + // the URL we were loaded from here. + + // grab the node we were loaded from + if(document && document.getElementsByTagName){ + var scripts = document.getElementsByTagName("script"); + var rePkg = /dojo(\.xd)?\.js(\W|$)/i; + for(var i = 0; i < scripts.length; i++){ + var src = scripts[i].getAttribute("src"); + if(!src){ continue; } + var m = src.match(rePkg); + if(m){ + // find out where we came from + if(!d.config.baseUrl){ + d.config.baseUrl = src.substring(0, m.index); + } + // and find out if we need to modify our behavior + var cfg = scripts[i].getAttribute("djConfig"); + if(cfg){ + var cfgo = eval("({ "+cfg+" })"); + for(var x in cfgo){ + dojo.config[x] = cfgo[x]; + } + } + break; // "first Dojo wins" + } + } + } + d.baseUrl = d.config.baseUrl; + + // fill in the rendering support information in dojo.render.* + var n = navigator; + var dua = n.userAgent, + dav = n.appVersion, + tv = parseFloat(dav); + + if(dua.indexOf("Opera") >= 0){ d.isOpera = tv; } + if(dua.indexOf("AdobeAIR") >= 0){ d.isAIR = 1; } + d.isKhtml = (dav.indexOf("Konqueror") >= 0) ? tv : 0; + d.isWebKit = parseFloat(dua.split("WebKit/")[1]) || undefined; + d.isChrome = parseFloat(dua.split("Chrome/")[1]) || undefined; + d.isMac = dav.indexOf("Macintosh") >= 0; + + // safari detection derived from: + // http://developer.apple.com/internet/safari/faq.html#anchor2 + // http://developer.apple.com/internet/safari/uamatrix.html + var index = Math.max(dav.indexOf("WebKit"), dav.indexOf("Safari"), 0); + if(index && !dojo.isChrome){ + // try to grab the explicit Safari version first. If we don't get + // one, look for less than 419.3 as the indication that we're on something + // "Safari 2-ish". + d.isSafari = parseFloat(dav.split("Version/")[1]); + if(!d.isSafari || parseFloat(dav.substr(index + 7)) <= 419.3){ + d.isSafari = 2; + } + } + + if(dua.indexOf("Gecko") >= 0 && !d.isKhtml && !d.isWebKit){ d.isMozilla = d.isMoz = tv; } + if(d.isMoz){ + //We really need to get away from this. Consider a sane isGecko approach for the future. + d.isFF = parseFloat(dua.split("Firefox/")[1] || dua.split("Minefield/")[1]) || undefined; + } + if(document.all && !d.isOpera){ + d.isIE = parseFloat(dav.split("MSIE ")[1]) || undefined; + //In cases where the page has an HTTP header or META tag with + //X-UA-Compatible, then it is in emulation mode. + //Make sure isIE reflects the desired version. + //document.documentMode of 5 means quirks mode. + //Only switch the value if documentMode's major version + //is different from isIE's major version. + var mode = document.documentMode; + if(mode && mode != 5 && Math.floor(d.isIE) != mode){ + d.isIE = mode; + } + } + + //Workaround to get local file loads of dojo to work on IE 7 + //by forcing to not use native xhr. + if(dojo.isIE && window.location.protocol === "file:"){ + dojo.config.ieForceActiveXXhr=true; + } + + d.isQuirks = document.compatMode == "BackCompat"; + + // TODO: is the HTML LANG attribute relevant? + d.locale = dojo.config.locale || (d.isIE ? n.userLanguage : n.language).toLowerCase(); + + // These are in order of decreasing likelihood; this will change in time. + d._XMLHTTP_PROGIDS = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0']; + + d._xhrObj = function(){ + // summary: + // does the work of portably generating a new XMLHTTPRequest object. + var http, last_e; + if(!dojo.isIE || !dojo.config.ieForceActiveXXhr){ + try{ http = new XMLHttpRequest(); }catch(e){} + } + if(!http){ + for(var i=0; i<3; ++i){ + var progid = d._XMLHTTP_PROGIDS[i]; + try{ + http = new ActiveXObject(progid); + }catch(e){ + last_e = e; + } + + if(http){ + d._XMLHTTP_PROGIDS = [progid]; // so faster next time + break; + } + } + } + + if(!http){ + throw new Error("XMLHTTP not available: "+last_e); + } + + return http; // XMLHTTPRequest instance + } + + d._isDocumentOk = function(http){ + var stat = http.status || 0, + lp = location.protocol; + return (stat >= 200 && stat < 300) || // Boolean + stat == 304 || // allow any 2XX response code + stat == 1223 || // get it out of the cache + // Internet Explorer mangled the status code OR we're Titanium/browser chrome/chrome extension requesting a local file + (!stat && (lp == "file:" || lp == "chrome:" || lp == "chrome-extension:" || lp == "app:") ); + } + + //See if base tag is in use. + //This is to fix http://trac.dojotoolkit.org/ticket/3973, + //but really, we need to find out how to get rid of the dojo._Url reference + //below and still have DOH work with the dojo.i18n test following some other + //test that uses the test frame to load a document (trac #2757). + //Opera still has problems, but perhaps a larger issue of base tag support + //with XHR requests (hasBase is true, but the request is still made to document + //path, not base path). + var owloc = window.location+""; + var base = document.getElementsByTagName("base"); + var hasBase = (base && base.length > 0); + + d._getText = function(/*URI*/ uri, /*Boolean*/ fail_ok){ + // summary: Read the contents of the specified uri and return those contents. + // uri: + // A relative or absolute uri. If absolute, it still must be in + // the same "domain" as we are. + // fail_ok: + // Default false. If fail_ok and loading fails, return null + // instead of throwing. + // returns: The response text. null is returned when there is a + // failure and failure is okay (an exception otherwise) + + // NOTE: must be declared before scope switches ie. this._xhrObj() + var http = d._xhrObj(); + + if(!hasBase && dojo._Url){ + uri = (new dojo._Url(owloc, uri)).toString(); + } + + if(d.config.cacheBust){ + //Make sure we have a string before string methods are used on uri + uri += ""; + uri += (uri.indexOf("?") == -1 ? "?" : "&") + String(d.config.cacheBust).replace(/\W+/g,""); + } + + http.open('GET', uri, false); + try{ + http.send(null); + if(!d._isDocumentOk(http)){ + var err = Error("Unable to load "+uri+" status:"+ http.status); + err.status = http.status; + err.responseText = http.responseText; + throw err; + } + }catch(e){ + if(fail_ok){ return null; } // null + // rethrow the exception + throw e; + } + return http.responseText; // String + } + + + var _w = window; + var _handleNodeEvent = function(/*String*/evtName, /*Function*/fp){ + // summary: + // non-destructively adds the specified function to the node's + // evtName handler. + // evtName: should be in the form "onclick" for "onclick" handlers. + // Make sure you pass in the "on" part. + var _a = _w.attachEvent || _w.addEventListener; + evtName = _w.attachEvent ? evtName : evtName.substring(2); + _a(evtName, function(){ + fp.apply(_w, arguments); + }, false); + }; + + + d._windowUnloaders = []; + + d.windowUnloaded = function(){ + // summary: + // signal fired by impending window destruction. You may use + // dojo.addOnWindowUnload() to register a listener for this + // event. NOTE: if you wish to dojo.connect() to this method + // to perform page/application cleanup, be aware that this + // event WILL NOT fire if no handler has been registered with + // dojo.addOnWindowUnload. This behavior started in Dojo 1.3. + // Previous versions always triggered dojo.windowUnloaded. See + // dojo.addOnWindowUnload for more info. + var mll = d._windowUnloaders; + while(mll.length){ + (mll.pop())(); + } + d = null; + }; + + var _onWindowUnloadAttached = 0; + d.addOnWindowUnload = function(/*Object?|Function?*/obj, /*String|Function?*/functionName){ + // summary: + // registers a function to be triggered when window.onunload + // fires. + // description: + // The first time that addOnWindowUnload is called Dojo + // will register a page listener to trigger your unload + // handler with. Note that registering these handlers may + // destory "fastback" page caching in browsers that support + // it. Be careful trying to modify the DOM or access + // JavaScript properties during this phase of page unloading: + // they may not always be available. Consider + // dojo.addOnUnload() if you need to modify the DOM or do + // heavy JavaScript work since it fires at the eqivalent of + // the page's "onbeforeunload" event. + // example: + // | dojo.addOnWindowUnload(functionPointer) + // | dojo.addOnWindowUnload(object, "functionName"); + // | dojo.addOnWindowUnload(object, function(){ /* ... */}); + + d._onto(d._windowUnloaders, obj, functionName); + if(!_onWindowUnloadAttached){ + _onWindowUnloadAttached = 1; + _handleNodeEvent("onunload", d.windowUnloaded); + } + }; + + var _onUnloadAttached = 0; + d.addOnUnload = function(/*Object?|Function?*/obj, /*String|Function?*/functionName){ + // summary: + // registers a function to be triggered when the page unloads. + // description: + // The first time that addOnUnload is called Dojo will + // register a page listener to trigger your unload handler + // with. + // + // In a browser enviroment, the functions will be triggered + // during the window.onbeforeunload event. Be careful of doing + // too much work in an unload handler. onbeforeunload can be + // triggered if a link to download a file is clicked, or if + // the link is a javascript: link. In these cases, the + // onbeforeunload event fires, but the document is not + // actually destroyed. So be careful about doing destructive + // operations in a dojo.addOnUnload callback. + // + // Further note that calling dojo.addOnUnload will prevent + // browsers from using a "fast back" cache to make page + // loading via back button instantaneous. + // example: + // | dojo.addOnUnload(functionPointer) + // | dojo.addOnUnload(object, "functionName") + // | dojo.addOnUnload(object, function(){ /* ... */}); + + d._onto(d._unloaders, obj, functionName); + if(!_onUnloadAttached){ + _onUnloadAttached = 1; + _handleNodeEvent("onbeforeunload", dojo.unloaded); + } + }; + + })(); + + //START DOMContentLoaded + dojo._initFired = false; + dojo._loadInit = function(e){ + if(dojo._scrollIntervalId){ + clearInterval(dojo._scrollIntervalId); + dojo._scrollIntervalId = 0; + } + + if(!dojo._initFired){ + dojo._initFired = true; + + //Help out IE to avoid memory leak. + if(!dojo.config.afterOnLoad && window.detachEvent){ + window.detachEvent("onload", dojo._loadInit); + } + + if(dojo._inFlightCount == 0){ + dojo._modulesLoaded(); + } + } + } + + if(!dojo.config.afterOnLoad){ + if(document.addEventListener){ + //Standards. Hooray! Assumption here that if standards based, + //it knows about DOMContentLoaded. It is OK if it does not, the fall through + //to window onload should be good enough. + document.addEventListener("DOMContentLoaded", dojo._loadInit, false); + window.addEventListener("load", dojo._loadInit, false); + }else if(window.attachEvent){ + window.attachEvent("onload", dojo._loadInit); + + //DOMContentLoaded approximation. Diego Perini found this MSDN article + //that indicates doScroll is available after DOM ready, so do a setTimeout + //to check when it is available. + //http://msdn.microsoft.com/en-us/library/ms531426.aspx + if(!dojo.config.skipIeDomLoaded && self === self.top){ + dojo._scrollIntervalId = setInterval(function (){ + try{ + //When dojo is loaded into an iframe in an IE HTML Application + //(HTA), such as in a selenium test, javascript in the iframe + //can't see anything outside of it, so self===self.top is true, + //but the iframe is not the top window and doScroll will be + //available before document.body is set. Test document.body + //before trying the doScroll trick + if(document.body){ + document.documentElement.doScroll("left"); + dojo._loadInit(); + } + }catch (e){} + }, 30); + } + } + } + + if(dojo.isIE){ + try{ + (function(){ + document.namespaces.add("v", "urn:schemas-microsoft-com:vml"); + var vmlElems = ["*", "group", "roundrect", "oval", "shape", "rect", "imagedata", "path", "textpath", "text"], + i = 0, l = 1, s = document.createStyleSheet(); + if(dojo.isIE >= 8){ + i = 1; + l = vmlElems.length; + } + for(; i < l; ++i){ + s.addRule("v\\:" + vmlElems[i], "behavior:url(#default#VML); display:inline-block"); + } + })(); + }catch(e){} + } + //END DOMContentLoaded + + + /* + OpenAjax.subscribe("OpenAjax", "onload", function(){ + if(dojo._inFlightCount == 0){ + dojo._modulesLoaded(); + } + }); + + OpenAjax.subscribe("OpenAjax", "onunload", function(){ + dojo.unloaded(); + }); + */ +} //if (typeof window != 'undefined') + +//Register any module paths set up in djConfig. Need to do this +//in the hostenvs since hostenv_browser can read djConfig from a +//script tag's attribute. (function(){ -var mp=dojo.config["modulePaths"]; -if(mp){ -for(var _1d in mp){ -dojo.registerModulePath(_1d,mp[_1d]); -} -} + var mp = dojo.config["modulePaths"]; + if(mp){ + for(var param in mp){ + dojo.registerModulePath(param, mp[param]); + } + } })(); + +//Load debug code if necessary. if(dojo.config.isDebug){ -dojo.require("dojo._firebug.firebug"); + dojo.require("dojo._firebug.firebug"); } + if(dojo.config.debugAtAllCosts){ -dojo.config.useXDomain=true; -dojo.require("dojo._base._loader.loader_xd"); -dojo.require("dojo._base._loader.loader_debug"); -dojo.require("dojo.i18n"); + dojo.config.useXDomain = true; + dojo.require("dojo._base._loader.loader_xd"); + dojo.require("dojo._base._loader.loader_debug"); + dojo.require("dojo.i18n"); } diff --git a/lib/dojo/_base/_loader/hostenv_ff_ext.js b/lib/dojo/_base/_loader/hostenv_ff_ext.js index 08242393d..94a0a8046 100644 --- a/lib/dojo/_base/_loader/hostenv_ff_ext.js +++ b/lib/dojo/_base/_loader/hostenv_ff_ext.js @@ -5,171 +5,334 @@ */ -if(typeof window!="undefined"){ -dojo.isBrowser=true; -dojo._name="browser"; -(function(){ -var d=dojo; -d.baseUrl=d.config.baseUrl; -var n=navigator; -var _1=n.userAgent; -var _2=n.appVersion; -var tv=parseFloat(_2); -d.isMozilla=d.isMoz=tv; -if(d.isMoz){ -d.isFF=parseFloat(_1.split("Firefox/")[1])||undefined; -} -d.isQuirks=document.compatMode=="BackCompat"; -d.locale=dojo.config.locale||n.language.toLowerCase(); -d._xhrObj=function(){ -return new XMLHttpRequest(); -}; -var _3=d._loadUri; -d._loadUri=function(_4,cb){ -var _5=["file:","chrome:","resource:"].some(function(_6){ -return String(_4).indexOf(_6)==0; -}); -if(_5){ -var l=Components.classes["@mozilla.org/moz/jssubscript-loader;1"].getService(Components.interfaces.mozIJSSubScriptLoader); -var _7=l.loadSubScript(_4,d.global); -if(cb){ -cb(_7); -} -return true; -}else{ -return _3.apply(d,arguments); -} -}; -d._isDocumentOk=function(_8){ -var _9=_8.status||0; -return (_9>=200&&_9<300)||_9==304||_9==1223||(!_9&&(location.protocol=="file:"||location.protocol=="chrome:")); -}; -var _a=false; -d._getText=function(_b,_c){ -var _d=d._xhrObj(); -if(!_a&&dojo._Url){ -_b=(new dojo._Url(_b)).toString(); -} -if(d.config.cacheBust){ -_b+=""; -_b+=(_b.indexOf("?")==-1?"?":"&")+String(d.config.cacheBust).replace(/\W+/g,""); -} -var _e=["file:","chrome:","resource:"].some(function(_f){ -return String(_b).indexOf(_f)==0; -}); -if(_e){ -var _10=Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService); -var _11=Components.classes["@mozilla.org/scriptableinputstream;1"].getService(Components.interfaces.nsIScriptableInputStream); -var _12=_10.newChannel(_b,null,null); -var _13=_12.open(); -_11.init(_13); -var str=_11.read(_13.available()); -_11.close(); -_13.close(); -return str; -}else{ -_d.open("GET",_b,false); -try{ -_d.send(null); -if(!d._isDocumentOk(_d)){ -var err=Error("Unable to load "+_b+" status:"+_d.status); -err.status=_d.status; -err.responseText=_d.responseText; -throw err; -} -} -catch(e){ -if(_c){ -return null; -} -throw e; -} -return _d.responseText; -} -}; -d._windowUnloaders=[]; -d.windowUnloaded=function(){ -var mll=d._windowUnloaders; -while(mll.length){ -(mll.pop())(); -} -}; -d.addOnWindowUnload=function(obj,_14){ -d._onto(d._windowUnloaders,obj,_14); -}; -var _15=[]; -var _16=null; -dojo._defaultContext=[window,document]; -dojo.pushContext=function(g,d){ -var old=[dojo.global,dojo.doc]; -_15.push(old); -var n; -if(!g&&!d){ -n=dojo._defaultContext; -}else{ -n=[g,d]; -if(!d&&dojo.isString(g)){ -var t=document.getElementById(g); -if(t.contentDocument){ -n=[t.contentWindow,t.contentDocument]; -} -} -} -_16=n; -dojo.setContext.apply(dojo,n); -return old; -}; -dojo.popContext=function(){ -var oc=_16; -if(!_15.length){ -return oc; -} -dojo.setContext.apply(dojo,_15.pop()); -return oc; -}; -dojo._inContext=function(g,d,f){ -var a=dojo._toArray(arguments); -f=a.pop(); -if(a.length==1){ -d=null; -} -dojo.pushContext(g,d); -var r=f(); -dojo.popContext(); -return r; -}; -})(); -dojo._initFired=false; -dojo._loadInit=function(e){ -dojo._initFired=true; -var _17=(e&&e.type)?e.type.toLowerCase():"load"; -if(arguments.callee.initialized||(_17!="domcontentloaded"&&_17!="load")){ -return; -} -arguments.callee.initialized=true; -if(dojo._inFlightCount==0){ -dojo._modulesLoaded(); -} -}; -if(!dojo.config.afterOnLoad){ -window.addEventListener("DOMContentLoaded",function(e){ -dojo._loadInit(e); -},false); -} -} +// a host environment specifically built for Mozilla extensions, but derived +// from the browser host environment +if(typeof window != 'undefined'){ + dojo.isBrowser = true; + dojo._name = "browser"; + + + // FIXME: PORTME + // http://developer.mozilla.org/en/mozIJSSubScriptLoader + + + // attempt to figure out the path to dojo if it isn't set in the config + (function(){ + var d = dojo; + // this is a scope protection closure. We set browser versions and grab + // the URL we were loaded from here. + + // FIXME: need to probably use a different reference to "document" to get the hosting XUL environment + + d.baseUrl = d.config.baseUrl; + + // fill in the rendering support information in dojo.render.* + var n = navigator; + var dua = n.userAgent; + var dav = n.appVersion; + var tv = parseFloat(dav); + + d.isMozilla = d.isMoz = tv; + if(d.isMoz){ + d.isFF = parseFloat(dua.split("Firefox/")[1]) || undefined; + } + + // FIXME + d.isQuirks = document.compatMode == "BackCompat"; + + // FIXME + // TODO: is the HTML LANG attribute relevant? + d.locale = dojo.config.locale || n.language.toLowerCase(); + + d._xhrObj = function(){ + return new XMLHttpRequest(); + } + + // monkey-patch _loadUri to handle file://, chrome://, and resource:// url's + var oldLoadUri = d._loadUri; + d._loadUri = function(uri, cb){ + var handleLocal = ["file:", "chrome:", "resource:"].some(function(prefix){ + return String(uri).indexOf(prefix) == 0; + }); + if(handleLocal){ + // see: + // http://developer.mozilla.org/en/mozIJSSubScriptLoader + var l = Components.classes["@mozilla.org/moz/jssubscript-loader;1"] + .getService(Components.interfaces.mozIJSSubScriptLoader); + var value = l.loadSubScript(uri, d.global) + if(cb){ cb(value); } + return true; + }else{ + // otherwise, call the pre-existing version + return oldLoadUri.apply(d, arguments); + } + } + + // FIXME: PORTME + d._isDocumentOk = function(http){ + var stat = http.status || 0; + return (stat >= 200 && stat < 300) || // Boolean + stat == 304 || // allow any 2XX response code + stat == 1223 || // get it out of the cache + (!stat && (location.protocol=="file:" || location.protocol=="chrome:") ); + } + + // FIXME: PORTME + // var owloc = window.location+""; + // var base = document.getElementsByTagName("base"); + // var hasBase = (base && base.length > 0); + var hasBase = false; + + d._getText = function(/*URI*/ uri, /*Boolean*/ fail_ok){ + // summary: Read the contents of the specified uri and return those contents. + // uri: + // A relative or absolute uri. If absolute, it still must be in + // the same "domain" as we are. + // fail_ok: + // Default false. If fail_ok and loading fails, return null + // instead of throwing. + // returns: The response text. null is returned when there is a + // failure and failure is okay (an exception otherwise) + + // alert("_getText: " + uri); + + // NOTE: must be declared before scope switches ie. this._xhrObj() + var http = d._xhrObj(); + + if(!hasBase && dojo._Url){ + uri = (new dojo._Url(uri)).toString(); + } + if(d.config.cacheBust){ + //Make sure we have a string before string methods are used on uri + uri += ""; + uri += (uri.indexOf("?") == -1 ? "?" : "&") + String(d.config.cacheBust).replace(/\W+/g,""); + } + var handleLocal = ["file:", "chrome:", "resource:"].some(function(prefix){ + return String(uri).indexOf(prefix) == 0; + }); + if(handleLocal){ + // see: + // http://forums.mozillazine.org/viewtopic.php?p=921150#921150 + var ioService = Components.classes["@mozilla.org/network/io-service;1"] + .getService(Components.interfaces.nsIIOService); + var scriptableStream=Components + .classes["@mozilla.org/scriptableinputstream;1"] + .getService(Components.interfaces.nsIScriptableInputStream); + + var channel = ioService.newChannel(uri, null, null); + var input = channel.open(); + scriptableStream.init(input); + var str = scriptableStream.read(input.available()); + scriptableStream.close(); + input.close(); + return str; + }else{ + http.open('GET', uri, false); + try{ + http.send(null); + // alert(http); + if(!d._isDocumentOk(http)){ + var err = Error("Unable to load "+uri+" status:"+ http.status); + err.status = http.status; + err.responseText = http.responseText; + throw err; + } + }catch(e){ + if(fail_ok){ return null; } // null + // rethrow the exception + throw e; + } + return http.responseText; // String + } + } + + d._windowUnloaders = []; + + // FIXME: PORTME + d.windowUnloaded = function(){ + // summary: + // signal fired by impending window destruction. You may use + // dojo.addOnWIndowUnload() or dojo.connect() to this method to perform + // page/application cleanup methods. See dojo.addOnWindowUnload for more info. + var mll = d._windowUnloaders; + while(mll.length){ + (mll.pop())(); + } + } + + // FIXME: PORTME + d.addOnWindowUnload = function(/*Object?*/obj, /*String|Function?*/functionName){ + // summary: + // registers a function to be triggered when window.onunload fires. + // Be careful trying to modify the DOM or access JavaScript properties + // during this phase of page unloading: they may not always be available. + // Consider dojo.addOnUnload() if you need to modify the DOM or do heavy + // JavaScript work. + // example: + // | dojo.addOnWindowUnload(functionPointer) + // | dojo.addOnWindowUnload(object, "functionName") + // | dojo.addOnWindowUnload(object, function(){ /* ... */}); + + d._onto(d._windowUnloaders, obj, functionName); + } + + // XUL specific APIs + var contexts = []; + var current = null; + dojo._defaultContext = [ window, document ]; + + dojo.pushContext = function(/*Object|String?*/g, /*MDocumentElement?*/d){ + // summary: + // causes subsequent calls to Dojo methods to assume the + // passed object and, optionally, document as the default + // scopes to use. A 2-element array of the previous global and + // document are returned. + // description: + // dojo.pushContext treats contexts as a stack. The + // auto-detected contexts which are initially provided using + // dojo.setContext() require authors to keep state in order to + // "return" to a previous context, whereas the + // dojo.pushContext and dojo.popContext methods provide a more + // natural way to augment blocks of code to ensure that they + // execute in a different window or frame without issue. If + // called without any arguments, the default context (the + // context when Dojo is first loaded) is instead pushed into + // the stack. If only a single string is passed, a node in the + // intitial context's document is looked up and its + // contextWindow and contextDocument properties are used as + // the context to push. This means that iframes can be given + // an ID and code can be executed in the scope of the iframe's + // document in subsequent calls easily. + // g: + // The global context. If a string, the id of the frame to + // search for a context and document. + // d: + // The document element to execute subsequent code with. + var old = [dojo.global, dojo.doc]; + contexts.push(old); + var n; + if(!g && !d){ + n = dojo._defaultContext; + }else{ + n = [ g, d ]; + if(!d && dojo.isString(g)){ + var t = document.getElementById(g); + if(t.contentDocument){ + n = [t.contentWindow, t.contentDocument]; + } + } + } + current = n; + dojo.setContext.apply(dojo, n); + return old; // Array + }; + + dojo.popContext = function(){ + // summary: + // If the context stack contains elements, ensure that + // subsequent code executes in the *previous* context to the + // current context. The current context set ([global, + // document]) is returned. + var oc = current; + if(!contexts.length){ + return oc; + } + dojo.setContext.apply(dojo, contexts.pop()); + return oc; + }; + + // FIXME: + // don't really like the current arguments and order to + // _inContext, so don't make it public until it's right! + dojo._inContext = function(g, d, f){ + var a = dojo._toArray(arguments); + f = a.pop(); + if(a.length == 1){ + d = null; + } + dojo.pushContext(g, d); + var r = f(); + dojo.popContext(); + return r; + }; + + })(); + + dojo._initFired = false; + // BEGIN DOMContentLoaded, from Dean Edwards (http://dean.edwards.name/weblog/2006/06/again/) + dojo._loadInit = function(e){ + dojo._initFired = true; + // allow multiple calls, only first one will take effect + // A bug in khtml calls events callbacks for document for event which isnt supported + // for example a created contextmenu event calls DOMContentLoaded, workaround + var type = (e && e.type) ? e.type.toLowerCase() : "load"; + if(arguments.callee.initialized || (type != "domcontentloaded" && type != "load")){ return; } + arguments.callee.initialized = true; + if(dojo._inFlightCount == 0){ + dojo._modulesLoaded(); + } + } + + /* + (function(){ + var _w = window; + var _handleNodeEvent = function(evtName, fp){ + // summary: + // non-destructively adds the specified function to the node's + // evtName handler. + // evtName: should be in the form "onclick" for "onclick" handlers. + // Make sure you pass in the "on" part. + var oldHandler = _w[evtName] || function(){}; + _w[evtName] = function(){ + fp.apply(_w, arguments); + oldHandler.apply(_w, arguments); + }; + }; + // FIXME: PORT + // FIXME: dojo.unloaded requires dojo scope, so using anon function wrapper. + _handleNodeEvent("onbeforeunload", function() { dojo.unloaded(); }); + _handleNodeEvent("onunload", function() { dojo.windowUnloaded(); }); + })(); + */ + + + // FIXME: PORTME + // this event fires a lot, namely for all plugin XUL overlays and for + // all iframes (in addition to window navigations). We only want + // Dojo's to fire once..but we might care if pages navigate. We'll + // probably need an extension-specific API + if(!dojo.config.afterOnLoad){ + window.addEventListener("DOMContentLoaded",function(e){ + dojo._loadInit(e); + // console.log("DOM content loaded", e); + }, false); + } + +} //if (typeof window != 'undefined') + +//Register any module paths set up in djConfig. Need to do this +//in the hostenvs since hostenv_browser can read djConfig from a +//script tag's attribute. (function(){ -var mp=dojo.config["modulePaths"]; -if(mp){ -for(var _18 in mp){ -dojo.registerModulePath(_18,mp[_18]); -} -} + var mp = dojo.config["modulePaths"]; + if(mp){ + for(var param in mp){ + dojo.registerModulePath(param, mp[param]); + } + } })(); + +//Load debug code if necessary. if(dojo.config.isDebug){ -console.log=function(m){ -var s=Components.classes["@mozilla.org/consoleservice;1"].getService(Components.interfaces.nsIConsoleService); -s.logStringMessage(m); -}; -console.debug=function(){ -}; + // logging stub for extension logging + console.log = function(m){ + var s = Components.classes["@mozilla.org/consoleservice;1"].getService( + Components.interfaces.nsIConsoleService + ); + s.logStringMessage(m); + } + console.debug = function(){ + console.log(dojo._toArray(arguments).join(" ")); + } + // FIXME: what about the rest of the console.* methods? And is there any way to reach into firebug and log into it directly? } diff --git a/lib/dojo/_base/_loader/hostenv_rhino.js b/lib/dojo/_base/_loader/hostenv_rhino.js index 9cd882713..ee9ad8b43 100644 --- a/lib/dojo/_base/_loader/hostenv_rhino.js +++ b/lib/dojo/_base/_loader/hostenv_rhino.js @@ -5,149 +5,204 @@ */ +/* +* Rhino host environment +*/ + if(dojo.config["baseUrl"]){ -dojo.baseUrl=dojo.config["baseUrl"]; + dojo.baseUrl = dojo.config["baseUrl"]; }else{ -dojo.baseUrl="./"; + dojo.baseUrl = "./"; } -dojo.locale=dojo.locale||String(java.util.Locale.getDefault().toString().replace("_","-").toLowerCase()); -dojo._name="rhino"; -dojo.isRhino=true; -if(typeof print=="function"){ -console.debug=print; + +dojo.locale = dojo.locale || String(java.util.Locale.getDefault().toString().replace('_','-').toLowerCase()); +dojo._name = 'rhino'; +dojo.isRhino = true; + +if(typeof print == "function"){ + console.debug = print; } + if(!("byId" in dojo)){ -dojo.byId=function(id,_1){ -if(id&&(typeof id=="string"||id instanceof String)){ -if(!_1){ -_1=document; -} -return _1.getElementById(id); -} -return id; -}; -} -dojo._isLocalUrl=function(_2){ -var _3=(new java.io.File(_2)).exists(); -if(!_3){ -var _4; -try{ -_4=(new java.net.URL(_2)).openStream(); -_4.close(); -} -finally{ -if(_4&&_4.close){ -_4.close(); -} -} -} -return _3; -}; -dojo._loadUri=function(_5,cb){ -try{ -var _6; -try{ -_6=dojo._isLocalUrl(_5); -} -catch(e){ -return false; -} -if(cb){ -var _7=(_6?readText:readUri)(_5,"UTF-8"); -if(!eval("''").length){ -_7=String(_7).replace(/[\u200E\u200F\u202A-\u202E]/g,function(_8){ -return "\\u"+_8.charCodeAt(0).toString(16); -}); -} -cb(eval("("+_7+")")); -}else{ -load(_5); -} -return true; -} -catch(e){ -return false; -} -}; -dojo.exit=function(_9){ -quit(_9); -}; -function readText(_a,_b){ -_b=_b||"utf-8"; -var jf=new java.io.File(_a); -var is=new java.io.FileInputStream(jf); -return dj_readInputStream(is,_b); -}; -function readUri(_c,_d){ -var _e=(new java.net.URL(_c)).openConnection(); -_d=_d||_e.getContentEncoding()||"utf-8"; -var is=_e.getInputStream(); -return dj_readInputStream(is,_d); -}; -function dj_readInputStream(is,_f){ -var _10=new java.io.BufferedReader(new java.io.InputStreamReader(is,_f)); -try{ -var sb=new java.lang.StringBuffer(); -var _11=""; -while((_11=_10.readLine())!==null){ -sb.append(_11); -sb.append(java.lang.System.getProperty("line.separator")); -} -return sb.toString(); + dojo.byId = function(id, doc){ + if(id && (typeof id == "string" || id instanceof String)){ + if(!doc){ doc = document; } + return doc.getElementById(id); + } + return id; // assume it's a node + } } -finally{ -_10.close(); -} -}; -dojo._getText=function(uri,_12){ -try{ -var _13=dojo._isLocalUrl(uri); -var _14=(_13?readText:readUri)(uri,"UTF-8"); -if(_14!==null){ -_14+=""; + +dojo._isLocalUrl = function(/*String*/ uri) { + // summary: + // determines if URI is local or not. + + var local = (new java.io.File(uri)).exists(); + if(!local){ + var stream; + //Try remote URL. Allow this method to throw, + //but still do cleanup. + try{ + // try it as a file first, URL second + stream = (new java.net.URL(uri)).openStream(); + // close the stream so we don't leak resources + stream.close(); + }finally{ + if(stream && stream.close){ + stream.close(); + } + } + } + return local; } -return _14; + +// see comments in spidermonkey loadUri +dojo._loadUri = function(uri, cb){ + try{ + var local; + try{ + local = dojo._isLocalUrl(uri); + }catch(e){ + // no debug output; this failure just means the uri was not found. + return false; + } + + //FIXME: Use Rhino 1.6 native readFile/readUrl if available? + if(cb){ + var contents = (local ? readText : readUri)(uri, "UTF-8"); + + // patch up the input to eval until https://bugzilla.mozilla.org/show_bug.cgi?id=471005 is fixed. + if(!eval("'\u200f'").length){ + contents = String(contents).replace(/[\u200E\u200F\u202A-\u202E]/g, function(match){ + return "\\u" + match.charCodeAt(0).toString(16); + }) + } + + cb(eval('('+contents+')')); + }else{ + load(uri); + } + return true; + }catch(e){ + console.debug("rhino load('" + uri + "') failed. Exception: " + e); + return false; + } } -catch(e){ -if(_12){ -return null; -}else{ -throw e; + +dojo.exit = function(exitcode){ + quit(exitcode); } + +// reading a file from disk in Java is a humiliating experience by any measure. +// Lets avoid that and just get the freaking text +function readText(path, encoding){ + encoding = encoding || "utf-8"; + // NOTE: we intentionally avoid handling exceptions, since the caller will + // want to know + var jf = new java.io.File(path); + var is = new java.io.FileInputStream(jf); + return dj_readInputStream(is, encoding); } -}; -dojo.doc=typeof document!="undefined"?document:null; -dojo.body=function(){ -return document.body; -}; -if(typeof setTimeout=="undefined"||typeof clearTimeout=="undefined"){ -dojo._timeouts=[]; -clearTimeout=function(idx){ -if(!dojo._timeouts[idx]){ -return; + +function readUri(uri, encoding){ + var conn = (new java.net.URL(uri)).openConnection(); + encoding = encoding || conn.getContentEncoding() || "utf-8"; + var is = conn.getInputStream(); + return dj_readInputStream(is, encoding); } -dojo._timeouts[idx].stop(); -}; -setTimeout=function(_15,_16){ -var def={sleepTime:_16,hasSlept:false,run:function(){ -if(!this.hasSlept){ -this.hasSlept=true; -java.lang.Thread.currentThread().sleep(this.sleepTime); + +function dj_readInputStream(is, encoding){ + var input = new java.io.BufferedReader(new java.io.InputStreamReader(is, encoding)); + try { + var sb = new java.lang.StringBuffer(); + var line = ""; + while((line = input.readLine()) !== null){ + sb.append(line); + sb.append(java.lang.System.getProperty("line.separator")); + } + return sb.toString(); + } finally { + input.close(); + } } -try{ -_15(); + +dojo._getText = function(/*URI*/ uri, /*Boolean*/ fail_ok){ + // summary: Read the contents of the specified uri and return those contents. + // uri: + // A relative or absolute uri. + // fail_ok: + // Default false. If fail_ok and loading fails, return null + // instead of throwing. + // returns: The response text. null is returned when there is a + // failure and failure is okay (an exception otherwise) + try{ + var local = dojo._isLocalUrl(uri); + var text = (local ? readText : readUri)(uri, "UTF-8"); + if(text !== null){ + //Force JavaScript string. + text += ""; + } + return text; + }catch(e){ + if(fail_ok){ + return null; + }else{ + throw e; + } + } } -catch(e){ + +// summary: +// return the document object associated with the dojo.global +dojo.doc = typeof document != "undefined" ? document : null; + +dojo.body = function(){ + return document.body; } -}}; -var _17=new java.lang.Runnable(def); -var _18=new java.lang.Thread(_17); -_18.start(); -return dojo._timeouts.push(_18)-1; -}; + +// Supply setTimeout/clearTimeout implementations if they aren't already there +// Note: this assumes that we define both if one is not provided... there might +// be a better way to do this if there is a use case where one is defined but +// not the other +if(typeof setTimeout == "undefined" || typeof clearTimeout == "undefined"){ + dojo._timeouts = []; + clearTimeout = function(idx){ + if(!dojo._timeouts[idx]){ return; } + dojo._timeouts[idx].stop(); + } + + setTimeout = function(func, delay){ + // summary: provides timed callbacks using Java threads + + var def={ + sleepTime:delay, + hasSlept:false, + + run:function(){ + if(!this.hasSlept){ + this.hasSlept=true; + java.lang.Thread.currentThread().sleep(this.sleepTime); + } + try{ + func(); + }catch(e){ + console.debug("Error running setTimeout thread:" + e); + } + } + }; + + var runnable = new java.lang.Runnable(def); + var thread = new java.lang.Thread(runnable); + thread.start(); + return dojo._timeouts.push(thread)-1; + } } + +//Register any module paths set up in djConfig. Need to do this +//in the hostenvs since hostenv_browser can read djConfig from a +//script tag's attribute. if(dojo.config["modulePaths"]){ -for(var param in dojo.config["modulePaths"]){ -dojo.registerModulePath(param,dojo.config["modulePaths"][param]); -} + for(var param in dojo.config["modulePaths"]){ + dojo.registerModulePath(param, dojo.config["modulePaths"][param]); + } } diff --git a/lib/dojo/_base/_loader/hostenv_spidermonkey.js b/lib/dojo/_base/_loader/hostenv_spidermonkey.js index ca63f16ce..17b21f5f0 100644 --- a/lib/dojo/_base/_loader/hostenv_spidermonkey.js +++ b/lib/dojo/_base/_loader/hostenv_spidermonkey.js @@ -5,46 +5,83 @@ */ +/* + * SpiderMonkey host environment + */ + if(dojo.config["baseUrl"]){ -dojo.baseUrl=dojo.config["baseUrl"]; + dojo.baseUrl = dojo.config["baseUrl"]; }else{ -dojo.baseUrl="./"; + dojo.baseUrl = "./"; } -dojo._name="spidermonkey"; -dojo.isSpidermonkey=true; -dojo.exit=function(_1){ -quit(_1); + +dojo._name = 'spidermonkey'; + +/*===== +dojo.isSpidermonkey = { + // summary: Detect spidermonkey }; -if(typeof print=="function"){ -console.debug=print; -} -if(typeof line2pc=="undefined"){ -throw new Error("attempt to use SpiderMonkey host environment when no 'line2pc' global"); +=====*/ + +dojo.isSpidermonkey = true; +dojo.exit = function(exitcode){ + quit(exitcode); } -dojo._spidermonkeyCurrentFile=function(_2){ -var s=""; -try{ -throw Error("whatever"); + +if(typeof print == "function"){ + console.debug = print; } -catch(e){ -s=e.stack; + +if(typeof line2pc == 'undefined'){ + throw new Error("attempt to use SpiderMonkey host environment when no 'line2pc' global"); } -var _3=s.match(/[^@]*\.js/gi); -if(!_3){ -throw Error("could not parse stack string: '"+s+"'"); + +dojo._spidermonkeyCurrentFile = function(depth){ + // + // This is a hack that determines the current script file by parsing a + // generated stack trace (relying on the non-standard "stack" member variable + // of the SpiderMonkey Error object). + // + // If param depth is passed in, it'll return the script file which is that far down + // the stack, but that does require that you know how deep your stack is when you are + // calling. + // + var s = ''; + try{ + throw Error("whatever"); + }catch(e){ + s = e.stack; + } + // lines are like: bu_getCurrentScriptURI_spidermonkey("ScriptLoader.js")@burst/Runtime.js:101 + var matches = s.match(/[^@]*\.js/gi); + if(!matches){ + throw Error("could not parse stack string: '" + s + "'"); + } + var fname = (typeof depth != 'undefined' && depth) ? matches[depth + 1] : matches[matches.length - 1]; + if(!fname){ + throw Error("could not find file name in stack string '" + s + "'"); + } + //print("SpiderMonkeyRuntime got fname '" + fname + "' from stack string '" + s + "'"); + return fname; } -var _4=(typeof _2!="undefined"&&_2)?_3[_2+1]:_3[_3.length-1]; -if(!_4){ -throw Error("could not find file name in stack string '"+s+"'"); + +// print(dojo._spidermonkeyCurrentFile(0)); + +dojo._loadUri = function(uri){ + // spidermonkey load() evaluates the contents into the global scope (which + // is what we want). + // TODO: sigh, load() does not return a useful value. + // Perhaps it is returning the value of the last thing evaluated? + var ok = load(uri); + // console.log("spidermonkey load(", uri, ") returned ", ok); + return 1; } -return _4; -}; -dojo._loadUri=function(_5){ -var ok=load(_5); -return 1; -}; + +//Register any module paths set up in djConfig. Need to do this +//in the hostenvs since hostenv_browser can read djConfig from a +//script tag's attribute. if(dojo.config["modulePaths"]){ -for(var param in dojo.config["modulePaths"]){ -dojo.registerModulePath(param,dojo.config["modulePaths"][param]); -} + for(var param in dojo.config["modulePaths"]){ + dojo.registerModulePath(param, dojo.config["modulePaths"][param]); + } } diff --git a/lib/dojo/_base/_loader/loader.js b/lib/dojo/_base/_loader/loader.js index 3f31040a1..9206de888 100644 --- a/lib/dojo/_base/_loader/loader.js +++ b/lib/dojo/_base/_loader/loader.js @@ -5,296 +5,800 @@ */ -if(!dojo._hasResource["dojo.foo"]){ -dojo._hasResource["dojo.foo"]=true; +if(!dojo._hasResource["dojo.foo"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo.foo"] = true; +/* + * loader.js - A bootstrap module. Runs before the hostenv_*.js file. Contains + * all of the package loading methods. + */ + (function(){ -var d=dojo; -d.mixin(d,{_loadedModules:{},_inFlightCount:0,_hasResource:{},_modulePrefixes:{dojo:{name:"dojo",value:"."},doh:{name:"doh",value:"../util/doh"},tests:{name:"tests",value:"tests"}},_moduleHasPrefix:function(_1){ -var mp=d._modulePrefixes; -return !!(mp[_1]&&mp[_1].value); -},_getModulePrefix:function(_2){ -var mp=d._modulePrefixes; -if(d._moduleHasPrefix(_2)){ -return mp[_2].value; -} -return _2; -},_loadedUrls:[],_postLoad:false,_loaders:[],_unloaders:[],_loadNotifying:false}); -dojo._loadPath=function(_3,_4,cb){ -var _5=((_3.charAt(0)=="/"||_3.match(/^\w+:/))?"":d.baseUrl)+_3; -try{ -return !_4?d._loadUri(_5,cb):d._loadUriAndCheck(_5,_4,cb); -} -catch(e){ -console.error(e); -return false; -} -}; -dojo._loadUri=function(_6,cb){ -if(d._loadedUrls[_6]){ -return true; -} -d._inFlightCount++; -var _7=d._getText(_6,true); -if(_7){ -d._loadedUrls[_6]=true; -d._loadedUrls.push(_6); -if(cb){ -_7="("+_7+")"; -}else{ -_7=d._scopePrefix+_7+d._scopeSuffix; -} -if(!d.isIE){ -_7+="\r\n//@ sourceURL="+_6; -} -var _8=d["eval"](_7); -if(cb){ -cb(_8); -} -} -if(--d._inFlightCount==0&&d._postLoad&&d._loaders.length){ -setTimeout(function(){ -if(d._inFlightCount==0){ -d._callLoaded(); -} -},0); -} -return !!_7; -}; -dojo._loadUriAndCheck=function(_9,_a,cb){ -var ok=false; -try{ -ok=d._loadUri(_9,cb); -} -catch(e){ -console.error("failed loading "+_9+" with error: "+e); -} -return !!(ok&&d._loadedModules[_a]); -}; -dojo.loaded=function(){ -d._loadNotifying=true; -d._postLoad=true; -var _b=d._loaders; -d._loaders=[]; -for(var x=0;x<_b.length;x++){ -_b[x](); -} -d._loadNotifying=false; -if(d._postLoad&&d._inFlightCount==0&&_b.length){ -d._callLoaded(); -} -}; -dojo.unloaded=function(){ -var _c=d._unloaders; -while(_c.length){ -(_c.pop())(); -} -}; -d._onto=function(_d,_e,fn){ -if(!fn){ -_d.push(_e); -}else{ -if(fn){ -var _f=(typeof fn=="string")?_e[fn]:fn; -_d.push(function(){ -_f.call(_e); -}); -} -} -}; -dojo.ready=dojo.addOnLoad=function(obj,_10){ -d._onto(d._loaders,obj,_10); -if(d._postLoad&&d._inFlightCount==0&&!d._loadNotifying){ -d._callLoaded(); -} -}; -var dca=d.config.addOnLoad; -if(dca){ -d.addOnLoad[(dca instanceof Array?"apply":"call")](d,dca); -} -dojo._modulesLoaded=function(){ -if(d._postLoad){ -return; -} -if(d._inFlightCount>0){ -console.warn("files still in flight!"); -return; -} -d._callLoaded(); -}; -dojo._callLoaded=function(){ -if(typeof setTimeout=="object"||(d.config.useXDomain&&d.isOpera)){ -setTimeout(d.isAIR?function(){ -d.loaded(); -}:d._scopeName+".loaded();",0); -}else{ -d.loaded(); -} -}; -dojo._getModuleSymbols=function(_11){ -var _12=_11.split("."); -for(var i=_12.length;i>0;i--){ -var _13=_12.slice(0,i).join("."); -if(i==1&&!d._moduleHasPrefix(_13)){ -_12[0]="../"+_12[0]; -}else{ -var _14=d._getModulePrefix(_13); -if(_14!=_13){ -_12.splice(0,i,_14); -break; -} -} -} -return _12; -}; -dojo._global_omit_module_check=false; -dojo.loadInit=function(_15){ -_15(); -}; -dojo._loadModule=dojo.require=function(_16,_17){ -_17=d._global_omit_module_check||_17; -var _18=d._loadedModules[_16]; -if(_18){ -return _18; -} -var _19=d._getModuleSymbols(_16).join("/")+".js"; -var _1a=!_17?_16:null; -var ok=d._loadPath(_19,_1a); -if(!ok&&!_17){ -throw new Error("Could not load '"+_16+"'; last tried '"+_19+"'"); -} -if(!_17&&!d._isXDomain){ -_18=d._loadedModules[_16]; -if(!_18){ -throw new Error("symbol '"+_16+"' is not defined after loading '"+_19+"'"); -} -} -return _18; -}; -dojo.provide=function(_1b){ -_1b=_1b+""; -return (d._loadedModules[_1b]=d.getObject(_1b,true)); -}; -dojo.platformRequire=function(_1c){ -var _1d=_1c.common||[]; -var _1e=_1d.concat(_1c[d._name]||_1c["default"]||[]); -for(var x=0;x<_1e.length;x++){ -var _1f=_1e[x]; -if(_1f.constructor==Array){ -d._loadModule.apply(d,_1f); -}else{ -d._loadModule(_1f); -} -} -}; -dojo.requireIf=function(_20,_21){ -if(_20===true){ -var _22=[]; -for(var i=1;i<arguments.length;i++){ -_22.push(arguments[i]); -} -d.require.apply(d,_22); -} -}; -dojo.requireAfterIf=d.requireIf; -dojo.registerModulePath=function(_23,_24){ -d._modulePrefixes[_23]={name:_23,value:_24}; -}; -dojo.requireLocalization=function(_25,_26,_27,_28){ -d.require("dojo.i18n"); -d.i18n._requireLocalization.apply(d.hostenv,arguments); -}; -var ore=new RegExp("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$"),ire=new RegExp("^((([^\\[:]+):)?([^@]+)@)?(\\[([^\\]]+)\\]|([^\\[:]*))(:([0-9]+))?$"); -dojo._Url=function(){ -var n=null,_29=arguments,uri=[_29[0]]; -for(var i=1;i<_29.length;i++){ -if(!_29[i]){ -continue; -} -var _2a=new d._Url(_29[i]+""),_2b=new d._Url(uri[0]+""); -if(_2a.path==""&&!_2a.scheme&&!_2a.authority&&!_2a.query){ -if(_2a.fragment!=n){ -_2b.fragment=_2a.fragment; -} -_2a=_2b; -}else{ -if(!_2a.scheme){ -_2a.scheme=_2b.scheme; -if(!_2a.authority){ -_2a.authority=_2b.authority; -if(_2a.path.charAt(0)!="/"){ -var _2c=_2b.path.substring(0,_2b.path.lastIndexOf("/")+1)+_2a.path; -var _2d=_2c.split("/"); -for(var j=0;j<_2d.length;j++){ -if(_2d[j]=="."){ -if(j==_2d.length-1){ -_2d[j]=""; -}else{ -_2d.splice(j,1); -j--; -} -}else{ -if(j>0&&!(j==1&&_2d[0]=="")&&_2d[j]==".."&&_2d[j-1]!=".."){ -if(j==(_2d.length-1)){ -_2d.splice(j,1); -_2d[j-1]=""; -}else{ -_2d.splice(j-1,2); -j-=2; -} -} -} -} -_2a.path=_2d.join("/"); -} -} -} -} -uri=[]; -if(_2a.scheme){ -uri.push(_2a.scheme,":"); -} -if(_2a.authority){ -uri.push("//",_2a.authority); -} -uri.push(_2a.path); -if(_2a.query){ -uri.push("?",_2a.query); -} -if(_2a.fragment){ -uri.push("#",_2a.fragment); -} -} -this.uri=uri.join(""); -var r=this.uri.match(ore); -this.scheme=r[2]||(r[1]?"":n); -this.authority=r[4]||(r[3]?"":n); -this.path=r[5]; -this.query=r[7]||(r[6]?"":n); -this.fragment=r[9]||(r[8]?"":n); -if(this.authority!=n){ -r=this.authority.match(ire); -this.user=r[3]||n; -this.password=r[4]||n; -this.host=r[6]||r[7]; -this.port=r[9]||n; -} -}; -dojo._Url.prototype.toString=function(){ -return this.uri; -}; -dojo.moduleUrl=function(_2e,url){ -var loc=d._getModuleSymbols(_2e).join("/"); -if(!loc){ -return null; -} -if(loc.lastIndexOf("/")!=loc.length-1){ -loc+="/"; -} -var _2f=loc.indexOf(":"); -if(loc.charAt(0)!="/"&&(_2f==-1||_2f>loc.indexOf("/"))){ -loc=d.baseUrl+loc; -} -return new d._Url(loc,url); -}; + var d = dojo; + + d.mixin(d, { + _loadedModules: {}, + _inFlightCount: 0, + _hasResource: {}, + + _modulePrefixes: { + dojo: { name: "dojo", value: "." }, + // dojox: { name: "dojox", value: "../dojox" }, + // dijit: { name: "dijit", value: "../dijit" }, + doh: { name: "doh", value: "../util/doh" }, + tests: { name: "tests", value: "tests" } + }, + + _moduleHasPrefix: function(/*String*/module){ + // summary: checks to see if module has been established + var mp = d._modulePrefixes; + return !!(mp[module] && mp[module].value); // Boolean + }, + + _getModulePrefix: function(/*String*/module){ + // summary: gets the prefix associated with module + var mp = d._modulePrefixes; + if(d._moduleHasPrefix(module)){ + return mp[module].value; // String + } + return module; // String + }, + + _loadedUrls: [], + + //WARNING: + // This variable is referenced by packages outside of bootstrap: + // FloatingPane.js and undo/browser.js + _postLoad: false, + + //Egad! Lots of test files push on this directly instead of using dojo.addOnLoad. + _loaders: [], + _unloaders: [], + _loadNotifying: false + }); + + + dojo._loadPath = function(/*String*/relpath, /*String?*/module, /*Function?*/cb){ + // summary: + // Load a Javascript module given a relative path + // + // description: + // Loads and interprets the script located at relpath, which is + // relative to the script root directory. If the script is found but + // its interpretation causes a runtime exception, that exception is + // not caught by us, so the caller will see it. We return a true + // value if and only if the script is found. + // + // relpath: + // A relative path to a script (no leading '/', and typically ending + // in '.js'). + // module: + // A module whose existance to check for after loading a path. Can be + // used to determine success or failure of the load. + // cb: + // a callback function to pass the result of evaluating the script + + var uri = ((relpath.charAt(0) == '/' || relpath.match(/^\w+:/)) ? "" : d.baseUrl) + relpath; + try{ + return !module ? d._loadUri(uri, cb) : d._loadUriAndCheck(uri, module, cb); // Boolean + }catch(e){ + console.error(e); + return false; // Boolean + } + } + + dojo._loadUri = function(/*String*/uri, /*Function?*/cb){ + // summary: + // Loads JavaScript from a URI + // description: + // Reads the contents of the URI, and evaluates the contents. This is + // used to load modules as well as resource bundles. Returns true if + // it succeeded. Returns false if the URI reading failed. Throws if + // the evaluation throws. + // uri: a uri which points at the script to be loaded + // cb: + // a callback function to process the result of evaluating the script + // as an expression, typically used by the resource bundle loader to + // load JSON-style resources + + if(d._loadedUrls[uri]){ + return true; // Boolean + } + d._inFlightCount++; // block addOnLoad calls that arrive while we're busy downloading + var contents = d._getText(uri, true); + if(contents){ // not 404, et al + d._loadedUrls[uri] = true; + d._loadedUrls.push(uri); + if(cb){ + contents = '('+contents+')'; + }else{ + //Only do the scoping if no callback. If a callback is specified, + //it is most likely the i18n bundle stuff. + contents = d._scopePrefix + contents + d._scopeSuffix; + } + if(!d.isIE){ contents += "\r\n//@ sourceURL=" + uri; } // debugging assist for Firebug + var value = d["eval"](contents); + if(cb){ cb(value); } + } + // Check to see if we need to call _callLoaded() due to an addOnLoad() that arrived while we were busy downloading + if(--d._inFlightCount == 0 && d._postLoad && d._loaders.length){ + // We shouldn't be allowed to get here but Firefox allows an event + // (mouse, keybd, async xhrGet) to interrupt a synchronous xhrGet. + // If the current script block contains multiple require() statements, then after each + // require() returns, inFlightCount == 0, but we want to hold the _callLoaded() until + // all require()s are done since the out-of-sequence addOnLoad() presumably needs them all. + // setTimeout allows the next require() to start (if needed), and then we check this again. + setTimeout(function(){ + // If inFlightCount > 0, then multiple require()s are running sequentially and + // the next require() started after setTimeout() was executed but before we got here. + if(d._inFlightCount == 0){ + d._callLoaded(); + } + }, 0); + } + return !!contents; // Boolean: contents? true : false + } + + // FIXME: probably need to add logging to this method + dojo._loadUriAndCheck = function(/*String*/uri, /*String*/moduleName, /*Function?*/cb){ + // summary: calls loadUri then findModule and returns true if both succeed + var ok = false; + try{ + ok = d._loadUri(uri, cb); + }catch(e){ + console.error("failed loading " + uri + " with error: " + e); + } + return !!(ok && d._loadedModules[moduleName]); // Boolean + } + + dojo.loaded = function(){ + // summary: + // signal fired when initial environment and package loading is + // complete. You should use dojo.addOnLoad() instead of doing a + // direct dojo.connect() to this method in order to handle + // initialization tasks that require the environment to be + // initialized. In a browser host, declarative widgets will + // be constructed when this function finishes runing. + d._loadNotifying = true; + d._postLoad = true; + var mll = d._loaders; + + //Clear listeners so new ones can be added + //For other xdomain package loads after the initial load. + d._loaders = []; + + for(var x = 0; x < mll.length; x++){ + mll[x](); + } + + d._loadNotifying = false; + + //Make sure nothing else got added to the onload queue + //after this first run. If something did, and we are not waiting for any + //more inflight resources, run again. + if(d._postLoad && d._inFlightCount == 0 && mll.length){ + d._callLoaded(); + } + } + + dojo.unloaded = function(){ + // summary: + // signal fired by impending environment destruction. You should use + // dojo.addOnUnload() instead of doing a direct dojo.connect() to this + // method to perform page/application cleanup methods. See + // dojo.addOnUnload for more info. + var mll = d._unloaders; + while(mll.length){ + (mll.pop())(); + } + } + + d._onto = function(arr, obj, fn){ + if(!fn){ + arr.push(obj); + }else if(fn){ + var func = (typeof fn == "string") ? obj[fn] : fn; + arr.push(function(){ func.call(obj); }); + } + } + + dojo.ready = dojo.addOnLoad = function(/*Object*/obj, /*String|Function?*/functionName){ + // summary: + // Registers a function to be triggered after the DOM and dojo.require() calls + // have finished loading. + // + // description: + // Registers a function to be triggered after the DOM has finished + // loading and `dojo.require` modules have loaded. Widgets declared in markup + // have been instantiated if `djConfig.parseOnLoad` is true when this fires. + // + // Images and CSS files may or may not have finished downloading when + // the specified function is called. (Note that widgets' CSS and HTML + // code is guaranteed to be downloaded before said widgets are + // instantiated, though including css resouces BEFORE any script elements + // is highly recommended). + // + // example: + // Register an anonymous function to run when everything is ready + // | dojo.addOnLoad(function(){ doStuff(); }); + // + // example: + // Register a function to run when everything is ready by pointer: + // | var init = function(){ doStuff(); } + // | dojo.addOnLoad(init); + // + // example: + // Register a function to run scoped to `object`, either by name or anonymously: + // | dojo.addOnLoad(object, "functionName"); + // | dojo.addOnLoad(object, function(){ doStuff(); }); + + d._onto(d._loaders, obj, functionName); + + //Added for xdomain loading. dojo.addOnLoad is used to + //indicate callbacks after doing some dojo.require() statements. + //In the xdomain case, if all the requires are loaded (after initial + //page load), then immediately call any listeners. + if(d._postLoad && d._inFlightCount == 0 && !d._loadNotifying){ + d._callLoaded(); + } + } + + //Support calling dojo.addOnLoad via djConfig.addOnLoad. Support all the + //call permutations of dojo.addOnLoad. Mainly useful when dojo is added + //to the page after the page has loaded. + var dca = d.config.addOnLoad; + if(dca){ + d.addOnLoad[(dca instanceof Array ? "apply" : "call")](d, dca); + } + + dojo._modulesLoaded = function(){ + if(d._postLoad){ return; } + if(d._inFlightCount > 0){ + console.warn("files still in flight!"); + return; + } + d._callLoaded(); + } + + dojo._callLoaded = function(){ + + // The "object" check is for IE, and the other opera check fixes an + // issue in Opera where it could not find the body element in some + // widget test cases. For 0.9, maybe route all browsers through the + // setTimeout (need protection still for non-browser environments + // though). This might also help the issue with FF 2.0 and freezing + // issues where we try to do sync xhr while background css images are + // being loaded (trac #2572)? Consider for 0.9. + if(typeof setTimeout == "object" || (d.config.useXDomain && d.isOpera)){ + setTimeout( + d.isAIR ? function(){ d.loaded(); } : d._scopeName + ".loaded();", + 0); + }else{ + d.loaded(); + } + } + + dojo._getModuleSymbols = function(/*String*/modulename){ + // summary: + // Converts a module name in dotted JS notation to an array + // representing the path in the source tree + var syms = modulename.split("."); + for(var i = syms.length; i>0; i--){ + var parentModule = syms.slice(0, i).join("."); + if(i == 1 && !d._moduleHasPrefix(parentModule)){ + // Support default module directory (sibling of dojo) for top-level modules + syms[0] = "../" + syms[0]; + }else{ + var parentModulePath = d._getModulePrefix(parentModule); + if(parentModulePath != parentModule){ + syms.splice(0, i, parentModulePath); + break; + } + } + } + return syms; // Array + } + + dojo._global_omit_module_check = false; + + dojo.loadInit = function(/*Function*/init){ + // summary: + // Executes a function that needs to be executed for the loader's dojo.requireIf + // resolutions to work. This is needed mostly for the xdomain loader case where + // a function needs to be executed to set up the possible values for a dojo.requireIf + // call. + // init: + // a function reference. Executed immediately. + // description: This function is mainly a marker for the xdomain loader to know parts of + // code that needs be executed outside the function wrappper that is placed around modules. + // The init function could be executed more than once, and it should make no assumptions + // on what is loaded, or what modules are available. Only the functionality in Dojo Base + // is allowed to be used. Avoid using this method. For a valid use case, + // see the source for dojox.gfx. + init(); + } + + dojo._loadModule = dojo.require = function(/*String*/moduleName, /*Boolean?*/omitModuleCheck){ + // summary: + // loads a Javascript module from the appropriate URI + // moduleName: + // module name to load, using periods for separators, + // e.g. "dojo.date.locale". Module paths are de-referenced by dojo's + // internal mapping of locations to names and are disambiguated by + // longest prefix. See `dojo.registerModulePath()` for details on + // registering new modules. + // omitModuleCheck: + // if `true`, omitModuleCheck skips the step of ensuring that the + // loaded file actually defines the symbol it is referenced by. + // For example if it called as `dojo.require("a.b.c")` and the + // file located at `a/b/c.js` does not define an object `a.b.c`, + // and exception will be throws whereas no exception is raised + // when called as `dojo.require("a.b.c", true)` + // description: + // Modules are loaded via dojo.require by using one of two loaders: the normal loader + // and the xdomain loader. The xdomain loader is used when dojo was built with a + // custom build that specified loader=xdomain and the module lives on a modulePath + // that is a whole URL, with protocol and a domain. The versions of Dojo that are on + // the Google and AOL CDNs use the xdomain loader. + // + // If the module is loaded via the xdomain loader, it is an asynchronous load, since + // the module is added via a dynamically created script tag. This + // means that dojo.require() can return before the module has loaded. However, this + // should only happen in the case where you do dojo.require calls in the top-level + // HTML page, or if you purposely avoid the loader checking for dojo.require + // dependencies in your module by using a syntax like dojo["require"] to load the module. + // + // Sometimes it is useful to not have the loader detect the dojo.require calls in the + // module so that you can dynamically load the modules as a result of an action on the + // page, instead of right at module load time. + // + // Also, for script blocks in an HTML page, the loader does not pre-process them, so + // it does not know to download the modules before the dojo.require calls occur. + // + // So, in those two cases, when you want on-the-fly module loading or for script blocks + // in the HTML page, special care must be taken if the dojo.required code is loaded + // asynchronously. To make sure you can execute code that depends on the dojo.required + // modules, be sure to add the code that depends on the modules in a dojo.addOnLoad() + // callback. dojo.addOnLoad waits for all outstanding modules to finish loading before + // executing. Example: + // + // | <script type="text/javascript"> + // | dojo.require("foo"); + // | dojo.require("bar"); + // | dojo.addOnLoad(function(){ + // | //you can now safely do something with foo and bar + // | }); + // | </script> + // + // This type of syntax works with both xdomain and normal loaders, so it is good + // practice to always use this idiom for on-the-fly code loading and in HTML script + // blocks. If at some point you change loaders and where the code is loaded from, + // it will all still work. + // + // More on how dojo.require + // `dojo.require("A.B")` first checks to see if symbol A.B is + // defined. If it is, it is simply returned (nothing to do). + // + // If it is not defined, it will look for `A/B.js` in the script root + // directory. + // + // `dojo.require` throws an excpetion if it cannot find a file + // to load, or if the symbol `A.B` is not defined after loading. + // + // It returns the object `A.B`, but note the caveats above about on-the-fly loading and + // HTML script blocks when the xdomain loader is loading a module. + // + // `dojo.require()` does nothing about importing symbols into + // the current namespace. It is presumed that the caller will + // take care of that. For example, to import all symbols into a + // local block, you might write: + // + // | with (dojo.require("A.B")) { + // | ... + // | } + // + // And to import just the leaf symbol to a local variable: + // + // | var B = dojo.require("A.B"); + // | ... + // returns: the required namespace object + omitModuleCheck = d._global_omit_module_check || omitModuleCheck; + + //Check if it is already loaded. + var module = d._loadedModules[moduleName]; + if(module){ + return module; + } + + // convert periods to slashes + var relpath = d._getModuleSymbols(moduleName).join("/") + '.js'; + + var modArg = !omitModuleCheck ? moduleName : null; + var ok = d._loadPath(relpath, modArg); + + if(!ok && !omitModuleCheck){ + throw new Error("Could not load '" + moduleName + "'; last tried '" + relpath + "'"); + } + + // check that the symbol was defined + // Don't bother if we're doing xdomain (asynchronous) loading. + if(!omitModuleCheck && !d._isXDomain){ + // pass in false so we can give better error + module = d._loadedModules[moduleName]; + if(!module){ + throw new Error("symbol '" + moduleName + "' is not defined after loading '" + relpath + "'"); + } + } + + return module; + } + + dojo.provide = function(/*String*/ resourceName){ + // summary: + // Register a resource with the package system. Works in conjunction with `dojo.require` + // + // description: + // Each javascript source file is called a resource. When a + // resource is loaded by the browser, `dojo.provide()` registers + // that it has been loaded. + // + // Each javascript source file must have at least one + // `dojo.provide()` call at the top of the file, corresponding to + // the file name. For example, `js/dojo/foo.js` must have + // `dojo.provide("dojo.foo");` before any calls to + // `dojo.require()` are made. + // + // For backwards compatibility reasons, in addition to registering + // the resource, `dojo.provide()` also ensures that the javascript + // object for the module exists. For example, + // `dojo.provide("dojox.data.FlickrStore")`, in addition to + // registering that `FlickrStore.js` is a resource for the + // `dojox.data` module, will ensure that the `dojox.data` + // javascript object exists, so that calls like + // `dojo.data.foo = function(){ ... }` don't fail. + // + // In the case of a build where multiple javascript source files + // are combined into one bigger file (similar to a .lib or .jar + // file), that file may contain multiple dojo.provide() calls, to + // note that it includes multiple resources. + // + // resourceName: String + // A dot-sperated string identifying a resource. + // + // example: + // Safely create a `my` object, and make dojo.require("my.CustomModule") work + // | dojo.provide("my.CustomModule"); + + //Make sure we have a string. + resourceName = resourceName + ""; + return (d._loadedModules[resourceName] = d.getObject(resourceName, true)); // Object + } + + //Start of old bootstrap2: + + dojo.platformRequire = function(/*Object*/modMap){ + // summary: + // require one or more modules based on which host environment + // Dojo is currently operating in + // description: + // This method takes a "map" of arrays which one can use to + // optionally load dojo modules. The map is indexed by the + // possible dojo.name_ values, with two additional values: + // "default" and "common". The items in the "default" array will + // be loaded if none of the other items have been choosen based on + // dojo.name_, set by your host environment. The items in the + // "common" array will *always* be loaded, regardless of which + // list is chosen. + // example: + // | dojo.platformRequire({ + // | browser: [ + // | "foo.sample", // simple module + // | "foo.test", + // | ["foo.bar.baz", true] // skip object check in _loadModule (dojo.require) + // | ], + // | default: [ "foo.sample._base" ], + // | common: [ "important.module.common" ] + // | }); + + var common = modMap.common || []; + var result = common.concat(modMap[d._name] || modMap["default"] || []); + + for(var x=0; x<result.length; x++){ + var curr = result[x]; + if(curr.constructor == Array){ + d._loadModule.apply(d, curr); + }else{ + d._loadModule(curr); + } + } + } + + dojo.requireIf = function(/*Boolean*/ condition, /*String*/ resourceName){ + // summary: + // If the condition is true then call `dojo.require()` for the specified + // resource + // + // example: + // | dojo.requireIf(dojo.isBrowser, "my.special.Module"); + + if(condition === true){ + // FIXME: why do we support chained require()'s here? does the build system? + var args = []; + for(var i = 1; i < arguments.length; i++){ + args.push(arguments[i]); + } + d.require.apply(d, args); + } + } + + dojo.requireAfterIf = d.requireIf; + + dojo.registerModulePath = function(/*String*/module, /*String*/prefix){ + // summary: + // Maps a module name to a path + // description: + // An unregistered module is given the default path of ../[module], + // relative to Dojo root. For example, module acme is mapped to + // ../acme. If you want to use a different module name, use + // dojo.registerModulePath. + // example: + // If your dojo.js is located at this location in the web root: + // | /myapp/js/dojo/dojo/dojo.js + // and your modules are located at: + // | /myapp/js/foo/bar.js + // | /myapp/js/foo/baz.js + // | /myapp/js/foo/thud/xyzzy.js + // Your application can tell Dojo to locate the "foo" namespace by calling: + // | dojo.registerModulePath("foo", "../../foo"); + // At which point you can then use dojo.require() to load the + // modules (assuming they provide() the same things which are + // required). The full code might be: + // | <script type="text/javascript" + // | src="/myapp/js/dojo/dojo/dojo.js"></script> + // | <script type="text/javascript"> + // | dojo.registerModulePath("foo", "../../foo"); + // | dojo.require("foo.bar"); + // | dojo.require("foo.baz"); + // | dojo.require("foo.thud.xyzzy"); + // | </script> + d._modulePrefixes[module] = { name: module, value: prefix }; + } + + dojo.requireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale, /*String?*/availableFlatLocales){ + // summary: + // Declares translated resources and loads them if necessary, in the + // same style as dojo.require. Contents of the resource bundle are + // typically strings, but may be any name/value pair, represented in + // JSON format. See also `dojo.i18n.getLocalization`. + // + // description: + // Load translated resource bundles provided underneath the "nls" + // directory within a package. Translated resources may be located in + // different packages throughout the source tree. + // + // Each directory is named for a locale as specified by RFC 3066, + // (http://www.ietf.org/rfc/rfc3066.txt), normalized in lowercase. + // Note that the two bundles in the example do not define all the + // same variants. For a given locale, bundles will be loaded for + // that locale and all more general locales above it, including a + // fallback at the root directory. For example, a declaration for + // the "de-at" locale will first load `nls/de-at/bundleone.js`, + // then `nls/de/bundleone.js` and finally `nls/bundleone.js`. The + // data will be flattened into a single Object so that lookups + // will follow this cascading pattern. An optional build step can + // preload the bundles to avoid data redundancy and the multiple + // network hits normally required to load these resources. + // + // moduleName: + // name of the package containing the "nls" directory in which the + // bundle is found + // + // bundleName: + // bundle name, i.e. the filename without the '.js' suffix. Using "nls" as a + // a bundle name is not supported, since "nls" is the name of the folder + // that holds bundles. Using "nls" as the bundle name will cause problems + // with the custom build. + // + // locale: + // the locale to load (optional) By default, the browser's user + // locale as defined by dojo.locale + // + // availableFlatLocales: + // A comma-separated list of the available, flattened locales for this + // bundle. This argument should only be set by the build process. + // + // example: + // A particular widget may define one or more resource bundles, + // structured in a program as follows, where moduleName is + // mycode.mywidget and bundleNames available include bundleone and + // bundletwo: + // | ... + // | mycode/ + // | mywidget/ + // | nls/ + // | bundleone.js (the fallback translation, English in this example) + // | bundletwo.js (also a fallback translation) + // | de/ + // | bundleone.js + // | bundletwo.js + // | de-at/ + // | bundleone.js + // | en/ + // | (empty; use the fallback translation) + // | en-us/ + // | bundleone.js + // | en-gb/ + // | bundleone.js + // | es/ + // | bundleone.js + // | bundletwo.js + // | ...etc + // | ... + // + + d.require("dojo.i18n"); + d.i18n._requireLocalization.apply(d.hostenv, arguments); + }; + + + var ore = new RegExp("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$"), + ire = new RegExp("^((([^\\[:]+):)?([^@]+)@)?(\\[([^\\]]+)\\]|([^\\[:]*))(:([0-9]+))?$"); + + dojo._Url = function(/*dojo._Url|String...*/){ + // summary: + // Constructor to create an object representing a URL. + // It is marked as private, since we might consider removing + // or simplifying it. + // description: + // Each argument is evaluated in order relative to the next until + // a canonical uri is produced. To get an absolute Uri relative to + // the current document use: + // new dojo._Url(document.baseURI, url) + + var n = null, + _a = arguments, + uri = [_a[0]]; + // resolve uri components relative to each other + for(var i = 1; i<_a.length; i++){ + if(!_a[i]){ continue; } + + // Safari doesn't support this.constructor so we have to be explicit + // FIXME: Tracked (and fixed) in Webkit bug 3537. + // http://bugs.webkit.org/show_bug.cgi?id=3537 + var relobj = new d._Url(_a[i]+""), + uriobj = new d._Url(uri[0]+""); + + if( + relobj.path == "" && + !relobj.scheme && + !relobj.authority && + !relobj.query + ){ + if(relobj.fragment != n){ + uriobj.fragment = relobj.fragment; + } + relobj = uriobj; + }else if(!relobj.scheme){ + relobj.scheme = uriobj.scheme; + + if(!relobj.authority){ + relobj.authority = uriobj.authority; + + if(relobj.path.charAt(0) != "/"){ + var path = uriobj.path.substring(0, + uriobj.path.lastIndexOf("/") + 1) + relobj.path; + + var segs = path.split("/"); + for(var j = 0; j < segs.length; j++){ + if(segs[j] == "."){ + // flatten "./" references + if(j == segs.length - 1){ + segs[j] = ""; + }else{ + segs.splice(j, 1); + j--; + } + }else if(j > 0 && !(j == 1 && segs[0] == "") && + segs[j] == ".." && segs[j-1] != ".."){ + // flatten "../" references + if(j == (segs.length - 1)){ + segs.splice(j, 1); + segs[j - 1] = ""; + }else{ + segs.splice(j - 1, 2); + j -= 2; + } + } + } + relobj.path = segs.join("/"); + } + } + } + + uri = []; + if(relobj.scheme){ + uri.push(relobj.scheme, ":"); + } + if(relobj.authority){ + uri.push("//", relobj.authority); + } + uri.push(relobj.path); + if(relobj.query){ + uri.push("?", relobj.query); + } + if(relobj.fragment){ + uri.push("#", relobj.fragment); + } + } + + this.uri = uri.join(""); + + // break the uri into its main components + var r = this.uri.match(ore); + + this.scheme = r[2] || (r[1] ? "" : n); + this.authority = r[4] || (r[3] ? "" : n); + this.path = r[5]; // can never be undefined + this.query = r[7] || (r[6] ? "" : n); + this.fragment = r[9] || (r[8] ? "" : n); + + if(this.authority != n){ + // server based naming authority + r = this.authority.match(ire); + + this.user = r[3] || n; + this.password = r[4] || n; + this.host = r[6] || r[7]; // ipv6 || ipv4 + this.port = r[9] || n; + } + } + + dojo._Url.prototype.toString = function(){ return this.uri; }; + + dojo.moduleUrl = function(/*String*/module, /*dojo._Url||String*/url){ + // summary: + // Returns a `dojo._Url` object relative to a module. + // example: + // | var pngPath = dojo.moduleUrl("acme","images/small.png"); + // | console.dir(pngPath); // list the object properties + // | // create an image and set it's source to pngPath's value: + // | var img = document.createElement("img"); + // | // NOTE: we assign the string representation of the url object + // | img.src = pngPath.toString(); + // | // add our image to the document + // | dojo.body().appendChild(img); + // example: + // you may de-reference as far as you like down the package + // hierarchy. This is sometimes handy to avoid lenghty relative + // urls or for building portable sub-packages. In this example, + // the `acme.widget` and `acme.util` directories may be located + // under different roots (see `dojo.registerModulePath`) but the + // the modules which reference them can be unaware of their + // relative locations on the filesystem: + // | // somewhere in a configuration block + // | dojo.registerModulePath("acme.widget", "../../acme/widget"); + // | dojo.registerModulePath("acme.util", "../../util"); + // | + // | // ... + // | + // | // code in a module using acme resources + // | var tmpltPath = dojo.moduleUrl("acme.widget","templates/template.html"); + // | var dataPath = dojo.moduleUrl("acme.util","resources/data.json"); + + var loc = d._getModuleSymbols(module).join('/'); + if(!loc){ return null; } + if(loc.lastIndexOf("/") != loc.length-1){ + loc += "/"; + } + + //If the path is an absolute path (starts with a / or is on another + //domain/xdomain) then don't add the baseUrl. + var colonIndex = loc.indexOf(":"); + if(loc.charAt(0) != "/" && (colonIndex == -1 || colonIndex > loc.indexOf("/"))){ + loc = d.baseUrl + loc; + } + + return new d._Url(loc, url); // dojo._Url + } })(); + } diff --git a/lib/dojo/_base/_loader/loader_debug.js b/lib/dojo/_base/_loader/loader_debug.js index a28040f58..fa26d8efb 100644 --- a/lib/dojo/_base/_loader/loader_debug.js +++ b/lib/dojo/_base/_loader/loader_debug.js @@ -5,55 +5,82 @@ */ -if(!dojo._hasResource["dojo._base._loader.loader_debug"]){ -dojo._hasResource["dojo._base._loader.loader_debug"]=true; +if(!dojo._hasResource["dojo._base._loader.loader_debug"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo._base._loader.loader_debug"] = true; dojo.provide("dojo._base._loader.loader_debug"); -dojo.nonDebugProvide=dojo.provide; -dojo.provide=function(_1){ -var _2=dojo["_xdDebugQueue"]; -if(_2&&_2.length>0&&_1==_2["currentResourceName"]){ -if(dojo.isAIR){ -window.setTimeout(function(){ -dojo._xdDebugFileLoaded(_1); -},1); -}else{ -window.setTimeout(dojo._scopeName+"._xdDebugFileLoaded('"+_1+"')",1); -} -} -return dojo.nonDebugProvide.apply(dojo,arguments); -}; -dojo._xdDebugFileLoaded=function(_3){ -if(!dojo._xdDebugScopeChecked){ -if(dojo._scopeName!="dojo"){ -window.dojo=window[dojo.config.scopeMap[0][1]]; -window.dijit=window[dojo.config.scopeMap[1][1]]; -window.dojox=window[dojo.config.scopeMap[2][1]]; -} -dojo._xdDebugScopeChecked=true; -} -var _4=dojo._xdDebugQueue; -if(_3&&_3==_4.currentResourceName){ -_4.shift(); -} -if(_4.length==0){ -dojo._xdWatchInFlight(); -} -if(_4.length==0){ -_4.currentResourceName=null; -for(var _5 in dojo._xdInFlight){ -if(dojo._xdInFlight[_5]===true){ -return; -} -} -dojo._xdNotifyLoaded(); -}else{ -if(_3==_4.currentResourceName){ -_4.currentResourceName=_4[0].resourceName; -var _6=document.createElement("script"); -_6.type="text/javascript"; -_6.src=_4[0].resourcePath; -document.getElementsByTagName("head")[0].appendChild(_6); + +//Override dojo.provide, so we can trigger the next +//script tag for the next local module. We can only add one +//at a time because there are browsers that execute script tags +//in the order that the code is received, and not in the DOM order. +dojo.nonDebugProvide = dojo.provide; + +dojo.provide = function(resourceName){ + var dbgQueue = dojo["_xdDebugQueue"]; + if(dbgQueue && dbgQueue.length > 0 && resourceName == dbgQueue["currentResourceName"]){ + //Set a timeout so the module can be executed into existence. Normally the + //dojo.provide call in a module is the first line. Don't want to risk attaching + //another script tag until the current one finishes executing. + if(dojo.isAIR){ + window.setTimeout(function(){dojo._xdDebugFileLoaded(resourceName);}, 1); + }else{ + window.setTimeout(dojo._scopeName + "._xdDebugFileLoaded('" + resourceName + "')", 1); + } + } + + return dojo.nonDebugProvide.apply(dojo, arguments); } + +dojo._xdDebugFileLoaded = function(resourceName){ + + if(!dojo._xdDebugScopeChecked){ + //If using a scoped dojo, we need to expose dojo as a real global + //for the debugAtAllCosts stuff to work. + if(dojo._scopeName != "dojo"){ + window.dojo = window[dojo.config.scopeMap[0][1]]; + window.dijit = window[dojo.config.scopeMap[1][1]]; + window.dojox = window[dojo.config.scopeMap[2][1]]; + } + + dojo._xdDebugScopeChecked = true; + } + + var dbgQueue = dojo._xdDebugQueue; + + if(resourceName && resourceName == dbgQueue.currentResourceName){ + dbgQueue.shift(); + } + + if(dbgQueue.length == 0){ + //Check for more modules that need debug loading. + //dojo._xdWatchInFlight will add more things to the debug + //queue if they just recently loaded but it was not detected + //between the dojo._xdWatchInFlight intervals. + dojo._xdWatchInFlight(); + } + + if(dbgQueue.length == 0){ + dbgQueue.currentResourceName = null; + + //Make sure nothing else is in flight. + //If something is still in flight, then it still + //needs to be added to debug queue after it loads. + for(var param in dojo._xdInFlight){ + if(dojo._xdInFlight[param] === true){ + return; + } + } + + dojo._xdNotifyLoaded(); + }else{ + if(resourceName == dbgQueue.currentResourceName){ + dbgQueue.currentResourceName = dbgQueue[0].resourceName; + var element = document.createElement("script"); + element.type = "text/javascript"; + element.src = dbgQueue[0].resourcePath; + document.getElementsByTagName("head")[0].appendChild(element); + } + } } -}; + } diff --git a/lib/dojo/_base/_loader/loader_xd.js b/lib/dojo/_base/_loader/loader_xd.js index 2ecab3db6..c60b86b05 100644 --- a/lib/dojo/_base/_loader/loader_xd.js +++ b/lib/dojo/_base/_loader/loader_xd.js @@ -5,461 +5,719 @@ */ -if(!dojo._hasResource["dojo._base._loader.loader_xd"]){ -dojo._hasResource["dojo._base._loader.loader_xd"]=true; +if(!dojo._hasResource["dojo._base._loader.loader_xd"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo._base._loader.loader_xd"] = true; +//Cross-domain resource loader. dojo.provide("dojo._base._loader.loader_xd"); -dojo._xdReset=function(){ -dojo._isXDomain=dojo.config.useXDomain||false; -dojo._xdClearInterval(); -dojo._xdInFlight={}; -dojo._xdOrderedReqs=[]; -dojo._xdDepMap={}; -dojo._xdContents=[]; -dojo._xdDefList=[]; -}; -dojo._xdClearInterval=function(){ -if(dojo._xdTimer){ -clearInterval(dojo._xdTimer); -dojo._xdTimer=0; -} -}; -dojo._xdReset(); -dojo._xdCreateResource=function(_1,_2,_3){ -var _4=_1.replace(/(\/\*([\s\S]*?)\*\/|\/\/(.*)$)/mg,""); -var _5=[]; -var _6=/dojo.(require|requireIf|provide|requireAfterIf|platformRequire|requireLocalization)\s*\(([\w\W]*?)\)/mg; -var _7; -while((_7=_6.exec(_4))!=null){ -if(_7[1]=="requireLocalization"){ -eval(_7[0]); -}else{ -_5.push("\""+_7[1]+"\", "+_7[2]); -} -} -var _8=[]; -_8.push(dojo._scopeName+"._xdResourceLoaded(function("+dojo._scopePrefixArgs+"){\n"); -var _9=dojo._xdExtractLoadInits(_1); -if(_9){ -_1=_9[0]; -for(var i=1;i<_9.length;i++){ -_8.push(_9[i]+";\n"); -} -} -_8.push("return {"); -if(_5.length>0){ -_8.push("depends: ["); -for(i=0;i<_5.length;i++){ -if(i>0){ -_8.push(",\n"); -} -_8.push("["+_5[i]+"]"); -} -_8.push("],"); -} -_8.push("\ndefineResource: function("+dojo._scopePrefixArgs+"){"); -if(!dojo.config["debugAtAllCosts"]||_2=="dojo._base._loader.loader_debug"){ -_8.push(_1); -} -_8.push("\n}, resourceName: '"+_2+"', resourcePath: '"+_3+"'};});"); -return _8.join(""); -}; -dojo._xdExtractLoadInits=function(_a){ -var _b=/dojo.loadInit\s*\(/g; -_b.lastIndex=0; -var _c=/[\(\)]/g; -_c.lastIndex=0; -var _d=[]; -var _e; -while((_e=_b.exec(_a))){ -_c.lastIndex=_b.lastIndex; -var _f=1; -var _10; -while((_10=_c.exec(_a))){ -if(_10[0]==")"){ -_f-=1; -}else{ -_f+=1; -} -if(_f==0){ -break; -} -} -if(_f!=0){ -throw "unmatched paren around character "+_c.lastIndex+" in: "+_a; -} -var _11=_b.lastIndex-_e[0].length; -_d.push(_a.substring(_11,_c.lastIndex)); -var _12=_c.lastIndex-_11; -_a=_a.substring(0,_11)+_a.substring(_c.lastIndex,_a.length); -_b.lastIndex=_c.lastIndex-_12; -_b.lastIndex=_c.lastIndex; -} -if(_d.length>0){ -_d.unshift(_a); -} -return (_d.length?_d:null); -}; -dojo._xdIsXDomainPath=function(_13){ -var _14=_13.indexOf(":"); -var _15=_13.indexOf("/"); -if(_14>0&&_14<_15){ -return true; -}else{ -var url=dojo.baseUrl; -_14=url.indexOf(":"); -_15=url.indexOf("/"); -if(_14>0&&_14<_15&&(!location.host||url.indexOf("http://"+location.host)!=0)){ -return true; -} -} -return false; -}; -dojo._loadPath=function(_16,_17,cb){ -var _18=dojo._xdIsXDomainPath(_16); -dojo._isXDomain|=_18; -var uri=((_16.charAt(0)=="/"||_16.match(/^\w+:/))?"":dojo.baseUrl)+_16; -try{ -return ((!_17||dojo._isXDomain)?dojo._loadUri(uri,cb,_18,_17):dojo._loadUriAndCheck(uri,_17,cb)); -} -catch(e){ -console.error(e); -return false; -} -}; -dojo._xdCharSet="utf-8"; -dojo._loadUri=function(uri,cb,_19,_1a){ -if(dojo._loadedUrls[uri]){ -return 1; -} -if(dojo._isXDomain&&_1a&&_1a!="dojo.i18n"){ -dojo._xdOrderedReqs.push(_1a); -if(_19||uri.indexOf("/nls/")==-1){ -dojo._xdInFlight[_1a]=true; -dojo._inFlightCount++; -} -if(!dojo._xdTimer){ -if(dojo.isAIR){ -dojo._xdTimer=setInterval(function(){ -dojo._xdWatchInFlight(); -},100); -}else{ -dojo._xdTimer=setInterval(dojo._scopeName+"._xdWatchInFlight();",100); -} -} -dojo._xdStartTime=(new Date()).getTime(); -} -if(_19){ -var _1b=uri.lastIndexOf("."); -if(_1b<=0){ -_1b=uri.length-1; -} -var _1c=uri.substring(0,_1b)+".xd"; -if(_1b!=uri.length-1){ -_1c+=uri.substring(_1b,uri.length); -} -if(dojo.isAIR){ -_1c=_1c.replace("app:/","/"); -} -var _1d=document.createElement("script"); -_1d.type="text/javascript"; -if(dojo._xdCharSet){ -_1d.charset=dojo._xdCharSet; -} -_1d.src=_1c; -if(!dojo.headElement){ -dojo._headElement=document.getElementsByTagName("head")[0]; -if(!dojo._headElement){ -dojo._headElement=document.getElementsByTagName("html")[0]; -} -} -dojo._headElement.appendChild(_1d); -}else{ -var _1e=dojo._getText(uri,null,true); -if(_1e==null){ -return 0; -} -if(dojo._isXDomain&&uri.indexOf("/nls/")==-1&&_1a!="dojo.i18n"){ -var res=dojo._xdCreateResource(_1e,_1a,uri); -dojo.eval(res); -}else{ -if(cb){ -_1e="("+_1e+")"; -}else{ -_1e=dojo._scopePrefix+_1e+dojo._scopeSuffix; -} -var _1f=dojo["eval"](_1e+"\r\n//@ sourceURL="+uri); -if(cb){ -cb(_1f); -} -} -} -dojo._loadedUrls[uri]=true; -dojo._loadedUrls.push(uri); -return true; -}; -dojo._xdResourceLoaded=function(res){ -res=res.apply(dojo.global,dojo._scopeArgs); -var _20=res.depends; -var _21=null; -var _22=null; -var _23=[]; -if(_20&&_20.length>0){ -var dep=null; -var _24=0; -var _25=false; -for(var i=0;i<_20.length;i++){ -dep=_20[i]; -if(dep[0]=="provide"){ -_23.push(dep[1]); -}else{ -if(!_21){ -_21=[]; -} -if(!_22){ -_22=[]; -} -var _26=dojo._xdUnpackDependency(dep); -if(_26.requires){ -_21=_21.concat(_26.requires); -} -if(_26.requiresAfter){ -_22=_22.concat(_26.requiresAfter); -} -} -var _27=dep[0]; -var _28=_27.split("."); -if(_28.length==2){ -dojo[_28[0]][_28[1]].apply(dojo[_28[0]],dep.slice(1)); -}else{ -dojo[_27].apply(dojo,dep.slice(1)); -} -} -if(_23.length==1&&_23[0]=="dojo._base._loader.loader_debug"){ -res.defineResource(dojo); -}else{ -var _29=dojo._xdContents.push({content:res.defineResource,resourceName:res["resourceName"],resourcePath:res["resourcePath"],isDefined:false})-1; -for(i=0;i<_23.length;i++){ -dojo._xdDepMap[_23[i]]={requires:_21,requiresAfter:_22,contentIndex:_29}; -} -} -for(i=0;i<_23.length;i++){ -dojo._xdInFlight[_23[i]]=false; -} -} -}; -dojo._xdLoadFlattenedBundle=function(_2a,_2b,_2c,_2d){ -_2c=_2c||"root"; -var _2e=dojo.i18n.normalizeLocale(_2c).replace("-","_"); -var _2f=[_2a,"nls",_2b].join("."); -var _30=dojo["provide"](_2f); -_30[_2e]=_2d; -var _31=[_2a,_2e,_2b].join("."); -var _32=dojo._xdBundleMap[_31]; -if(_32){ -for(var _33 in _32){ -_30[_33]=_2d; -} -} -}; -dojo._xdInitExtraLocales=function(){ -var _34=dojo.config.extraLocale; -if(_34){ -if(!_34 instanceof Array){ -_34=[_34]; -} -dojo._xdReqLoc=dojo.xdRequireLocalization; -dojo.xdRequireLocalization=function(m,b,_35,_36){ -dojo._xdReqLoc(m,b,_35,_36); -if(_35){ -return; -} -for(var i=0;i<_34.length;i++){ -dojo._xdReqLoc(m,b,_34[i],_36); -} -}; -} -}; -dojo._xdBundleMap={}; -dojo.xdRequireLocalization=function(_37,_38,_39,_3a){ -if(dojo._xdInitExtraLocales){ -dojo._xdInitExtraLocales(); -dojo._xdInitExtraLocales=null; -dojo.xdRequireLocalization.apply(dojo,arguments); -return; -} -var _3b=_3a.split(","); -var _3c=dojo.i18n.normalizeLocale(_39); -var _3d=""; -for(var i=0;i<_3b.length;i++){ -if(_3c.indexOf(_3b[i])==0){ -if(_3b[i].length>_3d.length){ -_3d=_3b[i]; -} -} -} -var _3e=_3d.replace("-","_"); -var _3f=dojo.getObject([_37,"nls",_38].join(".")); -if(!_3f||!_3f[_3e]){ -var _40=[_37,(_3e||"root"),_38].join("."); -var _41=dojo._xdBundleMap[_40]; -if(!_41){ -_41=dojo._xdBundleMap[_40]={}; -} -_41[_3c.replace("-","_")]=true; -dojo.require(_37+".nls"+(_3d?"."+_3d:"")+"."+_38); -} -}; -dojo._xdRealRequireLocalization=dojo.requireLocalization; -dojo.requireLocalization=function(_42,_43,_44,_45){ -var _46=dojo.moduleUrl(_42).toString(); -if(dojo._xdIsXDomainPath(_46)){ -return dojo.xdRequireLocalization.apply(dojo,arguments); -}else{ -return dojo._xdRealRequireLocalization.apply(dojo,arguments); -} -}; -dojo._xdUnpackDependency=function(dep){ -var _47=null; -var _48=null; -switch(dep[0]){ -case "requireIf": -case "requireAfterIf": -if(dep[1]===true){ -_47=[{name:dep[2],content:null}]; -} -break; -case "platformRequire": -var _49=dep[1]; -var _4a=_49["common"]||[]; -_47=(_49[dojo.hostenv.name_])?_4a.concat(_49[dojo.hostenv.name_]||[]):_4a.concat(_49["default"]||[]); -if(_47){ -for(var i=0;i<_47.length;i++){ -if(_47[i] instanceof Array){ -_47[i]={name:_47[i][0],content:null}; -}else{ -_47[i]={name:_47[i],content:null}; -} -} -} -break; -case "require": -_47=[{name:dep[1],content:null}]; -break; -case "i18n._preloadLocalizations": -dojo.i18n._preloadLocalizations.apply(dojo.i18n._preloadLocalizations,dep.slice(1)); -break; -} -if(dep[0]=="requireAfterIf"||dep[0]=="requireIf"){ -_48=_47; -_47=null; -} -return {requires:_47,requiresAfter:_48}; -}; -dojo._xdWalkReqs=function(){ -var _4b=null; -var req; -for(var i=0;i<dojo._xdOrderedReqs.length;i++){ -req=dojo._xdOrderedReqs[i]; -if(dojo._xdDepMap[req]){ -_4b=[req]; -_4b[req]=true; -dojo._xdEvalReqs(_4b); -} -} -}; -dojo._xdEvalReqs=function(_4c){ -while(_4c.length>0){ -var req=_4c[_4c.length-1]; -var res=dojo._xdDepMap[req]; -var i,_4d,_4e; -if(res){ -_4d=res.requires; -if(_4d&&_4d.length>0){ -for(i=0;i<_4d.length;i++){ -_4e=_4d[i].name; -if(_4e&&!_4c[_4e]){ -_4c.push(_4e); -_4c[_4e]=true; -dojo._xdEvalReqs(_4c); -} + +dojo._xdReset = function(){ + //summary: Internal xd loader function. Resets the xd state. + + //This flag indicates where or not we have crossed into xdomain territory. Once any resource says + //it is cross domain, then the rest of the resources have to be treated as xdomain because we need + //to evaluate resources in order. If there is a xdomain resource followed by a xhr resource, we can't load + //the xhr resource until the one before it finishes loading. The text of the xhr resource will be converted + //to match the format for a xd resource and put in the xd load queue. + dojo._isXDomain = dojo.config.useXDomain || false; + + dojo._xdClearInterval(); + dojo._xdInFlight = {}; + dojo._xdOrderedReqs = []; + dojo._xdDepMap = {}; + dojo._xdContents = []; + dojo._xdDefList = []; } + +dojo._xdClearInterval = function(){ + //summary: Internal xd loader function. + //Clears the interval timer used to check on the + //status of in-flight xd module resource requests. + if(dojo._xdTimer){ + clearInterval(dojo._xdTimer); + dojo._xdTimer = 0; + } } -var _4f=dojo._xdContents[res.contentIndex]; -if(!_4f.isDefined){ -var _50=_4f.content; -_50["resourceName"]=_4f["resourceName"]; -_50["resourcePath"]=_4f["resourcePath"]; -dojo._xdDefList.push(_50); -_4f.isDefined=true; + + +//Call reset immediately to set the state. +dojo._xdReset(); + +dojo._xdCreateResource = function(/*String*/contents, /*String*/resourceName, /*String*/resourcePath){ + //summary: Internal xd loader function. Creates an xd module source given an + //non-xd module contents. + + //Remove comments. Not perfect, but good enough for dependency resolution. + var depContents = contents.replace(/(\/\*([\s\S]*?)\*\/|\/\/(.*)$)/mg , ""); + + //Find dependencies. + var deps = []; + var depRegExp = /dojo.(require|requireIf|provide|requireAfterIf|platformRequire|requireLocalization)\s*\(([\w\W]*?)\)/mg; + var match; + while((match = depRegExp.exec(depContents)) != null){ + if(match[1] == "requireLocalization"){ + //Need to load the local bundles asap, since they are not + //part of the list of modules watched for loading. + eval(match[0]); + }else{ + deps.push('"' + match[1] + '", ' + match[2]); + } + } + + //Create resource object and the call to _xdResourceLoaded. + var output = []; + output.push(dojo._scopeName + "._xdResourceLoaded(function(" + dojo._scopePrefixArgs + "){\n"); + + //See if there are any dojo.loadInit calls + var loadInitCalls = dojo._xdExtractLoadInits(contents); + if(loadInitCalls){ + //Adjust fileContents since extractLoadInits removed something. + contents = loadInitCalls[0]; + + //Add any loadInit calls to the top of the xd file. + for(var i = 1; i < loadInitCalls.length; i++){ + output.push(loadInitCalls[i] + ";\n"); + } + } + + output.push("return {"); + + //Add dependencies + if(deps.length > 0){ + output.push("depends: ["); + for(i = 0; i < deps.length; i++){ + if(i > 0){ + output.push(",\n"); + } + output.push("[" + deps[i] + "]"); + } + output.push("],"); + } + + //Add the contents of the file inside a function. + //Pass in scope arguments so we can support multiple versions of the + //same module on a page. + output.push("\ndefineResource: function(" + dojo._scopePrefixArgs + "){"); + + //Don't put in the contents in the debugAtAllCosts case + //since the contents may have syntax errors. Let those + //get pushed up when the script tags are added to the page + //in the debugAtAllCosts case. + if(!dojo.config["debugAtAllCosts"] || resourceName == "dojo._base._loader.loader_debug"){ + output.push(contents); + } + //Add isLocal property so we know if we have to do something different + //in debugAtAllCosts situations. + output.push("\n}, resourceName: '" + resourceName + "', resourcePath: '" + resourcePath + "'};});"); + + return output.join(""); //String } -dojo._xdDepMap[req]=null; -_4d=res.requiresAfter; -if(_4d&&_4d.length>0){ -for(i=0;i<_4d.length;i++){ -_4e=_4d[i].name; -if(_4e&&!_4c[_4e]){ -_4c.push(_4e); -_4c[_4e]=true; -dojo._xdEvalReqs(_4c); + +dojo._xdExtractLoadInits = function(/*String*/fileContents){ + //Extracts + var regexp = /dojo.loadInit\s*\(/g; + regexp.lastIndex = 0; + + var parenRe = /[\(\)]/g; + parenRe.lastIndex = 0; + + var results = []; + var matches; + while((matches = regexp.exec(fileContents))){ + //Find end of the call by finding the matching end paren + parenRe.lastIndex = regexp.lastIndex; + var matchCount = 1; + var parenMatch; + while((parenMatch = parenRe.exec(fileContents))){ + if(parenMatch[0] == ")"){ + matchCount -= 1; + }else{ + matchCount += 1; + } + if(matchCount == 0){ + break; + } + } + + if(matchCount != 0){ + throw "unmatched paren around character " + parenRe.lastIndex + " in: " + fileContents; + } + + //Put the master matching string in the results. + var startIndex = regexp.lastIndex - matches[0].length; + results.push(fileContents.substring(startIndex, parenRe.lastIndex)); + + //Remove the matching section. + var remLength = parenRe.lastIndex - startIndex; + fileContents = fileContents.substring(0, startIndex) + fileContents.substring(parenRe.lastIndex, fileContents.length); + + //Move the master regexp past the last matching paren point. + regexp.lastIndex = parenRe.lastIndex - remLength; + + regexp.lastIndex = parenRe.lastIndex; + } + + if(results.length > 0){ + results.unshift(fileContents); + } + + return (results.length ? results : null); } + +dojo._xdIsXDomainPath = function(/*string*/relpath) { + //summary: Figure out whether the path is local or x-domain + //If there is a colon before the first / then, we have a URL with a protocol. + + var colonIndex = relpath.indexOf(":"); + var slashIndex = relpath.indexOf("/"); + + if(colonIndex > 0 && colonIndex < slashIndex){ + return true; + }else{ + //Is the base script URI-based URL a cross domain URL? + //If so, then the relpath will be evaluated relative to + //baseUrl, and therefore qualify as xdomain. + //Only treat it as xdomain if the page does not have a + //host (file:// url) or if the baseUrl does not match the + //current window's domain. + var url = dojo.baseUrl; + colonIndex = url.indexOf(":"); + slashIndex = url.indexOf("/"); + if(colonIndex > 0 && colonIndex < slashIndex && (!location.host || url.indexOf("http://" + location.host) != 0)){ + return true; + } + } + return false; } + +dojo._loadPath = function(/*String*/relpath, /*String?*/module, /*Function?*/cb){ + //summary: Internal xd loader function. Overrides loadPath() from loader.js. + //xd loading requires slightly different behavior from loadPath(). + + var currentIsXDomain = dojo._xdIsXDomainPath(relpath); + dojo._isXDomain |= currentIsXDomain; + + var uri = ((relpath.charAt(0) == '/' || relpath.match(/^\w+:/)) ? "" : dojo.baseUrl) + relpath; + + try{ + return ((!module || dojo._isXDomain) ? dojo._loadUri(uri, cb, currentIsXDomain, module) : dojo._loadUriAndCheck(uri, module, cb)); //Boolean + }catch(e){ + console.error(e); + return false; //Boolean + } } + +dojo._xdCharSet = "utf-8"; + +dojo._loadUri = function(/*String*/uri, /*Function?*/cb, /*boolean*/currentIsXDomain, /*String?*/module){ + //summary: Internal xd loader function. Overrides loadUri() from loader.js. + // xd loading requires slightly different behavior from loadPath(). + //description: Wanted to override getText(), but it is used by + // the widget code in too many, synchronous ways right now. + if(dojo._loadedUrls[uri]){ + return 1; //Boolean + } + + //Add the module (resource) to the list of modules. + //Only do this work if we have a modlue name. Otherwise, + //it is a non-xd i18n bundle, which can load immediately and does not + //need to be tracked. Also, don't track dojo.i18n, since it is a prerequisite + //and will be loaded correctly if we load it right away: it has no dependencies. + if(dojo._isXDomain && module && module != "dojo.i18n"){ + dojo._xdOrderedReqs.push(module); + + //Add to waiting resources if it is an xdomain resource. + //Don't add non-xdomain i18n bundles, those get evaled immediately. + if(currentIsXDomain || uri.indexOf("/nls/") == -1){ + dojo._xdInFlight[module] = true; + + //Increment inFlightCount + //This will stop the modulesLoaded from firing all the way. + dojo._inFlightCount++; + } + + //Start timer + if(!dojo._xdTimer){ + if(dojo.isAIR){ + dojo._xdTimer = setInterval(function(){dojo._xdWatchInFlight();}, 100); + }else{ + dojo._xdTimer = setInterval(dojo._scopeName + "._xdWatchInFlight();", 100); + } + } + dojo._xdStartTime = (new Date()).getTime(); + } + + if (currentIsXDomain){ + //Fix name to be a .xd.fileextension name. + var lastIndex = uri.lastIndexOf('.'); + if(lastIndex <= 0){ + lastIndex = uri.length - 1; + } + + var xdUri = uri.substring(0, lastIndex) + ".xd"; + if(lastIndex != uri.length - 1){ + xdUri += uri.substring(lastIndex, uri.length); + } + + if (dojo.isAIR){ + xdUri = xdUri.replace("app:/", "/"); + } + + //Add to script src + var element = document.createElement("script"); + element.type = "text/javascript"; + if(dojo._xdCharSet){ + element.charset = dojo._xdCharSet; + } + element.src = xdUri; + if(!dojo.headElement){ + dojo._headElement = document.getElementsByTagName("head")[0]; + + //Head element may not exist, particularly in html + //html 4 or tag soup cases where the page does not + //have a head tag in it. Use html element, since that will exist. + //Seems to be an issue mostly with Opera 9 and to lesser extent Safari 2 + if(!dojo._headElement){ + dojo._headElement = document.getElementsByTagName("html")[0]; + } + } + dojo._headElement.appendChild(element); + }else{ + var contents = dojo._getText(uri, null, true); + if(contents == null){ return 0; /*boolean*/} + + //If this is not xdomain, or if loading a i18n resource bundle, then send it down + //the normal eval/callback path. + if(dojo._isXDomain + && uri.indexOf("/nls/") == -1 + && module != "dojo.i18n"){ + var res = dojo._xdCreateResource(contents, module, uri); + dojo.eval(res); + }else{ + if(cb){ + contents = '('+contents+')'; + }else{ + //Only do the scoping if no callback. If a callback is specified, + //it is most likely the i18n bundle stuff. + contents = dojo._scopePrefix + contents + dojo._scopeSuffix; + } + var value = dojo["eval"](contents+"\r\n//@ sourceURL="+uri); + if(cb){ + cb(value); + } + } + } + + //These steps are done in the non-xd loader version of this function. + //Maintain these steps to fit in with the existing system. + dojo._loadedUrls[uri] = true; + dojo._loadedUrls.push(uri); + return true; //Boolean } -_4c.pop(); + +dojo._xdResourceLoaded = function(/*Object*/res){ + //summary: Internal xd loader function. Called by an xd module resource when + //it has been loaded via a script tag. + + //Evaluate the function with scopeArgs for multiversion support. + res = res.apply(dojo.global, dojo._scopeArgs); + + //Work through dependencies. + var deps = res.depends; + var requireList = null; + var requireAfterList = null; + var provideList = []; + if(deps && deps.length > 0){ + var dep = null; + var insertHint = 0; + var attachedResource = false; + for(var i = 0; i < deps.length; i++){ + dep = deps[i]; + + //Look for specific dependency indicators. + if (dep[0] == "provide"){ + provideList.push(dep[1]); + }else{ + if(!requireList){ + requireList = []; + } + if(!requireAfterList){ + requireAfterList = []; + } + + var unpackedDeps = dojo._xdUnpackDependency(dep); + if(unpackedDeps.requires){ + requireList = requireList.concat(unpackedDeps.requires); + } + if(unpackedDeps.requiresAfter){ + requireAfterList = requireAfterList.concat(unpackedDeps.requiresAfter); + } + } + + //Call the dependency indicator to allow for the normal dojo setup. + //Only allow for one dot reference, for the i18n._preloadLocalizations calls + //(and maybe future, one-dot things). + var depType = dep[0]; + var objPath = depType.split("."); + if(objPath.length == 2){ + dojo[objPath[0]][objPath[1]].apply(dojo[objPath[0]], dep.slice(1)); + }else{ + dojo[depType].apply(dojo, dep.slice(1)); + } + } + + + //If loading the debugAtAllCosts module, eval it right away since we need + //its functions to properly load the other modules. + if(provideList.length == 1 && provideList[0] == "dojo._base._loader.loader_debug"){ + res.defineResource(dojo); + }else{ + //Save off the resource contents for definition later. + var contentIndex = dojo._xdContents.push({ + content: res.defineResource, + resourceName: res["resourceName"], + resourcePath: res["resourcePath"], + isDefined: false + }) - 1; + + //Add provide/requires to dependency map. + for(i = 0; i < provideList.length; i++){ + dojo._xdDepMap[provideList[i]] = { requires: requireList, requiresAfter: requireAfterList, contentIndex: contentIndex }; + } + } + + //Now update the inflight status for any provided resources in this loaded resource. + //Do this at the very end (in a *separate* for loop) to avoid shutting down the + //inflight timer check too soon. + for(i = 0; i < provideList.length; i++){ + dojo._xdInFlight[provideList[i]] = false; + } + } } + +dojo._xdLoadFlattenedBundle = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale, /*Object*/bundleData){ + //summary: Internal xd loader function. Used when loading + //a flattened localized bundle via a script tag. + locale = locale || "root"; + var jsLoc = dojo.i18n.normalizeLocale(locale).replace('-', '_'); + var bundleResource = [moduleName, "nls", bundleName].join("."); + var bundle = dojo["provide"](bundleResource); + bundle[jsLoc] = bundleData; + + //Assign the bundle for the original locale(s) we wanted. + var mapName = [moduleName, jsLoc, bundleName].join("."); + var bundleMap = dojo._xdBundleMap[mapName]; + if(bundleMap){ + for(var param in bundleMap){ + bundle[param] = bundleData; + } + } }; -dojo._xdWatchInFlight=function(){ -var _51=""; -var _52=(dojo.config.xdWaitSeconds||15)*1000; -var _53=(dojo._xdStartTime+_52)<(new Date()).getTime(); -for(var _54 in dojo._xdInFlight){ -if(dojo._xdInFlight[_54]===true){ -if(_53){ -_51+=_54+" "; -}else{ -return; -} -} -} -dojo._xdClearInterval(); -if(_53){ -throw "Could not load cross-domain resources: "+_51; -} -dojo._xdWalkReqs(); -var _55=dojo._xdDefList.length; -for(var i=0;i<_55;i++){ -var _56=dojo._xdDefList[i]; -if(dojo.config["debugAtAllCosts"]&&_56["resourceName"]){ -if(!dojo["_xdDebugQueue"]){ -dojo._xdDebugQueue=[]; -} -dojo._xdDebugQueue.push({resourceName:_56.resourceName,resourcePath:_56.resourcePath}); -}else{ -_56.apply(dojo.global,dojo._scopeArgs); + + +dojo._xdInitExtraLocales = function(){ + // Simulate the extra locale work that dojo.requireLocalization does. + + var extra = dojo.config.extraLocale; + if(extra){ + if(!extra instanceof Array){ + extra = [extra]; + } + + dojo._xdReqLoc = dojo.xdRequireLocalization; + dojo.xdRequireLocalization = function(m, b, locale, fLocales){ + dojo._xdReqLoc(m,b,locale, fLocales); + if(locale){return;} + for(var i=0; i<extra.length; i++){ + dojo._xdReqLoc(m,b,extra[i], fLocales); + } + }; + } } + +dojo._xdBundleMap = {}; + +dojo.xdRequireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale, /*String*/availableFlatLocales){ + //summary: Internal xd loader function. The xd version of dojo.requireLocalization. + + + //Account for allowing multiple extra locales. Do this here inside the function + //since dojo._xdInitExtraLocales() depends on djConfig being set up, but that only + //happens after hostenv_browser runs. loader_xd has to come before hostenv_browser + //though since hostenv_browser can do a dojo.require for the debug module. + if(dojo._xdInitExtraLocales){ + dojo._xdInitExtraLocales(); + dojo._xdInitExtraLocales = null; + dojo.xdRequireLocalization.apply(dojo, arguments); + return; + } + + var locales = availableFlatLocales.split(","); + + //Find the best-match locale to load. + //Assumes dojo.i18n has already been loaded. This is true for xdomain builds, + //since it is included in dojo.xd.js. + var jsLoc = dojo.i18n.normalizeLocale(locale); + + var bestLocale = ""; + for(var i = 0; i < locales.length; i++){ + //Locale must match from start of string. + if(jsLoc.indexOf(locales[i]) == 0){ + if(locales[i].length > bestLocale.length){ + bestLocale = locales[i]; + } + } + } + + var fixedBestLocale = bestLocale.replace('-', '_'); + //See if the bundle we are going to use is already loaded. + var bundleResource = dojo.getObject([moduleName, "nls", bundleName].join(".")); + if(!bundleResource || !bundleResource[fixedBestLocale]){ + //Need to remember what locale we wanted and which one we actually use. + //Then when we load the one we are actually using, use that bundle for the one + //we originally wanted. + var mapName = [moduleName, (fixedBestLocale||"root"), bundleName].join("."); + var bundleMap = dojo._xdBundleMap[mapName]; + if(!bundleMap){ + bundleMap = dojo._xdBundleMap[mapName] = {}; + } + bundleMap[jsLoc.replace('-', '_')] = true; + + //Do just a normal dojo.require so the resource tracking stuff works as usual. + dojo.require(moduleName + ".nls" + (bestLocale ? "." + bestLocale : "") + "." + bundleName); + } } -for(i=0;i<dojo._xdContents.length;i++){ -var _57=dojo._xdContents[i]; -if(_57.content&&!_57.isDefined){ -_57.content.apply(dojo.global,dojo._scopeArgs); + +// Replace dojo.requireLocalization with a wrapper +dojo._xdRealRequireLocalization = dojo.requireLocalization; +dojo.requireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale, /*String*/availableFlatLocales){ + // summary: loads a bundle intelligently based on whether the module is + // local or xd. Overrides the local-case implementation. + + var modulePath = dojo.moduleUrl(moduleName).toString(); + if (dojo._xdIsXDomainPath(modulePath)) { + // call cross-domain loader + return dojo.xdRequireLocalization.apply(dojo, arguments); + } else { + // call local-loader + return dojo._xdRealRequireLocalization.apply(dojo, arguments); + } } + +//This is a bit brittle: it has to know about the dojo methods that deal with dependencies +//It would be ideal to intercept the actual methods and do something fancy at that point, +//but I have concern about knowing which provide to match to the dependency in that case, +//since scripts can load whenever they want, and trigger new calls to dojo._xdResourceLoaded(). +dojo._xdUnpackDependency = function(/*Array*/dep){ + //summary: Internal xd loader function. Determines what to do with a dependency + //that was listed in an xd version of a module contents. + + //Extract the dependency(ies). + var newDeps = null; + var newAfterDeps = null; + switch(dep[0]){ + case "requireIf": + case "requireAfterIf": + //First arg (dep[1]) is the test. Depedency is dep[2]. + if(dep[1] === true){ + newDeps = [{name: dep[2], content: null}]; + } + break; + case "platformRequire": + var modMap = dep[1]; + var common = modMap["common"]||[]; + newDeps = (modMap[dojo.hostenv.name_]) ? common.concat(modMap[dojo.hostenv.name_]||[]) : common.concat(modMap["default"]||[]); + //Flatten the array of arrays into a one-level deep array. + //Each result could be an array of 3 elements (the 3 arguments to dojo.require). + //We only need the first one. + if(newDeps){ + for(var i = 0; i < newDeps.length; i++){ + if(newDeps[i] instanceof Array){ + newDeps[i] = {name: newDeps[i][0], content: null}; + }else{ + newDeps[i] = {name: newDeps[i], content: null}; + } + } + } + break; + case "require": + //Just worry about dep[1] + newDeps = [{name: dep[1], content: null}]; + break; + case "i18n._preloadLocalizations": + //We can eval these immediately, since they load i18n bundles. + //Since i18n bundles have no dependencies, whenever they are loaded + //in a script tag, they are evaluated immediately, so we do not have to + //treat them has an explicit dependency for the dependency mapping. + //We can call it immediately since dojo.i18n is part of dojo.xd.js. + dojo.i18n._preloadLocalizations.apply(dojo.i18n._preloadLocalizations, dep.slice(1)); + break; + } + + //The requireIf and requireAfterIf needs to be evaluated after the current resource is evaluated. + if(dep[0] == "requireAfterIf" || dep[0] == "requireIf"){ + newAfterDeps = newDeps; + newDeps = null; + } + return {requires: newDeps, requiresAfter: newAfterDeps}; //Object } -dojo._xdReset(); -if(dojo["_xdDebugQueue"]&&dojo._xdDebugQueue.length>0){ -dojo._xdDebugFileLoaded(); -}else{ -dojo._xdNotifyLoaded(); + +dojo._xdWalkReqs = function(){ + //summary: Internal xd loader function. + //Walks the requires and evaluates module resource contents in + //the right order. + var reqChain = null; + var req; + for(var i = 0; i < dojo._xdOrderedReqs.length; i++){ + req = dojo._xdOrderedReqs[i]; + if(dojo._xdDepMap[req]){ + reqChain = [req]; + reqChain[req] = true; //Allow for fast lookup of the req in the array + dojo._xdEvalReqs(reqChain); + } + } } -}; -dojo._xdNotifyLoaded=function(){ -for(var _58 in dojo._xdInFlight){ -if(typeof dojo._xdInFlight[_58]=="boolean"){ -return; + +dojo._xdEvalReqs = function(/*Array*/reqChain){ + //summary: Internal xd loader function. + //Does a depth first, breadth second search and eval of required modules. + while(reqChain.length > 0){ + var req = reqChain[reqChain.length - 1]; + var res = dojo._xdDepMap[req]; + var i, reqs, nextReq; + if(res){ + //Trace down any requires for this resource. + //START dojo._xdTraceReqs() inlining for small Safari 2.0 call stack + reqs = res.requires; + if(reqs && reqs.length > 0){ + for(i = 0; i < reqs.length; i++){ + nextReq = reqs[i].name; + if(nextReq && !reqChain[nextReq]){ + //New req depedency. Follow it down. + reqChain.push(nextReq); + reqChain[nextReq] = true; + dojo._xdEvalReqs(reqChain); + } + } + } + //END dojo._xdTraceReqs() inlining for small Safari 2.0 call stack + + //Evaluate the resource. + var contents = dojo._xdContents[res.contentIndex]; + if(!contents.isDefined){ + var content = contents.content; + content["resourceName"] = contents["resourceName"]; + content["resourcePath"] = contents["resourcePath"]; + dojo._xdDefList.push(content); + contents.isDefined = true; + } + dojo._xdDepMap[req] = null; + + //Trace down any requireAfters for this resource. + //START dojo._xdTraceReqs() inlining for small Safari 2.0 call stack + reqs = res.requiresAfter; + if(reqs && reqs.length > 0){ + for(i = 0; i < reqs.length; i++){ + nextReq = reqs[i].name; + if(nextReq && !reqChain[nextReq]){ + //New req depedency. Follow it down. + reqChain.push(nextReq); + reqChain[nextReq] = true; + dojo._xdEvalReqs(reqChain); + } + } + } + //END dojo._xdTraceReqs() inlining for small Safari 2.0 call stack + } + + //Done with that require. Remove it and go to the next one. + reqChain.pop(); + } } + +dojo._xdWatchInFlight = function(){ + //summary: Internal xd loader function. + //Monitors in-flight requests for xd module resources. + + var noLoads = ""; + var waitInterval = (dojo.config.xdWaitSeconds || 15) * 1000; + var expired = (dojo._xdStartTime + waitInterval) < (new Date()).getTime(); + + //If any xdInFlight are true, then still waiting for something to load. + //Come back later. If we timed out, report the things that did not load. + for(var param in dojo._xdInFlight){ + if(dojo._xdInFlight[param] === true){ + if(expired){ + noLoads += param + " "; + }else{ + return; + } + } + } + + //All done. Clean up and notify. + dojo._xdClearInterval(); + + if(expired){ + throw "Could not load cross-domain resources: " + noLoads; + } + + dojo._xdWalkReqs(); + + var defLength = dojo._xdDefList.length; + for(var i= 0; i < defLength; i++){ + var content = dojo._xdDefList[i]; + if(dojo.config["debugAtAllCosts"] && content["resourceName"]){ + if(!dojo["_xdDebugQueue"]){ + dojo._xdDebugQueue = []; + } + dojo._xdDebugQueue.push({resourceName: content.resourceName, resourcePath: content.resourcePath}); + }else{ + //Evaluate the resource to bring it into being. + //Pass in scope args to allow multiple versions of modules in a page. + content.apply(dojo.global, dojo._scopeArgs); + } + } + + //Evaluate any resources that were not evaled before. + //This normally shouldn't happen with proper dojo.provide and dojo.require + //usage, but providing it just in case. Note that these may not be executed + //in the original order that the developer intended. + for(i = 0; i < dojo._xdContents.length; i++){ + var current = dojo._xdContents[i]; + if(current.content && !current.isDefined){ + //Pass in scope args to allow multiple versions of modules in a page. + current.content.apply(dojo.global, dojo._scopeArgs); + } + } + + //Clean up for the next round of xd loading. + dojo._xdReset(); + + if(dojo["_xdDebugQueue"] && dojo._xdDebugQueue.length > 0){ + dojo._xdDebugFileLoaded(); + }else{ + dojo._xdNotifyLoaded(); + } } -dojo._inFlightCount=0; -if(dojo._initFired&&!dojo._loadNotifying){ -dojo._callLoaded(); + +dojo._xdNotifyLoaded = function(){ + //Clear inflight count so we will finally do finish work. + + //Just having a legitimate status (true or false) for an inflight item + //means that it is still being processed. Do the typeof test + //to avoid bad JavaScript that might tinker with Object.prototype. + for(var prop in dojo._xdInFlight){ + if(typeof dojo._xdInFlight[prop] == "boolean"){ + return; + } + } + + dojo._inFlightCount = 0; + + //Only trigger call loaded if dj_load_init has run. + if(dojo._initFired && !dojo._loadNotifying){ + dojo._callLoaded(); + } } -}; + } diff --git a/lib/dojo/_base/array.js b/lib/dojo/_base/array.js index 83c21c8d8..26fa1900d 100644 --- a/lib/dojo/_base/array.js +++ b/lib/dojo/_base/array.js @@ -5,75 +5,258 @@ */ -if(!dojo._hasResource["dojo._base.array"]){ -dojo._hasResource["dojo._base.array"]=true; +if(!dojo._hasResource["dojo._base.array"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo._base.array"] = true; dojo.require("dojo._base.lang"); dojo.provide("dojo._base.array"); + (function(){ -var _1=function(_2,_3,cb){ -return [(typeof _2=="string")?_2.split(""):_2,_3||dojo.global,(typeof cb=="string")?new Function("item","index","array",cb):cb]; -}; -var _4=function(_5,_6,_7,_8){ -var _9=_1(_6,_8,_7); -_6=_9[0]; -for(var i=0,l=_6.length;i<l;++i){ -var _a=!!_9[2].call(_9[1],_6[i],i,_6); -if(_5^_a){ -return _a; -} -} -return _5; -}; -dojo.mixin(dojo,{indexOf:function(_b,_c,_d,_e){ -var _f=1,end=_b.length||0,i=0; -if(_e){ -i=end-1; -_f=end=-1; -} -if(_d!=undefined){ -i=_d; -} -if((_e&&i>end)||i<end){ -for(;i!=end;i+=_f){ -if(_b[i]==_c){ -return i; -} -} -} -return -1; -},lastIndexOf:function(_10,_11,_12){ -return dojo.indexOf(_10,_11,_12,true); -},forEach:function(arr,_13,_14){ -if(!arr||!arr.length){ -return; -} -var _15=_1(arr,_14,_13); -arr=_15[0]; -for(var i=0,l=arr.length;i<l;++i){ -_15[2].call(_15[1],arr[i],i,arr); -} -},every:function(arr,_16,_17){ -return _4(true,arr,_16,_17); -},some:function(arr,_18,_19){ -return _4(false,arr,_18,_19); -},map:function(arr,_1a,_1b){ -var _1c=_1(arr,_1b,_1a); -arr=_1c[0]; -var _1d=(arguments[3]?(new arguments[3]()):[]); -for(var i=0,l=arr.length;i<l;++i){ -_1d.push(_1c[2].call(_1c[1],arr[i],i,arr)); -} -return _1d; -},filter:function(arr,_1e,_1f){ -var _20=_1(arr,_1f,_1e); -arr=_20[0]; -var _21=[]; -for(var i=0,l=arr.length;i<l;++i){ -if(_20[2].call(_20[1],arr[i],i,arr)){ -_21.push(arr[i]); -} -} -return _21; -}}); + var _getParts = function(arr, obj, cb){ + return [ + (typeof arr == "string") ? arr.split("") : arr, + obj || dojo.global, + // FIXME: cache the anonymous functions we create here? + (typeof cb == "string") ? new Function("item", "index", "array", cb) : cb + ]; + }; + + var everyOrSome = function(/*Boolean*/every, /*Array|String*/arr, /*Function|String*/callback, /*Object?*/thisObject){ + var _p = _getParts(arr, thisObject, callback); arr = _p[0]; + for(var i=0,l=arr.length; i<l; ++i){ + var result = !!_p[2].call(_p[1], arr[i], i, arr); + if(every ^ result){ + return result; // Boolean + } + } + return every; // Boolean + }; + + dojo.mixin(dojo, { + indexOf: function( /*Array*/ array, + /*Object*/ value, + /*Integer?*/ fromIndex, + /*Boolean?*/ findLast){ + // summary: + // locates the first index of the provided value in the + // passed array. If the value is not found, -1 is returned. + // description: + // This method corresponds to the JavaScript 1.6 Array.indexOf method, with one difference: when + // run over sparse arrays, the Dojo function invokes the callback for every index whereas JavaScript + // 1.6's indexOf skips the holes in the sparse array. + // For details on this method, see: + // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/indexOf + + var step = 1, end = array.length || 0, i = 0; + if(findLast){ + i = end - 1; + step = end = -1; + } + if(fromIndex != undefined){ i = fromIndex; } + if((findLast && i > end) || i < end){ + for(; i != end; i += step){ + if(array[i] == value){ return i; } + } + } + return -1; // Number + }, + + lastIndexOf: function(/*Array*/array, /*Object*/value, /*Integer?*/fromIndex){ + // summary: + // locates the last index of the provided value in the passed + // array. If the value is not found, -1 is returned. + // description: + // This method corresponds to the JavaScript 1.6 Array.lastIndexOf method, with one difference: when + // run over sparse arrays, the Dojo function invokes the callback for every index whereas JavaScript + // 1.6's lastIndexOf skips the holes in the sparse array. + // For details on this method, see: + // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/lastIndexOf + return dojo.indexOf(array, value, fromIndex, true); // Number + }, + + forEach: function(/*Array|String*/arr, /*Function|String*/callback, /*Object?*/thisObject){ + // summary: + // for every item in arr, callback is invoked. Return values are ignored. + // If you want to break out of the loop, consider using dojo.every() or dojo.some(). + // forEach does not allow breaking out of the loop over the items in arr. + // arr: + // the array to iterate over. If a string, operates on individual characters. + // callback: + // a function is invoked with three arguments: item, index, and array + // thisObject: + // may be used to scope the call to callback + // description: + // This function corresponds to the JavaScript 1.6 Array.forEach() method, with one difference: when + // run over sparse arrays, this implemenation passes the "holes" in the sparse array to + // the callback function with a value of undefined. JavaScript 1.6's forEach skips the holes in the sparse array. + // For more details, see: + // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/forEach + // example: + // | // log out all members of the array: + // | dojo.forEach( + // | [ "thinger", "blah", "howdy", 10 ], + // | function(item){ + // | console.log(item); + // | } + // | ); + // example: + // | // log out the members and their indexes + // | dojo.forEach( + // | [ "thinger", "blah", "howdy", 10 ], + // | function(item, idx, arr){ + // | console.log(item, "at index:", idx); + // | } + // | ); + // example: + // | // use a scoped object member as the callback + // | + // | var obj = { + // | prefix: "logged via obj.callback:", + // | callback: function(item){ + // | console.log(this.prefix, item); + // | } + // | }; + // | + // | // specifying the scope function executes the callback in that scope + // | dojo.forEach( + // | [ "thinger", "blah", "howdy", 10 ], + // | obj.callback, + // | obj + // | ); + // | + // | // alternately, we can accomplish the same thing with dojo.hitch() + // | dojo.forEach( + // | [ "thinger", "blah", "howdy", 10 ], + // | dojo.hitch(obj, "callback") + // | ); + + // match the behavior of the built-in forEach WRT empty arrs + if(!arr || !arr.length){ return; } + + // FIXME: there are several ways of handilng thisObject. Is + // dojo.global always the default context? + var _p = _getParts(arr, thisObject, callback); arr = _p[0]; + for(var i=0,l=arr.length; i<l; ++i){ + _p[2].call(_p[1], arr[i], i, arr); + } + }, + + every: function(/*Array|String*/arr, /*Function|String*/callback, /*Object?*/thisObject){ + // summary: + // Determines whether or not every item in arr satisfies the + // condition implemented by callback. + // arr: + // the array to iterate on. If a string, operates on individual characters. + // callback: + // a function is invoked with three arguments: item, index, + // and array and returns true if the condition is met. + // thisObject: + // may be used to scope the call to callback + // description: + // This function corresponds to the JavaScript 1.6 Array.every() method, with one difference: when + // run over sparse arrays, this implemenation passes the "holes" in the sparse array to + // the callback function with a value of undefined. JavaScript 1.6's every skips the holes in the sparse array. + // For more details, see: + // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/every + // example: + // | // returns false + // | dojo.every([1, 2, 3, 4], function(item){ return item>1; }); + // example: + // | // returns true + // | dojo.every([1, 2, 3, 4], function(item){ return item>0; }); + return everyOrSome(true, arr, callback, thisObject); // Boolean + }, + + some: function(/*Array|String*/arr, /*Function|String*/callback, /*Object?*/thisObject){ + // summary: + // Determines whether or not any item in arr satisfies the + // condition implemented by callback. + // arr: + // the array to iterate over. If a string, operates on individual characters. + // callback: + // a function is invoked with three arguments: item, index, + // and array and returns true if the condition is met. + // thisObject: + // may be used to scope the call to callback + // description: + // This function corresponds to the JavaScript 1.6 Array.some() method, with one difference: when + // run over sparse arrays, this implemenation passes the "holes" in the sparse array to + // the callback function with a value of undefined. JavaScript 1.6's some skips the holes in the sparse array. + // For more details, see: + // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/some + // example: + // | // is true + // | dojo.some([1, 2, 3, 4], function(item){ return item>1; }); + // example: + // | // is false + // | dojo.some([1, 2, 3, 4], function(item){ return item<1; }); + return everyOrSome(false, arr, callback, thisObject); // Boolean + }, + + map: function(/*Array|String*/arr, /*Function|String*/callback, /*Function?*/thisObject){ + // summary: + // applies callback to each element of arr and returns + // an Array with the results + // arr: + // the array to iterate on. If a string, operates on + // individual characters. + // callback: + // a function is invoked with three arguments, (item, index, + // array), and returns a value + // thisObject: + // may be used to scope the call to callback + // description: + // This function corresponds to the JavaScript 1.6 Array.map() method, with one difference: when + // run over sparse arrays, this implemenation passes the "holes" in the sparse array to + // the callback function with a value of undefined. JavaScript 1.6's map skips the holes in the sparse array. + // For more details, see: + // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/map + // example: + // | // returns [2, 3, 4, 5] + // | dojo.map([1, 2, 3, 4], function(item){ return item+1 }); + + var _p = _getParts(arr, thisObject, callback); arr = _p[0]; + var outArr = (arguments[3] ? (new arguments[3]()) : []); + for(var i=0,l=arr.length; i<l; ++i){ + outArr.push(_p[2].call(_p[1], arr[i], i, arr)); + } + return outArr; // Array + }, + + filter: function(/*Array*/arr, /*Function|String*/callback, /*Object?*/thisObject){ + // summary: + // Returns a new Array with those items from arr that match the + // condition implemented by callback. + // arr: + // the array to iterate over. + // callback: + // a function that is invoked with three arguments (item, + // index, array). The return of this function is expected to + // be a boolean which determines whether the passed-in item + // will be included in the returned array. + // thisObject: + // may be used to scope the call to callback + // description: + // This function corresponds to the JavaScript 1.6 Array.filter() method, with one difference: when + // run over sparse arrays, this implemenation passes the "holes" in the sparse array to + // the callback function with a value of undefined. JavaScript 1.6's filter skips the holes in the sparse array. + // For more details, see: + // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/filter + // example: + // | // returns [2, 3, 4] + // | dojo.filter([1, 2, 3, 4], function(item){ return item>1; }); + + var _p = _getParts(arr, thisObject, callback); arr = _p[0]; + var outArr = []; + for(var i=0,l=arr.length; i<l; ++i){ + if(_p[2].call(_p[1], arr[i], i, arr)){ + outArr.push(arr[i]); + } + } + return outArr; // Array + } + }); })(); +/* +*/ + } diff --git a/lib/dojo/_base/browser.js b/lib/dojo/_base/browser.js index 67c1eef37..496fe46b3 100644 --- a/lib/dojo/_base/browser.js +++ b/lib/dojo/_base/browser.js @@ -5,9 +5,10 @@ */ -if(!dojo._hasResource["dojo._base.browser"]){ -dojo._hasResource["dojo._base.browser"]=true; +if(!dojo._hasResource["dojo._base.browser"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo._base.browser"] = true; dojo.provide("dojo._base.browser"); + dojo.require("dojo._base.window"); dojo.require("dojo._base.connect"); dojo.require("dojo._base.event"); @@ -16,7 +17,13 @@ dojo.require("dojo._base.NodeList"); dojo.require("dojo._base.query"); dojo.require("dojo._base.xhr"); dojo.require("dojo._base.fx"); -dojo.forEach(dojo.config.require,function(i){ -dojo["require"](i); + +//Need this to be the last code segment in base, so do not place any +//dojo.requireIf calls in this file. Otherwise, due to how the build system +//puts all requireIf dependencies after the current file, the require calls +//could be called before all of base is defined. +dojo.forEach(dojo.config.require, function(i){ + dojo["require"](i); }); + } diff --git a/lib/dojo/_base/connect.js b/lib/dojo/_base/connect.js index 3905d92e7..f37af65b2 100644 --- a/lib/dojo/_base/connect.js +++ b/lib/dojo/_base/connect.js @@ -5,81 +5,306 @@ */ -if(!dojo._hasResource["dojo._base.connect"]){ -dojo._hasResource["dojo._base.connect"]=true; +if(!dojo._hasResource["dojo._base.connect"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo._base.connect"] = true; dojo.provide("dojo._base.connect"); dojo.require("dojo._base.lang"); -dojo._listener={getDispatcher:function(){ -return function(){ -var ap=Array.prototype,c=arguments.callee,ls=c._listeners,t=c.target; -var r=t&&t.apply(this,arguments); -var i,_1; -_1=[].concat(ls); -for(i in _1){ -if(!(i in ap)){ -_1[i].apply(this,arguments); -} -} -return r; + +// this file courtesy of the TurboAjax Group, licensed under a Dojo CLA + +// low-level delegation machinery +dojo._listener = { + // create a dispatcher function + getDispatcher: function(){ + // following comments pulled out-of-line to prevent cloning them + // in the returned function. + // - indices (i) that are really in the array of listeners (ls) will + // not be in Array.prototype. This is the 'sparse array' trick + // that keeps us safe from libs that take liberties with built-in + // objects + // - listener is invoked with current scope (this) + return function(){ + var ap=Array.prototype, c=arguments.callee, ls=c._listeners, t=c.target; + // return value comes from original target function + var r = t && t.apply(this, arguments); + // make local copy of listener array so it is immutable during processing + var i, lls; + lls = [].concat(ls); + + // invoke listeners after target function + for(i in lls){ + if(!(i in ap)){ + lls[i].apply(this, arguments); + } + } + // return value comes from original target function + return r; + }; + }, + // add a listener to an object + add: function(/*Object*/ source, /*String*/ method, /*Function*/ listener){ + // Whenever 'method' is invoked, 'listener' will have the same scope. + // Trying to supporting a context object for the listener led to + // complexity. + // Non trivial to provide 'once' functionality here + // because listener could be the result of a dojo.hitch call, + // in which case two references to the same hitch target would not + // be equivalent. + source = source || dojo.global; + // The source method is either null, a dispatcher, or some other function + var f = source[method]; + // Ensure a dispatcher + if(!f || !f._listeners){ + var d = dojo._listener.getDispatcher(); + // original target function is special + d.target = f; + // dispatcher holds a list of listeners + d._listeners = []; + // redirect source to dispatcher + f = source[method] = d; + } + // The contract is that a handle is returned that can + // identify this listener for disconnect. + // + // The type of the handle is private. Here is it implemented as Integer. + // DOM event code has this same contract but handle is Function + // in non-IE browsers. + // + // We could have separate lists of before and after listeners. + return f._listeners.push(listener); /*Handle*/ + }, + // remove a listener from an object + remove: function(/*Object*/ source, /*String*/ method, /*Handle*/ handle){ + var f = (source || dojo.global)[method]; + // remember that handle is the index+1 (0 is not a valid handle) + if(f && f._listeners && handle--){ + delete f._listeners[handle]; + } + } }; -},add:function(_2,_3,_4){ -_2=_2||dojo.global; -var f=_2[_3]; -if(!f||!f._listeners){ -var d=dojo._listener.getDispatcher(); -d.target=f; -d._listeners=[]; -f=_2[_3]=d; + +// Multiple delegation for arbitrary methods. + +// This unit knows nothing about DOM, but we include DOM aware documentation +// and dontFix argument here to help the autodocs. Actual DOM aware code is in +// event.js. + +dojo.connect = function(/*Object|null*/ obj, + /*String*/ event, + /*Object|null*/ context, + /*String|Function*/ method, + /*Boolean?*/ dontFix){ + // summary: + // `dojo.connect` is the core event handling and delegation method in + // Dojo. It allows one function to "listen in" on the execution of + // any other, triggering the second whenever the first is called. Many + // listeners may be attached to a function, and source functions may + // be either regular function calls or DOM events. + // + // description: + // Connects listeners to actions, so that after event fires, a + // listener is called with the same arguments passed to the original + // function. + // + // Since `dojo.connect` allows the source of events to be either a + // "regular" JavaScript function or a DOM event, it provides a uniform + // interface for listening to all the types of events that an + // application is likely to deal with though a single, unified + // interface. DOM programmers may want to think of it as + // "addEventListener for everything and anything". + // + // When setting up a connection, the `event` parameter must be a + // string that is the name of the method/event to be listened for. If + // `obj` is null, `dojo.global` is assumed, meaning that connections + // to global methods are supported but also that you may inadvertently + // connect to a global by passing an incorrect object name or invalid + // reference. + // + // `dojo.connect` generally is forgiving. If you pass the name of a + // function or method that does not yet exist on `obj`, connect will + // not fail, but will instead set up a stub method. Similarly, null + // arguments may simply be omitted such that fewer than 4 arguments + // may be required to set up a connection See the examples for details. + // + // The return value is a handle that is needed to + // remove this connection with `dojo.disconnect`. + // + // obj: + // The source object for the event function. + // Defaults to `dojo.global` if null. + // If obj is a DOM node, the connection is delegated + // to the DOM event manager (unless dontFix is true). + // + // event: + // String name of the event function in obj. + // I.e. identifies a property `obj[event]`. + // + // context: + // The object that method will receive as "this". + // + // If context is null and method is a function, then method + // inherits the context of event. + // + // If method is a string then context must be the source + // object object for method (context[method]). If context is null, + // dojo.global is used. + // + // method: + // A function reference, or name of a function in context. + // The function identified by method fires after event does. + // method receives the same arguments as the event. + // See context argument comments for information on method's scope. + // + // dontFix: + // If obj is a DOM node, set dontFix to true to prevent delegation + // of this connection to the DOM event manager. + // + // example: + // When obj.onchange(), do ui.update(): + // | dojo.connect(obj, "onchange", ui, "update"); + // | dojo.connect(obj, "onchange", ui, ui.update); // same + // + // example: + // Using return value for disconnect: + // | var link = dojo.connect(obj, "onchange", ui, "update"); + // | ... + // | dojo.disconnect(link); + // + // example: + // When onglobalevent executes, watcher.handler is invoked: + // | dojo.connect(null, "onglobalevent", watcher, "handler"); + // + // example: + // When ob.onCustomEvent executes, customEventHandler is invoked: + // | dojo.connect(ob, "onCustomEvent", null, "customEventHandler"); + // | dojo.connect(ob, "onCustomEvent", "customEventHandler"); // same + // + // example: + // When ob.onCustomEvent executes, customEventHandler is invoked + // with the same scope (this): + // | dojo.connect(ob, "onCustomEvent", null, customEventHandler); + // | dojo.connect(ob, "onCustomEvent", customEventHandler); // same + // + // example: + // When globalEvent executes, globalHandler is invoked + // with the same scope (this): + // | dojo.connect(null, "globalEvent", null, globalHandler); + // | dojo.connect("globalEvent", globalHandler); // same + + // normalize arguments + var a=arguments, args=[], i=0; + // if a[0] is a String, obj was omitted + args.push(dojo.isString(a[0]) ? null : a[i++], a[i++]); + // if the arg-after-next is a String or Function, context was NOT omitted + var a1 = a[i+1]; + args.push(dojo.isString(a1)||dojo.isFunction(a1) ? a[i++] : null, a[i++]); + // absorb any additional arguments + for(var l=a.length; i<l; i++){ args.push(a[i]); } + // do the actual work + return dojo._connect.apply(this, args); /*Handle*/ } -return f._listeners.push(_4); -},remove:function(_5,_6,_7){ -var f=(_5||dojo.global)[_6]; -if(f&&f._listeners&&_7--){ -delete f._listeners[_7]; + +// used by non-browser hostenvs. always overriden by event.js +dojo._connect = function(obj, event, context, method){ + var l=dojo._listener, h=l.add(obj, event, dojo.hitch(context, method)); + return [obj, event, h, l]; // Handle } -}}; -dojo.connect=function(_8,_9,_a,_b,_c){ -var a=arguments,_d=[],i=0; -_d.push(dojo.isString(a[0])?null:a[i++],a[i++]); -var a1=a[i+1]; -_d.push(dojo.isString(a1)||dojo.isFunction(a1)?a[i++]:null,a[i++]); -for(var l=a.length;i<l;i++){ -_d.push(a[i]); + +dojo.disconnect = function(/*Handle*/ handle){ + // summary: + // Remove a link created by dojo.connect. + // description: + // Removes the connection between event and the method referenced by handle. + // handle: + // the return value of the dojo.connect call that created the connection. + if(handle && handle[0] !== undefined){ + dojo._disconnect.apply(this, handle); + // let's not keep this reference + delete handle[0]; + } } -return dojo._connect.apply(this,_d); -}; -dojo._connect=function(_e,_f,_10,_11){ -var l=dojo._listener,h=l.add(_e,_f,dojo.hitch(_10,_11)); -return [_e,_f,h,l]; -}; -dojo.disconnect=function(_12){ -if(_12&&_12[0]!==undefined){ -dojo._disconnect.apply(this,_12); -delete _12[0]; + +dojo._disconnect = function(obj, event, handle, listener){ + listener.remove(obj, event, handle); } -}; -dojo._disconnect=function(obj,_13,_14,_15){ -_15.remove(obj,_13,_14); -}; -dojo._topics={}; -dojo.subscribe=function(_16,_17,_18){ -return [_16,dojo._listener.add(dojo._topics,_16,dojo.hitch(_17,_18))]; -}; -dojo.unsubscribe=function(_19){ -if(_19){ -dojo._listener.remove(dojo._topics,_19[0],_19[1]); + +// topic publish/subscribe + +dojo._topics = {}; + +dojo.subscribe = function(/*String*/ topic, /*Object|null*/ context, /*String|Function*/ method){ + // summary: + // Attach a listener to a named topic. The listener function is invoked whenever the + // named topic is published (see: dojo.publish). + // Returns a handle which is needed to unsubscribe this listener. + // context: + // Scope in which method will be invoked, or null for default scope. + // method: + // The name of a function in context, or a function reference. This is the function that + // is invoked when topic is published. + // example: + // | dojo.subscribe("alerts", null, function(caption, message){ alert(caption + "\n" + message); }); + // | dojo.publish("alerts", [ "read this", "hello world" ]); + + // support for 2 argument invocation (omitting context) depends on hitch + return [topic, dojo._listener.add(dojo._topics, topic, dojo.hitch(context, method))]; /*Handle*/ } -}; -dojo.publish=function(_1a,_1b){ -var f=dojo._topics[_1a]; -if(f){ -f.apply(this,_1b||[]); + +dojo.unsubscribe = function(/*Handle*/ handle){ + // summary: + // Remove a topic listener. + // handle: + // The handle returned from a call to subscribe. + // example: + // | var alerter = dojo.subscribe("alerts", null, function(caption, message){ alert(caption + "\n" + message); }; + // | ... + // | dojo.unsubscribe(alerter); + if(handle){ + dojo._listener.remove(dojo._topics, handle[0], handle[1]); + } } + +dojo.publish = function(/*String*/ topic, /*Array*/ args){ + // summary: + // Invoke all listener method subscribed to topic. + // topic: + // The name of the topic to publish. + // args: + // An array of arguments. The arguments will be applied + // to each topic subscriber (as first class parameters, via apply). + // example: + // | dojo.subscribe("alerts", null, function(caption, message){ alert(caption + "\n" + message); }; + // | dojo.publish("alerts", [ "read this", "hello world" ]); + + // Note that args is an array, which is more efficient vs variable length + // argument list. Ideally, var args would be implemented via Array + // throughout the APIs. + var f = dojo._topics[topic]; + if(f){ + f.apply(this, args||[]); + } +} + +dojo.connectPublisher = function( /*String*/ topic, + /*Object|null*/ obj, + /*String*/ event){ + // summary: + // Ensure that every time obj.event() is called, a message is published + // on the topic. Returns a handle which can be passed to + // dojo.disconnect() to disable subsequent automatic publication on + // the topic. + // topic: + // The name of the topic to publish. + // obj: + // The source object for the event function. Defaults to dojo.global + // if null. + // event: + // The name of the event function in obj. + // I.e. identifies a property obj[event]. + // example: + // | dojo.connectPublisher("/ajax/start", dojo, "xhrGet"); + var pf = function(){ dojo.publish(topic, arguments); } + return event ? dojo.connect(obj, event, pf) : dojo.connect(obj, pf); //Handle }; -dojo.connectPublisher=function(_1c,obj,_1d){ -var pf=function(){ -dojo.publish(_1c,arguments); -}; -return _1d?dojo.connect(obj,_1d,pf):dojo.connect(obj,pf); -}; + } diff --git a/lib/dojo/_base/declare.js b/lib/dojo/_base/declare.js index f8ce201ec..8e46b12c0 100644 --- a/lib/dojo/_base/declare.js +++ b/lib/dojo/_base/declare.js @@ -5,419 +5,1044 @@ */ -if(!dojo._hasResource["dojo._base.declare"]){ -dojo._hasResource["dojo._base.declare"]=true; +if(!dojo._hasResource["dojo._base.declare"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo._base.declare"] = true; dojo.provide("dojo._base.declare"); + dojo.require("dojo._base.lang"); dojo.require("dojo._base.array"); + (function(){ -var d=dojo,_1=d._mixin,op=Object.prototype,_2=op.toString,_3=new Function,_4=0,_5="constructor"; -function _6(_7){ -throw new Error("declare: "+_7); -}; -function _8(_9){ -var _a=[],_b=[{cls:0,refs:[]}],_c={},_d=1,l=_9.length,i=0,j,_e,_f,top,_10,rec,_11,_12; -for(;i<l;++i){ -_f=_9[i]; -if(!_f){ -_6("mixin #"+i+" is unknown. Did you use dojo.require to pull it in?"); -}else{ -if(_2.call(_f)!="[object Function]"){ -_6("mixin #"+i+" is not a callable constructor."); -} -} -_e=_f._meta?_f._meta.bases:[_f]; -top=0; -for(j=_e.length-1;j>=0;--j){ -_10=_e[j].prototype; -if(!_10.hasOwnProperty("declaredClass")){ -_10.declaredClass="uniqName_"+(_4++); -} -_11=_10.declaredClass; -if(!_c.hasOwnProperty(_11)){ -_c[_11]={count:0,refs:[],cls:_e[j]}; -++_d; -} -rec=_c[_11]; -if(top&&top!==rec){ -rec.refs.push(top); -++top.count; -} -top=rec; -} -++top.count; -_b[0].refs.push(top); -} -while(_b.length){ -top=_b.pop(); -_a.push(top.cls); ---_d; -while(_12=top.refs,_12.length==1){ -top=_12[0]; -if(!top||--top.count){ -top=0; -break; -} -_a.push(top.cls); ---_d; -} -if(top){ -for(i=0,l=_12.length;i<l;++i){ -top=_12[i]; -if(!--top.count){ -_b.push(top); -} -} -} -} -if(_d){ -_6("can't build consistent linearization"); -} -_f=_9[0]; -_a[0]=_f?_f._meta&&_f===_a[_a.length-_f._meta.bases.length]?_f._meta.bases.length:1:0; -return _a; -}; -function _13(_14,a,f){ -var _15,_16,_17,_18,_19,_1a,_1b,opf,pos,_1c=this._inherited=this._inherited||{}; -if(typeof _14=="string"){ -_15=_14; -_14=a; -a=f; -} -f=0; -_18=_14.callee; -_15=_15||_18.nom; -if(!_15){ -_6("can't deduce a name to call inherited()"); -} -_19=this.constructor._meta; -_17=_19.bases; -pos=_1c.p; -if(_15!=_5){ -if(_1c.c!==_18){ -pos=0; -_1a=_17[0]; -_19=_1a._meta; -if(_19.hidden[_15]!==_18){ -_16=_19.chains; -if(_16&&typeof _16[_15]=="string"){ -_6("calling chained method with inherited: "+_15); -} -do{ -_19=_1a._meta; -_1b=_1a.prototype; -if(_19&&(_1b[_15]===_18&&_1b.hasOwnProperty(_15)||_19.hidden[_15]===_18)){ -break; -} -}while(_1a=_17[++pos]); -pos=_1a?pos:-1; -} -} -_1a=_17[++pos]; -if(_1a){ -_1b=_1a.prototype; -if(_1a._meta&&_1b.hasOwnProperty(_15)){ -f=_1b[_15]; -}else{ -opf=op[_15]; -do{ -_1b=_1a.prototype; -f=_1b[_15]; -if(f&&(_1a._meta?_1b.hasOwnProperty(_15):f!==opf)){ -break; -} -}while(_1a=_17[++pos]); -} -} -f=_1a&&f||op[_15]; -}else{ -if(_1c.c!==_18){ -pos=0; -_19=_17[0]._meta; -if(_19&&_19.ctor!==_18){ -_16=_19.chains; -if(!_16||_16.constructor!=="manual"){ -_6("calling chained constructor with inherited"); -} -while(_1a=_17[++pos]){ -_19=_1a._meta; -if(_19&&_19.ctor===_18){ -break; -} -} -pos=_1a?pos:-1; -} -} -while(_1a=_17[++pos]){ -_19=_1a._meta; -f=_19?_19.ctor:_1a; -if(f){ -break; -} -} -f=_1a&&f; -} -_1c.c=f; -_1c.p=pos; -if(f){ -return a===true?f:f.apply(this,a||_14); -} -}; -function _1d(_1e,_1f){ -if(typeof _1e=="string"){ -return this.inherited(_1e,_1f,true); -} -return this.inherited(_1e,true); -}; -function _20(cls){ -var _21=this.constructor._meta.bases; -for(var i=0,l=_21.length;i<l;++i){ -if(_21[i]===cls){ -return true; -} -} -return this instanceof cls; -}; -function _22(_23,_24){ -var _25,i=0,l=d._extraNames.length; -for(_25 in _24){ -if(_25!=_5&&_24.hasOwnProperty(_25)){ -_23[_25]=_24[_25]; -} -} -for(;i<l;++i){ -_25=d._extraNames[i]; -if(_25!=_5&&_24.hasOwnProperty(_25)){ -_23[_25]=_24[_25]; -} -} -}; -function _26(_27,_28){ -var _29,t,i=0,l=d._extraNames.length; -for(_29 in _28){ -t=_28[_29]; -if((t!==op[_29]||!(_29 in op))&&_29!=_5){ -if(_2.call(t)=="[object Function]"){ -t.nom=_29; -} -_27[_29]=t; -} -} -for(;i<l;++i){ -_29=d._extraNames[i]; -t=_28[_29]; -if((t!==op[_29]||!(_29 in op))&&_29!=_5){ -if(_2.call(t)=="[object Function]"){ -t.nom=_29; -} -_27[_29]=t; -} -} -return _27; -}; -function _2a(_2b){ -_26(this.prototype,_2b); -return this; -}; -function _2c(_2d,_2e){ -return function(){ -var a=arguments,_2f=a,a0=a[0],f,i,m,l=_2d.length,_30; -if(!(this instanceof a.callee)){ -return _31(a); -} -if(_2e&&(a0&&a0.preamble||this.preamble)){ -_30=new Array(_2d.length); -_30[0]=a; -for(i=0;;){ -a0=a[0]; -if(a0){ -f=a0.preamble; -if(f){ -a=f.apply(this,a)||a; -} -} -f=_2d[i].prototype; -f=f.hasOwnProperty("preamble")&&f.preamble; -if(f){ -a=f.apply(this,a)||a; -} -if(++i==l){ -break; -} -_30[i]=a; -} -} -for(i=l-1;i>=0;--i){ -f=_2d[i]; -m=f._meta; -f=m?m.ctor:f; -if(f){ -f.apply(this,_30?_30[i]:a); -} -} -f=this.postscript; -if(f){ -f.apply(this,_2f); -} -}; -}; -function _32(_33,_34){ -return function(){ -var a=arguments,t=a,a0=a[0],f; -if(!(this instanceof a.callee)){ -return _31(a); -} -if(_34){ -if(a0){ -f=a0.preamble; -if(f){ -t=f.apply(this,t)||t; -} -} -f=this.preamble; -if(f){ -f.apply(this,t); -} -} -if(_33){ -_33.apply(this,a); -} -f=this.postscript; -if(f){ -f.apply(this,a); -} -}; -}; -function _35(_36){ -return function(){ -var a=arguments,i=0,f,m; -if(!(this instanceof a.callee)){ -return _31(a); -} -for(;f=_36[i];++i){ -m=f._meta; -f=m?m.ctor:f; -if(f){ -f.apply(this,a); -break; -} -} -f=this.postscript; -if(f){ -f.apply(this,a); -} -}; -}; -function _37(_38,_39,_3a){ -return function(){ -var b,m,f,i=0,_3b=1; -if(_3a){ -i=_39.length-1; -_3b=-1; -} -for(;b=_39[i];i+=_3b){ -m=b._meta; -f=(m?m.hidden:b.prototype)[_38]; -if(f){ -f.apply(this,arguments); -} -} -}; -}; -function _3c(_3d){ -_3.prototype=_3d.prototype; -var t=new _3; -_3.prototype=null; -return t; -}; -function _31(_3e){ -var _3f=_3e.callee,t=_3c(_3f); -_3f.apply(t,_3e); -return t; -}; -d.declare=function(_40,_41,_42){ -if(typeof _40!="string"){ -_42=_41; -_41=_40; -_40=""; -} -_42=_42||{}; -var _43,i,t,_44,_45,_46,_47,_48=1,_49=_41; -if(_2.call(_41)=="[object Array]"){ -_46=_8(_41); -t=_46[0]; -_48=_46.length-t; -_41=_46[_48]; -}else{ -_46=[0]; -if(_41){ -if(_2.call(_41)=="[object Function]"){ -t=_41._meta; -_46=_46.concat(t?t.bases:_41); -}else{ -_6("base class is not a callable constructor."); -} -}else{ -if(_41!==null){ -_6("unknown base class. Did you use dojo.require to pull it in?"); -} -} -} -if(_41){ -for(i=_48-1;;--i){ -_43=_3c(_41); -if(!i){ -break; -} -t=_46[i]; -(t._meta?_22:_1)(_43,t.prototype); -_44=new Function; -_44.superclass=_41; -_44.prototype=_43; -_41=_43.constructor=_44; -} -}else{ -_43={}; -} -_26(_43,_42); -t=_42.constructor; -if(t!==op.constructor){ -t.nom=_5; -_43.constructor=t; -} -for(i=_48-1;i;--i){ -t=_46[i]._meta; -if(t&&t.chains){ -_47=_1(_47||{},t.chains); -} -} -if(_43["-chains-"]){ -_47=_1(_47||{},_43["-chains-"]); -} -t=!_47||!_47.hasOwnProperty(_5); -_46[0]=_44=(_47&&_47.constructor==="manual")?_35(_46):(_46.length==1?_32(_42.constructor,t):_2c(_46,t)); -_44._meta={bases:_46,hidden:_42,chains:_47,parents:_49,ctor:_42.constructor}; -_44.superclass=_41&&_41.prototype; -_44.extend=_2a; -_44.prototype=_43; -_43.constructor=_44; -_43.getInherited=_1d; -_43.inherited=_13; -_43.isInstanceOf=_20; -if(_40){ -_43.declaredClass=_40; -d.setObject(_40,_44); -} -if(_47){ -for(_45 in _47){ -if(_43[_45]&&typeof _47[_45]=="string"&&_45!=_5){ -t=_43[_45]=_37(_45,_46,_47[_45]==="after"); -t.nom=_45; -} -} -} -return _44; -}; -d.safeMixin=_26; + var d = dojo, mix = d._mixin, op = Object.prototype, opts = op.toString, + xtor = new Function, counter = 0, cname = "constructor"; + + function err(msg){ throw new Error("declare: " + msg); } + + // C3 Method Resolution Order (see http://www.python.org/download/releases/2.3/mro/) + function c3mro(bases){ + var result = [], roots = [{cls: 0, refs: []}], nameMap = {}, clsCount = 1, + l = bases.length, i = 0, j, lin, base, top, proto, rec, name, refs; + + // build a list of bases naming them if needed + for(; i < l; ++i){ + base = bases[i]; + if(!base){ + err("mixin #" + i + " is unknown. Did you use dojo.require to pull it in?"); + }else if(opts.call(base) != "[object Function]"){ + err("mixin #" + i + " is not a callable constructor."); + } + lin = base._meta ? base._meta.bases : [base]; + top = 0; + // add bases to the name map + for(j = lin.length - 1; j >= 0; --j){ + proto = lin[j].prototype; + if(!proto.hasOwnProperty("declaredClass")){ + proto.declaredClass = "uniqName_" + (counter++); + } + name = proto.declaredClass; + if(!nameMap.hasOwnProperty(name)){ + nameMap[name] = {count: 0, refs: [], cls: lin[j]}; + ++clsCount; + } + rec = nameMap[name]; + if(top && top !== rec){ + rec.refs.push(top); + ++top.count; + } + top = rec; + } + ++top.count; + roots[0].refs.push(top); + } + + // remove classes without external references recursively + while(roots.length){ + top = roots.pop(); + result.push(top.cls); + --clsCount; + // optimization: follow a single-linked chain + while(refs = top.refs, refs.length == 1){ + top = refs[0]; + if(!top || --top.count){ + // branch or end of chain => do not end to roots + top = 0; + break; + } + result.push(top.cls); + --clsCount; + } + if(top){ + // branch + for(i = 0, l = refs.length; i < l; ++i){ + top = refs[i]; + if(!--top.count){ + roots.push(top); + } + } + } + } + if(clsCount){ + err("can't build consistent linearization"); + } + + // calculate the superclass offset + base = bases[0]; + result[0] = base ? + base._meta && base === result[result.length - base._meta.bases.length] ? + base._meta.bases.length : 1 : 0; + + return result; + } + + function inherited(args, a, f){ + var name, chains, bases, caller, meta, base, proto, opf, pos, + cache = this._inherited = this._inherited || {}; + + // crack arguments + if(typeof args == "string"){ + name = args; + args = a; + a = f; + } + f = 0; + + caller = args.callee; + name = name || caller.nom; + if(!name){ + err("can't deduce a name to call inherited()"); + } + + meta = this.constructor._meta; + bases = meta.bases; + + pos = cache.p; + if(name != cname){ + // method + if(cache.c !== caller){ + // cache bust + pos = 0; + base = bases[0]; + meta = base._meta; + if(meta.hidden[name] !== caller){ + // error detection + chains = meta.chains; + if(chains && typeof chains[name] == "string"){ + err("calling chained method with inherited: " + name); + } + // find caller + do{ + meta = base._meta; + proto = base.prototype; + if(meta && (proto[name] === caller && proto.hasOwnProperty(name) || meta.hidden[name] === caller)){ + break; + } + }while(base = bases[++pos]); // intentional assignment + pos = base ? pos : -1; + } + } + // find next + base = bases[++pos]; + if(base){ + proto = base.prototype; + if(base._meta && proto.hasOwnProperty(name)){ + f = proto[name]; + }else{ + opf = op[name]; + do{ + proto = base.prototype; + f = proto[name]; + if(f && (base._meta ? proto.hasOwnProperty(name) : f !== opf)){ + break; + } + }while(base = bases[++pos]); // intentional assignment + } + } + f = base && f || op[name]; + }else{ + // constructor + if(cache.c !== caller){ + // cache bust + pos = 0; + meta = bases[0]._meta; + if(meta && meta.ctor !== caller){ + // error detection + chains = meta.chains; + if(!chains || chains.constructor !== "manual"){ + err("calling chained constructor with inherited"); + } + // find caller + while(base = bases[++pos]){ // intentional assignment + meta = base._meta; + if(meta && meta.ctor === caller){ + break; + } + } + pos = base ? pos : -1; + } + } + // find next + while(base = bases[++pos]){ // intentional assignment + meta = base._meta; + f = meta ? meta.ctor : base; + if(f){ + break; + } + } + f = base && f; + } + + // cache the found super method + cache.c = f; + cache.p = pos; + + // now we have the result + if(f){ + return a === true ? f : f.apply(this, a || args); + } + // intentionally if a super method was not found + } + + function getInherited(name, args){ + if(typeof name == "string"){ + return this.inherited(name, args, true); + } + return this.inherited(name, true); + } + + // emulation of "instanceof" + function isInstanceOf(cls){ + var bases = this.constructor._meta.bases; + for(var i = 0, l = bases.length; i < l; ++i){ + if(bases[i] === cls){ + return true; + } + } + return this instanceof cls; + } + + function mixOwn(target, source){ + var name, i = 0, l = d._extraNames.length; + // add props adding metadata for incoming functions skipping a constructor + for(name in source){ + if(name != cname && source.hasOwnProperty(name)){ + target[name] = source[name]; + } + } + // process unenumerable methods on IE + for(; i < l; ++i){ + name = d._extraNames[i]; + if(name != cname && source.hasOwnProperty(name)){ + target[name] = source[name]; + } + } + } + + // implementation of safe mixin function + function safeMixin(target, source){ + var name, t, i = 0, l = d._extraNames.length; + // add props adding metadata for incoming functions skipping a constructor + for(name in source){ + t = source[name]; + if((t !== op[name] || !(name in op)) && name != cname){ + if(opts.call(t) == "[object Function]"){ + // non-trivial function method => attach its name + t.nom = name; + } + target[name] = t; + } + } + // process unenumerable methods on IE + for(; i < l; ++i){ + name = d._extraNames[i]; + t = source[name]; + if((t !== op[name] || !(name in op)) && name != cname){ + if(opts.call(t) == "[object Function]"){ + // non-trivial function method => attach its name + t.nom = name; + } + target[name] = t; + } + } + return target; + } + + function extend(source){ + safeMixin(this.prototype, source); + return this; + } + + // chained constructor compatible with the legacy dojo.declare() + function chainedConstructor(bases, ctorSpecial){ + return function(){ + var a = arguments, args = a, a0 = a[0], f, i, m, + l = bases.length, preArgs; + + if(!(this instanceof a.callee)){ + // not called via new, so force it + return applyNew(a); + } + + //this._inherited = {}; + // perform the shaman's rituals of the original dojo.declare() + // 1) call two types of the preamble + if(ctorSpecial && (a0 && a0.preamble || this.preamble)){ + // full blown ritual + preArgs = new Array(bases.length); + // prepare parameters + preArgs[0] = a; + for(i = 0;;){ + // process the preamble of the 1st argument + a0 = a[0]; + if(a0){ + f = a0.preamble; + if(f){ + a = f.apply(this, a) || a; + } + } + // process the preamble of this class + f = bases[i].prototype; + f = f.hasOwnProperty("preamble") && f.preamble; + if(f){ + a = f.apply(this, a) || a; + } + // one peculiarity of the preamble: + // it is called if it is not needed, + // e.g., there is no constructor to call + // let's watch for the last constructor + // (see ticket #9795) + if(++i == l){ + break; + } + preArgs[i] = a; + } + } + // 2) call all non-trivial constructors using prepared arguments + for(i = l - 1; i >= 0; --i){ + f = bases[i]; + m = f._meta; + f = m ? m.ctor : f; + if(f){ + f.apply(this, preArgs ? preArgs[i] : a); + } + } + // 3) continue the original ritual: call the postscript + f = this.postscript; + if(f){ + f.apply(this, args); + } + }; + } + + + // chained constructor compatible with the legacy dojo.declare() + function singleConstructor(ctor, ctorSpecial){ + return function(){ + var a = arguments, t = a, a0 = a[0], f; + + if(!(this instanceof a.callee)){ + // not called via new, so force it + return applyNew(a); + } + + //this._inherited = {}; + // perform the shaman's rituals of the original dojo.declare() + // 1) call two types of the preamble + if(ctorSpecial){ + // full blown ritual + if(a0){ + // process the preamble of the 1st argument + f = a0.preamble; + if(f){ + t = f.apply(this, t) || t; + } + } + f = this.preamble; + if(f){ + // process the preamble of this class + f.apply(this, t); + // one peculiarity of the preamble: + // it is called even if it is not needed, + // e.g., there is no constructor to call + // let's watch for the last constructor + // (see ticket #9795) + } + } + // 2) call a constructor + if(ctor){ + ctor.apply(this, a); + } + // 3) continue the original ritual: call the postscript + f = this.postscript; + if(f){ + f.apply(this, a); + } + }; + } + + // plain vanilla constructor (can use inherited() to call its base constructor) + function simpleConstructor(bases){ + return function(){ + var a = arguments, i = 0, f, m; + + if(!(this instanceof a.callee)){ + // not called via new, so force it + return applyNew(a); + } + + //this._inherited = {}; + // perform the shaman's rituals of the original dojo.declare() + // 1) do not call the preamble + // 2) call the top constructor (it can use this.inherited()) + for(; f = bases[i]; ++i){ // intentional assignment + m = f._meta; + f = m ? m.ctor : f; + if(f){ + f.apply(this, a); + break; + } + } + // 3) call the postscript + f = this.postscript; + if(f){ + f.apply(this, a); + } + }; + } + + function chain(name, bases, reversed){ + return function(){ + var b, m, f, i = 0, step = 1; + if(reversed){ + i = bases.length - 1; + step = -1; + } + for(; b = bases[i]; i += step){ // intentional assignment + m = b._meta; + f = (m ? m.hidden : b.prototype)[name]; + if(f){ + f.apply(this, arguments); + } + } + }; + } + + // forceNew(ctor) + // return a new object that inherits from ctor.prototype but + // without actually running ctor on the object. + function forceNew(ctor){ + // create object with correct prototype using a do-nothing + // constructor + xtor.prototype = ctor.prototype; + var t = new xtor; + xtor.prototype = null; // clean up + return t; + } + + // applyNew(args) + // just like 'new ctor()' except that the constructor and its arguments come + // from args, which must be an array or an arguments object + function applyNew(args){ + // create an object with ctor's prototype but without + // calling ctor on it. + var ctor = args.callee, t = forceNew(ctor); + // execute the real constructor on the new object + ctor.apply(t, args); + return t; + } + + d.declare = function(className, superclass, props){ + // crack parameters + if(typeof className != "string"){ + props = superclass; + superclass = className; + className = ""; + } + props = props || {}; + + var proto, i, t, ctor, name, bases, chains, mixins = 1, parents = superclass; + + // build a prototype + if(opts.call(superclass) == "[object Array]"){ + // C3 MRO + bases = c3mro(superclass); + t = bases[0]; + mixins = bases.length - t; + superclass = bases[mixins]; + }else{ + bases = [0]; + if(superclass){ + if(opts.call(superclass) == "[object Function]"){ + t = superclass._meta; + bases = bases.concat(t ? t.bases : superclass); + }else{ + err("base class is not a callable constructor."); + } + }else if(superclass !== null){ + err("unknown base class. Did you use dojo.require to pull it in?") + } + } + if(superclass){ + for(i = mixins - 1;; --i){ + proto = forceNew(superclass); + if(!i){ + // stop if nothing to add (the last base) + break; + } + // mix in properties + t = bases[i]; + (t._meta ? mixOwn : mix)(proto, t.prototype); + // chain in new constructor + ctor = new Function; + ctor.superclass = superclass; + ctor.prototype = proto; + superclass = proto.constructor = ctor; + } + }else{ + proto = {}; + } + // add all properties + safeMixin(proto, props); + // add constructor + t = props.constructor; + if(t !== op.constructor){ + t.nom = cname; + proto.constructor = t; + } + + // collect chains and flags + for(i = mixins - 1; i; --i){ // intentional assignment + t = bases[i]._meta; + if(t && t.chains){ + chains = mix(chains || {}, t.chains); + } + } + if(proto["-chains-"]){ + chains = mix(chains || {}, proto["-chains-"]); + } + + // build ctor + t = !chains || !chains.hasOwnProperty(cname); + bases[0] = ctor = (chains && chains.constructor === "manual") ? simpleConstructor(bases) : + (bases.length == 1 ? singleConstructor(props.constructor, t) : chainedConstructor(bases, t)); + + // add meta information to the constructor + ctor._meta = {bases: bases, hidden: props, chains: chains, + parents: parents, ctor: props.constructor}; + ctor.superclass = superclass && superclass.prototype; + ctor.extend = extend; + ctor.prototype = proto; + proto.constructor = ctor; + + // add "standard" methods to the prototype + proto.getInherited = getInherited; + proto.inherited = inherited; + proto.isInstanceOf = isInstanceOf; + + // add name if specified + if(className){ + proto.declaredClass = className; + d.setObject(className, ctor); + } + + // build chains and add them to the prototype + if(chains){ + for(name in chains){ + if(proto[name] && typeof chains[name] == "string" && name != cname){ + t = proto[name] = chain(name, bases, chains[name] === "after"); + t.nom = name; + } + } + } + // chained methods do not return values + // no need to chain "invisible" functions + + return ctor; // Function + }; + + d.safeMixin = safeMixin; + + /*===== + dojo.declare = function(className, superclass, props){ + // summary: + // Create a feature-rich constructor from compact notation. + // className: String?: + // The optional name of the constructor (loosely, a "class") + // stored in the "declaredClass" property in the created prototype. + // It will be used as a global name for a created constructor. + // superclass: Function|Function[]: + // May be null, a Function, or an Array of Functions. This argument + // specifies a list of bases (the left-most one is the most deepest + // base). + // props: Object: + // An object whose properties are copied to the created prototype. + // Add an instance-initialization function by making it a property + // named "constructor". + // returns: + // New constructor function. + // description: + // Create a constructor using a compact notation for inheritance and + // prototype extension. + // + // Mixin ancestors provide a type of multiple inheritance. + // Prototypes of mixin ancestors are copied to the new class: + // changes to mixin prototypes will not affect classes to which + // they have been mixed in. + // + // Ancestors can be compound classes created by this version of + // dojo.declare. In complex cases all base classes are going to be + // linearized according to C3 MRO algorithm + // (see http://www.python.org/download/releases/2.3/mro/ for more + // details). + // + // "className" is cached in "declaredClass" property of the new class, + // if it was supplied. The immediate super class will be cached in + // "superclass" property of the new class. + // + // Methods in "props" will be copied and modified: "nom" property + // (the declared name of the method) will be added to all copied + // functions to help identify them for the internal machinery. Be + // very careful, while reusing methods: if you use the same + // function under different names, it can produce errors in some + // cases. + // + // It is possible to use constructors created "manually" (without + // dojo.declare) as bases. They will be called as usual during the + // creation of an instance, their methods will be chained, and even + // called by "this.inherited()". + // + // Special property "-chains-" governs how to chain methods. It is + // a dictionary, which uses method names as keys, and hint strings + // as values. If a hint string is "after", this method will be + // called after methods of its base classes. If a hint string is + // "before", this method will be called before methods of its base + // classes. + // + // If "constructor" is not mentioned in "-chains-" property, it will + // be chained using the legacy mode: using "after" chaining, + // calling preamble() method before each constructor, if available, + // and calling postscript() after all constructors were executed. + // If the hint is "after", it is chained as a regular method, but + // postscript() will be called after the chain of constructors. + // "constructor" cannot be chained "before", but it allows + // a special hint string: "manual", which means that constructors + // are not going to be chained in any way, and programmer will call + // them manually using this.inherited(). In the latter case + // postscript() will be called after the construction. + // + // All chaining hints are "inherited" from base classes and + // potentially can be overridden. Be very careful when overriding + // hints! Make sure that all chained methods can work in a proposed + // manner of chaining. + // + // Once a method was chained, it is impossible to unchain it. The + // only exception is "constructor". You don't need to define a + // method in order to supply a chaining hint. + // + // If a method is chained, it cannot use this.inherited() because + // all other methods in the hierarchy will be called automatically. + // + // Usually constructors and initializers of any kind are chained + // using "after" and destructors of any kind are chained as + // "before". Note that chaining assumes that chained methods do not + // return any value: any returned value will be discarded. + // + // example: + // | dojo.declare("my.classes.bar", my.classes.foo, { + // | // properties to be added to the class prototype + // | someValue: 2, + // | // initialization function + // | constructor: function(){ + // | this.myComplicatedObject = new ReallyComplicatedObject(); + // | }, + // | // other functions + // | someMethod: function(){ + // | doStuff(); + // | } + // | }); + // + // example: + // | var MyBase = dojo.declare(null, { + // | // constructor, properties, and methods go here + // | // ... + // | }); + // | var MyClass1 = dojo.declare(MyBase, { + // | // constructor, properties, and methods go here + // | // ... + // | }); + // | var MyClass2 = dojo.declare(MyBase, { + // | // constructor, properties, and methods go here + // | // ... + // | }); + // | var MyDiamond = dojo.declare([MyClass1, MyClass2], { + // | // constructor, properties, and methods go here + // | // ... + // | }); + // + // example: + // | var F = function(){ console.log("raw constructor"); }; + // | F.prototype.method = function(){ + // | console.log("raw method"); + // | }; + // | var A = dojo.declare(F, { + // | constructor: function(){ + // | console.log("A.constructor"); + // | }, + // | method: function(){ + // | console.log("before calling F.method..."); + // | this.inherited(arguments); + // | console.log("...back in A"); + // | } + // | }); + // | new A().method(); + // | // will print: + // | // raw constructor + // | // A.constructor + // | // before calling F.method... + // | // raw method + // | // ...back in A + // + // example: + // | var A = dojo.declare(null, { + // | "-chains-": { + // | destroy: "before" + // | } + // | }); + // | var B = dojo.declare(A, { + // | constructor: function(){ + // | console.log("B.constructor"); + // | }, + // | destroy: function(){ + // | console.log("B.destroy"); + // | } + // | }); + // | var C = dojo.declare(B, { + // | constructor: function(){ + // | console.log("C.constructor"); + // | }, + // | destroy: function(){ + // | console.log("C.destroy"); + // | } + // | }); + // | new C().destroy(); + // | // prints: + // | // B.constructor + // | // C.constructor + // | // C.destroy + // | // B.destroy + // + // example: + // | var A = dojo.declare(null, { + // | "-chains-": { + // | constructor: "manual" + // | } + // | }); + // | var B = dojo.declare(A, { + // | constructor: function(){ + // | // ... + // | // call the base constructor with new parameters + // | this.inherited(arguments, [1, 2, 3]); + // | // ... + // | } + // | }); + // + // example: + // | var A = dojo.declare(null, { + // | "-chains-": { + // | m1: "before" + // | }, + // | m1: function(){ + // | console.log("A.m1"); + // | }, + // | m2: function(){ + // | console.log("A.m2"); + // | } + // | }); + // | var B = dojo.declare(A, { + // | "-chains-": { + // | m2: "after" + // | }, + // | m1: function(){ + // | console.log("B.m1"); + // | }, + // | m2: function(){ + // | console.log("B.m2"); + // | } + // | }); + // | var x = new B(); + // | x.m1(); + // | // prints: + // | // B.m1 + // | // A.m1 + // | x.m2(); + // | // prints: + // | // A.m2 + // | // B.m2 + return new Function(); // Function + }; + =====*/ + + /*===== + dojo.safeMixin = function(target, source){ + // summary: + // Mix in properties skipping a constructor and decorating functions + // like it is done by dojo.declare. + // target: Object + // Target object to accept new properties. + // source: Object + // Source object for new properties. + // description: + // This function is used to mix in properties like dojo._mixin does, + // but it skips a constructor property and decorates functions like + // dojo.declare does. + // + // It is meant to be used with classes and objects produced with + // dojo.declare. Functions mixed in with dojo.safeMixin can use + // this.inherited() like normal methods. + // + // This function is used to implement extend() method of a constructor + // produced with dojo.declare(). + // + // example: + // | var A = dojo.declare(null, { + // | m1: function(){ + // | console.log("A.m1"); + // | }, + // | m2: function(){ + // | console.log("A.m2"); + // | } + // | }); + // | var B = dojo.declare(A, { + // | m1: function(){ + // | this.inherited(arguments); + // | console.log("B.m1"); + // | } + // | }); + // | B.extend({ + // | m2: function(){ + // | this.inherited(arguments); + // | console.log("B.m2"); + // | } + // | }); + // | var x = new B(); + // | dojo.safeMixin(x, { + // | m1: function(){ + // | this.inherited(arguments); + // | console.log("X.m1"); + // | }, + // | m2: function(){ + // | this.inherited(arguments); + // | console.log("X.m2"); + // | } + // | }); + // | x.m2(); + // | // prints: + // | // A.m1 + // | // B.m1 + // | // X.m1 + }; + =====*/ + + /*===== + Object.inherited = function(name, args, newArgs){ + // summary: + // Calls a super method. + // name: String? + // The optional method name. Should be the same as the caller's + // name. Usually "name" is specified in complex dynamic cases, when + // the calling method was dynamically added, undecorated by + // dojo.declare, and it cannot be determined. + // args: Arguments + // The caller supply this argument, which should be the original + // "arguments". + // newArgs: Object? + // If "true", the found function will be returned without + // executing it. + // If Array, it will be used to call a super method. Otherwise + // "args" will be used. + // returns: + // Whatever is returned by a super method, or a super method itself, + // if "true" was specified as newArgs. + // description: + // This method is used inside method of classes produced with + // dojo.declare to call a super method (next in the chain). It is + // used for manually controlled chaining. Consider using the regular + // chaining, because it is faster. Use "this.inherited()" only in + // complex cases. + // + // This method cannot me called from automatically chained + // constructors including the case of a special (legacy) + // constructor chaining. It cannot be called from chained methods. + // + // If "this.inherited()" cannot find the next-in-chain method, it + // does nothing and returns "undefined". The last method in chain + // can be a default method implemented in Object, which will be + // called last. + // + // If "name" is specified, it is assumed that the method that + // received "args" is the parent method for this call. It is looked + // up in the chain list and if it is found the next-in-chain method + // is called. If it is not found, the first-in-chain method is + // called. + // + // If "name" is not specified, it will be derived from the calling + // method (using a methoid property "nom"). + // + // example: + // | var B = dojo.declare(A, { + // | method1: function(a, b, c){ + // | this.inherited(arguments); + // | }, + // | method2: function(a, b){ + // | return this.inherited(arguments, [a + b]); + // | } + // | }); + // | // next method is not in the chain list because it is added + // | // manually after the class was created. + // | B.prototype.method3 = function(){ + // | console.log("This is a dynamically-added method."); + // | this.inherited("method3", arguments); + // | }; + // example: + // | var B = dojo.declare(A, { + // | method: function(a, b){ + // | var super = this.inherited(arguments, true); + // | // ... + // | if(!super){ + // | console.log("there is no super method"); + // | return 0; + // | } + // | return super.apply(this, arguments); + // | } + // | }); + return {}; // Object + } + =====*/ + + /*===== + Object.getInherited = function(name, args){ + // summary: + // Returns a super method. + // name: String? + // The optional method name. Should be the same as the caller's + // name. Usually "name" is specified in complex dynamic cases, when + // the calling method was dynamically added, undecorated by + // dojo.declare, and it cannot be determined. + // args: Arguments + // The caller supply this argument, which should be the original + // "arguments". + // returns: + // Returns a super method (Function) or "undefined". + // description: + // This method is a convenience method for "this.inherited()". + // It uses the same algorithm but instead of executing a super + // method, it returns it, or "undefined" if not found. + // + // example: + // | var B = dojo.declare(A, { + // | method: function(a, b){ + // | var super = this.getInherited(arguments); + // | // ... + // | if(!super){ + // | console.log("there is no super method"); + // | return 0; + // | } + // | return super.apply(this, arguments); + // | } + // | }); + return {}; // Object + } + =====*/ + + /*===== + Object.isInstanceOf = function(cls){ + // summary: + // Checks the inheritance chain to see if it is inherited from this + // class. + // cls: Function + // Class constructor. + // returns: + // "true", if this object is inherited from this class, "false" + // otherwise. + // description: + // This method is used with instances of classes produced with + // dojo.declare to determine of they support a certain interface or + // not. It models "instanceof" operator. + // + // example: + // | var A = dojo.declare(null, { + // | // constructor, properties, and methods go here + // | // ... + // | }); + // | var B = dojo.declare(null, { + // | // constructor, properties, and methods go here + // | // ... + // | }); + // | var C = dojo.declare([A, B], { + // | // constructor, properties, and methods go here + // | // ... + // | }); + // | var D = dojo.declare(A, { + // | // constructor, properties, and methods go here + // | // ... + // | }); + // | + // | var a = new A(), b = new B(), c = new C(), d = new D(); + // | + // | console.log(a.isInstanceOf(A)); // true + // | console.log(b.isInstanceOf(A)); // false + // | console.log(c.isInstanceOf(A)); // true + // | console.log(d.isInstanceOf(A)); // true + // | + // | console.log(a.isInstanceOf(B)); // false + // | console.log(b.isInstanceOf(B)); // true + // | console.log(c.isInstanceOf(B)); // true + // | console.log(d.isInstanceOf(B)); // false + // | + // | console.log(a.isInstanceOf(C)); // false + // | console.log(b.isInstanceOf(C)); // false + // | console.log(c.isInstanceOf(C)); // true + // | console.log(d.isInstanceOf(C)); // false + // | + // | console.log(a.isInstanceOf(D)); // false + // | console.log(b.isInstanceOf(D)); // false + // | console.log(c.isInstanceOf(D)); // false + // | console.log(d.isInstanceOf(D)); // true + return {}; // Object + } + =====*/ + + /*===== + Object.extend = function(source){ + // summary: + // Adds all properties and methods of source to constructor's + // prototype, making them available to all instances created with + // constructor. This method is specific to constructors created with + // dojo.declare. + // source: Object + // Source object which properties are going to be copied to the + // constructor's prototype. + // description: + // Adds source properties to the constructor's prototype. It can + // override existing properties. + // + // This method is similar to dojo.extend function, but it is specific + // to constructors produced by dojo.declare. It is implemented + // using dojo.safeMixin, and it skips a constructor property, + // and properly decorates copied functions. + // + // example: + // | var A = dojo.declare(null, { + // | m1: function(){}, + // | s1: "Popokatepetl" + // | }); + // | A.extend({ + // | m1: function(){}, + // | m2: function(){}, + // | f1: true, + // | d1: 42 + // | }); + }; + =====*/ })(); + } diff --git a/lib/dojo/_base/event.js b/lib/dojo/_base/event.js index 1e6ef788a..5268c6cff 100644 --- a/lib/dojo/_base/event.js +++ b/lib/dojo/_base/event.js @@ -5,355 +5,641 @@ */ -if(!dojo._hasResource["dojo._base.event"]){ -dojo._hasResource["dojo._base.event"]=true; +if(!dojo._hasResource["dojo._base.event"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo._base.event"] = true; dojo.provide("dojo._base.event"); dojo.require("dojo._base.connect"); + +// this file courtesy of the TurboAjax Group, licensed under a Dojo CLA + (function(){ -var _1=(dojo._event_listener={add:function(_2,_3,fp){ -if(!_2){ -return; -} -_3=_1._normalizeEventName(_3); -fp=_1._fixCallback(_3,fp); -var _4=_3; -if(!dojo.isIE&&(_3=="mouseenter"||_3=="mouseleave")){ -var _5=fp; -_3=(_3=="mouseenter")?"mouseover":"mouseout"; -fp=function(e){ -if(!dojo.isDescendant(e.relatedTarget,_2)){ -return _5.call(this,e); -} -}; -} -_2.addEventListener(_3,fp,false); -return fp; -},remove:function(_6,_7,_8){ -if(_6){ -_7=_1._normalizeEventName(_7); -if(!dojo.isIE&&(_7=="mouseenter"||_7=="mouseleave")){ -_7=(_7=="mouseenter")?"mouseover":"mouseout"; -} -_6.removeEventListener(_7,_8,false); -} -},_normalizeEventName:function(_9){ -return _9.slice(0,2)=="on"?_9.slice(2):_9; -},_fixCallback:function(_a,fp){ -return _a!="keypress"?fp:function(e){ -return fp.call(this,_1._fixEvent(e,this)); -}; -},_fixEvent:function(_b,_c){ -switch(_b.type){ -case "keypress": -_1._setKeyChar(_b); -break; -} -return _b; -},_setKeyChar:function(_d){ -_d.keyChar=_d.charCode?String.fromCharCode(_d.charCode):""; -_d.charOrCode=_d.keyChar||_d.keyCode; -},_punctMap:{106:42,111:47,186:59,187:43,188:44,189:45,190:46,191:47,192:96,219:91,220:92,221:93,222:39}}); -dojo.fixEvent=function(_e,_f){ -return _1._fixEvent(_e,_f); -}; -dojo.stopEvent=function(evt){ -evt.preventDefault(); -evt.stopPropagation(); -}; -var _10=dojo._listener; -dojo._connect=function(obj,_11,_12,_13,_14){ -var _15=obj&&(obj.nodeType||obj.attachEvent||obj.addEventListener); -var lid=_15?(_14?2:1):0,l=[dojo._listener,_1,_10][lid]; -var h=l.add(obj,_11,dojo.hitch(_12,_13)); -return [obj,_11,h,lid]; -}; -dojo._disconnect=function(obj,_16,_17,_18){ -([dojo._listener,_1,_10][_18]).remove(obj,_16,_17); -}; -dojo.keys={BACKSPACE:8,TAB:9,CLEAR:12,ENTER:13,SHIFT:16,CTRL:17,ALT:18,META:dojo.isSafari?91:224,PAUSE:19,CAPS_LOCK:20,ESCAPE:27,SPACE:32,PAGE_UP:33,PAGE_DOWN:34,END:35,HOME:36,LEFT_ARROW:37,UP_ARROW:38,RIGHT_ARROW:39,DOWN_ARROW:40,INSERT:45,DELETE:46,HELP:47,LEFT_WINDOW:91,RIGHT_WINDOW:92,SELECT:93,NUMPAD_0:96,NUMPAD_1:97,NUMPAD_2:98,NUMPAD_3:99,NUMPAD_4:100,NUMPAD_5:101,NUMPAD_6:102,NUMPAD_7:103,NUMPAD_8:104,NUMPAD_9:105,NUMPAD_MULTIPLY:106,NUMPAD_PLUS:107,NUMPAD_ENTER:108,NUMPAD_MINUS:109,NUMPAD_PERIOD:110,NUMPAD_DIVIDE:111,F1:112,F2:113,F3:114,F4:115,F5:116,F6:117,F7:118,F8:119,F9:120,F10:121,F11:122,F12:123,F13:124,F14:125,F15:126,NUM_LOCK:144,SCROLL_LOCK:145,copyKey:dojo.isMac&&!dojo.isAIR?(dojo.isSafari?91:224):17}; -var _19=dojo.isMac?"metaKey":"ctrlKey"; -dojo.isCopyKey=function(e){ -return e[_19]; -}; -if(dojo.isIE){ -dojo.mouseButtons={LEFT:1,MIDDLE:4,RIGHT:2,isButton:function(e,_1a){ -return e.button&_1a; -},isLeft:function(e){ -return e.button&1; -},isMiddle:function(e){ -return e.button&4; -},isRight:function(e){ -return e.button&2; -}}; -}else{ -dojo.mouseButtons={LEFT:0,MIDDLE:1,RIGHT:2,isButton:function(e,_1b){ -return e.button==_1b; -},isLeft:function(e){ -return e.button==0; -},isMiddle:function(e){ -return e.button==1; -},isRight:function(e){ -return e.button==2; -}}; -} -if(dojo.isIE){ -var _1c=function(e,_1d){ -try{ -return (e.keyCode=_1d); -} -catch(e){ -return 0; -} -}; -var iel=dojo._listener; -var _1e=(dojo._ieListenersName="_"+dojo._scopeName+"_listeners"); -if(!dojo.config._allow_leaks){ -_10=iel=dojo._ie_listener={handlers:[],add:function(_1f,_20,_21){ -_1f=_1f||dojo.global; -var f=_1f[_20]; -if(!f||!f[_1e]){ -var d=dojo._getIeDispatcher(); -d.target=f&&(ieh.push(f)-1); -d[_1e]=[]; -f=_1f[_20]=d; -} -return f[_1e].push(ieh.push(_21)-1); -},remove:function(_22,_23,_24){ -var f=(_22||dojo.global)[_23],l=f&&f[_1e]; -if(f&&l&&_24--){ -delete ieh[l[_24]]; -delete l[_24]; -} -}}; -var ieh=iel.handlers; -} -dojo.mixin(_1,{add:function(_25,_26,fp){ -if(!_25){ -return; -} -_26=_1._normalizeEventName(_26); -if(_26=="onkeypress"){ -var kd=_25.onkeydown; -if(!kd||!kd[_1e]||!kd._stealthKeydownHandle){ -var h=_1.add(_25,"onkeydown",_1._stealthKeyDown); -kd=_25.onkeydown; -kd._stealthKeydownHandle=h; -kd._stealthKeydownRefs=1; -}else{ -kd._stealthKeydownRefs++; -} -} -return iel.add(_25,_26,_1._fixCallback(fp)); -},remove:function(_27,_28,_29){ -_28=_1._normalizeEventName(_28); -iel.remove(_27,_28,_29); -if(_28=="onkeypress"){ -var kd=_27.onkeydown; -if(--kd._stealthKeydownRefs<=0){ -iel.remove(_27,"onkeydown",kd._stealthKeydownHandle); -delete kd._stealthKeydownHandle; -} -} -},_normalizeEventName:function(_2a){ -return _2a.slice(0,2)!="on"?"on"+_2a:_2a; -},_nop:function(){ -},_fixEvent:function(evt,_2b){ -if(!evt){ -var w=_2b&&(_2b.ownerDocument||_2b.document||_2b).parentWindow||window; -evt=w.event; -} -if(!evt){ -return (evt); -} -evt.target=evt.srcElement; -evt.currentTarget=(_2b||evt.srcElement); -evt.layerX=evt.offsetX; -evt.layerY=evt.offsetY; -var se=evt.srcElement,doc=(se&&se.ownerDocument)||document; -var _2c=((dojo.isIE<6)||(doc["compatMode"]=="BackCompat"))?doc.body:doc.documentElement; -var _2d=dojo._getIeDocumentElementOffset(); -evt.pageX=evt.clientX+dojo._fixIeBiDiScrollLeft(_2c.scrollLeft||0)-_2d.x; -evt.pageY=evt.clientY+(_2c.scrollTop||0)-_2d.y; -if(evt.type=="mouseover"){ -evt.relatedTarget=evt.fromElement; -} -if(evt.type=="mouseout"){ -evt.relatedTarget=evt.toElement; -} -evt.stopPropagation=_1._stopPropagation; -evt.preventDefault=_1._preventDefault; -return _1._fixKeys(evt); -},_fixKeys:function(evt){ -switch(evt.type){ -case "keypress": -var c=("charCode" in evt?evt.charCode:evt.keyCode); -if(c==10){ -c=0; -evt.keyCode=13; -}else{ -if(c==13||c==27){ -c=0; -}else{ -if(c==3){ -c=99; -} -} -} -evt.charCode=c; -_1._setKeyChar(evt); -break; -} -return evt; -},_stealthKeyDown:function(evt){ -var kp=evt.currentTarget.onkeypress; -if(!kp||!kp[_1e]){ -return; -} -var k=evt.keyCode; -var _2e=k!=13&&k!=32&&k!=27&&(k<48||k>90)&&(k<96||k>111)&&(k<186||k>192)&&(k<219||k>222); -if(_2e||evt.ctrlKey){ -var c=_2e?0:k; -if(evt.ctrlKey){ -if(k==3||k==13){ -return; -}else{ -if(c>95&&c<106){ -c-=48; -}else{ -if((!evt.shiftKey)&&(c>=65&&c<=90)){ -c+=32; -}else{ -c=_1._punctMap[c]||c; -} -} -} -} -var _2f=_1._synthesizeEvent(evt,{type:"keypress",faux:true,charCode:c}); -kp.call(evt.currentTarget,_2f); -evt.cancelBubble=_2f.cancelBubble; -evt.returnValue=_2f.returnValue; -_1c(evt,_2f.keyCode); -} -},_stopPropagation:function(){ -this.cancelBubble=true; -},_preventDefault:function(){ -this.bubbledKeyCode=this.keyCode; -if(this.ctrlKey){ -_1c(this,0); -} -this.returnValue=false; -}}); -dojo.stopEvent=function(evt){ -evt=evt||window.event; -_1._stopPropagation.call(evt); -_1._preventDefault.call(evt); -}; -} -_1._synthesizeEvent=function(evt,_30){ -var _31=dojo.mixin({},evt,_30); -_1._setKeyChar(_31); -_31.preventDefault=function(){ -evt.preventDefault(); -}; -_31.stopPropagation=function(){ -evt.stopPropagation(); -}; -return _31; -}; -if(dojo.isOpera){ -dojo.mixin(_1,{_fixEvent:function(evt,_32){ -switch(evt.type){ -case "keypress": -var c=evt.which; -if(c==3){ -c=99; -} -c=c<41&&!evt.shiftKey?0:c; -if(evt.ctrlKey&&!evt.shiftKey&&c>=65&&c<=90){ -c+=32; -} -return _1._synthesizeEvent(evt,{charCode:c}); -} -return evt; -}}); -} -if(dojo.isWebKit){ -_1._add=_1.add; -_1._remove=_1.remove; -dojo.mixin(_1,{add:function(_33,_34,fp){ -if(!_33){ -return; -} -var _35=_1._add(_33,_34,fp); -if(_1._normalizeEventName(_34)=="keypress"){ -_35._stealthKeyDownHandle=_1._add(_33,"keydown",function(evt){ -var k=evt.keyCode; -var _36=k!=13&&k!=32&&(k<48||k>90)&&(k<96||k>111)&&(k<186||k>192)&&(k<219||k>222); -if(_36||evt.ctrlKey){ -var c=_36?0:k; -if(evt.ctrlKey){ -if(k==3||k==13){ -return; -}else{ -if(c>95&&c<106){ -c-=48; -}else{ -if(!evt.shiftKey&&c>=65&&c<=90){ -c+=32; -}else{ -c=_1._punctMap[c]||c; -} -} -} -} -var _37=_1._synthesizeEvent(evt,{type:"keypress",faux:true,charCode:c}); -fp.call(evt.currentTarget,_37); -} -}); -} -return _35; -},remove:function(_38,_39,_3a){ -if(_38){ -if(_3a._stealthKeyDownHandle){ -_1._remove(_38,"keydown",_3a._stealthKeyDownHandle); -} -_1._remove(_38,_39,_3a); -} -},_fixEvent:function(evt,_3b){ -switch(evt.type){ -case "keypress": -if(evt.faux){ -return evt; -} -var c=evt.charCode; -c=c>=32?c:0; -return _1._synthesizeEvent(evt,{charCode:c,faux:true}); -} -return evt; -}}); -} -})(); + // DOM event listener machinery + var del = (dojo._event_listener = { + add: function(/*DOMNode*/ node, /*String*/ name, /*Function*/ fp){ + if(!node){return;} + name = del._normalizeEventName(name); + fp = del._fixCallback(name, fp); + var oname = name; + if( + !dojo.isIE && + (name == "mouseenter" || name == "mouseleave") + ){ + var ofp = fp; + //oname = name; + name = (name == "mouseenter") ? "mouseover" : "mouseout"; + fp = function(e){ + if(!dojo.isDescendant(e.relatedTarget, node)){ + // e.type = oname; // FIXME: doesn't take? SJM: event.type is generally immutable. + return ofp.call(this, e); + } + } + } + node.addEventListener(name, fp, false); + return fp; /*Handle*/ + }, + remove: function(/*DOMNode*/ node, /*String*/ event, /*Handle*/ handle){ + // summary: + // clobbers the listener from the node + // node: + // DOM node to attach the event to + // event: + // the name of the handler to remove the function from + // handle: + // the handle returned from add + if(node){ + event = del._normalizeEventName(event); + if(!dojo.isIE && (event == "mouseenter" || event == "mouseleave")){ + event = (event == "mouseenter") ? "mouseover" : "mouseout"; + } + + node.removeEventListener(event, handle, false); + } + }, + _normalizeEventName: function(/*String*/ name){ + // Generally, name should be lower case, unless it is special + // somehow (e.g. a Mozilla DOM event). + // Remove 'on'. + return name.slice(0,2) =="on" ? name.slice(2) : name; + }, + _fixCallback: function(/*String*/ name, fp){ + // By default, we only invoke _fixEvent for 'keypress' + // If code is added to _fixEvent for other events, we have + // to revisit this optimization. + // This also applies to _fixEvent overrides for Safari and Opera + // below. + return name != "keypress" ? fp : function(e){ return fp.call(this, del._fixEvent(e, this)); }; + }, + _fixEvent: function(evt, sender){ + // _fixCallback only attaches us to keypress. + // Switch on evt.type anyway because we might + // be called directly from dojo.fixEvent. + switch(evt.type){ + case "keypress": + del._setKeyChar(evt); + break; + } + return evt; + }, + _setKeyChar: function(evt){ + evt.keyChar = evt.charCode ? String.fromCharCode(evt.charCode) : ''; + evt.charOrCode = evt.keyChar || evt.keyCode; + }, + // For IE and Safari: some ctrl-key combinations (mostly w/punctuation) do not emit a char code in IE + // we map those virtual key codes to ascii here + // not valid for all (non-US) keyboards, so maybe we shouldn't bother + _punctMap: { + 106:42, + 111:47, + 186:59, + 187:43, + 188:44, + 189:45, + 190:46, + 191:47, + 192:96, + 219:91, + 220:92, + 221:93, + 222:39 + } + }); + + // DOM events + + dojo.fixEvent = function(/*Event*/ evt, /*DOMNode*/ sender){ + // summary: + // normalizes properties on the event object including event + // bubbling methods, keystroke normalization, and x/y positions + // evt: Event + // native event object + // sender: DOMNode + // node to treat as "currentTarget" + return del._fixEvent(evt, sender); + } + + dojo.stopEvent = function(/*Event*/ evt){ + // summary: + // prevents propagation and clobbers the default action of the + // passed event + // evt: Event + // The event object. If omitted, window.event is used on IE. + evt.preventDefault(); + evt.stopPropagation(); + // NOTE: below, this method is overridden for IE + } + + // the default listener to use on dontFix nodes, overriden for IE + var node_listener = dojo._listener; + + // Unify connect and event listeners + dojo._connect = function(obj, event, context, method, dontFix){ + // FIXME: need a more strict test + var isNode = obj && (obj.nodeType||obj.attachEvent||obj.addEventListener); + // choose one of three listener options: raw (connect.js), DOM event on a Node, custom event on a Node + // we need the third option to provide leak prevention on broken browsers (IE) + var lid = isNode ? (dontFix ? 2 : 1) : 0, l = [dojo._listener, del, node_listener][lid]; + // create a listener + var h = l.add(obj, event, dojo.hitch(context, method)); + // formerly, the disconnect package contained "l" directly, but if client code + // leaks the disconnect package (by connecting it to a node), referencing "l" + // compounds the problem. + // instead we return a listener id, which requires custom _disconnect below. + // return disconnect package + return [ obj, event, h, lid ]; + } + + dojo._disconnect = function(obj, event, handle, listener){ + ([dojo._listener, del, node_listener][listener]).remove(obj, event, handle); + } + + // Constants + + // Public: client code should test + // keyCode against these named constants, as the + // actual codes can vary by browser. + dojo.keys = { + // summary: + // Definitions for common key values + BACKSPACE: 8, + TAB: 9, + CLEAR: 12, + ENTER: 13, + SHIFT: 16, + CTRL: 17, + ALT: 18, + META: dojo.isSafari ? 91 : 224, // the apple key on macs + PAUSE: 19, + CAPS_LOCK: 20, + ESCAPE: 27, + SPACE: 32, + PAGE_UP: 33, + PAGE_DOWN: 34, + END: 35, + HOME: 36, + LEFT_ARROW: 37, + UP_ARROW: 38, + RIGHT_ARROW: 39, + DOWN_ARROW: 40, + INSERT: 45, + DELETE: 46, + HELP: 47, + LEFT_WINDOW: 91, + RIGHT_WINDOW: 92, + SELECT: 93, + NUMPAD_0: 96, + NUMPAD_1: 97, + NUMPAD_2: 98, + NUMPAD_3: 99, + NUMPAD_4: 100, + NUMPAD_5: 101, + NUMPAD_6: 102, + NUMPAD_7: 103, + NUMPAD_8: 104, + NUMPAD_9: 105, + NUMPAD_MULTIPLY: 106, + NUMPAD_PLUS: 107, + NUMPAD_ENTER: 108, + NUMPAD_MINUS: 109, + NUMPAD_PERIOD: 110, + NUMPAD_DIVIDE: 111, + F1: 112, + F2: 113, + F3: 114, + F4: 115, + F5: 116, + F6: 117, + F7: 118, + F8: 119, + F9: 120, + F10: 121, + F11: 122, + F12: 123, + F13: 124, + F14: 125, + F15: 126, + NUM_LOCK: 144, + SCROLL_LOCK: 145, + // virtual key mapping + copyKey: dojo.isMac && !dojo.isAIR ? (dojo.isSafari ? 91 : 224 ) : 17 + }; + + var evtCopyKey = dojo.isMac ? "metaKey" : "ctrlKey"; + + dojo.isCopyKey = function(e){ + // summary: + // Checks an event for the copy key (meta on Mac, and ctrl anywhere else) + // e: Event + // Event object to examine + return e[evtCopyKey]; // Boolean + }; + + // Public: decoding mouse buttons from events + +/*===== + dojo.mouseButtons = { + // LEFT: Number + // Numeric value of the left mouse button for the platform. + LEFT: 0, + // MIDDLE: Number + // Numeric value of the middle mouse button for the platform. + MIDDLE: 1, + // RIGHT: Number + // Numeric value of the right mouse button for the platform. + RIGHT: 2, + + isButton: function(e, button){ + // summary: + // Checks an event object for a pressed button + // e: Event + // Event object to examine + // button: Number + // The button value (example: dojo.mouseButton.LEFT) + return e.button == button; // Boolean + }, + isLeft: function(e){ + // summary: + // Checks an event object for the pressed left button + // e: Event + // Event object to examine + return e.button == 0; // Boolean + }, + isMiddle: function(e){ + // summary: + // Checks an event object for the pressed middle button + // e: Event + // Event object to examine + return e.button == 1; // Boolean + }, + isRight: function(e){ + // summary: + // Checks an event object for the pressed right button + // e: Event + // Event object to examine + return e.button == 2; // Boolean + } + }; +=====*/ + + if(dojo.isIE){ + dojo.mouseButtons = { + LEFT: 1, + MIDDLE: 4, + RIGHT: 2, + // helper functions + isButton: function(e, button){ return e.button & button; }, + isLeft: function(e){ return e.button & 1; }, + isMiddle: function(e){ return e.button & 4; }, + isRight: function(e){ return e.button & 2; } + }; + }else{ + dojo.mouseButtons = { + LEFT: 0, + MIDDLE: 1, + RIGHT: 2, + // helper functions + isButton: function(e, button){ return e.button == button; }, + isLeft: function(e){ return e.button == 0; }, + isMiddle: function(e){ return e.button == 1; }, + isRight: function(e){ return e.button == 2; } + }; + } + + // IE event normalization + if(dojo.isIE){ + var _trySetKeyCode = function(e, code){ + try{ + // squelch errors when keyCode is read-only + // (e.g. if keyCode is ctrl or shift) + return (e.keyCode = code); + }catch(e){ + return 0; + } + } + + // by default, use the standard listener + var iel = dojo._listener; + var listenersName = (dojo._ieListenersName = "_" + dojo._scopeName + "_listeners"); + // dispatcher tracking property + if(!dojo.config._allow_leaks){ + // custom listener that handles leak protection for DOM events + node_listener = iel = dojo._ie_listener = { + // support handler indirection: event handler functions are + // referenced here. Event dispatchers hold only indices. + handlers: [], + // add a listener to an object + add: function(/*Object*/ source, /*String*/ method, /*Function*/ listener){ + source = source || dojo.global; + var f = source[method]; + if(!f||!f[listenersName]){ + var d = dojo._getIeDispatcher(); + // original target function is special + d.target = f && (ieh.push(f) - 1); + // dispatcher holds a list of indices into handlers table + d[listenersName] = []; + // redirect source to dispatcher + f = source[method] = d; + } + return f[listenersName].push(ieh.push(listener) - 1) ; /*Handle*/ + }, + // remove a listener from an object + remove: function(/*Object*/ source, /*String*/ method, /*Handle*/ handle){ + var f = (source||dojo.global)[method], l = f && f[listenersName]; + if(f && l && handle--){ + delete ieh[l[handle]]; + delete l[handle]; + } + } + }; + // alias used above + var ieh = iel.handlers; + } + + dojo.mixin(del, { + add: function(/*DOMNode*/ node, /*String*/ event, /*Function*/ fp){ + if(!node){return;} // undefined + event = del._normalizeEventName(event); + if(event=="onkeypress"){ + // we need to listen to onkeydown to synthesize + // keypress events that otherwise won't fire + // on IE + var kd = node.onkeydown; + if(!kd || !kd[listenersName] || !kd._stealthKeydownHandle){ + var h = del.add(node, "onkeydown", del._stealthKeyDown); + kd = node.onkeydown; + kd._stealthKeydownHandle = h; + kd._stealthKeydownRefs = 1; + }else{ + kd._stealthKeydownRefs++; + } + } + return iel.add(node, event, del._fixCallback(fp)); + }, + remove: function(/*DOMNode*/ node, /*String*/ event, /*Handle*/ handle){ + event = del._normalizeEventName(event); + iel.remove(node, event, handle); + if(event=="onkeypress"){ + var kd = node.onkeydown; + if(--kd._stealthKeydownRefs <= 0){ + iel.remove(node, "onkeydown", kd._stealthKeydownHandle); + delete kd._stealthKeydownHandle; + } + } + }, + _normalizeEventName: function(/*String*/ eventName){ + // Generally, eventName should be lower case, unless it is + // special somehow (e.g. a Mozilla event) + // ensure 'on' + return eventName.slice(0,2) != "on" ? "on" + eventName : eventName; + }, + _nop: function(){}, + _fixEvent: function(/*Event*/ evt, /*DOMNode*/ sender){ + // summary: + // normalizes properties on the event object including event + // bubbling methods, keystroke normalization, and x/y positions + // evt: + // native event object + // sender: + // node to treat as "currentTarget" + if(!evt){ + var w = sender && (sender.ownerDocument || sender.document || sender).parentWindow || window; + evt = w.event; + } + if(!evt){return(evt);} + evt.target = evt.srcElement; + evt.currentTarget = (sender || evt.srcElement); + evt.layerX = evt.offsetX; + evt.layerY = evt.offsetY; + // FIXME: scroll position query is duped from dojo.html to + // avoid dependency on that entire module. Now that HTML is in + // Base, we should convert back to something similar there. + var se = evt.srcElement, doc = (se && se.ownerDocument) || document; + // DO NOT replace the following to use dojo.body(), in IE, document.documentElement should be used + // here rather than document.body + var docBody = ((dojo.isIE < 6) || (doc["compatMode"] == "BackCompat")) ? doc.body : doc.documentElement; + var offset = dojo._getIeDocumentElementOffset(); + evt.pageX = evt.clientX + dojo._fixIeBiDiScrollLeft(docBody.scrollLeft || 0) - offset.x; + evt.pageY = evt.clientY + (docBody.scrollTop || 0) - offset.y; + if(evt.type == "mouseover"){ + evt.relatedTarget = evt.fromElement; + } + if(evt.type == "mouseout"){ + evt.relatedTarget = evt.toElement; + } + evt.stopPropagation = del._stopPropagation; + evt.preventDefault = del._preventDefault; + return del._fixKeys(evt); + }, + _fixKeys: function(evt){ + switch(evt.type){ + case "keypress": + var c = ("charCode" in evt ? evt.charCode : evt.keyCode); + if (c==10){ + // CTRL-ENTER is CTRL-ASCII(10) on IE, but CTRL-ENTER on Mozilla + c=0; + evt.keyCode = 13; + }else if(c==13||c==27){ + c=0; // Mozilla considers ENTER and ESC non-printable + }else if(c==3){ + c=99; // Mozilla maps CTRL-BREAK to CTRL-c + } + // Mozilla sets keyCode to 0 when there is a charCode + // but that stops the event on IE. + evt.charCode = c; + del._setKeyChar(evt); + break; + } + return evt; + }, + _stealthKeyDown: function(evt){ + // IE doesn't fire keypress for most non-printable characters. + // other browsers do, we simulate it here. + var kp = evt.currentTarget.onkeypress; + // only works if kp exists and is a dispatcher + if(!kp || !kp[listenersName]){ return; } + // munge key/charCode + var k=evt.keyCode; + // These are Windows Virtual Key Codes + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/UserInput/VirtualKeyCodes.asp + var unprintable = k!=13 && k!=32 && k!=27 && (k<48||k>90) && (k<96||k>111) && (k<186||k>192) && (k<219||k>222); + // synthesize keypress for most unprintables and CTRL-keys + if(unprintable||evt.ctrlKey){ + var c = unprintable ? 0 : k; + if(evt.ctrlKey){ + if(k==3 || k==13){ + return; // IE will post CTRL-BREAK, CTRL-ENTER as keypress natively + }else if(c>95 && c<106){ + c -= 48; // map CTRL-[numpad 0-9] to ASCII + }else if((!evt.shiftKey)&&(c>=65&&c<=90)){ + c += 32; // map CTRL-[A-Z] to lowercase + }else{ + c = del._punctMap[c] || c; // map other problematic CTRL combinations to ASCII + } + } + // simulate a keypress event + var faux = del._synthesizeEvent(evt, {type: 'keypress', faux: true, charCode: c}); + kp.call(evt.currentTarget, faux); + evt.cancelBubble = faux.cancelBubble; + evt.returnValue = faux.returnValue; + _trySetKeyCode(evt, faux.keyCode); + } + }, + // Called in Event scope + _stopPropagation: function(){ + this.cancelBubble = true; + }, + _preventDefault: function(){ + // Setting keyCode to 0 is the only way to prevent certain keypresses (namely + // ctrl-combinations that correspond to menu accelerator keys). + // Otoh, it prevents upstream listeners from getting this information + // Try to split the difference here by clobbering keyCode only for ctrl + // combinations. If you still need to access the key upstream, bubbledKeyCode is + // provided as a workaround. + this.bubbledKeyCode = this.keyCode; + if(this.ctrlKey){_trySetKeyCode(this, 0);} + this.returnValue = false; + } + }); + + // override stopEvent for IE + dojo.stopEvent = function(evt){ + evt = evt || window.event; + del._stopPropagation.call(evt); + del._preventDefault.call(evt); + } + } + + del._synthesizeEvent = function(evt, props){ + var faux = dojo.mixin({}, evt, props); + del._setKeyChar(faux); + // FIXME: would prefer to use dojo.hitch: dojo.hitch(evt, evt.preventDefault); + // but it throws an error when preventDefault is invoked on Safari + // does Event.preventDefault not support "apply" on Safari? + faux.preventDefault = function(){ evt.preventDefault(); }; + faux.stopPropagation = function(){ evt.stopPropagation(); }; + return faux; + } + + // Opera event normalization + if(dojo.isOpera){ + dojo.mixin(del, { + _fixEvent: function(evt, sender){ + switch(evt.type){ + case "keypress": + var c = evt.which; + if(c==3){ + c=99; // Mozilla maps CTRL-BREAK to CTRL-c + } + // can't trap some keys at all, like INSERT and DELETE + // there is no differentiating info between DELETE and ".", or INSERT and "-" + c = c<41 && !evt.shiftKey ? 0 : c; + if(evt.ctrlKey && !evt.shiftKey && c>=65 && c<=90){ + // lowercase CTRL-[A-Z] keys + c += 32; + } + return del._synthesizeEvent(evt, { charCode: c }); + } + return evt; + } + }); + } + + // Webkit event normalization + if(dojo.isWebKit){ + del._add = del.add; + del._remove = del.remove; + + dojo.mixin(del, { + add: function(/*DOMNode*/ node, /*String*/ event, /*Function*/ fp){ + if(!node){return;} // undefined + var handle = del._add(node, event, fp); + if(del._normalizeEventName(event) == "keypress"){ + // we need to listen to onkeydown to synthesize + // keypress events that otherwise won't fire + // in Safari 3.1+: https://lists.webkit.org/pipermail/webkit-dev/2007-December/002992.html + handle._stealthKeyDownHandle = del._add(node, "keydown", function(evt){ + //A variation on the IE _stealthKeydown function + //Synthesize an onkeypress event, but only for unprintable characters. + var k=evt.keyCode; + // These are Windows Virtual Key Codes + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/UserInput/VirtualKeyCodes.asp + var unprintable = k!=13 && k!=32 && (k<48 || k>90) && (k<96 || k>111) && (k<186 || k>192) && (k<219 || k>222); + // synthesize keypress for most unprintables and CTRL-keys + if(unprintable || evt.ctrlKey){ + var c = unprintable ? 0 : k; + if(evt.ctrlKey){ + if(k==3 || k==13){ + return; // IE will post CTRL-BREAK, CTRL-ENTER as keypress natively + }else if(c>95 && c<106){ + c -= 48; // map CTRL-[numpad 0-9] to ASCII + }else if(!evt.shiftKey && c>=65 && c<=90){ + c += 32; // map CTRL-[A-Z] to lowercase + }else{ + c = del._punctMap[c] || c; // map other problematic CTRL combinations to ASCII + } + } + // simulate a keypress event + var faux = del._synthesizeEvent(evt, {type: 'keypress', faux: true, charCode: c}); + fp.call(evt.currentTarget, faux); + } + }); + } + return handle; /*Handle*/ + }, + + remove: function(/*DOMNode*/ node, /*String*/ event, /*Handle*/ handle){ + if(node){ + if(handle._stealthKeyDownHandle){ + del._remove(node, "keydown", handle._stealthKeyDownHandle); + } + del._remove(node, event, handle); + } + }, + _fixEvent: function(evt, sender){ + switch(evt.type){ + case "keypress": + if(evt.faux){ return evt; } + var c = evt.charCode; + c = c>=32 ? c : 0; + return del._synthesizeEvent(evt, {charCode: c, faux: true}); + } + return evt; + } + }); + } + })(); + if(dojo.isIE){ -dojo._ieDispatcher=function(_3c,_3d){ -var ap=Array.prototype,h=dojo._ie_listener.handlers,c=_3c.callee,ls=c[dojo._ieListenersName],t=h[c.target]; -var r=t&&t.apply(_3d,_3c); -var lls=[].concat(ls); -for(var i in lls){ -var f=h[lls[i]]; -if(!(i in ap)&&f){ -f.apply(_3d,_3c); -} -} -return r; -}; -dojo._getIeDispatcher=function(){ -return new Function(dojo._scopeName+"._ieDispatcher(arguments, this)"); -}; -dojo._event_listener._fixCallback=function(fp){ -var f=dojo._event_listener._fixEvent; -return function(e){ -return fp.call(this,f(e,this)); -}; -}; + // keep this out of the closure + // closing over 'iel' or 'ieh' b0rks leak prevention + // ls[i] is an index into the master handler array + dojo._ieDispatcher = function(args, sender){ + var ap = Array.prototype, + h = dojo._ie_listener.handlers, + c = args.callee, + ls = c[dojo._ieListenersName], + t = h[c.target]; + // return value comes from original target function + var r = t && t.apply(sender, args); + // make local copy of listener array so it's immutable during processing + var lls = [].concat(ls); + // invoke listeners after target function + for(var i in lls){ + var f = h[lls[i]]; + if(!(i in ap) && f){ + f.apply(sender, args); + } + } + return r; + } + dojo._getIeDispatcher = function(){ + // ensure the returned function closes over nothing ("new Function" apparently doesn't close) + return new Function(dojo._scopeName + "._ieDispatcher(arguments, this)"); // function + } + // keep this out of the closure to reduce RAM allocation + dojo._event_listener._fixCallback = function(fp){ + var f = dojo._event_listener._fixEvent; + return function(e){ return fp.call(this, f(e, this)); }; + } } + } diff --git a/lib/dojo/_base/fx.js b/lib/dojo/_base/fx.js index 1c589402e..21243c1c9 100644 --- a/lib/dojo/_base/fx.js +++ b/lib/dojo/_base/fx.js @@ -5,298 +5,665 @@ */ -if(!dojo._hasResource["dojo._base.fx"]){ -dojo._hasResource["dojo._base.fx"]=true; +if(!dojo._hasResource["dojo._base.fx"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo._base.fx"] = true; dojo.provide("dojo._base.fx"); dojo.require("dojo._base.Color"); dojo.require("dojo._base.connect"); dojo.require("dojo._base.lang"); dojo.require("dojo._base.html"); + +/* + Animation loosely package based on Dan Pupius' work, contributed under CLA: + http://pupius.co.uk/js/Toolkit.Drawing.js +*/ (function(){ -var d=dojo; -var _1=d._mixin; -dojo._Line=function(_2,_3){ -this.start=_2; -this.end=_3; -}; -dojo._Line.prototype.getValue=function(n){ -return ((this.end-this.start)*n)+this.start; -}; -dojo.Animation=function(_4){ -_1(this,_4); -if(d.isArray(this.curve)){ -this.curve=new d._Line(this.curve[0],this.curve[1]); -} -}; -d._Animation=d.Animation; -d.extend(dojo.Animation,{duration:350,repeat:0,rate:20,_percent:0,_startRepeatCount:0,_getStep:function(){ -var _5=this._percent,_6=this.easing; -return _6?_6(_5):_5; -},_fire:function(_7,_8){ -var a=_8||[]; -if(this[_7]){ -if(d.config.debugAtAllCosts){ -this[_7].apply(this,a); -}else{ -try{ -this[_7].apply(this,a); -} -catch(e){ -console.error("exception in animation handler for:",_7); -console.error(e); -} -} -} -return this; -},play:function(_9,_a){ -var _b=this; -if(_b._delayTimer){ -_b._clearTimer(); -} -if(_a){ -_b._stopTimer(); -_b._active=_b._paused=false; -_b._percent=0; -}else{ -if(_b._active&&!_b._paused){ -return _b; -} -} -_b._fire("beforeBegin",[_b.node]); -var de=_9||_b.delay,_c=dojo.hitch(_b,"_play",_a); -if(de>0){ -_b._delayTimer=setTimeout(_c,de); -return _b; -} -_c(); -return _b; -},_play:function(_d){ -var _e=this; -if(_e._delayTimer){ -_e._clearTimer(); -} -_e._startTime=new Date().valueOf(); -if(_e._paused){ -_e._startTime-=_e.duration*_e._percent; -} -_e._active=true; -_e._paused=false; -var _f=_e.curve.getValue(_e._getStep()); -if(!_e._percent){ -if(!_e._startRepeatCount){ -_e._startRepeatCount=_e.repeat; -} -_e._fire("onBegin",[_f]); -} -_e._fire("onPlay",[_f]); -_e._cycle(); -return _e; -},pause:function(){ -var _10=this; -if(_10._delayTimer){ -_10._clearTimer(); -} -_10._stopTimer(); -if(!_10._active){ -return _10; -} -_10._paused=true; -_10._fire("onPause",[_10.curve.getValue(_10._getStep())]); -return _10; -},gotoPercent:function(_11,_12){ -var _13=this; -_13._stopTimer(); -_13._active=_13._paused=true; -_13._percent=_11; -if(_12){ -_13.play(); -} -return _13; -},stop:function(_14){ -var _15=this; -if(_15._delayTimer){ -_15._clearTimer(); -} -if(!_15._timer){ -return _15; -} -_15._stopTimer(); -if(_14){ -_15._percent=1; -} -_15._fire("onStop",[_15.curve.getValue(_15._getStep())]); -_15._active=_15._paused=false; -return _15; -},status:function(){ -if(this._active){ -return this._paused?"paused":"playing"; -} -return "stopped"; -},_cycle:function(){ -var _16=this; -if(_16._active){ -var _17=new Date().valueOf(); -var _18=(_17-_16._startTime)/(_16.duration); -if(_18>=1){ -_18=1; -} -_16._percent=_18; -if(_16.easing){ -_18=_16.easing(_18); -} -_16._fire("onAnimate",[_16.curve.getValue(_18)]); -if(_16._percent<1){ -_16._startTimer(); -}else{ -_16._active=false; -if(_16.repeat>0){ -_16.repeat--; -_16.play(null,true); -}else{ -if(_16.repeat==-1){ -_16.play(null,true); -}else{ -if(_16._startRepeatCount){ -_16.repeat=_16._startRepeatCount; -_16._startRepeatCount=0; -} -} -} -_16._percent=0; -_16._fire("onEnd",[_16.node]); -!_16.repeat&&_16._stopTimer(); -} -} -return _16; -},_clearTimer:function(){ -clearTimeout(this._delayTimer); -delete this._delayTimer; -}}); -var ctr=0,_19=null,_1a={run:function(){ -}}; -d.extend(d.Animation,{_startTimer:function(){ -if(!this._timer){ -this._timer=d.connect(_1a,"run",this,"_cycle"); -ctr++; -} -if(!_19){ -_19=setInterval(d.hitch(_1a,"run"),this.rate); -} -},_stopTimer:function(){ -if(this._timer){ -d.disconnect(this._timer); -this._timer=null; -ctr--; -} -if(ctr<=0){ -clearInterval(_19); -_19=null; -ctr=0; -} -}}); -var _1b=d.isIE?function(_1c){ -var ns=_1c.style; -if(!ns.width.length&&d.style(_1c,"width")=="auto"){ -ns.width="auto"; -} -}:function(){ -}; -dojo._fade=function(_1d){ -_1d.node=d.byId(_1d.node); -var _1e=_1({properties:{}},_1d),_1f=(_1e.properties.opacity={}); -_1f.start=!("start" in _1e)?function(){ -return +d.style(_1e.node,"opacity")||0; -}:_1e.start; -_1f.end=_1e.end; -var _20=d.animateProperty(_1e); -d.connect(_20,"beforeBegin",d.partial(_1b,_1e.node)); -return _20; -}; -dojo.fadeIn=function(_21){ -return d._fade(_1({end:1},_21)); -}; -dojo.fadeOut=function(_22){ -return d._fade(_1({end:0},_22)); -}; -dojo._defaultEasing=function(n){ -return 0.5+((Math.sin((n+1.5)*Math.PI))/2); -}; -var _23=function(_24){ -this._properties=_24; -for(var p in _24){ -var _25=_24[p]; -if(_25.start instanceof d.Color){ -_25.tempColor=new d.Color(); -} -} -}; -_23.prototype.getValue=function(r){ -var ret={}; -for(var p in this._properties){ -var _26=this._properties[p],_27=_26.start; -if(_27 instanceof d.Color){ -ret[p]=d.blendColors(_27,_26.end,r,_26.tempColor).toCss(); -}else{ -if(!d.isArray(_27)){ -ret[p]=((_26.end-_27)*r)+_27+(p!="opacity"?_26.units||"px":0); -} -} -} -return ret; -}; -dojo.animateProperty=function(_28){ -var n=_28.node=d.byId(_28.node); -if(!_28.easing){ -_28.easing=d._defaultEasing; -} -var _29=new d.Animation(_28); -d.connect(_29,"beforeBegin",_29,function(){ -var pm={}; -for(var p in this.properties){ -if(p=="width"||p=="height"){ -this.node.display="block"; -} -var _2a=this.properties[p]; -if(d.isFunction(_2a)){ -_2a=_2a(n); -} -_2a=pm[p]=_1({},(d.isObject(_2a)?_2a:{end:_2a})); -if(d.isFunction(_2a.start)){ -_2a.start=_2a.start(n); -} -if(d.isFunction(_2a.end)){ -_2a.end=_2a.end(n); -} -var _2b=(p.toLowerCase().indexOf("color")>=0); -function _2c(_2d,p){ -var v={height:_2d.offsetHeight,width:_2d.offsetWidth}[p]; -if(v!==undefined){ -return v; -} -v=d.style(_2d,p); -return (p=="opacity")?+v:(_2b?v:parseFloat(v)); -}; -if(!("end" in _2a)){ -_2a.end=_2c(n,p); -}else{ -if(!("start" in _2a)){ -_2a.start=_2c(n,p); -} -} -if(_2b){ -_2a.start=new d.Color(_2a.start); -_2a.end=new d.Color(_2a.end); -}else{ -_2a.start=(p=="opacity")?+_2a.start:parseFloat(_2a.start); -} -} -this.curve=new _23(pm); -}); -d.connect(_29,"onAnimate",d.hitch(d,"style",_29.node)); -return _29; -}; -dojo.anim=function(_2e,_2f,_30,_31,_32,_33){ -return d.animateProperty({node:_2e,duration:_30||d.Animation.prototype.duration,properties:_2f,easing:_31,onEnd:_32}).play(_33||0); -}; + var d = dojo; + var _mixin = d._mixin; + + dojo._Line = function(/*int*/ start, /*int*/ end){ + // summary: + // dojo._Line is the object used to generate values from a start value + // to an end value + // start: int + // Beginning value for range + // end: int + // Ending value for range + this.start = start; + this.end = end; + }; + + dojo._Line.prototype.getValue = function(/*float*/ n){ + // summary: Returns the point on the line + // n: a floating point number greater than 0 and less than 1 + return ((this.end - this.start) * n) + this.start; // Decimal + }; + + dojo.Animation = function(args){ + // summary: + // A generic animation class that fires callbacks into its handlers + // object at various states. + // description: + // A generic animation class that fires callbacks into its handlers + // object at various states. Nearly all dojo animation functions + // return an instance of this method, usually without calling the + // .play() method beforehand. Therefore, you will likely need to + // call .play() on instances of `dojo.Animation` when one is + // returned. + // args: Object + // The 'magic argument', mixing all the properties into this + // animation instance. + + _mixin(this, args); + if(d.isArray(this.curve)){ + this.curve = new d._Line(this.curve[0], this.curve[1]); + } + + }; + + // Alias to drop come 2.0: + d._Animation = d.Animation; + + d.extend(dojo.Animation, { + // duration: Integer + // The time in milliseonds the animation will take to run + duration: 350, + + /*===== + // curve: dojo._Line|Array + // A two element array of start and end values, or a `dojo._Line` instance to be + // used in the Animation. + curve: null, + + // easing: Function? + // A Function to adjust the acceleration (or deceleration) of the progress + // across a dojo._Line + easing: null, + =====*/ + + // repeat: Integer? + // The number of times to loop the animation + repeat: 0, + + // rate: Integer? + // the time in milliseconds to wait before advancing to next frame + // (used as a fps timer: 1000/rate = fps) + rate: 20 /* 50 fps */, + + /*===== + // delay: Integer? + // The time in milliseconds to wait before starting animation after it + // has been .play()'ed + delay: null, + + // beforeBegin: Event? + // Synthetic event fired before a dojo.Animation begins playing (synchronous) + beforeBegin: null, + + // onBegin: Event? + // Synthetic event fired as a dojo.Animation begins playing (useful?) + onBegin: null, + + // onAnimate: Event? + // Synthetic event fired at each interval of a `dojo.Animation` + onAnimate: null, + + // onEnd: Event? + // Synthetic event fired after the final frame of a `dojo.Animation` + onEnd: null, + + // onPlay: Event? + // Synthetic event fired any time a `dojo.Animation` is play()'ed + onPlay: null, + + // onPause: Event? + // Synthetic event fired when a `dojo.Animation` is paused + onPause: null, + + // onStop: Event + // Synthetic event fires when a `dojo.Animation` is stopped + onStop: null, + + =====*/ + + _percent: 0, + _startRepeatCount: 0, + + _getStep: function(){ + var _p = this._percent, + _e = this.easing + ; + return _e ? _e(_p) : _p; + }, + _fire: function(/*Event*/ evt, /*Array?*/ args){ + // summary: + // Convenience function. Fire event "evt" and pass it the + // arguments specified in "args". + // description: + // Convenience function. Fire event "evt" and pass it the + // arguments specified in "args". + // Fires the callback in the scope of the `dojo.Animation` + // instance. + // evt: + // The event to fire. + // args: + // The arguments to pass to the event. + var a = args||[]; + if(this[evt]){ + if(d.config.debugAtAllCosts){ + this[evt].apply(this, a); + }else{ + try{ + this[evt].apply(this, a); + }catch(e){ + // squelch and log because we shouldn't allow exceptions in + // synthetic event handlers to cause the internal timer to run + // amuck, potentially pegging the CPU. I'm not a fan of this + // squelch, but hopefully logging will make it clear what's + // going on + console.error("exception in animation handler for:", evt); + console.error(e); + } + } + } + return this; // dojo.Animation + }, + + play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){ + // summary: + // Start the animation. + // delay: + // How many milliseconds to delay before starting. + // gotoStart: + // If true, starts the animation from the beginning; otherwise, + // starts it from its current position. + // returns: dojo.Animation + // The instance to allow chaining. + + var _t = this; + if(_t._delayTimer){ _t._clearTimer(); } + if(gotoStart){ + _t._stopTimer(); + _t._active = _t._paused = false; + _t._percent = 0; + }else if(_t._active && !_t._paused){ + return _t; + } + + _t._fire("beforeBegin", [_t.node]); + + var de = delay || _t.delay, + _p = dojo.hitch(_t, "_play", gotoStart); + + if(de > 0){ + _t._delayTimer = setTimeout(_p, de); + return _t; + } + _p(); + return _t; + }, + + _play: function(gotoStart){ + var _t = this; + if(_t._delayTimer){ _t._clearTimer(); } + _t._startTime = new Date().valueOf(); + if(_t._paused){ + _t._startTime -= _t.duration * _t._percent; + } + + _t._active = true; + _t._paused = false; + var value = _t.curve.getValue(_t._getStep()); + if(!_t._percent){ + if(!_t._startRepeatCount){ + _t._startRepeatCount = _t.repeat; + } + _t._fire("onBegin", [value]); + } + + _t._fire("onPlay", [value]); + + _t._cycle(); + return _t; // dojo.Animation + }, + + pause: function(){ + // summary: Pauses a running animation. + var _t = this; + if(_t._delayTimer){ _t._clearTimer(); } + _t._stopTimer(); + if(!_t._active){ return _t; /*dojo.Animation*/ } + _t._paused = true; + _t._fire("onPause", [_t.curve.getValue(_t._getStep())]); + return _t; // dojo.Animation + }, + + gotoPercent: function(/*Decimal*/ percent, /*Boolean?*/ andPlay){ + // summary: + // Sets the progress of the animation. + // percent: + // A percentage in decimal notation (between and including 0.0 and 1.0). + // andPlay: + // If true, play the animation after setting the progress. + var _t = this; + _t._stopTimer(); + _t._active = _t._paused = true; + _t._percent = percent; + if(andPlay){ _t.play(); } + return _t; // dojo.Animation + }, + + stop: function(/*boolean?*/ gotoEnd){ + // summary: Stops a running animation. + // gotoEnd: If true, the animation will end. + var _t = this; + if(_t._delayTimer){ _t._clearTimer(); } + if(!_t._timer){ return _t; /* dojo.Animation */ } + _t._stopTimer(); + if(gotoEnd){ + _t._percent = 1; + } + _t._fire("onStop", [_t.curve.getValue(_t._getStep())]); + _t._active = _t._paused = false; + return _t; // dojo.Animation + }, + + status: function(){ + // summary: + // Returns a string token representation of the status of + // the animation, one of: "paused", "playing", "stopped" + if(this._active){ + return this._paused ? "paused" : "playing"; // String + } + return "stopped"; // String + }, + + _cycle: function(){ + var _t = this; + if(_t._active){ + var curr = new Date().valueOf(); + var step = (curr - _t._startTime) / (_t.duration); + + if(step >= 1){ + step = 1; + } + _t._percent = step; + + // Perform easing + if(_t.easing){ + step = _t.easing(step); + } + + _t._fire("onAnimate", [_t.curve.getValue(step)]); + + if(_t._percent < 1){ + _t._startTimer(); + }else{ + _t._active = false; + + if(_t.repeat > 0){ + _t.repeat--; + _t.play(null, true); + }else if(_t.repeat == -1){ + _t.play(null, true); + }else{ + if(_t._startRepeatCount){ + _t.repeat = _t._startRepeatCount; + _t._startRepeatCount = 0; + } + } + _t._percent = 0; + _t._fire("onEnd", [_t.node]); + !_t.repeat && _t._stopTimer(); + } + } + return _t; // dojo.Animation + }, + + _clearTimer: function(){ + // summary: Clear the play delay timer + clearTimeout(this._delayTimer); + delete this._delayTimer; + } + + }); + + // the local timer, stubbed into all Animation instances + var ctr = 0, + timer = null, + runner = { + run: function(){} + }; + + d.extend(d.Animation, { + + _startTimer: function(){ + if(!this._timer){ + this._timer = d.connect(runner, "run", this, "_cycle"); + ctr++; + } + if(!timer){ + timer = setInterval(d.hitch(runner, "run"), this.rate); + } + }, + + _stopTimer: function(){ + if(this._timer){ + d.disconnect(this._timer); + this._timer = null; + ctr--; + } + if(ctr <= 0){ + clearInterval(timer); + timer = null; + ctr = 0; + } + } + + }); + + var _makeFadeable = + d.isIE ? function(node){ + // only set the zoom if the "tickle" value would be the same as the + // default + var ns = node.style; + // don't set the width to auto if it didn't already cascade that way. + // We don't want to f anyones designs + if(!ns.width.length && d.style(node, "width") == "auto"){ + ns.width = "auto"; + } + } : + function(){}; + + dojo._fade = function(/*Object*/ args){ + // summary: + // Returns an animation that will fade the node defined by + // args.node from the start to end values passed (args.start + // args.end) (end is mandatory, start is optional) + + args.node = d.byId(args.node); + var fArgs = _mixin({ properties: {} }, args), + props = (fArgs.properties.opacity = {}); + + props.start = !("start" in fArgs) ? + function(){ + return +d.style(fArgs.node, "opacity")||0; + } : fArgs.start; + props.end = fArgs.end; + + var anim = d.animateProperty(fArgs); + d.connect(anim, "beforeBegin", d.partial(_makeFadeable, fArgs.node)); + + return anim; // dojo.Animation + }; + + /*===== + dojo.__FadeArgs = function(node, duration, easing){ + // node: DOMNode|String + // The node referenced in the animation + // duration: Integer? + // Duration of the animation in milliseconds. + // easing: Function? + // An easing function. + this.node = node; + this.duration = duration; + this.easing = easing; + } + =====*/ + + dojo.fadeIn = function(/*dojo.__FadeArgs*/ args){ + // summary: + // Returns an animation that will fade node defined in 'args' from + // its current opacity to fully opaque. + return d._fade(_mixin({ end: 1 }, args)); // dojo.Animation + }; + + dojo.fadeOut = function(/*dojo.__FadeArgs*/ args){ + // summary: + // Returns an animation that will fade node defined in 'args' + // from its current opacity to fully transparent. + return d._fade(_mixin({ end: 0 }, args)); // dojo.Animation + }; + + dojo._defaultEasing = function(/*Decimal?*/ n){ + // summary: The default easing function for dojo.Animation(s) + return 0.5 + ((Math.sin((n + 1.5) * Math.PI)) / 2); + }; + + var PropLine = function(properties){ + // PropLine is an internal class which is used to model the values of + // an a group of CSS properties across an animation lifecycle. In + // particular, the "getValue" function handles getting interpolated + // values between start and end for a particular CSS value. + this._properties = properties; + for(var p in properties){ + var prop = properties[p]; + if(prop.start instanceof d.Color){ + // create a reusable temp color object to keep intermediate results + prop.tempColor = new d.Color(); + } + } + }; + + PropLine.prototype.getValue = function(r){ + var ret = {}; + for(var p in this._properties){ + var prop = this._properties[p], + start = prop.start; + if(start instanceof d.Color){ + ret[p] = d.blendColors(start, prop.end, r, prop.tempColor).toCss(); + }else if(!d.isArray(start)){ + ret[p] = ((prop.end - start) * r) + start + (p != "opacity" ? prop.units || "px" : 0); + } + } + return ret; + }; + + /*===== + dojo.declare("dojo.__AnimArgs", [dojo.__FadeArgs], { + // Properties: Object? + // A hash map of style properties to Objects describing the transition, + // such as the properties of dojo._Line with an additional 'units' property + properties: {} + + //TODOC: add event callbacks + }); + =====*/ + + dojo.animateProperty = function(/*dojo.__AnimArgs*/ args){ + // summary: + // Returns an animation that will transition the properties of + // node defined in `args` depending how they are defined in + // `args.properties` + // + // description: + // `dojo.animateProperty` is the foundation of most `dojo.fx` + // animations. It takes an object of "properties" corresponding to + // style properties, and animates them in parallel over a set + // duration. + // + // example: + // A simple animation that changes the width of the specified node. + // | dojo.animateProperty({ + // | node: "nodeId", + // | properties: { width: 400 }, + // | }).play(); + // Dojo figures out the start value for the width and converts the + // integer specified for the width to the more expressive but + // verbose form `{ width: { end: '400', units: 'px' } }` which you + // can also specify directly. Defaults to 'px' if ommitted. + // + // example: + // Animate width, height, and padding over 2 seconds... the + // pedantic way: + // | dojo.animateProperty({ node: node, duration:2000, + // | properties: { + // | width: { start: '200', end: '400', units:"px" }, + // | height: { start:'200', end: '400', units:"px" }, + // | paddingTop: { start:'5', end:'50', units:"px" } + // | } + // | }).play(); + // Note 'paddingTop' is used over 'padding-top'. Multi-name CSS properties + // are written using "mixed case", as the hyphen is illegal as an object key. + // + // example: + // Plug in a different easing function and register a callback for + // when the animation ends. Easing functions accept values between + // zero and one and return a value on that basis. In this case, an + // exponential-in curve. + // | dojo.animateProperty({ + // | node: "nodeId", + // | // dojo figures out the start value + // | properties: { width: { end: 400 } }, + // | easing: function(n){ + // | return (n==0) ? 0 : Math.pow(2, 10 * (n - 1)); + // | }, + // | onEnd: function(node){ + // | // called when the animation finishes. The animation + // | // target is passed to this function + // | } + // | }).play(500); // delay playing half a second + // + // example: + // Like all `dojo.Animation`s, animateProperty returns a handle to the + // Animation instance, which fires the events common to Dojo FX. Use `dojo.connect` + // to access these events outside of the Animation definiton: + // | var anim = dojo.animateProperty({ + // | node:"someId", + // | properties:{ + // | width:400, height:500 + // | } + // | }); + // | dojo.connect(anim,"onEnd", function(){ + // | console.log("animation ended"); + // | }); + // | // play the animation now: + // | anim.play(); + // + // example: + // Each property can be a function whose return value is substituted along. + // Additionally, each measurement (eg: start, end) can be a function. The node + // reference is passed direcly to callbacks. + // | dojo.animateProperty({ + // | node:"mine", + // | properties:{ + // | height:function(node){ + // | // shrink this node by 50% + // | return dojo.position(node).h / 2 + // | }, + // | width:{ + // | start:function(node){ return 100; }, + // | end:function(node){ return 200; } + // | } + // | } + // | }).play(); + // + + var n = args.node = d.byId(args.node); + if(!args.easing){ args.easing = d._defaultEasing; } + + var anim = new d.Animation(args); + d.connect(anim, "beforeBegin", anim, function(){ + var pm = {}; + for(var p in this.properties){ + // Make shallow copy of properties into pm because we overwrite + // some values below. In particular if start/end are functions + // we don't want to overwrite them or the functions won't be + // called if the animation is reused. + if(p == "width" || p == "height"){ + this.node.display = "block"; + } + var prop = this.properties[p]; + if(d.isFunction(prop)){ + prop = prop(n); + } + prop = pm[p] = _mixin({}, (d.isObject(prop) ? prop: { end: prop })); + + if(d.isFunction(prop.start)){ + prop.start = prop.start(n); + } + if(d.isFunction(prop.end)){ + prop.end = prop.end(n); + } + var isColor = (p.toLowerCase().indexOf("color") >= 0); + function getStyle(node, p){ + // dojo.style(node, "height") can return "auto" or "" on IE; this is more reliable: + var v = { height: node.offsetHeight, width: node.offsetWidth }[p]; + if(v !== undefined){ return v; } + v = d.style(node, p); + return (p == "opacity") ? +v : (isColor ? v : parseFloat(v)); + } + if(!("end" in prop)){ + prop.end = getStyle(n, p); + }else if(!("start" in prop)){ + prop.start = getStyle(n, p); + } + + if(isColor){ + prop.start = new d.Color(prop.start); + prop.end = new d.Color(prop.end); + }else{ + prop.start = (p == "opacity") ? +prop.start : parseFloat(prop.start); + } + } + this.curve = new PropLine(pm); + }); + d.connect(anim, "onAnimate", d.hitch(d, "style", anim.node)); + return anim; // dojo.Animation + }; + + dojo.anim = function( /*DOMNode|String*/ node, + /*Object*/ properties, + /*Integer?*/ duration, + /*Function?*/ easing, + /*Function?*/ onEnd, + /*Integer?*/ delay){ + // summary: + // A simpler interface to `dojo.animateProperty()`, also returns + // an instance of `dojo.Animation` but begins the animation + // immediately, unlike nearly every other Dojo animation API. + // description: + // `dojo.anim` is a simpler (but somewhat less powerful) version + // of `dojo.animateProperty`. It uses defaults for many basic properties + // and allows for positional parameters to be used in place of the + // packed "property bag" which is used for other Dojo animation + // methods. + // + // The `dojo.Animation` object returned from `dojo.anim` will be + // already playing when it is returned from this function, so + // calling play() on it again is (usually) a no-op. + // node: + // a DOM node or the id of a node to animate CSS properties on + // duration: + // The number of milliseconds over which the animation + // should run. Defaults to the global animation default duration + // (350ms). + // easing: + // An easing function over which to calculate acceleration + // and deceleration of the animation through its duration. + // A default easing algorithm is provided, but you may + // plug in any you wish. A large selection of easing algorithms + // are available in `dojo.fx.easing`. + // onEnd: + // A function to be called when the animation finishes + // running. + // delay: + // The number of milliseconds to delay beginning the + // animation by. The default is 0. + // example: + // Fade out a node + // | dojo.anim("id", { opacity: 0 }); + // example: + // Fade out a node over a full second + // | dojo.anim("id", { opacity: 0 }, 1000); + return d.animateProperty({ // dojo.Animation + node: node, + duration: duration || d.Animation.prototype.duration, + properties: properties, + easing: easing, + onEnd: onEnd + }).play(delay || 0); + }; })(); + } diff --git a/lib/dojo/_base/html.js b/lib/dojo/_base/html.js index 050841531..be5fd2aaa 100644 --- a/lib/dojo/_base/html.js +++ b/lib/dojo/_base/html.js @@ -5,745 +5,1830 @@ */ -if(!dojo._hasResource["dojo._base.html"]){ -dojo._hasResource["dojo._base.html"]=true; +if(!dojo._hasResource["dojo._base.html"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo._base.html"] = true; dojo.require("dojo._base.lang"); dojo.provide("dojo._base.html"); + +// FIXME: need to add unit tests for all the semi-public methods + try{ -document.execCommand("BackgroundImageCache",false,true); -} -catch(e){ + document.execCommand("BackgroundImageCache", false, true); +}catch(e){ + // sane browsers don't have cache "issues" } -if(dojo.isIE||dojo.isOpera){ -dojo.byId=function(id,_1){ -if(typeof id!="string"){ -return id; -} -var _2=_1||dojo.doc,te=_2.getElementById(id); -if(te&&(te.attributes.id.value==id||te.id==id)){ -return te; + +// ============================= +// DOM Functions +// ============================= + +/*===== +dojo.byId = function(id, doc){ + // summary: + // Returns DOM node with matching `id` attribute or `null` + // if not found. If `id` is a DomNode, this function is a no-op. + // + // id: String|DOMNode + // A string to match an HTML id attribute or a reference to a DOM Node + // + // doc: Document? + // Document to work in. Defaults to the current value of + // dojo.doc. Can be used to retrieve + // node references from other documents. + // + // example: + // Look up a node by ID: + // | var n = dojo.byId("foo"); + // + // example: + // Check if a node exists, and use it. + // | var n = dojo.byId("bar"); + // | if(n){ doStuff() ... } + // + // example: + // Allow string or DomNode references to be passed to a custom function: + // | var foo = function(nodeOrId){ + // | nodeOrId = dojo.byId(nodeOrId); + // | // ... more stuff + // | } +=====*/ + +if(dojo.isIE || dojo.isOpera){ + dojo.byId = function(id, doc){ + if(typeof id != "string"){ + return id; + } + var _d = doc || dojo.doc, te = _d.getElementById(id); + // attributes.id.value is better than just id in case the + // user has a name=id inside a form + if(te && (te.attributes.id.value == id || te.id == id)){ + return te; + }else{ + var eles = _d.all[id]; + if(!eles || eles.nodeName){ + eles = [eles]; + } + // if more than 1, choose first with the correct id + var i=0; + while((te=eles[i++])){ + if((te.attributes && te.attributes.id && te.attributes.id.value == id) + || te.id == id){ + return te; + } + } + } + }; }else{ -var _3=_2.all[id]; -if(!_3||_3.nodeName){ -_3=[_3]; -} -var i=0; -while((te=_3[i++])){ -if((te.attributes&&te.attributes.id&&te.attributes.id.value==id)||te.id==id){ -return te; -} -} + dojo.byId = function(id, doc){ + // inline'd type check + return (typeof id == "string") ? (doc || dojo.doc).getElementById(id) : id; // DomNode + }; } +/*===== }; -}else{ -dojo.byId=function(id,_4){ -return (typeof id=="string")?(_4||dojo.doc).getElementById(id):id; -}; -} +=====*/ + (function(){ -var d=dojo; -var _5=d.byId; -var _6=null,_7; -d.addOnWindowUnload(function(){ -_6=null; -}); -dojo._destroyElement=dojo.destroy=function(_8){ -_8=_5(_8); -try{ -var _9=_8.ownerDocument; -if(!_6||_7!=_9){ -_6=_9.createElement("div"); -_7=_9; -} -_6.appendChild(_8.parentNode?_8.parentNode.removeChild(_8):_8); -_6.innerHTML=""; -} -catch(e){ -} -}; -dojo.isDescendant=function(_a,_b){ -try{ -_a=_5(_a); -_b=_5(_b); -while(_a){ -if(_a==_b){ -return true; -} -_a=_a.parentNode; -} -} -catch(e){ -} -return false; -}; -dojo.setSelectable=function(_c,_d){ -_c=_5(_c); -if(d.isMozilla){ -_c.style.MozUserSelect=_d?"":"none"; -}else{ -if(d.isKhtml||d.isWebKit){ -_c.style.KhtmlUserSelect=_d?"auto":"none"; -}else{ -if(d.isIE){ -var v=(_c.unselectable=_d?"":"on"); -d.query("*",_c).forEach("item.unselectable = '"+v+"'"); -} -} -} -}; -var _e=function(_f,ref){ -var _10=ref.parentNode; -if(_10){ -_10.insertBefore(_f,ref); -} -}; -var _11=function(_12,ref){ -var _13=ref.parentNode; -if(_13){ -if(_13.lastChild==ref){ -_13.appendChild(_12); -}else{ -_13.insertBefore(_12,ref.nextSibling); -} -} -}; -dojo.place=function(_14,_15,_16){ -_15=_5(_15); -if(typeof _14=="string"){ -_14=_14.charAt(0)=="<"?d._toDom(_14,_15.ownerDocument):_5(_14); -} -if(typeof _16=="number"){ -var cn=_15.childNodes; -if(!cn.length||cn.length<=_16){ -_15.appendChild(_14); -}else{ -_e(_14,cn[_16<0?0:_16]); -} -}else{ -switch(_16){ -case "before": -_e(_14,_15); -break; -case "after": -_11(_14,_15); -break; -case "replace": -_15.parentNode.replaceChild(_14,_15); -break; -case "only": -d.empty(_15); -_15.appendChild(_14); -break; -case "first": -if(_15.firstChild){ -_e(_14,_15.firstChild); -break; -} -default: -_15.appendChild(_14); -} -} -return _14; -}; -dojo.boxModel="content-box"; -if(d.isIE){ -d.boxModel=document.compatMode=="BackCompat"?"border-box":"content-box"; -} -var gcs; -if(d.isWebKit){ -gcs=function(_17){ -var s; -if(_17.nodeType==1){ -var dv=_17.ownerDocument.defaultView; -s=dv.getComputedStyle(_17,null); -if(!s&&_17.style){ -_17.style.display=""; -s=dv.getComputedStyle(_17,null); -} -} -return s||{}; -}; -}else{ -if(d.isIE){ -gcs=function(_18){ -return _18.nodeType==1?_18.currentStyle:{}; -}; -}else{ -gcs=function(_19){ -return _19.nodeType==1?_19.ownerDocument.defaultView.getComputedStyle(_19,null):{}; -}; -} -} -dojo.getComputedStyle=gcs; -if(!d.isIE){ -d._toPixelValue=function(_1a,_1b){ -return parseFloat(_1b)||0; -}; -}else{ -d._toPixelValue=function(_1c,_1d){ -if(!_1d){ -return 0; -} -if(_1d=="medium"){ -return 4; -} -if(_1d.slice&&_1d.slice(-2)=="px"){ -return parseFloat(_1d); -} -with(_1c){ -var _1e=style.left; -var _1f=runtimeStyle.left; -runtimeStyle.left=currentStyle.left; -try{ -style.left=_1d; -_1d=style.pixelLeft; -} -catch(e){ -_1d=0; -} -style.left=_1e; -runtimeStyle.left=_1f; -} -return _1d; -}; -} -var px=d._toPixelValue; -var _20="DXImageTransform.Microsoft.Alpha"; -var af=function(n,f){ -try{ -return n.filters.item(_20); -} -catch(e){ -return f?{}:null; -} -}; -dojo._getOpacity=d.isIE?function(_21){ -try{ -return af(_21).Opacity/100; -} -catch(e){ -return 1; -} -}:function(_22){ -return gcs(_22).opacity; -}; -dojo._setOpacity=d.isIE?function(_23,_24){ -var ov=_24*100,_25=_24==1; -_23.style.zoom=_25?"":1; -if(!af(_23)){ -if(_25){ -return _24; -} -_23.style.filter+=" progid:"+_20+"(Opacity="+ov+")"; -}else{ -af(_23,1).Opacity=ov; -} -af(_23,1).Enabled=!_25; -if(_23.nodeName.toLowerCase()=="tr"){ -d.query("> td",_23).forEach(function(i){ -d._setOpacity(i,_24); -}); -} -return _24; -}:function(_26,_27){ -return _26.style.opacity=_27; -}; -var _28={left:true,top:true}; -var _29=/margin|padding|width|height|max|min|offset/; -var _2a=function(_2b,_2c,_2d){ -_2c=_2c.toLowerCase(); -if(d.isIE){ -if(_2d=="auto"){ -if(_2c=="height"){ -return _2b.offsetHeight; -} -if(_2c=="width"){ -return _2b.offsetWidth; -} -} -if(_2c=="fontweight"){ -switch(_2d){ -case 700: -return "bold"; -case 400: -default: -return "normal"; -} -} -} -if(!(_2c in _28)){ -_28[_2c]=_29.test(_2c); -} -return _28[_2c]?px(_2b,_2d):_2d; -}; -var _2e=d.isIE?"styleFloat":"cssFloat",_2f={"cssFloat":_2e,"styleFloat":_2e,"float":_2e}; -dojo.style=function(_30,_31,_32){ -var n=_5(_30),_33=arguments.length,op=(_31=="opacity"); -_31=_2f[_31]||_31; -if(_33==3){ -return op?d._setOpacity(n,_32):n.style[_31]=_32; -} -if(_33==2&&op){ -return d._getOpacity(n); -} -var s=gcs(n); -if(_33==2&&typeof _31!="string"){ -for(var x in _31){ -d.style(_30,x,_31[x]); -} -return s; -} -return (_33==1)?s:_2a(n,_31,s[_31]||n.style[_31]); -}; -dojo._getPadExtents=function(n,_34){ -var s=_34||gcs(n),l=px(n,s.paddingLeft),t=px(n,s.paddingTop); -return {l:l,t:t,w:l+px(n,s.paddingRight),h:t+px(n,s.paddingBottom)}; -}; -dojo._getBorderExtents=function(n,_35){ -var ne="none",s=_35||gcs(n),bl=(s.borderLeftStyle!=ne?px(n,s.borderLeftWidth):0),bt=(s.borderTopStyle!=ne?px(n,s.borderTopWidth):0); -return {l:bl,t:bt,w:bl+(s.borderRightStyle!=ne?px(n,s.borderRightWidth):0),h:bt+(s.borderBottomStyle!=ne?px(n,s.borderBottomWidth):0)}; -}; -dojo._getPadBorderExtents=function(n,_36){ -var s=_36||gcs(n),p=d._getPadExtents(n,s),b=d._getBorderExtents(n,s); -return {l:p.l+b.l,t:p.t+b.t,w:p.w+b.w,h:p.h+b.h}; -}; -dojo._getMarginExtents=function(n,_37){ -var s=_37||gcs(n),l=px(n,s.marginLeft),t=px(n,s.marginTop),r=px(n,s.marginRight),b=px(n,s.marginBottom); -if(d.isWebKit&&(s.position!="absolute")){ -r=l; -} -return {l:l,t:t,w:l+r,h:t+b}; -}; -dojo._getMarginBox=function(_38,_39){ -var s=_39||gcs(_38),me=d._getMarginExtents(_38,s); -var l=_38.offsetLeft-me.l,t=_38.offsetTop-me.t,p=_38.parentNode; -if(d.isMoz){ -var sl=parseFloat(s.left),st=parseFloat(s.top); -if(!isNaN(sl)&&!isNaN(st)){ -l=sl,t=st; -}else{ -if(p&&p.style){ -var pcs=gcs(p); -if(pcs.overflow!="visible"){ -var be=d._getBorderExtents(p,pcs); -l+=be.l,t+=be.t; -} -} -} -}else{ -if(d.isOpera||(d.isIE>7&&!d.isQuirks)){ -if(p){ -be=d._getBorderExtents(p); -l-=be.l; -t-=be.t; -} -} -} -return {l:l,t:t,w:_38.offsetWidth+me.w,h:_38.offsetHeight+me.h}; -}; -dojo._getContentBox=function(_3a,_3b){ -var s=_3b||gcs(_3a),pe=d._getPadExtents(_3a,s),be=d._getBorderExtents(_3a,s),w=_3a.clientWidth,h; -if(!w){ -w=_3a.offsetWidth,h=_3a.offsetHeight; -}else{ -h=_3a.clientHeight,be.w=be.h=0; -} -if(d.isOpera){ -pe.l+=be.l; -pe.t+=be.t; -} -return {l:pe.l,t:pe.t,w:w-pe.w-be.w,h:h-pe.h-be.h}; -}; -dojo._getBorderBox=function(_3c,_3d){ -var s=_3d||gcs(_3c),pe=d._getPadExtents(_3c,s),cb=d._getContentBox(_3c,s); -return {l:cb.l-pe.l,t:cb.t-pe.t,w:cb.w+pe.w,h:cb.h+pe.h}; -}; -dojo._setBox=function(_3e,l,t,w,h,u){ -u=u||"px"; -var s=_3e.style; -if(!isNaN(l)){ -s.left=l+u; -} -if(!isNaN(t)){ -s.top=t+u; -} -if(w>=0){ -s.width=w+u; -} -if(h>=0){ -s.height=h+u; -} -}; -dojo._isButtonTag=function(_3f){ -return _3f.tagName=="BUTTON"||_3f.tagName=="INPUT"&&(_3f.getAttribute("type")||"").toUpperCase()=="BUTTON"; -}; -dojo._usesBorderBox=function(_40){ -var n=_40.tagName; -return d.boxModel=="border-box"||n=="TABLE"||d._isButtonTag(_40); -}; -dojo._setContentSize=function(_41,_42,_43,_44){ -if(d._usesBorderBox(_41)){ -var pb=d._getPadBorderExtents(_41,_44); -if(_42>=0){ -_42+=pb.w; -} -if(_43>=0){ -_43+=pb.h; -} -} -d._setBox(_41,NaN,NaN,_42,_43); -}; -dojo._setMarginBox=function(_45,_46,_47,_48,_49,_4a){ -var s=_4a||gcs(_45),bb=d._usesBorderBox(_45),pb=bb?_4b:d._getPadBorderExtents(_45,s); -if(d.isWebKit){ -if(d._isButtonTag(_45)){ -var ns=_45.style; -if(_48>=0&&!ns.width){ -ns.width="4px"; -} -if(_49>=0&&!ns.height){ -ns.height="4px"; -} -} -} -var mb=d._getMarginExtents(_45,s); -if(_48>=0){ -_48=Math.max(_48-pb.w-mb.w,0); -} -if(_49>=0){ -_49=Math.max(_49-pb.h-mb.h,0); -} -d._setBox(_45,_46,_47,_48,_49); -}; -var _4b={l:0,t:0,w:0,h:0}; -dojo.marginBox=function(_4c,box){ -var n=_5(_4c),s=gcs(n),b=box; -return !b?d._getMarginBox(n,s):d._setMarginBox(n,b.l,b.t,b.w,b.h,s); -}; -dojo.contentBox=function(_4d,box){ -var n=_5(_4d),s=gcs(n),b=box; -return !b?d._getContentBox(n,s):d._setContentSize(n,b.w,b.h,s); -}; -var _4e=function(_4f,_50){ -if(!(_4f=(_4f||0).parentNode)){ -return 0; -} -var val,_51=0,_52=d.body(); -while(_4f&&_4f.style){ -if(gcs(_4f).position=="fixed"){ -return 0; -} -val=_4f[_50]; -if(val){ -_51+=val-0; -if(_4f==_52){ -break; -} -} -_4f=_4f.parentNode; -} -return _51; -}; -dojo._docScroll=function(){ -var n=d.global; -return "pageXOffset" in n?{x:n.pageXOffset,y:n.pageYOffset}:(n=d.doc.documentElement,n.clientHeight?{x:d._fixIeBiDiScrollLeft(n.scrollLeft),y:n.scrollTop}:(n=d.body(),{x:n.scrollLeft||0,y:n.scrollTop||0})); -}; -dojo._isBodyLtr=function(){ -return "_bodyLtr" in d?d._bodyLtr:d._bodyLtr=(d.body().dir||d.doc.documentElement.dir||"ltr").toLowerCase()=="ltr"; -}; -dojo._getIeDocumentElementOffset=function(){ -var de=d.doc.documentElement; -if(d.isIE<8){ -var r=de.getBoundingClientRect(); -var l=r.left,t=r.top; -if(d.isIE<7){ -l+=de.clientLeft; -t+=de.clientTop; -} -return {x:l<0?0:l,y:t<0?0:t}; -}else{ -return {x:0,y:0}; -} -}; -dojo._fixIeBiDiScrollLeft=function(_53){ -var dd=d.doc; -if(d.isIE<8&&!d._isBodyLtr()){ -var de=d.isQuirks?dd.body:dd.documentElement; -return _53+de.clientWidth-de.scrollWidth; -} -return _53; -}; -dojo._abs=dojo.position=function(_54,_55){ -var db=d.body(),dh=db.parentNode,ret; -_54=_5(_54); -if(_54["getBoundingClientRect"]){ -ret=_54.getBoundingClientRect(); -ret={x:ret.left,y:ret.top,w:ret.right-ret.left,h:ret.bottom-ret.top}; -if(d.isIE){ -var _56=d._getIeDocumentElementOffset(); -ret.x-=_56.x+(d.isQuirks?db.clientLeft+db.offsetLeft:0); -ret.y-=_56.y+(d.isQuirks?db.clientTop+db.offsetTop:0); -}else{ -if(d.isFF==3){ -var cs=gcs(dh); -ret.x-=px(dh,cs.marginLeft)+px(dh,cs.borderLeftWidth); -ret.y-=px(dh,cs.marginTop)+px(dh,cs.borderTopWidth); -} -} -}else{ -ret={x:0,y:0,w:_54.offsetWidth,h:_54.offsetHeight}; -if(_54["offsetParent"]){ -ret.x-=_4e(_54,"scrollLeft"); -ret.y-=_4e(_54,"scrollTop"); -var _57=_54; -do{ -var n=_57.offsetLeft,t=_57.offsetTop; -ret.x+=isNaN(n)?0:n; -ret.y+=isNaN(t)?0:t; -cs=gcs(_57); -if(_57!=_54){ -if(d.isMoz){ -ret.x+=2*px(_57,cs.borderLeftWidth); -ret.y+=2*px(_57,cs.borderTopWidth); -}else{ -ret.x+=px(_57,cs.borderLeftWidth); -ret.y+=px(_57,cs.borderTopWidth); -} -} -if(d.isMoz&&cs.position=="static"){ -var _58=_57.parentNode; -while(_58!=_57.offsetParent){ -var pcs=gcs(_58); -if(pcs.position=="static"){ -ret.x+=px(_57,pcs.borderLeftWidth); -ret.y+=px(_57,pcs.borderTopWidth); -} -_58=_58.parentNode; -} -} -_57=_57.offsetParent; -}while((_57!=dh)&&_57); -}else{ -if(_54.x&&_54.y){ -ret.x+=isNaN(_54.x)?0:_54.x; -ret.y+=isNaN(_54.y)?0:_54.y; -} -} -} -if(_55){ -var _59=d._docScroll(); -ret.x+=_59.x; -ret.y+=_59.y; -} -return ret; -}; -dojo.coords=function(_5a,_5b){ -var n=_5(_5a),s=gcs(n),mb=d._getMarginBox(n,s); -var abs=d.position(n,_5b); -mb.x=abs.x; -mb.y=abs.y; -return mb; -}; -var _5c={"class":"className","for":"htmlFor",tabindex:"tabIndex",readonly:"readOnly",colspan:"colSpan",frameborder:"frameBorder",rowspan:"rowSpan",valuetype:"valueType"},_5d={classname:"class",htmlfor:"for",tabindex:"tabIndex",readonly:"readOnly"},_5e={innerHTML:1,className:1,htmlFor:d.isIE,value:1}; -var _5f=function(_60){ -return _5d[_60.toLowerCase()]||_60; -}; -var _61=function(_62,_63){ -var _64=_62.getAttributeNode&&_62.getAttributeNode(_63); -return _64&&_64.specified; -}; -dojo.hasAttr=function(_65,_66){ -var lc=_66.toLowerCase(); -return _5e[_5c[lc]||_66]||_61(_5(_65),_5d[lc]||_66); -}; -var _67={},_68=0,_69=dojo._scopeName+"attrid",_6a={col:1,colgroup:1,table:1,tbody:1,tfoot:1,thead:1,tr:1,title:1}; -dojo.attr=function(_6b,_6c,_6d){ -_6b=_5(_6b); -var _6e=arguments.length,_6f; -if(_6e==2&&typeof _6c!="string"){ -for(var x in _6c){ -d.attr(_6b,x,_6c[x]); -} -return _6b; -} -var lc=_6c.toLowerCase(),_70=_5c[lc]||_6c,_71=_5e[_70],_72=_5d[lc]||_6c; -if(_6e==3){ -do{ -if(_70=="style"&&typeof _6d!="string"){ -d.style(_6b,_6d); -break; -} -if(_70=="innerHTML"){ -if(d.isIE&&_6b.tagName.toLowerCase() in _6a){ -d.empty(_6b); -_6b.appendChild(d._toDom(_6d,_6b.ownerDocument)); -}else{ -_6b[_70]=_6d; -} -break; -} -if(d.isFunction(_6d)){ -var _73=d.attr(_6b,_69); -if(!_73){ -_73=_68++; -d.attr(_6b,_69,_73); -} -if(!_67[_73]){ -_67[_73]={}; -} -var h=_67[_73][_70]; -if(h){ -d.disconnect(h); -}else{ -try{ -delete _6b[_70]; -} -catch(e){ -} -} -_67[_73][_70]=d.connect(_6b,_70,_6d); -break; -} -if(_71||typeof _6d=="boolean"){ -_6b[_70]=_6d; -break; -} -_6b.setAttribute(_72,_6d); -}while(false); -return _6b; -} -_6d=_6b[_70]; -if(_71&&typeof _6d!="undefined"){ -return _6d; -} -if(_70!="href"&&(typeof _6d=="boolean"||d.isFunction(_6d))){ -return _6d; -} -return _61(_6b,_72)?_6b.getAttribute(_72):null; -}; -dojo.removeAttr=function(_74,_75){ -_5(_74).removeAttribute(_5f(_75)); -}; -dojo.getNodeProp=function(_76,_77){ -_76=_5(_76); -var lc=_77.toLowerCase(),_78=_5c[lc]||_77; -if((_78 in _76)&&_78!="href"){ -return _76[_78]; -} -var _79=_5d[lc]||_77; -return _61(_76,_79)?_76.getAttribute(_79):null; -}; -dojo.create=function(tag,_7a,_7b,pos){ -var doc=d.doc; -if(_7b){ -_7b=_5(_7b); -doc=_7b.ownerDocument; -} -if(typeof tag=="string"){ -tag=doc.createElement(tag); -} -if(_7a){ -d.attr(tag,_7a); -} -if(_7b){ -d.place(tag,_7b,pos); -} -return tag; -}; -d.empty=d.isIE?function(_7c){ -_7c=_5(_7c); -for(var c;c=_7c.lastChild;){ -d.destroy(c); -} -}:function(_7d){ -_5(_7d).innerHTML=""; -}; -var _7e={option:["select"],tbody:["table"],thead:["table"],tfoot:["table"],tr:["table","tbody"],td:["table","tbody","tr"],th:["table","thead","tr"],legend:["fieldset"],caption:["table"],colgroup:["table"],col:["table","colgroup"],li:["ul"]},_7f=/<\s*([\w\:]+)/,_80={},_81=0,_82="__"+d._scopeName+"ToDomId"; -for(var _83 in _7e){ -var tw=_7e[_83]; -tw.pre=_83=="option"?"<select multiple=\"multiple\">":"<"+tw.join("><")+">"; -tw.post="</"+tw.reverse().join("></")+">"; -} -d._toDom=function(_84,doc){ -doc=doc||d.doc; -var _85=doc[_82]; -if(!_85){ -doc[_82]=_85=++_81+""; -_80[_85]=doc.createElement("div"); -} -_84+=""; -var _86=_84.match(_7f),tag=_86?_86[1].toLowerCase():"",_87=_80[_85],_88,i,fc,df; -if(_86&&_7e[tag]){ -_88=_7e[tag]; -_87.innerHTML=_88.pre+_84+_88.post; -for(i=_88.length;i;--i){ -_87=_87.firstChild; -} -}else{ -_87.innerHTML=_84; -} -if(_87.childNodes.length==1){ -return _87.removeChild(_87.firstChild); -} -df=doc.createDocumentFragment(); -while(fc=_87.firstChild){ -df.appendChild(fc); -} -return df; -}; -var _89="className"; -dojo.hasClass=function(_8a,_8b){ -return ((" "+_5(_8a)[_89]+" ").indexOf(" "+_8b+" ")>=0); -}; -var _8c=/\s+/,a1=[""],_8d=function(s){ -if(typeof s=="string"||s instanceof String){ -if(s.indexOf(" ")<0){ -a1[0]=s; -return a1; -}else{ -return s.split(_8c); -} -} -return s||""; -}; -dojo.addClass=function(_8e,_8f){ -_8e=_5(_8e); -_8f=_8d(_8f); -var cls=_8e[_89],_90; -cls=cls?" "+cls+" ":" "; -_90=cls.length; -for(var i=0,len=_8f.length,c;i<len;++i){ -c=_8f[i]; -if(c&&cls.indexOf(" "+c+" ")<0){ -cls+=c+" "; -} -} -if(_90<cls.length){ -_8e[_89]=cls.substr(1,cls.length-2); -} -}; -dojo.removeClass=function(_91,_92){ -_91=_5(_91); -var cls; -if(_92!==undefined){ -_92=_8d(_92); -cls=" "+_91[_89]+" "; -for(var i=0,len=_92.length;i<len;++i){ -cls=cls.replace(" "+_92[i]+" "," "); -} -cls=d.trim(cls); -}else{ -cls=""; -} -if(_91[_89]!=cls){ -_91[_89]=cls; -} -}; -dojo.toggleClass=function(_93,_94,_95){ -if(_95===undefined){ -_95=!d.hasClass(_93,_94); -} -d[_95?"addClass":"removeClass"](_93,_94); -}; + var d = dojo; + var byId = d.byId; + + var _destroyContainer = null, + _destroyDoc; + d.addOnWindowUnload(function(){ + _destroyContainer = null; //prevent IE leak + }); + +/*===== + dojo._destroyElement = function(node){ + // summary: + // Existing alias for `dojo.destroy`. Deprecated, will be removed + // in 2.0 + } +=====*/ + dojo._destroyElement = dojo.destroy = function(/*String|DomNode*/node){ + // summary: + // Removes a node from its parent, clobbering it and all of its + // children. + // + // description: + // Removes a node from its parent, clobbering it and all of its + // children. Function only works with DomNodes, and returns nothing. + // + // node: + // A String ID or DomNode reference of the element to be destroyed + // + // example: + // Destroy a node byId: + // | dojo.destroy("someId"); + // + // example: + // Destroy all nodes in a list by reference: + // | dojo.query(".someNode").forEach(dojo.destroy); + + node = byId(node); + try{ + var doc = node.ownerDocument; + // cannot use _destroyContainer.ownerDocument since this can throw an exception on IE + if(!_destroyContainer || _destroyDoc != doc){ + _destroyContainer = doc.createElement("div"); + _destroyDoc = doc; + } + _destroyContainer.appendChild(node.parentNode ? node.parentNode.removeChild(node) : node); + // NOTE: see http://trac.dojotoolkit.org/ticket/2931. This may be a bug and not a feature + _destroyContainer.innerHTML = ""; + }catch(e){ + /* squelch */ + } + }; + + dojo.isDescendant = function(/*DomNode|String*/node, /*DomNode|String*/ancestor){ + // summary: + // Returns true if node is a descendant of ancestor + // node: string id or node reference to test + // ancestor: string id or node reference of potential parent to test against + // + // example: + // Test is node id="bar" is a descendant of node id="foo" + // | if(dojo.isDescendant("bar", "foo")){ ... } + try{ + node = byId(node); + ancestor = byId(ancestor); + while(node){ + if(node == ancestor){ + return true; // Boolean + } + node = node.parentNode; + } + }catch(e){ /* squelch, return false */ } + return false; // Boolean + }; + + dojo.setSelectable = function(/*DomNode|String*/node, /*Boolean*/selectable){ + // summary: + // Enable or disable selection on a node + // node: + // id or reference to node + // selectable: + // state to put the node in. false indicates unselectable, true + // allows selection. + // example: + // Make the node id="bar" unselectable + // | dojo.setSelectable("bar"); + // example: + // Make the node id="bar" selectable + // | dojo.setSelectable("bar", true); + node = byId(node); + if(d.isMozilla){ + node.style.MozUserSelect = selectable ? "" : "none"; + }else if(d.isKhtml || d.isWebKit){ + node.style.KhtmlUserSelect = selectable ? "auto" : "none"; + }else if(d.isIE){ + var v = (node.unselectable = selectable ? "" : "on"); + d.query("*", node).forEach("item.unselectable = '"+v+"'"); + } + //FIXME: else? Opera? + }; + + var _insertBefore = function(/*DomNode*/node, /*DomNode*/ref){ + var parent = ref.parentNode; + if(parent){ + parent.insertBefore(node, ref); + } + }; + + var _insertAfter = function(/*DomNode*/node, /*DomNode*/ref){ + // summary: + // Try to insert node after ref + var parent = ref.parentNode; + if(parent){ + if(parent.lastChild == ref){ + parent.appendChild(node); + }else{ + parent.insertBefore(node, ref.nextSibling); + } + } + }; + + dojo.place = function(node, refNode, position){ + // summary: + // Attempt to insert node into the DOM, choosing from various positioning options. + // Returns the first argument resolved to a DOM node. + // + // node: String|DomNode + // id or node reference, or HTML fragment starting with "<" to place relative to refNode + // + // refNode: String|DomNode + // id or node reference to use as basis for placement + // + // position: String|Number? + // string noting the position of node relative to refNode or a + // number indicating the location in the childNodes collection of refNode. + // Accepted string values are: + // | * before + // | * after + // | * replace + // | * only + // | * first + // | * last + // "first" and "last" indicate positions as children of refNode, "replace" replaces refNode, + // "only" replaces all children. position defaults to "last" if not specified + // + // returns: DomNode + // Returned values is the first argument resolved to a DOM node. + // + // .place() is also a method of `dojo.NodeList`, allowing `dojo.query` node lookups. + // + // example: + // Place a node by string id as the last child of another node by string id: + // | dojo.place("someNode", "anotherNode"); + // + // example: + // Place a node by string id before another node by string id + // | dojo.place("someNode", "anotherNode", "before"); + // + // example: + // Create a Node, and place it in the body element (last child): + // | dojo.place("<div></div>", dojo.body()); + // + // example: + // Put a new LI as the first child of a list by id: + // | dojo.place("<li></li>", "someUl", "first"); + + refNode = byId(refNode); + if(typeof node == "string"){ // inline'd type check + node = node.charAt(0) == "<" ? d._toDom(node, refNode.ownerDocument) : byId(node); + } + if(typeof position == "number"){ // inline'd type check + var cn = refNode.childNodes; + if(!cn.length || cn.length <= position){ + refNode.appendChild(node); + }else{ + _insertBefore(node, cn[position < 0 ? 0 : position]); + } + }else{ + switch(position){ + case "before": + _insertBefore(node, refNode); + break; + case "after": + _insertAfter(node, refNode); + break; + case "replace": + refNode.parentNode.replaceChild(node, refNode); + break; + case "only": + d.empty(refNode); + refNode.appendChild(node); + break; + case "first": + if(refNode.firstChild){ + _insertBefore(node, refNode.firstChild); + break; + } + // else fallthrough... + default: // aka: last + refNode.appendChild(node); + } + } + return node; // DomNode + } + + // Box functions will assume this model. + // On IE/Opera, BORDER_BOX will be set if the primary document is in quirks mode. + // Can be set to change behavior of box setters. + + // can be either: + // "border-box" + // "content-box" (default) + dojo.boxModel = "content-box"; + + // We punt per-node box mode testing completely. + // If anybody cares, we can provide an additional (optional) unit + // that overrides existing code to include per-node box sensitivity. + + // Opera documentation claims that Opera 9 uses border-box in BackCompat mode. + // but experiments (Opera 9.10.8679 on Windows Vista) indicate that it actually continues to use content-box. + // IIRC, earlier versions of Opera did in fact use border-box. + // Opera guys, this is really confusing. Opera being broken in quirks mode is not our fault. + + if(d.isIE /*|| dojo.isOpera*/){ + // client code may have to adjust if compatMode varies across iframes + d.boxModel = document.compatMode == "BackCompat" ? "border-box" : "content-box"; + } + + // ============================= + // Style Functions + // ============================= + + // getComputedStyle drives most of the style code. + // Wherever possible, reuse the returned object. + // + // API functions below that need to access computed styles accept an + // optional computedStyle parameter. + // If this parameter is omitted, the functions will call getComputedStyle themselves. + // This way, calling code can access computedStyle once, and then pass the reference to + // multiple API functions. + +/*===== + dojo.getComputedStyle = function(node){ + // summary: + // Returns a "computed style" object. + // + // description: + // Gets a "computed style" object which can be used to gather + // information about the current state of the rendered node. + // + // Note that this may behave differently on different browsers. + // Values may have different formats and value encodings across + // browsers. + // + // Note also that this method is expensive. Wherever possible, + // reuse the returned object. + // + // Use the dojo.style() method for more consistent (pixelized) + // return values. + // + // node: DOMNode + // A reference to a DOM node. Does NOT support taking an + // ID string for speed reasons. + // example: + // | dojo.getComputedStyle(dojo.byId('foo')).borderWidth; + // + // example: + // Reusing the returned object, avoiding multiple lookups: + // | var cs = dojo.getComputedStyle(dojo.byId("someNode")); + // | var w = cs.width, h = cs.height; + return; // CSS2Properties + } +=====*/ + + // Although we normally eschew argument validation at this + // level, here we test argument 'node' for (duck)type, + // by testing nodeType, ecause 'document' is the 'parentNode' of 'body' + // it is frequently sent to this function even + // though it is not Element. + var gcs; + if(d.isWebKit){ + gcs = function(/*DomNode*/node){ + var s; + if(node.nodeType == 1){ + var dv = node.ownerDocument.defaultView; + s = dv.getComputedStyle(node, null); + if(!s && node.style){ + node.style.display = ""; + s = dv.getComputedStyle(node, null); + } + } + return s || {}; + }; + }else if(d.isIE){ + gcs = function(node){ + // IE (as of 7) doesn't expose Element like sane browsers + return node.nodeType == 1 /* ELEMENT_NODE*/ ? node.currentStyle : {}; + }; + }else{ + gcs = function(node){ + return node.nodeType == 1 ? + node.ownerDocument.defaultView.getComputedStyle(node, null) : {}; + }; + } + dojo.getComputedStyle = gcs; + + if(!d.isIE){ + d._toPixelValue = function(element, value){ + // style values can be floats, client code may want + // to round for integer pixels. + return parseFloat(value) || 0; + }; + }else{ + d._toPixelValue = function(element, avalue){ + if(!avalue){ return 0; } + // on IE7, medium is usually 4 pixels + if(avalue == "medium"){ return 4; } + // style values can be floats, client code may + // want to round this value for integer pixels. + if(avalue.slice && avalue.slice(-2) == 'px'){ return parseFloat(avalue); } + with(element){ + var sLeft = style.left; + var rsLeft = runtimeStyle.left; + runtimeStyle.left = currentStyle.left; + try{ + // 'avalue' may be incompatible with style.left, which can cause IE to throw + // this has been observed for border widths using "thin", "medium", "thick" constants + // those particular constants could be trapped by a lookup + // but perhaps there are more + style.left = avalue; + avalue = style.pixelLeft; + }catch(e){ + avalue = 0; + } + style.left = sLeft; + runtimeStyle.left = rsLeft; + } + return avalue; + } + } + var px = d._toPixelValue; + + // FIXME: there opacity quirks on FF that we haven't ported over. Hrm. + /*===== + dojo._getOpacity = function(node){ + // summary: + // Returns the current opacity of the passed node as a + // floating-point value between 0 and 1. + // node: DomNode + // a reference to a DOM node. Does NOT support taking an + // ID string for speed reasons. + // returns: Number between 0 and 1 + return; // Number + } + =====*/ + + var astr = "DXImageTransform.Microsoft.Alpha"; + var af = function(n, f){ + try{ + return n.filters.item(astr); + }catch(e){ + return f ? {} : null; + } + }; + + dojo._getOpacity = + d.isIE ? function(node){ + try{ + return af(node).Opacity / 100; // Number + }catch(e){ + return 1; // Number + } + } : + function(node){ + return gcs(node).opacity; + }; + + /*===== + dojo._setOpacity = function(node, opacity){ + // summary: + // set the opacity of the passed node portably. Returns the + // new opacity of the node. + // node: DOMNode + // a reference to a DOM node. Does NOT support taking an + // ID string for performance reasons. + // opacity: Number + // A Number between 0 and 1. 0 specifies transparent. + // returns: Number between 0 and 1 + return; // Number + } + =====*/ + + dojo._setOpacity = + d.isIE ? function(/*DomNode*/node, /*Number*/opacity){ + var ov = opacity * 100, opaque = opacity == 1; + node.style.zoom = opaque ? "" : 1; + + if(!af(node)){ + if(opaque){ + return opacity; + } + node.style.filter += " progid:" + astr + "(Opacity=" + ov + ")"; + }else{ + af(node, 1).Opacity = ov; + } + + // on IE7 Alpha(Filter opacity=100) makes text look fuzzy so disable it altogether (bug #2661), + //but still update the opacity value so we can get a correct reading if it is read later. + af(node, 1).Enabled = !opaque; + + if(node.nodeName.toLowerCase() == "tr"){ + d.query("> td", node).forEach(function(i){ + d._setOpacity(i, opacity); + }); + } + return opacity; + } : + function(node, opacity){ + return node.style.opacity = opacity; + }; + + var _pixelNamesCache = { + left: true, top: true + }; + var _pixelRegExp = /margin|padding|width|height|max|min|offset/; // |border + var _toStyleValue = function(node, type, value){ + type = type.toLowerCase(); // FIXME: should we really be doing string case conversion here? Should we cache it? Need to profile! + if(d.isIE){ + if(value == "auto"){ + if(type == "height"){ return node.offsetHeight; } + if(type == "width"){ return node.offsetWidth; } + } + if(type == "fontweight"){ + switch(value){ + case 700: return "bold"; + case 400: + default: return "normal"; + } + } + } + if(!(type in _pixelNamesCache)){ + _pixelNamesCache[type] = _pixelRegExp.test(type); + } + return _pixelNamesCache[type] ? px(node, value) : value; + }; + + var _floatStyle = d.isIE ? "styleFloat" : "cssFloat", + _floatAliases = { "cssFloat": _floatStyle, "styleFloat": _floatStyle, "float": _floatStyle } + ; + + // public API + + dojo.style = function( /*DomNode|String*/ node, + /*String?|Object?*/ style, + /*String?*/ value){ + // summary: + // Accesses styles on a node. If 2 arguments are + // passed, acts as a getter. If 3 arguments are passed, acts + // as a setter. + // description: + // Getting the style value uses the computed style for the node, so the value + // will be a calculated value, not just the immediate node.style value. + // Also when getting values, use specific style names, + // like "borderBottomWidth" instead of "border" since compound values like + // "border" are not necessarily reflected as expected. + // If you want to get node dimensions, use `dojo.marginBox()`, + // `dojo.contentBox()` or `dojo.position()`. + // node: + // id or reference to node to get/set style for + // style: + // the style property to set in DOM-accessor format + // ("borderWidth", not "border-width") or an object with key/value + // pairs suitable for setting each property. + // value: + // If passed, sets value on the node for style, handling + // cross-browser concerns. When setting a pixel value, + // be sure to include "px" in the value. For instance, top: "200px". + // Otherwise, in some cases, some browsers will not apply the style. + // example: + // Passing only an ID or node returns the computed style object of + // the node: + // | dojo.style("thinger"); + // example: + // Passing a node and a style property returns the current + // normalized, computed value for that property: + // | dojo.style("thinger", "opacity"); // 1 by default + // + // example: + // Passing a node, a style property, and a value changes the + // current display of the node and returns the new computed value + // | dojo.style("thinger", "opacity", 0.5); // == 0.5 + // + // example: + // Passing a node, an object-style style property sets each of the values in turn and returns the computed style object of the node: + // | dojo.style("thinger", { + // | "opacity": 0.5, + // | "border": "3px solid black", + // | "height": "300px" + // | }); + // + // example: + // When the CSS style property is hyphenated, the JavaScript property is camelCased. + // font-size becomes fontSize, and so on. + // | dojo.style("thinger",{ + // | fontSize:"14pt", + // | letterSpacing:"1.2em" + // | }); + // + // example: + // dojo.NodeList implements .style() using the same syntax, omitting the "node" parameter, calling + // dojo.style() on every element of the list. See: `dojo.query()` and `dojo.NodeList()` + // | dojo.query(".someClassName").style("visibility","hidden"); + // | // or + // | dojo.query("#baz > div").style({ + // | opacity:0.75, + // | fontSize:"13pt" + // | }); + + var n = byId(node), args = arguments.length, op = (style == "opacity"); + style = _floatAliases[style] || style; + if(args == 3){ + return op ? d._setOpacity(n, value) : n.style[style] = value; /*Number*/ + } + if(args == 2 && op){ + return d._getOpacity(n); + } + var s = gcs(n); + if(args == 2 && typeof style != "string"){ // inline'd type check + for(var x in style){ + d.style(node, x, style[x]); + } + return s; + } + return (args == 1) ? s : _toStyleValue(n, style, s[style] || n.style[style]); /* CSS2Properties||String||Number */ + } + + // ============================= + // Box Functions + // ============================= + + dojo._getPadExtents = function(/*DomNode*/n, /*Object*/computedStyle){ + // summary: + // Returns object with special values specifically useful for node + // fitting. + // description: + // Returns an object with `w`, `h`, `l`, `t` properties: + // | l/t = left/top padding (respectively) + // | w = the total of the left and right padding + // | h = the total of the top and bottom padding + // If 'node' has position, l/t forms the origin for child nodes. + // The w/h are used for calculating boxes. + // Normally application code will not need to invoke this + // directly, and will use the ...box... functions instead. + var + s = computedStyle||gcs(n), + l = px(n, s.paddingLeft), + t = px(n, s.paddingTop); + return { + l: l, + t: t, + w: l+px(n, s.paddingRight), + h: t+px(n, s.paddingBottom) + }; + } + + dojo._getBorderExtents = function(/*DomNode*/n, /*Object*/computedStyle){ + // summary: + // returns an object with properties useful for noting the border + // dimensions. + // description: + // * l/t = the sum of left/top border (respectively) + // * w = the sum of the left and right border + // * h = the sum of the top and bottom border + // + // The w/h are used for calculating boxes. + // Normally application code will not need to invoke this + // directly, and will use the ...box... functions instead. + var + ne = "none", + s = computedStyle||gcs(n), + bl = (s.borderLeftStyle != ne ? px(n, s.borderLeftWidth) : 0), + bt = (s.borderTopStyle != ne ? px(n, s.borderTopWidth) : 0); + return { + l: bl, + t: bt, + w: bl + (s.borderRightStyle!=ne ? px(n, s.borderRightWidth) : 0), + h: bt + (s.borderBottomStyle!=ne ? px(n, s.borderBottomWidth) : 0) + }; + } + + dojo._getPadBorderExtents = function(/*DomNode*/n, /*Object*/computedStyle){ + // summary: + // Returns object with properties useful for box fitting with + // regards to padding. + // description: + // * l/t = the sum of left/top padding and left/top border (respectively) + // * w = the sum of the left and right padding and border + // * h = the sum of the top and bottom padding and border + // + // The w/h are used for calculating boxes. + // Normally application code will not need to invoke this + // directly, and will use the ...box... functions instead. + var + s = computedStyle||gcs(n), + p = d._getPadExtents(n, s), + b = d._getBorderExtents(n, s); + return { + l: p.l + b.l, + t: p.t + b.t, + w: p.w + b.w, + h: p.h + b.h + }; + } + + dojo._getMarginExtents = function(n, computedStyle){ + // summary: + // returns object with properties useful for box fitting with + // regards to box margins (i.e., the outer-box). + // + // * l/t = marginLeft, marginTop, respectively + // * w = total width, margin inclusive + // * h = total height, margin inclusive + // + // The w/h are used for calculating boxes. + // Normally application code will not need to invoke this + // directly, and will use the ...box... functions instead. + var + s = computedStyle||gcs(n), + l = px(n, s.marginLeft), + t = px(n, s.marginTop), + r = px(n, s.marginRight), + b = px(n, s.marginBottom); + if(d.isWebKit && (s.position != "absolute")){ + // FIXME: Safari's version of the computed right margin + // is the space between our right edge and the right edge + // of our offsetParent. + // What we are looking for is the actual margin value as + // determined by CSS. + // Hack solution is to assume left/right margins are the same. + r = l; + } + return { + l: l, + t: t, + w: l+r, + h: t+b + }; + } + + // Box getters work in any box context because offsetWidth/clientWidth + // are invariant wrt box context + // + // They do *not* work for display: inline objects that have padding styles + // because the user agent ignores padding (it's bogus styling in any case) + // + // Be careful with IMGs because they are inline or block depending on + // browser and browser mode. + + // Although it would be easier to read, there are not separate versions of + // _getMarginBox for each browser because: + // 1. the branching is not expensive + // 2. factoring the shared code wastes cycles (function call overhead) + // 3. duplicating the shared code wastes bytes + + dojo._getMarginBox = function(/*DomNode*/node, /*Object*/computedStyle){ + // summary: + // returns an object that encodes the width, height, left and top + // positions of the node's margin box. + var s = computedStyle || gcs(node), me = d._getMarginExtents(node, s); + var l = node.offsetLeft - me.l, t = node.offsetTop - me.t, p = node.parentNode; + if(d.isMoz){ + // Mozilla: + // If offsetParent has a computed overflow != visible, the offsetLeft is decreased + // by the parent's border. + // We don't want to compute the parent's style, so instead we examine node's + // computed left/top which is more stable. + var sl = parseFloat(s.left), st = parseFloat(s.top); + if(!isNaN(sl) && !isNaN(st)){ + l = sl, t = st; + }else{ + // If child's computed left/top are not parseable as a number (e.g. "auto"), we + // have no choice but to examine the parent's computed style. + if(p && p.style){ + var pcs = gcs(p); + if(pcs.overflow != "visible"){ + var be = d._getBorderExtents(p, pcs); + l += be.l, t += be.t; + } + } + } + }else if(d.isOpera || (d.isIE > 7 && !d.isQuirks)){ + // On Opera and IE 8, offsetLeft/Top includes the parent's border + if(p){ + be = d._getBorderExtents(p); + l -= be.l; + t -= be.t; + } + } + return { + l: l, + t: t, + w: node.offsetWidth + me.w, + h: node.offsetHeight + me.h + }; + } + + dojo._getContentBox = function(node, computedStyle){ + // summary: + // Returns an object that encodes the width, height, left and top + // positions of the node's content box, irrespective of the + // current box model. + + // clientWidth/Height are important since the automatically account for scrollbars + // fallback to offsetWidth/Height for special cases (see #3378) + var s = computedStyle || gcs(node), + pe = d._getPadExtents(node, s), + be = d._getBorderExtents(node, s), + w = node.clientWidth, + h + ; + if(!w){ + w = node.offsetWidth, h = node.offsetHeight; + }else{ + h = node.clientHeight, be.w = be.h = 0; + } + // On Opera, offsetLeft includes the parent's border + if(d.isOpera){ pe.l += be.l; pe.t += be.t; }; + return { + l: pe.l, + t: pe.t, + w: w - pe.w - be.w, + h: h - pe.h - be.h + }; + } + + dojo._getBorderBox = function(node, computedStyle){ + var s = computedStyle || gcs(node), + pe = d._getPadExtents(node, s), + cb = d._getContentBox(node, s) + ; + return { + l: cb.l - pe.l, + t: cb.t - pe.t, + w: cb.w + pe.w, + h: cb.h + pe.h + }; + } + + // Box setters depend on box context because interpretation of width/height styles + // vary wrt box context. + // + // The value of dojo.boxModel is used to determine box context. + // dojo.boxModel can be set directly to change behavior. + // + // Beware of display: inline objects that have padding styles + // because the user agent ignores padding (it's a bogus setup anyway) + // + // Be careful with IMGs because they are inline or block depending on + // browser and browser mode. + // + // Elements other than DIV may have special quirks, like built-in + // margins or padding, or values not detectable via computedStyle. + // In particular, margins on TABLE do not seems to appear + // at all in computedStyle on Mozilla. + + dojo._setBox = function(/*DomNode*/node, /*Number?*/l, /*Number?*/t, /*Number?*/w, /*Number?*/h, /*String?*/u){ + // summary: + // sets width/height/left/top in the current (native) box-model + // dimentions. Uses the unit passed in u. + // node: + // DOM Node reference. Id string not supported for performance + // reasons. + // l: + // left offset from parent. + // t: + // top offset from parent. + // w: + // width in current box model. + // h: + // width in current box model. + // u: + // unit measure to use for other measures. Defaults to "px". + u = u || "px"; + var s = node.style; + if(!isNaN(l)){ s.left = l + u; } + if(!isNaN(t)){ s.top = t + u; } + if(w >= 0){ s.width = w + u; } + if(h >= 0){ s.height = h + u; } + } + + dojo._isButtonTag = function(/*DomNode*/node) { + // summary: + // True if the node is BUTTON or INPUT.type="button". + return node.tagName == "BUTTON" + || node.tagName=="INPUT" && (node.getAttribute("type")||'').toUpperCase() == "BUTTON"; // boolean + } + + dojo._usesBorderBox = function(/*DomNode*/node){ + // summary: + // True if the node uses border-box layout. + + // We could test the computed style of node to see if a particular box + // has been specified, but there are details and we choose not to bother. + + // TABLE and BUTTON (and INPUT type=button) are always border-box by default. + // If you have assigned a different box to either one via CSS then + // box functions will break. + + var n = node.tagName; + return d.boxModel=="border-box" || n=="TABLE" || d._isButtonTag(node); // boolean + } + + dojo._setContentSize = function(/*DomNode*/node, /*Number*/widthPx, /*Number*/heightPx, /*Object*/computedStyle){ + // summary: + // Sets the size of the node's contents, irrespective of margins, + // padding, or borders. + if(d._usesBorderBox(node)){ + var pb = d._getPadBorderExtents(node, computedStyle); + if(widthPx >= 0){ widthPx += pb.w; } + if(heightPx >= 0){ heightPx += pb.h; } + } + d._setBox(node, NaN, NaN, widthPx, heightPx); + } + + dojo._setMarginBox = function(/*DomNode*/node, /*Number?*/leftPx, /*Number?*/topPx, + /*Number?*/widthPx, /*Number?*/heightPx, + /*Object*/computedStyle){ + // summary: + // sets the size of the node's margin box and placement + // (left/top), irrespective of box model. Think of it as a + // passthrough to dojo._setBox that handles box-model vagaries for + // you. + + var s = computedStyle || gcs(node), + // Some elements have special padding, margin, and box-model settings. + // To use box functions you may need to set padding, margin explicitly. + // Controlling box-model is harder, in a pinch you might set dojo.boxModel. + bb = d._usesBorderBox(node), + pb = bb ? _nilExtents : d._getPadBorderExtents(node, s) + ; + if(d.isWebKit){ + // on Safari (3.1.2), button nodes with no explicit size have a default margin + // setting an explicit size eliminates the margin. + // We have to swizzle the width to get correct margin reading. + if(d._isButtonTag(node)){ + var ns = node.style; + if(widthPx >= 0 && !ns.width) { ns.width = "4px"; } + if(heightPx >= 0 && !ns.height) { ns.height = "4px"; } + } + } + var mb = d._getMarginExtents(node, s); + if(widthPx >= 0){ widthPx = Math.max(widthPx - pb.w - mb.w, 0); } + if(heightPx >= 0){ heightPx = Math.max(heightPx - pb.h - mb.h, 0); } + d._setBox(node, leftPx, topPx, widthPx, heightPx); + } + + var _nilExtents = { l:0, t:0, w:0, h:0 }; + + // public API + + dojo.marginBox = function(/*DomNode|String*/node, /*Object?*/box){ + // summary: + // Getter/setter for the margin-box of node. + // description: + // Getter/setter for the margin-box of node. + // Returns an object in the expected format of box (regardless + // if box is passed). The object might look like: + // `{ l: 50, t: 200, w: 300: h: 150 }` + // for a node offset from its parent 50px to the left, 200px from + // the top with a margin width of 300px and a margin-height of + // 150px. + // node: + // id or reference to DOM Node to get/set box for + // box: + // If passed, denotes that dojo.marginBox() should + // update/set the margin box for node. Box is an object in the + // above format. All properties are optional if passed. + // example: + // Retrieve the marginbox of a passed node + // | var box = dojo.marginBox("someNodeId"); + // | console.dir(box); + // + // example: + // Set a node's marginbox to the size of another node + // | var box = dojo.marginBox("someNodeId"); + // | dojo.marginBox("someOtherNode", box); + + var n = byId(node), s = gcs(n), b = box; + return !b ? d._getMarginBox(n, s) : d._setMarginBox(n, b.l, b.t, b.w, b.h, s); // Object + } + + dojo.contentBox = function(/*DomNode|String*/node, /*Object?*/box){ + // summary: + // Getter/setter for the content-box of node. + // description: + // Returns an object in the expected format of box (regardless if box is passed). + // The object might look like: + // `{ l: 50, t: 200, w: 300: h: 150 }` + // for a node offset from its parent 50px to the left, 200px from + // the top with a content width of 300px and a content-height of + // 150px. Note that the content box may have a much larger border + // or margin box, depending on the box model currently in use and + // CSS values set/inherited for node. + // While the getter will return top and left values, the + // setter only accepts setting the width and height. + // node: + // id or reference to DOM Node to get/set box for + // box: + // If passed, denotes that dojo.contentBox() should + // update/set the content box for node. Box is an object in the + // above format, but only w (width) and h (height) are supported. + // All properties are optional if passed. + var n = byId(node), s = gcs(n), b = box; + return !b ? d._getContentBox(n, s) : d._setContentSize(n, b.w, b.h, s); // Object + } + + // ============================= + // Positioning + // ============================= + + var _sumAncestorProperties = function(node, prop){ + if(!(node = (node||0).parentNode)){return 0} + var val, retVal = 0, _b = d.body(); + while(node && node.style){ + if(gcs(node).position == "fixed"){ + return 0; + } + val = node[prop]; + if(val){ + retVal += val - 0; + // opera and khtml #body & #html has the same values, we only + // need one value + if(node == _b){ break; } + } + node = node.parentNode; + } + return retVal; // integer + } + + dojo._docScroll = function(){ + var n = d.global; + return "pageXOffset" in n? { x:n.pageXOffset, y:n.pageYOffset } : + (n=d.doc.documentElement, n.clientHeight? { x:d._fixIeBiDiScrollLeft(n.scrollLeft), y:n.scrollTop } : + (n=d.body(), { x:n.scrollLeft||0, y:n.scrollTop||0 })); + }; + + dojo._isBodyLtr = function(){ + return "_bodyLtr" in d? d._bodyLtr : + d._bodyLtr = (d.body().dir || d.doc.documentElement.dir || "ltr").toLowerCase() == "ltr"; // Boolean + } + + dojo._getIeDocumentElementOffset = function(){ + // summary: + // returns the offset in x and y from the document body to the + // visual edge of the page + // description: + // The following values in IE contain an offset: + // | event.clientX + // | event.clientY + // | node.getBoundingClientRect().left + // | node.getBoundingClientRect().top + // But other position related values do not contain this offset, + // such as node.offsetLeft, node.offsetTop, node.style.left and + // node.style.top. The offset is always (2, 2) in LTR direction. + // When the body is in RTL direction, the offset counts the width + // of left scroll bar's width. This function computes the actual + // offset. + + //NOTE: assumes we're being called in an IE browser + + var de = d.doc.documentElement; // only deal with HTML element here, _abs handles body/quirks + + if(d.isIE < 8){ + var r = de.getBoundingClientRect(); // works well for IE6+ + //console.debug('rect left,top = ' + r.left+','+r.top + ', html client left/top = ' + de.clientLeft+','+de.clientTop + ', rtl = ' + (!d._isBodyLtr()) + ', quirks = ' + d.isQuirks); + var l = r.left, + t = r.top; + if(d.isIE < 7){ + l += de.clientLeft; // scrollbar size in strict/RTL, or, + t += de.clientTop; // HTML border size in strict + } + return { + x: l < 0? 0 : l, // FRAME element border size can lead to inaccurate negative values + y: t < 0? 0 : t + }; + }else{ + return { + x: 0, + y: 0 + }; + } + + }; + + dojo._fixIeBiDiScrollLeft = function(/*Integer*/ scrollLeft){ + // In RTL direction, scrollLeft should be a negative value, but IE < 8 + // returns a positive one. All codes using documentElement.scrollLeft + // must call this function to fix this error, otherwise the position + // will offset to right when there is a horizontal scrollbar. + + var dd = d.doc; + if(d.isIE < 8 && !d._isBodyLtr()){ + var de = d.isQuirks ? dd.body : dd.documentElement; + return scrollLeft + de.clientWidth - de.scrollWidth; // Integer + } + return scrollLeft; // Integer + } + + // FIXME: need a setter for coords or a moveTo!! + dojo._abs = dojo.position = function(/*DomNode*/node, /*Boolean?*/includeScroll){ + // summary: + // Gets the position and size of the passed element relative to + // the viewport (if includeScroll==false), or relative to the + // document root (if includeScroll==true). + // + // description: + // Returns an object of the form: + // { x: 100, y: 300, w: 20, h: 15 } + // If includeScroll==true, the x and y values will include any + // document offsets that may affect the position relative to the + // viewport. + // Uses the border-box model (inclusive of border and padding but + // not margin). Does not act as a setter. + + var db = d.body(), dh = db.parentNode, ret; + node = byId(node); + if(node["getBoundingClientRect"]){ + // IE6+, FF3+, super-modern WebKit, and Opera 9.6+ all take this branch + ret = node.getBoundingClientRect(); + ret = { x: ret.left, y: ret.top, w: ret.right - ret.left, h: ret.bottom - ret.top }; + if(d.isIE){ + // On IE there's a 2px offset that we need to adjust for, see _getIeDocumentElementOffset() + var offset = d._getIeDocumentElementOffset(); + + // fixes the position in IE, quirks mode + ret.x -= offset.x + (d.isQuirks ? db.clientLeft+db.offsetLeft : 0); + ret.y -= offset.y + (d.isQuirks ? db.clientTop+db.offsetTop : 0); + }else if(d.isFF == 3){ + // In FF3 you have to subtract the document element margins. + // Fixed in FF3.5 though. + var cs = gcs(dh); + ret.x -= px(dh, cs.marginLeft) + px(dh, cs.borderLeftWidth); + ret.y -= px(dh, cs.marginTop) + px(dh, cs.borderTopWidth); + } + }else{ + // FF2 and older WebKit + ret = { + x: 0, + y: 0, + w: node.offsetWidth, + h: node.offsetHeight + }; + if(node["offsetParent"]){ + ret.x -= _sumAncestorProperties(node, "scrollLeft"); + ret.y -= _sumAncestorProperties(node, "scrollTop"); + + var curnode = node; + do{ + var n = curnode.offsetLeft, + t = curnode.offsetTop; + ret.x += isNaN(n) ? 0 : n; + ret.y += isNaN(t) ? 0 : t; + + cs = gcs(curnode); + if(curnode != node){ + if(d.isMoz){ + // tried left+right with differently sized left/right borders + // it really is 2xleft border in FF, not left+right, even in RTL! + ret.x += 2 * px(curnode,cs.borderLeftWidth); + ret.y += 2 * px(curnode,cs.borderTopWidth); + }else{ + ret.x += px(curnode, cs.borderLeftWidth); + ret.y += px(curnode, cs.borderTopWidth); + } + } + // static children in a static div in FF2 are affected by the div's border as well + // but offsetParent will skip this div! + if(d.isMoz && cs.position=="static"){ + var parent=curnode.parentNode; + while(parent!=curnode.offsetParent){ + var pcs=gcs(parent); + if(pcs.position=="static"){ + ret.x += px(curnode,pcs.borderLeftWidth); + ret.y += px(curnode,pcs.borderTopWidth); + } + parent=parent.parentNode; + } + } + curnode = curnode.offsetParent; + }while((curnode != dh) && curnode); + }else if(node.x && node.y){ + ret.x += isNaN(node.x) ? 0 : node.x; + ret.y += isNaN(node.y) ? 0 : node.y; + } + } + // account for document scrolling + // if offsetParent is used, ret value already includes scroll position + // so we may have to actually remove that value if !includeScroll + if(includeScroll){ + var scroll = d._docScroll(); + ret.x += scroll.x; + ret.y += scroll.y; + } + + return ret; // Object + } + + dojo.coords = function(/*DomNode|String*/node, /*Boolean?*/includeScroll){ + // summary: + // Deprecated: Use position() for border-box x/y/w/h + // or marginBox() for margin-box w/h/l/t. + // Returns an object representing a node's size and position. + // + // description: + // Returns an object that measures margin-box (w)idth/(h)eight + // and absolute position x/y of the border-box. Also returned + // is computed (l)eft and (t)op values in pixels from the + // node's offsetParent as returned from marginBox(). + // Return value will be in the form: + //| { l: 50, t: 200, w: 300: h: 150, x: 100, y: 300 } + // Does not act as a setter. If includeScroll is passed, the x and + // y params are affected as one would expect in dojo.position(). + var n = byId(node), s = gcs(n), mb = d._getMarginBox(n, s); + var abs = d.position(n, includeScroll); + mb.x = abs.x; + mb.y = abs.y; + return mb; + } + + // ============================= + // Element attribute Functions + // ============================= + + // dojo.attr() should conform to http://www.w3.org/TR/DOM-Level-2-Core/ + + var _propNames = { + // properties renamed to avoid clashes with reserved words + "class": "className", + "for": "htmlFor", + // properties written as camelCase + tabindex: "tabIndex", + readonly: "readOnly", + colspan: "colSpan", + frameborder: "frameBorder", + rowspan: "rowSpan", + valuetype: "valueType" + }, + _attrNames = { + // original attribute names + classname: "class", + htmlfor: "for", + // for IE + tabindex: "tabIndex", + readonly: "readOnly" + }, + _forcePropNames = { + innerHTML: 1, + className: 1, + htmlFor: d.isIE, + value: 1 + }; + + var _fixAttrName = function(/*String*/ name){ + return _attrNames[name.toLowerCase()] || name; + }; + + var _hasAttr = function(node, name){ + var attr = node.getAttributeNode && node.getAttributeNode(name); + return attr && attr.specified; // Boolean + }; + + // There is a difference in the presence of certain properties and their default values + // between browsers. For example, on IE "disabled" is present on all elements, + // but it is value is "false"; "tabIndex" of <div> returns 0 by default on IE, yet other browsers + // can return -1. + + dojo.hasAttr = function(/*DomNode|String*/node, /*String*/name){ + // summary: + // Returns true if the requested attribute is specified on the + // given element, and false otherwise. + // node: + // id or reference to the element to check + // name: + // the name of the attribute + // returns: + // true if the requested attribute is specified on the + // given element, and false otherwise + var lc = name.toLowerCase(); + return _forcePropNames[_propNames[lc] || name] || _hasAttr(byId(node), _attrNames[lc] || name); // Boolean + } + + var _evtHdlrMap = {}, _ctr = 0, + _attrId = dojo._scopeName + "attrid", + // the next dictionary lists elements with read-only innerHTML on IE + _roInnerHtml = {col: 1, colgroup: 1, + // frameset: 1, head: 1, html: 1, style: 1, + table: 1, tbody: 1, tfoot: 1, thead: 1, tr: 1, title: 1}; + + dojo.attr = function(/*DomNode|String*/node, /*String|Object*/name, /*String?*/value){ + // summary: + // Gets or sets an attribute on an HTML element. + // description: + // Handles normalized getting and setting of attributes on DOM + // Nodes. If 2 arguments are passed, and a the second argumnt is a + // string, acts as a getter. + // + // If a third argument is passed, or if the second argument is a + // map of attributes, acts as a setter. + // + // When passing functions as values, note that they will not be + // directly assigned to slots on the node, but rather the default + // behavior will be removed and the new behavior will be added + // using `dojo.connect()`, meaning that event handler properties + // will be normalized and that some caveats with regards to + // non-standard behaviors for onsubmit apply. Namely that you + // should cancel form submission using `dojo.stopEvent()` on the + // passed event object instead of returning a boolean value from + // the handler itself. + // node: + // id or reference to the element to get or set the attribute on + // name: + // the name of the attribute to get or set. + // value: + // The value to set for the attribute + // returns: + // when used as a getter, the value of the requested attribute + // or null if that attribute does not have a specified or + // default value; + // + // when used as a setter, the DOM node + // + // example: + // | // get the current value of the "foo" attribute on a node + // | dojo.attr(dojo.byId("nodeId"), "foo"); + // | // or we can just pass the id: + // | dojo.attr("nodeId", "foo"); + // + // example: + // | // use attr() to set the tab index + // | dojo.attr("nodeId", "tabIndex", 3); + // | + // + // example: + // Set multiple values at once, including event handlers: + // | dojo.attr("formId", { + // | "foo": "bar", + // | "tabIndex": -1, + // | "method": "POST", + // | "onsubmit": function(e){ + // | // stop submitting the form. Note that the IE behavior + // | // of returning true or false will have no effect here + // | // since our handler is connect()ed to the built-in + // | // onsubmit behavior and so we need to use + // | // dojo.stopEvent() to ensure that the submission + // | // doesn't proceed. + // | dojo.stopEvent(e); + // | + // | // submit the form with Ajax + // | dojo.xhrPost({ form: "formId" }); + // | } + // | }); + // + // example: + // Style is s special case: Only set with an object hash of styles + // | dojo.attr("someNode",{ + // | id:"bar", + // | style:{ + // | width:"200px", height:"100px", color:"#000" + // | } + // | }); + // + // example: + // Again, only set style as an object hash of styles: + // | var obj = { color:"#fff", backgroundColor:"#000" }; + // | dojo.attr("someNode", "style", obj); + // | + // | // though shorter to use `dojo.style()` in this case: + // | dojo.style("someNode", obj); + + node = byId(node); + var args = arguments.length, prop; + if(args == 2 && typeof name != "string"){ // inline'd type check + // the object form of setter: the 2nd argument is a dictionary + for(var x in name){ + d.attr(node, x, name[x]); + } + return node; // DomNode + } + var lc = name.toLowerCase(), + propName = _propNames[lc] || name, + forceProp = _forcePropNames[propName], + attrName = _attrNames[lc] || name; + if(args == 3){ + // setter + do{ + if(propName == "style" && typeof value != "string"){ // inline'd type check + // special case: setting a style + d.style(node, value); + break; + } + if(propName == "innerHTML"){ + // special case: assigning HTML + if(d.isIE && node.tagName.toLowerCase() in _roInnerHtml){ + d.empty(node); + node.appendChild(d._toDom(value, node.ownerDocument)); + }else{ + node[propName] = value; + } + break; + } + if(d.isFunction(value)){ + // special case: assigning an event handler + // clobber if we can + var attrId = d.attr(node, _attrId); + if(!attrId){ + attrId = _ctr++; + d.attr(node, _attrId, attrId); + } + if(!_evtHdlrMap[attrId]){ + _evtHdlrMap[attrId] = {}; + } + var h = _evtHdlrMap[attrId][propName]; + if(h){ + d.disconnect(h); + }else{ + try{ + delete node[propName]; + }catch(e){} + } + // ensure that event objects are normalized, etc. + _evtHdlrMap[attrId][propName] = d.connect(node, propName, value); + break; + } + if(forceProp || typeof value == "boolean"){ + // special case: forcing assignment to the property + // special case: setting boolean to a property instead of attribute + node[propName] = value; + break; + } + // node's attribute + node.setAttribute(attrName, value); + }while(false); + return node; // DomNode + } + // getter + // should we access this attribute via a property or + // via getAttribute()? + value = node[propName]; + if(forceProp && typeof value != "undefined"){ + // node's property + return value; // Anything + } + if(propName != "href" && (typeof value == "boolean" || d.isFunction(value))){ + // node's property + return value; // Anything + } + // node's attribute + // we need _hasAttr() here to guard against IE returning a default value + return _hasAttr(node, attrName) ? node.getAttribute(attrName) : null; // Anything + } + + dojo.removeAttr = function(/*DomNode|String*/ node, /*String*/ name){ + // summary: + // Removes an attribute from an HTML element. + // node: + // id or reference to the element to remove the attribute from + // name: + // the name of the attribute to remove + byId(node).removeAttribute(_fixAttrName(name)); + } + + dojo.getNodeProp = function(/*DomNode|String*/ node, /*String*/ name){ + // summary: + // Returns an effective value of a property or an attribute. + // node: + // id or reference to the element to remove the attribute from + // name: + // the name of the attribute + node = byId(node); + var lc = name.toLowerCase(), + propName = _propNames[lc] || name; + if((propName in node) && propName != "href"){ + // node's property + return node[propName]; // Anything + } + // node's attribute + var attrName = _attrNames[lc] || name; + return _hasAttr(node, attrName) ? node.getAttribute(attrName) : null; // Anything + } + + dojo.create = function(tag, attrs, refNode, pos){ + // summary: + // Create an element, allowing for optional attribute decoration + // and placement. + // + // description: + // A DOM Element creation function. A shorthand method for creating a node or + // a fragment, and allowing for a convenient optional attribute setting step, + // as well as an optional DOM placement reference. + //| + // Attributes are set by passing the optional object through `dojo.attr`. + // See `dojo.attr` for noted caveats and nuances, and API if applicable. + //| + // Placement is done via `dojo.place`, assuming the new node to be the action + // node, passing along the optional reference node and position. + // + // tag: String|DomNode + // A string of the element to create (eg: "div", "a", "p", "li", "script", "br"), + // or an existing DOM node to process. + // + // attrs: Object + // An object-hash of attributes to set on the newly created node. + // Can be null, if you don't want to set any attributes/styles. + // See: `dojo.attr` for a description of available attributes. + // + // refNode: String?|DomNode? + // Optional reference node. Used by `dojo.place` to place the newly created + // node somewhere in the dom relative to refNode. Can be a DomNode reference + // or String ID of a node. + // + // pos: String? + // Optional positional reference. Defaults to "last" by way of `dojo.place`, + // though can be set to "first","after","before","last", "replace" or "only" + // to further control the placement of the new node relative to the refNode. + // 'refNode' is required if a 'pos' is specified. + // + // returns: DomNode + // + // example: + // Create a DIV: + // | var n = dojo.create("div"); + // + // example: + // Create a DIV with content: + // | var n = dojo.create("div", { innerHTML:"<p>hi</p>" }); + // + // example: + // Place a new DIV in the BODY, with no attributes set + // | var n = dojo.create("div", null, dojo.body()); + // + // example: + // Create an UL, and populate it with LI's. Place the list as the first-child of a + // node with id="someId": + // | var ul = dojo.create("ul", null, "someId", "first"); + // | var items = ["one", "two", "three", "four"]; + // | dojo.forEach(items, function(data){ + // | dojo.create("li", { innerHTML: data }, ul); + // | }); + // + // example: + // Create an anchor, with an href. Place in BODY: + // | dojo.create("a", { href:"foo.html", title:"Goto FOO!" }, dojo.body()); + // + // example: + // Create a `dojo.NodeList()` from a new element (for syntatic sugar): + // | dojo.query(dojo.create('div')) + // | .addClass("newDiv") + // | .onclick(function(e){ console.log('clicked', e.target) }) + // | .place("#someNode"); // redundant, but cleaner. + + var doc = d.doc; + if(refNode){ + refNode = byId(refNode); + doc = refNode.ownerDocument; + } + if(typeof tag == "string"){ // inline'd type check + tag = doc.createElement(tag); + } + if(attrs){ d.attr(tag, attrs); } + if(refNode){ d.place(tag, refNode, pos); } + return tag; // DomNode + } + + /*===== + dojo.empty = function(node){ + // summary: + // safely removes all children of the node. + // node: DOMNode|String + // a reference to a DOM node or an id. + // example: + // Destroy node's children byId: + // | dojo.empty("someId"); + // + // example: + // Destroy all nodes' children in a list by reference: + // | dojo.query(".someNode").forEach(dojo.empty); + } + =====*/ + + d.empty = + d.isIE ? function(node){ + node = byId(node); + for(var c; c = node.lastChild;){ // intentional assignment + d.destroy(c); + } + } : + function(node){ + byId(node).innerHTML = ""; + }; + + /*===== + dojo._toDom = function(frag, doc){ + // summary: + // instantiates an HTML fragment returning the corresponding DOM. + // frag: String + // the HTML fragment + // doc: DocumentNode? + // optional document to use when creating DOM nodes, defaults to + // dojo.doc if not specified. + // returns: DocumentFragment + // + // example: + // Create a table row: + // | var tr = dojo._toDom("<tr><td>First!</td></tr>"); + } + =====*/ + + // support stuff for dojo._toDom + var tagWrap = { + option: ["select"], + tbody: ["table"], + thead: ["table"], + tfoot: ["table"], + tr: ["table", "tbody"], + td: ["table", "tbody", "tr"], + th: ["table", "thead", "tr"], + legend: ["fieldset"], + caption: ["table"], + colgroup: ["table"], + col: ["table", "colgroup"], + li: ["ul"] + }, + reTag = /<\s*([\w\:]+)/, + masterNode = {}, masterNum = 0, + masterName = "__" + d._scopeName + "ToDomId"; + + // generate start/end tag strings to use + // for the injection for each special tag wrap case. + for(var param in tagWrap){ + var tw = tagWrap[param]; + tw.pre = param == "option" ? '<select multiple="multiple">' : "<" + tw.join("><") + ">"; + tw.post = "</" + tw.reverse().join("></") + ">"; + // the last line is destructive: it reverses the array, + // but we don't care at this point + } + + d._toDom = function(frag, doc){ + // summary: + // converts HTML string into DOM nodes. + + doc = doc || d.doc; + var masterId = doc[masterName]; + if(!masterId){ + doc[masterName] = masterId = ++masterNum + ""; + masterNode[masterId] = doc.createElement("div"); + } + + // make sure the frag is a string. + frag += ""; + + // find the starting tag, and get node wrapper + var match = frag.match(reTag), + tag = match ? match[1].toLowerCase() : "", + master = masterNode[masterId], + wrap, i, fc, df; + if(match && tagWrap[tag]){ + wrap = tagWrap[tag]; + master.innerHTML = wrap.pre + frag + wrap.post; + for(i = wrap.length; i; --i){ + master = master.firstChild; + } + }else{ + master.innerHTML = frag; + } + + // one node shortcut => return the node itself + if(master.childNodes.length == 1){ + return master.removeChild(master.firstChild); // DOMNode + } + + // return multiple nodes as a document fragment + df = doc.createDocumentFragment(); + while(fc = master.firstChild){ // intentional assignment + df.appendChild(fc); + } + return df; // DOMNode + } + + // ============================= + // (CSS) Class Functions + // ============================= + var _className = "className"; + + dojo.hasClass = function(/*DomNode|String*/node, /*String*/classStr){ + // summary: + // Returns whether or not the specified classes are a portion of the + // class list currently applied to the node. + // + // node: + // String ID or DomNode reference to check the class for. + // + // classStr: + // A string class name to look for. + // + // example: + // Do something if a node with id="someNode" has class="aSillyClassName" present + // | if(dojo.hasClass("someNode","aSillyClassName")){ ... } + + return ((" "+ byId(node)[_className] +" ").indexOf(" " + classStr + " ") >= 0); // Boolean + }; + + var spaces = /\s+/, a1 = [""], + str2array = function(s){ + if(typeof s == "string" || s instanceof String){ + if(s.indexOf(" ") < 0){ + a1[0] = s; + return a1; + }else{ + return s.split(spaces); + } + } + // assumed to be an array + return s || ""; + }; + + dojo.addClass = function(/*DomNode|String*/node, /*String|Array*/classStr){ + // summary: + // Adds the specified classes to the end of the class list on the + // passed node. Will not re-apply duplicate classes. + // + // node: + // String ID or DomNode reference to add a class string too + // + // classStr: + // A String class name to add, or several space-separated class names, + // or an array of class names. + // + // example: + // Add a class to some node: + // | dojo.addClass("someNode", "anewClass"); + // + // example: + // Add two classes at once: + // | dojo.addClass("someNode", "firstClass secondClass"); + // + // example: + // Add two classes at once (using array): + // | dojo.addClass("someNode", ["firstClass", "secondClass"]); + // + // example: + // Available in `dojo.NodeList` for multiple additions + // | dojo.query("ul > li").addClass("firstLevel"); + + node = byId(node); + classStr = str2array(classStr); + var cls = node[_className], oldLen; + cls = cls ? " " + cls + " " : " "; + oldLen = cls.length; + for(var i = 0, len = classStr.length, c; i < len; ++i){ + c = classStr[i]; + if(c && cls.indexOf(" " + c + " ") < 0){ + cls += c + " "; + } + } + if(oldLen < cls.length){ + node[_className] = cls.substr(1, cls.length - 2); + } + }; + + dojo.removeClass = function(/*DomNode|String*/node, /*String|Array?*/classStr){ + // summary: + // Removes the specified classes from node. No `dojo.hasClass` + // check is required. + // + // node: + // String ID or DomNode reference to remove the class from. + // + // classStr: + // An optional String class name to remove, or several space-separated + // class names, or an array of class names. If omitted, all class names + // will be deleted. + // + // example: + // Remove a class from some node: + // | dojo.removeClass("someNode", "firstClass"); + // + // example: + // Remove two classes from some node: + // | dojo.removeClass("someNode", "firstClass secondClass"); + // + // example: + // Remove two classes from some node (using array): + // | dojo.removeClass("someNode", ["firstClass", "secondClass"]); + // + // example: + // Remove all classes from some node: + // | dojo.removeClass("someNode"); + // + // example: + // Available in `dojo.NodeList()` for multiple removal + // | dojo.query(".foo").removeClass("foo"); + + node = byId(node); + var cls; + if(classStr !== undefined){ + classStr = str2array(classStr); + cls = " " + node[_className] + " "; + for(var i = 0, len = classStr.length; i < len; ++i){ + cls = cls.replace(" " + classStr[i] + " ", " "); + } + cls = d.trim(cls); + }else{ + cls = ""; + } + if(node[_className] != cls){ node[_className] = cls; } + }; + + dojo.toggleClass = function(/*DomNode|String*/node, /*String|Array*/classStr, /*Boolean?*/condition){ + // summary: + // Adds a class to node if not present, or removes if present. + // Pass a boolean condition if you want to explicitly add or remove. + // condition: + // If passed, true means to add the class, false means to remove. + // + // example: + // | dojo.toggleClass("someNode", "hovered"); + // + // example: + // Forcefully add a class + // | dojo.toggleClass("someNode", "hovered", true); + // + // example: + // Available in `dojo.NodeList()` for multiple toggles + // | dojo.query(".toggleMe").toggleClass("toggleMe"); + + if(condition === undefined){ + condition = !d.hasClass(node, classStr); + } + d[condition ? "addClass" : "removeClass"](node, classStr); + }; + })(); + } diff --git a/lib/dojo/_base/json.js b/lib/dojo/_base/json.js index 7d8c5af65..4d50400c9 100644 --- a/lib/dojo/_base/json.js +++ b/lib/dojo/_base/json.js @@ -5,77 +5,150 @@ */ -if(!dojo._hasResource["dojo._base.json"]){ -dojo._hasResource["dojo._base.json"]=true; +if(!dojo._hasResource["dojo._base.json"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo._base.json"] = true; dojo.provide("dojo._base.json"); -dojo.fromJson=function(_1){ -return eval("("+_1+")"); -}; -dojo._escapeString=function(_2){ -return ("\""+_2.replace(/(["\\])/g,"\\$1")+"\"").replace(/[\f]/g,"\\f").replace(/[\b]/g,"\\b").replace(/[\n]/g,"\\n").replace(/[\t]/g,"\\t").replace(/[\r]/g,"\\r"); -}; -dojo.toJsonIndentStr="\t"; -dojo.toJson=function(it,_3,_4){ -if(it===undefined){ -return "undefined"; -} -var _5=typeof it; -if(_5=="number"||_5=="boolean"){ -return it+""; -} -if(it===null){ -return "null"; -} -if(dojo.isString(it)){ -return dojo._escapeString(it); -} -var _6=arguments.callee; -var _7; -_4=_4||""; -var _8=_3?_4+dojo.toJsonIndentStr:""; -var tf=it.__json__||it.json; -if(dojo.isFunction(tf)){ -_7=tf.call(it); -if(it!==_7){ -return _6(_7,_3,_8); -} -} -if(it.nodeType&&it.cloneNode){ -throw new Error("Can't serialize DOM nodes"); -} -var _9=_3?" ":""; -var _a=_3?"\n":""; -if(dojo.isArray(it)){ -var _b=dojo.map(it,function(_c){ -var _d=_6(_c,_3,_8); -if(typeof _d!="string"){ -_d="undefined"; -} -return _a+_8+_d; -}); -return "["+_b.join(","+_9)+_a+_4+"]"; -} -if(_5=="function"){ -return null; -} -var _e=[],_f; -for(_f in it){ -var _10,val; -if(typeof _f=="number"){ -_10="\""+_f+"\""; -}else{ -if(typeof _f=="string"){ -_10=dojo._escapeString(_f); -}else{ -continue; -} + +dojo.fromJson = function(/*String*/ json){ + // summary: + // Parses a [JSON](http://json.org) string to return a JavaScript object. + // description: + // Throws for invalid JSON strings, but it does not use a strict JSON parser. It + // delegates to eval(). The content passed to this method must therefore come + // from a trusted source. + // json: + // a string literal of a JSON item, for instance: + // `'{ "foo": [ "bar", 1, { "baz": "thud" } ] }'` + + return eval("(" + json + ")"); // Object } -val=_6(it[_f],_3,_8); -if(typeof val!="string"){ -continue; + +dojo._escapeString = function(/*String*/str){ + //summary: + // Adds escape sequences for non-visual characters, double quote and + // backslash and surrounds with double quotes to form a valid string + // literal. + return ('"' + str.replace(/(["\\])/g, '\\$1') + '"'). + replace(/[\f]/g, "\\f").replace(/[\b]/g, "\\b").replace(/[\n]/g, "\\n"). + replace(/[\t]/g, "\\t").replace(/[\r]/g, "\\r"); // string } -_e.push(_a+_8+_10+":"+_9+val); + +dojo.toJsonIndentStr = "\t"; +dojo.toJson = function(/*Object*/ it, /*Boolean?*/ prettyPrint, /*String?*/ _indentStr){ + // summary: + // Returns a [JSON](http://json.org) serialization of an object. + // description: + // Returns a [JSON](http://json.org) serialization of an object. + // Note that this doesn't check for infinite recursion, so don't do that! + // it: + // an object to be serialized. Objects may define their own + // serialization via a special "__json__" or "json" function + // property. If a specialized serializer has been defined, it will + // be used as a fallback. + // prettyPrint: + // if true, we indent objects and arrays to make the output prettier. + // The variable `dojo.toJsonIndentStr` is used as the indent string -- + // to use something other than the default (tab), change that variable + // before calling dojo.toJson(). + // _indentStr: + // private variable for recursive calls when pretty printing, do not use. + // example: + // simple serialization of a trivial object + // | var jsonStr = dojo.toJson({ howdy: "stranger!", isStrange: true }); + // | doh.is('{"howdy":"stranger!","isStrange":true}', jsonStr); + // example: + // a custom serializer for an objects of a particular class: + // | dojo.declare("Furby", null, { + // | furbies: "are strange", + // | furbyCount: 10, + // | __json__: function(){ + // | }, + // | }); + + if(it === undefined){ + return "undefined"; + } + var objtype = typeof it; + if(objtype == "number" || objtype == "boolean"){ + return it + ""; + } + if(it === null){ + return "null"; + } + if(dojo.isString(it)){ + return dojo._escapeString(it); + } + // recurse + var recurse = arguments.callee; + // short-circuit for objects that support "json" serialization + // if they return "self" then just pass-through... + var newObj; + _indentStr = _indentStr || ""; + var nextIndent = prettyPrint ? _indentStr + dojo.toJsonIndentStr : ""; + var tf = it.__json__||it.json; + if(dojo.isFunction(tf)){ + newObj = tf.call(it); + if(it !== newObj){ + return recurse(newObj, prettyPrint, nextIndent); + } + } + if(it.nodeType && it.cloneNode){ // isNode + // we can't seriailize DOM nodes as regular objects because they have cycles + // DOM nodes could be serialized with something like outerHTML, but + // that can be provided by users in the form of .json or .__json__ function. + throw new Error("Can't serialize DOM nodes"); + } + + var sep = prettyPrint ? " " : ""; + var newLine = prettyPrint ? "\n" : ""; + + // array + if(dojo.isArray(it)){ + var res = dojo.map(it, function(obj){ + var val = recurse(obj, prettyPrint, nextIndent); + if(typeof val != "string"){ + val = "undefined"; + } + return newLine + nextIndent + val; + }); + return "[" + res.join("," + sep) + newLine + _indentStr + "]"; + } + /* + // look in the registry + try { + window.o = it; + newObj = dojo.json.jsonRegistry.match(it); + return recurse(newObj, prettyPrint, nextIndent); + }catch(e){ + // console.log(e); + } + // it's a function with no adapter, skip it + */ + if(objtype == "function"){ + return null; // null + } + // generic object code path + var output = [], key; + for(key in it){ + var keyStr, val; + if(typeof key == "number"){ + keyStr = '"' + key + '"'; + }else if(typeof key == "string"){ + keyStr = dojo._escapeString(key); + }else{ + // skip non-string or number keys + continue; + } + val = recurse(it[key], prettyPrint, nextIndent); + if(typeof val != "string"){ + // skip non-serializable values + continue; + } + // FIXME: use += on Moz!! + // MOW NOTE: using += is a pain because you have to account for the dangling comma... + output.push(newLine + nextIndent + keyStr + ":" + sep + val); + } + return "{" + output.join("," + sep) + newLine + _indentStr + "}"; // String } -return "{"+_e.join(","+_9)+_a+_4+"}"; -}; + } diff --git a/lib/dojo/_base/lang.js b/lib/dojo/_base/lang.js index e7721a352..0e9c7c2f9 100644 --- a/lib/dojo/_base/lang.js +++ b/lib/dojo/_base/lang.js @@ -5,144 +5,388 @@ */ -if(!dojo._hasResource["dojo._base.lang"]){ -dojo._hasResource["dojo._base.lang"]=true; +if(!dojo._hasResource["dojo._base.lang"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo._base.lang"] = true; dojo.provide("dojo._base.lang"); + (function(){ -var d=dojo,_1=Object.prototype.toString; -dojo.isString=function(it){ -return (typeof it=="string"||it instanceof String); -}; -dojo.isArray=function(it){ -return it&&(it instanceof Array||typeof it=="array"); -}; -dojo.isFunction=function(it){ -return _1.call(it)==="[object Function]"; -}; -dojo.isObject=function(it){ -return it!==undefined&&(it===null||typeof it=="object"||d.isArray(it)||d.isFunction(it)); -}; -dojo.isArrayLike=function(it){ -return it&&it!==undefined&&!d.isString(it)&&!d.isFunction(it)&&!(it.tagName&&it.tagName.toLowerCase()=="form")&&(d.isArray(it)||isFinite(it.length)); -}; -dojo.isAlien=function(it){ -return it&&!d.isFunction(it)&&/\{\s*\[native code\]\s*\}/.test(String(it)); -}; -dojo.extend=function(_2,_3){ -for(var i=1,l=arguments.length;i<l;i++){ -d._mixin(_2.prototype,arguments[i]); -} -return _2; -}; -dojo._hitchArgs=function(_4,_5){ -var _6=d._toArray(arguments,2); -var _7=d.isString(_5); -return function(){ -var _8=d._toArray(arguments); -var f=_7?(_4||d.global)[_5]:_5; -return f&&f.apply(_4||this,_6.concat(_8)); -}; -}; -dojo.hitch=function(_9,_a){ -if(arguments.length>2){ -return d._hitchArgs.apply(d,arguments); -} -if(!_a){ -_a=_9; -_9=null; -} -if(d.isString(_a)){ -_9=_9||d.global; -if(!_9[_a]){ -throw (["dojo.hitch: scope[\"",_a,"\"] is null (scope=\"",_9,"\")"].join("")); -} -return function(){ -return _9[_a].apply(_9,arguments||[]); -}; -} -return !_9?_a:function(){ -return _a.apply(_9,arguments||[]); -}; -}; -dojo.delegate=dojo._delegate=(function(){ -function _b(){ -}; -return function(_c,_d){ -_b.prototype=_c; -var _e=new _b(); -_b.prototype=null; -if(_d){ -d._mixin(_e,_d); -} -return _e; -}; -})(); -var _f=function(obj,_10,_11){ -return (_11||[]).concat(Array.prototype.slice.call(obj,_10||0)); -}; -var _12=function(obj,_13,_14){ -var arr=_14||[]; -for(var x=_13||0;x<obj.length;x++){ -arr.push(obj[x]); -} -return arr; -}; -dojo._toArray=d.isIE?function(obj){ -return ((obj.item)?_12:_f).apply(this,arguments); -}:_f; -dojo.partial=function(_15){ -var arr=[null]; -return d.hitch.apply(d,arr.concat(d._toArray(arguments))); -}; -var _16=d._extraNames,_17=_16.length,_18={}; -dojo.clone=function(o){ -if(!o||typeof o!="object"||d.isFunction(o)){ -return o; -} -if(o.nodeType&&"cloneNode" in o){ -return o.cloneNode(true); -} -if(o instanceof Date){ -return new Date(o.getTime()); -} -var r,i,l,s,_19; -if(d.isArray(o)){ -r=[]; -for(i=0,l=o.length;i<l;++i){ -if(i in o){ -r.push(d.clone(o[i])); -} -} -}else{ -r=o.constructor?new o.constructor():{}; -} -for(_19 in o){ -s=o[_19]; -if(!(_19 in r)||(r[_19]!==s&&(!(_19 in _18)||_18[_19]!==s))){ -r[_19]=d.clone(s); -} -} -if(_17){ -for(i=0;i<_17;++i){ -_19=_16[i]; -s=o[_19]; -if(!(_19 in r)||(r[_19]!==s&&(!(_19 in _18)||_18[_19]!==s))){ -r[_19]=s; -} -} -} -return r; -}; -dojo.trim=String.prototype.trim?function(str){ -return str.trim(); -}:function(str){ -return str.replace(/^\s\s*/,"").replace(/\s\s*$/,""); -}; -var _1a=/\{([^\}]+)\}/g; -dojo.replace=function(_1b,map,_1c){ -return _1b.replace(_1c||_1a,d.isFunction(map)?map:function(_1d,k){ -return d.getObject(k,false,map); -}); -}; + var d = dojo, opts = Object.prototype.toString; + + // Crockford (ish) functions + + dojo.isString = function(/*anything*/ it){ + // summary: + // Return true if it is a String + return (typeof it == "string" || it instanceof String); // Boolean + } + + dojo.isArray = function(/*anything*/ it){ + // summary: + // Return true if it is an Array. + // Does not work on Arrays created in other windows. + return it && (it instanceof Array || typeof it == "array"); // Boolean + } + + dojo.isFunction = function(/*anything*/ it){ + // summary: + // Return true if it is a Function + return opts.call(it) === "[object Function]"; + }; + + dojo.isObject = function(/*anything*/ it){ + // summary: + // Returns true if it is a JavaScript object (or an Array, a Function + // or null) + return it !== undefined && + (it === null || typeof it == "object" || d.isArray(it) || d.isFunction(it)); // Boolean + } + + dojo.isArrayLike = function(/*anything*/ it){ + // summary: + // similar to dojo.isArray() but more permissive + // description: + // Doesn't strongly test for "arrayness". Instead, settles for "isn't + // a string or number and has a length property". Arguments objects + // and DOM collections will return true when passed to + // dojo.isArrayLike(), but will return false when passed to + // dojo.isArray(). + // returns: + // If it walks like a duck and quacks like a duck, return `true` + return it && it !== undefined && // Boolean + // keep out built-in constructors (Number, String, ...) which have length + // properties + !d.isString(it) && !d.isFunction(it) && + !(it.tagName && it.tagName.toLowerCase() == 'form') && + (d.isArray(it) || isFinite(it.length)); + } + + dojo.isAlien = function(/*anything*/ it){ + // summary: + // Returns true if it is a built-in function or some other kind of + // oddball that *should* report as a function but doesn't + return it && !d.isFunction(it) && /\{\s*\[native code\]\s*\}/.test(String(it)); // Boolean + } + + dojo.extend = function(/*Object*/ constructor, /*Object...*/ props){ + // summary: + // Adds all properties and methods of props to constructor's + // prototype, making them available to all instances created with + // constructor. + for(var i=1, l=arguments.length; i<l; i++){ + d._mixin(constructor.prototype, arguments[i]); + } + return constructor; // Object + } + + dojo._hitchArgs = function(scope, method /*,...*/){ + var pre = d._toArray(arguments, 2); + var named = d.isString(method); + return function(){ + // arrayify arguments + var args = d._toArray(arguments); + // locate our method + var f = named ? (scope||d.global)[method] : method; + // invoke with collected args + return f && f.apply(scope || this, pre.concat(args)); // mixed + } // Function + } + + dojo.hitch = function(/*Object*/scope, /*Function|String*/method /*,...*/){ + // summary: + // Returns a function that will only ever execute in the a given scope. + // This allows for easy use of object member functions + // in callbacks and other places in which the "this" keyword may + // otherwise not reference the expected scope. + // Any number of default positional arguments may be passed as parameters + // beyond "method". + // Each of these values will be used to "placehold" (similar to curry) + // for the hitched function. + // scope: + // The scope to use when method executes. If method is a string, + // scope is also the object containing method. + // method: + // A function to be hitched to scope, or the name of the method in + // scope to be hitched. + // example: + // | dojo.hitch(foo, "bar")(); + // runs foo.bar() in the scope of foo + // example: + // | dojo.hitch(foo, myFunction); + // returns a function that runs myFunction in the scope of foo + // example: + // Expansion on the default positional arguments passed along from + // hitch. Passed args are mixed first, additional args after. + // | var foo = { bar: function(a, b, c){ console.log(a, b, c); } }; + // | var fn = dojo.hitch(foo, "bar", 1, 2); + // | fn(3); // logs "1, 2, 3" + // example: + // | var foo = { bar: 2 }; + // | dojo.hitch(foo, function(){ this.bar = 10; })(); + // execute an anonymous function in scope of foo + + if(arguments.length > 2){ + return d._hitchArgs.apply(d, arguments); // Function + } + if(!method){ + method = scope; + scope = null; + } + if(d.isString(method)){ + scope = scope || d.global; + if(!scope[method]){ throw(['dojo.hitch: scope["', method, '"] is null (scope="', scope, '")'].join('')); } + return function(){ return scope[method].apply(scope, arguments || []); }; // Function + } + return !scope ? method : function(){ return method.apply(scope, arguments || []); }; // Function + } + + /*===== + dojo.delegate = function(obj, props){ + // summary: + // Returns a new object which "looks" to obj for properties which it + // does not have a value for. Optionally takes a bag of properties to + // seed the returned object with initially. + // description: + // This is a small implementaton of the Boodman/Crockford delegation + // pattern in JavaScript. An intermediate object constructor mediates + // the prototype chain for the returned object, using it to delegate + // down to obj for property lookup when object-local lookup fails. + // This can be thought of similarly to ES4's "wrap", save that it does + // not act on types but rather on pure objects. + // obj: + // The object to delegate to for properties not found directly on the + // return object or in props. + // props: + // an object containing properties to assign to the returned object + // returns: + // an Object of anonymous type + // example: + // | var foo = { bar: "baz" }; + // | var thinger = dojo.delegate(foo, { thud: "xyzzy"}); + // | thinger.bar == "baz"; // delegated to foo + // | foo.thud == undefined; // by definition + // | thinger.thud == "xyzzy"; // mixed in from props + // | foo.bar = "thonk"; + // | thinger.bar == "thonk"; // still delegated to foo's bar + } + =====*/ + + dojo.delegate = dojo._delegate = (function(){ + // boodman/crockford delegation w/ cornford optimization + function TMP(){} + return function(obj, props){ + TMP.prototype = obj; + var tmp = new TMP(); + TMP.prototype = null; + if(props){ + d._mixin(tmp, props); + } + return tmp; // Object + } + })(); + + /*===== + dojo._toArray = function(obj, offset, startWith){ + // summary: + // Converts an array-like object (i.e. arguments, DOMCollection) to an + // array. Returns a new Array with the elements of obj. + // obj: Object + // the object to "arrayify". We expect the object to have, at a + // minimum, a length property which corresponds to integer-indexed + // properties. + // offset: Number? + // the location in obj to start iterating from. Defaults to 0. + // Optional. + // startWith: Array? + // An array to pack with the properties of obj. If provided, + // properties in obj are appended at the end of startWith and + // startWith is the returned array. + } + =====*/ + + var efficient = function(obj, offset, startWith){ + return (startWith||[]).concat(Array.prototype.slice.call(obj, offset||0)); + }; + + var slow = function(obj, offset, startWith){ + var arr = startWith||[]; + for(var x = offset || 0; x < obj.length; x++){ + arr.push(obj[x]); + } + return arr; + }; + + dojo._toArray = + d.isIE ? function(obj){ + return ((obj.item) ? slow : efficient).apply(this, arguments); + } : + efficient; + + dojo.partial = function(/*Function|String*/method /*, ...*/){ + // summary: + // similar to hitch() except that the scope object is left to be + // whatever the execution context eventually becomes. + // description: + // Calling dojo.partial is the functional equivalent of calling: + // | dojo.hitch(null, funcName, ...); + var arr = [ null ]; + return d.hitch.apply(d, arr.concat(d._toArray(arguments))); // Function + } + + var extraNames = d._extraNames, extraLen = extraNames.length, empty = {}; + + dojo.clone = function(/*anything*/ o){ + // summary: + // Clones objects (including DOM nodes) and all children. + // Warning: do not clone cyclic structures. + if(!o || typeof o != "object" || d.isFunction(o)){ + // null, undefined, any non-object, or function + return o; // anything + } + if(o.nodeType && "cloneNode" in o){ + // DOM Node + return o.cloneNode(true); // Node + } + if(o instanceof Date){ + // Date + return new Date(o.getTime()); // Date + } + var r, i, l, s, name; + if(d.isArray(o)){ + // array + r = []; + for(i = 0, l = o.length; i < l; ++i){ + if(i in o){ + r.push(d.clone(o[i])); + } + } +// we don't clone functions for performance reasons +// }else if(d.isFunction(o)){ +// // function +// r = function(){ return o.apply(this, arguments); }; + }else{ + // generic objects + r = o.constructor ? new o.constructor() : {}; + } + for(name in o){ + // the "tobj" condition avoid copying properties in "source" + // inherited from Object.prototype. For example, if target has a custom + // toString() method, don't overwrite it with the toString() method + // that source inherited from Object.prototype + s = o[name]; + if(!(name in r) || (r[name] !== s && (!(name in empty) || empty[name] !== s))){ + r[name] = d.clone(s); + } + } + // IE doesn't recognize some custom functions in for..in + if(extraLen){ + for(i = 0; i < extraLen; ++i){ + name = extraNames[i]; + s = o[name]; + if(!(name in r) || (r[name] !== s && (!(name in empty) || empty[name] !== s))){ + r[name] = s; // functions only, we don't clone them + } + } + } + return r; // Object + } + + /*===== + dojo.trim = function(str){ + // summary: + // Trims whitespace from both sides of the string + // str: String + // String to be trimmed + // returns: String + // Returns the trimmed string + // description: + // This version of trim() was selected for inclusion into the base due + // to its compact size and relatively good performance + // (see [Steven Levithan's blog](http://blog.stevenlevithan.com/archives/faster-trim-javascript) + // Uses String.prototype.trim instead, if available. + // The fastest but longest version of this function is located at + // dojo.string.trim() + return ""; // String + } + =====*/ + + dojo.trim = String.prototype.trim ? + function(str){ return str.trim(); } : + function(str){ return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); }; + + /*===== + dojo.replace = function(tmpl, map, pattern){ + // summary: + // Performs parameterized substitutions on a string. Throws an + // exception if any parameter is unmatched. + // tmpl: String + // String to be used as a template. + // map: Object|Function + // If an object, it is used as a dictionary to look up substitutions. + // If a function, it is called for every substitution with following + // parameters: a whole match, a name, an offset, and the whole template + // string (see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/replace + // for more details). + // pattern: RegEx? + // Optional regular expression objects that overrides the default pattern. + // Must be global and match one item. The default is: /\{([^\}]+)\}/g, + // which matches patterns like that: "{xxx}", where "xxx" is any sequence + // of characters, which doesn't include "}". + // returns: String + // Returns the substituted string. + // example: + // | // uses a dictionary for substitutions: + // | dojo.replace("Hello, {name.first} {name.last} AKA {nick}!", + // | { + // | nick: "Bob", + // | name: { + // | first: "Robert", + // | middle: "X", + // | last: "Cringely" + // | } + // | }); + // | // returns: Hello, Robert Cringely AKA Bob! + // example: + // | // uses an array for substitutions: + // | dojo.replace("Hello, {0} {2}!", + // | ["Robert", "X", "Cringely"]); + // | // returns: Hello, Robert Cringely! + // example: + // | // uses a function for substitutions: + // | function sum(a){ + // | var t = 0; + // | dojo.forEach(a, function(x){ t += x; }); + // | return t; + // | } + // | dojo.replace( + // | "{count} payments averaging {avg} USD per payment.", + // | dojo.hitch( + // | { payments: [11, 16, 12] }, + // | function(_, key){ + // | switch(key){ + // | case "count": return this.payments.length; + // | case "min": return Math.min.apply(Math, this.payments); + // | case "max": return Math.max.apply(Math, this.payments); + // | case "sum": return sum(this.payments); + // | case "avg": return sum(this.payments) / this.payments.length; + // | } + // | } + // | ) + // | ); + // | // prints: 3 payments averaging 13 USD per payment. + // example: + // | // uses an alternative PHP-like pattern for substitutions: + // | dojo.replace("Hello, ${0} ${2}!", + // | ["Robert", "X", "Cringely"], /\$\{([^\}]+)\}/g); + // | // returns: Hello, Robert Cringely! + return ""; // String + } + =====*/ + + var _pattern = /\{([^\}]+)\}/g; + dojo.replace = function(tmpl, map, pattern){ + return tmpl.replace(pattern || _pattern, d.isFunction(map) ? + map : function(_, k){ return d.getObject(k, false, map); }); + }; })(); + } diff --git a/lib/dojo/_base/query-sizzle.js b/lib/dojo/_base/query-sizzle.js index ac26c1816..5d160ec55 100644 --- a/lib/dojo/_base/query-sizzle.js +++ b/lib/dojo/_base/query-sizzle.js @@ -5,624 +5,865 @@ */ -if(!dojo._hasResource["dojo._base.query"]){ -dojo._hasResource["dojo._base.query"]=true; -if(typeof dojo!="undefined"){ -dojo.provide("dojo._base.query"); -dojo.require("dojo._base.NodeList"); -dojo.query=function(_1,_2,_3){ -_3=_3||dojo.NodeList; -if(!_1){ -return new _3(); -} -if(_1.constructor==_3){ -return _1; -} -if(!dojo.isString(_1)){ -return new _3(_1); -} -if(dojo.isString(_2)){ -_2=dojo.byId(_2); -if(!_2){ -return new _3(); -} -} -return dojo.Sizzle(_1,_2,new _3()); -}; -dojo._filterQueryResult=function(_4,_5){ -return dojo.Sizzle.filter(_5,_4); -}; -} -(function(ns){ -var _6=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|[^[\]]+)+\]|\\.|[^ >+~,(\[]+)+|[>+~])(\s*,\s*)?/g,_7=0,_8=Object.prototype.toString; -var _9=function(_a,_b,_c,_d){ -_c=_c||[]; -_b=_b||document; -if(_b.nodeType!==1&&_b.nodeType!==9){ -return []; -} -if(!_a||typeof _a!=="string"){ -return _c; -} -var _e=[],m,_f,_10,_11,_12,_13,_14=true; -_6.lastIndex=0; -while((m=_6.exec(_a))!==null){ -_e.push(m[1]); -if(m[2]){ -_13=RegExp.rightContext; -break; -} -} -if(_e.length>1&&_15.match.POS.exec(_a)){ -if(_e.length===2&&_15.relative[_e[0]]){ -var _16="",_17; -while((_17=_15.match.POS.exec(_a))){ -_16+=_17[0]; -_a=_a.replace(_15.match.POS,""); -} -_f=_9.filter(_16,_9(_a,_b)); -}else{ -_f=_15.relative[_e[0]]?[_b]:_9(_e.shift(),_b); -while(_e.length){ -var _18=[]; -_a=_e.shift(); -if(_15.relative[_a]){ -_a+=_e.shift(); -} -for(var i=0,l=_f.length;i<l;i++){ -_9(_a,_f[i],_18); -} -_f=_18; -} -} -}else{ -var ret=_d?{expr:_e.pop(),set:_19(_d)}:_9.find(_e.pop(),_e.length===1&&_b.parentNode?_b.parentNode:_b); -_f=_9.filter(ret.expr,ret.set); -if(_e.length>0){ -_10=_19(_f); -}else{ -_14=false; -} -while(_e.length){ -var cur=_e.pop(),pop=cur; -if(!_15.relative[cur]){ -cur=""; -}else{ -pop=_e.pop(); -} -if(pop==null){ -pop=_b; -} -_15.relative[cur](_10,pop); -} -} -if(!_10){ -_10=_f; -} -if(!_10){ -throw "Syntax error, unrecognized expression: "+(cur||_a); -} -if(_8.call(_10)==="[object Array]"){ -if(!_14){ -_c.push.apply(_c,_10); -}else{ -if(_b.nodeType===1){ -for(var i=0;_10[i]!=null;i++){ -if(_10[i]&&(_10[i]===true||_10[i].nodeType===1&&_1a(_b,_10[i]))){ -_c.push(_f[i]); -} -} -}else{ -for(var i=0;_10[i]!=null;i++){ -if(_10[i]&&_10[i].nodeType===1){ -_c.push(_f[i]); -} -} -} -} -}else{ -_19(_10,_c); -} -if(_13){ -_9(_13,_b,_c,_d); +if(!dojo._hasResource["dojo._base.query"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo._base.query"] = true; +/*! + * Sizzle CSS Selector Engine - v0.9 + * Copyright 2009, John Resig + * Redistributed with the Dojo Toolkit under the terms of the New BSD license. + * More information: http://sizzlejs.com/ + * + * This version from github, dated 1/23/2009, commit: e374a73bbffc12ec3b5f252e7f76e593c508dfa5 + * Modified for dojo loader, and to fit into dojo namespace. This was done by passing + * dojo object to anonymous function, then assigning Sizzle to dojo.Sizzle instead of window.Sizzle. + * Then an alias for dojo.query and dojo._filterQueryResult(). dojo.psuedos is not mapped. + * Finally, dojo.provide/require added. + */ + +//This file gets copied to dojo/_base/query.js, so set the provide accordingly. +if(typeof dojo != "undefined"){ + dojo.provide("dojo._base.query"); + dojo.require("dojo._base.NodeList"); + + //Start Dojo mappings. + dojo.query = function(/*String*/ query, /*String|DOMNode?*/ root, /*Function?*/listCtor){ + listCtor = listCtor || dojo.NodeList; + + if(!query){ + return new listCtor(); + } + + if(query.constructor == listCtor){ + return query; + } + if(!dojo.isString(query)){ + return new listCtor(query); // dojo.NodeList + } + if(dojo.isString(root)){ + root = dojo.byId(root); + if(!root){ return new listCtor(); } + } + + return dojo.Sizzle(query, root, new listCtor()); + } + + dojo._filterQueryResult = function(nodeList, simpleFilter){ + return dojo.Sizzle.filter(simpleFilter, nodeList); + } } -return _c; + +//Main Sizzle code follows... +//ns argument, added for dojo, used at the end of the file. +;(function(ns){ + +var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|[^[\]]+)+\]|\\.|[^ >+~,(\[]+)+|[>+~])(\s*,\s*)?/g, + done = 0, + toString = Object.prototype.toString; + +var Sizzle = function(selector, context, results, seed) { + results = results || []; + context = context || document; + + if ( context.nodeType !== 1 && context.nodeType !== 9 ) + return []; + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + var parts = [], m, set, checkSet, check, mode, extra, prune = true; + + // Reset the position of the chunker regexp (start from head) + chunker.lastIndex = 0; + + while ( (m = chunker.exec(selector)) !== null ) { + parts.push( m[1] ); + + if ( m[2] ) { + extra = RegExp.rightContext; + break; + } + } + + if ( parts.length > 1 && Expr.match.POS.exec( selector ) ) { + if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { + var later = "", match; + + // Position selectors must be done after the filter + while ( (match = Expr.match.POS.exec( selector )) ) { + later += match[0]; + selector = selector.replace( Expr.match.POS, "" ); + } + + set = Sizzle.filter( later, Sizzle( selector, context ) ); + } else { + set = Expr.relative[ parts[0] ] ? + [ context ] : + Sizzle( parts.shift(), context ); + + while ( parts.length ) { + var tmpSet = []; + + selector = parts.shift(); + if ( Expr.relative[ selector ] ) + selector += parts.shift(); + + for ( var i = 0, l = set.length; i < l; i++ ) { + Sizzle( selector, set[i], tmpSet ); + } + + set = tmpSet; + } + } + } else { + var ret = seed ? + { expr: parts.pop(), set: makeArray(seed) } : + Sizzle.find( parts.pop(), parts.length === 1 && context.parentNode ? context.parentNode : context ); + set = Sizzle.filter( ret.expr, ret.set ); + + if ( parts.length > 0 ) { + checkSet = makeArray(set); + } else { + prune = false; + } + + while ( parts.length ) { + var cur = parts.pop(), pop = cur; + + if ( !Expr.relative[ cur ] ) { + cur = ""; + } else { + pop = parts.pop(); + } + + if ( pop == null ) { + pop = context; + } + + Expr.relative[ cur ]( checkSet, pop ); + } + } + + if ( !checkSet ) { + checkSet = set; + } + + if ( !checkSet ) { + throw "Syntax error, unrecognized expression: " + (cur || selector); + } + + if ( toString.call(checkSet) === "[object Array]" ) { + if ( !prune ) { + results.push.apply( results, checkSet ); + } else if ( context.nodeType === 1 ) { + for ( var i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) { + results.push( set[i] ); + } + } + } else { + for ( var i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && checkSet[i].nodeType === 1 ) { + results.push( set[i] ); + } + } + } + } else { + makeArray( checkSet, results ); + } + + if ( extra ) { + Sizzle( extra, context, results, seed ); + } + + return results; }; -_9.matches=function(_1b,set){ -return _9(_1b,null,null,set); + +Sizzle.matches = function(expr, set){ + return Sizzle(expr, null, null, set); }; -_9.find=function(_1c,_1d){ -var set,_1e; -if(!_1c){ -return []; -} -for(var i=0,l=_15.order.length;i<l;i++){ -var _1f=_15.order[i],_1e; -if((_1e=_15.match[_1f].exec(_1c))){ -var _20=RegExp.leftContext; -if(_20.substr(_20.length-1)!=="\\"){ -_1e[1]=(_1e[1]||"").replace(/\\/g,""); -set=_15.find[_1f](_1e,_1d); -if(set!=null){ -_1c=_1c.replace(_15.match[_1f],""); -break; -} -} -} -} -if(!set){ -set=_1d.getElementsByTagName("*"); -} -return {set:set,expr:_1c}; + +Sizzle.find = function(expr, context){ + var set, match; + + if ( !expr ) { + return []; + } + + for ( var i = 0, l = Expr.order.length; i < l; i++ ) { + var type = Expr.order[i], match; + + if ( (match = Expr.match[ type ].exec( expr )) ) { + var left = RegExp.leftContext; + + if ( left.substr( left.length - 1 ) !== "\\" ) { + match[1] = (match[1] || "").replace(/\\/g, ""); + set = Expr.find[ type ]( match, context ); + if ( set != null ) { + expr = expr.replace( Expr.match[ type ], "" ); + break; + } + } + } + } + + if ( !set ) { + set = context.getElementsByTagName("*"); + } + + return {set: set, expr: expr}; }; -_9.filter=function(_21,set,_22,not){ -var old=_21,_23=[],_24=set,_25,_26; -while(_21&&set.length){ -for(var _27 in _15.filter){ -if((_25=_15.match[_27].exec(_21))!=null){ -var _28=_15.filter[_27],_29=null,_2a=0,_2b,_2c; -_26=false; -if(_24==_23){ -_23=[]; -} -if(_15.preFilter[_27]){ -_25=_15.preFilter[_27](_25,_24,_22,_23,not); -if(!_25){ -_26=_2b=true; -}else{ -if(_25[0]===true){ -_29=[]; -var _2d=null,_2e; -for(var i=0;(_2e=_24[i])!==undefined;i++){ -if(_2e&&_2d!==_2e){ -_29.push(_2e); -_2d=_2e; -} -} -} -} -} -if(_25){ -for(var i=0;(_2c=_24[i])!==undefined;i++){ -if(_2c){ -if(_29&&_2c!=_29[_2a]){ -_2a++; -} -_2b=_28(_2c,_25,_2a,_29); -var _2f=not^!!_2b; -if(_22&&_2b!=null){ -if(_2f){ -_26=true; -}else{ -_24[i]=false; -} -}else{ -if(_2f){ -_23.push(_2c); -_26=true; -} -} -} -} -} -if(_2b!==undefined){ -if(!_22){ -_24=_23; -} -_21=_21.replace(_15.match[_27],""); -if(!_26){ -return []; -} -break; -} -} -} -_21=_21.replace(/\s*,\s*/,""); -if(_21==old){ -if(_26==null){ -throw "Syntax error, unrecognized expression: "+_21; -}else{ -break; -} -} -old=_21; -} -return _24; + +Sizzle.filter = function(expr, set, inplace, not){ + var old = expr, result = [], curLoop = set, match, anyFound; + + while ( expr && set.length ) { + for ( var type in Expr.filter ) { + if ( (match = Expr.match[ type ].exec( expr )) != null ) { + var filter = Expr.filter[ type ], goodArray = null, goodPos = 0, found, item; + anyFound = false; + + if ( curLoop == result ) { + result = []; + } + + if ( Expr.preFilter[ type ] ) { + match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not ); + + if ( !match ) { + anyFound = found = true; + } else if ( match[0] === true ) { + goodArray = []; + var last = null, elem; + for ( var i = 0; (elem = curLoop[i]) !== undefined; i++ ) { + if ( elem && last !== elem ) { + goodArray.push( elem ); + last = elem; + } + } + } + } + + if ( match ) { + for ( var i = 0; (item = curLoop[i]) !== undefined; i++ ) { + if ( item ) { + if ( goodArray && item != goodArray[goodPos] ) { + goodPos++; + } + + found = filter( item, match, goodPos, goodArray ); + var pass = not ^ !!found; + + if ( inplace && found != null ) { + if ( pass ) { + anyFound = true; + } else { + curLoop[i] = false; + } + } else if ( pass ) { + result.push( item ); + anyFound = true; + } + } + } + } + + if ( found !== undefined ) { + if ( !inplace ) { + curLoop = result; + } + + expr = expr.replace( Expr.match[ type ], "" ); + + if ( !anyFound ) { + return []; + } + + break; + } + } + } + + expr = expr.replace(/\s*,\s*/, ""); + + // Improper expression + if ( expr == old ) { + if ( anyFound == null ) { + throw "Syntax error, unrecognized expression: " + expr; + } else { + break; + } + } + + old = expr; + } + + return curLoop; }; -var _15=_9.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u0128-\uFFFF_-]|\\.)+)/,CLASS:/\.((?:[\w\u0128-\uFFFF_-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u0128-\uFFFF_-]|\\.)+)['"]*\]/,ATTR:/\[((?:[\w\u0128-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\]/,TAG:/^((?:[\w\u0128-\uFFFF\*_-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child\(?(even|odd|[\dn+-]*)\)?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)\(?(\d*)\)?(?:[^-]|$)/,PSEUDO:/:((?:[\w\u0128-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/},attrMap:{"class":"className","for":"htmlFor"},relative:{"+":function(_30,_31){ -for(var i=0,l=_30.length;i<l;i++){ -var _32=_30[i]; -if(_32){ -var cur=_32.previousSibling; -while(cur&&cur.nodeType!==1){ -cur=cur.previousSibling; -} -_30[i]=typeof _31==="string"?cur||false:cur===_31; -} -} -if(typeof _31==="string"){ -_9.filter(_31,_30,true); -} -},">":function(_33,_34){ -if(typeof _34==="string"&&!/\W/.test(_34)){ -_34=_34.toUpperCase(); -for(var i=0,l=_33.length;i<l;i++){ -var _35=_33[i]; -if(_35){ -var _36=_35.parentNode; -_33[i]=_36.nodeName===_34?_36:false; -} -} -}else{ -for(var i=0,l=_33.length;i<l;i++){ -var _35=_33[i]; -if(_35){ -_33[i]=typeof _34==="string"?_35.parentNode:_35.parentNode===_34; -} -} -if(typeof _34==="string"){ -_9.filter(_34,_33,true); -} -} -},"":function(_37,_38){ -var _39="done"+(_7++),_3a=_3b; -if(!_38.match(/\W/)){ -var _3c=_38=_38.toUpperCase(); -_3a=_3d; -} -_3a("parentNode",_38,_39,_37,_3c); -},"~":function(_3e,_3f){ -var _40="done"+(_7++),_41=_3b; -if(typeof _3f==="string"&&!_3f.match(/\W/)){ -var _42=_3f=_3f.toUpperCase(); -_41=_3d; -} -_41("previousSibling",_3f,_40,_3e,_42); -}},find:{ID:function(_43,_44){ -if(_44.getElementById){ -var m=_44.getElementById(_43[1]); -return m?[m]:[]; -} -},NAME:function(_45,_46){ -return _46.getElementsByName?_46.getElementsByName(_45[1]):null; -},TAG:function(_47,_48){ -return _48.getElementsByTagName(_47[1]); -}},preFilter:{CLASS:function(_49,_4a,_4b,_4c,not){ -_49=" "+_49[1].replace(/\\/g,"")+" "; -for(var i=0;_4a[i];i++){ -if(not^(" "+_4a[i].className+" ").indexOf(_49)>=0){ -if(!_4b){ -_4c.push(_4a[i]); -} -}else{ -if(_4b){ -_4a[i]=false; -} -} -} -return false; -},ID:function(_4d){ -return _4d[1]; -},TAG:function(_4e){ -return _4e[1].toUpperCase(); -},CHILD:function(_4f){ -if(_4f[1]=="nth"){ -var _50=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(_4f[2]=="even"&&"2n"||_4f[2]=="odd"&&"2n+1"||!/\D/.test(_4f[2])&&"0n+"+_4f[2]||_4f[2]); -_4f[2]=(_50[1]+(_50[2]||1))-0; -_4f[3]=_50[3]-0; -} -_4f[0]="done"+(_7++); -return _4f; -},ATTR:function(_51){ -var _52=_51[1]; -if(_15.attrMap[_52]){ -_51[1]=_15.attrMap[_52]; -} -if(_51[2]==="~="){ -_51[4]=" "+_51[4]+" "; -} -return _51; -},PSEUDO:function(_53,_54,_55,_56,not){ -if(_53[1]==="not"){ -if(_53[3].match(_6).length>1){ -_53[3]=_9(_53[3],null,null,_54); -}else{ -var ret=_9.filter(_53[3],_54,_55,true^not); -if(!_55){ -_56.push.apply(_56,ret); -} -return false; -} -} -return _53; -},POS:function(_57){ -_57.unshift(true); -return _57; -}},filters:{enabled:function(_58){ -return _58.disabled===false&&_58.type!=="hidden"; -},disabled:function(_59){ -return _59.disabled===true; -},checked:function(_5a){ -return _5a.checked===true; -},selected:function(_5b){ -_5b.parentNode.selectedIndex; -return _5b.selected===true; -},parent:function(_5c){ -return !!_5c.firstChild; -},empty:function(_5d){ -return !_5d.firstChild; -},has:function(_5e,i,_5f){ -return !!_9(_5f[3],_5e).length; -},header:function(_60){ -return /h\d/i.test(_60.nodeName); -},text:function(_61){ -return "text"===_61.type; -},radio:function(_62){ -return "radio"===_62.type; -},checkbox:function(_63){ -return "checkbox"===_63.type; -},file:function(_64){ -return "file"===_64.type; -},password:function(_65){ -return "password"===_65.type; -},submit:function(_66){ -return "submit"===_66.type; -},image:function(_67){ -return "image"===_67.type; -},reset:function(_68){ -return "reset"===_68.type; -},button:function(_69){ -return "button"===_69.type||_69.nodeName.toUpperCase()==="BUTTON"; -},input:function(_6a){ -return /input|select|textarea|button/i.test(_6a.nodeName); -}},setFilters:{first:function(_6b,i){ -return i===0; -},last:function(_6c,i,_6d,_6e){ -return i===_6e.length-1; -},even:function(_6f,i){ -return i%2===0; -},odd:function(_70,i){ -return i%2===1; -},lt:function(_71,i,_72){ -return i<_72[3]-0; -},gt:function(_73,i,_74){ -return i>_74[3]-0; -},nth:function(_75,i,_76){ -return _76[3]-0==i; -},eq:function(_77,i,_78){ -return _78[3]-0==i; -}},filter:{CHILD:function(_79,_7a){ -var _7b=_7a[1],_7c=_79.parentNode; -var _7d=_7a[0]; -if(_7c&&!_7c[_7d]){ -var _7e=1; -for(var _7f=_7c.firstChild;_7f;_7f=_7f.nextSibling){ -if(_7f.nodeType==1){ -_7f.nodeIndex=_7e++; -} -} -_7c[_7d]=_7e-1; -} -if(_7b=="first"){ -return _79.nodeIndex==1; -}else{ -if(_7b=="last"){ -return _79.nodeIndex==_7c[_7d]; -}else{ -if(_7b=="only"){ -return _7c[_7d]==1; -}else{ -if(_7b=="nth"){ -var add=false,_80=_7a[2],_81=_7a[3]; -if(_80==1&&_81==0){ -return true; -} -if(_80==0){ -if(_79.nodeIndex==_81){ -add=true; -} -}else{ -if((_79.nodeIndex-_81)%_80==0&&(_79.nodeIndex-_81)/_80>=0){ -add=true; -} -} -return add; -} -} -} -} -},PSEUDO:function(_82,_83,i,_84){ -var _85=_83[1],_86=_15.filters[_85]; -if(_86){ -return _86(_82,i,_83,_84); -}else{ -if(_85==="contains"){ -return (_82.textContent||_82.innerText||"").indexOf(_83[3])>=0; -}else{ -if(_85==="not"){ -var not=_83[3]; -for(var i=0,l=not.length;i<l;i++){ -if(not[i]===_82){ -return false; -} -} -return true; -} -} -} -},ID:function(_87,_88){ -return _87.nodeType===1&&_87.getAttribute("id")===_88; -},TAG:function(_89,_8a){ -return (_8a==="*"&&_89.nodeType===1)||_89.nodeName===_8a; -},CLASS:function(_8b,_8c){ -return _8c.test(_8b.className); -},ATTR:function(_8d,_8e){ -var _8f=_8d[_8e[1]]||_8d.getAttribute(_8e[1]),_90=_8f+"",_91=_8e[2],_92=_8e[4]; -return _8f==null?false:_91==="="?_90===_92:_91==="*="?_90.indexOf(_92)>=0:_91==="~="?(" "+_90+" ").indexOf(_92)>=0:!_8e[4]?_8f:_91==="!="?_90!=_92:_91==="^="?_90.indexOf(_92)===0:_91==="$="?_90.substr(_90.length-_92.length)===_92:_91==="|="?_90===_92||_90.substr(0,_92.length+1)===_92+"-":false; -},POS:function(_93,_94,i,_95){ -var _96=_94[2],_97=_15.setFilters[_96]; -if(_97){ -return _97(_93,i,_94,_95); -} -}}}; -for(var _98 in _15.match){ -_15.match[_98]=RegExp(_15.match[_98].source+/(?![^\[]*\])(?![^\(]*\))/.source); -} -var _19=function(_99,_9a){ -_99=Array.prototype.slice.call(_99); -if(_9a){ -_9a.push.apply(_9a,_99); -return _9a; -} -return _99; + +var Expr = Sizzle.selectors = { + order: [ "ID", "NAME", "TAG" ], + match: { + ID: /#((?:[\w\u0128-\uFFFF_-]|\\.)+)/, + CLASS: /\.((?:[\w\u0128-\uFFFF_-]|\\.)+)/, + NAME: /\[name=['"]*((?:[\w\u0128-\uFFFF_-]|\\.)+)['"]*\]/, + ATTR: /\[((?:[\w\u0128-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\]/, + TAG: /^((?:[\w\u0128-\uFFFF\*_-]|\\.)+)/, + CHILD: /:(only|nth|last|first)-child\(?(even|odd|[\dn+-]*)\)?/, + POS: /:(nth|eq|gt|lt|first|last|even|odd)\(?(\d*)\)?(?:[^-]|$)/, + PSEUDO: /:((?:[\w\u0128-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/ + }, + attrMap: { + "class": "className", + "for": "htmlFor" + }, + relative: { + "+": function(checkSet, part){ + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + if ( elem ) { + var cur = elem.previousSibling; + while ( cur && cur.nodeType !== 1 ) { + cur = cur.previousSibling; + } + checkSet[i] = typeof part === "string" ? + cur || false : + cur === part; + } + } + + if ( typeof part === "string" ) { + Sizzle.filter( part, checkSet, true ); + } + }, + ">": function(checkSet, part){ + if ( typeof part === "string" && !/\W/.test(part) ) { + part = part.toUpperCase(); + + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + if ( elem ) { + var parent = elem.parentNode; + checkSet[i] = parent.nodeName === part ? parent : false; + } + } + } else { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + if ( elem ) { + checkSet[i] = typeof part === "string" ? + elem.parentNode : + elem.parentNode === part; + } + } + + if ( typeof part === "string" ) { + Sizzle.filter( part, checkSet, true ); + } + } + }, + "": function(checkSet, part){ + var doneName = "done" + (done++), checkFn = dirCheck; + + if ( !part.match(/\W/) ) { + var nodeCheck = part = part.toUpperCase(); + checkFn = dirNodeCheck; + } + + checkFn("parentNode", part, doneName, checkSet, nodeCheck); + }, + "~": function(checkSet, part){ + var doneName = "done" + (done++), checkFn = dirCheck; + + if ( typeof part === "string" && !part.match(/\W/) ) { + var nodeCheck = part = part.toUpperCase(); + checkFn = dirNodeCheck; + } + + checkFn("previousSibling", part, doneName, checkSet, nodeCheck); + } + }, + find: { + ID: function(match, context){ + if ( context.getElementById ) { + var m = context.getElementById(match[1]); + return m ? [m] : []; + } + }, + NAME: function(match, context){ + return context.getElementsByName ? context.getElementsByName(match[1]) : null; + }, + TAG: function(match, context){ + return context.getElementsByTagName(match[1]); + } + }, + preFilter: { + CLASS: function(match, curLoop, inplace, result, not){ + match = " " + match[1].replace(/\\/g, "") + " "; + + for ( var i = 0; curLoop[i]; i++ ) { + if ( not ^ (" " + curLoop[i].className + " ").indexOf(match) >= 0 ) { + if ( !inplace ) + result.push( curLoop[i] ); + } else if ( inplace ) { + curLoop[i] = false; + } + } + + return false; + }, + ID: function(match){ + return match[1]; + }, + TAG: function(match){ + return match[1].toUpperCase(); + }, + CHILD: function(match){ + if ( match[1] == "nth" ) { + // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' + var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec( + match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" || + !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); + + // calculate the numbers (first)n+(last) including if they are negative + match[2] = (test[1] + (test[2] || 1)) - 0; + match[3] = test[3] - 0; + } + + // TODO: Move to normal caching system + match[0] = "done" + (done++); + + return match; + }, + ATTR: function(match){ + var name = match[1]; + + if ( Expr.attrMap[name] ) { + match[1] = Expr.attrMap[name]; + } + + if ( match[2] === "~=" ) { + match[4] = " " + match[4] + " "; + } + + return match; + }, + PSEUDO: function(match, curLoop, inplace, result, not){ + if ( match[1] === "not" ) { + // If we're dealing with a complex expression, or a simple one + if ( match[3].match(chunker).length > 1 ) { + match[3] = Sizzle(match[3], null, null, curLoop); + } else { + var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); + if ( !inplace ) { + result.push.apply( result, ret ); + } + return false; + } + } + + return match; + }, + POS: function(match){ + match.unshift( true ); + return match; + } + }, + filters: { + enabled: function(elem){ + return elem.disabled === false && elem.type !== "hidden"; + }, + disabled: function(elem){ + return elem.disabled === true; + }, + checked: function(elem){ + return elem.checked === true; + }, + selected: function(elem){ + // Accessing this property makes selected-by-default + // options in Safari work properly + elem.parentNode.selectedIndex; + return elem.selected === true; + }, + parent: function(elem){ + return !!elem.firstChild; + }, + empty: function(elem){ + return !elem.firstChild; + }, + has: function(elem, i, match){ + return !!Sizzle( match[3], elem ).length; + }, + header: function(elem){ + return /h\d/i.test( elem.nodeName ); + }, + text: function(elem){ + return "text" === elem.type; + }, + radio: function(elem){ + return "radio" === elem.type; + }, + checkbox: function(elem){ + return "checkbox" === elem.type; + }, + file: function(elem){ + return "file" === elem.type; + }, + password: function(elem){ + return "password" === elem.type; + }, + submit: function(elem){ + return "submit" === elem.type; + }, + image: function(elem){ + return "image" === elem.type; + }, + reset: function(elem){ + return "reset" === elem.type; + }, + button: function(elem){ + return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON"; + }, + input: function(elem){ + return /input|select|textarea|button/i.test(elem.nodeName); + } + }, + setFilters: { + first: function(elem, i){ + return i === 0; + }, + last: function(elem, i, match, array){ + return i === array.length - 1; + }, + even: function(elem, i){ + return i % 2 === 0; + }, + odd: function(elem, i){ + return i % 2 === 1; + }, + lt: function(elem, i, match){ + return i < match[3] - 0; + }, + gt: function(elem, i, match){ + return i > match[3] - 0; + }, + nth: function(elem, i, match){ + return match[3] - 0 == i; + }, + eq: function(elem, i, match){ + return match[3] - 0 == i; + } + }, + filter: { + CHILD: function(elem, match){ + var type = match[1], parent = elem.parentNode; + + var doneName = match[0]; + + if ( parent && !parent[ doneName ] ) { + var count = 1; + + for ( var node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType == 1 ) { + node.nodeIndex = count++; + } + } + + parent[ doneName ] = count - 1; + } + + if ( type == "first" ) { + return elem.nodeIndex == 1; + } else if ( type == "last" ) { + return elem.nodeIndex == parent[ doneName ]; + } else if ( type == "only" ) { + return parent[ doneName ] == 1; + } else if ( type == "nth" ) { + var add = false, first = match[2], last = match[3]; + + if ( first == 1 && last == 0 ) { + return true; + } + + if ( first == 0 ) { + if ( elem.nodeIndex == last ) { + add = true; + } + } else if ( (elem.nodeIndex - last) % first == 0 && (elem.nodeIndex - last) / first >= 0 ) { + add = true; + } + + return add; + } + }, + PSEUDO: function(elem, match, i, array){ + var name = match[1], filter = Expr.filters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } else if ( name === "contains" ) { + return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0; + } else if ( name === "not" ) { + var not = match[3]; + + for ( var i = 0, l = not.length; i < l; i++ ) { + if ( not[i] === elem ) { + return false; + } + } + + return true; + } + }, + ID: function(elem, match){ + return elem.nodeType === 1 && elem.getAttribute("id") === match; + }, + TAG: function(elem, match){ + return (match === "*" && elem.nodeType === 1) || elem.nodeName === match; + }, + CLASS: function(elem, match){ + return match.test( elem.className ); + }, + ATTR: function(elem, match){ + var result = elem[ match[1] ] || elem.getAttribute( match[1] ), value = result + "", type = match[2], check = match[4]; + return result == null ? + false : + type === "=" ? + value === check : + type === "*=" ? + value.indexOf(check) >= 0 : + type === "~=" ? + (" " + value + " ").indexOf(check) >= 0 : + !match[4] ? + result : + type === "!=" ? + value != check : + type === "^=" ? + value.indexOf(check) === 0 : + type === "$=" ? + value.substr(value.length - check.length) === check : + type === "|=" ? + value === check || value.substr(0, check.length + 1) === check + "-" : + false; + }, + POS: function(elem, match, i, array){ + var name = match[2], filter = Expr.setFilters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } + } + } }; -try{ -Array.prototype.slice.call(document.documentElement.childNodes); -} -catch(e){ -_19=function(_9b,_9c){ -var ret=_9c||[]; -if(_8.call(_9b)==="[object Array]"){ -Array.prototype.push.apply(ret,_9b); -}else{ -if(typeof _9b.length==="number"){ -for(var i=0,l=_9b.length;i<l;i++){ -ret.push(_9b[i]); -} -}else{ -for(var i=0;_9b[i];i++){ -ret.push(_9b[i]); -} -} + +for ( var type in Expr.match ) { + Expr.match[ type ] = RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source ); } -return ret; + +var makeArray = function(array, results) { + array = Array.prototype.slice.call( array ); + + if ( results ) { + results.push.apply( results, array ); + return results; + } + + return array; }; + +// Perform a simple check to determine if the browser is capable of +// converting a NodeList to an array using builtin methods. +try { + Array.prototype.slice.call( document.documentElement.childNodes ); + +// Provide a fallback method if it does not work +} catch(e){ + makeArray = function(array, results) { + var ret = results || []; + + if ( toString.call(array) === "[object Array]" ) { + Array.prototype.push.apply( ret, array ); + } else { + if ( typeof array.length === "number" ) { + for ( var i = 0, l = array.length; i < l; i++ ) { + ret.push( array[i] ); + } + } else { + for ( var i = 0; array[i]; i++ ) { + ret.push( array[i] ); + } + } + } + + return ret; + }; } + +// Check to see if the browser returns elements by name when +// querying by getElementById (and provide a workaround) (function(){ -var _9d=document.createElement("form"),id="script"+(new Date).getTime(); -_9d.innerHTML="<input name='"+id+"'/>"; -var _9e=document.documentElement; -_9e.insertBefore(_9d,_9e.firstChild); -if(!!document.getElementById(id)){ -_15.find.ID=function(_9f,_a0){ -if(_a0.getElementById){ -var m=_a0.getElementById(_9f[1]); -return m?m.id===_9f[1]||m.getAttributeNode&&m.getAttributeNode("id").nodeValue===_9f[1]?[m]:undefined:[]; -} -}; -_15.filter.ID=function(_a1,_a2){ -var _a3=_a1.getAttributeNode&&_a1.getAttributeNode("id"); -return _a1.nodeType===1&&_a3&&_a3.nodeValue===_a2; -}; -} -_9e.removeChild(_9d); + // We're going to inject a fake input element with a specified name + var form = document.createElement("form"), + id = "script" + (new Date).getTime(); + form.innerHTML = "<input name='" + id + "'/>"; + + // Inject it into the root element, check its status, and remove it quickly + var root = document.documentElement; + root.insertBefore( form, root.firstChild ); + + // The workaround has to do additional checks after a getElementById + // Which slows things down for other browsers (hence the branching) + if ( !!document.getElementById( id ) ) { + Expr.find.ID = function(match, context){ + if ( context.getElementById ) { + var m = context.getElementById(match[1]); + return m ? m.id === match[1] || m.getAttributeNode && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : []; + } + }; + + Expr.filter.ID = function(elem, match){ + var node = elem.getAttributeNode && elem.getAttributeNode("id"); + return elem.nodeType === 1 && node && node.nodeValue === match; + }; + } + + root.removeChild( form ); })(); + +// Check to see if the browser returns only elements +// when doing getElementsByTagName("*") (function(){ -var div=document.createElement("div"); -div.appendChild(document.createComment("")); -if(div.getElementsByTagName("*").length>0){ -_15.find.TAG=function(_a4,_a5){ -var _a6=_a5.getElementsByTagName(_a4[1]); -if(_a4[1]==="*"){ -var tmp=[]; -for(var i=0;_a6[i];i++){ -if(_a6[i].nodeType===1){ -tmp.push(_a6[i]); -} -} -_a6=tmp; -} -return _a6; -}; -} + // Create a fake element + var div = document.createElement("div"); + div.appendChild( document.createComment("") ); + + // Make sure no comments are found + if ( div.getElementsByTagName("*").length > 0 ) { + Expr.find.TAG = function(match, context){ + var results = context.getElementsByTagName(match[1]); + + // Filter out possible comments + if ( match[1] === "*" ) { + var tmp = []; + + for ( var i = 0; results[i]; i++ ) { + if ( results[i].nodeType === 1 ) { + tmp.push( results[i] ); + } + } + + results = tmp; + } + + return results; + }; + } })(); -if(document.querySelectorAll){ -(function(){ -var _a7=_9; -_9=function(_a8,_a9,_aa,_ab){ -_a9=_a9||document; -if(!_ab&&_a9.nodeType===9){ -try{ -return _19(_a9.querySelectorAll(_a8),_aa); -} -catch(e){ -} -} -return _a7(_a8,_a9,_aa,_ab); -}; -_9.find=_a7.find; -_9.filter=_a7.filter; -_9.selectors=_a7.selectors; -_9.matches=_a7.matches; + +if ( document.querySelectorAll ) (function(){ + var oldSizzle = Sizzle; + + Sizzle = function(query, context, extra, seed){ + context = context || document; + + if ( !seed && context.nodeType === 9 ) { + try { + return makeArray( context.querySelectorAll(query), extra ); + } catch(e){} + } + + return oldSizzle(query, context, extra, seed); + }; + + Sizzle.find = oldSizzle.find; + Sizzle.filter = oldSizzle.filter; + Sizzle.selectors = oldSizzle.selectors; + Sizzle.matches = oldSizzle.matches; })(); + +if ( document.documentElement.getElementsByClassName ) { + Expr.order.splice(1, 0, "CLASS"); + Expr.find.CLASS = function(match, context) { + return context.getElementsByClassName(match[1]); + }; } -if(document.documentElement.getElementsByClassName){ -_15.order.splice(1,0,"CLASS"); -_15.find.CLASS=function(_ac,_ad){ -return _ad.getElementsByClassName(_ac[1]); -}; -} -function _3d(dir,cur,_ae,_af,_b0){ -for(var i=0,l=_af.length;i<l;i++){ -var _b1=_af[i]; -if(_b1){ -_b1=_b1[dir]; -var _b2=false; -while(_b1&&_b1.nodeType){ -var _b3=_b1[_ae]; -if(_b3){ -_b2=_af[_b3]; -break; -} -if(_b1.nodeType===1){ -_b1[_ae]=i; -} -if(_b1.nodeName===cur){ -_b2=_b1; -break; -} -_b1=_b1[dir]; -} -_af[i]=_b2; -} -} -}; -function _3b(dir,cur,_b4,_b5,_b6){ -for(var i=0,l=_b5.length;i<l;i++){ -var _b7=_b5[i]; -if(_b7){ -_b7=_b7[dir]; -var _b8=false; -while(_b7&&_b7.nodeType){ -if(_b7[_b4]){ -_b8=_b5[_b7[_b4]]; -break; -} -if(_b7.nodeType===1){ -_b7[_b4]=i; -if(typeof cur!=="string"){ -if(_b7===cur){ -_b8=true; -break; -} -}else{ -if(_9.filter(cur,[_b7]).length>0){ -_b8=_b7; -break; -} -} -} -_b7=_b7[dir]; -} -_b5[i]=_b8; + +function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + if ( elem ) { + elem = elem[dir]; + var match = false; + + while ( elem && elem.nodeType ) { + var done = elem[doneName]; + if ( done ) { + match = checkSet[ done ]; + break; + } + + if ( elem.nodeType === 1 ) + elem[doneName] = i; + + if ( elem.nodeName === cur ) { + match = elem; + break; + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } } + +function dirCheck( dir, cur, doneName, checkSet, nodeCheck ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + if ( elem ) { + elem = elem[dir]; + var match = false; + + while ( elem && elem.nodeType ) { + if ( elem[doneName] ) { + match = checkSet[ elem[doneName] ]; + break; + } + + if ( elem.nodeType === 1 ) { + elem[doneName] = i; + + if ( typeof cur !== "string" ) { + if ( elem === cur ) { + match = true; + break; + } + + } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { + match = elem; + break; + } + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } } + +var contains = document.compareDocumentPosition ? function(a, b){ + return a.compareDocumentPosition(b) & 16; +} : function(a, b){ + return a !== b && (a.contains ? a.contains(b) : true); }; -var _1a=document.compareDocumentPosition?function(a,b){ -return a.compareDocumentPosition(b)&16; -}:function(a,b){ -return a!==b&&(a.contains?a.contains(b):true); -}; -(ns||window).Sizzle=_9; -})(typeof dojo=="undefined"?null:dojo); + +// EXPOSE + +(ns || window).Sizzle = Sizzle; + +})(typeof dojo == "undefined" ? null : dojo); + } diff --git a/lib/dojo/_base/query.js b/lib/dojo/_base/query.js index ecf84682c..7b9878e47 100644 --- a/lib/dojo/_base/query.js +++ b/lib/dojo/_base/query.js @@ -5,793 +5,1520 @@ */ -if(!dojo._hasResource["dojo._base.query"]){ -dojo._hasResource["dojo._base.query"]=true; -if(typeof dojo!="undefined"){ -dojo.provide("dojo._base.query"); -dojo.require("dojo._base.NodeList"); -dojo.require("dojo._base.lang"); -} -(function(d){ -var _1=d.trim; -var _2=d.forEach; -var _3=d._NodeListCtor=d.NodeList; -var _4=function(){ -return d.doc; -}; -var _5=((d.isWebKit||d.isMozilla)&&((_4().compatMode)=="BackCompat")); -var _6=!!_4().firstChild["children"]?"children":"childNodes"; -var _7=">~+"; -var _8=false; -var _9=function(){ -return true; -}; -var _a=function(_b){ -if(_7.indexOf(_b.slice(-1))>=0){ -_b+=" * "; -}else{ -_b+=" "; -} -var ts=function(s,e){ -return _1(_b.slice(s,e)); -}; -var _c=[]; -var _d=-1,_e=-1,_f=-1,_10=-1,_11=-1,_12=-1,_13=-1,lc="",cc="",_14; -var x=0,ql=_b.length,_15=null,_16=null; -var _17=function(){ -if(_13>=0){ -var tv=(_13==x)?null:ts(_13,x); -_15[(_7.indexOf(tv)<0)?"tag":"oper"]=tv; -_13=-1; -} -}; -var _18=function(){ -if(_12>=0){ -_15.id=ts(_12,x).replace(/\\/g,""); -_12=-1; -} -}; -var _19=function(){ -if(_11>=0){ -_15.classes.push(ts(_11+1,x).replace(/\\/g,"")); -_11=-1; -} -}; -var _1a=function(){ -_18(); -_17(); -_19(); -}; -var _1b=function(){ -_1a(); -if(_10>=0){ -_15.pseudos.push({name:ts(_10+1,x)}); -} -_15.loops=(_15.pseudos.length||_15.attrs.length||_15.classes.length); -_15.oquery=_15.query=ts(_14,x); -_15.otag=_15.tag=(_15["oper"])?null:(_15.tag||"*"); -if(_15.tag){ -_15.tag=_15.tag.toUpperCase(); -} -if(_c.length&&(_c[_c.length-1].oper)){ -_15.infixOper=_c.pop(); -_15.query=_15.infixOper.query+" "+_15.query; -} -_c.push(_15); -_15=null; -}; -for(;lc=cc,cc=_b.charAt(x),x<ql;x++){ -if(lc=="\\"){ -continue; -} -if(!_15){ -_14=x; -_15={query:null,pseudos:[],attrs:[],classes:[],tag:null,oper:null,id:null,getTag:function(){ -return (_8)?this.otag:this.tag; -}}; -_13=x; -} -if(_d>=0){ -if(cc=="]"){ -if(!_16.attr){ -_16.attr=ts(_d+1,x); -}else{ -_16.matchFor=ts((_f||_d+1),x); -} -var cmf=_16.matchFor; -if(cmf){ -if((cmf.charAt(0)=="\"")||(cmf.charAt(0)=="'")){ -_16.matchFor=cmf.slice(1,-1); -} -} -_15.attrs.push(_16); -_16=null; -_d=_f=-1; -}else{ -if(cc=="="){ -var _1c=("|~^$*".indexOf(lc)>=0)?lc:""; -_16.type=_1c+cc; -_16.attr=ts(_d+1,x-_1c.length); -_f=x+1; -} -} -}else{ -if(_e>=0){ -if(cc==")"){ -if(_10>=0){ -_16.value=ts(_e+1,x); -} -_10=_e=-1; -} -}else{ -if(cc=="#"){ -_1a(); -_12=x+1; -}else{ -if(cc=="."){ -_1a(); -_11=x; -}else{ -if(cc==":"){ -_1a(); -_10=x; -}else{ -if(cc=="["){ -_1a(); -_d=x; -_16={}; -}else{ -if(cc=="("){ -if(_10>=0){ -_16={name:ts(_10+1,x),value:null}; -_15.pseudos.push(_16); -} -_e=x; -}else{ -if((cc==" ")&&(lc!=cc)){ -_1b(); -} -} -} -} -} -} -} -} -} -return _c; -}; -var _1d=function(_1e,_1f){ -if(!_1e){ -return _1f; -} -if(!_1f){ -return _1e; -} -return function(){ -return _1e.apply(window,arguments)&&_1f.apply(window,arguments); -}; -}; -var _20=function(i,arr){ -var r=arr||[]; -if(i){ -r.push(i); -} -return r; -}; -var _21=function(n){ -return (1==n.nodeType); -}; -var _22=""; -var _23=function(_24,_25){ -if(!_24){ -return _22; -} -if(_25=="class"){ -return _24.className||_22; -} -if(_25=="for"){ -return _24.htmlFor||_22; -} -if(_25=="style"){ -return _24.style.cssText||_22; -} -return (_8?_24.getAttribute(_25):_24.getAttribute(_25,2))||_22; -}; -var _26={"*=":function(_27,_28){ -return function(_29){ -return (_23(_29,_27).indexOf(_28)>=0); -}; -},"^=":function(_2a,_2b){ -return function(_2c){ -return (_23(_2c,_2a).indexOf(_2b)==0); -}; -},"$=":function(_2d,_2e){ -var _2f=" "+_2e; -return function(_30){ -var ea=" "+_23(_30,_2d); -return (ea.lastIndexOf(_2e)==(ea.length-_2e.length)); -}; -},"~=":function(_31,_32){ -var _33=" "+_32+" "; -return function(_34){ -var ea=" "+_23(_34,_31)+" "; -return (ea.indexOf(_33)>=0); -}; -},"|=":function(_35,_36){ -var _37=" "+_36+"-"; -return function(_38){ -var ea=" "+_23(_38,_35); -return ((ea==_36)||(ea.indexOf(_37)==0)); -}; -},"=":function(_39,_3a){ -return function(_3b){ -return (_23(_3b,_39)==_3a); -}; -}}; -var _3c=(typeof _4().firstChild.nextElementSibling=="undefined"); -var _3d=!_3c?"nextElementSibling":"nextSibling"; -var _3e=!_3c?"previousElementSibling":"previousSibling"; -var _3f=(_3c?_21:_9); -var _40=function(_41){ -while(_41=_41[_3e]){ -if(_3f(_41)){ -return false; -} -} -return true; -}; -var _42=function(_43){ -while(_43=_43[_3d]){ -if(_3f(_43)){ -return false; -} -} -return true; -}; -var _44=function(_45){ -var _46=_45.parentNode; -var i=0,_47=_46[_6],ci=(_45["_i"]||-1),cl=(_46["_l"]||-1); -if(!_47){ -return -1; -} -var l=_47.length; -if(cl==l&&ci>=0&&cl>=0){ -return ci; -} -_46["_l"]=l; -ci=-1; -for(var te=_46["firstElementChild"]||_46["firstChild"];te;te=te[_3d]){ -if(_3f(te)){ -te["_i"]=++i; -if(_45===te){ -ci=i; -} -} -} -return ci; -}; -var _48=function(_49){ -return !((_44(_49))%2); -}; -var _4a=function(_4b){ -return ((_44(_4b))%2); -}; -var _4c={"checked":function(_4d,_4e){ -return function(_4f){ -return !!("checked" in _4f?_4f.checked:_4f.selected); -}; -},"first-child":function(){ -return _40; -},"last-child":function(){ -return _42; -},"only-child":function(_50,_51){ -return function(_52){ -if(!_40(_52)){ -return false; -} -if(!_42(_52)){ -return false; -} -return true; -}; -},"empty":function(_53,_54){ -return function(_55){ -var cn=_55.childNodes; -var cnl=_55.childNodes.length; -for(var x=cnl-1;x>=0;x--){ -var nt=cn[x].nodeType; -if((nt===1)||(nt==3)){ -return false; -} -} -return true; -}; -},"contains":function(_56,_57){ -var cz=_57.charAt(0); -if(cz=="\""||cz=="'"){ -_57=_57.slice(1,-1); -} -return function(_58){ -return (_58.innerHTML.indexOf(_57)>=0); -}; -},"not":function(_59,_5a){ -var p=_a(_5a)[0]; -var _5b={el:1}; -if(p.tag!="*"){ -_5b.tag=1; -} -if(!p.classes.length){ -_5b.classes=1; -} -var ntf=_5c(p,_5b); -return function(_5d){ -return (!ntf(_5d)); -}; -},"nth-child":function(_5e,_5f){ -var pi=parseInt; -if(_5f=="odd"){ -return _4a; -}else{ -if(_5f=="even"){ -return _48; -} -} -if(_5f.indexOf("n")!=-1){ -var _60=_5f.split("n",2); -var _61=_60[0]?((_60[0]=="-")?-1:pi(_60[0])):1; -var idx=_60[1]?pi(_60[1]):0; -var lb=0,ub=-1; -if(_61>0){ -if(idx<0){ -idx=(idx%_61)&&(_61+(idx%_61)); -}else{ -if(idx>0){ -if(idx>=_61){ -lb=idx-idx%_61; -} -idx=idx%_61; -} -} -}else{ -if(_61<0){ -_61*=-1; -if(idx>0){ -ub=idx; -idx=idx%_61; -} -} -} -if(_61>0){ -return function(_62){ -var i=_44(_62); -return (i>=lb)&&(ub<0||i<=ub)&&((i%_61)==idx); -}; -}else{ -_5f=idx; -} -} -var _63=pi(_5f); -return function(_64){ -return (_44(_64)==_63); -}; -}}; -var _65=(d.isIE)?function(_66){ -var clc=_66.toLowerCase(); -if(clc=="class"){ -_66="className"; -} -return function(_67){ -return (_8?_67.getAttribute(_66):_67[_66]||_67[clc]); -}; -}:function(_68){ -return function(_69){ -return (_69&&_69.getAttribute&&_69.hasAttribute(_68)); -}; -}; -var _5c=function(_6a,_6b){ -if(!_6a){ -return _9; -} -_6b=_6b||{}; -var ff=null; -if(!("el" in _6b)){ -ff=_1d(ff,_21); -} -if(!("tag" in _6b)){ -if(_6a.tag!="*"){ -ff=_1d(ff,function(_6c){ -return (_6c&&(_6c.tagName==_6a.getTag())); -}); -} -} -if(!("classes" in _6b)){ -_2(_6a.classes,function(_6d,idx,arr){ -var re=new RegExp("(?:^|\\s)"+_6d+"(?:\\s|$)"); -ff=_1d(ff,function(_6e){ -return re.test(_6e.className); -}); -ff.count=idx; -}); -} -if(!("pseudos" in _6b)){ -_2(_6a.pseudos,function(_6f){ -var pn=_6f.name; -if(_4c[pn]){ -ff=_1d(ff,_4c[pn](pn,_6f.value)); -} -}); -} -if(!("attrs" in _6b)){ -_2(_6a.attrs,function(_70){ -var _71; -var a=_70.attr; -if(_70.type&&_26[_70.type]){ -_71=_26[_70.type](a,_70.matchFor); -}else{ -if(a.length){ -_71=_65(a); -} -} -if(_71){ -ff=_1d(ff,_71); -} -}); -} -if(!("id" in _6b)){ -if(_6a.id){ -ff=_1d(ff,function(_72){ -return (!!_72&&(_72.id==_6a.id)); -}); -} -} -if(!ff){ -if(!("default" in _6b)){ -ff=_9; -} -} -return ff; -}; -var _73=function(_74){ -return function(_75,ret,bag){ -while(_75=_75[_3d]){ -if(_3c&&(!_21(_75))){ -continue; -} -if((!bag||_76(_75,bag))&&_74(_75)){ -ret.push(_75); -} -break; -} -return ret; -}; -}; -var _77=function(_78){ -return function(_79,ret,bag){ -var te=_79[_3d]; -while(te){ -if(_3f(te)){ -if(bag&&!_76(te,bag)){ -break; -} -if(_78(te)){ -ret.push(te); -} -} -te=te[_3d]; -} -return ret; -}; -}; -var _7a=function(_7b){ -_7b=_7b||_9; -return function(_7c,ret,bag){ -var te,x=0,_7d=_7c[_6]; -while(te=_7d[x++]){ -if(_3f(te)&&(!bag||_76(te,bag))&&(_7b(te,x))){ -ret.push(te); -} -} -return ret; -}; -}; -var _7e=function(_7f,_80){ -var pn=_7f.parentNode; -while(pn){ -if(pn==_80){ -break; -} -pn=pn.parentNode; -} -return !!pn; -}; -var _81={}; -var _82=function(_83){ -var _84=_81[_83.query]; -if(_84){ -return _84; -} -var io=_83.infixOper; -var _85=(io?io.oper:""); -var _86=_5c(_83,{el:1}); -var qt=_83.tag; -var _87=("*"==qt); -var ecs=_4()["getElementsByClassName"]; -if(!_85){ -if(_83.id){ -_86=(!_83.loops&&_87)?_9:_5c(_83,{el:1,id:1}); -_84=function(_88,arr){ -var te=d.byId(_83.id,(_88.ownerDocument||_88)); -if(!te||!_86(te)){ -return; -} -if(9==_88.nodeType){ -return _20(te,arr); -}else{ -if(_7e(te,_88)){ -return _20(te,arr); -} -} -}; -}else{ -if(ecs&&/\{\s*\[native code\]\s*\}/.test(String(ecs))&&_83.classes.length&&!_5){ -_86=_5c(_83,{el:1,classes:1,id:1}); -var _89=_83.classes.join(" "); -_84=function(_8a,arr,bag){ -var ret=_20(0,arr),te,x=0; -var _8b=_8a.getElementsByClassName(_89); -while((te=_8b[x++])){ -if(_86(te,_8a)&&_76(te,bag)){ -ret.push(te); -} -} -return ret; -}; -}else{ -if(!_87&&!_83.loops){ -_84=function(_8c,arr,bag){ -var ret=_20(0,arr),te,x=0; -var _8d=_8c.getElementsByTagName(_83.getTag()); -while((te=_8d[x++])){ -if(_76(te,bag)){ -ret.push(te); -} -} -return ret; -}; -}else{ -_86=_5c(_83,{el:1,tag:1,id:1}); -_84=function(_8e,arr,bag){ -var ret=_20(0,arr),te,x=0; -var _8f=_8e.getElementsByTagName(_83.getTag()); -while((te=_8f[x++])){ -if(_86(te,_8e)&&_76(te,bag)){ -ret.push(te); -} -} -return ret; -}; -} -} -} -}else{ -var _90={el:1}; -if(_87){ -_90.tag=1; -} -_86=_5c(_83,_90); -if("+"==_85){ -_84=_73(_86); -}else{ -if("~"==_85){ -_84=_77(_86); -}else{ -if(">"==_85){ -_84=_7a(_86); -} -} -} -} -return _81[_83.query]=_84; -}; -var _91=function(_92,_93){ -var _94=_20(_92),qp,x,te,qpl=_93.length,bag,ret; -for(var i=0;i<qpl;i++){ -ret=[]; -qp=_93[i]; -x=_94.length-1; -if(x>0){ -bag={}; -ret.nozip=true; -} -var gef=_82(qp); -for(var j=0;(te=_94[j]);j++){ -gef(te,ret,bag); -} -if(!ret.length){ -break; -} -_94=ret; -} -return ret; -}; -var _95={},_96={}; -var _97=function(_98){ -var _99=_a(_1(_98)); -if(_99.length==1){ -var tef=_82(_99[0]); -return function(_9a){ -var r=tef(_9a,new _3()); -if(r){ -r.nozip=true; -} -return r; -}; -} -return function(_9b){ -return _91(_9b,_99); -}; -}; -var nua=navigator.userAgent; -var wk="WebKit/"; -var _9c=(d.isWebKit&&(nua.indexOf(wk)>0)&&(parseFloat(nua.split(wk)[1])>528)); -var _9d=d.isIE?"commentStrip":"nozip"; -var qsa="querySelectorAll"; -var _9e=(!!_4()[qsa]&&(!d.isSafari||(d.isSafari>3.1)||_9c)); -var _9f=/n\+\d|([^ ])?([>~+])([^ =])?/g; -var _a0=function(_a1,pre,ch,_a2){ -return ch?(pre?pre+" ":"")+ch+(_a2?" "+_a2:""):_a1; -}; -var _a3=function(_a4,_a5){ -_a4=_a4.replace(_9f,_a0); -if(_9e){ -var _a6=_96[_a4]; -if(_a6&&!_a5){ -return _a6; -} -} -var _a7=_95[_a4]; -if(_a7){ -return _a7; -} -var qcz=_a4.charAt(0); -var _a8=(-1==_a4.indexOf(" ")); -if((_a4.indexOf("#")>=0)&&(_a8)){ -_a5=true; -} -var _a9=(_9e&&(!_a5)&&(_7.indexOf(qcz)==-1)&&(!d.isIE||(_a4.indexOf(":")==-1))&&(!(_5&&(_a4.indexOf(".")>=0)))&&(_a4.indexOf(":contains")==-1)&&(_a4.indexOf(":checked")==-1)&&(_a4.indexOf("|=")==-1)); -if(_a9){ -var tq=(_7.indexOf(_a4.charAt(_a4.length-1))>=0)?(_a4+" *"):_a4; -return _96[_a4]=function(_aa){ -try{ -if(!((9==_aa.nodeType)||_a8)){ -throw ""; -} -var r=_aa[qsa](tq); -r[_9d]=true; -return r; -} -catch(e){ -return _a3(_a4,true)(_aa); -} -}; -}else{ -var _ab=_a4.split(/\s*,\s*/); -return _95[_a4]=((_ab.length<2)?_97(_a4):function(_ac){ -var _ad=0,ret=[],tp; -while((tp=_ab[_ad++])){ -ret=ret.concat(_97(tp)(_ac)); -} -return ret; -}); -} -}; -var _ae=0; -var _af=d.isIE?function(_b0){ -if(_8){ -return (_b0.getAttribute("_uid")||_b0.setAttribute("_uid",++_ae)||_ae); -}else{ -return _b0.uniqueID; -} -}:function(_b1){ -return (_b1._uid||(_b1._uid=++_ae)); -}; -var _76=function(_b2,bag){ -if(!bag){ -return 1; -} -var id=_af(_b2); -if(!bag[id]){ -return bag[id]=1; -} -return 0; -}; -var _b3="_zipIdx"; -var _b4=function(arr){ -if(arr&&arr.nozip){ -return (_3._wrap)?_3._wrap(arr):arr; -} -var ret=new _3(); -if(!arr||!arr.length){ -return ret; -} -if(arr[0]){ -ret.push(arr[0]); -} -if(arr.length<2){ -return ret; -} -_ae++; -if(d.isIE&&_8){ -var _b5=_ae+""; -arr[0].setAttribute(_b3,_b5); -for(var x=1,te;te=arr[x];x++){ -if(arr[x].getAttribute(_b3)!=_b5){ -ret.push(te); -} -te.setAttribute(_b3,_b5); -} -}else{ -if(d.isIE&&arr.commentStrip){ -try{ -for(var x=1,te;te=arr[x];x++){ -if(_21(te)){ -ret.push(te); -} -} -} -catch(e){ -} -}else{ -if(arr[0]){ -arr[0][_b3]=_ae; -} -for(var x=1,te;te=arr[x];x++){ -if(arr[x][_b3]!=_ae){ -ret.push(te); -} -te[_b3]=_ae; -} -} -} -return ret; -}; -d.query=function(_b6,_b7){ -_3=d._NodeListCtor; -if(!_b6){ -return new _3(); -} -if(_b6.constructor==_3){ -return _b6; -} -if(typeof _b6!="string"){ -return new _3(_b6); -} -if(typeof _b7=="string"){ -_b7=d.byId(_b7); -if(!_b7){ -return new _3(); -} -} -_b7=_b7||_4(); -var od=_b7.ownerDocument||_b7.documentElement; -_8=(_b7.contentType&&_b7.contentType=="application/xml")||(d.isOpera&&(_b7.doctype||od.toString()=="[object XMLDocument]"))||(!!od)&&(d.isIE?od.xml:(_b7.xmlVersion||od.xmlVersion)); -var r=_a3(_b6)(_b7); -if(r&&r.nozip&&!_3._wrap){ -return r; -} -return _b4(r); -}; -d.query.pseudos=_4c; -d._filterQueryResult=function(_b8,_b9){ -var _ba=new d._NodeListCtor(); -var _bb=_5c(_a(_b9)[0]); -for(var x=0,te;te=_b8[x];x++){ -if(_bb(te)){ -_ba.push(te); -} +if(!dojo._hasResource["dojo._base.query"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo._base.query"] = true; +if(typeof dojo != "undefined"){ + dojo.provide("dojo._base.query"); + dojo.require("dojo._base.NodeList"); + dojo.require("dojo._base.lang"); + } -return _ba; -}; + +/* + dojo.query() architectural overview: + + dojo.query is a relatively full-featured CSS3 query library. It is + designed to take any valid CSS3 selector and return the nodes matching + the selector. To do this quickly, it processes queries in several + steps, applying caching where profitable. + + The steps (roughly in reverse order of the way they appear in the code): + 1.) check to see if we already have a "query dispatcher" + - if so, use that with the given parameterization. Skip to step 4. + 2.) attempt to determine which branch to dispatch the query to: + - JS (optimized DOM iteration) + - native (FF3.1+, Safari 3.1+, IE 8+) + 3.) tokenize and convert to executable "query dispatcher" + - this is where the lion's share of the complexity in the + system lies. In the DOM version, the query dispatcher is + assembled as a chain of "yes/no" test functions pertaining to + a section of a simple query statement (".blah:nth-child(odd)" + but not "div div", which is 2 simple statements). Individual + statement dispatchers are cached (to prevent re-definition) + as are entire dispatch chains (to make re-execution of the + same query fast) + 4.) the resulting query dispatcher is called in the passed scope + (by default the top-level document) + - for DOM queries, this results in a recursive, top-down + evaluation of nodes based on each simple query section + - for native implementations, this may mean working around spec + bugs. So be it. + 5.) matched nodes are pruned to ensure they are unique (if necessary) +*/ + +;(function(d){ + // define everything in a closure for compressability reasons. "d" is an + // alias to "dojo" (or the toolkit alias object, e.g., "acme"). + + //////////////////////////////////////////////////////////////////////// + // Toolkit aliases + //////////////////////////////////////////////////////////////////////// + + // if you are extracing dojo.query for use in your own system, you will + // need to provide these methods and properties. No other porting should be + // necessary, save for configuring the system to use a class other than + // dojo.NodeList as the return instance instantiator + var trim = d.trim; + var each = d.forEach; + // d.isIE; // float + // d.isSafari; // float + // d.isOpera; // float + // d.isWebKit; // float + // d.doc ; // document element + var qlc = d._NodeListCtor = d.NodeList; + + var getDoc = function(){ return d.doc; }; + // NOTE(alex): the spec is idiotic. CSS queries should ALWAYS be case-sensitive, but nooooooo + var cssCaseBug = ((d.isWebKit||d.isMozilla) && ((getDoc().compatMode) == "BackCompat")); + + //////////////////////////////////////////////////////////////////////// + // Global utilities + //////////////////////////////////////////////////////////////////////// + + + // on browsers that support the "children" collection we can avoid a lot of + // iteration on chaff (non-element) nodes. + // why. + var childNodesName = !!getDoc().firstChild["children"] ? "children" : "childNodes"; + + var specials = ">~+"; + + // global thunk to determine whether we should treat the current query as + // case sensitive or not. This switch is flipped by the query evaluator + // based on the document passed as the context to search. + var caseSensitive = false; + + // how high? + var yesman = function(){ return true; }; + + //////////////////////////////////////////////////////////////////////// + // Tokenizer + //////////////////////////////////////////////////////////////////////// + + var getQueryParts = function(query){ + // summary: + // state machine for query tokenization + // description: + // instead of using a brittle and slow regex-based CSS parser, + // dojo.query implements an AST-style query representation. This + // representation is only generated once per query. For example, + // the same query run multiple times or under different root nodes + // does not re-parse the selector expression but instead uses the + // cached data structure. The state machine implemented here + // terminates on the last " " (space) charachter and returns an + // ordered array of query component structures (or "parts"). Each + // part represents an operator or a simple CSS filtering + // expression. The structure for parts is documented in the code + // below. + + + // NOTE: + // this code is designed to run fast and compress well. Sacrifices + // to readibility and maintainability have been made. Your best + // bet when hacking the tokenizer is to put The Donnas on *really* + // loud (may we recommend their "Spend The Night" release?) and + // just assume you're gonna make mistakes. Keep the unit tests + // open and run them frequently. Knowing is half the battle ;-) + if(specials.indexOf(query.slice(-1)) >= 0){ + // if we end with a ">", "+", or "~", that means we're implicitly + // searching all children, so make it explicit + query += " * " + }else{ + // if you have not provided a terminator, one will be provided for + // you... + query += " "; + } + + var ts = function(/*Integer*/ s, /*Integer*/ e){ + // trim and slice. + + // take an index to start a string slice from and an end position + // and return a trimmed copy of that sub-string + return trim(query.slice(s, e)); + } + + // the overall data graph of the full query, as represented by queryPart objects + var queryParts = []; + + + // state keeping vars + var inBrackets = -1, inParens = -1, inMatchFor = -1, + inPseudo = -1, inClass = -1, inId = -1, inTag = -1, + lc = "", cc = "", pStart; + + // iteration vars + var x = 0, // index in the query + ql = query.length, + currentPart = null, // data structure representing the entire clause + _cp = null; // the current pseudo or attr matcher + + // several temporary variables are assigned to this structure durring a + // potential sub-expression match: + // attr: + // a string representing the current full attribute match in a + // bracket expression + // type: + // if there's an operator in a bracket expression, this is + // used to keep track of it + // value: + // the internals of parenthetical expression for a pseudo. for + // :nth-child(2n+1), value might be "2n+1" + + var endTag = function(){ + // called when the tokenizer hits the end of a particular tag name. + // Re-sets state variables for tag matching and sets up the matcher + // to handle the next type of token (tag or operator). + if(inTag >= 0){ + var tv = (inTag == x) ? null : ts(inTag, x); // .toLowerCase(); + currentPart[ (specials.indexOf(tv) < 0) ? "tag" : "oper" ] = tv; + inTag = -1; + } + } + + var endId = function(){ + // called when the tokenizer might be at the end of an ID portion of a match + if(inId >= 0){ + currentPart.id = ts(inId, x).replace(/\\/g, ""); + inId = -1; + } + } + + var endClass = function(){ + // called when the tokenizer might be at the end of a class name + // match. CSS allows for multiple classes, so we augment the + // current item with another class in its list + if(inClass >= 0){ + currentPart.classes.push(ts(inClass+1, x).replace(/\\/g, "")); + inClass = -1; + } + } + + var endAll = function(){ + // at the end of a simple fragment, so wall off the matches + endId(); endTag(); endClass(); + } + + var endPart = function(){ + endAll(); + if(inPseudo >= 0){ + currentPart.pseudos.push({ name: ts(inPseudo+1, x) }); + } + // hint to the selector engine to tell it whether or not it + // needs to do any iteration. Many simple selectors don't, and + // we can avoid significant construction-time work by advising + // the system to skip them + currentPart.loops = ( + currentPart.pseudos.length || + currentPart.attrs.length || + currentPart.classes.length ); + + currentPart.oquery = currentPart.query = ts(pStart, x); // save the full expression as a string + + + // otag/tag are hints to suggest to the system whether or not + // it's an operator or a tag. We save a copy of otag since the + // tag name is cast to upper-case in regular HTML matches. The + // system has a global switch to figure out if the current + // expression needs to be case sensitive or not and it will use + // otag or tag accordingly + currentPart.otag = currentPart.tag = (currentPart["oper"]) ? null : (currentPart.tag || "*"); + + if(currentPart.tag){ + // if we're in a case-insensitive HTML doc, we likely want + // the toUpperCase when matching on element.tagName. If we + // do it here, we can skip the string op per node + // comparison + currentPart.tag = currentPart.tag.toUpperCase(); + } + + // add the part to the list + if(queryParts.length && (queryParts[queryParts.length-1].oper)){ + // operators are always infix, so we remove them from the + // list and attach them to the next match. The evaluator is + // responsible for sorting out how to handle them. + currentPart.infixOper = queryParts.pop(); + currentPart.query = currentPart.infixOper.query + " " + currentPart.query; + /* + console.debug( "swapping out the infix", + currentPart.infixOper, + "and attaching it to", + currentPart); + */ + } + queryParts.push(currentPart); + + currentPart = null; + } + + // iterate over the query, charachter by charachter, building up a + // list of query part objects + for(; lc=cc, cc=query.charAt(x), x < ql; x++){ + // cc: the current character in the match + // lc: the last charachter (if any) + + // someone is trying to escape something, so don't try to match any + // fragments. We assume we're inside a literal. + if(lc == "\\"){ continue; } + if(!currentPart){ // a part was just ended or none has yet been created + // NOTE: I hate all this alloc, but it's shorter than writing tons of if's + pStart = x; + // rules describe full CSS sub-expressions, like: + // #someId + // .className:first-child + // but not: + // thinger > div.howdy[type=thinger] + // the indidual components of the previous query would be + // split into 3 parts that would be represented a structure + // like: + // [ + // { + // query: "thinger", + // tag: "thinger", + // }, + // { + // query: "div.howdy[type=thinger]", + // classes: ["howdy"], + // infixOper: { + // query: ">", + // oper: ">", + // } + // }, + // ] + currentPart = { + query: null, // the full text of the part's rule + pseudos: [], // CSS supports multiple pseud-class matches in a single rule + attrs: [], // CSS supports multi-attribute match, so we need an array + classes: [], // class matches may be additive, e.g.: .thinger.blah.howdy + tag: null, // only one tag... + oper: null, // ...or operator per component. Note that these wind up being exclusive. + id: null, // the id component of a rule + getTag: function(){ + return (caseSensitive) ? this.otag : this.tag; + } + }; + + // if we don't have a part, we assume we're going to start at + // the beginning of a match, which should be a tag name. This + // might fault a little later on, but we detect that and this + // iteration will still be fine. + inTag = x; + } + + if(inBrackets >= 0){ + // look for a the close first + if(cc == "]"){ // if we're in a [...] clause and we end, do assignment + if(!_cp.attr){ + // no attribute match was previously begun, so we + // assume this is an attribute existance match in the + // form of [someAttributeName] + _cp.attr = ts(inBrackets+1, x); + }else{ + // we had an attribute already, so we know that we're + // matching some sort of value, as in [attrName=howdy] + _cp.matchFor = ts((inMatchFor||inBrackets+1), x); + } + var cmf = _cp.matchFor; + if(cmf){ + // try to strip quotes from the matchFor value. We want + // [attrName=howdy] to match the same + // as [attrName = 'howdy' ] + if( (cmf.charAt(0) == '"') || (cmf.charAt(0) == "'") ){ + _cp.matchFor = cmf.slice(1, -1); + } + } + // end the attribute by adding it to the list of attributes. + currentPart.attrs.push(_cp); + _cp = null; // necessary? + inBrackets = inMatchFor = -1; + }else if(cc == "="){ + // if the last char was an operator prefix, make sure we + // record it along with the "=" operator. + var addToCc = ("|~^$*".indexOf(lc) >=0 ) ? lc : ""; + _cp.type = addToCc+cc; + _cp.attr = ts(inBrackets+1, x-addToCc.length); + inMatchFor = x+1; + } + // now look for other clause parts + }else if(inParens >= 0){ + // if we're in a parenthetical expression, we need to figure + // out if it's attached to a pseduo-selector rule like + // :nth-child(1) + if(cc == ")"){ + if(inPseudo >= 0){ + _cp.value = ts(inParens+1, x); + } + inPseudo = inParens = -1; + } + }else if(cc == "#"){ + // start of an ID match + endAll(); + inId = x+1; + }else if(cc == "."){ + // start of a class match + endAll(); + inClass = x; + }else if(cc == ":"){ + // start of a pseudo-selector match + endAll(); + inPseudo = x; + }else if(cc == "["){ + // start of an attribute match. + endAll(); + inBrackets = x; + // provide a new structure for the attribute match to fill-in + _cp = { + /*===== + attr: null, type: null, matchFor: null + =====*/ + }; + }else if(cc == "("){ + // we really only care if we've entered a parenthetical + // expression if we're already inside a pseudo-selector match + if(inPseudo >= 0){ + // provide a new structure for the pseudo match to fill-in + _cp = { + name: ts(inPseudo+1, x), + value: null + } + currentPart.pseudos.push(_cp); + } + inParens = x; + }else if( + (cc == " ") && + // if it's a space char and the last char is too, consume the + // current one without doing more work + (lc != cc) + ){ + endPart(); + } + } + return queryParts; + }; + + + //////////////////////////////////////////////////////////////////////// + // DOM query infrastructure + //////////////////////////////////////////////////////////////////////// + + var agree = function(first, second){ + // the basic building block of the yes/no chaining system. agree(f1, + // f2) generates a new function which returns the boolean results of + // both of the passed functions to a single logical-anded result. If + // either are not possed, the other is used exclusively. + if(!first){ return second; } + if(!second){ return first; } + + return function(){ + return first.apply(window, arguments) && second.apply(window, arguments); + } + }; + + var getArr = function(i, arr){ + // helps us avoid array alloc when we don't need it + var r = arr||[]; // FIXME: should this be 'new d._NodeListCtor()' ? + if(i){ r.push(i); } + return r; + }; + + var _isElement = function(n){ return (1 == n.nodeType); }; + + // FIXME: need to coalesce _getAttr with defaultGetter + var blank = ""; + var _getAttr = function(elem, attr){ + if(!elem){ return blank; } + if(attr == "class"){ + return elem.className || blank; + } + if(attr == "for"){ + return elem.htmlFor || blank; + } + if(attr == "style"){ + return elem.style.cssText || blank; + } + return (caseSensitive ? elem.getAttribute(attr) : elem.getAttribute(attr, 2)) || blank; + }; + + var attrs = { + "*=": function(attr, value){ + return function(elem){ + // E[foo*="bar"] + // an E element whose "foo" attribute value contains + // the substring "bar" + return (_getAttr(elem, attr).indexOf(value)>=0); + } + }, + "^=": function(attr, value){ + // E[foo^="bar"] + // an E element whose "foo" attribute value begins exactly + // with the string "bar" + return function(elem){ + return (_getAttr(elem, attr).indexOf(value)==0); + } + }, + "$=": function(attr, value){ + // E[foo$="bar"] + // an E element whose "foo" attribute value ends exactly + // with the string "bar" + var tval = " "+value; + return function(elem){ + var ea = " "+_getAttr(elem, attr); + return (ea.lastIndexOf(value)==(ea.length-value.length)); + } + }, + "~=": function(attr, value){ + // E[foo~="bar"] + // an E element whose "foo" attribute value is a list of + // space-separated values, one of which is exactly equal + // to "bar" + + // return "[contains(concat(' ',@"+attr+",' '), ' "+ value +" ')]"; + var tval = " "+value+" "; + return function(elem){ + var ea = " "+_getAttr(elem, attr)+" "; + return (ea.indexOf(tval)>=0); + } + }, + "|=": function(attr, value){ + // E[hreflang|="en"] + // an E element whose "hreflang" attribute has a + // hyphen-separated list of values beginning (from the + // left) with "en" + var valueDash = " "+value+"-"; + return function(elem){ + var ea = " "+_getAttr(elem, attr); + return ( + (ea == value) || + (ea.indexOf(valueDash)==0) + ); + } + }, + "=": function(attr, value){ + return function(elem){ + return (_getAttr(elem, attr) == value); + } + } + }; + + // avoid testing for node type if we can. Defining this in the negative + // here to avoid negation in the fast path. + var _noNES = (typeof getDoc().firstChild.nextElementSibling == "undefined"); + var _ns = !_noNES ? "nextElementSibling" : "nextSibling"; + var _ps = !_noNES ? "previousElementSibling" : "previousSibling"; + var _simpleNodeTest = (_noNES ? _isElement : yesman); + + var _lookLeft = function(node){ + // look left + while(node = node[_ps]){ + if(_simpleNodeTest(node)){ return false; } + } + return true; + }; + + var _lookRight = function(node){ + // look right + while(node = node[_ns]){ + if(_simpleNodeTest(node)){ return false; } + } + return true; + }; + + var getNodeIndex = function(node){ + var root = node.parentNode; + var i = 0, + tret = root[childNodesName], + ci = (node["_i"]||-1), + cl = (root["_l"]||-1); + + if(!tret){ return -1; } + var l = tret.length; + + // we calcuate the parent length as a cheap way to invalidate the + // cache. It's not 100% accurate, but it's much more honest than what + // other libraries do + if( cl == l && ci >= 0 && cl >= 0 ){ + // if it's legit, tag and release + return ci; + } + + // else re-key things + root["_l"] = l; + ci = -1; + for(var te = root["firstElementChild"]||root["firstChild"]; te; te = te[_ns]){ + if(_simpleNodeTest(te)){ + te["_i"] = ++i; + if(node === te){ + // NOTE: + // shortcuting the return at this step in indexing works + // very well for benchmarking but we avoid it here since + // it leads to potential O(n^2) behavior in sequential + // getNodexIndex operations on a previously un-indexed + // parent. We may revisit this at a later time, but for + // now we just want to get the right answer more often + // than not. + ci = i; + } + } + } + return ci; + }; + + var isEven = function(elem){ + return !((getNodeIndex(elem)) % 2); + }; + + var isOdd = function(elem){ + return ((getNodeIndex(elem)) % 2); + }; + + var pseudos = { + "checked": function(name, condition){ + return function(elem){ + return !!("checked" in elem ? elem.checked : elem.selected); + } + }, + "first-child": function(){ return _lookLeft; }, + "last-child": function(){ return _lookRight; }, + "only-child": function(name, condition){ + return function(node){ + if(!_lookLeft(node)){ return false; } + if(!_lookRight(node)){ return false; } + return true; + }; + }, + "empty": function(name, condition){ + return function(elem){ + // DomQuery and jQuery get this wrong, oddly enough. + // The CSS 3 selectors spec is pretty explicit about it, too. + var cn = elem.childNodes; + var cnl = elem.childNodes.length; + // if(!cnl){ return true; } + for(var x=cnl-1; x >= 0; x--){ + var nt = cn[x].nodeType; + if((nt === 1)||(nt == 3)){ return false; } + } + return true; + } + }, + "contains": function(name, condition){ + var cz = condition.charAt(0); + if( cz == '"' || cz == "'" ){ //remove quote + condition = condition.slice(1, -1); + } + return function(elem){ + return (elem.innerHTML.indexOf(condition) >= 0); + } + }, + "not": function(name, condition){ + var p = getQueryParts(condition)[0]; + var ignores = { el: 1 }; + if(p.tag != "*"){ + ignores.tag = 1; + } + if(!p.classes.length){ + ignores.classes = 1; + } + var ntf = getSimpleFilterFunc(p, ignores); + return function(elem){ + return (!ntf(elem)); + } + }, + "nth-child": function(name, condition){ + var pi = parseInt; + // avoid re-defining function objects if we can + if(condition == "odd"){ + return isOdd; + }else if(condition == "even"){ + return isEven; + } + // FIXME: can we shorten this? + if(condition.indexOf("n") != -1){ + var tparts = condition.split("n", 2); + var pred = tparts[0] ? ((tparts[0] == '-') ? -1 : pi(tparts[0])) : 1; + var idx = tparts[1] ? pi(tparts[1]) : 0; + var lb = 0, ub = -1; + if(pred > 0){ + if(idx < 0){ + idx = (idx % pred) && (pred + (idx % pred)); + }else if(idx>0){ + if(idx >= pred){ + lb = idx - idx % pred; + } + idx = idx % pred; + } + }else if(pred<0){ + pred *= -1; + // idx has to be greater than 0 when pred is negative; + // shall we throw an error here? + if(idx > 0){ + ub = idx; + idx = idx % pred; + } + } + if(pred > 0){ + return function(elem){ + var i = getNodeIndex(elem); + return (i>=lb) && (ub<0 || i<=ub) && ((i % pred) == idx); + } + }else{ + condition = idx; + } + } + var ncount = pi(condition); + return function(elem){ + return (getNodeIndex(elem) == ncount); + } + } + }; + + var defaultGetter = (d.isIE) ? function(cond){ + var clc = cond.toLowerCase(); + if(clc == "class"){ cond = "className"; } + return function(elem){ + return (caseSensitive ? elem.getAttribute(cond) : elem[cond]||elem[clc]); + } + } : function(cond){ + return function(elem){ + return (elem && elem.getAttribute && elem.hasAttribute(cond)); + } + }; + + var getSimpleFilterFunc = function(query, ignores){ + // generates a node tester function based on the passed query part. The + // query part is one of the structures generatd by the query parser + // when it creates the query AST. The "ignores" object specifies which + // (if any) tests to skip, allowing the system to avoid duplicating + // work where it may have already been taken into account by other + // factors such as how the nodes to test were fetched in the first + // place + if(!query){ return yesman; } + ignores = ignores||{}; + + var ff = null; + + if(!("el" in ignores)){ + ff = agree(ff, _isElement); + } + + if(!("tag" in ignores)){ + if(query.tag != "*"){ + ff = agree(ff, function(elem){ + return (elem && (elem.tagName == query.getTag())); + }); + } + } + + if(!("classes" in ignores)){ + each(query.classes, function(cname, idx, arr){ + // get the class name + /* + var isWildcard = cname.charAt(cname.length-1) == "*"; + if(isWildcard){ + cname = cname.substr(0, cname.length-1); + } + // I dislike the regex thing, even if memozied in a cache, but it's VERY short + var re = new RegExp("(?:^|\\s)" + cname + (isWildcard ? ".*" : "") + "(?:\\s|$)"); + */ + var re = new RegExp("(?:^|\\s)" + cname + "(?:\\s|$)"); + ff = agree(ff, function(elem){ + return re.test(elem.className); + }); + ff.count = idx; + }); + } + + if(!("pseudos" in ignores)){ + each(query.pseudos, function(pseudo){ + var pn = pseudo.name; + if(pseudos[pn]){ + ff = agree(ff, pseudos[pn](pn, pseudo.value)); + } + }); + } + + if(!("attrs" in ignores)){ + each(query.attrs, function(attr){ + var matcher; + var a = attr.attr; + // type, attr, matchFor + if(attr.type && attrs[attr.type]){ + matcher = attrs[attr.type](a, attr.matchFor); + }else if(a.length){ + matcher = defaultGetter(a); + } + if(matcher){ + ff = agree(ff, matcher); + } + }); + } + + if(!("id" in ignores)){ + if(query.id){ + ff = agree(ff, function(elem){ + return (!!elem && (elem.id == query.id)); + }); + } + } + + if(!ff){ + if(!("default" in ignores)){ + ff = yesman; + } + } + return ff; + }; + + var _nextSibling = function(filterFunc){ + return function(node, ret, bag){ + while(node = node[_ns]){ + if(_noNES && (!_isElement(node))){ continue; } + if( + (!bag || _isUnique(node, bag)) && + filterFunc(node) + ){ + ret.push(node); + } + break; + } + return ret; + } + }; + + var _nextSiblings = function(filterFunc){ + return function(root, ret, bag){ + var te = root[_ns]; + while(te){ + if(_simpleNodeTest(te)){ + if(bag && !_isUnique(te, bag)){ + break; + } + if(filterFunc(te)){ + ret.push(te); + } + } + te = te[_ns]; + } + return ret; + } + }; + + // get an array of child *elements*, skipping text and comment nodes + var _childElements = function(filterFunc){ + filterFunc = filterFunc||yesman; + return function(root, ret, bag){ + // get an array of child elements, skipping text and comment nodes + var te, x = 0, tret = root[childNodesName]; + while(te = tret[x++]){ + if( + _simpleNodeTest(te) && + (!bag || _isUnique(te, bag)) && + (filterFunc(te, x)) + ){ + ret.push(te); + } + } + return ret; + }; + }; + + /* + // thanks, Dean! + var itemIsAfterRoot = d.isIE ? function(item, root){ + return (item.sourceIndex > root.sourceIndex); + } : function(item, root){ + return (item.compareDocumentPosition(root) == 2); + }; + */ + + // test to see if node is below root + var _isDescendant = function(node, root){ + var pn = node.parentNode; + while(pn){ + if(pn == root){ + break; + } + pn = pn.parentNode; + } + return !!pn; + }; + + var _getElementsFuncCache = {}; + + var getElementsFunc = function(query){ + var retFunc = _getElementsFuncCache[query.query]; + // if we've got a cached dispatcher, just use that + if(retFunc){ return retFunc; } + // else, generate a new on + + // NOTE: + // this function returns a function that searches for nodes and + // filters them. The search may be specialized by infix operators + // (">", "~", or "+") else it will default to searching all + // descendants (the " " selector). Once a group of children is + // founde, a test function is applied to weed out the ones we + // don't want. Many common cases can be fast-pathed. We spend a + // lot of cycles to create a dispatcher that doesn't do more work + // than necessary at any point since, unlike this function, the + // dispatchers will be called every time. The logic of generating + // efficient dispatchers looks like this in pseudo code: + // + // # if it's a purely descendant query (no ">", "+", or "~" modifiers) + // if infixOperator == " ": + // if only(id): + // return def(root): + // return d.byId(id, root); + // + // elif id: + // return def(root): + // return filter(d.byId(id, root)); + // + // elif cssClass && getElementsByClassName: + // return def(root): + // return filter(root.getElementsByClassName(cssClass)); + // + // elif only(tag): + // return def(root): + // return root.getElementsByTagName(tagName); + // + // else: + // # search by tag name, then filter + // return def(root): + // return filter(root.getElementsByTagName(tagName||"*")); + // + // elif infixOperator == ">": + // # search direct children + // return def(root): + // return filter(root.children); + // + // elif infixOperator == "+": + // # search next sibling + // return def(root): + // return filter(root.nextElementSibling); + // + // elif infixOperator == "~": + // # search rightward siblings + // return def(root): + // return filter(nextSiblings(root)); + + var io = query.infixOper; + var oper = (io ? io.oper : ""); + // the default filter func which tests for all conditions in the query + // part. This is potentially inefficient, so some optimized paths may + // re-define it to test fewer things. + var filterFunc = getSimpleFilterFunc(query, { el: 1 }); + var qt = query.tag; + var wildcardTag = ("*" == qt); + var ecs = getDoc()["getElementsByClassName"]; + + if(!oper){ + // if there's no infix operator, then it's a descendant query. ID + // and "elements by class name" variants can be accelerated so we + // call them out explicitly: + if(query.id){ + // testing shows that the overhead of yesman() is acceptable + // and can save us some bytes vs. re-defining the function + // everywhere. + filterFunc = (!query.loops && wildcardTag) ? + yesman : + getSimpleFilterFunc(query, { el: 1, id: 1 }); + + retFunc = function(root, arr){ + var te = d.byId(query.id, (root.ownerDocument||root)); + if(!te || !filterFunc(te)){ return; } + if(9 == root.nodeType){ // if root's a doc, we just return directly + return getArr(te, arr); + }else{ // otherwise check ancestry + if(_isDescendant(te, root)){ + return getArr(te, arr); + } + } + } + }else if( + ecs && + // isAlien check. Workaround for Prototype.js being totally evil/dumb. + /\{\s*\[native code\]\s*\}/.test(String(ecs)) && + query.classes.length && + !cssCaseBug + ){ + // it's a class-based query and we've got a fast way to run it. + + // ignore class and ID filters since we will have handled both + filterFunc = getSimpleFilterFunc(query, { el: 1, classes: 1, id: 1 }); + var classesString = query.classes.join(" "); + retFunc = function(root, arr, bag){ + var ret = getArr(0, arr), te, x=0; + var tret = root.getElementsByClassName(classesString); + while((te = tret[x++])){ + if(filterFunc(te, root) && _isUnique(te, bag)){ + ret.push(te); + } + } + return ret; + }; + + }else if(!wildcardTag && !query.loops){ + // it's tag only. Fast-path it. + retFunc = function(root, arr, bag){ + var ret = getArr(0, arr), te, x=0; + var tret = root.getElementsByTagName(query.getTag()); + while((te = tret[x++])){ + if(_isUnique(te, bag)){ + ret.push(te); + } + } + return ret; + }; + }else{ + // the common case: + // a descendant selector without a fast path. By now it's got + // to have a tag selector, even if it's just "*" so we query + // by that and filter + filterFunc = getSimpleFilterFunc(query, { el: 1, tag: 1, id: 1 }); + retFunc = function(root, arr, bag){ + var ret = getArr(0, arr), te, x=0; + // we use getTag() to avoid case sensitivity issues + var tret = root.getElementsByTagName(query.getTag()); + while((te = tret[x++])){ + if(filterFunc(te, root) && _isUnique(te, bag)){ + ret.push(te); + } + } + return ret; + }; + } + }else{ + // the query is scoped in some way. Instead of querying by tag we + // use some other collection to find candidate nodes + var skipFilters = { el: 1 }; + if(wildcardTag){ + skipFilters.tag = 1; + } + filterFunc = getSimpleFilterFunc(query, skipFilters); + if("+" == oper){ + retFunc = _nextSibling(filterFunc); + }else if("~" == oper){ + retFunc = _nextSiblings(filterFunc); + }else if(">" == oper){ + retFunc = _childElements(filterFunc); + } + } + // cache it and return + return _getElementsFuncCache[query.query] = retFunc; + }; + + var filterDown = function(root, queryParts){ + // NOTE: + // this is the guts of the DOM query system. It takes a list of + // parsed query parts and a root and finds children which match + // the selector represented by the parts + var candidates = getArr(root), qp, x, te, qpl = queryParts.length, bag, ret; + + for(var i = 0; i < qpl; i++){ + ret = []; + qp = queryParts[i]; + x = candidates.length - 1; + if(x > 0){ + // if we have more than one root at this level, provide a new + // hash to use for checking group membership but tell the + // system not to post-filter us since we will already have been + // gauranteed to be unique + bag = {}; + ret.nozip = true; + } + var gef = getElementsFunc(qp); + for(var j = 0; (te = candidates[j]); j++){ + // for every root, get the elements that match the descendant + // selector, adding them to the "ret" array and filtering them + // via membership in this level's bag. If there are more query + // parts, then this level's return will be used as the next + // level's candidates + gef(te, ret, bag); + } + if(!ret.length){ break; } + candidates = ret; + } + return ret; + }; + + //////////////////////////////////////////////////////////////////////// + // the query runner + //////////////////////////////////////////////////////////////////////// + + // these are the primary caches for full-query results. The query + // dispatcher functions are generated then stored here for hash lookup in + // the future + var _queryFuncCacheDOM = {}, + _queryFuncCacheQSA = {}; + + // this is the second level of spliting, from full-length queries (e.g., + // "div.foo .bar") into simple query expressions (e.g., ["div.foo", + // ".bar"]) + var getStepQueryFunc = function(query){ + var qparts = getQueryParts(trim(query)); + + // if it's trivial, avoid iteration and zipping costs + if(qparts.length == 1){ + // we optimize this case here to prevent dispatch further down the + // chain, potentially slowing things down. We could more elegantly + // handle this in filterDown(), but it's slower for simple things + // that need to be fast (e.g., "#someId"). + var tef = getElementsFunc(qparts[0]); + return function(root){ + var r = tef(root, new qlc()); + if(r){ r.nozip = true; } + return r; + } + } + + // otherwise, break it up and return a runner that iterates over the parts recursively + return function(root){ + return filterDown(root, qparts); + } + }; + + // NOTES: + // * we can't trust QSA for anything but document-rooted queries, so + // caching is split into DOM query evaluators and QSA query evaluators + // * caching query results is dirty and leak-prone (or, at a minimum, + // prone to unbounded growth). Other toolkits may go this route, but + // they totally destroy their own ability to manage their memory + // footprint. If we implement it, it should only ever be with a fixed + // total element reference # limit and an LRU-style algorithm since JS + // has no weakref support. Caching compiled query evaluators is also + // potentially problematic, but even on large documents the size of the + // query evaluators is often < 100 function objects per evaluator (and + // LRU can be applied if it's ever shown to be an issue). + // * since IE's QSA support is currently only for HTML documents and even + // then only in IE 8's "standards mode", we have to detect our dispatch + // route at query time and keep 2 separate caches. Ugg. + + // we need to determine if we think we can run a given query via + // querySelectorAll or if we'll need to fall back on DOM queries to get + // there. We need a lot of information about the environment and the query + // to make the determiniation (e.g. does it support QSA, does the query in + // question work in the native QSA impl, etc.). + var nua = navigator.userAgent; + // some versions of Safari provided QSA, but it was buggy and crash-prone. + // We need te detect the right "internal" webkit version to make this work. + var wk = "WebKit/"; + var is525 = ( + d.isWebKit && + (nua.indexOf(wk) > 0) && + (parseFloat(nua.split(wk)[1]) > 528) + ); + + // IE QSA queries may incorrectly include comment nodes, so we throw the + // zipping function into "remove" comments mode instead of the normal "skip + // it" which every other QSA-clued browser enjoys + var noZip = d.isIE ? "commentStrip" : "nozip"; + + var qsa = "querySelectorAll"; + var qsaAvail = ( + !!getDoc()[qsa] && + // see #5832 + (!d.isSafari || (d.isSafari > 3.1) || is525 ) + ); + + //Don't bother with n+3 type of matches, IE complains if we modify those. + var infixSpaceRe = /n\+\d|([^ ])?([>~+])([^ =])?/g; + var infixSpaceFunc = function(match, pre, ch, post) { + return ch ? (pre ? pre + " " : "") + ch + (post ? " " + post : "") : /*n+3*/ match; + }; + + var getQueryFunc = function(query, forceDOM){ + //Normalize query. The CSS3 selectors spec allows for omitting spaces around + //infix operators, >, ~ and + + //Do the work here since detection for spaces is used as a simple "not use QSA" + //test below. + query = query.replace(infixSpaceRe, infixSpaceFunc); + + if(qsaAvail){ + // if we've got a cached variant and we think we can do it, run it! + var qsaCached = _queryFuncCacheQSA[query]; + if(qsaCached && !forceDOM){ return qsaCached; } + } + + // else if we've got a DOM cached variant, assume that we already know + // all we need to and use it + var domCached = _queryFuncCacheDOM[query]; + if(domCached){ return domCached; } + + // TODO: + // today we're caching DOM and QSA branches separately so we + // recalc useQSA every time. If we had a way to tag root+query + // efficiently, we'd be in good shape to do a global cache. + + var qcz = query.charAt(0); + var nospace = (-1 == query.indexOf(" ")); + + // byId searches are wicked fast compared to QSA, even when filtering + // is required + if( (query.indexOf("#") >= 0) && (nospace) ){ + forceDOM = true; + } + + var useQSA = ( + qsaAvail && (!forceDOM) && + // as per CSS 3, we can't currently start w/ combinator: + // http://www.w3.org/TR/css3-selectors/#w3cselgrammar + (specials.indexOf(qcz) == -1) && + // IE's QSA impl sucks on pseudos + (!d.isIE || (query.indexOf(":") == -1)) && + + (!(cssCaseBug && (query.indexOf(".") >= 0))) && + + // FIXME: + // need to tighten up browser rules on ":contains" and "|=" to + // figure out which aren't good + // Latest webkit (around 531.21.8) does not seem to do well with :checked on option + // elements, even though according to spec, selected options should + // match :checked. So go nonQSA for it: + // http://bugs.dojotoolkit.org/ticket/5179 + (query.indexOf(":contains") == -1) && (query.indexOf(":checked") == -1) && + (query.indexOf("|=") == -1) // some browsers don't grok it + ); + + // TODO: + // if we've got a descendant query (e.g., "> .thinger" instead of + // just ".thinger") in a QSA-able doc, but are passed a child as a + // root, it should be possible to give the item a synthetic ID and + // trivially rewrite the query to the form "#synid > .thinger" to + // use the QSA branch + + + if(useQSA){ + var tq = (specials.indexOf(query.charAt(query.length-1)) >= 0) ? + (query + " *") : query; + return _queryFuncCacheQSA[query] = function(root){ + try{ + // the QSA system contains an egregious spec bug which + // limits us, effectively, to only running QSA queries over + // entire documents. See: + // http://ejohn.org/blog/thoughts-on-queryselectorall/ + // despite this, we can also handle QSA runs on simple + // selectors, but we don't want detection to be expensive + // so we're just checking for the presence of a space char + // right now. Not elegant, but it's cheaper than running + // the query parser when we might not need to + if(!((9 == root.nodeType) || nospace)){ throw ""; } + var r = root[qsa](tq); + // skip expensive duplication checks and just wrap in a NodeList + r[noZip] = true; + return r; + }catch(e){ + // else run the DOM branch on this query, ensuring that we + // default that way in the future + return getQueryFunc(query, true)(root); + } + } + }else{ + // DOM branch + var parts = query.split(/\s*,\s*/); + return _queryFuncCacheDOM[query] = ((parts.length < 2) ? + // if not a compound query (e.g., ".foo, .bar"), cache and return a dispatcher + getStepQueryFunc(query) : + // if it *is* a complex query, break it up into its + // constituent parts and return a dispatcher that will + // merge the parts when run + function(root){ + var pindex = 0, // avoid array alloc for every invocation + ret = [], + tp; + while((tp = parts[pindex++])){ + ret = ret.concat(getStepQueryFunc(tp)(root)); + } + return ret; + } + ); + } + }; + + var _zipIdx = 0; + + // NOTE: + // this function is Moo inspired, but our own impl to deal correctly + // with XML in IE + var _nodeUID = d.isIE ? function(node){ + if(caseSensitive){ + // XML docs don't have uniqueID on their nodes + return (node.getAttribute("_uid") || node.setAttribute("_uid", ++_zipIdx) || _zipIdx); + + }else{ + return node.uniqueID; + } + } : + function(node){ + return (node._uid || (node._uid = ++_zipIdx)); + }; + + // determine if a node in is unique in a "bag". In this case we don't want + // to flatten a list of unique items, but rather just tell if the item in + // question is already in the bag. Normally we'd just use hash lookup to do + // this for us but IE's DOM is busted so we can't really count on that. On + // the upside, it gives us a built in unique ID function. + var _isUnique = function(node, bag){ + if(!bag){ return 1; } + var id = _nodeUID(node); + if(!bag[id]){ return bag[id] = 1; } + return 0; + }; + + // attempt to efficiently determine if an item in a list is a dupe, + // returning a list of "uniques", hopefully in doucment order + var _zipIdxName = "_zipIdx"; + var _zip = function(arr){ + if(arr && arr.nozip){ + return (qlc._wrap) ? qlc._wrap(arr) : arr; + } + // var ret = new d._NodeListCtor(); + var ret = new qlc(); + if(!arr || !arr.length){ return ret; } + if(arr[0]){ + ret.push(arr[0]); + } + if(arr.length < 2){ return ret; } + + _zipIdx++; + + // we have to fork here for IE and XML docs because we can't set + // expandos on their nodes (apparently). *sigh* + if(d.isIE && caseSensitive){ + var szidx = _zipIdx+""; + arr[0].setAttribute(_zipIdxName, szidx); + for(var x = 1, te; te = arr[x]; x++){ + if(arr[x].getAttribute(_zipIdxName) != szidx){ + ret.push(te); + } + te.setAttribute(_zipIdxName, szidx); + } + }else if(d.isIE && arr.commentStrip){ + try{ + for(var x = 1, te; te = arr[x]; x++){ + if(_isElement(te)){ + ret.push(te); + } + } + }catch(e){ /* squelch */ } + }else{ + if(arr[0]){ arr[0][_zipIdxName] = _zipIdx; } + for(var x = 1, te; te = arr[x]; x++){ + if(arr[x][_zipIdxName] != _zipIdx){ + ret.push(te); + } + te[_zipIdxName] = _zipIdx; + } + } + return ret; + }; + + // the main executor + d.query = function(/*String*/ query, /*String|DOMNode?*/ root){ + // summary: + // Returns nodes which match the given CSS3 selector, searching the + // entire document by default but optionally taking a node to scope + // the search by. Returns an instance of dojo.NodeList. + // description: + // dojo.query() is the swiss army knife of DOM node manipulation in + // Dojo. Much like Prototype's "$$" (bling-bling) function or JQuery's + // "$" function, dojo.query provides robust, high-performance + // CSS-based node selector support with the option of scoping searches + // to a particular sub-tree of a document. + // + // Supported Selectors: + // -------------------- + // + // dojo.query() supports a rich set of CSS3 selectors, including: + // + // * class selectors (e.g., `.foo`) + // * node type selectors like `span` + // * ` ` descendant selectors + // * `>` child element selectors + // * `#foo` style ID selectors + // * `*` universal selector + // * `~`, the immediately preceeded-by sibling selector + // * `+`, the preceeded-by sibling selector + // * attribute queries: + // | * `[foo]` attribute presence selector + // | * `[foo='bar']` attribute value exact match + // | * `[foo~='bar']` attribute value list item match + // | * `[foo^='bar']` attribute start match + // | * `[foo$='bar']` attribute end match + // | * `[foo*='bar']` attribute substring match + // * `:first-child`, `:last-child`, and `:only-child` positional selectors + // * `:empty` content emtpy selector + // * `:checked` pseudo selector + // * `:nth-child(n)`, `:nth-child(2n+1)` style positional calculations + // * `:nth-child(even)`, `:nth-child(odd)` positional selectors + // * `:not(...)` negation pseudo selectors + // + // Any legal combination of these selectors will work with + // `dojo.query()`, including compound selectors ("," delimited). + // Very complex and useful searches can be constructed with this + // palette of selectors and when combined with functions for + // manipulation presented by dojo.NodeList, many types of DOM + // manipulation operations become very straightforward. + // + // Unsupported Selectors: + // ---------------------- + // + // While dojo.query handles many CSS3 selectors, some fall outside of + // what's resaonable for a programmatic node querying engine to + // handle. Currently unsupported selectors include: + // + // * namespace-differentiated selectors of any form + // * all `::` pseduo-element selectors + // * certain pseduo-selectors which don't get a lot of day-to-day use: + // | * `:root`, `:lang()`, `:target`, `:focus` + // * all visual and state selectors: + // | * `:root`, `:active`, `:hover`, `:visisted`, `:link`, + // `:enabled`, `:disabled` + // * `:*-of-type` pseudo selectors + // + // dojo.query and XML Documents: + // ----------------------------- + // + // `dojo.query` (as of dojo 1.2) supports searching XML documents + // in a case-sensitive manner. If an HTML document is served with + // a doctype that forces case-sensitivity (e.g., XHTML 1.1 + // Strict), dojo.query() will detect this and "do the right + // thing". Case sensitivity is dependent upon the document being + // searched and not the query used. It is therefore possible to + // use case-sensitive queries on strict sub-documents (iframes, + // etc.) or XML documents while still assuming case-insensitivity + // for a host/root document. + // + // Non-selector Queries: + // --------------------- + // + // If something other than a String is passed for the query, + // `dojo.query` will return a new `dojo.NodeList` instance + // constructed from that parameter alone and all further + // processing will stop. This means that if you have a reference + // to a node or NodeList, you can quickly construct a new NodeList + // from the original by calling `dojo.query(node)` or + // `dojo.query(list)`. + // + // query: + // The CSS3 expression to match against. For details on the syntax of + // CSS3 selectors, see <http://www.w3.org/TR/css3-selectors/#selectors> + // root: + // A DOMNode (or node id) to scope the search from. Optional. + // returns: dojo.NodeList + // An instance of `dojo.NodeList`. Many methods are available on + // NodeLists for searching, iterating, manipulating, and handling + // events on the matched nodes in the returned list. + // example: + // search the entire document for elements with the class "foo": + // | dojo.query(".foo"); + // these elements will match: + // | <span class="foo"></span> + // | <span class="foo bar"></span> + // | <p class="thud foo"></p> + // example: + // search the entire document for elements with the classes "foo" *and* "bar": + // | dojo.query(".foo.bar"); + // these elements will match: + // | <span class="foo bar"></span> + // while these will not: + // | <span class="foo"></span> + // | <p class="thud foo"></p> + // example: + // find `<span>` elements which are descendants of paragraphs and + // which have a "highlighted" class: + // | dojo.query("p span.highlighted"); + // the innermost span in this fragment matches: + // | <p class="foo"> + // | <span>... + // | <span class="highlighted foo bar">...</span> + // | </span> + // | </p> + // example: + // set an "odd" class on all odd table rows inside of the table + // `#tabular_data`, using the `>` (direct child) selector to avoid + // affecting any nested tables: + // | dojo.query("#tabular_data > tbody > tr:nth-child(odd)").addClass("odd"); + // example: + // remove all elements with the class "error" from the document + // and store them in a list: + // | var errors = dojo.query(".error").orphan(); + // example: + // add an onclick handler to every submit button in the document + // which causes the form to be sent via Ajax instead: + // | dojo.query("input[type='submit']").onclick(function(e){ + // | dojo.stopEvent(e); // prevent sending the form + // | var btn = e.target; + // | dojo.xhrPost({ + // | form: btn.form, + // | load: function(data){ + // | // replace the form with the response + // | var div = dojo.doc.createElement("div"); + // | dojo.place(div, btn.form, "after"); + // | div.innerHTML = data; + // | dojo.style(btn.form, "display", "none"); + // | } + // | }); + // | }); + + //Set list constructor to desired value. This can change + //between calls, so always re-assign here. + qlc = d._NodeListCtor; + + if(!query){ + return new qlc(); + } + + if(query.constructor == qlc){ + return query; + } + if(typeof query != "string"){ // inline'd type check + return new qlc(query); // dojo.NodeList + } + if(typeof root == "string"){ // inline'd type check + root = d.byId(root); + if(!root){ return new qlc(); } + } + + root = root||getDoc(); + var od = root.ownerDocument||root.documentElement; + + // throw the big case sensitivity switch + + // NOTE: + // Opera in XHTML mode doesn't detect case-sensitivity correctly + // and it's not clear that there's any way to test for it + caseSensitive = (root.contentType && root.contentType=="application/xml") || + (d.isOpera && (root.doctype || od.toString() == "[object XMLDocument]")) || + (!!od) && + (d.isIE ? od.xml : (root.xmlVersion||od.xmlVersion)); + + // NOTE: + // adding "true" as the 2nd argument to getQueryFunc is useful for + // testing the DOM branch without worrying about the + // behavior/performance of the QSA branch. + var r = getQueryFunc(query)(root); + + // FIXME: + // need to investigate this branch WRT #8074 and #8075 + if(r && r.nozip && !qlc._wrap){ + return r; + } + return _zip(r); // dojo.NodeList + } + + // FIXME: need to add infrastructure for post-filtering pseudos, ala :last + d.query.pseudos = pseudos; + + // one-off function for filtering a NodeList based on a simple selector + d._filterQueryResult = function(nodeList, simpleFilter){ + var tmpNodeList = new d._NodeListCtor(); + var filterFunc = getSimpleFilterFunc(getQueryParts(simpleFilter)[0]); + for(var x = 0, te; te = nodeList[x]; x++){ + if(filterFunc(te)){ tmpNodeList.push(te); } + } + return tmpNodeList; + } })(this["queryPortability"]||this["acme"]||dojo); + +/* +*/ + } diff --git a/lib/dojo/_base/window.js b/lib/dojo/_base/window.js index 44239d92c..5c6e2e952 100644 --- a/lib/dojo/_base/window.js +++ b/lib/dojo/_base/window.js @@ -5,45 +5,104 @@ */ -if(!dojo._hasResource["dojo._base.window"]){ -dojo._hasResource["dojo._base.window"]=true; +if(!dojo._hasResource["dojo._base.window"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo._base.window"] = true; dojo.provide("dojo._base.window"); -dojo.doc=window["document"]||null; -dojo.body=function(){ -return dojo.doc.body||dojo.doc.getElementsByTagName("body")[0]; -}; -dojo.setContext=function(_1,_2){ -dojo.global=_1; -dojo.doc=_2; -}; -dojo.withGlobal=function(_3,_4,_5,_6){ -var _7=dojo.global; -try{ -dojo.global=_3; -return dojo.withDoc.call(null,_3.document,_4,_5,_6); + +/*===== +dojo.doc = { + // summary: + // Alias for the current document. 'dojo.doc' can be modified + // for temporary context shifting. Also see dojo.withDoc(). + // description: + // Refer to dojo.doc rather + // than referring to 'window.document' to ensure your code runs + // correctly in managed contexts. + // example: + // | n.appendChild(dojo.doc.createElement('div')); } -finally{ -dojo.global=_7; +=====*/ +dojo.doc = window["document"] || null; + +dojo.body = function(){ + // summary: + // Return the body element of the document + // return the body object associated with dojo.doc + // example: + // | dojo.body().appendChild(dojo.doc.createElement('div')); + + // Note: document.body is not defined for a strict xhtml document + // Would like to memoize this, but dojo.doc can change vi dojo.withDoc(). + return dojo.doc.body || dojo.doc.getElementsByTagName("body")[0]; // Node } + +dojo.setContext = function(/*Object*/globalObject, /*DocumentElement*/globalDocument){ + // summary: + // changes the behavior of many core Dojo functions that deal with + // namespace and DOM lookup, changing them to work in a new global + // context (e.g., an iframe). The varibles dojo.global and dojo.doc + // are modified as a result of calling this function and the result of + // `dojo.body()` likewise differs. + dojo.global = globalObject; + dojo.doc = globalDocument; }; -dojo.withDoc=function(_8,_9,_a,_b){ -var _c=dojo.doc,_d=dojo._bodyLtr,_e=dojo.isQuirks; -try{ -dojo.doc=_8; -delete dojo._bodyLtr; -dojo.isQuirks=dojo.doc.compatMode=="BackCompat"; -if(_a&&typeof _9=="string"){ -_9=_a[_9]; -} -return _9.apply(_a,_b||[]); -} -finally{ -dojo.doc=_c; -delete dojo._bodyLtr; -if(_d!==undefined){ -dojo._bodyLtr=_d; -} -dojo.isQuirks=_e; + +dojo.withGlobal = function( /*Object*/globalObject, + /*Function*/callback, + /*Object?*/thisObject, + /*Array?*/cbArguments){ + // summary: + // Invoke callback with globalObject as dojo.global and + // globalObject.document as dojo.doc. + // description: + // Invoke callback with globalObject as dojo.global and + // globalObject.document as dojo.doc. If provided, globalObject + // will be executed in the context of object thisObject + // When callback() returns or throws an error, the dojo.global + // and dojo.doc will be restored to its previous state. + + var oldGlob = dojo.global; + try{ + dojo.global = globalObject; + return dojo.withDoc.call(null, globalObject.document, callback, thisObject, cbArguments); + }finally{ + dojo.global = oldGlob; + } } + +dojo.withDoc = function( /*DocumentElement*/documentObject, + /*Function*/callback, + /*Object?*/thisObject, + /*Array?*/cbArguments){ + // summary: + // Invoke callback with documentObject as dojo.doc. + // description: + // Invoke callback with documentObject as dojo.doc. If provided, + // callback will be executed in the context of object thisObject + // When callback() returns or throws an error, the dojo.doc will + // be restored to its previous state. + + var oldDoc = dojo.doc, + oldLtr = dojo._bodyLtr, + oldQ = dojo.isQuirks; + + try{ + dojo.doc = documentObject; + delete dojo._bodyLtr; // uncache + dojo.isQuirks = dojo.doc.compatMode == "BackCompat"; // no need to check for QuirksMode which was Opera 7 only + + if(thisObject && typeof callback == "string"){ + callback = thisObject[callback]; + } + + return callback.apply(thisObject, cbArguments || []); + }finally{ + dojo.doc = oldDoc; + delete dojo._bodyLtr; // in case it was undefined originally, and set to true/false by the alternate document + if(oldLtr !== undefined){ dojo._bodyLtr = oldLtr; } + dojo.isQuirks = oldQ; + } }; + + } diff --git a/lib/dojo/_base/xhr.js b/lib/dojo/_base/xhr.js index 5823371cf..818f8e418 100644 --- a/lib/dojo/_base/xhr.js +++ b/lib/dojo/_base/xhr.js @@ -5,433 +5,937 @@ */ -if(!dojo._hasResource["dojo._base.xhr"]){ -dojo._hasResource["dojo._base.xhr"]=true; +if(!dojo._hasResource["dojo._base.xhr"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo._base.xhr"] = true; dojo.provide("dojo._base.xhr"); dojo.require("dojo._base.Deferred"); dojo.require("dojo._base.json"); dojo.require("dojo._base.lang"); dojo.require("dojo._base.query"); + (function(){ -var _1=dojo,_2=_1.config; -function _3(_4,_5,_6){ -if(_6===null){ -return; -} -var _7=_4[_5]; -if(typeof _7=="string"){ -_4[_5]=[_7,_6]; -}else{ -if(_1.isArray(_7)){ -_7.push(_6); -}else{ -_4[_5]=_6; -} -} -}; -dojo.fieldToObject=function(_8){ -var _9=null; -var _a=_1.byId(_8); -if(_a){ -var _b=_a.name; -var _c=(_a.type||"").toLowerCase(); -if(_b&&_c&&!_a.disabled){ -if(_c=="radio"||_c=="checkbox"){ -if(_a.checked){ -_9=_a.value; -} -}else{ -if(_a.multiple){ -_9=[]; -_1.query("option",_a).forEach(function(_d){ -if(_d.selected){ -_9.push(_d.value); -} -}); -}else{ -_9=_a.value; -} -} -} -} -return _9; -}; -dojo.formToObject=function(_e){ -var _f={}; -var _10="file|submit|image|reset|button|"; -_1.forEach(dojo.byId(_e).elements,function(_11){ -var _12=_11.name; -var _13=(_11.type||"").toLowerCase(); -if(_12&&_13&&_10.indexOf(_13)==-1&&!_11.disabled){ -_3(_f,_12,_1.fieldToObject(_11)); -if(_13=="image"){ -_f[_12+".x"]=_f[_12+".y"]=_f[_12].x=_f[_12].y=0; -} -} -}); -return _f; -}; -dojo.objectToQuery=function(map){ -var enc=encodeURIComponent; -var _14=[]; -var _15={}; -for(var _16 in map){ -var _17=map[_16]; -if(_17!=_15[_16]){ -var _18=enc(_16)+"="; -if(_1.isArray(_17)){ -for(var i=0;i<_17.length;i++){ -_14.push(_18+enc(_17[i])); -} -}else{ -_14.push(_18+enc(_17)); -} -} -} -return _14.join("&"); -}; -dojo.formToQuery=function(_19){ -return _1.objectToQuery(_1.formToObject(_19)); -}; -dojo.formToJson=function(_1a,_1b){ -return _1.toJson(_1.formToObject(_1a),_1b); -}; -dojo.queryToObject=function(str){ -var ret={}; -var qp=str.split("&"); -var dec=decodeURIComponent; -_1.forEach(qp,function(_1c){ -if(_1c.length){ -var _1d=_1c.split("="); -var _1e=dec(_1d.shift()); -var val=dec(_1d.join("=")); -if(typeof ret[_1e]=="string"){ -ret[_1e]=[ret[_1e]]; -} -if(_1.isArray(ret[_1e])){ -ret[_1e].push(val); -}else{ -ret[_1e]=val; -} -} -}); -return ret; -}; -dojo._blockAsync=false; -var _1f=_1._contentHandlers=dojo.contentHandlers={text:function(xhr){ -return xhr.responseText; -},json:function(xhr){ -return _1.fromJson(xhr.responseText||null); -},"json-comment-filtered":function(xhr){ -if(!dojo.config.useCommentedJson){ -console.warn("Consider using the standard mimetype:application/json."+" json-commenting can introduce security issues. To"+" decrease the chances of hijacking, use the standard the 'json' handler and"+" prefix your json with: {}&&\n"+"Use djConfig.useCommentedJson=true to turn off this message."); -} -var _20=xhr.responseText; -var _21=_20.indexOf("/*"); -var _22=_20.lastIndexOf("*/"); -if(_21==-1||_22==-1){ -throw new Error("JSON was not comment filtered"); -} -return _1.fromJson(_20.substring(_21+2,_22)); -},javascript:function(xhr){ -return _1.eval(xhr.responseText); -},xml:function(xhr){ -var _23=xhr.responseXML; -if(_1.isIE&&(!_23||!_23.documentElement)){ -var ms=function(n){ -return "MSXML"+n+".DOMDocument"; -}; -var dp=["Microsoft.XMLDOM",ms(6),ms(4),ms(3),ms(2)]; -_1.some(dp,function(p){ -try{ -var dom=new ActiveXObject(p); -dom.async=false; -dom.loadXML(xhr.responseText); -_23=dom; -} -catch(e){ -return false; -} -return true; -}); -} -return _23; -},"json-comment-optional":function(xhr){ -if(xhr.responseText&&/^[^{\[]*\/\*/.test(xhr.responseText)){ -return _1f["json-comment-filtered"](xhr); -}else{ -return _1f["json"](xhr); -} -}}; -dojo._ioSetArgs=function(_24,_25,_26,_27){ -var _28={args:_24,url:_24.url}; -var _29=null; -if(_24.form){ -var _2a=_1.byId(_24.form); -var _2b=_2a.getAttributeNode("action"); -_28.url=_28.url||(_2b?_2b.value:null); -_29=_1.formToObject(_2a); -} -var _2c=[{}]; -if(_29){ -_2c.push(_29); -} -if(_24.content){ -_2c.push(_24.content); -} -if(_24.preventCache){ -_2c.push({"dojo.preventCache":new Date().valueOf()}); -} -_28.query=_1.objectToQuery(_1.mixin.apply(null,_2c)); -_28.handleAs=_24.handleAs||"text"; -var d=new _1.Deferred(_25); -d.addCallbacks(_26,function(_2d){ -return _27(_2d,d); -}); -var ld=_24.load; -if(ld&&_1.isFunction(ld)){ -d.addCallback(function(_2e){ -return ld.call(_24,_2e,_28); -}); -} -var err=_24.error; -if(err&&_1.isFunction(err)){ -d.addErrback(function(_2f){ -return err.call(_24,_2f,_28); -}); -} -var _30=_24.handle; -if(_30&&_1.isFunction(_30)){ -d.addBoth(function(_31){ -return _30.call(_24,_31,_28); -}); -} -if(_2.ioPublish&&_1.publish&&_28.args.ioPublish!==false){ -d.addCallbacks(function(res){ -_1.publish("/dojo/io/load",[d,res]); -return res; -},function(res){ -_1.publish("/dojo/io/error",[d,res]); -return res; -}); -d.addBoth(function(res){ -_1.publish("/dojo/io/done",[d,res]); -return res; -}); -} -d.ioArgs=_28; -return d; -}; -var _32=function(dfd){ -dfd.canceled=true; -var xhr=dfd.ioArgs.xhr; -var _33=typeof xhr.abort; -if(_33=="function"||_33=="object"||_33=="unknown"){ -xhr.abort(); -} -var err=dfd.ioArgs.error; -if(!err){ -err=new Error("xhr cancelled"); -err.dojoType="cancel"; -} -return err; -}; -var _34=function(dfd){ -var ret=_1f[dfd.ioArgs.handleAs](dfd.ioArgs.xhr); -return ret===undefined?null:ret; -}; -var _35=function(_36,dfd){ -if(!dfd.ioArgs.args.failOk){ -console.error(_36); -} -return _36; -}; -var _37=null; -var _38=[]; -var _39=0; -var _3a=function(dfd){ -if(_39<=0){ -_39=0; -if(_2.ioPublish&&_1.publish&&(!dfd||dfd&&dfd.ioArgs.args.ioPublish!==false)){ -_1.publish("/dojo/io/stop"); -} -} -}; -var _3b=function(){ -var now=(new Date()).getTime(); -if(!_1._blockAsync){ -for(var i=0,tif;i<_38.length&&(tif=_38[i]);i++){ -var dfd=tif.dfd; -var _3c=function(){ -if(!dfd||dfd.canceled||!tif.validCheck(dfd)){ -_38.splice(i--,1); -_39-=1; -}else{ -if(tif.ioCheck(dfd)){ -_38.splice(i--,1); -tif.resHandle(dfd); -_39-=1; -}else{ -if(dfd.startTime){ -if(dfd.startTime+(dfd.ioArgs.args.timeout||0)<now){ -_38.splice(i--,1); -var err=new Error("timeout exceeded"); -err.dojoType="timeout"; -dfd.errback(err); -dfd.cancel(); -_39-=1; -} -} -} -} -}; -if(dojo.config.debugAtAllCosts){ -_3c.call(this); -}else{ -try{ -_3c.call(this); -} -catch(e){ -dfd.errback(e); -} -} -} -} -_3a(dfd); -if(!_38.length){ -clearInterval(_37); -_37=null; -return; -} -}; -dojo._ioCancelAll=function(){ -try{ -_1.forEach(_38,function(i){ -try{ -i.dfd.cancel(); -} -catch(e){ -} -}); -} -catch(e){ -} -}; -if(_1.isIE){ -_1.addOnWindowUnload(_1._ioCancelAll); -} -_1._ioNotifyStart=function(dfd){ -if(_2.ioPublish&&_1.publish&&dfd.ioArgs.args.ioPublish!==false){ -if(!_39){ -_1.publish("/dojo/io/start"); -} -_39+=1; -_1.publish("/dojo/io/send",[dfd]); -} -}; -_1._ioWatch=function(dfd,_3d,_3e,_3f){ -var _40=dfd.ioArgs.args; -if(_40.timeout){ -dfd.startTime=(new Date()).getTime(); -} -_38.push({dfd:dfd,validCheck:_3d,ioCheck:_3e,resHandle:_3f}); -if(!_37){ -_37=setInterval(_3b,50); -} -if(_40.sync){ -_3b(); -} -}; -var _41="application/x-www-form-urlencoded"; -var _42=function(dfd){ -return dfd.ioArgs.xhr.readyState; -}; -var _43=function(dfd){ -return 4==dfd.ioArgs.xhr.readyState; -}; -var _44=function(dfd){ -var xhr=dfd.ioArgs.xhr; -if(_1._isDocumentOk(xhr)){ -dfd.callback(dfd); -}else{ -var err=new Error("Unable to load "+dfd.ioArgs.url+" status:"+xhr.status); -err.status=xhr.status; -err.responseText=xhr.responseText; -dfd.errback(err); -} -}; -dojo._ioAddQueryToUrl=function(_45){ -if(_45.query.length){ -_45.url+=(_45.url.indexOf("?")==-1?"?":"&")+_45.query; -_45.query=null; -} -}; -dojo.xhr=function(_46,_47,_48){ -var dfd=_1._ioSetArgs(_47,_32,_34,_35); -var _49=dfd.ioArgs; -var xhr=_49.xhr=_1._xhrObj(_49.args); -if(!xhr){ -dfd.cancel(); -return dfd; -} -if("postData" in _47){ -_49.query=_47.postData; -}else{ -if("putData" in _47){ -_49.query=_47.putData; -}else{ -if("rawBody" in _47){ -_49.query=_47.rawBody; -}else{ -if((arguments.length>2&&!_48)||"POST|PUT".indexOf(_46.toUpperCase())==-1){ -_1._ioAddQueryToUrl(_49); -} -} -} -} -xhr.open(_46,_49.url,_47.sync!==true,_47.user||undefined,_47.password||undefined); -if(_47.headers){ -for(var hdr in _47.headers){ -if(hdr.toLowerCase()==="content-type"&&!_47.contentType){ -_47.contentType=_47.headers[hdr]; -}else{ -if(_47.headers[hdr]){ -xhr.setRequestHeader(hdr,_47.headers[hdr]); -} -} -} -} -xhr.setRequestHeader("Content-Type",_47.contentType||_41); -if(!_47.headers||!("X-Requested-With" in _47.headers)){ -xhr.setRequestHeader("X-Requested-With","XMLHttpRequest"); -} -_1._ioNotifyStart(dfd); -if(dojo.config.debugAtAllCosts){ -xhr.send(_49.query); -}else{ -try{ -xhr.send(_49.query); -} -catch(e){ -_49.error=e; -dfd.cancel(); -} -} -_1._ioWatch(dfd,_42,_43,_44); -xhr=null; -return dfd; -}; -dojo.xhrGet=function(_4a){ -return _1.xhr("GET",_4a); -}; -dojo.rawXhrPost=dojo.xhrPost=function(_4b){ -return _1.xhr("POST",_4b,true); -}; -dojo.rawXhrPut=dojo.xhrPut=function(_4c){ -return _1.xhr("PUT",_4c,true); -}; -dojo.xhrDelete=function(_4d){ -return _1.xhr("DELETE",_4d); -}; + var _d = dojo, cfg = _d.config; + + function setValue(/*Object*/obj, /*String*/name, /*String*/value){ + //summary: + // For the named property in object, set the value. If a value + // already exists and it is a string, convert the value to be an + // array of values. + + //Skip it if there is no value + if(value === null){ + return; + } + + var val = obj[name]; + if(typeof val == "string"){ // inline'd type check + obj[name] = [val, value]; + }else if(_d.isArray(val)){ + val.push(value); + }else{ + obj[name] = value; + } + } + + dojo.fieldToObject = function(/*DOMNode||String*/ inputNode){ + // summary: + // Serialize a form field to a JavaScript object. + // + // description: + // Returns the value encoded in a form field as + // as a string or an array of strings. Disabled form elements + // and unchecked radio and checkboxes are skipped. Multi-select + // elements are returned as an array of string values. + var ret = null; + var item = _d.byId(inputNode); + if(item){ + var _in = item.name; + var type = (item.type||"").toLowerCase(); + if(_in && type && !item.disabled){ + if(type == "radio" || type == "checkbox"){ + if(item.checked){ ret = item.value } + }else if(item.multiple){ + ret = []; + _d.query("option", item).forEach(function(opt){ + if(opt.selected){ + ret.push(opt.value); + } + }); + }else{ + ret = item.value; + } + } + } + return ret; // Object + } + + dojo.formToObject = function(/*DOMNode||String*/ formNode){ + // summary: + // Serialize a form node to a JavaScript object. + // description: + // Returns the values encoded in an HTML form as + // string properties in an object which it then returns. Disabled form + // elements, buttons, and other non-value form elements are skipped. + // Multi-select elements are returned as an array of string values. + // + // example: + // This form: + // | <form id="test_form"> + // | <input type="text" name="blah" value="blah"> + // | <input type="text" name="no_value" value="blah" disabled> + // | <input type="button" name="no_value2" value="blah"> + // | <select type="select" multiple name="multi" size="5"> + // | <option value="blah">blah</option> + // | <option value="thud" selected>thud</option> + // | <option value="thonk" selected>thonk</option> + // | </select> + // | </form> + // + // yields this object structure as the result of a call to + // formToObject(): + // + // | { + // | blah: "blah", + // | multi: [ + // | "thud", + // | "thonk" + // | ] + // | }; + + var ret = {}; + var exclude = "file|submit|image|reset|button|"; + _d.forEach(dojo.byId(formNode).elements, function(item){ + var _in = item.name; + var type = (item.type||"").toLowerCase(); + if(_in && type && exclude.indexOf(type) == -1 && !item.disabled){ + setValue(ret, _in, _d.fieldToObject(item)); + if(type == "image"){ + ret[_in+".x"] = ret[_in+".y"] = ret[_in].x = ret[_in].y = 0; + } + } + }); + return ret; // Object + } + + dojo.objectToQuery = function(/*Object*/ map){ + // summary: + // takes a name/value mapping object and returns a string representing + // a URL-encoded version of that object. + // example: + // this object: + // + // | { + // | blah: "blah", + // | multi: [ + // | "thud", + // | "thonk" + // | ] + // | }; + // + // yields the following query string: + // + // | "blah=blah&multi=thud&multi=thonk" + + // FIXME: need to implement encodeAscii!! + var enc = encodeURIComponent; + var pairs = []; + var backstop = {}; + for(var name in map){ + var value = map[name]; + if(value != backstop[name]){ + var assign = enc(name) + "="; + if(_d.isArray(value)){ + for(var i=0; i < value.length; i++){ + pairs.push(assign + enc(value[i])); + } + }else{ + pairs.push(assign + enc(value)); + } + } + } + return pairs.join("&"); // String + } + + dojo.formToQuery = function(/*DOMNode||String*/ formNode){ + // summary: + // Returns a URL-encoded string representing the form passed as either a + // node or string ID identifying the form to serialize + return _d.objectToQuery(_d.formToObject(formNode)); // String + } + + dojo.formToJson = function(/*DOMNode||String*/ formNode, /*Boolean?*/prettyPrint){ + // summary: + // Create a serialized JSON string from a form node or string + // ID identifying the form to serialize + return _d.toJson(_d.formToObject(formNode), prettyPrint); // String + } + + dojo.queryToObject = function(/*String*/ str){ + // summary: + // Create an object representing a de-serialized query section of a + // URL. Query keys with multiple values are returned in an array. + // + // example: + // This string: + // + // | "foo=bar&foo=baz&thinger=%20spaces%20=blah&zonk=blarg&" + // + // results in this object structure: + // + // | { + // | foo: [ "bar", "baz" ], + // | thinger: " spaces =blah", + // | zonk: "blarg" + // | } + // + // Note that spaces and other urlencoded entities are correctly + // handled. + + // FIXME: should we grab the URL string if we're not passed one? + var ret = {}; + var qp = str.split("&"); + var dec = decodeURIComponent; + _d.forEach(qp, function(item){ + if(item.length){ + var parts = item.split("="); + var name = dec(parts.shift()); + var val = dec(parts.join("=")); + if(typeof ret[name] == "string"){ // inline'd type check + ret[name] = [ret[name]]; + } + + if(_d.isArray(ret[name])){ + ret[name].push(val); + }else{ + ret[name] = val; + } + } + }); + return ret; // Object + } + + // need to block async callbacks from snatching this thread as the result + // of an async callback might call another sync XHR, this hangs khtml forever + // must checked by watchInFlight() + + dojo._blockAsync = false; + + // MOW: remove dojo._contentHandlers alias in 2.0 + var handlers = _d._contentHandlers = dojo.contentHandlers = { + // summary: + // A map of availble XHR transport handle types. Name matches the + // `handleAs` attribute passed to XHR calls. + // + // description: + // A map of availble XHR transport handle types. Name matches the + // `handleAs` attribute passed to XHR calls. Each contentHandler is + // called, passing the xhr object for manipulation. The return value + // from the contentHandler will be passed to the `load` or `handle` + // functions defined in the original xhr call. + // + // example: + // Creating a custom content-handler: + // | dojo.contentHandlers.makeCaps = function(xhr){ + // | return xhr.responseText.toUpperCase(); + // | } + // | // and later: + // | dojo.xhrGet({ + // | url:"foo.txt", + // | handleAs:"makeCaps", + // | load: function(data){ /* data is a toUpper version of foo.txt */ } + // | }); + + text: function(xhr){ + // summary: A contentHandler which simply returns the plaintext response data + return xhr.responseText; + }, + json: function(xhr){ + // summary: A contentHandler which returns a JavaScript object created from the response data + return _d.fromJson(xhr.responseText || null); + }, + "json-comment-filtered": function(xhr){ + // summary: A contentHandler which expects comment-filtered JSON. + // description: + // A contentHandler which expects comment-filtered JSON. + // the json-comment-filtered option was implemented to prevent + // "JavaScript Hijacking", but it is less secure than standard JSON. Use + // standard JSON instead. JSON prefixing can be used to subvert hijacking. + // + // Will throw a notice suggesting to use application/json mimetype, as + // json-commenting can introduce security issues. To decrease the chances of hijacking, + // use the standard `json` contentHandler, and prefix your "JSON" with: {}&& + // + // use djConfig.useCommentedJson = true to turn off the notice + if(!dojo.config.useCommentedJson){ + console.warn("Consider using the standard mimetype:application/json." + + " json-commenting can introduce security issues. To" + + " decrease the chances of hijacking, use the standard the 'json' handler and" + + " prefix your json with: {}&&\n" + + "Use djConfig.useCommentedJson=true to turn off this message."); + } + + var value = xhr.responseText; + var cStartIdx = value.indexOf("\/*"); + var cEndIdx = value.lastIndexOf("*\/"); + if(cStartIdx == -1 || cEndIdx == -1){ + throw new Error("JSON was not comment filtered"); + } + return _d.fromJson(value.substring(cStartIdx+2, cEndIdx)); + }, + javascript: function(xhr){ + // summary: A contentHandler which evaluates the response data, expecting it to be valid JavaScript + + // FIXME: try Moz and IE specific eval variants? + return _d.eval(xhr.responseText); + }, + xml: function(xhr){ + // summary: A contentHandler returning an XML Document parsed from the response data + var result = xhr.responseXML; + if(_d.isIE && (!result || !result.documentElement)){ + //WARNING: this branch used by the xml handling in dojo.io.iframe, + //so be sure to test dojo.io.iframe if making changes below. + var ms = function(n){ return "MSXML" + n + ".DOMDocument"; } + var dp = ["Microsoft.XMLDOM", ms(6), ms(4), ms(3), ms(2)]; + _d.some(dp, function(p){ + try{ + var dom = new ActiveXObject(p); + dom.async = false; + dom.loadXML(xhr.responseText); + result = dom; + }catch(e){ return false; } + return true; + }); + } + return result; // DOMDocument + }, + "json-comment-optional": function(xhr){ + // summary: A contentHandler which checks the presence of comment-filtered JSON and + // alternates between the `json` and `json-comment-filtered` contentHandlers. + if(xhr.responseText && /^[^{\[]*\/\*/.test(xhr.responseText)){ + return handlers["json-comment-filtered"](xhr); + }else{ + return handlers["json"](xhr); + } + } + }; + + /*===== + dojo.__IoArgs = function(){ + // url: String + // URL to server endpoint. + // content: Object? + // Contains properties with string values. These + // properties will be serialized as name1=value2 and + // passed in the request. + // timeout: Integer? + // Milliseconds to wait for the response. If this time + // passes, the then error callbacks are called. + // form: DOMNode? + // DOM node for a form. Used to extract the form values + // and send to the server. + // preventCache: Boolean? + // Default is false. If true, then a + // "dojo.preventCache" parameter is sent in the request + // with a value that changes with each request + // (timestamp). Useful only with GET-type requests. + // handleAs: String? + // Acceptable values depend on the type of IO + // transport (see specific IO calls for more information). + // rawBody: String? + // Sets the raw body for an HTTP request. If this is used, then the content + // property is ignored. This is mostly useful for HTTP methods that have + // a body to their requests, like PUT or POST. This property can be used instead + // of postData and putData for dojo.rawXhrPost and dojo.rawXhrPut respectively. + // ioPublish: Boolean? + // Set this explicitly to false to prevent publishing of topics related to + // IO operations. Otherwise, if djConfig.ioPublish is set to true, topics + // will be published via dojo.publish for different phases of an IO operation. + // See dojo.__IoPublish for a list of topics that are published. + // load: Function? + // This function will be + // called on a successful HTTP response code. + // error: Function? + // This function will + // be called when the request fails due to a network or server error, the url + // is invalid, etc. It will also be called if the load or handle callback throws an + // exception, unless djConfig.debugAtAllCosts is true. This allows deployed applications + // to continue to run even when a logic error happens in the callback, while making + // it easier to troubleshoot while in debug mode. + // handle: Function? + // This function will + // be called at the end of every request, whether or not an error occurs. + this.url = url; + this.content = content; + this.timeout = timeout; + this.form = form; + this.preventCache = preventCache; + this.handleAs = handleAs; + this.ioPublish = ioPublish; + this.load = function(response, ioArgs){ + // ioArgs: dojo.__IoCallbackArgs + // Provides additional information about the request. + // response: Object + // The response in the format as defined with handleAs. + } + this.error = function(response, ioArgs){ + // ioArgs: dojo.__IoCallbackArgs + // Provides additional information about the request. + // response: Object + // The response in the format as defined with handleAs. + } + this.handle = function(loadOrError, response, ioArgs){ + // loadOrError: String + // Provides a string that tells you whether this function + // was called because of success (load) or failure (error). + // response: Object + // The response in the format as defined with handleAs. + // ioArgs: dojo.__IoCallbackArgs + // Provides additional information about the request. + } + } + =====*/ + + /*===== + dojo.__IoCallbackArgs = function(args, xhr, url, query, handleAs, id, canDelete, json){ + // args: Object + // the original object argument to the IO call. + // xhr: XMLHttpRequest + // For XMLHttpRequest calls only, the + // XMLHttpRequest object that was used for the + // request. + // url: String + // The final URL used for the call. Many times it + // will be different than the original args.url + // value. + // query: String + // For non-GET requests, the + // name1=value1&name2=value2 parameters sent up in + // the request. + // handleAs: String + // The final indicator on how the response will be + // handled. + // id: String + // For dojo.io.script calls only, the internal + // script ID used for the request. + // canDelete: Boolean + // For dojo.io.script calls only, indicates + // whether the script tag that represents the + // request can be deleted after callbacks have + // been called. Used internally to know when + // cleanup can happen on JSONP-type requests. + // json: Object + // For dojo.io.script calls only: holds the JSON + // response for JSONP-type requests. Used + // internally to hold on to the JSON responses. + // You should not need to access it directly -- + // the same object should be passed to the success + // callbacks directly. + this.args = args; + this.xhr = xhr; + this.url = url; + this.query = query; + this.handleAs = handleAs; + this.id = id; + this.canDelete = canDelete; + this.json = json; + } + =====*/ + + + /*===== + dojo.__IoPublish = function(){ + // summary: + // This is a list of IO topics that can be published + // if djConfig.ioPublish is set to true. IO topics can be + // published for any Input/Output, network operation. So, + // dojo.xhr, dojo.io.script and dojo.io.iframe can all + // trigger these topics to be published. + // start: String + // "/dojo/io/start" is sent when there are no outstanding IO + // requests, and a new IO request is started. No arguments + // are passed with this topic. + // send: String + // "/dojo/io/send" is sent whenever a new IO request is started. + // It passes the dojo.Deferred for the request with the topic. + // load: String + // "/dojo/io/load" is sent whenever an IO request has loaded + // successfully. It passes the response and the dojo.Deferred + // for the request with the topic. + // error: String + // "/dojo/io/error" is sent whenever an IO request has errored. + // It passes the error and the dojo.Deferred + // for the request with the topic. + // done: String + // "/dojo/io/done" is sent whenever an IO request has completed, + // either by loading or by erroring. It passes the error and + // the dojo.Deferred for the request with the topic. + // stop: String + // "/dojo/io/stop" is sent when all outstanding IO requests have + // finished. No arguments are passed with this topic. + this.start = "/dojo/io/start"; + this.send = "/dojo/io/send"; + this.load = "/dojo/io/load"; + this.error = "/dojo/io/error"; + this.done = "/dojo/io/done"; + this.stop = "/dojo/io/stop"; + } + =====*/ + + + dojo._ioSetArgs = function(/*dojo.__IoArgs*/args, + /*Function*/canceller, + /*Function*/okHandler, + /*Function*/errHandler){ + // summary: + // sets up the Deferred and ioArgs property on the Deferred so it + // can be used in an io call. + // args: + // The args object passed into the public io call. Recognized properties on + // the args object are: + // canceller: + // The canceller function used for the Deferred object. The function + // will receive one argument, the Deferred object that is related to the + // canceller. + // okHandler: + // The first OK callback to be registered with Deferred. It has the opportunity + // to transform the OK response. It will receive one argument -- the Deferred + // object returned from this function. + // errHandler: + // The first error callback to be registered with Deferred. It has the opportunity + // to do cleanup on an error. It will receive two arguments: error (the + // Error object) and dfd, the Deferred object returned from this function. + + var ioArgs = {args: args, url: args.url}; + + //Get values from form if requestd. + var formObject = null; + if(args.form){ + var form = _d.byId(args.form); + //IE requires going through getAttributeNode instead of just getAttribute in some form cases, + //so use it for all. See #2844 + var actnNode = form.getAttributeNode("action"); + ioArgs.url = ioArgs.url || (actnNode ? actnNode.value : null); + formObject = _d.formToObject(form); + } + + // set up the query params + var miArgs = [{}]; + + if(formObject){ + // potentially over-ride url-provided params w/ form values + miArgs.push(formObject); + } + if(args.content){ + // stuff in content over-rides what's set by form + miArgs.push(args.content); + } + if(args.preventCache){ + miArgs.push({"dojo.preventCache": new Date().valueOf()}); + } + ioArgs.query = _d.objectToQuery(_d.mixin.apply(null, miArgs)); + + // .. and the real work of getting the deferred in order, etc. + ioArgs.handleAs = args.handleAs || "text"; + var d = new _d.Deferred(canceller); + d.addCallbacks(okHandler, function(error){ + return errHandler(error, d); + }); + + //Support specifying load, error and handle callback functions from the args. + //For those callbacks, the "this" object will be the args object. + //The callbacks will get the deferred result value as the + //first argument and the ioArgs object as the second argument. + var ld = args.load; + if(ld && _d.isFunction(ld)){ + d.addCallback(function(value){ + return ld.call(args, value, ioArgs); + }); + } + var err = args.error; + if(err && _d.isFunction(err)){ + d.addErrback(function(value){ + return err.call(args, value, ioArgs); + }); + } + var handle = args.handle; + if(handle && _d.isFunction(handle)){ + d.addBoth(function(value){ + return handle.call(args, value, ioArgs); + }); + } + + //Plug in topic publishing, if dojo.publish is loaded. + if(cfg.ioPublish && _d.publish && ioArgs.args.ioPublish !== false){ + d.addCallbacks( + function(res){ + _d.publish("/dojo/io/load", [d, res]); + return res; + }, + function(res){ + _d.publish("/dojo/io/error", [d, res]); + return res; + } + ); + d.addBoth(function(res){ + _d.publish("/dojo/io/done", [d, res]); + return res; + }); + } + + d.ioArgs = ioArgs; + + // FIXME: need to wire up the xhr object's abort method to something + // analagous in the Deferred + return d; + } + + var _deferredCancel = function(/*Deferred*/dfd){ + // summary: canceller function for dojo._ioSetArgs call. + + dfd.canceled = true; + var xhr = dfd.ioArgs.xhr; + var _at = typeof xhr.abort; + if(_at == "function" || _at == "object" || _at == "unknown"){ + xhr.abort(); + } + var err = dfd.ioArgs.error; + if(!err){ + err = new Error("xhr cancelled"); + err.dojoType="cancel"; + } + return err; + } + var _deferredOk = function(/*Deferred*/dfd){ + // summary: okHandler function for dojo._ioSetArgs call. + + var ret = handlers[dfd.ioArgs.handleAs](dfd.ioArgs.xhr); + return ret === undefined ? null : ret; + } + var _deferError = function(/*Error*/error, /*Deferred*/dfd){ + // summary: errHandler function for dojo._ioSetArgs call. + + if(!dfd.ioArgs.args.failOk){ + console.error(error); + } + return error; + } + + // avoid setting a timer per request. It degrades performance on IE + // something fierece if we don't use unified loops. + var _inFlightIntvl = null; + var _inFlight = []; + + + //Use a separate count for knowing if we are starting/stopping io calls. + //Cannot use _inFlight.length since it can change at a different time than + //when we want to do this kind of test. We only want to decrement the count + //after a callback/errback has finished, since the callback/errback should be + //considered as part of finishing a request. + var _pubCount = 0; + var _checkPubCount = function(dfd){ + if(_pubCount <= 0){ + _pubCount = 0; + if(cfg.ioPublish && _d.publish && (!dfd || dfd && dfd.ioArgs.args.ioPublish !== false)){ + _d.publish("/dojo/io/stop"); + } + } + }; + + var _watchInFlight = function(){ + //summary: + // internal method that checks each inflight XMLHttpRequest to see + // if it has completed or if the timeout situation applies. + + var now = (new Date()).getTime(); + // make sure sync calls stay thread safe, if this callback is called + // during a sync call and this results in another sync call before the + // first sync call ends the browser hangs + if(!_d._blockAsync){ + // we need manual loop because we often modify _inFlight (and therefore 'i') while iterating + // note: the second clause is an assigment on purpose, lint may complain + for(var i = 0, tif; i < _inFlight.length && (tif = _inFlight[i]); i++){ + var dfd = tif.dfd; + var func = function(){ + if(!dfd || dfd.canceled || !tif.validCheck(dfd)){ + _inFlight.splice(i--, 1); + _pubCount -= 1; + }else if(tif.ioCheck(dfd)){ + _inFlight.splice(i--, 1); + tif.resHandle(dfd); + _pubCount -= 1; + }else if(dfd.startTime){ + //did we timeout? + if(dfd.startTime + (dfd.ioArgs.args.timeout || 0) < now){ + _inFlight.splice(i--, 1); + var err = new Error("timeout exceeded"); + err.dojoType = "timeout"; + dfd.errback(err); + //Cancel the request so the io module can do appropriate cleanup. + dfd.cancel(); + _pubCount -= 1; + } + } + }; + if(dojo.config.debugAtAllCosts){ + func.call(this); + }else{ + try{ + func.call(this); + }catch(e){ + dfd.errback(e); + } + } + } + } + + _checkPubCount(dfd); + + if(!_inFlight.length){ + clearInterval(_inFlightIntvl); + _inFlightIntvl = null; + return; + } + } + + dojo._ioCancelAll = function(){ + //summary: Cancels all pending IO requests, regardless of IO type + //(xhr, script, iframe). + try{ + _d.forEach(_inFlight, function(i){ + try{ + i.dfd.cancel(); + }catch(e){/*squelch*/} + }); + }catch(e){/*squelch*/} + } + + //Automatically call cancel all io calls on unload + //in IE for trac issue #2357. + if(_d.isIE){ + _d.addOnWindowUnload(_d._ioCancelAll); + } + + _d._ioNotifyStart = function(/*Deferred*/dfd){ + // summary: + // If dojo.publish is available, publish topics + // about the start of a request queue and/or the + // the beginning of request. + // description: + // Used by IO transports. An IO transport should + // call this method before making the network connection. + if(cfg.ioPublish && _d.publish && dfd.ioArgs.args.ioPublish !== false){ + if(!_pubCount){ + _d.publish("/dojo/io/start"); + } + _pubCount += 1; + _d.publish("/dojo/io/send", [dfd]); + } + } + + _d._ioWatch = function(dfd, validCheck, ioCheck, resHandle){ + // summary: + // Watches the io request represented by dfd to see if it completes. + // dfd: Deferred + // The Deferred object to watch. + // validCheck: Function + // Function used to check if the IO request is still valid. Gets the dfd + // object as its only argument. + // ioCheck: Function + // Function used to check if basic IO call worked. Gets the dfd + // object as its only argument. + // resHandle: Function + // Function used to process response. Gets the dfd + // object as its only argument. + var args = dfd.ioArgs.args; + if(args.timeout){ + dfd.startTime = (new Date()).getTime(); + } + + _inFlight.push({dfd: dfd, validCheck: validCheck, ioCheck: ioCheck, resHandle: resHandle}); + if(!_inFlightIntvl){ + _inFlightIntvl = setInterval(_watchInFlight, 50); + } + // handle sync requests + //A weakness: async calls in flight + //could have their handlers called as part of the + //_watchInFlight call, before the sync's callbacks + // are called. + if(args.sync){ + _watchInFlight(); + } + } + + var _defaultContentType = "application/x-www-form-urlencoded"; + + var _validCheck = function(/*Deferred*/dfd){ + return dfd.ioArgs.xhr.readyState; //boolean + } + var _ioCheck = function(/*Deferred*/dfd){ + return 4 == dfd.ioArgs.xhr.readyState; //boolean + } + var _resHandle = function(/*Deferred*/dfd){ + var xhr = dfd.ioArgs.xhr; + if(_d._isDocumentOk(xhr)){ + dfd.callback(dfd); + }else{ + var err = new Error("Unable to load " + dfd.ioArgs.url + " status:" + xhr.status); + err.status = xhr.status; + err.responseText = xhr.responseText; + dfd.errback(err); + } + } + + dojo._ioAddQueryToUrl = function(/*dojo.__IoCallbackArgs*/ioArgs){ + //summary: Adds query params discovered by the io deferred construction to the URL. + //Only use this for operations which are fundamentally GET-type operations. + if(ioArgs.query.length){ + ioArgs.url += (ioArgs.url.indexOf("?") == -1 ? "?" : "&") + ioArgs.query; + ioArgs.query = null; + } + } + + /*===== + dojo.declare("dojo.__XhrArgs", dojo.__IoArgs, { + constructor: function(){ + // summary: + // In addition to the properties listed for the dojo._IoArgs type, + // the following properties are allowed for dojo.xhr* methods. + // handleAs: String? + // Acceptable values are: text (default), json, json-comment-optional, + // json-comment-filtered, javascript, xml. See `dojo.contentHandlers` + // sync: Boolean? + // false is default. Indicates whether the request should + // be a synchronous (blocking) request. + // headers: Object? + // Additional HTTP headers to send in the request. + // failOk: Boolean? + // false is default. Indicates whether a request should be + // allowed to fail (and therefore no console error message in + // the event of a failure) + this.handleAs = handleAs; + this.sync = sync; + this.headers = headers; + this.failOk = failOk; + } + }); + =====*/ + + dojo.xhr = function(/*String*/ method, /*dojo.__XhrArgs*/ args, /*Boolean?*/ hasBody){ + // summary: + // Sends an HTTP request with the given method. + // description: + // Sends an HTTP request with the given method. + // See also dojo.xhrGet(), xhrPost(), xhrPut() and dojo.xhrDelete() for shortcuts + // for those HTTP methods. There are also methods for "raw" PUT and POST methods + // via dojo.rawXhrPut() and dojo.rawXhrPost() respectively. + // method: + // HTTP method to be used, such as GET, POST, PUT, DELETE. Should be uppercase. + // hasBody: + // If the request has an HTTP body, then pass true for hasBody. + + //Make the Deferred object for this xhr request. + var dfd = _d._ioSetArgs(args, _deferredCancel, _deferredOk, _deferError); + var ioArgs = dfd.ioArgs; + + //Pass the args to _xhrObj, to allow alternate XHR calls based specific calls, like + //the one used for iframe proxies. + var xhr = ioArgs.xhr = _d._xhrObj(ioArgs.args); + //If XHR factory fails, cancel the deferred. + if(!xhr){ + dfd.cancel(); + return dfd; + } + + //Allow for specifying the HTTP body completely. + if("postData" in args){ + ioArgs.query = args.postData; + }else if("putData" in args){ + ioArgs.query = args.putData; + }else if("rawBody" in args){ + ioArgs.query = args.rawBody; + }else if((arguments.length > 2 && !hasBody) || "POST|PUT".indexOf(method.toUpperCase()) == -1){ + //Check for hasBody being passed. If no hasBody, + //then only append query string if not a POST or PUT request. + _d._ioAddQueryToUrl(ioArgs); + } + + // IE 6 is a steaming pile. It won't let you call apply() on the native function (xhr.open). + // workaround for IE6's apply() "issues" + xhr.open(method, ioArgs.url, args.sync !== true, args.user || undefined, args.password || undefined); + if(args.headers){ + for(var hdr in args.headers){ + if(hdr.toLowerCase() === "content-type" && !args.contentType){ + args.contentType = args.headers[hdr]; + }else if(args.headers[hdr]){ + //Only add header if it has a value. This allows for instnace, skipping + //insertion of X-Requested-With by specifying empty value. + xhr.setRequestHeader(hdr, args.headers[hdr]); + } + } + } + // FIXME: is this appropriate for all content types? + xhr.setRequestHeader("Content-Type", args.contentType || _defaultContentType); + if(!args.headers || !("X-Requested-With" in args.headers)){ + xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); + } + // FIXME: set other headers here! + _d._ioNotifyStart(dfd); + if(dojo.config.debugAtAllCosts){ + xhr.send(ioArgs.query); + }else{ + try{ + xhr.send(ioArgs.query); + }catch(e){ + ioArgs.error = e; + dfd.cancel(); + } + } + _d._ioWatch(dfd, _validCheck, _ioCheck, _resHandle); + xhr = null; + return dfd; // dojo.Deferred + } + + dojo.xhrGet = function(/*dojo.__XhrArgs*/ args){ + // summary: + // Sends an HTTP GET request to the server. + return _d.xhr("GET", args); // dojo.Deferred + } + + dojo.rawXhrPost = dojo.xhrPost = function(/*dojo.__XhrArgs*/ args){ + // summary: + // Sends an HTTP POST request to the server. In addtion to the properties + // listed for the dojo.__XhrArgs type, the following property is allowed: + // postData: + // String. Send raw data in the body of the POST request. + return _d.xhr("POST", args, true); // dojo.Deferred + } + + dojo.rawXhrPut = dojo.xhrPut = function(/*dojo.__XhrArgs*/ args){ + // summary: + // Sends an HTTP PUT request to the server. In addtion to the properties + // listed for the dojo.__XhrArgs type, the following property is allowed: + // putData: + // String. Send raw data in the body of the PUT request. + return _d.xhr("PUT", args, true); // dojo.Deferred + } + + dojo.xhrDelete = function(/*dojo.__XhrArgs*/ args){ + // summary: + // Sends an HTTP DELETE request to the server. + return _d.xhr("DELETE", args); //dojo.Deferred + } + + /* + dojo.wrapForm = function(formNode){ + //summary: + // A replacement for FormBind, but not implemented yet. + + // FIXME: need to think harder about what extensions to this we might + // want. What should we allow folks to do w/ this? What events to + // set/send? + throw new Error("dojo.wrapForm not yet implemented"); + } + */ })(); + } |