// | ... // | // | // |
// 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"); // | } // | }); // | }); // NOTE: elementsById is not currently supported // NOTE: ignores xpath-ish queries for now if(query.constructor == d.NodeList){ return query; } if(!d.isString(query)){ return new d.NodeList(query); // dojo.NodeList } if(d.isString(root)){ root = d.byId(root); } return _zip(getQueryFunc(query)(root||d.doc)); // dojo.NodeList } /* // exposing this was a mistake d.query.attrs = attrs; */ // exposing this because new pseudo matches are only executed through the // DOM query path (never through the xpath optimizing branch) d.query.pseudos = pseudos; // one-off function for filtering a NodeList based on a simple selector d._filterQueryResult = function(nodeList, simpleFilter){ var tnl = new d.NodeList(); var ff = (simpleFilter) ? getFilterFunc(getQueryParts(simpleFilter)[0]) : function(){ return true; }; for(var x=0, te; te = nodeList[x]; x++){ if(ff(te)){ tnl.push(te); } } return tnl; } })(); } 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"); (function(){ var _d = dojo; function setValue(/*Object*/obj, /*String*/name, /*String*/value){ //summary: // For the nameed 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. var val = obj[name]; if(_d.isString(val)){ obj[name] = [val, value]; }else if(_d.isArray(val)){ val.push(value); }else{ obj[name] = value; } } dojo.formToObject = function(/*DOMNode||String*/ formNode){ // summary: // dojo.formToObject 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. // description: // This form: // // | // // yields this object structure as the result of a call to // formToObject(): // // | { // | blah: "blah", // | multi: [ // | "thud", // | "thonk" // | ] // | }; var ret = {}; var iq = "input:not([type=file]):not([type=submit]):not([type=image]):not([type=reset]):not([type=button]), select, textarea"; _d.query(iq, formNode).filter(function(node){ return !node.disabled && node.name; }).forEach(function(item){ var _in = item.name; var type = (item.type||"").toLowerCase(); if(type == "radio" || type == "checkbox"){ if(item.checked){ setValue(ret, _in, item.value); } }else if(item.multiple){ ret[_in] = []; _d.query("option", item).forEach(function(opt){ if(opt.selected){ setValue(ret, _in, opt.value); } }); }else{ setValue(ret, _in, item.value); 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: // return 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: // returns an object representing a de-serialized query section of a // URL. Query keys with multiple values are returned in an array. // description: // 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(_d.isString(ret[name])){ ret[name] = [ret[name]]; } if(_d.isArray(ret[name])){ ret[name].push(val); }else{ ret[name] = val; } } }); return ret; // Object } /* from refactor.txt: all bind() replacement APIs take the following argument structure: { url: "blah.html", // all below are optional, but must be supported in some form by // every IO API timeout: 1000, // milliseconds handleAs: "text", // replaces the always-wrong "mimetype" content: { key: "value" }, // browser-specific, MAY be unsupported sync: true, // defaults to false form: dojo.byId("someForm") } */ // 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; dojo._contentHandlers = { "text": function(xhr){ return xhr.responseText; }, "json": function(xhr){ if(!dojo.config.usePlainJson){ console.warn("Consider using mimetype:text/json-comment-filtered" + " to avoid potential security issues with JSON endpoints" + " (use djConfig.usePlainJson=true to turn off this message)"); } return (xhr.status == 204) ? undefined : _d.fromJson(xhr.responseText); }, "json-comment-filtered": function(xhr){ // NOTE: we provide the json-comment-filtered option as one solution to // the "JavaScript Hijacking" issue noted by Fortify and others. It is // not appropriate for all circumstances. 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 (xhr.status == 204) ? undefined : _d.fromJson(value.substring(cStartIdx+2, cEndIdx)); }, "javascript": function(xhr){ // FIXME: try Moz and IE specific eval variants? return _d.eval(xhr.responseText); }, "xml": function(xhr){ var result = xhr.responseXML; if(_d.isIE && (!result || window.location.protocol == "file:")){ _d.forEach(["MSXML2", "Microsoft", "MSXML", "MSXML3"], function(prefix){ try{ var dom = new ActiveXObject(prefix + ".XMLDOM"); dom.async = false; dom.loadXML(xhr.responseText); result = dom; }catch(e){ /* Not available. Squelch and try next one. */ } }); } return result; // DOMDocument } }; dojo._contentHandlers["json-comment-optional"] = function(xhr){ var handlers = _d._contentHandlers; try{ return handlers["json-comment-filtered"](xhr); }catch(e){ 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). // load: Function? // function(response, ioArgs){}. response is an Object, ioArgs // is of type dojo.__IoCallbackArgs. The load function will be // called on a successful response. // error: Function? // function(response, ioArgs){}. response is an Object, ioArgs // is of type dojo.__IoCallbackArgs. The error function will // be called in an error case. // handle: Function? // function(response, ioArgs){}. response is an Object, ioArgs // is of type dojo.__IoCallbackArgs. The handle function will // be called in either the successful or error case. For // the load, error and handle functions, the ioArgs object // will contain the following properties: this.url = url; this.content = content; this.timeout = timeout; this.form = form; this.preventCache = preventCache; this.handleAs = handleAs; this.load = load; this.error = error; this.handle = handle; } =====*/ /*===== 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._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); }); } 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 == "unknown"){ xhr.abort(); } var err = new Error("xhr cancelled"); err.dojoType = "cancel"; return err; } var _deferredOk = function(/*Deferred*/dfd){ //summary: okHandler function for dojo._ioSetArgs call. return _d._contentHandlers[dfd.ioArgs.handleAs](dfd.ioArgs.xhr); } var _deferError = function(/*Error*/error, /*Deferred*/dfd){ //summary: errHandler function for dojo._ioSetArgs call. // console.debug("xhr error in:", dfd.ioArgs.xhr); console.debug(error); return error; } var _makeXhrDeferred = function(/*dojo.__XhrArgs*/args){ //summary: makes the Deferred object for this xhr request. var dfd = _d._ioSetArgs(args, _deferredCancel, _deferredOk, _deferError); //Pass the args to _xhrObj, to allow xhr iframe proxy interceptions. dfd.ioArgs.xhr = _d._xhrObj(dfd.ioArgs.args); return dfd; } // 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 = []; 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; try{ if(!dfd || dfd.canceled || !tif.validCheck(dfd)){ _inFlight.splice(i--, 1); }else if(tif.ioCheck(dfd)){ _inFlight.splice(i--, 1); tif.resHandle(dfd); }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(); } } }catch(e){ // FIXME: make sure we errback! (fixed? remove console.debug?) console.debug(e); dfd.errback(new Error("_watchInFlightError!")); } } } 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){ i.dfd.cancel(); }); }catch(e){/*squelch*/} } //Automatically call cancel all io calls on unload //in IE for trac issue #2357. if(_d.isIE){ _d.addOnUnload(_d._ioCancelAll); } _d._ioWatch = function(/*Deferred*/dfd, /*Function*/validCheck, /*Function*/ioCheck, /*Function*/resHandle){ //summary: watches the io request represented by dfd to see if it completes. //dfd: // The Deferred object to watch. //validCheck: // Function used to check if the IO request is still valid. Gets the dfd // object as its only argument. //ioCheck: // Function used to check if basic IO call worked. Gets the dfd // object as its only argument. //resHandle: // Function used to process response. Gets the dfd // object as its only argument. if(dfd.ioArgs.args.timeout){ dfd.startTime = (new Date()).getTime(); } _inFlight.push({dfd: dfd, validCheck: validCheck, ioCheck: ioCheck, resHandle: resHandle}); if(!_inFlightIntvl){ _inFlightIntvl = setInterval(_watchInFlight, 50); } _watchInFlight(); // handle sync requests } 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); } } var _doIt = function(/*String*/type, /*Deferred*/dfd){ // 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" var ioArgs = dfd.ioArgs; var args = ioArgs.args; var xhr = ioArgs.xhr; xhr.open(type, 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{ xhr.setRequestHeader(hdr, args.headers[hdr]); } } } // FIXME: is this appropriate for all content types? xhr.setRequestHeader("Content-Type", args.contentType || _defaultContentType); if(!args.headers || !args.headers["X-Requested-With"]){ xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); } // FIXME: set other headers here! try{ xhr.send(ioArgs.query); }catch(e){ dfd.cancel(); } _d._ioWatch(dfd, _validCheck, _ioCheck, _resHandle); xhr = null; return dfd; //Deferred } 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 // 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. this.handleAs = handleAs; this.sync = sync; this.headers = headers; } }); =====*/ dojo.xhr = function(/*String*/ method, /*dojo.__XhrArgs*/ args, /*Boolean?*/ hasBody){ // summary: // Sends an HTTP request with the given method. If the request has an // HTTP body, then pass true for hasBody. The method argument should be uppercase. // Also look at 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. var dfd = _makeXhrDeferred(args); if(!hasBody){ _d._ioAddQueryToUrl(dfd.ioArgs); } return _doIt(method, 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.xhrPost = function(/*dojo.__XhrArgs*/ args){ //summary: // Sends an HTTP POST request to the server. return _d.xhr("POST", args, true); // dojo.Deferred } dojo.rawXhrPost = 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. The raw data to send in the body of the POST request. var dfd = _makeXhrDeferred(args); dfd.ioArgs.query = args.postData; return _doIt("POST", dfd); // dojo.Deferred } dojo.xhrPut = function(/*dojo.__XhrArgs*/ args){ // summary: // Sends an HTTP PUT request to the server. return _d.xhr("PUT", args, true); // dojo.Deferred } dojo.rawXhrPut = 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. The raw data to send in the body of the PUT request. var dfd = _makeXhrDeferred(args); var ioArgs = dfd.ioArgs; if(args.putData){ ioArgs.query = args.putData; args.putData = null; } return _doIt("PUT", dfd); // 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"); } */ })(); } 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"); /* Animation losely package based on Dan Pupius' work, contributed under CLA: http://pupius.co.uk/js/Toolkit.Drawing.js */ (function(){ var d = dojo; 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; this.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 } } d.declare("dojo._Animation", null, { // summary // 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. constructor: function(/*Object*/ args){ d.mixin(this, args); if(d.isArray(this.curve)){ /* curve: Array pId: a */ this.curve = new d._Line(this.curve[0], this.curve[1]); } }, // 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: rate/1000 = fps) rate: 10 /* 100 fps */, /*===== // delay: Integer // The time in milliseconds to wait before starting animation after it has been .play()'ed delay: null, // events // // 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, _fire: function(/*Event*/ evt, /*Array?*/ args){ // summary: // Convenience function. Fire event "evt" and pass it the // arguments specified in "args". // evt: // The event to fire. // args: // The arguments to pass to the event. try{ if(this[evt]){ this[evt].apply(this, args||[]); } }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. var _t = this; if(gotoStart){ _t._stopTimer(); _t._active = _t._paused = false; _t._percent = 0; }else if(_t._active && !_t._paused){ return _t; // dojo._Animation } _t._fire("beforeBegin"); var de = delay||_t.delay; var _p = dojo.hitch(_t, "_play", gotoStart); if(de > 0){ setTimeout(_p, de); return _t; // dojo._Animation } _p(); return _t; }, _play: function(gotoStart){ var _t = this; _t._startTime = new Date().valueOf(); if(_t._paused){ _t._startTime -= _t.duration * _t._percent; } _t._endTime = _t._startTime + _t.duration; _t._active = true; _t._paused = false; var value = _t.curve.getValue(_t._percent); 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. this._stopTimer(); if(!this._active){ return this; /*dojo._Animation*/ } this._paused = true; this._fire("onPause", [this.curve.getValue(this._percent)]); return this; // 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. this._stopTimer(); this._active = this._paused = true; this._percent = percent; if(andPlay){ this.play(); } return this; // dojo._Animation }, stop: function(/*boolean?*/ gotoEnd){ // summary: Stops a running animation. // gotoEnd: If true, the animation will end. if(!this._timer){ return this; /* dojo._Animation */ } this._stopTimer(); if(gotoEnd){ this._percent = 1; } this._fire("onStop", [this.curve.getValue(this._percent)]); this._active = this._paused = false; return this; // 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._endTime - _t._startTime); 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._stopTimer(); } } return _t; // dojo._Animation } }); var ctr = 0; var _globalTimerList = []; var runner = { run: function(){ } }; var timer = null; dojo._Animation.prototype._startTimer = function(){ // this._timer = setTimeout(dojo.hitch(this, "_cycle"), this.rate); if(!this._timer){ this._timer = d.connect(runner, "run", this, "_cycle"); ctr++; } if(!timer){ timer = setInterval(d.hitch(runner, "run"), this.rate); } }; dojo._Animation.prototype._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; if(!ns.zoom.length && d.style(node, "zoom") == "normal"){ // make sure the node "hasLayout" // NOTE: this has been tested with larger and smaller user-set text // sizes and works fine ns.zoom = "1"; // node.style.zoom = "normal"; } // 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 = d.mixin({ properties: {} }, args); var props = (fArgs.properties.opacity = {}); props.start = !("start" in fArgs) ? function(){ return Number(d.style(fArgs.node, "opacity")); } : 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(d.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(d.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(); } } this.getValue = function(r){ var ret = {}; for(var p in this._properties){ var prop = this._properties[p]; var 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" : ""); } } 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 'unit' 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 // example: // animate width, height, and padding over 2 seconds...the // pedantic way: // | dojo.animateProperty({ node: node, duration:2000, // | properties: { // | width: { start: '200', end: '400', unit:"px" }, // | height: { start:'200', end: '400', unit:"px" }, // | paddingTop: { start:'5', end:'50', unit:"px" } // | } // | }).play(); // // 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(){ // | // called when the animation finishes // | } // | }).play(500); // delay playing half a second 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]; prop = pm[p] = d.mixin({}, (d.isObject(prop) ? prop: { end: prop })); if(d.isFunction(prop.start)){ prop.start = prop.start(); } if(d.isFunction(prop.end)){ prop.end = prop.end(); } 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") ? Number(v) : (isColor ? v : parseFloat(v)); } if(!("end" in prop)){ prop.end = getStyle(this.node, p); }else if(!("start" in prop)){ prop.start = getStyle(this.node, p); } if(isColor){ prop.start = new d.Color(prop.start); prop.end = new d.Color(prop.end); }else{ prop.start = (p == "opacity") ? Number(prop.start) : parseFloat(prop.start); } } this.curve = new PropLine(pm); }); d.connect(anim, "onAnimate", anim, function(propValues){ // try{ for(var s in propValues){ d.style(this.node, s, propValues[s]); // this.node.style[s] = propValues[s]; } }); 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 `dojox.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({ node: node, duration: duration||d._Animation.prototype.duration, properties: properties, easing: easing, onEnd: onEnd }).play(delay||0); } })(); } 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"); //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. if(dojo.config.require){ dojo.forEach(dojo.config.require, "dojo['require'](item);"); } } if(dojo.config.afterOnLoad && dojo.isBrowser){ //Dojo is being added to the page after page load, so just trigger //the init sequence after a timeout. Using a timeout so the rest of this //script gets evaluated properly. This work needs to happen after the //dojo.config.require work done in dojo._base. window.setTimeout(dojo._fakeLoadInit, 1000); } })();