[Pkg-javascript-commits] [dojo] 02/87: Set svn:eol-style to 'native' on files missing it. refs #7776 !strict
David Prévot
taffit at moszumanska.debian.org
Thu Aug 21 17:39:13 UTC 2014
This is an automated email from the git hooks/post-receive script.
taffit pushed a commit to annotated tag 1.7.5
in repository dojo.
commit 961b9d4eae0af09e6faba2eec1510c453decfc00
Author: Bryan Forbes <bryan at reigndropsfall.net>
Date: Fri Oct 28 20:54:48 2011 +0000
Set svn:eol-style to 'native' on files missing it. refs #7776 !strict
git-svn-id: http://svn.dojotoolkit.org/src/branches/1.7/dojo@26925 560b804f-0ae3-0310-86f3-f6aa0a117693
---
Evented.js | 64 +-
aspect.js | 408 +++----
json.js | 298 +++---
keys.js | 160 +--
mouse.js | 254 ++---
on.js | 946 ++++++++--------
query.js | 1424 ++++++++++++-------------
selector/_loader.js | 90 +-
selector/acme.js | 2960 +++++++++++++++++++++++++--------------------------
selector/lite.js | 528 ++++-----
store/api/Store.js | 594 +++++------
tests/aspect.js | 238 ++---
tests/json.js | 332 +++---
tests/on.js | 458 ++++----
topic.js | 66 +-
15 files changed, 4410 insertions(+), 4410 deletions(-)
diff --git a/Evented.js b/Evented.js
index 955cbc4..420dfca 100644
--- a/Evented.js
+++ b/Evented.js
@@ -1,32 +1,32 @@
-define(["./aspect", "./on"], function(aspect, on){
- // summary:
- // The export of this module is a class that can be used as a mixin or base class,
- // to add on() and emit() methods to a class
- // for listening for events and emiting events:
- // |define(["dojo/Evented"], function(Evented){
- // | var EventedWidget = dojo.declare([Evented, dijit._Widget], {...});
- // | widget = new EventedWidget();
- // | widget.on("open", function(event){
- // | ... do something with event
- // | });
- // |
- // | widget.emit("open", {name:"some event", ...});
-
- "use strict";
- var after = aspect.after;
- function Evented(){
- }
- Evented.prototype = {
- on: function(type, listener){
- return on.parse(this, type, listener, function(target, type){
- return after(target, 'on' + type, listener, true);
- });
- },
- emit: function(type, event){
- var args = [this];
- args.push.apply(args, arguments);
- return on.emit.apply(on, args);
- }
- };
- return Evented;
-});
+define(["./aspect", "./on"], function(aspect, on){
+ // summary:
+ // The export of this module is a class that can be used as a mixin or base class,
+ // to add on() and emit() methods to a class
+ // for listening for events and emiting events:
+ // |define(["dojo/Evented"], function(Evented){
+ // | var EventedWidget = dojo.declare([Evented, dijit._Widget], {...});
+ // | widget = new EventedWidget();
+ // | widget.on("open", function(event){
+ // | ... do something with event
+ // | });
+ // |
+ // | widget.emit("open", {name:"some event", ...});
+
+ "use strict";
+ var after = aspect.after;
+ function Evented(){
+ }
+ Evented.prototype = {
+ on: function(type, listener){
+ return on.parse(this, type, listener, function(target, type){
+ return after(target, 'on' + type, listener, true);
+ });
+ },
+ emit: function(type, event){
+ var args = [this];
+ args.push.apply(args, arguments);
+ return on.emit.apply(on, args);
+ }
+ };
+ return Evented;
+});
diff --git a/aspect.js b/aspect.js
index 163febd..faa149a 100644
--- a/aspect.js
+++ b/aspect.js
@@ -1,204 +1,204 @@
-define([], function(){
-
-// TODOC: after/before/around return object
-// TODOC: after/before/around param types.
-
-/*=====
- dojo.aspect = {
- // summary: provides aspect oriented programming functionality, allowing for
- // one to add before, around, or after advice on existing methods.
- //
- // example:
- // | define(["dojo/aspect"], function(aspect){
- // | var signal = aspect.after(targetObject, "methodName", function(someArgument){
- // | this will be called when targetObject.methodName() is called, after the original function is called
- // | });
- //
- // example:
- // The returned signal object can be used to cancel the advice.
- // | signal.remove(); // this will stop the advice from being executed anymore
- // | aspect.before(targetObject, "methodName", function(someArgument){
- // | // this will be called when targetObject.methodName() is called, before the original function is called
- // | });
-
- after: function(target, methodName, advice, receiveArguments){
- // summary: The "after" export of the aspect module is a function that can be used to attach
- // "after" advice to a method. This function will be executed after the original method
- // is executed. By default the function will be called with a single argument, the return
- // value of the original method, or the the return value of the last executed advice (if a previous one exists).
- // The fourth (optional) argument can be set to true to so the function receives the original
- // arguments (from when the original method was called) rather than the return value.
- // If there are multiple "after" advisors, they are executed in the order they were registered.
- // target: Object
- // This is the target object
- // methodName: String
- // This is the name of the method to attach to.
- // advice: Function
- // This is function to be called after the original method
- // receiveArguments: Boolean?
- // If this is set to true, the advice function receives the original arguments (from when the original mehtod
- // was called) rather than the return value of the original/previous method.
- // returns:
- // A signal object that can be used to cancel the advice. If remove() is called on this signal object, it will
- // stop the advice function from being executed.
- },
-
- before: function(target, methodName, advice){
- // summary: The "before" export of the aspect module is a function that can be used to attach
- // "before" advice to a method. This function will be executed before the original method
- // is executed. This function will be called with the arguments used to call the method.
- // This function may optionally return an array as the new arguments to use to call
- // the original method (or the previous, next-to-execute before advice, if one exists).
- // If the before method doesn't return anything (returns undefined) the original arguments
- // will be preserved.
- // If there are multiple "before" advisors, they are executed in the reverse order they were registered.
- //
- // target: Object
- // This is the target object
- // methodName: String
- // This is the name of the method to attach to.
- // advice: Function
- // This is function to be called before the original method
- },
-
- around: function(target, methodName, advice){
- // summary: The "around" export of the aspect module is a function that can be used to attach
- // "around" advice to a method. The advisor function is immediately executed when
- // the around() is called, is passed a single argument that is a function that can be
- // called to continue execution of the original method (or the next around advisor).
- // The advisor function should return a function, and this function will be called whenever
- // the method is called. It will be called with the arguments used to call the method.
- // Whatever this function returns will be returned as the result of the method call (unless after advise changes it).
- //
- // example:
- // If there are multiple "around" advisors, the most recent one is executed first,
- // which can then delegate to the next one and so on. For example:
- // | around(obj, "foo", function(originalFoo){
- // | return function(){
- // | var start = new Date().getTime();
- // | var results = originalFoo.apply(this, arguments); // call the original
- // | var end = new Date().getTime();
- // | console.log("foo execution took " + (end - start) + " ms");
- // | return results;
- // | };
- // | });
- //
- // target: Object
- // This is the target object
- // methodName: String
- // This is the name of the method to attach to.
- // advice: Function
- // This is function to be called around the original method
- }
-
- };
-=====*/
-
- "use strict";
- function advise(dispatcher, type, advice, receiveArguments){
- var previous = dispatcher[type];
- var around = type == "around";
- var signal;
- if(around){
- var advised = advice(function(){
- return previous.advice(this, arguments);
- });
- signal = {
- remove: function(){
- signal.cancelled = true;
- },
- advice: function(target, args){
- return signal.cancelled ?
- previous.advice(target, args) : // cancelled, skip to next one
- advised.apply(target, args); // called the advised function
- }
- };
- }else{
- // create the remove handler
- signal = {
- remove: function(){
- var previous = signal.previous;
- var next = signal.next;
- if(!next && !previous){
- delete dispatcher[type];
- }else{
- if(previous){
- previous.next = next;
- }else{
- dispatcher[type] = next;
- }
- if(next){
- next.previous = previous;
- }
- }
- },
- advice: advice,
- receiveArguments: receiveArguments
- };
- }
- if(previous && !around){
- if(type == "after"){
- // add the listener to the end of the list
- var next = previous;
- while(next){
- previous = next;
- next = next.next;
- }
- previous.next = signal;
- signal.previous = previous;
- }else if(type == "before"){
- // add to beginning
- dispatcher[type] = signal;
- signal.next = previous;
- previous.previous = signal;
- }
- }else{
- // around or first one just replaces
- dispatcher[type] = signal;
- }
- return signal;
- }
- function aspect(type){
- return function(target, methodName, advice, receiveArguments){
- var existing = target[methodName], dispatcher;
- if(!existing || existing.target != target){
- // no dispatcher in place
- dispatcher = target[methodName] = function(){
- // before advice
- var args = arguments;
- var before = dispatcher.before;
- while(before){
- args = before.advice.apply(this, args) || args;
- before = before.next;
- }
- // around advice
- if(dispatcher.around){
- var results = dispatcher.around.advice(this, args);
- }
- // after advice
- var after = dispatcher.after;
- while(after){
- results = after.receiveArguments ? after.advice.apply(this, args) || results :
- after.advice.call(this, results);
- after = after.next;
- }
- return results;
- };
- if(existing){
- dispatcher.around = {advice: function(target, args){
- return existing.apply(target, args);
- }};
- }
- dispatcher.target = target;
- }
- var results = advise((dispatcher || existing), type, advice, receiveArguments);
- advice = null;
- return results;
- };
- }
- return {
- before: aspect("before"),
- around: aspect("around"),
- after: aspect("after")
- };
-});
+define([], function(){
+
+// TODOC: after/before/around return object
+// TODOC: after/before/around param types.
+
+/*=====
+ dojo.aspect = {
+ // summary: provides aspect oriented programming functionality, allowing for
+ // one to add before, around, or after advice on existing methods.
+ //
+ // example:
+ // | define(["dojo/aspect"], function(aspect){
+ // | var signal = aspect.after(targetObject, "methodName", function(someArgument){
+ // | this will be called when targetObject.methodName() is called, after the original function is called
+ // | });
+ //
+ // example:
+ // The returned signal object can be used to cancel the advice.
+ // | signal.remove(); // this will stop the advice from being executed anymore
+ // | aspect.before(targetObject, "methodName", function(someArgument){
+ // | // this will be called when targetObject.methodName() is called, before the original function is called
+ // | });
+
+ after: function(target, methodName, advice, receiveArguments){
+ // summary: The "after" export of the aspect module is a function that can be used to attach
+ // "after" advice to a method. This function will be executed after the original method
+ // is executed. By default the function will be called with a single argument, the return
+ // value of the original method, or the the return value of the last executed advice (if a previous one exists).
+ // The fourth (optional) argument can be set to true to so the function receives the original
+ // arguments (from when the original method was called) rather than the return value.
+ // If there are multiple "after" advisors, they are executed in the order they were registered.
+ // target: Object
+ // This is the target object
+ // methodName: String
+ // This is the name of the method to attach to.
+ // advice: Function
+ // This is function to be called after the original method
+ // receiveArguments: Boolean?
+ // If this is set to true, the advice function receives the original arguments (from when the original mehtod
+ // was called) rather than the return value of the original/previous method.
+ // returns:
+ // A signal object that can be used to cancel the advice. If remove() is called on this signal object, it will
+ // stop the advice function from being executed.
+ },
+
+ before: function(target, methodName, advice){
+ // summary: The "before" export of the aspect module is a function that can be used to attach
+ // "before" advice to a method. This function will be executed before the original method
+ // is executed. This function will be called with the arguments used to call the method.
+ // This function may optionally return an array as the new arguments to use to call
+ // the original method (or the previous, next-to-execute before advice, if one exists).
+ // If the before method doesn't return anything (returns undefined) the original arguments
+ // will be preserved.
+ // If there are multiple "before" advisors, they are executed in the reverse order they were registered.
+ //
+ // target: Object
+ // This is the target object
+ // methodName: String
+ // This is the name of the method to attach to.
+ // advice: Function
+ // This is function to be called before the original method
+ },
+
+ around: function(target, methodName, advice){
+ // summary: The "around" export of the aspect module is a function that can be used to attach
+ // "around" advice to a method. The advisor function is immediately executed when
+ // the around() is called, is passed a single argument that is a function that can be
+ // called to continue execution of the original method (or the next around advisor).
+ // The advisor function should return a function, and this function will be called whenever
+ // the method is called. It will be called with the arguments used to call the method.
+ // Whatever this function returns will be returned as the result of the method call (unless after advise changes it).
+ //
+ // example:
+ // If there are multiple "around" advisors, the most recent one is executed first,
+ // which can then delegate to the next one and so on. For example:
+ // | around(obj, "foo", function(originalFoo){
+ // | return function(){
+ // | var start = new Date().getTime();
+ // | var results = originalFoo.apply(this, arguments); // call the original
+ // | var end = new Date().getTime();
+ // | console.log("foo execution took " + (end - start) + " ms");
+ // | return results;
+ // | };
+ // | });
+ //
+ // target: Object
+ // This is the target object
+ // methodName: String
+ // This is the name of the method to attach to.
+ // advice: Function
+ // This is function to be called around the original method
+ }
+
+ };
+=====*/
+
+ "use strict";
+ function advise(dispatcher, type, advice, receiveArguments){
+ var previous = dispatcher[type];
+ var around = type == "around";
+ var signal;
+ if(around){
+ var advised = advice(function(){
+ return previous.advice(this, arguments);
+ });
+ signal = {
+ remove: function(){
+ signal.cancelled = true;
+ },
+ advice: function(target, args){
+ return signal.cancelled ?
+ previous.advice(target, args) : // cancelled, skip to next one
+ advised.apply(target, args); // called the advised function
+ }
+ };
+ }else{
+ // create the remove handler
+ signal = {
+ remove: function(){
+ var previous = signal.previous;
+ var next = signal.next;
+ if(!next && !previous){
+ delete dispatcher[type];
+ }else{
+ if(previous){
+ previous.next = next;
+ }else{
+ dispatcher[type] = next;
+ }
+ if(next){
+ next.previous = previous;
+ }
+ }
+ },
+ advice: advice,
+ receiveArguments: receiveArguments
+ };
+ }
+ if(previous && !around){
+ if(type == "after"){
+ // add the listener to the end of the list
+ var next = previous;
+ while(next){
+ previous = next;
+ next = next.next;
+ }
+ previous.next = signal;
+ signal.previous = previous;
+ }else if(type == "before"){
+ // add to beginning
+ dispatcher[type] = signal;
+ signal.next = previous;
+ previous.previous = signal;
+ }
+ }else{
+ // around or first one just replaces
+ dispatcher[type] = signal;
+ }
+ return signal;
+ }
+ function aspect(type){
+ return function(target, methodName, advice, receiveArguments){
+ var existing = target[methodName], dispatcher;
+ if(!existing || existing.target != target){
+ // no dispatcher in place
+ dispatcher = target[methodName] = function(){
+ // before advice
+ var args = arguments;
+ var before = dispatcher.before;
+ while(before){
+ args = before.advice.apply(this, args) || args;
+ before = before.next;
+ }
+ // around advice
+ if(dispatcher.around){
+ var results = dispatcher.around.advice(this, args);
+ }
+ // after advice
+ var after = dispatcher.after;
+ while(after){
+ results = after.receiveArguments ? after.advice.apply(this, args) || results :
+ after.advice.call(this, results);
+ after = after.next;
+ }
+ return results;
+ };
+ if(existing){
+ dispatcher.around = {advice: function(target, args){
+ return existing.apply(target, args);
+ }};
+ }
+ dispatcher.target = target;
+ }
+ var results = advise((dispatcher || existing), type, advice, receiveArguments);
+ advice = null;
+ return results;
+ };
+ }
+ return {
+ before: aspect("before"),
+ around: aspect("around"),
+ after: aspect("after")
+ };
+});
diff --git a/json.js b/json.js
index 798a4c6..b975b29 100644
--- a/json.js
+++ b/json.js
@@ -1,149 +1,149 @@
-define(["./has"], function(has){
- "use strict";
- var hasJSON = typeof JSON != "undefined";
- has.add("json-parse", hasJSON); // all the parsers work fine
- // Firefox 3.5/Gecko 1.9 fails to use replacer in stringify properly https://bugzilla.mozilla.org/show_bug.cgi?id=509184
- has.add("json-stringify", hasJSON && JSON.stringify({a:0}, function(k,v){return v||1;}) == '{"a":1}');
- if(has("json-stringify")){
- return JSON;
- }
- else{
- var 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
- };
- return {
- parse: has("json-parse") ? JSON.parse : function(str, strict){
- // summary:
- // Parses a [JSON](http://json.org) string to return a JavaScript object.
- // description:
- // This function follows [native JSON API](https://developer.mozilla.org/en/JSON)
- // Throws for invalid JSON strings. This delegates to eval() if native JSON
- // support is not available. By default this will evaluate any valid JS expression.
- // With the strict parameter set to true, the parser will ensure that only
- // valid JSON strings are parsed (otherwise throwing an error). Without the strict
- // parameter, the content passed to this method must come
- // from a trusted source.
- // str:
- // a string literal of a JSON item, for instance:
- // `'{ "foo": [ "bar", 1, { "baz": "thud" } ] }'`
- // strict:
- // When set to true, this will ensure that only valid, secure JSON is ever parsed.
- // Make sure this is set to true for untrusted content. Note that on browsers/engines
- // without native JSON support, setting this to true will run slower.
- if(strict && !/^([\s\[\{]*(?:"(?:\\.|[^"])+"|-?\d[\d\.]*(?:[Ee][+-]?\d+)?|null|true|false|)[\s\]\}]*(?:,|:|$))+$/.test(str)){
- throw new SyntaxError("Invalid characters in JSON");
- }
- return eval('(' + str + ')');
- },
- stringify: function(value, replacer, spacer){
- // summary:
- // Returns a [JSON](http://json.org) serialization of an object.
- // description:
- // Returns a [JSON](http://json.org) serialization of an object.
- // This function follows [native JSON API](https://developer.mozilla.org/en/JSON)
- // Note that this doesn't check for infinite recursion, so don't do that!
- // value:
- // A value to be serialized.
- // replacer:
- // A replacer function that is called for each value and can return a replacement
- // spacer:
- // A spacer string to be used for pretty printing of JSON
- //
- // example:
- // simple serialization of a trivial object
- // | define(["dojo/json"], function(JSON){
- // | var jsonStr = JSON.stringify({ howdy: "stranger!", isStrange: true });
- // | doh.is('{"howdy":"stranger!","isStrange":true}', jsonStr);
- var undef;
- if(typeof replacer == "string"){
- spacer = replacer;
- replacer = null;
- }
- function stringify(it, indent, key){
- if(replacer){
- it = replacer(key, it);
- }
- var val, objtype = typeof it;
- if(objtype == "number"){
- return isFinite(it) ? it + "" : "null";
- }
- if(objtype == "boolean"){
- return it + "";
- }
- if(it === null){
- return "null";
- }
- if(typeof it == "string"){
- return escapeString(it);
- }
- if(objtype == "function" || objtype == "undefined"){
- return undef; // undefined
- }
- // short-circuit for objects that support "json" serialization
- // if they return "self" then just pass-through...
- if(typeof it.toJSON == "function"){
- return stringify(it.toJSON(key), indent, key);
- }
- if(it instanceof Date){
- return '"{FullYear}-{Month+}-{Date}T{Hours}:{Minutes}:{Seconds}Z"'.replace(/\{(\w+)(\+)?\}/g, function(t, prop, plus){
- var num = it["getUTC" + prop]() + (plus ? 1 : 0);
- return num < 10 ? "0" + num : num;
- });
- }
- if(it.valueOf() !== it){
- // primitive wrapper, try again unwrapped:
- return stringify(it.valueOf(), indent, key);
- }
- var nextIndent= spacer ? (indent + spacer) : "";
- /* we used to test for DOM nodes and throw, but FF serializes them as {}, so cross-browser consistency is probably not efficiently attainable */
-
- var sep = spacer ? " " : "";
- var newLine = spacer ? "\n" : "";
-
- // array
- if(it instanceof Array){
- var itl = it.length, res = [];
- for(key = 0; key < itl; key++){
- var obj = it[key];
- val = stringify(obj, nextIndent, key);
- if(typeof val != "string"){
- val = "null";
- }
- res.push(newLine + nextIndent + val);
- }
- return "[" + res.join(",") + newLine + indent + "]";
- }
- // generic object code path
- var output = [];
- for(key in it){
- var keyStr;
- if(typeof key == "number"){
- keyStr = '"' + key + '"';
- }else if(typeof key == "string"){
- keyStr = escapeString(key);
- }else{
- // skip non-string or number keys
- continue;
- }
- val = stringify(it[key], nextIndent, key);
- if(typeof val != "string"){
- // skip non-serializable values
- continue;
- }
- // At this point, the most non-IE browsers don't get in this branch
- // (they have native JSON), so push is definitely the way to
- output.push(newLine + nextIndent + keyStr + ":" + sep + val);
- }
- return "{" + output.join(",") + newLine + indent + "}"; // String
- }
- return stringify(value, "", "");
- }
- };
- }
-});
+define(["./has"], function(has){
+ "use strict";
+ var hasJSON = typeof JSON != "undefined";
+ has.add("json-parse", hasJSON); // all the parsers work fine
+ // Firefox 3.5/Gecko 1.9 fails to use replacer in stringify properly https://bugzilla.mozilla.org/show_bug.cgi?id=509184
+ has.add("json-stringify", hasJSON && JSON.stringify({a:0}, function(k,v){return v||1;}) == '{"a":1}');
+ if(has("json-stringify")){
+ return JSON;
+ }
+ else{
+ var 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
+ };
+ return {
+ parse: has("json-parse") ? JSON.parse : function(str, strict){
+ // summary:
+ // Parses a [JSON](http://json.org) string to return a JavaScript object.
+ // description:
+ // This function follows [native JSON API](https://developer.mozilla.org/en/JSON)
+ // Throws for invalid JSON strings. This delegates to eval() if native JSON
+ // support is not available. By default this will evaluate any valid JS expression.
+ // With the strict parameter set to true, the parser will ensure that only
+ // valid JSON strings are parsed (otherwise throwing an error). Without the strict
+ // parameter, the content passed to this method must come
+ // from a trusted source.
+ // str:
+ // a string literal of a JSON item, for instance:
+ // `'{ "foo": [ "bar", 1, { "baz": "thud" } ] }'`
+ // strict:
+ // When set to true, this will ensure that only valid, secure JSON is ever parsed.
+ // Make sure this is set to true for untrusted content. Note that on browsers/engines
+ // without native JSON support, setting this to true will run slower.
+ if(strict && !/^([\s\[\{]*(?:"(?:\\.|[^"])+"|-?\d[\d\.]*(?:[Ee][+-]?\d+)?|null|true|false|)[\s\]\}]*(?:,|:|$))+$/.test(str)){
+ throw new SyntaxError("Invalid characters in JSON");
+ }
+ return eval('(' + str + ')');
+ },
+ stringify: function(value, replacer, spacer){
+ // summary:
+ // Returns a [JSON](http://json.org) serialization of an object.
+ // description:
+ // Returns a [JSON](http://json.org) serialization of an object.
+ // This function follows [native JSON API](https://developer.mozilla.org/en/JSON)
+ // Note that this doesn't check for infinite recursion, so don't do that!
+ // value:
+ // A value to be serialized.
+ // replacer:
+ // A replacer function that is called for each value and can return a replacement
+ // spacer:
+ // A spacer string to be used for pretty printing of JSON
+ //
+ // example:
+ // simple serialization of a trivial object
+ // | define(["dojo/json"], function(JSON){
+ // | var jsonStr = JSON.stringify({ howdy: "stranger!", isStrange: true });
+ // | doh.is('{"howdy":"stranger!","isStrange":true}', jsonStr);
+ var undef;
+ if(typeof replacer == "string"){
+ spacer = replacer;
+ replacer = null;
+ }
+ function stringify(it, indent, key){
+ if(replacer){
+ it = replacer(key, it);
+ }
+ var val, objtype = typeof it;
+ if(objtype == "number"){
+ return isFinite(it) ? it + "" : "null";
+ }
+ if(objtype == "boolean"){
+ return it + "";
+ }
+ if(it === null){
+ return "null";
+ }
+ if(typeof it == "string"){
+ return escapeString(it);
+ }
+ if(objtype == "function" || objtype == "undefined"){
+ return undef; // undefined
+ }
+ // short-circuit for objects that support "json" serialization
+ // if they return "self" then just pass-through...
+ if(typeof it.toJSON == "function"){
+ return stringify(it.toJSON(key), indent, key);
+ }
+ if(it instanceof Date){
+ return '"{FullYear}-{Month+}-{Date}T{Hours}:{Minutes}:{Seconds}Z"'.replace(/\{(\w+)(\+)?\}/g, function(t, prop, plus){
+ var num = it["getUTC" + prop]() + (plus ? 1 : 0);
+ return num < 10 ? "0" + num : num;
+ });
+ }
+ if(it.valueOf() !== it){
+ // primitive wrapper, try again unwrapped:
+ return stringify(it.valueOf(), indent, key);
+ }
+ var nextIndent= spacer ? (indent + spacer) : "";
+ /* we used to test for DOM nodes and throw, but FF serializes them as {}, so cross-browser consistency is probably not efficiently attainable */
+
+ var sep = spacer ? " " : "";
+ var newLine = spacer ? "\n" : "";
+
+ // array
+ if(it instanceof Array){
+ var itl = it.length, res = [];
+ for(key = 0; key < itl; key++){
+ var obj = it[key];
+ val = stringify(obj, nextIndent, key);
+ if(typeof val != "string"){
+ val = "null";
+ }
+ res.push(newLine + nextIndent + val);
+ }
+ return "[" + res.join(",") + newLine + indent + "]";
+ }
+ // generic object code path
+ var output = [];
+ for(key in it){
+ var keyStr;
+ if(typeof key == "number"){
+ keyStr = '"' + key + '"';
+ }else if(typeof key == "string"){
+ keyStr = escapeString(key);
+ }else{
+ // skip non-string or number keys
+ continue;
+ }
+ val = stringify(it[key], nextIndent, key);
+ if(typeof val != "string"){
+ // skip non-serializable values
+ continue;
+ }
+ // At this point, the most non-IE browsers don't get in this branch
+ // (they have native JSON), so push is definitely the way to
+ output.push(newLine + nextIndent + keyStr + ":" + sep + val);
+ }
+ return "{" + output.join(",") + newLine + indent + "}"; // String
+ }
+ return stringify(value, "", "");
+ }
+ };
+ }
+});
diff --git a/keys.js b/keys.js
index d2bfc00..3347e74 100644
--- a/keys.js
+++ b/keys.js
@@ -1,80 +1,80 @@
-define(["./_base/kernel", "./_base/sniff"], function(dojo, has) {
- // module:
- // dojo/keys
- // summary:
- // key constants
-// Constants
-
-// Public: client code should test
-// keyCode against these named constants, as the
-// actual codes can vary by browser.
-return dojo.keys = {
- // summary:
- // Definitions for common key values
- BACKSPACE: 8,
- TAB: 9,
- CLEAR: 12,
- ENTER: 13,
- SHIFT: 16,
- CTRL: 17,
- ALT: 18,
- META: has("safari") ? 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,
- UP_DPAD: 175,
- DOWN_DPAD: 176,
- LEFT_DPAD: 177,
- RIGHT_DPAD: 178,
- // virtual key mapping
- copyKey: has("mac") && !has("air") ? (has("safari") ? 91 : 224 ) : 17
-};
-});
+define(["./_base/kernel", "./_base/sniff"], function(dojo, has) {
+ // module:
+ // dojo/keys
+ // summary:
+ // key constants
+// Constants
+
+// Public: client code should test
+// keyCode against these named constants, as the
+// actual codes can vary by browser.
+return dojo.keys = {
+ // summary:
+ // Definitions for common key values
+ BACKSPACE: 8,
+ TAB: 9,
+ CLEAR: 12,
+ ENTER: 13,
+ SHIFT: 16,
+ CTRL: 17,
+ ALT: 18,
+ META: has("safari") ? 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,
+ UP_DPAD: 175,
+ DOWN_DPAD: 176,
+ LEFT_DPAD: 177,
+ RIGHT_DPAD: 178,
+ // virtual key mapping
+ copyKey: has("mac") && !has("air") ? (has("safari") ? 91 : 224 ) : 17
+};
+});
diff --git a/mouse.js b/mouse.js
index 48a0f86..9d47f82 100644
--- a/mouse.js
+++ b/mouse.js
@@ -1,127 +1,127 @@
-define(["./_base/kernel", "./on", "./has", "./dom", "./_base/window"], function(dojo, on, has, dom, win){
-
- /*=====
- dojo.mouse = {
- // summary:
- // This module provide mouse event handling utility functions and exports
- // mouseenter and mouseleave event emulation.
- // enter: Synthetic Event
- // This is an extension event for the mouseenter that IE provides, emulating the
- // behavior on other browsers.
- // leave: Synthetic Event
- // This is an extension event for the mouseleave that IE provides, emulating the
- // behavior on other browsers.
- // isLeft: Function
- // Test an event object (from a mousedown event) to see if the left button was pressed.
- // isMiddle: Function
- // Test an event object (from a mousedown event) to see if the middle button was pressed.
- // isRight: Function
- // Test an event object (from a mousedown event) to see if the right button was pressed.
- // example:
- // To use these events, you register a mouseenter like this:
- // | define(["dojo/on", dojo/mouse"], function(on, mouse){
- // | on(targetNode, mouse.enter, function(event){
- // | dojo.addClass(targetNode, "highlighted");
- // | });
- // | on(targetNode, mouse.leave, function(event){
- // | dojo.removeClass(targetNode, "highlighted");
- // | });
- };
- ======*/
-
- has.add("dom-quirks", win.doc && win.doc.compatMode == "BackCompat");
- has.add("events-mouseenter", win.doc && "onmouseenter" in win.doc.createElement("div"));
-
- var mouseButtons;
- if(has("dom-quirks") || !has("dom-addeventlistener")){
- 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{
- 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; }
- };
- }
- dojo.mouseButtons = mouseButtons;
-
-/*=====
- 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
- }
- };
-=====*/
-
- function eventHandler(type, mustBubble){
- // emulation of mouseenter/leave with mouseover/out using descendant checking
- var handler = function(node, listener){
- return on(node, type, function(evt){
- if(!dom.isDescendant(evt.relatedTarget, mustBubble ? evt.target : node)){
- return listener.call(this, evt);
- }
- });
- };
- if(!mustBubble){
- handler.bubble = eventHandler(type, true);
- }
- return handler;
- }
- return {
- enter: eventHandler("mouseover"),
- leave: eventHandler("mouseout"),
- isLeft: mouseButtons.isLeft,
- isMiddle: mouseButtons.isMiddle,
- isRight: mouseButtons.isRight
- };
-});
+define(["./_base/kernel", "./on", "./has", "./dom", "./_base/window"], function(dojo, on, has, dom, win){
+
+ /*=====
+ dojo.mouse = {
+ // summary:
+ // This module provide mouse event handling utility functions and exports
+ // mouseenter and mouseleave event emulation.
+ // enter: Synthetic Event
+ // This is an extension event for the mouseenter that IE provides, emulating the
+ // behavior on other browsers.
+ // leave: Synthetic Event
+ // This is an extension event for the mouseleave that IE provides, emulating the
+ // behavior on other browsers.
+ // isLeft: Function
+ // Test an event object (from a mousedown event) to see if the left button was pressed.
+ // isMiddle: Function
+ // Test an event object (from a mousedown event) to see if the middle button was pressed.
+ // isRight: Function
+ // Test an event object (from a mousedown event) to see if the right button was pressed.
+ // example:
+ // To use these events, you register a mouseenter like this:
+ // | define(["dojo/on", dojo/mouse"], function(on, mouse){
+ // | on(targetNode, mouse.enter, function(event){
+ // | dojo.addClass(targetNode, "highlighted");
+ // | });
+ // | on(targetNode, mouse.leave, function(event){
+ // | dojo.removeClass(targetNode, "highlighted");
+ // | });
+ };
+ ======*/
+
+ has.add("dom-quirks", win.doc && win.doc.compatMode == "BackCompat");
+ has.add("events-mouseenter", win.doc && "onmouseenter" in win.doc.createElement("div"));
+
+ var mouseButtons;
+ if(has("dom-quirks") || !has("dom-addeventlistener")){
+ 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{
+ 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; }
+ };
+ }
+ dojo.mouseButtons = mouseButtons;
+
+/*=====
+ 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
+ }
+ };
+=====*/
+
+ function eventHandler(type, mustBubble){
+ // emulation of mouseenter/leave with mouseover/out using descendant checking
+ var handler = function(node, listener){
+ return on(node, type, function(evt){
+ if(!dom.isDescendant(evt.relatedTarget, mustBubble ? evt.target : node)){
+ return listener.call(this, evt);
+ }
+ });
+ };
+ if(!mustBubble){
+ handler.bubble = eventHandler(type, true);
+ }
+ return handler;
+ }
+ return {
+ enter: eventHandler("mouseover"),
+ leave: eventHandler("mouseout"),
+ isLeft: mouseButtons.isLeft,
+ isMiddle: mouseButtons.isMiddle,
+ isRight: mouseButtons.isRight
+ };
+});
diff --git a/on.js b/on.js
index 08880c9..dcc0613 100644
--- a/on.js
+++ b/on.js
@@ -1,473 +1,473 @@
-define(["./has!dom-addeventlistener?:./aspect", "./_base/kernel", "./has"], function(aspect, dojo, has){
- // summary:
- // The export of this module is a function that provides core event listening functionality. With this function
- // you can provide a target, event type, and listener to be notified of
- // future matching events that are fired.
- // target: Element|Object
- // This is the target object or DOM element that to receive events from
- // type: String|Function
- // This is the name of the event to listen for or an extension event type.
- // listener: Function
- // This is the function that should be called when the event fires.
- // returns: Object
- // An object with a remove() method that can be used to stop listening for this
- // event.
- // description:
- // To listen for "click" events on a button node, we can do:
- // | define(["dojo/on"], function(listen){
- // | on(button, "click", clickHandler);
- // | ...
- // Evented JavaScript objects can also have their own events.
- // | var obj = new Evented;
- // | on(obj, "foo", fooHandler);
- // And then we could publish a "foo" event:
- // | on.emit(obj, "foo", {key: "value"});
- // We can use extension events as well. For example, you could listen for a tap gesture:
- // | define(["dojo/on", "dojo/gesture/tap", function(listen, tap){
- // | on(button, tap, tapHandler);
- // | ...
- // which would trigger fooHandler. Note that for a simple object this is equivalent to calling:
- // | obj.onfoo({key:"value"});
- // If you use on.emit on a DOM node, it will use native event dispatching when possible.
-
- "use strict";
- if(has("dom")){ // check to make sure we are in a browser, this module should work anywhere
- var major = window.ScriptEngineMajorVersion;
- has.add("jscript", major && (major() + ScriptEngineMinorVersion() / 10));
- has.add("event-orientationchange", has("touch") && !has("android")); // TODO: how do we detect this?
- }
- var on = function(target, type, listener, dontFix){
- if(target.on){
- // delegate to the target's on() method, so it can handle it's own listening if it wants
- return target.on(type, listener);
- }
- // delegate to main listener code
- return on.parse(target, type, listener, addListener, dontFix, this);
- };
- on.pausable = function(target, type, listener, dontFix){
- // summary:
- // This function acts the same as on(), but with pausable functionality. The
- // returned signal object has pause() and resume() functions. Calling the
- // pause() method will cause the listener to not be called for future events. Calling the
- // resume() method will cause the listener to again be called for future events.
- var paused;
- var signal = on(target, type, function(){
- if(!paused){
- return listener.apply(this, arguments);
- }
- }, dontFix);
- signal.pause = function(){
- paused = true;
- };
- signal.resume = function(){
- paused = false;
- };
- return signal;
- };
- on.once = function(target, type, listener, dontFix){
- // summary:
- // This function acts the same as on(), but will only call the listener once. The
- // listener will be called for the first
- // event that takes place and then listener will automatically be removed.
- var signal = on(target, type, function(){
- // remove this listener
- signal.remove();
- // proceed to call the listener
- return listener.apply(this, arguments);
- });
- return signal;
- };
- on.parse = function(target, type, listener, addListener, dontFix, matchesTarget){
- if(type.call){
- // event handler function
- // on(node, dojo.touch.press, touchListener);
- return type.call(matchesTarget, target, listener);
- }
-
- if(type.indexOf(",") > -1){
- // we allow comma delimited event names, so you can register for multiple events at once
- var events = type.split(/\s*,\s*/);
- var handles = [];
- var i = 0;
- var eventName;
- while(eventName = events[i++]){
- handles.push(addListener(target, eventName, listener, dontFix, matchesTarget));
- }
- handles.remove = function(){
- for(var i = 0; i < handles.length; i++){
- handles[i].remove();
- }
- };
- return handles;
- }
- return addListener(target, type, listener, dontFix, matchesTarget)
- };
- var touchEvents = /^touch/;
- function addListener(target, type, listener, dontFix, matchesTarget){
- // event delegation:
- var selector = type.match(/(.*):(.*)/);
- // if we have a selector:event, the last one is interpreted as an event, and we use event delegation
- if(selector){
- type = selector[2];
- selector = selector[1];
- // create the extension event for selectors and directly call it
- return on.selector(selector, type).call(matchesTarget, target, listener);
- }
- // test to see if it a touch event right now, so we don't have to do it every time it fires
- if(has("touch")){
- if(touchEvents.test(type)){
- // touch event, fix it
- listener = fixTouchListener(listener);
- }
- if(!has("event-orientationchange") && (type == "orientationchange")){
- //"orientationchange" not supported <= Android 2.1,
- //but works through "resize" on window
- type = "resize";
- target = window;
- listener = fixTouchListener(listener);
- }
- }
- // normal path, the target is |this|
- if(target.addEventListener){
- // the target has addEventListener, which should be used if available (might or might not be a node, non-nodes can implement this method as well)
- // check for capture conversions
- var capture = type in captures;
- target.addEventListener(capture ? captures[type] : type, listener, capture);
- // create and return the signal
- return {
- remove: function(){
- target.removeEventListener(type, listener, capture);
- }
- };
- }
- type = "on" + type;
- if(fixAttach && target.attachEvent){
- return fixAttach(target, type, listener);
- }
- throw new Error("Target must be an event emitter");
- }
-
- on.selector = function(selector, eventType, children){
- // summary:
- // Creates a new extension event with event delegation. This is based on
- // the provided event type (can be extension event) that
- // only calls the listener when the CSS selector matches the target of the event.
- // selector:
- // The CSS selector to use for filter events and determine the |this| of the event listener.
- // eventType:
- // The event to listen for
- // children:
- // Indicates if children elements of the selector should be allowed. This defaults to
- // true (except in the case of normally non-bubbling events like mouse.enter, in which case it defaults to false).
- // example:
- // define(["dojo/on", "dojo/mouse"], function(listen, mouse){
- // on(node, on.selector(".my-class", mouse.enter), handlerForMyHover);
- return function(target, listener){
- var matchesTarget = this;
- var bubble = eventType.bubble;
- if(bubble){
- // the event type doesn't naturally bubble, but has a bubbling form, use that
- eventType = bubble;
- }else if(children !== false){
- // for normal bubbling events we default to allowing children of the selector
- children = true;
- }
- return on(target, eventType, function(event){
- var eventTarget = event.target;
- // see if we have a valid matchesTarget or default to dojo.query
- matchesTarget = matchesTarget && matchesTarget.matches ? matchesTarget : dojo.query;
- // there is a selector, so make sure it matches
- while(!matchesTarget.matches(eventTarget, selector, target)){
- if(eventTarget == target || !children || !(eventTarget = eventTarget.parentNode)){ // intentional assignment
- return;
- }
- }
- return listener.call(eventTarget, event);
- });
- };
- };
-
- function syntheticPreventDefault(){
- this.cancelable = false;
- }
- function syntheticStopPropagation(){
- this.bubbles = false;
- }
- var slice = [].slice,
- syntheticDispatch = on.emit = function(target, type, event){
- // summary:
- // Fires an event on the target object.
- // target:
- // The target object to fire the event on. This can be a DOM element or a plain
- // JS object. If the target is a DOM element, native event emiting mechanisms
- // are used when possible.
- // type:
- // The event type name. You can emulate standard native events like "click" and
- // "mouseover" or create custom events like "open" or "finish".
- // event:
- // An object that provides the properties for the event. See https://developer.mozilla.org/en/DOM/event.initEvent
- // for some of the properties. These properties are copied to the event object.
- // Of particular importance are the cancelable and bubbles properties. The
- // cancelable property indicates whether or not the event has a default action
- // that can be cancelled. The event is cancelled by calling preventDefault() on
- // the event object. The bubbles property indicates whether or not the
- // event will bubble up the DOM tree. If bubbles is true, the event will be called
- // on the target and then each parent successively until the top of the tree
- // is reached or stopPropagation() is called. Both bubbles and cancelable
- // default to false.
- // returns:
- // If the event is cancelable and the event is not cancelled,
- // emit will return true. If the event is cancelable and the event is cancelled,
- // emit will return false.
- // details:
- // Note that this is designed to emit events for listeners registered through
- // dojo/on. It should actually work with any event listener except those
- // added through IE's attachEvent (IE8 and below's non-W3C event emiting
- // doesn't support custom event types). It should work with all events registered
- // through dojo/on. Also note that the emit method does do any default
- // action, it only returns a value to indicate if the default action should take
- // place. For example, emiting a keypress event would not cause a character
- // to appear in a textbox.
- // example:
- // To fire our own click event
- // | on.emit(dojo.byId("button"), "click", {
- // | cancelable: true,
- // | bubbles: true,
- // | screenX: 33,
- // | screenY: 44
- // | });
- // We can also fire our own custom events:
- // | on.emit(dojo.byId("slider"), "slide", {
- // | cancelable: true,
- // | bubbles: true,
- // | direction: "left-to-right"
- // | });
- var args = slice.call(arguments, 2);
- var method = "on" + type;
- if("parentNode" in target){
- // node (or node-like), create event controller methods
- var newEvent = args[0] = {};
- for(var i in event){
- newEvent[i] = event[i];
- }
- newEvent.preventDefault = syntheticPreventDefault;
- newEvent.stopPropagation = syntheticStopPropagation;
- newEvent.target = target;
- newEvent.type = type;
- event = newEvent;
- }
- do{
- // call any node which has a handler (note that ideally we would try/catch to simulate normal event propagation but that causes too much pain for debugging)
- target[method] && target[method].apply(target, args);
- // and then continue up the parent node chain if it is still bubbling (if started as bubbles and stopPropagation hasn't been called)
- }while(event && event.bubbles && (target = target.parentNode));
- return event && event.cancelable && event; // if it is still true (was cancelable and was cancelled), return the event to indicate default action should happen
- };
- var captures = {};
- if(has("dom-addeventlistener")){
- // normalize focusin and focusout
- captures = {
- focusin: "focus",
- focusout: "blur"
- };
- if(has("opera")){
- captures.keydown = "keypress"; // this one needs to be transformed because Opera doesn't support repeating keys on keydown (and keypress works because it incorrectly fires on all keydown events)
- }
-
- // emiter that works with native event handling
- on.emit = function(target, type, event){
- if(target.dispatchEvent && document.createEvent){
- // use the native event emiting mechanism if it is available on the target object
- // create a generic event
- // we could create branch into the different types of event constructors, but
- // that would be a lot of extra code, with little benefit that I can see, seems
- // best to use the generic constructor and copy properties over, making it
- // easy to have events look like the ones created with specific initializers
- var nativeEvent = document.createEvent("HTMLEvents");
- nativeEvent.initEvent(type, !!event.bubbles, !!event.cancelable);
- // and copy all our properties over
- for(var i in event){
- var value = event[i];
- if(!(i in nativeEvent)){
- nativeEvent[i] = event[i];
- }
- }
- return target.dispatchEvent(nativeEvent) && nativeEvent;
- }
- return syntheticDispatch.apply(on, arguments); // emit for a non-node
- };
- }else{
- // no addEventListener, basically old IE event normalization
- on._fixEvent = function(evt, 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);}
- if(!evt.target){ // check to see if it has been fixed yet
- evt.target = evt.srcElement;
- evt.currentTarget = (sender || evt.srcElement);
- if(evt.type == "mouseover"){
- evt.relatedTarget = evt.fromElement;
- }
- if(evt.type == "mouseout"){
- evt.relatedTarget = evt.toElement;
- }
- if(!evt.stopPropagation){
- evt.stopPropagation = stopPropagation;
- evt.preventDefault = preventDefault;
- }
- 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;
- _setKeyChar(evt);
- break;
- }
- }
- return evt;
- };
- var IESignal = function(handle){
- this.handle = handle;
- };
- IESignal.prototype.remove = function(){
- delete _dojoIEListeners_[this.handle];
- };
- var fixListener = function(listener){
- // this is a minimal function for closing on the previous listener with as few as variables as possible
- return function(evt){
- evt = on._fixEvent(evt, this);
- return listener.call(this, evt);
- }
- }
- var fixAttach = function(target, type, listener){
- listener = fixListener(listener);
- if(((target.ownerDocument ? target.ownerDocument.parentWindow : target.parentWindow || target.window || window) != top ||
- has("jscript") < 5.8) &&
- !has("config-_allow_leaks")){
- // IE will leak memory on certain handlers in frames (IE8 and earlier) and in unattached DOM nodes for JScript 5.7 and below.
- // Here we use global redirection to solve the memory leaks
- if(typeof _dojoIEListeners_ == "undefined"){
- _dojoIEListeners_ = [];
- }
- var emiter = target[type];
- if(!emiter || !emiter.listeners){
- var oldListener = emiter;
- target[type] = emiter = Function('event', 'var callee = arguments.callee; for(var i = 0; i<callee.listeners.length; i++){var listener = _dojoIEListeners_[callee.listeners[i]]; if(listener){listener.call(this,event);}}');
- emiter.listeners = [];
- if(oldListener){
- emiter.listeners.push(_dojoIEListeners_.push(oldListener) - 1);
- }
- }
- var handle;
- emiter.listeners.push(handle = (_dojoIEListeners_.push(listener) - 1));
- return new IESignal(handle);
- }
- return aspect.after(target, type, listener, true);
- };
-
- var _setKeyChar = function(evt){
- evt.keyChar = evt.charCode ? String.fromCharCode(evt.charCode) : '';
- evt.charOrCode = evt.keyChar || evt.keyCode;
- };
- // Called in Event scope
- var stopPropagation = function(){
- this.cancelBubble = true;
- };
- var preventDefault = on._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){
- try{
- // squelch errors when keyCode is read-only
- // (e.g. if keyCode is ctrl or shift)
- this.keyCode = 0;
- }catch(e){
- }
- }
- this.returnValue = false;
- };
- }
- if(has("touch")){
- var Event = function (){};
- var windowOrientation = window.orientation;
- var fixTouchListener = function(listener){
- return function(originalEvent){
- //Event normalization(for ontouchxxx and resize):
- //1.incorrect e.pageX|pageY in iOS
- //2.there are no "e.rotation", "e.scale" and "onorientationchange" in Andriod
- //3.More TBD e.g. force | screenX | screenX | clientX | clientY | radiusX | radiusY
-
- // see if it has already been corrected
- var event = originalEvent.corrected;
- if(!event){
- var type = originalEvent.type;
- try{
- delete originalEvent.type; // on some JS engines (android), deleting properties make them mutable
- }catch(e){}
- if(originalEvent.type){
- // deleting properties doesn't work (older iOS), have to use delegation
- Event.prototype = originalEvent;
- var event = new Event;
- // have to delegate methods to make them work
- event.preventDefault = function(){
- originalEvent.preventDefault();
- };
- event.stopPropagation = function(){
- originalEvent.stopPropagation();
- };
- }else{
- // deletion worked, use property as is
- event = originalEvent;
- event.type = type;
- }
- originalEvent.corrected = event;
- if(type == 'resize'){
- if(windowOrientation == window.orientation){
- return null;//double tap causes an unexpected 'resize' in Andriod
- }
- windowOrientation = window.orientation;
- event.type = "orientationchange";
- return listener.call(this, event);
- }
- // We use the original event and augment, rather than doing an expensive mixin operation
- if(!("rotation" in event)){ // test to see if it has rotation
- event.rotation = 0;
- event.scale = 1;
- }
- //use event.changedTouches[0].pageX|pageY|screenX|screenY|clientX|clientY|target
- var firstChangeTouch = event.changedTouches[0];
- for(var i in firstChangeTouch){ // use for-in, we don't need to have dependency on dojo/_base/lang here
- delete event[i]; // delete it first to make it mutable
- event[i] = firstChangeTouch[i];
- }
- }
- return listener.call(this, event);
- };
- };
- }
- return on;
-});
+define(["./has!dom-addeventlistener?:./aspect", "./_base/kernel", "./has"], function(aspect, dojo, has){
+ // summary:
+ // The export of this module is a function that provides core event listening functionality. With this function
+ // you can provide a target, event type, and listener to be notified of
+ // future matching events that are fired.
+ // target: Element|Object
+ // This is the target object or DOM element that to receive events from
+ // type: String|Function
+ // This is the name of the event to listen for or an extension event type.
+ // listener: Function
+ // This is the function that should be called when the event fires.
+ // returns: Object
+ // An object with a remove() method that can be used to stop listening for this
+ // event.
+ // description:
+ // To listen for "click" events on a button node, we can do:
+ // | define(["dojo/on"], function(listen){
+ // | on(button, "click", clickHandler);
+ // | ...
+ // Evented JavaScript objects can also have their own events.
+ // | var obj = new Evented;
+ // | on(obj, "foo", fooHandler);
+ // And then we could publish a "foo" event:
+ // | on.emit(obj, "foo", {key: "value"});
+ // We can use extension events as well. For example, you could listen for a tap gesture:
+ // | define(["dojo/on", "dojo/gesture/tap", function(listen, tap){
+ // | on(button, tap, tapHandler);
+ // | ...
+ // which would trigger fooHandler. Note that for a simple object this is equivalent to calling:
+ // | obj.onfoo({key:"value"});
+ // If you use on.emit on a DOM node, it will use native event dispatching when possible.
+
+ "use strict";
+ if(has("dom")){ // check to make sure we are in a browser, this module should work anywhere
+ var major = window.ScriptEngineMajorVersion;
+ has.add("jscript", major && (major() + ScriptEngineMinorVersion() / 10));
+ has.add("event-orientationchange", has("touch") && !has("android")); // TODO: how do we detect this?
+ }
+ var on = function(target, type, listener, dontFix){
+ if(target.on){
+ // delegate to the target's on() method, so it can handle it's own listening if it wants
+ return target.on(type, listener);
+ }
+ // delegate to main listener code
+ return on.parse(target, type, listener, addListener, dontFix, this);
+ };
+ on.pausable = function(target, type, listener, dontFix){
+ // summary:
+ // This function acts the same as on(), but with pausable functionality. The
+ // returned signal object has pause() and resume() functions. Calling the
+ // pause() method will cause the listener to not be called for future events. Calling the
+ // resume() method will cause the listener to again be called for future events.
+ var paused;
+ var signal = on(target, type, function(){
+ if(!paused){
+ return listener.apply(this, arguments);
+ }
+ }, dontFix);
+ signal.pause = function(){
+ paused = true;
+ };
+ signal.resume = function(){
+ paused = false;
+ };
+ return signal;
+ };
+ on.once = function(target, type, listener, dontFix){
+ // summary:
+ // This function acts the same as on(), but will only call the listener once. The
+ // listener will be called for the first
+ // event that takes place and then listener will automatically be removed.
+ var signal = on(target, type, function(){
+ // remove this listener
+ signal.remove();
+ // proceed to call the listener
+ return listener.apply(this, arguments);
+ });
+ return signal;
+ };
+ on.parse = function(target, type, listener, addListener, dontFix, matchesTarget){
+ if(type.call){
+ // event handler function
+ // on(node, dojo.touch.press, touchListener);
+ return type.call(matchesTarget, target, listener);
+ }
+
+ if(type.indexOf(",") > -1){
+ // we allow comma delimited event names, so you can register for multiple events at once
+ var events = type.split(/\s*,\s*/);
+ var handles = [];
+ var i = 0;
+ var eventName;
+ while(eventName = events[i++]){
+ handles.push(addListener(target, eventName, listener, dontFix, matchesTarget));
+ }
+ handles.remove = function(){
+ for(var i = 0; i < handles.length; i++){
+ handles[i].remove();
+ }
+ };
+ return handles;
+ }
+ return addListener(target, type, listener, dontFix, matchesTarget)
+ };
+ var touchEvents = /^touch/;
+ function addListener(target, type, listener, dontFix, matchesTarget){
+ // event delegation:
+ var selector = type.match(/(.*):(.*)/);
+ // if we have a selector:event, the last one is interpreted as an event, and we use event delegation
+ if(selector){
+ type = selector[2];
+ selector = selector[1];
+ // create the extension event for selectors and directly call it
+ return on.selector(selector, type).call(matchesTarget, target, listener);
+ }
+ // test to see if it a touch event right now, so we don't have to do it every time it fires
+ if(has("touch")){
+ if(touchEvents.test(type)){
+ // touch event, fix it
+ listener = fixTouchListener(listener);
+ }
+ if(!has("event-orientationchange") && (type == "orientationchange")){
+ //"orientationchange" not supported <= Android 2.1,
+ //but works through "resize" on window
+ type = "resize";
+ target = window;
+ listener = fixTouchListener(listener);
+ }
+ }
+ // normal path, the target is |this|
+ if(target.addEventListener){
+ // the target has addEventListener, which should be used if available (might or might not be a node, non-nodes can implement this method as well)
+ // check for capture conversions
+ var capture = type in captures;
+ target.addEventListener(capture ? captures[type] : type, listener, capture);
+ // create and return the signal
+ return {
+ remove: function(){
+ target.removeEventListener(type, listener, capture);
+ }
+ };
+ }
+ type = "on" + type;
+ if(fixAttach && target.attachEvent){
+ return fixAttach(target, type, listener);
+ }
+ throw new Error("Target must be an event emitter");
+ }
+
+ on.selector = function(selector, eventType, children){
+ // summary:
+ // Creates a new extension event with event delegation. This is based on
+ // the provided event type (can be extension event) that
+ // only calls the listener when the CSS selector matches the target of the event.
+ // selector:
+ // The CSS selector to use for filter events and determine the |this| of the event listener.
+ // eventType:
+ // The event to listen for
+ // children:
+ // Indicates if children elements of the selector should be allowed. This defaults to
+ // true (except in the case of normally non-bubbling events like mouse.enter, in which case it defaults to false).
+ // example:
+ // define(["dojo/on", "dojo/mouse"], function(listen, mouse){
+ // on(node, on.selector(".my-class", mouse.enter), handlerForMyHover);
+ return function(target, listener){
+ var matchesTarget = this;
+ var bubble = eventType.bubble;
+ if(bubble){
+ // the event type doesn't naturally bubble, but has a bubbling form, use that
+ eventType = bubble;
+ }else if(children !== false){
+ // for normal bubbling events we default to allowing children of the selector
+ children = true;
+ }
+ return on(target, eventType, function(event){
+ var eventTarget = event.target;
+ // see if we have a valid matchesTarget or default to dojo.query
+ matchesTarget = matchesTarget && matchesTarget.matches ? matchesTarget : dojo.query;
+ // there is a selector, so make sure it matches
+ while(!matchesTarget.matches(eventTarget, selector, target)){
+ if(eventTarget == target || !children || !(eventTarget = eventTarget.parentNode)){ // intentional assignment
+ return;
+ }
+ }
+ return listener.call(eventTarget, event);
+ });
+ };
+ };
+
+ function syntheticPreventDefault(){
+ this.cancelable = false;
+ }
+ function syntheticStopPropagation(){
+ this.bubbles = false;
+ }
+ var slice = [].slice,
+ syntheticDispatch = on.emit = function(target, type, event){
+ // summary:
+ // Fires an event on the target object.
+ // target:
+ // The target object to fire the event on. This can be a DOM element or a plain
+ // JS object. If the target is a DOM element, native event emiting mechanisms
+ // are used when possible.
+ // type:
+ // The event type name. You can emulate standard native events like "click" and
+ // "mouseover" or create custom events like "open" or "finish".
+ // event:
+ // An object that provides the properties for the event. See https://developer.mozilla.org/en/DOM/event.initEvent
+ // for some of the properties. These properties are copied to the event object.
+ // Of particular importance are the cancelable and bubbles properties. The
+ // cancelable property indicates whether or not the event has a default action
+ // that can be cancelled. The event is cancelled by calling preventDefault() on
+ // the event object. The bubbles property indicates whether or not the
+ // event will bubble up the DOM tree. If bubbles is true, the event will be called
+ // on the target and then each parent successively until the top of the tree
+ // is reached or stopPropagation() is called. Both bubbles and cancelable
+ // default to false.
+ // returns:
+ // If the event is cancelable and the event is not cancelled,
+ // emit will return true. If the event is cancelable and the event is cancelled,
+ // emit will return false.
+ // details:
+ // Note that this is designed to emit events for listeners registered through
+ // dojo/on. It should actually work with any event listener except those
+ // added through IE's attachEvent (IE8 and below's non-W3C event emiting
+ // doesn't support custom event types). It should work with all events registered
+ // through dojo/on. Also note that the emit method does do any default
+ // action, it only returns a value to indicate if the default action should take
+ // place. For example, emiting a keypress event would not cause a character
+ // to appear in a textbox.
+ // example:
+ // To fire our own click event
+ // | on.emit(dojo.byId("button"), "click", {
+ // | cancelable: true,
+ // | bubbles: true,
+ // | screenX: 33,
+ // | screenY: 44
+ // | });
+ // We can also fire our own custom events:
+ // | on.emit(dojo.byId("slider"), "slide", {
+ // | cancelable: true,
+ // | bubbles: true,
+ // | direction: "left-to-right"
+ // | });
+ var args = slice.call(arguments, 2);
+ var method = "on" + type;
+ if("parentNode" in target){
+ // node (or node-like), create event controller methods
+ var newEvent = args[0] = {};
+ for(var i in event){
+ newEvent[i] = event[i];
+ }
+ newEvent.preventDefault = syntheticPreventDefault;
+ newEvent.stopPropagation = syntheticStopPropagation;
+ newEvent.target = target;
+ newEvent.type = type;
+ event = newEvent;
+ }
+ do{
+ // call any node which has a handler (note that ideally we would try/catch to simulate normal event propagation but that causes too much pain for debugging)
+ target[method] && target[method].apply(target, args);
+ // and then continue up the parent node chain if it is still bubbling (if started as bubbles and stopPropagation hasn't been called)
+ }while(event && event.bubbles && (target = target.parentNode));
+ return event && event.cancelable && event; // if it is still true (was cancelable and was cancelled), return the event to indicate default action should happen
+ };
+ var captures = {};
+ if(has("dom-addeventlistener")){
+ // normalize focusin and focusout
+ captures = {
+ focusin: "focus",
+ focusout: "blur"
+ };
+ if(has("opera")){
+ captures.keydown = "keypress"; // this one needs to be transformed because Opera doesn't support repeating keys on keydown (and keypress works because it incorrectly fires on all keydown events)
+ }
+
+ // emiter that works with native event handling
+ on.emit = function(target, type, event){
+ if(target.dispatchEvent && document.createEvent){
+ // use the native event emiting mechanism if it is available on the target object
+ // create a generic event
+ // we could create branch into the different types of event constructors, but
+ // that would be a lot of extra code, with little benefit that I can see, seems
+ // best to use the generic constructor and copy properties over, making it
+ // easy to have events look like the ones created with specific initializers
+ var nativeEvent = document.createEvent("HTMLEvents");
+ nativeEvent.initEvent(type, !!event.bubbles, !!event.cancelable);
+ // and copy all our properties over
+ for(var i in event){
+ var value = event[i];
+ if(!(i in nativeEvent)){
+ nativeEvent[i] = event[i];
+ }
+ }
+ return target.dispatchEvent(nativeEvent) && nativeEvent;
+ }
+ return syntheticDispatch.apply(on, arguments); // emit for a non-node
+ };
+ }else{
+ // no addEventListener, basically old IE event normalization
+ on._fixEvent = function(evt, 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);}
+ if(!evt.target){ // check to see if it has been fixed yet
+ evt.target = evt.srcElement;
+ evt.currentTarget = (sender || evt.srcElement);
+ if(evt.type == "mouseover"){
+ evt.relatedTarget = evt.fromElement;
+ }
+ if(evt.type == "mouseout"){
+ evt.relatedTarget = evt.toElement;
+ }
+ if(!evt.stopPropagation){
+ evt.stopPropagation = stopPropagation;
+ evt.preventDefault = preventDefault;
+ }
+ 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;
+ _setKeyChar(evt);
+ break;
+ }
+ }
+ return evt;
+ };
+ var IESignal = function(handle){
+ this.handle = handle;
+ };
+ IESignal.prototype.remove = function(){
+ delete _dojoIEListeners_[this.handle];
+ };
+ var fixListener = function(listener){
+ // this is a minimal function for closing on the previous listener with as few as variables as possible
+ return function(evt){
+ evt = on._fixEvent(evt, this);
+ return listener.call(this, evt);
+ }
+ }
+ var fixAttach = function(target, type, listener){
+ listener = fixListener(listener);
+ if(((target.ownerDocument ? target.ownerDocument.parentWindow : target.parentWindow || target.window || window) != top ||
+ has("jscript") < 5.8) &&
+ !has("config-_allow_leaks")){
+ // IE will leak memory on certain handlers in frames (IE8 and earlier) and in unattached DOM nodes for JScript 5.7 and below.
+ // Here we use global redirection to solve the memory leaks
+ if(typeof _dojoIEListeners_ == "undefined"){
+ _dojoIEListeners_ = [];
+ }
+ var emiter = target[type];
+ if(!emiter || !emiter.listeners){
+ var oldListener = emiter;
+ target[type] = emiter = Function('event', 'var callee = arguments.callee; for(var i = 0; i<callee.listeners.length; i++){var listener = _dojoIEListeners_[callee.listeners[i]]; if(listener){listener.call(this,event);}}');
+ emiter.listeners = [];
+ if(oldListener){
+ emiter.listeners.push(_dojoIEListeners_.push(oldListener) - 1);
+ }
+ }
+ var handle;
+ emiter.listeners.push(handle = (_dojoIEListeners_.push(listener) - 1));
+ return new IESignal(handle);
+ }
+ return aspect.after(target, type, listener, true);
+ };
+
+ var _setKeyChar = function(evt){
+ evt.keyChar = evt.charCode ? String.fromCharCode(evt.charCode) : '';
+ evt.charOrCode = evt.keyChar || evt.keyCode;
+ };
+ // Called in Event scope
+ var stopPropagation = function(){
+ this.cancelBubble = true;
+ };
+ var preventDefault = on._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){
+ try{
+ // squelch errors when keyCode is read-only
+ // (e.g. if keyCode is ctrl or shift)
+ this.keyCode = 0;
+ }catch(e){
+ }
+ }
+ this.returnValue = false;
+ };
+ }
+ if(has("touch")){
+ var Event = function (){};
+ var windowOrientation = window.orientation;
+ var fixTouchListener = function(listener){
+ return function(originalEvent){
+ //Event normalization(for ontouchxxx and resize):
+ //1.incorrect e.pageX|pageY in iOS
+ //2.there are no "e.rotation", "e.scale" and "onorientationchange" in Andriod
+ //3.More TBD e.g. force | screenX | screenX | clientX | clientY | radiusX | radiusY
+
+ // see if it has already been corrected
+ var event = originalEvent.corrected;
+ if(!event){
+ var type = originalEvent.type;
+ try{
+ delete originalEvent.type; // on some JS engines (android), deleting properties make them mutable
+ }catch(e){}
+ if(originalEvent.type){
+ // deleting properties doesn't work (older iOS), have to use delegation
+ Event.prototype = originalEvent;
+ var event = new Event;
+ // have to delegate methods to make them work
+ event.preventDefault = function(){
+ originalEvent.preventDefault();
+ };
+ event.stopPropagation = function(){
+ originalEvent.stopPropagation();
+ };
+ }else{
+ // deletion worked, use property as is
+ event = originalEvent;
+ event.type = type;
+ }
+ originalEvent.corrected = event;
+ if(type == 'resize'){
+ if(windowOrientation == window.orientation){
+ return null;//double tap causes an unexpected 'resize' in Andriod
+ }
+ windowOrientation = window.orientation;
+ event.type = "orientationchange";
+ return listener.call(this, event);
+ }
+ // We use the original event and augment, rather than doing an expensive mixin operation
+ if(!("rotation" in event)){ // test to see if it has rotation
+ event.rotation = 0;
+ event.scale = 1;
+ }
+ //use event.changedTouches[0].pageX|pageY|screenX|screenY|clientX|clientY|target
+ var firstChangeTouch = event.changedTouches[0];
+ for(var i in firstChangeTouch){ // use for-in, we don't need to have dependency on dojo/_base/lang here
+ delete event[i]; // delete it first to make it mutable
+ event[i] = firstChangeTouch[i];
+ }
+ }
+ return listener.call(this, event);
+ };
+ };
+ }
+ return on;
+});
diff --git a/query.js b/query.js
index 9aa601c..2f1aeab 100644
--- a/query.js
+++ b/query.js
@@ -1,712 +1,712 @@
-define(["./_base/kernel", "./has", "./on", "./_base/array", "./_base/lang", "./selector/_loader", "./selector/_loader!default"], function(dojo, has, on, array, lang, loader, defaultEngine){
-"use strict";
-
- has.add("array-extensible", function(){
- // test to see if we can extend an array (not supported in old IE)
- return lang.delegate([], {length: 1}).length == 1 && !has("bug-for-in-skips-shadowed");
- });
-
- var ap = Array.prototype, aps = ap.slice, apc = ap.concat, forEach = array.forEach;
-
- 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.
- var nodeList = new (NodeListCtor || this._NodeListCtor || nl)(a);
- return parent ? nodeList._stash(parent) : nodeList;
- };
-
- var loopBody = function(f, a, o){
- a = [0].concat(aps.call(a, 0));
- o = o || dojo.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 || dojo.global, a)){
- return this.map(body); // self
- }
- this.forEach(body);
- return this; // self
- };
- };
-
- var NodeList = function(array){
- // summary:
- // dojo.NodeList is an of Array-like object 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);
- var isNew = this instanceof nl && has("array-extensible");
- if(typeof array == "number"){
- array = Array(array);
- }
- var nodeArray = (array && "length" in array) ? array : arguments;
- if(isNew || !nodeArray.sort){
- // make sure it's a real array before we pass it on to be wrapped
- var target = isNew ? this : [],
- l = target.length = nodeArray.length;
- for(var i = 0; i < l; i++){
- target[i] = nodeArray[i];
- }
- if(isNew){
- // called with new operator, this means we are going to use this instance and push
- // the nodes on to it. This is usually much faster since the NodeList properties
- // don't need to be copied (unless the list of nodes is extremely large).
- return target;
- }
- nodeArray = target;
- }
- // called without new operator, use a real array and copy prototype properties,
- // this is slower and exists for back-compat. Should be removed in 2.0.
- lang._mixin(nodeArray, nlp);
- nodeArray._NodeListCtor = function(array){
- // call without new operator to preserve back-compat behavior
- return nl(array);
- }
- return nodeArray;
- };
-
- var nl = NodeList, nlp = nl.prototype =
- has("array-extensible") ? [] : {};// extend an array if it is extensible
-
- // 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
- 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
- forEach(["indexOf", "lastIndexOf", "every", "some"], function(name){
- var f = array[name];
- nlp[name] = function(){ return f.apply(dojo, [this].concat(aps.call(arguments, 0))); };
- });
-
- /*===== var NodeList = dojo.NodeList; =====*/
- lang.extend(NodeList, {
- // copy the constructors
- constructor: nl,
- _NodeListCtor: nl,
- toString: function(){
- // Array.prototype.toString can't be applied to objects, so we use join
- return this.join(",");
- },
- _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
- },
-
- on: function(eventName, listener){
- // summary:
- // Listen for events on the nodes in the NodeList. Basic usage is:
- // | query(".my-class").on("click", listener);
- // This supports event delegation by using selectors as the first argument with the event names as
- // pseudo selectors. For example:
- // | dojo.query("#my-list").on("li:click", listener);
- // This will listen for click events within <li> elements that are inside the #my-list element.
- // Because on supports CSS selector syntax, we can use comma-delimited events as well:
- // | dojo.query("#my-list").on("li button:mouseover, li:click", listener);
- var handles = this.map(function(node){
- return on(node, eventName, listener); // TODO: apply to the NodeList so the same selector engine is used for matches
- });
- handles.remove = function(){
- for(var i = 0; i < handles.length; i++){
- handles[i].remove();
- }
- };
- return handles;
- },
-
- 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 empty list.
- return new this._NodeListCtor(0);
- }
- },
-
- // 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 location 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 location 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 = lang.isArray(this) ? this : aps.call(this, 0),
- m = array.map(arguments, function(a){
- return a && !lang.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(array.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.
- forEach(this, callback, thisObj);
- // non-standard return to allow easier chaining
- return this; // dojo.NodeList
- },
- filter: function(/*String|Function*/ filter){
- // 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.
- // filter:
- // If a string, a CSS rule like ".thinger" or "div > span".
- // 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 filter == "string"){ // inline'd type check
- items = query._filterResult(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(array.filter(items, a[start], a[start + 1]), 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 = lang.isFunction(declaredClass) ? declaredClass : lang.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(0);
- 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
- }
- });
-
-
-/*=====
-dojo.query = function(selector, context){
- // summary:
- // This modules provides DOM querying functionality. The module export is a function
- // that can be used to query for DOM nodes by CSS selector and returns a dojo.NodeList
- // representing the matching nodes.
- //
- // selector: String
- // A CSS selector to search for.
- // context: String|DomNode?
- // An optional context to limit the searching scope. Only nodes under `context` will be
- // scanned.
- //
- // example:
- // add an onclick handler to every submit button in the document
- // which causes the form to be sent via Ajax instead:
- // | define(["dojo/query"], function(query){
- // | query("input[type='submit']").on("click", 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");
- // | }
- // | });
- // | });
- //
- // description:
- // dojo/query is responsible for loading the appropriate query engine and wrapping
- // its results with a `dojo.NodeList`. You can use dojo/query with a specific selector engine
- // by using it as a plugin. For example, if you installed the sizzle package, you could
- // use it as the selector engine with:
- // | define("dojo/query!sizzle", function(query){
- // | query("div")...
- //
- // The id after the ! can be a module id of the selector engine or one of the following values:
- // | + acme: This is the default engine used by Dojo base, and will ensure that the full
- // | Acme engine is always loaded.
- // |
- // | + css2: If the browser has a native selector engine, this will be used, otherwise a
- // | very minimal lightweight selector engine will be loaded that can do simple CSS2 selectors
- // | (by #id, .class, tag, and [name=value] attributes, with standard child or descendant (>)
- // | operators) and nothing more.
- // |
- // | + css2.1: If the browser has a native selector engine, this will be used, otherwise the
- // | full Acme engine will be loaded.
- // |
- // | + css3: If the browser has a native selector engine with support for CSS3 pseudo
- // | selectors (most modern browsers except IE8), this will be used, otherwise the
- // | full Acme engine will be loaded.
- // |
- // | + Or the module id of a selector engine can be used to explicitly choose the selector engine
- //
- // For example, if you are using CSS3 pseudo selectors in module, you can specify that
- // you will need support them with:
- // | define("dojo/query!css3", function(query){
- // | query('#t > h3:nth-child(odd)')...
- //
- // You can also choose the selector engine/load configuration by setting the <FIXME:what is the configuration setting?>.
- // For example:
- // | <script data-dojo-config="query-selector:'css3'" src="dojo.js"></script>
- //
- return new dojo.NodeList(); // dojo.NodeList
-};
-=====*/
-
-function queryForEngine(engine, NodeList){
- var query = function(/*String*/ query, /*String|DOMNode?*/ root){
- // summary:
- // Returns nodes which match the given CSS selector, searching the
- // entire document by default but optionally taking a node to scope
- // the search by. Returns an instance of dojo.NodeList.
- if(typeof root == "string"){
- root = dojo.byId(root);
- if(!root){
- return new NodeList([]);
- }
- }
- var results = typeof query == "string" ? engine(query, root) : query.orphan ? query : [query];
- if(results.orphan){
- // already wrapped
- return results;
- }
- return new NodeList(results);
- };
- query.matches = engine.match || function(node, selector, root){
- // summary:
- // Test to see if a node matches a selector
- return query.filter([node], selector, root).length > 0;
- };
- // the engine provides a filtering function, use it to for matching
- query.filter = engine.filter || function(nodes, selector, root){
- // summary:
- // Filters an array of nodes. Note that this does not guarantee to return a dojo.NodeList, just an array.
- return query(selector, root).filter(function(node){
- return dojo.indexOf(nodes, node) > -1;
- });
- };
- if(typeof engine != "function"){
- var search = engine.search;
- engine = function(selector, root){
- // Slick does it backwards (or everyone else does it backwards, probably the latter)
- return search(root || document, selector);
- };
- }
- return query;
-}
-var query = queryForEngine(defaultEngine, NodeList);
-// the query that is returned from this module is slightly different than dojo.query,
-// because dojo.query has to maintain backwards compatibility with returning a
-// true array which has performance problems. The query returned from the module
-// does not use true arrays, but rather inherits from Array, making it much faster to
-// instantiate.
-dojo.query = queryForEngine(defaultEngine, function(array){
- // call it without the new operator to invoke the back-compat behavior that returns a true array
- return NodeList(array);
-});
-
-query.load = /*===== dojo.query.load= ======*/ function(id, parentRequire, loaded, config){
- // summary: can be used as AMD plugin to conditionally load new query engine
- // example:
- // | define(["dojo/query!custom"], function(qsa){
- // | // loaded selector/custom.js as engine
- // | qsa("#foobar").forEach(...);
- // | });
- loader.load(id, parentRequire, function(engine){
- loaded(queryForEngine(engine, NodeList));
- });
-};
-
-dojo._filterQueryResult = query._filterResult = function(nodes, selector, root){
- return new NodeList(query.filter(nodes, selector, root));
-};
-dojo.NodeList = query.NodeList = NodeList;
-return query;
-});
+define(["./_base/kernel", "./has", "./on", "./_base/array", "./_base/lang", "./selector/_loader", "./selector/_loader!default"], function(dojo, has, on, array, lang, loader, defaultEngine){
+"use strict";
+
+ has.add("array-extensible", function(){
+ // test to see if we can extend an array (not supported in old IE)
+ return lang.delegate([], {length: 1}).length == 1 && !has("bug-for-in-skips-shadowed");
+ });
+
+ var ap = Array.prototype, aps = ap.slice, apc = ap.concat, forEach = array.forEach;
+
+ 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.
+ var nodeList = new (NodeListCtor || this._NodeListCtor || nl)(a);
+ return parent ? nodeList._stash(parent) : nodeList;
+ };
+
+ var loopBody = function(f, a, o){
+ a = [0].concat(aps.call(a, 0));
+ o = o || dojo.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 || dojo.global, a)){
+ return this.map(body); // self
+ }
+ this.forEach(body);
+ return this; // self
+ };
+ };
+
+ var NodeList = function(array){
+ // summary:
+ // dojo.NodeList is an of Array-like object 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);
+ var isNew = this instanceof nl && has("array-extensible");
+ if(typeof array == "number"){
+ array = Array(array);
+ }
+ var nodeArray = (array && "length" in array) ? array : arguments;
+ if(isNew || !nodeArray.sort){
+ // make sure it's a real array before we pass it on to be wrapped
+ var target = isNew ? this : [],
+ l = target.length = nodeArray.length;
+ for(var i = 0; i < l; i++){
+ target[i] = nodeArray[i];
+ }
+ if(isNew){
+ // called with new operator, this means we are going to use this instance and push
+ // the nodes on to it. This is usually much faster since the NodeList properties
+ // don't need to be copied (unless the list of nodes is extremely large).
+ return target;
+ }
+ nodeArray = target;
+ }
+ // called without new operator, use a real array and copy prototype properties,
+ // this is slower and exists for back-compat. Should be removed in 2.0.
+ lang._mixin(nodeArray, nlp);
+ nodeArray._NodeListCtor = function(array){
+ // call without new operator to preserve back-compat behavior
+ return nl(array);
+ }
+ return nodeArray;
+ };
+
+ var nl = NodeList, nlp = nl.prototype =
+ has("array-extensible") ? [] : {};// extend an array if it is extensible
+
+ // 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
+ 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
+ forEach(["indexOf", "lastIndexOf", "every", "some"], function(name){
+ var f = array[name];
+ nlp[name] = function(){ return f.apply(dojo, [this].concat(aps.call(arguments, 0))); };
+ });
+
+ /*===== var NodeList = dojo.NodeList; =====*/
+ lang.extend(NodeList, {
+ // copy the constructors
+ constructor: nl,
+ _NodeListCtor: nl,
+ toString: function(){
+ // Array.prototype.toString can't be applied to objects, so we use join
+ return this.join(",");
+ },
+ _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
+ },
+
+ on: function(eventName, listener){
+ // summary:
+ // Listen for events on the nodes in the NodeList. Basic usage is:
+ // | query(".my-class").on("click", listener);
+ // This supports event delegation by using selectors as the first argument with the event names as
+ // pseudo selectors. For example:
+ // | dojo.query("#my-list").on("li:click", listener);
+ // This will listen for click events within <li> elements that are inside the #my-list element.
+ // Because on supports CSS selector syntax, we can use comma-delimited events as well:
+ // | dojo.query("#my-list").on("li button:mouseover, li:click", listener);
+ var handles = this.map(function(node){
+ return on(node, eventName, listener); // TODO: apply to the NodeList so the same selector engine is used for matches
+ });
+ handles.remove = function(){
+ for(var i = 0; i < handles.length; i++){
+ handles[i].remove();
+ }
+ };
+ return handles;
+ },
+
+ 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 empty list.
+ return new this._NodeListCtor(0);
+ }
+ },
+
+ // 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 location 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 location 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 = lang.isArray(this) ? this : aps.call(this, 0),
+ m = array.map(arguments, function(a){
+ return a && !lang.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(array.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.
+ forEach(this, callback, thisObj);
+ // non-standard return to allow easier chaining
+ return this; // dojo.NodeList
+ },
+ filter: function(/*String|Function*/ filter){
+ // 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.
+ // filter:
+ // If a string, a CSS rule like ".thinger" or "div > span".
+ // 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 filter == "string"){ // inline'd type check
+ items = query._filterResult(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(array.filter(items, a[start], a[start + 1]), 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 = lang.isFunction(declaredClass) ? declaredClass : lang.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(0);
+ 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
+ }
+ });
+
+
+/*=====
+dojo.query = function(selector, context){
+ // summary:
+ // This modules provides DOM querying functionality. The module export is a function
+ // that can be used to query for DOM nodes by CSS selector and returns a dojo.NodeList
+ // representing the matching nodes.
+ //
+ // selector: String
+ // A CSS selector to search for.
+ // context: String|DomNode?
+ // An optional context to limit the searching scope. Only nodes under `context` will be
+ // scanned.
+ //
+ // example:
+ // add an onclick handler to every submit button in the document
+ // which causes the form to be sent via Ajax instead:
+ // | define(["dojo/query"], function(query){
+ // | query("input[type='submit']").on("click", 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");
+ // | }
+ // | });
+ // | });
+ //
+ // description:
+ // dojo/query is responsible for loading the appropriate query engine and wrapping
+ // its results with a `dojo.NodeList`. You can use dojo/query with a specific selector engine
+ // by using it as a plugin. For example, if you installed the sizzle package, you could
+ // use it as the selector engine with:
+ // | define("dojo/query!sizzle", function(query){
+ // | query("div")...
+ //
+ // The id after the ! can be a module id of the selector engine or one of the following values:
+ // | + acme: This is the default engine used by Dojo base, and will ensure that the full
+ // | Acme engine is always loaded.
+ // |
+ // | + css2: If the browser has a native selector engine, this will be used, otherwise a
+ // | very minimal lightweight selector engine will be loaded that can do simple CSS2 selectors
+ // | (by #id, .class, tag, and [name=value] attributes, with standard child or descendant (>)
+ // | operators) and nothing more.
+ // |
+ // | + css2.1: If the browser has a native selector engine, this will be used, otherwise the
+ // | full Acme engine will be loaded.
+ // |
+ // | + css3: If the browser has a native selector engine with support for CSS3 pseudo
+ // | selectors (most modern browsers except IE8), this will be used, otherwise the
+ // | full Acme engine will be loaded.
+ // |
+ // | + Or the module id of a selector engine can be used to explicitly choose the selector engine
+ //
+ // For example, if you are using CSS3 pseudo selectors in module, you can specify that
+ // you will need support them with:
+ // | define("dojo/query!css3", function(query){
+ // | query('#t > h3:nth-child(odd)')...
+ //
+ // You can also choose the selector engine/load configuration by setting the <FIXME:what is the configuration setting?>.
+ // For example:
+ // | <script data-dojo-config="query-selector:'css3'" src="dojo.js"></script>
+ //
+ return new dojo.NodeList(); // dojo.NodeList
+};
+=====*/
+
+function queryForEngine(engine, NodeList){
+ var query = function(/*String*/ query, /*String|DOMNode?*/ root){
+ // summary:
+ // Returns nodes which match the given CSS selector, searching the
+ // entire document by default but optionally taking a node to scope
+ // the search by. Returns an instance of dojo.NodeList.
+ if(typeof root == "string"){
+ root = dojo.byId(root);
+ if(!root){
+ return new NodeList([]);
+ }
+ }
+ var results = typeof query == "string" ? engine(query, root) : query.orphan ? query : [query];
+ if(results.orphan){
+ // already wrapped
+ return results;
+ }
+ return new NodeList(results);
+ };
+ query.matches = engine.match || function(node, selector, root){
+ // summary:
+ // Test to see if a node matches a selector
+ return query.filter([node], selector, root).length > 0;
+ };
+ // the engine provides a filtering function, use it to for matching
+ query.filter = engine.filter || function(nodes, selector, root){
+ // summary:
+ // Filters an array of nodes. Note that this does not guarantee to return a dojo.NodeList, just an array.
+ return query(selector, root).filter(function(node){
+ return dojo.indexOf(nodes, node) > -1;
+ });
+ };
+ if(typeof engine != "function"){
+ var search = engine.search;
+ engine = function(selector, root){
+ // Slick does it backwards (or everyone else does it backwards, probably the latter)
+ return search(root || document, selector);
+ };
+ }
+ return query;
+}
+var query = queryForEngine(defaultEngine, NodeList);
+// the query that is returned from this module is slightly different than dojo.query,
+// because dojo.query has to maintain backwards compatibility with returning a
+// true array which has performance problems. The query returned from the module
+// does not use true arrays, but rather inherits from Array, making it much faster to
+// instantiate.
+dojo.query = queryForEngine(defaultEngine, function(array){
+ // call it without the new operator to invoke the back-compat behavior that returns a true array
+ return NodeList(array);
+});
+
+query.load = /*===== dojo.query.load= ======*/ function(id, parentRequire, loaded, config){
+ // summary: can be used as AMD plugin to conditionally load new query engine
+ // example:
+ // | define(["dojo/query!custom"], function(qsa){
+ // | // loaded selector/custom.js as engine
+ // | qsa("#foobar").forEach(...);
+ // | });
+ loader.load(id, parentRequire, function(engine){
+ loaded(queryForEngine(engine, NodeList));
+ });
+};
+
+dojo._filterQueryResult = query._filterResult = function(nodes, selector, root){
+ return new NodeList(query.filter(nodes, selector, root));
+};
+dojo.NodeList = query.NodeList = NodeList;
+return query;
+});
diff --git a/selector/_loader.js b/selector/_loader.js
index a8d233b..e5c9094 100644
--- a/selector/_loader.js
+++ b/selector/_loader.js
@@ -1,45 +1,45 @@
-define(["../has", "require"],
- function(has, require){
-// summary:
-// This module handles loading the appropriate selector engine for the given browser
-"use strict";
-var testDiv = document.createElement("div");
-has.add("dom-qsa2.1", !!testDiv.querySelectorAll);
-has.add("dom-qsa3", function(){
- // test to see if we have a reasonable native selector engine available
- try{
- testDiv.innerHTML = "<p class='TEST'></p>"; // test kind of from sizzle
- // Safari can't handle uppercase or unicode characters when
- // in quirks mode, IE8 can't handle pseudos like :empty
- return testDiv.querySelectorAll(".TEST:empty").length == 1;
- }catch(e){}
- });
-var fullEngine;
-var acme = "./acme", lite = "./lite";
-return {
- load: function(id, parentRequire, loaded, config){
- var req = require;
- // here we implement the default logic for choosing a selector engine
- id = id == "default" ? has("config-selectorEngine") || "css3" : id;
- id = id == "css2" || id == "lite" ? lite :
- id == "css2.1" ? has("dom-qsa2.1") ? lite : acme :
- id == "css3" ? has("dom-qsa3") ? lite : acme :
- id == "acme" ? acme : (req = parentRequire) && id;
- if(id.charAt(id.length-1) == '?'){
- id = id.substring(0,id.length - 1);
- var optionalLoad = true;
- }
- // the query engine is optional, only load it if a native one is not available or existing one has not been loaded
- if(optionalLoad && (has("dom-compliant-qsa") || fullEngine)){
- return loaded(fullEngine);
- }
- // load the referenced selector engine
- req([id], function(engine){
- if(id != "./lite"){
- fullEngine = engine;
- }
- loaded(engine);
- });
- }
-};
-});
+define(["../has", "require"],
+ function(has, require){
+// summary:
+// This module handles loading the appropriate selector engine for the given browser
+"use strict";
+var testDiv = document.createElement("div");
+has.add("dom-qsa2.1", !!testDiv.querySelectorAll);
+has.add("dom-qsa3", function(){
+ // test to see if we have a reasonable native selector engine available
+ try{
+ testDiv.innerHTML = "<p class='TEST'></p>"; // test kind of from sizzle
+ // Safari can't handle uppercase or unicode characters when
+ // in quirks mode, IE8 can't handle pseudos like :empty
+ return testDiv.querySelectorAll(".TEST:empty").length == 1;
+ }catch(e){}
+ });
+var fullEngine;
+var acme = "./acme", lite = "./lite";
+return {
+ load: function(id, parentRequire, loaded, config){
+ var req = require;
+ // here we implement the default logic for choosing a selector engine
+ id = id == "default" ? has("config-selectorEngine") || "css3" : id;
+ id = id == "css2" || id == "lite" ? lite :
+ id == "css2.1" ? has("dom-qsa2.1") ? lite : acme :
+ id == "css3" ? has("dom-qsa3") ? lite : acme :
+ id == "acme" ? acme : (req = parentRequire) && id;
+ if(id.charAt(id.length-1) == '?'){
+ id = id.substring(0,id.length - 1);
+ var optionalLoad = true;
+ }
+ // the query engine is optional, only load it if a native one is not available or existing one has not been loaded
+ if(optionalLoad && (has("dom-compliant-qsa") || fullEngine)){
+ return loaded(fullEngine);
+ }
+ // load the referenced selector engine
+ req([id], function(engine){
+ if(id != "./lite"){
+ fullEngine = engine;
+ }
+ loaded(engine);
+ });
+ }
+};
+});
diff --git a/selector/acme.js b/selector/acme.js
index 7fade66..d90dc72 100644
--- a/selector/acme.js
+++ b/selector/acme.js
@@ -1,1480 +1,1480 @@
-define(["../_base/kernel", "../has", "../dom", "../_base/sniff", "../_base/array", "../_base/lang", "../_base/window"], function(dojo, has, dom){
- // module:
- // dojo/selector/acme
- // summary:
- // This module defines the Acme selector engine
-
-/*
- acme architectural overview:
-
- acme 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)
-*/
-
-
- ////////////////////////////////////////////////////////////////////////
- // Toolkit aliases
- ////////////////////////////////////////////////////////////////////////
-
- // if you are extracting acme 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 = dojo.trim;
- var each = dojo.forEach;
- // d.isIE; // float
- // d.isSafari; // float
- // d.isOpera; // float
- // d.isWebKit; // float
- // d.doc ; // document element
-
- var getDoc = function(){ return dojo.doc; };
- // NOTE(alex): the spec is idiotic. CSS queries should ALWAYS be case-sensitive, but nooooooo
- var cssCaseBug = ((dojo.isWebKit||dojo.isMozilla) && ((getDoc().compatMode) == "BackCompat"));
-
- ////////////////////////////////////////////////////////////////////////
- // Global utilities
- ////////////////////////////////////////////////////////////////////////
-
-
- 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,
- // acme 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) character 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 readability 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 during 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, character by character, 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 character (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 existence 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 pseudo-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 passed, 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"
- 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.children || root.childNodes,
- ci = (node["_i"]||-1),
- cl = (root["_l"]||-1);
-
- if(!tret){ return -1; }
- var l = tret.length;
-
- // we calculate 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:
- // shortcutting 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){
- return _lookLeft(node) && _lookRight(node);
- };
- },
- "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 = (dojo.isIE && (dojo.isIE < 9 || dojo.isQuirks)) ? 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 generated 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 memoized 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.children || root.childNodes;
- 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
- // found, 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 = dom.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, []);
- 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 = (
- dojo.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 = dojo.isIE ? "commentStrip" : "nozip";
-
- var qsa = "querySelectorAll";
- var qsaAvail = (
- !!getDoc()[qsa] &&
- // see #5832
- (!dojo.isSafari || (dojo.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
- (!dojo.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 = dojo.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 arr;
- }
- var ret = [];
- 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(dojo.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(dojo.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
- var 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 array.
- // 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:
- // --------------------
- //
- // acme 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 preceded-by sibling selector
- // * `+`, the immediately preceded-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 reasonable 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: Array
- // 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");
- // | }
- // | });
- // | });
-
- 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") ||
- (dojo.isOpera && (root.doctype || od.toString() == "[object XMLDocument]")) ||
- (!!od) &&
- (dojo.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){
- return r;
- }
- return _zip(r); // dojo.NodeList
- };
- query.filter = function(/*Node[]*/ nodeList, /*String*/ filter, /*String|DOMNode?*/ root){
- // summary:
- // function for filtering a NodeList based on a selector, optimized for simple selectors
- var tmpNodeList = [],
- parts = getQueryParts(filter),
- filterFunc =
- (parts.length == 1 && !/[^\w#\.]/.test(filter)) ?
- getSimpleFilterFunc(parts[0]) :
- function(node){
- return dojo.query(filter, root).indexOf(node) != -1;
- };
- for(var x = 0, te; te = nodeList[x]; x++){
- if(filterFunc(te)){ tmpNodeList.push(te); }
- }
- return tmpNodeList;
- };
- return query;
-});//end defineQuery
+define(["../_base/kernel", "../has", "../dom", "../_base/sniff", "../_base/array", "../_base/lang", "../_base/window"], function(dojo, has, dom){
+ // module:
+ // dojo/selector/acme
+ // summary:
+ // This module defines the Acme selector engine
+
+/*
+ acme architectural overview:
+
+ acme 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)
+*/
+
+
+ ////////////////////////////////////////////////////////////////////////
+ // Toolkit aliases
+ ////////////////////////////////////////////////////////////////////////
+
+ // if you are extracting acme 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 = dojo.trim;
+ var each = dojo.forEach;
+ // d.isIE; // float
+ // d.isSafari; // float
+ // d.isOpera; // float
+ // d.isWebKit; // float
+ // d.doc ; // document element
+
+ var getDoc = function(){ return dojo.doc; };
+ // NOTE(alex): the spec is idiotic. CSS queries should ALWAYS be case-sensitive, but nooooooo
+ var cssCaseBug = ((dojo.isWebKit||dojo.isMozilla) && ((getDoc().compatMode) == "BackCompat"));
+
+ ////////////////////////////////////////////////////////////////////////
+ // Global utilities
+ ////////////////////////////////////////////////////////////////////////
+
+
+ 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,
+ // acme 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) character 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 readability 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 during 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, character by character, 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 character (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 existence 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 pseudo-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 passed, 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"
+ 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.children || root.childNodes,
+ ci = (node["_i"]||-1),
+ cl = (root["_l"]||-1);
+
+ if(!tret){ return -1; }
+ var l = tret.length;
+
+ // we calculate 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:
+ // shortcutting 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){
+ return _lookLeft(node) && _lookRight(node);
+ };
+ },
+ "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 = (dojo.isIE && (dojo.isIE < 9 || dojo.isQuirks)) ? 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 generated 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 memoized 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.children || root.childNodes;
+ 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
+ // found, 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 = dom.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, []);
+ 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 = (
+ dojo.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 = dojo.isIE ? "commentStrip" : "nozip";
+
+ var qsa = "querySelectorAll";
+ var qsaAvail = (
+ !!getDoc()[qsa] &&
+ // see #5832
+ (!dojo.isSafari || (dojo.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
+ (!dojo.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 = dojo.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 arr;
+ }
+ var ret = [];
+ 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(dojo.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(dojo.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
+ var 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 array.
+ // 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:
+ // --------------------
+ //
+ // acme 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 preceded-by sibling selector
+ // * `+`, the immediately preceded-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 reasonable 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: Array
+ // 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");
+ // | }
+ // | });
+ // | });
+
+ 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") ||
+ (dojo.isOpera && (root.doctype || od.toString() == "[object XMLDocument]")) ||
+ (!!od) &&
+ (dojo.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){
+ return r;
+ }
+ return _zip(r); // dojo.NodeList
+ };
+ query.filter = function(/*Node[]*/ nodeList, /*String*/ filter, /*String|DOMNode?*/ root){
+ // summary:
+ // function for filtering a NodeList based on a selector, optimized for simple selectors
+ var tmpNodeList = [],
+ parts = getQueryParts(filter),
+ filterFunc =
+ (parts.length == 1 && !/[^\w#\.]/.test(filter)) ?
+ getSimpleFilterFunc(parts[0]) :
+ function(node){
+ return dojo.query(filter, root).indexOf(node) != -1;
+ };
+ for(var x = 0, te; te = nodeList[x]; x++){
+ if(filterFunc(te)){ tmpNodeList.push(te); }
+ }
+ return tmpNodeList;
+ };
+ return query;
+});//end defineQuery
diff --git a/selector/lite.js b/selector/lite.js
index 550edf1..8228828 100644
--- a/selector/lite.js
+++ b/selector/lite.js
@@ -1,264 +1,264 @@
-define(["../has", "../_base/kernel"], function(has, dojo){
-"use strict";
-// summary:
-// A small lightweight query selector engine that implements CSS2.1 selectors
-// minus pseudo-classes and the sibling combinator, plus CSS3 attribute selectors
-var testDiv = document.createElement("div");
-var matchesSelector = testDiv.matchesSelector || testDiv.webkitMatchesSelector || testDiv.mozMatchesSelector || testDiv.msMatchesSelector || testDiv.oMatchesSelector; // IE9, WebKit, Firefox have this, but not Opera yet
-var querySelectorAll = testDiv.querySelectorAll;
-has.add("dom-matches-selector", !!matchesSelector);
-has.add("dom-qsa", !!querySelectorAll);
-
-// this is a simple query engine. It has handles basic selectors, and for simple
-// common selectors is extremely fast
-var liteEngine = function(selector, root){
- if(combine && selector.indexOf(',') > -1){
- return combine(selector, root);
- }
- var match = (querySelectorAll ?
- /^([\w]*)#([\w\-]+$)|^(\.)([\w\-\*]+$)|^(\w+$)/ : // this one only matches on simple queries where we can beat qSA with specific methods
- /^([\w]*)#([\w\-]+)(?:\s+(.*))?$|(?:^|(>|.+\s+))([\w\-\*]+)(\S*$)/) // this one matches parts of the query that we can use to speed up manual filtering
- .exec(selector);
- root = root || document;
- if(match){
- // fast path regardless of whether or not querySelectorAll exists
- if(match[2]){
- // an #id
- // use dojo.byId if available as it fixes the id retrieval in IE
- var found = dojo.byId ? dojo.byId(match[2]) : document.getElementById(match[2]);
- if(!found || (match[1] && match[1] != found.tagName.toLowerCase())){
- // if there is a tag qualifer and it doesn't match, no matches
- return [];
- }
- if(root != document){
- // there is a root element, make sure we are a child of it
- var parent = found;
- while(parent != root){
- parent = parent.parentNode;
- if(!parent){
- return [];
- }
- }
- }
- return match[3] ?
- liteEngine(match[3], found)
- : [found];
- }
- if(match[3] && root.getElementsByClassName){
- // a .class
- return root.getElementsByClassName(match[4]);
- }
- var found;
- if(match[5]){
- // a tag
- found = root.getElementsByTagName(match[5]);
- if(match[4] || match[6]){
- selector = (match[4] || "") + match[6];
- }else{
- // that was the entirety of the query, return results
- return found;
- }
- }
- }
- if(querySelectorAll){
- // qSA works strangely on Element-rooted queries
- // We can work around this by specifying an extra ID on the root
- // and working up from there (Thanks to Andrew Dupont for the technique)
- // IE 8 doesn't work on object elements
- if (root.nodeType === 1 && root.nodeName.toLowerCase() !== "object"){
- return useRoot(root, selector, root.querySelectorAll);
- }else{
- // we can use the native qSA
- return root.querySelectorAll(selector);
- }
- }else if(!found){
- // search all children and then filter
- found = root.getElementsByTagName("*");
- }
- // now we filter the nodes that were found using the matchesSelector
- var results = [];
- for(var i = 0, l = found.length; i < l; i++){
- var node = found[i];
- if(node.nodeType == 1 && jsMatchesSelector(node, selector, root)){
- // keep the nodes that match the selector
- results.push(node);
- }
- }
- return results;
-};
-var useRoot = function(context, query, method){
- // this function creates a temporary id so we can do rooted qSA queries, this is taken from sizzle
- var oldContext = context,
- old = context.getAttribute( "id" ),
- nid = old || "__dojo__",
- hasParent = context.parentNode,
- relativeHierarchySelector = /^\s*[+~]/.test( query );
-
- if(relativeHierarchySelector && !hasParent){
- return [];
- }
- if(!old){
- context.setAttribute("id", nid);
- }else{
- nid = nid.replace(/'/g, "\\$&");
- }
- if(relativeHierarchySelector && hasParent){
- context = context.parentNode;
- }
-
- try {
- return method.call(context, "[id='" + nid + "'] " + query );
- } finally {
- if ( !old ) {
- oldContext.removeAttribute( "id" );
- }
- }
-};
-
-if(!has("dom-matches-selector")){
- var jsMatchesSelector = (function(){
- // a JS implementation of CSS selector matching, first we start with the various handlers
- var caseFix = testDiv.tagName == "div" ? "toLowerCase" : "toUpperCase";
- function tag(tagName){
- tagName = tagName[caseFix]();
- return function(node){
- return node.tagName == tagName;
- }
- }
- function className(className){
- var classNameSpaced = ' ' + className + ' ';
- return function(node){
- return node.className.indexOf(className) > -1 && (' ' + node.className + ' ').indexOf(classNameSpaced) > -1;
- }
- }
- var attrComparators = {
- "^=": function(attrValue, value){
- return attrValue.indexOf(value) == 0;
- },
- "*=": function(attrValue, value){
- return attrValue.indexOf(value) > -1;
- },
- "$=": function(attrValue, value){
- return attrValue.substring(attrValue.length - value.length, attrValue.length) == value;
- },
- "~=": function(attrValue, value){
- return (' ' + attrValue + ' ').indexOf(' ' + value + ' ') > -1;
- },
- "|=": function(attrValue, value){
- return (attrValue + '-').indexOf(value + '-') == 0;
- },
- "=": function(attrValue, value){
- return attrValue == value;
- },
- "": function(attrValue, value){
- return true;
- }
- };
- function attr(name, value, type){
- if(value.match(/['"]/)){
- // it is quoted, do an eval to parse the string (CSS and JS parsing are close enough)
- value = eval(value);
- }
- var comparator = attrComparators[type || ""];
- return function(node){
- var attrValue = node.getAttribute(name);
- return attrValue && comparator(attrValue, value);
- }
- }
- function ancestor(matcher){
- return function(node, root){
- while((node = node.parentNode) != root){
- if(matcher(node, root)){
- return true;
- }
- }
- };
- }
- function parent(matcher){
- return function(node, root){
- node = node.parentNode;
- return matcher ?
- node != root && matcher(node, root)
- : node == root;
- };
- }
- var cache = {};
- function and(matcher, next){
- return matcher ?
- function(node, root){
- return next(node) && matcher(node, root);
- }
- : next;
- }
- return function(node, selector, root){
- // this returns true or false based on if the node matches the selector (optionally within the given root)
- var matcher = cache[selector]; // check to see if we have created a matcher function for the given selector
- if(!matcher){
- // create a matcher function for the given selector
- // parse the selectors
- if(selector.replace(/(?:\s*([> ])\s*)|(\.)?([\w-]+)|\[([\w-]+)\s*(.?=)?\s*([^\]]*)\]/g, function(t, combinator, type, value, attrName, attrType, attrValue){
- if(value){
- if(type == "."){
- matcher = and(matcher, className(value));
- }
- else{
- matcher = and(matcher, tag(value));
- }
- }
- else if(combinator){
- matcher = (combinator == " " ? ancestor : parent)(matcher);
- }
- else if(attrName){
- matcher = and(matcher, attr(attrName, attrValue, attrType));
- }
- return "";
- })){
- throw new Error("Syntax error in query");
- }
- if(!matcher){
- return true;
- }
- cache[selector] = matcher;
- }
- // now run the matcher function on the node
- return matcher(node, root);
- };
- })();
-}
-if(!has("dom-qsa")){
- var combine = function(selector, root){
- // combined queries
- selector = selector.split(/\s*,\s*/);
- var indexed = [];
- // add all results and keep unique ones, this only runs in IE, so we take advantage
- // of known IE features, particularly sourceIndex which is unique and allows us to
- // order the results
- for(var i = 0; i < selector.length; i++){
- var results = liteEngine(selector[i], root);
- for(var j = 0, l = results.length; j < l; j++){
- var node = results[j];
- indexed[node.sourceIndex] = node;
- }
- }
- // now convert from a sparse array to a dense array
- var totalResults = [];
- for(i in indexed){
- totalResults.push(indexed[i]);
- }
- return totalResults;
- };
-}
-
-liteEngine.match = matchesSelector ? function(node, selector, root){
- if(root){
- // doesn't support three args, use rooted id trick
- return useRoot(root, selector, function(query){
- return matchesSelector.call(node, query);
- });
- }
- // we have a native matchesSelector, use that
- return matchesSelector.call(node, selector);
-} : jsMatchesSelector; // otherwise use the JS matches impl
-
-return liteEngine;
-});
+define(["../has", "../_base/kernel"], function(has, dojo){
+"use strict";
+// summary:
+// A small lightweight query selector engine that implements CSS2.1 selectors
+// minus pseudo-classes and the sibling combinator, plus CSS3 attribute selectors
+var testDiv = document.createElement("div");
+var matchesSelector = testDiv.matchesSelector || testDiv.webkitMatchesSelector || testDiv.mozMatchesSelector || testDiv.msMatchesSelector || testDiv.oMatchesSelector; // IE9, WebKit, Firefox have this, but not Opera yet
+var querySelectorAll = testDiv.querySelectorAll;
+has.add("dom-matches-selector", !!matchesSelector);
+has.add("dom-qsa", !!querySelectorAll);
+
+// this is a simple query engine. It has handles basic selectors, and for simple
+// common selectors is extremely fast
+var liteEngine = function(selector, root){
+ if(combine && selector.indexOf(',') > -1){
+ return combine(selector, root);
+ }
+ var match = (querySelectorAll ?
+ /^([\w]*)#([\w\-]+$)|^(\.)([\w\-\*]+$)|^(\w+$)/ : // this one only matches on simple queries where we can beat qSA with specific methods
+ /^([\w]*)#([\w\-]+)(?:\s+(.*))?$|(?:^|(>|.+\s+))([\w\-\*]+)(\S*$)/) // this one matches parts of the query that we can use to speed up manual filtering
+ .exec(selector);
+ root = root || document;
+ if(match){
+ // fast path regardless of whether or not querySelectorAll exists
+ if(match[2]){
+ // an #id
+ // use dojo.byId if available as it fixes the id retrieval in IE
+ var found = dojo.byId ? dojo.byId(match[2]) : document.getElementById(match[2]);
+ if(!found || (match[1] && match[1] != found.tagName.toLowerCase())){
+ // if there is a tag qualifer and it doesn't match, no matches
+ return [];
+ }
+ if(root != document){
+ // there is a root element, make sure we are a child of it
+ var parent = found;
+ while(parent != root){
+ parent = parent.parentNode;
+ if(!parent){
+ return [];
+ }
+ }
+ }
+ return match[3] ?
+ liteEngine(match[3], found)
+ : [found];
+ }
+ if(match[3] && root.getElementsByClassName){
+ // a .class
+ return root.getElementsByClassName(match[4]);
+ }
+ var found;
+ if(match[5]){
+ // a tag
+ found = root.getElementsByTagName(match[5]);
+ if(match[4] || match[6]){
+ selector = (match[4] || "") + match[6];
+ }else{
+ // that was the entirety of the query, return results
+ return found;
+ }
+ }
+ }
+ if(querySelectorAll){
+ // qSA works strangely on Element-rooted queries
+ // We can work around this by specifying an extra ID on the root
+ // and working up from there (Thanks to Andrew Dupont for the technique)
+ // IE 8 doesn't work on object elements
+ if (root.nodeType === 1 && root.nodeName.toLowerCase() !== "object"){
+ return useRoot(root, selector, root.querySelectorAll);
+ }else{
+ // we can use the native qSA
+ return root.querySelectorAll(selector);
+ }
+ }else if(!found){
+ // search all children and then filter
+ found = root.getElementsByTagName("*");
+ }
+ // now we filter the nodes that were found using the matchesSelector
+ var results = [];
+ for(var i = 0, l = found.length; i < l; i++){
+ var node = found[i];
+ if(node.nodeType == 1 && jsMatchesSelector(node, selector, root)){
+ // keep the nodes that match the selector
+ results.push(node);
+ }
+ }
+ return results;
+};
+var useRoot = function(context, query, method){
+ // this function creates a temporary id so we can do rooted qSA queries, this is taken from sizzle
+ var oldContext = context,
+ old = context.getAttribute( "id" ),
+ nid = old || "__dojo__",
+ hasParent = context.parentNode,
+ relativeHierarchySelector = /^\s*[+~]/.test( query );
+
+ if(relativeHierarchySelector && !hasParent){
+ return [];
+ }
+ if(!old){
+ context.setAttribute("id", nid);
+ }else{
+ nid = nid.replace(/'/g, "\\$&");
+ }
+ if(relativeHierarchySelector && hasParent){
+ context = context.parentNode;
+ }
+
+ try {
+ return method.call(context, "[id='" + nid + "'] " + query );
+ } finally {
+ if ( !old ) {
+ oldContext.removeAttribute( "id" );
+ }
+ }
+};
+
+if(!has("dom-matches-selector")){
+ var jsMatchesSelector = (function(){
+ // a JS implementation of CSS selector matching, first we start with the various handlers
+ var caseFix = testDiv.tagName == "div" ? "toLowerCase" : "toUpperCase";
+ function tag(tagName){
+ tagName = tagName[caseFix]();
+ return function(node){
+ return node.tagName == tagName;
+ }
+ }
+ function className(className){
+ var classNameSpaced = ' ' + className + ' ';
+ return function(node){
+ return node.className.indexOf(className) > -1 && (' ' + node.className + ' ').indexOf(classNameSpaced) > -1;
+ }
+ }
+ var attrComparators = {
+ "^=": function(attrValue, value){
+ return attrValue.indexOf(value) == 0;
+ },
+ "*=": function(attrValue, value){
+ return attrValue.indexOf(value) > -1;
+ },
+ "$=": function(attrValue, value){
+ return attrValue.substring(attrValue.length - value.length, attrValue.length) == value;
+ },
+ "~=": function(attrValue, value){
+ return (' ' + attrValue + ' ').indexOf(' ' + value + ' ') > -1;
+ },
+ "|=": function(attrValue, value){
+ return (attrValue + '-').indexOf(value + '-') == 0;
+ },
+ "=": function(attrValue, value){
+ return attrValue == value;
+ },
+ "": function(attrValue, value){
+ return true;
+ }
+ };
+ function attr(name, value, type){
+ if(value.match(/['"]/)){
+ // it is quoted, do an eval to parse the string (CSS and JS parsing are close enough)
+ value = eval(value);
+ }
+ var comparator = attrComparators[type || ""];
+ return function(node){
+ var attrValue = node.getAttribute(name);
+ return attrValue && comparator(attrValue, value);
+ }
+ }
+ function ancestor(matcher){
+ return function(node, root){
+ while((node = node.parentNode) != root){
+ if(matcher(node, root)){
+ return true;
+ }
+ }
+ };
+ }
+ function parent(matcher){
+ return function(node, root){
+ node = node.parentNode;
+ return matcher ?
+ node != root && matcher(node, root)
+ : node == root;
+ };
+ }
+ var cache = {};
+ function and(matcher, next){
+ return matcher ?
+ function(node, root){
+ return next(node) && matcher(node, root);
+ }
+ : next;
+ }
+ return function(node, selector, root){
+ // this returns true or false based on if the node matches the selector (optionally within the given root)
+ var matcher = cache[selector]; // check to see if we have created a matcher function for the given selector
+ if(!matcher){
+ // create a matcher function for the given selector
+ // parse the selectors
+ if(selector.replace(/(?:\s*([> ])\s*)|(\.)?([\w-]+)|\[([\w-]+)\s*(.?=)?\s*([^\]]*)\]/g, function(t, combinator, type, value, attrName, attrType, attrValue){
+ if(value){
+ if(type == "."){
+ matcher = and(matcher, className(value));
+ }
+ else{
+ matcher = and(matcher, tag(value));
+ }
+ }
+ else if(combinator){
+ matcher = (combinator == " " ? ancestor : parent)(matcher);
+ }
+ else if(attrName){
+ matcher = and(matcher, attr(attrName, attrValue, attrType));
+ }
+ return "";
+ })){
+ throw new Error("Syntax error in query");
+ }
+ if(!matcher){
+ return true;
+ }
+ cache[selector] = matcher;
+ }
+ // now run the matcher function on the node
+ return matcher(node, root);
+ };
+ })();
+}
+if(!has("dom-qsa")){
+ var combine = function(selector, root){
+ // combined queries
+ selector = selector.split(/\s*,\s*/);
+ var indexed = [];
+ // add all results and keep unique ones, this only runs in IE, so we take advantage
+ // of known IE features, particularly sourceIndex which is unique and allows us to
+ // order the results
+ for(var i = 0; i < selector.length; i++){
+ var results = liteEngine(selector[i], root);
+ for(var j = 0, l = results.length; j < l; j++){
+ var node = results[j];
+ indexed[node.sourceIndex] = node;
+ }
+ }
+ // now convert from a sparse array to a dense array
+ var totalResults = [];
+ for(i in indexed){
+ totalResults.push(indexed[i]);
+ }
+ return totalResults;
+ };
+}
+
+liteEngine.match = matchesSelector ? function(node, selector, root){
+ if(root){
+ // doesn't support three args, use rooted id trick
+ return useRoot(root, selector, function(query){
+ return matchesSelector.call(node, query);
+ });
+ }
+ // we have a native matchesSelector, use that
+ return matchesSelector.call(node, selector);
+} : jsMatchesSelector; // otherwise use the JS matches impl
+
+return liteEngine;
+});
diff --git a/store/api/Store.js b/store/api/Store.js
index 7f2533d..f16a836 100644
--- a/store/api/Store.js
+++ b/store/api/Store.js
@@ -1,297 +1,297 @@
-define(["dojo/_base/declare"], function(declare) {
- // module:
- // dojo/store/api/Store
- // summary:
- // The module defines the Dojo object store interface.
-
-var Store = declare("dojo.store.api.Store", null, {
- // summary:
- // This is an abstract API that data provider implementations conform to.
- // This file defines methods signatures and intentionally leaves all the
- // methods unimplemented. For more information on the dojo.store APIs,
- // please visit: http://dojotoolkit.org/reference-guide/dojo/store.html
- // Every method and property is optional, and is only needed if the functionality
- // it provides is required.
- // Every method may return a promise for the specified return value if the
- // execution of the operation is asynchronous (except
- // for query() which already defines an async return value).
-
- // idProperty: String
- // If the store has a single primary key, this tndicates the property to use as the
- // identity property. The values of this property should be unique.
- idProperty: "id",
-
- // queryEngine: Function
- // If the store can be queried locally (on the client side in JS), this defines
- // the query engine to use for querying the data store.
- // This takes a query and query options and returns a function that can execute
- // the provided query on a JavaScript array. The queryEngine may be replace to
- // provide more sophisticated querying capabilities. For example:
- // | var query = store.queryEngine({foo:"bar"}, {count:10});
- // | query(someArray) -> filtered array
- // The returned query function may have a "matches" property that can be
- // used to determine if an object matches the query. For example:
- // | query.matches({id:"some-object", foo:"bar"}) -> true
- // | query.matches({id:"some-object", foo:"something else"}) -> false
- queryEngine: null,
-
- get: function(id){
- // summary:
- // Retrieves an object by its identity
- // id: Number
- // The identity to use to lookup the object
- // returns: Object
- // The object in the store that matches the given id.
- },
- getIdentity: function(object){
- // summary:
- // Returns an object's identity
- // object: Object
- // The object to get the identity from
- // returns: String|Number
- },
- put: function(object, directives){
- // summary:
- // Stores an object
- // object: Object
- // The object to store.
- // directives: dojo.store.api.Store.PutDirectives?
- // Additional directives for storing objects.
- // returns: Number|String
- },
- add: function(object, directives){
- // summary:
- // Creates an object, throws an error if the object already exists
- // object: Object
- // The object to store.
- // directives: dojo.store.api.Store.PutDirectives?
- // Additional directives for creating objects.
- // returns: Number|String
- },
- remove: function(id){
- // summary:
- // Deletes an object by its identity
- // id: Number
- // The identity to use to delete the object
- delete this.index[id];
- var data = this.data,
- idProperty = this.idProperty;
- for(var i = 0, l = data.length; i < l; i++){
- if(data[i][idProperty] == id){
- data.splice(i, 1);
- return;
- }
- }
- },
- query: function(query, options){
- // summary:
- // Queries the store for objects. This does not alter the store, but returns a
- // set of data from the store.
- // query: String|Object|Function
- // The query to use for retrieving objects from the store.
- // options: dojo.store.api.Store.QueryOptions
- // The optional arguments to apply to the resultset.
- // returns: dojo.store.api.Store.QueryResults
- // The results of the query, extended with iterative methods.
- //
- // example:
- // Given the following store:
- //
- // ...find all items where "prime" is true:
- //
- // | store.query({ prime: true }).forEach(function(object){
- // | // handle each object
- // | });
- },
- transaction: function(){
- // summary:
- // Starts a new transaction.
- // Note that a store user might not call transaction() prior to using put,
- // delete, etc. in which case these operations effectively could be thought of
- // as "auto-commit" style actions.
- // returns: dojo.store.api.Store.Transaction
- // This represents the new current transaction.
- },
- getChildren: function(parent, options){
- // summary:
- // Retrieves the children of an object.
- // parent: Object
- // The object to find the children of.
- // options: dojo.store.api.Store.QueryOptions?
- // Additional options to apply to the retrieval of the children.
- // returns: dojo.store.api.Store.QueryResults
- // A result set of the children of the parent object.
- },
- getMetadata: function(object){
- // summary:
- // Returns any metadata about the object. This may include attribution,
- // cache directives, history, or version information.
- // object: Object
- // The object to return metadata for.
- // returns: Object
- // An object containing metadata.
- }
-});
-
-Store.PutDirectives = function(id, before, parent, overwrite){
- // summary:
- // Directives passed to put() and add() handlers for guiding the update and
- // creation of stored objects.
- // id: String|Number?
- // Indicates the identity of the object if a new object is created
- // before: Object?
- // If the collection of objects in the store has a natural ordering,
- // this indicates that the created or updated object should be placed before the
- // object specified by the value of this property. A value of null indicates that the
- // object should be last.
- // parent: Object?,
- // If the store is hierarchical (with single parenting) this property indicates the
- // new parent of the created or updated object.
- // overwrite: Boolean?
- // If this is provided as a boolean it indicates that the object should or should not
- // overwrite an existing object. A value of true indicates that a new object
- // should not be created, the operation should update an existing object. A
- // value of false indicates that an existing object should not be updated, a new
- // object should be created (which is the same as an add() operation). When
- // this property is not provided, either an update or creation is acceptable.
- this.id = id;
- this.before = before;
- this.parent = parent;
- this.overwrite = overwrite;
-};
-
-Store.SortInformation = function(attribute, descending){
- // summary:
- // An object describing what attribute to sort on, and the direction of the sort.
- // attribute: String
- // The name of the attribute to sort on.
- // descending: Boolean
- // The direction of the sort. Default is false.
- this.attribute = attribute;
- this.descending = descending;
-};
-
-Store.QueryOptions = function(sort, start, count){
- // summary:
- // Optional object with additional parameters for query results.
- // sort: dojo.store.api.Store.SortInformation[]?
- // A list of attributes to sort on, as well as direction
- // For example:
- // | [{attribute:"price, descending: true}].
- // If the sort parameter is omitted, then the natural order of the store may be
- // applied if there is a natural order.
- // start: Number?
- // The first result to begin iteration on
- // count: Number?
- // The number of how many results should be returned.
- this.sort = sort;
- this.start = start;
- this.count = count;
-};
-
-declare("dojo.store.api.Store.QueryResults", null, {
- // summary:
- // This is an object returned from query() calls that provides access to the results
- // of a query. Queries may be executed asynchronously.
-
- forEach: function(callback, thisObject){
- // summary:
- // Iterates over the query results, based on
- // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/forEach.
- // Note that this may executed asynchronously. The callback may be called
- // after this function returns.
- // callback:
- // Function that is called for each object in the query results
- // thisObject:
- // The object to use as |this| in the callback.
-
- },
- filter: function(callback, thisObject){
- // summary:
- // Filters the query results, based on
- // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/filter.
- // Note that this may executed asynchronously. The callback may be called
- // after this function returns.
- // callback:
- // Function that is called for each object in the query results
- // thisObject:
- // The object to use as |this| in the callback.
- // returns: dojo.store.api.Store.QueryResults
- },
- map: function(callback, thisObject){
- // summary:
- // Maps the query results, based on
- // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/map.
- // Note that this may executed asynchronously. The callback may be called
- // after this function returns.
- // callback:
- // Function that is called for each object in the query results
- // thisObject:
- // The object to use as |this| in the callback.
- // returns: dojo.store.api.Store.QueryResults
- },
- then: function(callback, errorHandler){
- // summary:
- // This registers a callback for when the query is complete, if the query is asynchronous.
- // This is an optional method, and may not be present for synchronous queries.
- // callback:
- // This is called when the query is completed successfully, and is passed a single argument
- // that is an array representing the query results.
- // errorHandler:
- // This is called if the query failed, and is passed a single argument that is the error
- // for the failure.
- },
- observe: function(listener, includeAllUpdates){
- // summary:
- // This registers a callback for notification of when data is modified in the query results.
- // This is an optional method, and is usually provided by dojo.store.Observable.
- // listener: Function
- // The listener function is called when objects in the query results are modified
- // to affect the query result. The listener function is called with the following
- // arguments:
- // | listener(object, removedFrom, insertedInto);
- // * The object parameter indicates the object that was create, modified, or deleted.
- // * The removedFrom parameter indicates the index in the result array where
- // the object used to be. If the value is -1, then the object is an addition to
- // this result set (due to a new object being created, or changed such that it
- // is a part of the result set).
- // * The insertedInto parameter indicates the index in the result array where
- // the object should be now. If the value is -1, then the object is a removal
- // from this result set (due to an object being deleted, or changed such that it
- // is not a part of the result set).
- // includeAllUpdates:
- // This indicates whether or not to include object updates that do not affect
- // the inclusion or order of the object in the query results. By default this is false,
- // which means that if any object is updated in such a way that it remains
- // in the result set and it's position in result sets is not affected, then the listener
- // will not be fired.
-
- },
- // total: Number|Promise?
- // This property should be included in if the query options included the "count"
- // property limiting the result set. This property indicates the total number of objects
- // matching the query (as if "start" and "count" weren't present). This may be
- // a promise if the query is asynchronous.
- total: 0
-});
-
-declare("dojo.store.api.Store.Transaction", null, {
- // summary:
- // This is an object returned from transaction() calls that represents the current
- // transaction.
-
- commit: function(){
- // summary:
- // Commits the transaction. This may throw an error if it fails. Of if the operation
- // is asynchronous, it may return a promise that represents the eventual success
- // or failure of the commit.
- },
- abort: function(callback, thisObject){
- // summary:
- // Aborts the transaction. This may throw an error if it fails. Of if the operation
- // is asynchronous, it may return a promise that represents the eventual success
- // or failure of the abort.
- }
-});
-return Store;
-});
+define(["dojo/_base/declare"], function(declare) {
+ // module:
+ // dojo/store/api/Store
+ // summary:
+ // The module defines the Dojo object store interface.
+
+var Store = declare("dojo.store.api.Store", null, {
+ // summary:
+ // This is an abstract API that data provider implementations conform to.
+ // This file defines methods signatures and intentionally leaves all the
+ // methods unimplemented. For more information on the dojo.store APIs,
+ // please visit: http://dojotoolkit.org/reference-guide/dojo/store.html
+ // Every method and property is optional, and is only needed if the functionality
+ // it provides is required.
+ // Every method may return a promise for the specified return value if the
+ // execution of the operation is asynchronous (except
+ // for query() which already defines an async return value).
+
+ // idProperty: String
+ // If the store has a single primary key, this tndicates the property to use as the
+ // identity property. The values of this property should be unique.
+ idProperty: "id",
+
+ // queryEngine: Function
+ // If the store can be queried locally (on the client side in JS), this defines
+ // the query engine to use for querying the data store.
+ // This takes a query and query options and returns a function that can execute
+ // the provided query on a JavaScript array. The queryEngine may be replace to
+ // provide more sophisticated querying capabilities. For example:
+ // | var query = store.queryEngine({foo:"bar"}, {count:10});
+ // | query(someArray) -> filtered array
+ // The returned query function may have a "matches" property that can be
+ // used to determine if an object matches the query. For example:
+ // | query.matches({id:"some-object", foo:"bar"}) -> true
+ // | query.matches({id:"some-object", foo:"something else"}) -> false
+ queryEngine: null,
+
+ get: function(id){
+ // summary:
+ // Retrieves an object by its identity
+ // id: Number
+ // The identity to use to lookup the object
+ // returns: Object
+ // The object in the store that matches the given id.
+ },
+ getIdentity: function(object){
+ // summary:
+ // Returns an object's identity
+ // object: Object
+ // The object to get the identity from
+ // returns: String|Number
+ },
+ put: function(object, directives){
+ // summary:
+ // Stores an object
+ // object: Object
+ // The object to store.
+ // directives: dojo.store.api.Store.PutDirectives?
+ // Additional directives for storing objects.
+ // returns: Number|String
+ },
+ add: function(object, directives){
+ // summary:
+ // Creates an object, throws an error if the object already exists
+ // object: Object
+ // The object to store.
+ // directives: dojo.store.api.Store.PutDirectives?
+ // Additional directives for creating objects.
+ // returns: Number|String
+ },
+ remove: function(id){
+ // summary:
+ // Deletes an object by its identity
+ // id: Number
+ // The identity to use to delete the object
+ delete this.index[id];
+ var data = this.data,
+ idProperty = this.idProperty;
+ for(var i = 0, l = data.length; i < l; i++){
+ if(data[i][idProperty] == id){
+ data.splice(i, 1);
+ return;
+ }
+ }
+ },
+ query: function(query, options){
+ // summary:
+ // Queries the store for objects. This does not alter the store, but returns a
+ // set of data from the store.
+ // query: String|Object|Function
+ // The query to use for retrieving objects from the store.
+ // options: dojo.store.api.Store.QueryOptions
+ // The optional arguments to apply to the resultset.
+ // returns: dojo.store.api.Store.QueryResults
+ // The results of the query, extended with iterative methods.
+ //
+ // example:
+ // Given the following store:
+ //
+ // ...find all items where "prime" is true:
+ //
+ // | store.query({ prime: true }).forEach(function(object){
+ // | // handle each object
+ // | });
+ },
+ transaction: function(){
+ // summary:
+ // Starts a new transaction.
+ // Note that a store user might not call transaction() prior to using put,
+ // delete, etc. in which case these operations effectively could be thought of
+ // as "auto-commit" style actions.
+ // returns: dojo.store.api.Store.Transaction
+ // This represents the new current transaction.
+ },
+ getChildren: function(parent, options){
+ // summary:
+ // Retrieves the children of an object.
+ // parent: Object
+ // The object to find the children of.
+ // options: dojo.store.api.Store.QueryOptions?
+ // Additional options to apply to the retrieval of the children.
+ // returns: dojo.store.api.Store.QueryResults
+ // A result set of the children of the parent object.
+ },
+ getMetadata: function(object){
+ // summary:
+ // Returns any metadata about the object. This may include attribution,
+ // cache directives, history, or version information.
+ // object: Object
+ // The object to return metadata for.
+ // returns: Object
+ // An object containing metadata.
+ }
+});
+
+Store.PutDirectives = function(id, before, parent, overwrite){
+ // summary:
+ // Directives passed to put() and add() handlers for guiding the update and
+ // creation of stored objects.
+ // id: String|Number?
+ // Indicates the identity of the object if a new object is created
+ // before: Object?
+ // If the collection of objects in the store has a natural ordering,
+ // this indicates that the created or updated object should be placed before the
+ // object specified by the value of this property. A value of null indicates that the
+ // object should be last.
+ // parent: Object?,
+ // If the store is hierarchical (with single parenting) this property indicates the
+ // new parent of the created or updated object.
+ // overwrite: Boolean?
+ // If this is provided as a boolean it indicates that the object should or should not
+ // overwrite an existing object. A value of true indicates that a new object
+ // should not be created, the operation should update an existing object. A
+ // value of false indicates that an existing object should not be updated, a new
+ // object should be created (which is the same as an add() operation). When
+ // this property is not provided, either an update or creation is acceptable.
+ this.id = id;
+ this.before = before;
+ this.parent = parent;
+ this.overwrite = overwrite;
+};
+
+Store.SortInformation = function(attribute, descending){
+ // summary:
+ // An object describing what attribute to sort on, and the direction of the sort.
+ // attribute: String
+ // The name of the attribute to sort on.
+ // descending: Boolean
+ // The direction of the sort. Default is false.
+ this.attribute = attribute;
+ this.descending = descending;
+};
+
+Store.QueryOptions = function(sort, start, count){
+ // summary:
+ // Optional object with additional parameters for query results.
+ // sort: dojo.store.api.Store.SortInformation[]?
+ // A list of attributes to sort on, as well as direction
+ // For example:
+ // | [{attribute:"price, descending: true}].
+ // If the sort parameter is omitted, then the natural order of the store may be
+ // applied if there is a natural order.
+ // start: Number?
+ // The first result to begin iteration on
+ // count: Number?
+ // The number of how many results should be returned.
+ this.sort = sort;
+ this.start = start;
+ this.count = count;
+};
+
+declare("dojo.store.api.Store.QueryResults", null, {
+ // summary:
+ // This is an object returned from query() calls that provides access to the results
+ // of a query. Queries may be executed asynchronously.
+
+ forEach: function(callback, thisObject){
+ // summary:
+ // Iterates over the query results, based on
+ // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/forEach.
+ // Note that this may executed asynchronously. The callback may be called
+ // after this function returns.
+ // callback:
+ // Function that is called for each object in the query results
+ // thisObject:
+ // The object to use as |this| in the callback.
+
+ },
+ filter: function(callback, thisObject){
+ // summary:
+ // Filters the query results, based on
+ // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/filter.
+ // Note that this may executed asynchronously. The callback may be called
+ // after this function returns.
+ // callback:
+ // Function that is called for each object in the query results
+ // thisObject:
+ // The object to use as |this| in the callback.
+ // returns: dojo.store.api.Store.QueryResults
+ },
+ map: function(callback, thisObject){
+ // summary:
+ // Maps the query results, based on
+ // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/map.
+ // Note that this may executed asynchronously. The callback may be called
+ // after this function returns.
+ // callback:
+ // Function that is called for each object in the query results
+ // thisObject:
+ // The object to use as |this| in the callback.
+ // returns: dojo.store.api.Store.QueryResults
+ },
+ then: function(callback, errorHandler){
+ // summary:
+ // This registers a callback for when the query is complete, if the query is asynchronous.
+ // This is an optional method, and may not be present for synchronous queries.
+ // callback:
+ // This is called when the query is completed successfully, and is passed a single argument
+ // that is an array representing the query results.
+ // errorHandler:
+ // This is called if the query failed, and is passed a single argument that is the error
+ // for the failure.
+ },
+ observe: function(listener, includeAllUpdates){
+ // summary:
+ // This registers a callback for notification of when data is modified in the query results.
+ // This is an optional method, and is usually provided by dojo.store.Observable.
+ // listener: Function
+ // The listener function is called when objects in the query results are modified
+ // to affect the query result. The listener function is called with the following
+ // arguments:
+ // | listener(object, removedFrom, insertedInto);
+ // * The object parameter indicates the object that was create, modified, or deleted.
+ // * The removedFrom parameter indicates the index in the result array where
+ // the object used to be. If the value is -1, then the object is an addition to
+ // this result set (due to a new object being created, or changed such that it
+ // is a part of the result set).
+ // * The insertedInto parameter indicates the index in the result array where
+ // the object should be now. If the value is -1, then the object is a removal
+ // from this result set (due to an object being deleted, or changed such that it
+ // is not a part of the result set).
+ // includeAllUpdates:
+ // This indicates whether or not to include object updates that do not affect
+ // the inclusion or order of the object in the query results. By default this is false,
+ // which means that if any object is updated in such a way that it remains
+ // in the result set and it's position in result sets is not affected, then the listener
+ // will not be fired.
+
+ },
+ // total: Number|Promise?
+ // This property should be included in if the query options included the "count"
+ // property limiting the result set. This property indicates the total number of objects
+ // matching the query (as if "start" and "count" weren't present). This may be
+ // a promise if the query is asynchronous.
+ total: 0
+});
+
+declare("dojo.store.api.Store.Transaction", null, {
+ // summary:
+ // This is an object returned from transaction() calls that represents the current
+ // transaction.
+
+ commit: function(){
+ // summary:
+ // Commits the transaction. This may throw an error if it fails. Of if the operation
+ // is asynchronous, it may return a promise that represents the eventual success
+ // or failure of the commit.
+ },
+ abort: function(callback, thisObject){
+ // summary:
+ // Aborts the transaction. This may throw an error if it fails. Of if the operation
+ // is asynchronous, it may return a promise that represents the eventual success
+ // or failure of the abort.
+ }
+});
+return Store;
+});
diff --git a/tests/aspect.js b/tests/aspect.js
index fa739d9..be1bd6e 100644
--- a/tests/aspect.js
+++ b/tests/aspect.js
@@ -1,119 +1,119 @@
-dojo.provide("dojo.tests.aspect");
-
-var aspect = dojo.require("dojo.aspect");
-
-doh.register("tests.aspect",
- [
- function before(t){
- var order = [];
- var obj = {
- method: function(a){
- order.push(a);
- }
- };
- var signal = aspect.before(obj, "method", function(a){
- order.push(a);
- return [a+1];
- });
- obj.method(0);
- obj.method(2);
- var signal2 = aspect.before(obj, "method", function(a){
- order.push(a);
- return [a+1];
- });
- obj.method(4);
- signal.remove();
- obj.method(7);
- signal2.remove();
- obj.method(9);
- t.is(order, [0,1,2,3,4,5,6,7,8,9]);
- },
-
- function after(t){
- var order = [];
- var obj = {
- method: function(a){
- order.push(a);
- return a+1;
- }
- };
- var signal = aspect.after(obj, "method", function(a){
- order.push(0);
- return a+1;
- });
- obj.method(0);
- var signal2 = aspect.after(obj, "method", function(a){
- order.push(a);
- });
- obj.method(3);
- var signal3 = aspect.after(obj, "method", function(a){
- order.push(3);
- }, true);
- obj.method(3);
- signal2.remove();
- obj.method(6);
- signal3.remove();
- var signal4 = aspect.after(obj, "method", function(a){
- order.push(4);
- }, true);
- signal.remove();
- obj.method(7);
- t.is(order, [0, 0, 3, 0, 5, 3, 0, 5, 3, 6, 0, 3, 7, 4]);
- },
- function around(t){
- var order = [];
- var obj = {
- method: function(a){
- order.push(a);
- return a+1;
- }
- };
- var beforeSignal = aspect.before(obj, "method", function(a){
- order.push(a);
- });
- var signal = aspect.around(obj, "method", function(original){
- return function(a){
- a= a + 1;
- a = original(a);
- order.push(a);
- return a+1;
- }
- });
- order.push(obj.method(0));
- obj.method(4);
- t.is(order, [0,1,2,3,4,5,6]);
- },
- function delegation(t){
- var order = [];
- var proto = {
- foo: function(x){
- order.push(x);
- return x;
- },
- bar: function(){
- }
- };
- aspect.after(proto, "foo", function(x){
- order.push(x + 1);
- return x;
- });
- aspect.after(proto, "bar", function(x){
- t.t(this.isInstance);
- });
- proto.foo(0);
- function Class(){
- }
- Class.prototype = proto;
- var instance = new Class();
- instance.isInstance = true;
- aspect.after(instance, "foo", function(x){
- order.push(x + 2);
- return x;
- });
- instance.bar();
- instance.foo(2);
- proto.foo(5);
- t.is(order, [0,1,2,3,4,5,6]);
- }
- ]
-);
+dojo.provide("dojo.tests.aspect");
+
+var aspect = dojo.require("dojo.aspect");
+
+doh.register("tests.aspect",
+ [
+ function before(t){
+ var order = [];
+ var obj = {
+ method: function(a){
+ order.push(a);
+ }
+ };
+ var signal = aspect.before(obj, "method", function(a){
+ order.push(a);
+ return [a+1];
+ });
+ obj.method(0);
+ obj.method(2);
+ var signal2 = aspect.before(obj, "method", function(a){
+ order.push(a);
+ return [a+1];
+ });
+ obj.method(4);
+ signal.remove();
+ obj.method(7);
+ signal2.remove();
+ obj.method(9);
+ t.is(order, [0,1,2,3,4,5,6,7,8,9]);
+ },
+
+ function after(t){
+ var order = [];
+ var obj = {
+ method: function(a){
+ order.push(a);
+ return a+1;
+ }
+ };
+ var signal = aspect.after(obj, "method", function(a){
+ order.push(0);
+ return a+1;
+ });
+ obj.method(0);
+ var signal2 = aspect.after(obj, "method", function(a){
+ order.push(a);
+ });
+ obj.method(3);
+ var signal3 = aspect.after(obj, "method", function(a){
+ order.push(3);
+ }, true);
+ obj.method(3);
+ signal2.remove();
+ obj.method(6);
+ signal3.remove();
+ var signal4 = aspect.after(obj, "method", function(a){
+ order.push(4);
+ }, true);
+ signal.remove();
+ obj.method(7);
+ t.is(order, [0, 0, 3, 0, 5, 3, 0, 5, 3, 6, 0, 3, 7, 4]);
+ },
+ function around(t){
+ var order = [];
+ var obj = {
+ method: function(a){
+ order.push(a);
+ return a+1;
+ }
+ };
+ var beforeSignal = aspect.before(obj, "method", function(a){
+ order.push(a);
+ });
+ var signal = aspect.around(obj, "method", function(original){
+ return function(a){
+ a= a + 1;
+ a = original(a);
+ order.push(a);
+ return a+1;
+ }
+ });
+ order.push(obj.method(0));
+ obj.method(4);
+ t.is(order, [0,1,2,3,4,5,6]);
+ },
+ function delegation(t){
+ var order = [];
+ var proto = {
+ foo: function(x){
+ order.push(x);
+ return x;
+ },
+ bar: function(){
+ }
+ };
+ aspect.after(proto, "foo", function(x){
+ order.push(x + 1);
+ return x;
+ });
+ aspect.after(proto, "bar", function(x){
+ t.t(this.isInstance);
+ });
+ proto.foo(0);
+ function Class(){
+ }
+ Class.prototype = proto;
+ var instance = new Class();
+ instance.isInstance = true;
+ aspect.after(instance, "foo", function(x){
+ order.push(x + 2);
+ return x;
+ });
+ instance.bar();
+ instance.foo(2);
+ proto.foo(5);
+ t.is(order, [0,1,2,3,4,5,6]);
+ }
+ ]
+);
diff --git a/tests/json.js b/tests/json.js
index 1d15cb1..ee777a7 100644
--- a/tests/json.js
+++ b/tests/json.js
@@ -1,166 +1,166 @@
-define(["../main", "doh", "../json"], function(dojo, doh, JSON){
-
- var mustThrow = function(json){
- try{
- JSON.parse(json, true);
- }catch(e){
- return;
- }
- throw new Error("Invalid JSON " + json + " should have been rejected");
- };
-
- doh.register("tests.json", [
- // all tests below are taken from #4.2 of the CSS3 Color Module
- function simpleString(t){ t.is("bar", JSON.parse('{"foo":"bar"}').foo)},
- function simpleTrue(t){ t.is(true, JSON.parse('{"foo":true}').foo)},
- function simpleFalse(t){ t.is(false, JSON.parse('{"foo":false}').foo)},
- function simpleNull(t){ t.is(null, JSON.parse('{"foo":null}').foo)},
- function simpleNumber(t){ t.is(3.3, JSON.parse('{"foo":3.3}', true).foo)},
- function strictString(t){ t.is("bar", JSON.parse('{"foo":"bar"}', true).foo)},
- function strictStringEsc(t){ t.is("b\n\t\"ar()", JSON.parse('{"foo":"b\\n\\t\\"ar()"}', true).foo)},
- function strictTrue(t){ t.is(true, JSON.parse('{"foo":true}', true).foo)},
- function strictFalse(t){ t.is(false, JSON.parse('{"foo":false}', true).foo)},
- function strictNull(t){ t.is(null, JSON.parse('{"foo":null}', true).foo)},
- function strictNumber(t){ t.is(3.3, JSON.parse('{"foo":3.3}', true).foo)},
- function strictNumberNeg(t){ t.is(-3.3, JSON.parse('{"foo":-3.3}', true).foo)},
- function exponentNegative(t){ t.is(3.3e-33, JSON.parse('{"foo":3.3e-33}', true).foo)},
- function exponent(t){ t.is(3.3e33, JSON.parse('{"foo":3.3e33}', true).foo)},
- function array(t){ t.is(3, JSON.parse('{"foo":[3,true,[]]}', true).foo[0])},
- function badCall(t){ mustThrow('{"foo":alert()}')},
- function badMath(t){ mustThrow('{"foo":3+4}')},
- function badIndex(t){ mustThrow('{"foo":"bar"}[3]')},
- function badKey(t){ mustThrow('{foo:"bar"}')},
- //function badKey2(t){ mustThrow('{2:"bar"}')},
- function badUnbalanced(t){ mustThrow('[')},
- function badUnbalanced2(t){ mustThrow('}')},
- function badType(t){ mustThrow('["foo":"bar"]')},
- function badUnbalanced2(t){ mustThrow('}')},
- function serializeString(t){ t.is('{"foo":"bar"}', JSON.stringify({"foo":"bar"}))},
- function serializeNull(t){ t.is('{"foo":null}', JSON.stringify({"foo":null}))},
- function serializeFunction(t){ t.is('{}', JSON.stringify({"foo":function(){}}))},
- function serializeNaN(t){ t.is('{"foo":null}', JSON.stringify({"foo":NaN}))},
- function serializeInfinity(t){ t.is('{"foo":null}', JSON.stringify({"foo":Infinity}))},
- // there is differences in how many decimals of accuracies in seconds in how Dates are serialized between browsers
- function serializeDate(t){ t.t(/1970-01-01T00:00:00.*Z/.test(JSON.parse(JSON.stringify({"foo":new Date(1)})).foo));},
- function serializeCircular(t){
- try{
- var a = {};
- a.a = a;
- console.log("circular: " + JSON.stringify(a));
- }catch(e){
- return;
- }
- throw new Error("stringify must throw for circular references");
-
- },
- /*Apparently Firefox doesn't pass the key to the toJSON method*/
- function serializeToJSON(t){ t.is('{"foo":{"name":"value"}}', JSON.stringify({foo:{toJSON:function(key){return {name:"value"};}}}))}
- ]);
-
-var smallDataSet = {
- prop1: null,
- prop2: true,
- prop3: [],
- prop4: 3.4325222223332266,
- prop5: 10003,
- prop6: "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean semper",
- prop7: "sagittis velit. Cras in mi. Duis porta mauris ut ligula. Proin porta rutrum",
- prop8: "lacus. Etiam consequat scelerisque quam. Nulla facilisi. Maecenas luctus",
- prop9: "venenatis nulla. In sit amet dui non mi semper iaculis. Sed molestie",
- prop10: "tortor at ipsum. Morbi dictum rutrum magna. Sed vitae risus." +
- "Aliquam vitae enim. Duis scelerisque metus auctor est venenatis imperdiet." +
- "Fusce dignissim porta augue. Nulla vestibulum. Integer lorem nunc," +
- "ullamcorper a, commodo ac, malesuada sed, dolor. Aenean id mi in massa" +
- "bibendum suscipit. Integer eros. Nullam suscipit mauris. In pellentesque." +
- "Mauris ipsum est, pharetra semper, pharetra in, viverra quis, tellus. Etiam" +
- "purus. Quisque egestas, tortor ac cursus lacinia, felis leo adipiscing" +
- "nisi, et rhoncus elit dolor eget eros. Fusce ut quam. Suspendisse eleifend" +
- "leo vitae ligula. Nulla facilisi."
-};
-var smallJson = JSON.stringify(smallDataSet);
-
-var i, mediumDataSet = [];
-for(i = 0; i < 20; i++){
- mediumDataSet.push({
- prop1: null,
- prop2: true,
- prop3: false,
- prop4: 3.4325222223332266 - i,
- prop5: 10003 + i,
- prop6: "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean semper",
- prop7: "sagittis velit. Cras in mi. Duis porta mauris ut ligula. Proin porta rutrum",
- prop8: "lacus. Etiam consequat scelerisque quam. Nulla facilisi. Maecenas luctus",
- prop9: "venenatis nulla. In sit amet dui non mi semper iaculis. Sed molestie",
- prop10: "tortor at ipsum. Morbi dictum rutrum magna. Sed vitae risus." +
- "Aliquam vitae enim."
- });
-}
-var mediumJson = JSON.stringify(mediumDataSet);
-
-var largeDataSet = [];
-for(i = 0; i < 100; i++){
- largeDataSet.push({
- prop1: null,
- prop2: true,
- prop3: false,
- prop4: 3.4325222223332266 - i,
- prop5: ["Mauris ipsum est, pharetra semper, pharetra in, viverra quis, tellus. Etiam" +
- "purus. Quisque egestas, tortor ac cursus lacinia, felis leo adipiscing",
- "nisi, et rhoncus elit dolor eget eros. Fusce ut quam. Suspendisse eleifend" +
- "leo vitae ligula. Nulla facilisi."
- ],
- prop6: "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean semper",
- prop7: "sagittis velit. Cras in mi. Duis porta mauris ut ligula. Proin porta rutrum",
- prop8: "lacus. Etiam consequat scelerisque quam. Nulla facilisi. Maecenas luctus",
- prop9: "venenatis nulla. In sit amet dui non mi semper iaculis. Sed molestie",
- prop10: "tortor at ipsum. Morbi dictum rutrum magna. Sed vitae risus." +
- "Aliquam vitae enim. Duis scelerisque metus auctor est venenatis imperdiet." +
- "Fusce dignissim porta augue. Nulla vestibulum. Integer lorem nunc," +
- "ullamcorper a, commodo ac, malesuada sed, dolor. Aenean id mi in massa" +
- "bibendum suscipit. Integer eros. Nullam suscipit mauris. In pellentesque."
- });
-}
-var largeJson = JSON.stringify(largeDataSet);
-
-doh.register("tests.json.performance", [
- // all tests below are taken from #4.2 of the CSS3 Color Module
- function small(){
- var i = 10000;
- while(i-->0){
- var result = JSON.parse(smallJson);
- }
- },
- function strictSmall(){
- var i = 10000;
- while(i-->0){
- var result = JSON.parse(smallJson, true);
- }
- },
- function medium(){
- var i = 1000;
- while(i-->0){
- var result = JSON.parse(mediumJson);
- }
- },
- function strictMedium(){
- var i = 1000;
- while(i-->0){
- var result = JSON.parse(mediumJson, true);
- }
- },
- function large(){
- var i = 100;
- while(i-->0){
- var result = JSON.parse(largeJson);
- }
- },
- function strictLarge(){
- var i = 100;
- while(i-->0){
- var result = JSON.parse(largeJson, true);
- }
- }
- ]);
-
-});
-
+define(["../main", "doh", "../json"], function(dojo, doh, JSON){
+
+ var mustThrow = function(json){
+ try{
+ JSON.parse(json, true);
+ }catch(e){
+ return;
+ }
+ throw new Error("Invalid JSON " + json + " should have been rejected");
+ };
+
+ doh.register("tests.json", [
+ // all tests below are taken from #4.2 of the CSS3 Color Module
+ function simpleString(t){ t.is("bar", JSON.parse('{"foo":"bar"}').foo)},
+ function simpleTrue(t){ t.is(true, JSON.parse('{"foo":true}').foo)},
+ function simpleFalse(t){ t.is(false, JSON.parse('{"foo":false}').foo)},
+ function simpleNull(t){ t.is(null, JSON.parse('{"foo":null}').foo)},
+ function simpleNumber(t){ t.is(3.3, JSON.parse('{"foo":3.3}', true).foo)},
+ function strictString(t){ t.is("bar", JSON.parse('{"foo":"bar"}', true).foo)},
+ function strictStringEsc(t){ t.is("b\n\t\"ar()", JSON.parse('{"foo":"b\\n\\t\\"ar()"}', true).foo)},
+ function strictTrue(t){ t.is(true, JSON.parse('{"foo":true}', true).foo)},
+ function strictFalse(t){ t.is(false, JSON.parse('{"foo":false}', true).foo)},
+ function strictNull(t){ t.is(null, JSON.parse('{"foo":null}', true).foo)},
+ function strictNumber(t){ t.is(3.3, JSON.parse('{"foo":3.3}', true).foo)},
+ function strictNumberNeg(t){ t.is(-3.3, JSON.parse('{"foo":-3.3}', true).foo)},
+ function exponentNegative(t){ t.is(3.3e-33, JSON.parse('{"foo":3.3e-33}', true).foo)},
+ function exponent(t){ t.is(3.3e33, JSON.parse('{"foo":3.3e33}', true).foo)},
+ function array(t){ t.is(3, JSON.parse('{"foo":[3,true,[]]}', true).foo[0])},
+ function badCall(t){ mustThrow('{"foo":alert()}')},
+ function badMath(t){ mustThrow('{"foo":3+4}')},
+ function badIndex(t){ mustThrow('{"foo":"bar"}[3]')},
+ function badKey(t){ mustThrow('{foo:"bar"}')},
+ //function badKey2(t){ mustThrow('{2:"bar"}')},
+ function badUnbalanced(t){ mustThrow('[')},
+ function badUnbalanced2(t){ mustThrow('}')},
+ function badType(t){ mustThrow('["foo":"bar"]')},
+ function badUnbalanced2(t){ mustThrow('}')},
+ function serializeString(t){ t.is('{"foo":"bar"}', JSON.stringify({"foo":"bar"}))},
+ function serializeNull(t){ t.is('{"foo":null}', JSON.stringify({"foo":null}))},
+ function serializeFunction(t){ t.is('{}', JSON.stringify({"foo":function(){}}))},
+ function serializeNaN(t){ t.is('{"foo":null}', JSON.stringify({"foo":NaN}))},
+ function serializeInfinity(t){ t.is('{"foo":null}', JSON.stringify({"foo":Infinity}))},
+ // there is differences in how many decimals of accuracies in seconds in how Dates are serialized between browsers
+ function serializeDate(t){ t.t(/1970-01-01T00:00:00.*Z/.test(JSON.parse(JSON.stringify({"foo":new Date(1)})).foo));},
+ function serializeCircular(t){
+ try{
+ var a = {};
+ a.a = a;
+ console.log("circular: " + JSON.stringify(a));
+ }catch(e){
+ return;
+ }
+ throw new Error("stringify must throw for circular references");
+
+ },
+ /*Apparently Firefox doesn't pass the key to the toJSON method*/
+ function serializeToJSON(t){ t.is('{"foo":{"name":"value"}}', JSON.stringify({foo:{toJSON:function(key){return {name:"value"};}}}))}
+ ]);
+
+var smallDataSet = {
+ prop1: null,
+ prop2: true,
+ prop3: [],
+ prop4: 3.4325222223332266,
+ prop5: 10003,
+ prop6: "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean semper",
+ prop7: "sagittis velit. Cras in mi. Duis porta mauris ut ligula. Proin porta rutrum",
+ prop8: "lacus. Etiam consequat scelerisque quam. Nulla facilisi. Maecenas luctus",
+ prop9: "venenatis nulla. In sit amet dui non mi semper iaculis. Sed molestie",
+ prop10: "tortor at ipsum. Morbi dictum rutrum magna. Sed vitae risus." +
+ "Aliquam vitae enim. Duis scelerisque metus auctor est venenatis imperdiet." +
+ "Fusce dignissim porta augue. Nulla vestibulum. Integer lorem nunc," +
+ "ullamcorper a, commodo ac, malesuada sed, dolor. Aenean id mi in massa" +
+ "bibendum suscipit. Integer eros. Nullam suscipit mauris. In pellentesque." +
+ "Mauris ipsum est, pharetra semper, pharetra in, viverra quis, tellus. Etiam" +
+ "purus. Quisque egestas, tortor ac cursus lacinia, felis leo adipiscing" +
+ "nisi, et rhoncus elit dolor eget eros. Fusce ut quam. Suspendisse eleifend" +
+ "leo vitae ligula. Nulla facilisi."
+};
+var smallJson = JSON.stringify(smallDataSet);
+
+var i, mediumDataSet = [];
+for(i = 0; i < 20; i++){
+ mediumDataSet.push({
+ prop1: null,
+ prop2: true,
+ prop3: false,
+ prop4: 3.4325222223332266 - i,
+ prop5: 10003 + i,
+ prop6: "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean semper",
+ prop7: "sagittis velit. Cras in mi. Duis porta mauris ut ligula. Proin porta rutrum",
+ prop8: "lacus. Etiam consequat scelerisque quam. Nulla facilisi. Maecenas luctus",
+ prop9: "venenatis nulla. In sit amet dui non mi semper iaculis. Sed molestie",
+ prop10: "tortor at ipsum. Morbi dictum rutrum magna. Sed vitae risus." +
+ "Aliquam vitae enim."
+ });
+}
+var mediumJson = JSON.stringify(mediumDataSet);
+
+var largeDataSet = [];
+for(i = 0; i < 100; i++){
+ largeDataSet.push({
+ prop1: null,
+ prop2: true,
+ prop3: false,
+ prop4: 3.4325222223332266 - i,
+ prop5: ["Mauris ipsum est, pharetra semper, pharetra in, viverra quis, tellus. Etiam" +
+ "purus. Quisque egestas, tortor ac cursus lacinia, felis leo adipiscing",
+ "nisi, et rhoncus elit dolor eget eros. Fusce ut quam. Suspendisse eleifend" +
+ "leo vitae ligula. Nulla facilisi."
+ ],
+ prop6: "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean semper",
+ prop7: "sagittis velit. Cras in mi. Duis porta mauris ut ligula. Proin porta rutrum",
+ prop8: "lacus. Etiam consequat scelerisque quam. Nulla facilisi. Maecenas luctus",
+ prop9: "venenatis nulla. In sit amet dui non mi semper iaculis. Sed molestie",
+ prop10: "tortor at ipsum. Morbi dictum rutrum magna. Sed vitae risus." +
+ "Aliquam vitae enim. Duis scelerisque metus auctor est venenatis imperdiet." +
+ "Fusce dignissim porta augue. Nulla vestibulum. Integer lorem nunc," +
+ "ullamcorper a, commodo ac, malesuada sed, dolor. Aenean id mi in massa" +
+ "bibendum suscipit. Integer eros. Nullam suscipit mauris. In pellentesque."
+ });
+}
+var largeJson = JSON.stringify(largeDataSet);
+
+doh.register("tests.json.performance", [
+ // all tests below are taken from #4.2 of the CSS3 Color Module
+ function small(){
+ var i = 10000;
+ while(i-->0){
+ var result = JSON.parse(smallJson);
+ }
+ },
+ function strictSmall(){
+ var i = 10000;
+ while(i-->0){
+ var result = JSON.parse(smallJson, true);
+ }
+ },
+ function medium(){
+ var i = 1000;
+ while(i-->0){
+ var result = JSON.parse(mediumJson);
+ }
+ },
+ function strictMedium(){
+ var i = 1000;
+ while(i-->0){
+ var result = JSON.parse(mediumJson, true);
+ }
+ },
+ function large(){
+ var i = 100;
+ while(i-->0){
+ var result = JSON.parse(largeJson);
+ }
+ },
+ function strictLarge(){
+ var i = 100;
+ while(i-->0){
+ var result = JSON.parse(largeJson, true);
+ }
+ }
+ ]);
+
+});
+
diff --git a/tests/on.js b/tests/on.js
index 7091889..73adabb 100644
--- a/tests/on.js
+++ b/tests/on.js
@@ -1,229 +1,229 @@
-dojo.provide("dojo.tests.on");
-
-var on = dojo.require("dojo.on");
-var has = dojo.require("dojo.has");
-var topic = dojo.require("dojo.topic");
-var Evented = dojo.require("dojo.Evented");
-doh.register("tests.on",
- [
- function object(t){
- var order = [];
- var obj = new dojo.Evented();
- obj.oncustom = function(event){
- order.push(event.a);
- return event.a+1;
- };
- var signal = on.pausable(obj, "custom", function(event){
- order.push(0);
- return event.a+1;
- });
- obj.oncustom({a:0});
- var signal2 = on(obj, "custom, foo", function(event){
- order.push(event.a);
- });
- on.emit(obj, "custom", {
- a: 3
- });
- signal.pause();
- var signal3 = on(obj, "custom", function(a){
- order.push(3);
- }, true);
- on.emit(obj, "custom", {
- a: 3
- });
- signal2.remove();
- signal.resume();
- on.emit(obj, "custom", {
- a: 6
- });
- signal3.remove();
- var signal4 = on(obj, "foo, custom", function(a){
- order.push(4);
- }, true);
- signal.remove();
- on.emit(obj, "custom", {
- a: 7
- });
- t.is(order, [0,0,3,0,3,3,3,3,6,0,3,7,4]);
- },
- function once(t){
- var order = [];
- var obj = new dojo.Evented();
- obj.on("custom", function(event){
- order.push(event.a);
- });
- var signal = on.once(obj, "custom", function(event){
- order.push(1);
- });
- obj.emit("custom",{a:0});
- obj.oncustom({a:2}); // should call original method, but not listener
- t.is(order, [0,1,2]);
- },
- function dom(t){
- var div = document.body.appendChild(document.createElement("div"));
- var span = div.appendChild(document.createElement("span"));
- var order = [];
- var signal = on(div,"custom", function(event){
- order.push(event.a);
- event.addedProp += "ue";
- });
- on(span,"custom", function(event){
- event.addedProp = "val";
- });
- on.emit(div, "custom", {
- target: div,
- currentTarget:div,
- relatedTarget: div,
- a: 0
- });
- on.emit(div, "otherevent", {
- a: 0
- });
- t.is(on.emit(span, "custom", {
- a: 1,
- bubbles: true,
- cancelable: true
- }).addedProp, "value");
- t.t(on.emit(span, "custom", {
- a: 1,
- bubbles: false,
- cancelable: true
- }));
- var signal2 = on.pausable(div,"custom", function(event){
- order.push(event.a + 1);
- event.preventDefault();
- });
- t.f(on.emit(span, "custom", {
- a: 2,
- bubbles: true,
- cancelable: true
- }));
- signal2.pause();
- t.is(on.emit(span, "custom", {
- a: 4,
- bubbles: true,
- cancelable: true
- }).type, "custom");
- signal2.resume();
- signal.remove();
- t.f(on.emit(span, "custom", {
- a: 4,
- bubbles: true,
- cancelable: true
- }));
- on(span, "custom", function(event){
- order.push(6);
- event.stopPropagation();
- });
- t.t(on.emit(span, "custom", {
- a: 1,
- bubbles: true,
- cancelable: true
- }));
- var button = div.appendChild(document.createElement("button"));
- // make sure we are propagating natively created events too
- signal = on(div, "click", function(){
- order.push(7);
- });
- button.click();
- signal.remove();
- // test out event delegation
- if(dojo.query){
- // if dojo.query is loaded, test event delegation
- on(div, "button:click", function(){
- order.push(8);
- });
- button.click();
- }else{//just pass then
- order.push(8);
- }
- t.is(order, [0, 1, 2, 3, 4, 5, 6, 7, 8]);
- },
-/*
- This only works if the test page has the focus, so you can enable if you want to test focus functionality and allow the test page to have focus
- function focus(t){
- var div = document.body.appendChild(document.createElement("div"));
- var input = div.appendChild(document.createElement("input"));
- var order = [];
- var signal = on(div,"input:focusin", function(event){
- order.push('in');
- });
- var signal = on(div,"input:focusout", function(event){
- order.push('out');
- });
- var otherInput = document.body.appendChild(document.createElement("input"));
- input.focus();
- otherInput.focus();
- d = new doh.Deferred();
- setTimeout(function(){
- t.is(['in', 'out'], order);
- d.callback(true);
- }, 1);
- return d;
- },*/
- function extensionEvent(t){
- var div = document.body.appendChild(document.createElement("div"));
- var span = div.appendChild(document.createElement("span"));
- span.setAttribute("foo", 2);
- var order = [];
- var customEvent = function(target, listener){
- return on(target, "custom", listener);
- };
- var signal = on(div, customEvent, function(event){
- order.push(event.a);
- });
- var signal = on(div, on.selector("span", customEvent), function(event){
- order.push(+this.getAttribute("foo"));
- });
- on.emit(div, "custom", {
- a: 0
- });
- // should trigger selector
- t.t(on.emit(span, "custom", {
- a: 1,
- bubbles: true,
- cancelable: true
- }));
- // shouldn't trigger selector
- t.t(on.emit(div, "custom", {
- a: 3,
- bubbles: true,
- cancelable: true
- }));
- t.is(order, [0, 1, 2, 3]);
- },
- function testEvented(t){
- var MyClass = dojo.declare([Evented],{
-
- });
- var order = [];
- myObject = new MyClass;
- myObject.on("custom", function(event){
- order.push(event.a);
- });
- myObject.emit("custom", {a:0});
- t.is(order, [0]);
- },
- function pubsub(t){
- var fooCount = 0;
- topic.subscribe("/test/foo", function(event, secondArg){
- t.is("value", event.foo);
- t.is("second", secondArg);
- fooCount++;
- });
- topic.publish("/test/foo", {foo: "value"}, "second");
- t.is(1, fooCount);
- },
- function touch(t){
- console.log("has", has);
- if(has("touch")){
- var div = document.body.appendChild(document.createElement("div"));
- on(div, "touchstart", function(event){
- t.t("rotation" in event);
- t.t("pageX" in event);
- });
- on.emit(div, "touchstart", {changedTouches: [{pageX:100}]});
- }
- }
- ]
-);
+dojo.provide("dojo.tests.on");
+
+var on = dojo.require("dojo.on");
+var has = dojo.require("dojo.has");
+var topic = dojo.require("dojo.topic");
+var Evented = dojo.require("dojo.Evented");
+doh.register("tests.on",
+ [
+ function object(t){
+ var order = [];
+ var obj = new dojo.Evented();
+ obj.oncustom = function(event){
+ order.push(event.a);
+ return event.a+1;
+ };
+ var signal = on.pausable(obj, "custom", function(event){
+ order.push(0);
+ return event.a+1;
+ });
+ obj.oncustom({a:0});
+ var signal2 = on(obj, "custom, foo", function(event){
+ order.push(event.a);
+ });
+ on.emit(obj, "custom", {
+ a: 3
+ });
+ signal.pause();
+ var signal3 = on(obj, "custom", function(a){
+ order.push(3);
+ }, true);
+ on.emit(obj, "custom", {
+ a: 3
+ });
+ signal2.remove();
+ signal.resume();
+ on.emit(obj, "custom", {
+ a: 6
+ });
+ signal3.remove();
+ var signal4 = on(obj, "foo, custom", function(a){
+ order.push(4);
+ }, true);
+ signal.remove();
+ on.emit(obj, "custom", {
+ a: 7
+ });
+ t.is(order, [0,0,3,0,3,3,3,3,6,0,3,7,4]);
+ },
+ function once(t){
+ var order = [];
+ var obj = new dojo.Evented();
+ obj.on("custom", function(event){
+ order.push(event.a);
+ });
+ var signal = on.once(obj, "custom", function(event){
+ order.push(1);
+ });
+ obj.emit("custom",{a:0});
+ obj.oncustom({a:2}); // should call original method, but not listener
+ t.is(order, [0,1,2]);
+ },
+ function dom(t){
+ var div = document.body.appendChild(document.createElement("div"));
+ var span = div.appendChild(document.createElement("span"));
+ var order = [];
+ var signal = on(div,"custom", function(event){
+ order.push(event.a);
+ event.addedProp += "ue";
+ });
+ on(span,"custom", function(event){
+ event.addedProp = "val";
+ });
+ on.emit(div, "custom", {
+ target: div,
+ currentTarget:div,
+ relatedTarget: div,
+ a: 0
+ });
+ on.emit(div, "otherevent", {
+ a: 0
+ });
+ t.is(on.emit(span, "custom", {
+ a: 1,
+ bubbles: true,
+ cancelable: true
+ }).addedProp, "value");
+ t.t(on.emit(span, "custom", {
+ a: 1,
+ bubbles: false,
+ cancelable: true
+ }));
+ var signal2 = on.pausable(div,"custom", function(event){
+ order.push(event.a + 1);
+ event.preventDefault();
+ });
+ t.f(on.emit(span, "custom", {
+ a: 2,
+ bubbles: true,
+ cancelable: true
+ }));
+ signal2.pause();
+ t.is(on.emit(span, "custom", {
+ a: 4,
+ bubbles: true,
+ cancelable: true
+ }).type, "custom");
+ signal2.resume();
+ signal.remove();
+ t.f(on.emit(span, "custom", {
+ a: 4,
+ bubbles: true,
+ cancelable: true
+ }));
+ on(span, "custom", function(event){
+ order.push(6);
+ event.stopPropagation();
+ });
+ t.t(on.emit(span, "custom", {
+ a: 1,
+ bubbles: true,
+ cancelable: true
+ }));
+ var button = div.appendChild(document.createElement("button"));
+ // make sure we are propagating natively created events too
+ signal = on(div, "click", function(){
+ order.push(7);
+ });
+ button.click();
+ signal.remove();
+ // test out event delegation
+ if(dojo.query){
+ // if dojo.query is loaded, test event delegation
+ on(div, "button:click", function(){
+ order.push(8);
+ });
+ button.click();
+ }else{//just pass then
+ order.push(8);
+ }
+ t.is(order, [0, 1, 2, 3, 4, 5, 6, 7, 8]);
+ },
+/*
+ This only works if the test page has the focus, so you can enable if you want to test focus functionality and allow the test page to have focus
+ function focus(t){
+ var div = document.body.appendChild(document.createElement("div"));
+ var input = div.appendChild(document.createElement("input"));
+ var order = [];
+ var signal = on(div,"input:focusin", function(event){
+ order.push('in');
+ });
+ var signal = on(div,"input:focusout", function(event){
+ order.push('out');
+ });
+ var otherInput = document.body.appendChild(document.createElement("input"));
+ input.focus();
+ otherInput.focus();
+ d = new doh.Deferred();
+ setTimeout(function(){
+ t.is(['in', 'out'], order);
+ d.callback(true);
+ }, 1);
+ return d;
+ },*/
+ function extensionEvent(t){
+ var div = document.body.appendChild(document.createElement("div"));
+ var span = div.appendChild(document.createElement("span"));
+ span.setAttribute("foo", 2);
+ var order = [];
+ var customEvent = function(target, listener){
+ return on(target, "custom", listener);
+ };
+ var signal = on(div, customEvent, function(event){
+ order.push(event.a);
+ });
+ var signal = on(div, on.selector("span", customEvent), function(event){
+ order.push(+this.getAttribute("foo"));
+ });
+ on.emit(div, "custom", {
+ a: 0
+ });
+ // should trigger selector
+ t.t(on.emit(span, "custom", {
+ a: 1,
+ bubbles: true,
+ cancelable: true
+ }));
+ // shouldn't trigger selector
+ t.t(on.emit(div, "custom", {
+ a: 3,
+ bubbles: true,
+ cancelable: true
+ }));
+ t.is(order, [0, 1, 2, 3]);
+ },
+ function testEvented(t){
+ var MyClass = dojo.declare([Evented],{
+
+ });
+ var order = [];
+ myObject = new MyClass;
+ myObject.on("custom", function(event){
+ order.push(event.a);
+ });
+ myObject.emit("custom", {a:0});
+ t.is(order, [0]);
+ },
+ function pubsub(t){
+ var fooCount = 0;
+ topic.subscribe("/test/foo", function(event, secondArg){
+ t.is("value", event.foo);
+ t.is("second", secondArg);
+ fooCount++;
+ });
+ topic.publish("/test/foo", {foo: "value"}, "second");
+ t.is(1, fooCount);
+ },
+ function touch(t){
+ console.log("has", has);
+ if(has("touch")){
+ var div = document.body.appendChild(document.createElement("div"));
+ on(div, "touchstart", function(event){
+ t.t("rotation" in event);
+ t.t("pageX" in event);
+ });
+ on.emit(div, "touchstart", {changedTouches: [{pageX:100}]});
+ }
+ }
+ ]
+);
diff --git a/topic.js b/topic.js
index 96dcc90..b29cfa3 100644
--- a/topic.js
+++ b/topic.js
@@ -1,33 +1,33 @@
-define(["./Evented"], function(Evented){
- // summary:
- // The export of this module is a pubsub hub
- // You can also use listen function itself as a pub/sub hub:
- // | topic.subscribe("some/topic", function(event){
- // | ... do something with event
- // | });
- // | topic.publish("some/topic", {name:"some event", ...});
-
- var hub = new Evented;
- return {
- publish: function(topic, event){
- // summary:
- // Publishes a message to a topic on the pub/sub hub. All arguments after
- // the first will be passed to the subscribers, so any number of arguments
- // can be provided (not just event).
- // topic: String
- // The name of the topic to publish to
- // event: Object
- // An event to distribute to the topic listeners
- return hub.emit.apply(hub, arguments);
- },
- subscribe: function(topic, listener){
- // summary:
- // Subcribes to a topic on the pub/sub hub
- // topic: String
- // The topic to subscribe to
- // listener: Function
- // A function to call when a message is published to the given topic
- return hub.on.apply(hub, arguments);
- }
- }
-});
+define(["./Evented"], function(Evented){
+ // summary:
+ // The export of this module is a pubsub hub
+ // You can also use listen function itself as a pub/sub hub:
+ // | topic.subscribe("some/topic", function(event){
+ // | ... do something with event
+ // | });
+ // | topic.publish("some/topic", {name:"some event", ...});
+
+ var hub = new Evented;
+ return {
+ publish: function(topic, event){
+ // summary:
+ // Publishes a message to a topic on the pub/sub hub. All arguments after
+ // the first will be passed to the subscribers, so any number of arguments
+ // can be provided (not just event).
+ // topic: String
+ // The name of the topic to publish to
+ // event: Object
+ // An event to distribute to the topic listeners
+ return hub.emit.apply(hub, arguments);
+ },
+ subscribe: function(topic, listener){
+ // summary:
+ // Subcribes to a topic on the pub/sub hub
+ // topic: String
+ // The topic to subscribe to
+ // listener: Function
+ // A function to call when a message is published to the given topic
+ return hub.on.apply(hub, arguments);
+ }
+ }
+});
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/dojo.git
More information about the Pkg-javascript-commits
mailing list