1 /*
  2  * ! Sizzle CSS Selector Engine - v1.0 Copyright 2009, The Dojo Foundation
  3  * Released under the MIT, BSD, and GPL Licenses. More information:
  4  * http://sizzlejs.com/
  5  */
  6 (function() {
  7     
  8     var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,
  9         done = 0,
 10         toString = Object.prototype.toString,
 11         hasDuplicate = false;
 12     
 13     var Sizzle = function(selector, context, results, seed) {
 14         results = results || [];
 15         var origContext = context = context || document;
 16         
 17         if (context.nodeType !== 1 && context.nodeType !== 9) {
 18             return [];
 19         }
 20         
 21         if (!selector || typeof selector !== "string") {
 22             return results;
 23         }
 24         
 25         var parts = [], sortOrder, m, set, checkSet, extra,
 26             prune = true,
 27             contextXML = isXML(context);
 28         
 29         // Reset the position of the chunker regexp (start from head)
 30         chunker.lastIndex = 0;
 31         
 32         while ((m = chunker.exec(selector)) !== null) {
 33             parts.push(m[1]);
 34             
 35             if (m[2]) {
 36                 extra = RegExp.rightContext;
 37                 break;
 38             }
 39         }
 40         
 41         if (parts.length > 1 && origPOS.exec(selector)) {
 42             if (parts.length === 2 && Expr.relative[parts[0]]) {
 43                 set = posProcess(parts[0] + parts[1], context);
 44             } else {
 45                 set = Expr.relative[parts[0]]
 46                     ? [context]
 47                     : Sizzle(parts.shift(), context);
 48                 
 49                 while (parts.length) {
 50                     selector = parts.shift();
 51                     
 52                     if (Expr.relative[selector]) selector += parts.shift();
 53                     
 54                     set = posProcess(selector, set);
 55                 }
 56             }
 57         } else {
 58             // Take a shortcut and set the context if the root selector is an ID
 59             // (but not if it'll be faster if the inner selector is an ID)
 60             if (!seed && parts.length > 1 && context.nodeType === 9 &&
 61                 !contextXML && Expr.match.ID.test(parts[0]) &&
 62                 !Expr.match.ID.test(parts[parts.length - 1])) {
 63                 var ret = Sizzle.find(parts.shift(), context, contextXML);
 64                 context = ret.expr
 65                     ? Sizzle.filter(ret.expr, ret.set)[0]
 66                     : ret.set[0];
 67             }
 68             
 69             if (context) {
 70                 var ret = seed
 71                     ? {
 72                         expr : parts.pop(),
 73                         set  : makeArray(seed)
 74                     }
 75                     : Sizzle.find(parts.pop(), parts.length === 1 &&
 76                         (parts[0] === "~" || parts[0] === "+") &&
 77                         context.parentNode
 78                         ? context.parentNode
 79                         : context, contextXML);
 80                 set = ret.expr
 81                     ? Sizzle.filter(ret.expr, ret.set)
 82                     : ret.set;
 83                 
 84                 if (parts.length > 0) {
 85                     checkSet = makeArray(set);
 86                 } else {
 87                     prune = false;
 88                 }
 89                 
 90                 while (parts.length) {
 91                     var cur = parts.pop(),
 92                         pop = cur;
 93                     
 94                     if (!Expr.relative[cur]) {
 95                         cur = "";
 96                     } else {
 97                         pop = parts.pop();
 98                     }
 99                     
100                     if (pop == null) {
101                         pop = context;
102                     }
103                     
104                     Expr.relative[cur](checkSet, pop, contextXML);
105                 }
106             } else {
107                 checkSet = parts = [];
108             }
109         }
110         
111         if (!checkSet) {
112             checkSet = set;
113         }
114         
115         if (!checkSet) {
116             throw "Syntax error, unrecognized expression: " + (cur || selector);
117         }
118         
119         if (toString.call(checkSet) === "[object Array]") {
120             if (!prune) {
121                 results.push.apply(results, checkSet);
122             } else if (context && context.nodeType === 1) {
123                 for (var i = 0; checkSet[i] != null; i++) {
124                     if (checkSet[i] &&
125                         (checkSet[i] === true || checkSet[i].nodeType === 1 &&
126                             contains(context, checkSet[i]))) {
127                         results.push(set[i]);
128                     }
129                 }
130             } else {
131                 for (var i = 0; checkSet[i] != null; i++) {
132                     if (checkSet[i] && checkSet[i].nodeType === 1) {
133                         results.push(set[i]);
134                     }
135                 }
136             }
137         } else {
138             makeArray(checkSet, results);
139         }
140         
141         if (extra) {
142             Sizzle(extra, origContext, results, seed);
143             Sizzle.uniqueSort(results);
144         }
145         
146         return results;
147     };
148     
149     Sizzle.uniqueSort = function(results) {
150         if (sortOrder) {
151             hasDuplicate = false;
152             results.sort(sortOrder);
153             
154             if (hasDuplicate) {
155                 for (var i = 1; i < results.length; i++) {
156                     if (results[i] === results[i - 1]) {
157                         results.splice(i--, 1);
158                     }
159                 }
160             }
161         }
162     };
163     
164     Sizzle.matches = function(expr, set) {
165         return Sizzle(expr, null, null, set);
166     };
167     
168     Sizzle.find = function(expr, context, isXML) {
169         var set, match;
170         
171         if (!expr) {
172             return [];
173         }
174         
175         for (var i = 0, l = Expr.order.length; i < l; i++) {
176             var type = Expr.order[i], match;
177             
178             if ((match = Expr.match[type].exec(expr))) {
179                 var left = RegExp.leftContext;
180                 
181                 if (left.substr(left.length - 1) !== "\\") {
182                     match[1] = (match[1] || "").replace(/\\/g, "");
183                     set = Expr.find[type](match, context, isXML);
184                     if (set != null) {
185                         expr = expr.replace(Expr.match[type], "");
186                         break;
187                     }
188                 }
189             }
190         }
191         
192         if (!set) {
193             set = context.getElementsByTagName("*");
194         }
195         
196         return {
197             set  : set,
198             expr : expr
199         };
200     };
201     
202     Sizzle.filter = function(expr, set, inplace, not) {
203         var old = expr,
204             result = [],
205             curLoop = set, match, anyFound,
206             isXMLFilter = set && set[0] && isXML(set[0]);
207         
208         while (expr && set.length) {
209             for (var type in Expr.filter) {
210                 if ((match = Expr.match[type].exec(expr)) != null) {
211                     var filter = Expr.filter[type], found, item;
212                     anyFound = false;
213                     
214                     if (curLoop == result) {
215                         result = [];
216                     }
217                     
218                     if (Expr.preFilter[type]) {
219                         match = Expr.preFilter[type](match, curLoop, inplace, result, not, isXMLFilter);
220                         
221                         if (!match) {
222                             anyFound = found = true;
223                         } else if (match === true) {
224                             continue;
225                         }
226                     }
227                     
228                     if (match) {
229                         for (var i = 0; (item = curLoop[i]) != null; i++) {
230                             if (item) {
231                                 found = filter(item, match, i, curLoop);
232                                 var pass = not ^ !!found;
233                                 
234                                 if (inplace && found != null) {
235                                     if (pass) {
236                                         anyFound = true;
237                                     } else {
238                                         curLoop[i] = false;
239                                     }
240                                 } else if (pass) {
241                                     result.push(item);
242                                     anyFound = true;
243                                 }
244                             }
245                         }
246                     }
247                     
248                     if (found !== undefined) {
249                         if (!inplace) {
250                             curLoop = result;
251                         }
252                         
253                         expr = expr.replace(Expr.match[type], "");
254                         
255                         if (!anyFound) {
256                             return [];
257                         }
258                         
259                         break;
260                     }
261                 }
262             }
263             
264             // Improper expression
265             if (expr == old) {
266                 if (anyFound == null) {
267                     throw "Syntax error, unrecognized expression: " + expr;
268                 } else {
269                     break;
270                 }
271             }
272             
273             old = expr;
274         }
275         
276         return curLoop;
277     };
278     
279     var Expr = Sizzle.selectors = {
280         order      : ["ID", "NAME", "TAG"],
281         match      : {
282             ID     : /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
283             CLASS  : /\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
284             NAME   : /\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,
285             ATTR   : /\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
286             TAG    : /^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,
287             CHILD  : /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
288             POS    : /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
289             PSEUDO : /:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
290         },
291         attrMap    : {
292             "class" : "className",
293             "for"   : "htmlFor"
294         },
295         attrHandle : {
296             href : function(elem) {
297                 return elem.getAttribute("href");
298             }
299         },
300         relative   : {
301             "+" : function(checkSet, part, isXML) {
302                 var isPartStr = typeof part === "string",
303                     isTag = isPartStr && !/\W/.test(part),
304                     isPartStrNotTag = isPartStr && !isTag;
305                 
306                 if (isTag && !isXML) {
307                     part = part.toUpperCase();
308                 }
309                 
310                 for (var i = 0, l = checkSet.length, elem; i < l; i++) {
311                     if ((elem = checkSet[i])) {
312                         while ((elem = elem.previousSibling) &&
313                             elem.nodeType !== 1) {}
314                         
315                         checkSet[i] = isPartStrNotTag || elem &&
316                             elem.nodeName === part
317                             ? elem || false
318                             : elem === part;
319                     }
320                 }
321                 
322                 if (isPartStrNotTag) {
323                     Sizzle.filter(part, checkSet, true);
324                 }
325             },
326             ">" : function(checkSet, part, isXML) {
327                 var isPartStr = typeof part === "string";
328                 
329                 if (isPartStr && !/\W/.test(part)) {
330                     part = isXML
331                         ? part
332                         : part.toUpperCase();
333                     
334                     for (var i = 0, l = checkSet.length; i < l; i++) {
335                         var elem = checkSet[i];
336                         if (elem) {
337                             var parent = elem.parentNode;
338                             checkSet[i] = parent.nodeName === part
339                                 ? parent
340                                 : false;
341                         }
342                     }
343                 } else {
344                     for (var i = 0, l = checkSet.length; i < l; i++) {
345                         var elem = checkSet[i];
346                         if (elem) {
347                             checkSet[i] = isPartStr
348                                 ? elem.parentNode
349                                 : elem.parentNode === part;
350                         }
351                     }
352                     
353                     if (isPartStr) {
354                         Sizzle.filter(part, checkSet, true);
355                     }
356                 }
357             },
358             ""  : function(checkSet, part, isXML) {
359                 var doneName = done++,
360                     checkFn = dirCheck;
361                 
362                 if (!part.match(/\W/)) {
363                     var nodeCheck = part = isXML
364                         ? part
365                         : part.toUpperCase();
366                     checkFn = dirNodeCheck;
367                 }
368                 
369                 checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
370             },
371             "~" : function(checkSet, part, isXML) {
372                 var doneName = done++,
373                     checkFn = dirCheck;
374                 
375                 if (typeof part === "string" && !part.match(/\W/)) {
376                     var nodeCheck = part = isXML
377                         ? part
378                         : part.toUpperCase();
379                     checkFn = dirNodeCheck;
380                 }
381                 
382                 checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
383             }
384         },
385         find       : {
386             ID   : function(match, context, isXML) {
387                 if (typeof context.getElementById !== "undefined" && !isXML) {
388                     var m = context.getElementById(match[1]);
389                     return m
390                         ? [m]
391                         : [];
392                 }
393             },
394             NAME : function(match, context, isXML) {
395                 if (typeof context.getElementsByName !== "undefined") {
396                     var ret = [],
397                         results = context.getElementsByName(match[1]);
398                     
399                     for (var i = 0, l = results.length; i < l; i++) {
400                         if (results[i].getAttribute("name") === match[1]) {
401                             ret.push(results[i]);
402                         }
403                     }
404                     
405                     return ret.length === 0
406                         ? null
407                         : ret;
408                 }
409             },
410             TAG  : function(match, context) {
411                 return context.getElementsByTagName(match[1]);
412             }
413         },
414         preFilter  : {
415             CLASS  : function(match, curLoop, inplace, result, not, isXML) {
416                 match = " " + match[1].replace(/\\/g, "") + " ";
417                 
418                 if (isXML) {
419                     return match;
420                 }
421                 
422                 for (var i = 0, elem; (elem = curLoop[i]) != null; i++) {
423                     if (elem) {
424                         if (not ^
425                             (elem.className && (" " + elem.className + " ").indexOf(match) >= 0)) {
426                             if (!inplace) result.push(elem);
427                         } else if (inplace) {
428                             curLoop[i] = false;
429                         }
430                     }
431                 }
432                 
433                 return false;
434             },
435             ID     : function(match) {
436                 return match[1].replace(/\\/g, "");
437             },
438             TAG    : function(match, curLoop) {
439                 for (var i = 0; curLoop[i] === false; i++) {}
440                 return curLoop[i] && isXML(curLoop[i])
441                     ? match[1]
442                     : match[1].toUpperCase();
443             },
444             CHILD  : function(match) {
445                 if (match[1] == "nth") {
446                     // parse equations like 'even', 'odd', '5', '2n', '3n+2',
447                     // '4n-1', '-n+6'
448                     var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(match[2] == "even" &&
449                         "2n" ||
450                         match[2] == "odd" &&
451                         "2n+1" ||
452                         !/\D/.test(match[2]) && "0n+" + match[2] || match[2]);
453                     
454                     // calculate the numbers (first)n+(last) including if they
455                     // are negative
456                     match[2] = (test[1] + (test[2] || 1)) - 0;
457                     match[3] = test[3] - 0;
458                 }
459                 
460                 // TODO: Move to normal caching system
461                 match[0] = done++;
462                 
463                 return match;
464             },
465             ATTR   : function(match, curLoop, inplace, result, not, isXML) {
466                 var name = match[1].replace(/\\/g, "");
467                 
468                 if (!isXML && Expr.attrMap[name]) {
469                     match[1] = Expr.attrMap[name];
470                 }
471                 
472                 if (match[2] === "~=") {
473                     match[4] = " " + match[4] + " ";
474                 }
475                 
476                 return match;
477             },
478             PSEUDO : function(match, curLoop, inplace, result, not) {
479                 if (match[1] === "not") {
480                     // If we're dealing with a complex expression, or a simple
481                     // one
482                     if (match[3].match(chunker).length > 1 ||
483                         /^\w/.test(match[3])) {
484                         match[3] = Sizzle(match[3], null, null, curLoop);
485                     } else {
486                         var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
487                         if (!inplace) {
488                             result.push.apply(result, ret);
489                         }
490                         return false;
491                     }
492                 } else if (Expr.match.POS.test(match[0]) ||
493                     Expr.match.CHILD.test(match[0])) {
494                     return true;
495                 }
496                 
497                 return match;
498             },
499             POS    : function(match) {
500                 match.unshift(true);
501                 return match;
502             }
503         },
504         filters    : {
505             enabled  : function(elem) {
506                 return elem.disabled === false && elem.type !== "hidden";
507             },
508             disabled : function(elem) {
509                 return elem.disabled === true;
510             },
511             checked  : function(elem) {
512                 return elem.checked === true;
513             },
514             selected : function(elem) {
515                 // Accessing this property makes selected-by-default
516                 // options in Safari work properly
517                 elem.parentNode.selectedIndex;
518                 return elem.selected === true;
519             },
520             parent   : function(elem) {
521                 return !!elem.firstChild;
522             },
523             empty    : function(elem) {
524                 return !elem.firstChild;
525             },
526             has      : function(elem, i, match) {
527                 return !!Sizzle(match[3], elem).length;
528             },
529             header   : function(elem) {
530                 return /h\d/i.test(elem.nodeName);
531             },
532             text     : function(elem) {
533                 return "text" === elem.type;
534             },
535             radio    : function(elem) {
536                 return "radio" === elem.type;
537             },
538             checkbox : function(elem) {
539                 return "checkbox" === elem.type;
540             },
541             file     : function(elem) {
542                 return "file" === elem.type;
543             },
544             password : function(elem) {
545                 return "password" === elem.type;
546             },
547             submit   : function(elem) {
548                 return "submit" === elem.type;
549             },
550             image    : function(elem) {
551                 return "image" === elem.type;
552             },
553             reset    : function(elem) {
554                 return "reset" === elem.type;
555             },
556             button   : function(elem) {
557                 return "button" === elem.type ||
558                     elem.nodeName.toUpperCase() === "BUTTON";
559             },
560             input    : function(elem) {
561                 return /input|select|textarea|button/i.test(elem.nodeName);
562             }
563         },
564         setFilters : {
565             first : function(elem, i) {
566                 return i === 0;
567             },
568             last  : function(elem, i, match, array) {
569                 return i === array.length - 1;
570             },
571             even  : function(elem, i) {
572                 return i % 2 === 0;
573             },
574             odd   : function(elem, i) {
575                 return i % 2 === 1;
576             },
577             lt    : function(elem, i, match) {
578                 return i < match[3] - 0;
579             },
580             gt    : function(elem, i, match) {
581                 return i > match[3] - 0;
582             },
583             nth   : function(elem, i, match) {
584                 return match[3] - 0 == i;
585             },
586             eq    : function(elem, i, match) {
587                 return match[3] - 0 == i;
588             }
589         },
590         filter     : {
591             PSEUDO : function(elem, match, i, array) {
592                 var name = match[1],
593                     filter = Expr.filters[name];
594                 
595                 if (filter) {
596                     return filter(elem, i, match, array);
597                 } else if (name === "contains") {
598                     return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;
599                 } else if (name === "not") {
600                     var not = match[3];
601                     
602                     for (i = 0, l = not.length; i < l; i++) {
603                         if (not[i] === elem) {
604                             return false;
605                         }
606                     }
607                     
608                     return true;
609                 }
610             },
611             CHILD  : function(elem, match) {
612                 var type = match[1],
613                     node = elem;
614                 switch (type) {
615                     case 'only' :
616                     case 'first' :
617                         while ((node = node.previousSibling)) {
618                             if (node.nodeType === 1) return false;
619                         }
620                         if (type == 'first') return true;
621                         node = elem;
622                     case 'last' :
623                         while ((node = node.nextSibling)) {
624                             if (node.nodeType === 1) return false;
625                         }
626                         return true;
627                     case 'nth' :
628                         var first = match[2],
629                             last = match[3];
630                         
631                         if (first == 1 && last == 0) {
632                             return true;
633                         }
634                         
635                         var doneName = match[0],
636                             parent = elem.parentNode;
637                         
638                         if (parent &&
639                             (parent.sizcache !== doneName || !elem.nodeIndex)) {
640                             var count = 0;
641                             for (node = parent.firstChild; node; node = node.nextSibling) {
642                                 if (node.nodeType === 1) {
643                                     node.nodeIndex = ++count;
644                                 }
645                             }
646                             parent.sizcache = doneName;
647                         }
648                         
649                         var diff = elem.nodeIndex - last;
650                         if (first == 0) {
651                             return diff == 0;
652                         } else {
653                             return (diff % first == 0 && diff / first >= 0);
654                         }
655                 }
656             },
657             ID     : function(elem, match) {
658                 return elem.nodeType === 1 && elem.getAttribute("id") === match;
659             },
660             TAG    : function(elem, match) {
661                 return (match === "*" && elem.nodeType === 1) ||
662                     elem.nodeName === match;
663             },
664             CLASS  : function(elem, match) {
665                 return (" " + (elem.className || elem.getAttribute("class")) + " ").indexOf(match) > -1;
666             },
667             ATTR   : function(elem, match) {
668                 var name = match[1],
669                     result = Expr.attrHandle[name]
670                         ? Expr.attrHandle[name](elem)
671                         : elem[name] != null
672                             ? elem[name]
673                             : elem.getAttribute(name),
674                     value = result + "",
675                     type = match[2],
676                     check = match[4];
677                 
678                 return result == null
679                     ? type === "!="
680                     : type === "="
681                         ? value === check
682                         : type === "*="
683                             ? value.indexOf(check) >= 0
684                             : type === "~="
685                                 ? (" " + value + " ").indexOf(check) >= 0
686                                 : !check
687                                     ? value && result !== false
688                                     : type === "!="
689                                         ? value != check
690                                         : type === "^="
691                                             ? value.indexOf(check) === 0
692                                             : type === "$="
693                                                 ? value.substr(value.length -
694                                                     check.length) === check
695                                                 : type === "|="
696                                                     ? value === check ||
697                                                         value.substr(0, check.length +
698                                                             1) === check + "-"
699                                                     : false;
700             },
701             POS    : function(elem, match, i, array) {
702                 var name = match[2],
703                     filter = Expr.setFilters[name];
704                 
705                 if (filter) {
706                     return filter(elem, i, match, array);
707                 }
708             }
709         }
710     };
711     
712     var origPOS = Expr.match.POS;
713     
714     for (var type in Expr.match) {
715         Expr.match[type] = new RegExp(Expr.match[type].source +
716             /(?![^\[]*\])(?![^\(]*\))/.source);
717     }
718     
719     var makeArray = function(array, results) {
720         array = Array.prototype.slice.call(array);
721         
722         if (results) {
723             results.push.apply(results, array);
724             return results;
725         }
726         
727         return array;
728     };
729     
730     // Perform a simple check to determine if the browser is capable of
731     // converting a NodeList to an array using builtin methods.
732     try {
733         Array.prototype.slice.call(document.documentElement.childNodes);
734         
735         // Provide a fallback method if it does not work
736     } catch (e) {
737         makeArray = function(array, results) {
738             var ret = results || [];
739             
740             if (toString.call(array) === "[object Array]") {
741                 Array.prototype.push.apply(ret, array);
742             } else {
743                 if (typeof array.length === "number") {
744                     for (var i = 0, l = array.length; i < l; i++) {
745                         ret.push(array[i]);
746                     }
747                 } else {
748                     for (var i = 0; array[i]; i++) {
749                         ret.push(array[i]);
750                     }
751                 }
752             }
753             
754             return ret;
755         };
756     }
757     
758     if (document.documentElement.compareDocumentPosition) {
759         /** @ignore */
760         sortOrder = function(a, b) {
761             var ret = a.compareDocumentPosition(b) & 4
762                 ? -1
763                 : a === b
764                     ? 0
765                     : 1;
766             if (ret === 0) {
767                 hasDuplicate = true;
768             }
769             return ret;
770         };
771     } else if ("sourceIndex" in document.documentElement) {
772         /** @ignore */
773         sortOrder = function(a, b) {
774             var ret = a.sourceIndex - b.sourceIndex;
775             if (ret === 0) {
776                 hasDuplicate = true;
777             }
778             return ret;
779         };
780     } else if (document.createRange) {
781         /** @ignore */
782         sortOrder = function(a, b) {
783             var aRange = a.ownerDocument.createRange(),
784                 bRange = b.ownerDocument.createRange();
785             aRange.selectNode(a);
786             aRange.collapse(true);
787             bRange.selectNode(b);
788             bRange.collapse(true);
789             var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
790             if (ret === 0) {
791                 hasDuplicate = true;
792             }
793             return ret;
794         };
795     }
796     
797     // Check to see if the browser returns elements by name when
798     // querying by getElementById (and provide a workaround)
799     (function() {
800         // We're going to inject a fake input element with a specified name
801         var form = document.createElement("div"),
802             id = "script" + (new Date).getTime();
803         form.innerHTML = "<a name='" + id + "'/>";
804         
805         // Inject it into the root element, check its status, and remove it
806         // quickly
807         var root = document.documentElement;
808         root.insertBefore(form, root.firstChild);
809         
810         // The workaround has to do additional checks after a getElementById
811         // Which slows things down for other browsers (hence the branching)
812         if (!!document.getElementById(id)) {
813             Expr.find.ID = function(match, context, isXML) {
814                 if (typeof context.getElementById !== "undefined" && !isXML) {
815                     var m = context.getElementById(match[1]);
816                     return m
817                         ? m.id === match[1] ||
818                             typeof m.getAttributeNode !== "undefined" &&
819                             m.getAttributeNode("id").nodeValue === match[1]
820                             ? [m]
821                             : undefined
822                         : [];
823                 }
824             };
825             
826             Expr.filter.ID = function(elem, match) {
827                 var node = typeof elem.getAttributeNode !== "undefined" &&
828                     elem.getAttributeNode("id");
829                 return elem.nodeType === 1 && node && node.nodeValue === match;
830             };
831         }
832         
833         root.removeChild(form);
834         root = form = null; // release memory in IE
835     })();
836     
837     (function() {
838         // Check to see if the browser returns only elements
839         // when doing getElementsByTagName("*")
840         
841         // Create a fake element
842         var div = document.createElement("div");
843         div.appendChild(document.createComment(""));
844         
845         // Make sure no comments are found
846         if (div.getElementsByTagName("*").length > 0) {
847             Expr.find.TAG = function(match, context) {
848                 var results = context.getElementsByTagName(match[1]);
849                 
850                 // Filter out possible comments
851                 if (match[1] === "*") {
852                     var tmp = [];
853                     
854                     for (var i = 0; results[i]; i++) {
855                         if (results[i].nodeType === 1) {
856                             tmp.push(results[i]);
857                         }
858                     }
859                     
860                     results = tmp;
861                 }
862                 
863                 return results;
864             };
865         }
866         
867         // Check to see if an attribute returns normalized href attributes
868         div.innerHTML = "<a href='#'></a>";
869         if (div.firstChild &&
870             typeof div.firstChild.getAttribute !== "undefined" &&
871             div.firstChild.getAttribute("href") !== "#") {
872             Expr.attrHandle.href = function(elem) {
873                 return elem.getAttribute("href", 2);
874             };
875         }
876         
877         div = null; // release memory in IE
878     })();
879     
880     if (document.querySelectorAll) (function() {
881         var oldSizzle = Sizzle,
882             div = document.createElement("div");
883         div.innerHTML = "<p class='TEST'></p>";
884         
885         // Safari can't handle uppercase or unicode characters when
886         // in quirks mode.
887         if (div.querySelectorAll && div.querySelectorAll(".TEST").length === 0) {
888             return;
889         }
890         
891         Sizzle = function(query, context, extra, seed) {
892             context = context || document;
893             
894             // Only use querySelectorAll on non-XML documents
895             // (ID selectors don't work in non-HTML documents)
896             if (!seed && context.nodeType === 9 && !isXML(context)) {
897                 try {
898                     return makeArray(context.querySelectorAll(query), extra);
899                 } catch (e) {}
900             }
901             
902             return oldSizzle(query, context, extra, seed);
903         };
904         
905         for (var prop in oldSizzle) {
906             Sizzle[prop] = oldSizzle[prop];
907         }
908         
909         div = null; // release memory in IE
910     })();
911     
912     if (document.getElementsByClassName &&
913         document.documentElement.getElementsByClassName) (function() {
914         var div = document.createElement("div");
915         div.innerHTML = "<div class='test e'></div><div class='test'></div>";
916         
917         // Opera can't find a second classname (in 9.6)
918         if (div.getElementsByClassName("e").length === 0) return;
919         
920         // Safari caches class attributes, doesn't catch changes (in 3.2)
921         div.lastChild.className = "e";
922         
923         if (div.getElementsByClassName("e").length === 1) return;
924         
925         Expr.order.splice(1, 0, "CLASS");
926         Expr.find.CLASS = function(match, context, isXML) {
927             if (typeof context.getElementsByClassName !== "undefined" && !isXML) {
928                 return context.getElementsByClassName(match[1]);
929             }
930         };
931         
932         div = null; // release memory in IE
933     })();
934     
935     function dirNodeCheck(dir, cur, doneName, checkSet, nodeCheck, isXML) {
936         var sibDir = dir == "previousSibling" && !isXML;
937         for (var i = 0, l = checkSet.length; i < l; i++) {
938             var elem = checkSet[i];
939             if (elem) {
940                 if (sibDir && elem.nodeType === 1) {
941                     elem.sizcache = doneName;
942                     elem.sizset = i;
943                 }
944                 elem = elem[dir];
945                 var match = false;
946                 
947                 while (elem) {
948                     if (elem.sizcache === doneName) {
949                         match = checkSet[elem.sizset];
950                         break;
951                     }
952                     
953                     if (elem.nodeType === 1 && !isXML) {
954                         elem.sizcache = doneName;
955                         elem.sizset = i;
956                     }
957                     
958                     if (elem.nodeName === cur) {
959                         match = elem;
960                         break;
961                     }
962                     
963                     elem = elem[dir];
964                 }
965                 
966                 checkSet[i] = match;
967             }
968         }
969     }
970     
971     function dirCheck(dir, cur, doneName, checkSet, nodeCheck, isXML) {
972         var sibDir = dir == "previousSibling" && !isXML;
973         for (var i = 0, l = checkSet.length; i < l; i++) {
974             var elem = checkSet[i];
975             if (elem) {
976                 if (sibDir && elem.nodeType === 1) {
977                     elem.sizcache = doneName;
978                     elem.sizset = i;
979                 }
980                 elem = elem[dir];
981                 var match = false;
982                 
983                 while (elem) {
984                     if (elem.sizcache === doneName) {
985                         match = checkSet[elem.sizset];
986                         break;
987                     }
988                     
989                     if (elem.nodeType === 1) {
990                         if (!isXML) {
991                             elem.sizcache = doneName;
992                             elem.sizset = i;
993                         }
994                         if (typeof cur !== "string") {
995                             if (elem === cur) {
996                                 match = true;
997                                 break;
998                             }
999                             
1000                         } else if (Sizzle.filter(cur, [elem]).length > 0) {
1001                             match = elem;
1002                             break;
1003                         }
1004                     }
1005                     
1006                     elem = elem[dir];
1007                 }
1008                 
1009                 checkSet[i] = match;
1010             }
1011         }
1012     }
1013     
1014     var contains = document.compareDocumentPosition
1015         ? function(a, b) {
1016             return a.compareDocumentPosition(b) & 16;
1017         }
1018         : function(a, b) {
1019             return a !== b && (a.contains
1020                 ? a.contains(b)
1021                 : true);
1022         };
1023     
1024     var isXML = function(elem) {
1025         return elem.nodeType === 9 &&
1026             elem.documentElement.nodeName !== "HTML" || !!elem.ownerDocument &&
1027             elem.ownerDocument.documentElement.nodeName !== "HTML";
1028     };
1029     
1030     var posProcess = function(selector, context) {
1031         var tmpSet = [],
1032             later = "", match,
1033             root = context.nodeType
1034                 ? [context]
1035                 : context;
1036         
1037         // Position selectors must be done after the filter
1038         // And so must :not(positional) so we move all PSEUDOs to the end
1039         while ((match = Expr.match.PSEUDO.exec(selector))) {
1040             later += match[0];
1041             selector = selector.replace(Expr.match.PSEUDO, "");
1042         }
1043         
1044         selector = Expr.relative[selector]
1045             ? selector + "*"
1046             : selector;
1047         
1048         for (var i = 0, l = root.length; i < l; i++) {
1049             Sizzle(selector, root[i], tmpSet);
1050         }
1051         
1052         return Sizzle.filter(later, tmpSet);
1053     };
1054     
1055     // EXPOSE
1056     window.Sizzle = Sizzle;
1057     
1058 })();
1059 
1060 /**
1061  * @namespace Provides methods to collect and filter DOM elements.
1062  */
1063 Jelo.Dom = function() {
1064     
1065     /** @scope Jelo.Dom */
1066     return {
1067         /**
1068          * Converts a string of HTML to actual DOM elements.
1069          * 
1070          * @param {String} html The HTML to convert to DOM nodes.
1071          * @returns {Node} A DocumentFragment object containing the specified
1072          *          nodes.
1073          */
1074         fromString   : function(str) {
1075             var frag = document.createDocumentFragment();
1076             if (typeof str != 'string') {
1077                 return frag;
1078             }
1079             var div = document.createElement('div');
1080             div.innerHTML = str;
1081             while (div.firstChild) {
1082                 frag.appendChild(div.firstChild);
1083             }
1084             return frag;
1085         },
1086         /**
1087          * Reduces a set of DOM nodes to those that also match the given CSS
1088          * selector. The selector can be a full selector (for example, "div >
1089          * span.foo") and not just a fragment or simple selector.
1090          * 
1091          * @param {String} selector The CSS selector or xpath query.
1092          * @param {Array} [set] The set of elements to filter.
1093          */
1094         filter       : function(selector, set) {
1095             return Sizzle.matches(selector, set);
1096         },
1097         /**
1098          * Selects a group of elements that match a given CSS selector.
1099          * 
1100          * @param {String} selector The CSS selector or xpath query.
1101          * @param {Node} [context=document] The root node within which to
1102          *        conduct this search.
1103          * @param {Array} [results] The collection returned from this function.
1104          * @returns {Array}
1105          */
1106         select       : function(selector, context, results) {
1107             return (selector && selector.isArray)
1108                 ? selector
1109                 : Sizzle(selector, context, results);
1110         },
1111         /**
1112          * Selects the FIRST instance of a matching element.
1113          * 
1114          * @param {String} selector The CSS selector or xpath query.
1115          * @param {Node} [context=document] The root node within which to
1116          *        conduct this search.
1117          * @param {Array} [results] The collection returned from this function.
1118          * @returns {Node}
1119          */
1120         selectNode   : function(selector, context, results) {
1121             return (selector && selector.nodeType)
1122                 ? selector
1123                 : Sizzle(selector, context, results)[0];
1124         },
1125         /**
1126          * Finds the position of an element on the page.
1127          * 
1128          * @param {HTMLElement} The element to inspect
1129          * @returns {Array} [left, top] calculated in pixels, output as Numbers.
1130          */
1131         findPosition : function(el) {
1132             var l = 0;
1133             var t = 0;
1134             if (el.offsetParent) {
1135                 do {
1136                     l += el.offsetLeft;
1137                     t += el.offsetTop;
1138                 } while (el = el.offsetParent);
1139             }
1140             return [l, t];
1141         }
1142     };
1143 }();
1144 
1145 $ = window['$'] || Jelo.Dom.selectNode;
1146 $$ = window['$$'] || Jelo.Dom.select;
1147