PageSourceSearch

https://www.montrealites.ca/montrealites_words/mt.js

js montrealites.ca collected 2026-10-02 14:52:46 UTC 169,182 bytes, 6,393 lines download raw bytes

1// The cookie name to use for storing the blog-side comment session cookie.
2var mtCookieName = "mt_blog_user";
3var mtCookieDomain = ".montrealites.ca";
4var mtCookiePath = "/";
5var mtCookieTimeout = 14400;
6
7
8function mtHide(id) {
9    var el = (typeof id == "string") ? document.getElementById(id) : id;
10    if (el) el.style.display = 'none';
11}
12
13
14function mtShow(id) {
15    var el = (typeof id == "string") ? document.getElementById(id) : id;
16    if (el) el.style.display = 'block';
17}
18
19
20function mtAttachEvent(eventName,func) {
21    var onEventName = 'on' + eventName;
22    var old = window[onEventName];
23    if( typeof old != 'function' )
24        window[onEventName] = func;
25    else {
26        window[onEventName] = function( evt ) {
27            old( evt );
28            return func( evt );
29        };
30    }
31}
32
33
34function mtFireEvent(eventName,param) {
35    var fn = window['on' + eventName];
36    if (typeof fn == 'function') return fn(param);
37    return;
38}
39
40if(!this.JSON){JSON={};}(function(){function f(n){return n<10?'0'+n:n;}if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(key){return this.getUTCFullYear()+'-'+f(this.getUTCMonth()+1)+'-'+f(this.getUTCDate())+'T'+f(this.getUTCHours())+':'+f(this.getUTCMinutes())+':'+f(this.getUTCSeconds())+'Z';};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf();};}var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+string+'"';}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==='object'&&typeof value.toJSON==='function'){value=value.toJSON(key);}if(typeof rep==='function'){value=rep.call(holder,key,value);}switch(typeof value){case'string':return quote(value);case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==='[object Array]'){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||'null';}v=partial.length===0?'[]':gap?'[\n'+gap+partial.join(',\n'+gap)+'\n'+mind+']':'['+partial.join(',')+']';gap=mind;return v;}if(rep&&typeof rep==='object'){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==='string'){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}v=partial.length===0?'{}':gap?'{\n'+gap+partial.join(',\n'+gap)+'\n'+mind+'}':'{'+partial.join(',')+'}';gap=mind;return v;}}if(typeof JSON.stringify!=='function'){JSON.stringify=function(value,replacer,space){var i;gap='';indent='';if(typeof space==='number'){for(i=0;i<space;i+=1){indent+=' ';}}else if(typeof space==='string'){indent=space;}rep=replacer;if(replacer&&typeof replacer!=='function'&&(typeof replacer!=='object'||typeof replacer.length!=='number')){throw new Error('JSON.stringify');}return str('',{'':value});};}if(typeof JSON.parse!=='function'){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==='object'){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v;}else{delete value[k];}}}}return reviver.call(holder,key,value);}cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);});}if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){j=eval('('+text+')');return typeof reviver==='function'?walk({'':j},''):j;}throw new SyntaxError('JSON.parse');};}}());
41
42var MT = window.MT || {};
43
44MT.cons = function () {
45  return {
46    LOG : 'log',
47    WARN : 'warn',
48    DEBUG : 'debug',
49    INFO : 'info',
50    ERR : 'error',
51    JSON : 'json'
52  };
53}();
54
55
56MT.core = function (o) {
57  var _debug = false;
58  
59  return {
60    
61    connect : function (url,respType,respHandler) {
62      var xh = mtGetXmlHttp();
63      if (!xh) return false;
64      
65      xh.onreadystatechange = function() {
66        if ( xh.readyState == 4 ) {
67          if ( xh.status && ( xh.status != 200 ) ) {
68            // error - ignore
69          } else {
70            switch (respType) {
71              case 'json':
72                respHandler(JSON.parse(xh.responseText));
73                break;
74                
75              case 'xml':
76                break;
77                
78              case 'text':
79                break;
80            }
81          }
82        }
83      };
84      
85      xh.open('GET',url);
86      xh.send(null);
87    },
88    
89    getEl : function (el) {
90      return MT.util.checkNodeType(el)==='element' ? id : (document.getElementById(el) || false);
91    },
92    
93    addEvent : function (el,type,func,obj) {
94      if(!obj && document.addEventListener) {
95        el.addEventListener(type,func,false);
96      } else if(obj && document.addEventListener) {
97        el.addEventListener(type,function () {
98          func.call(obj,event);
99        },false);
100      } else {
101        if(obj) {
102          el.attachEvent('on' + type,function () {
103            func.call(obj,event);
104          });
105        } else {
106          el.attachEvent('on' + type,function () {          
107            func.call(el,event);
108          });
109        }
110      }
111    },
112    
113    
114    log : function (level,msg) {
115      if(_debug && window.console) {
116        switch(level) {
117          case 'warn':
118          case 'debug':
119          case 'info':
120          case 'error':
121          case 'log':
122            console[level](msg);
123            break;
124            
125          default:
126            return false; 
127        }
128      } else {
129        return false;
130      }
131    }
132  }
133}();
134
135
136MT.util = function () {
137  return {
138    toggleVisibility : {
139      show : function () {
140        var i = arguments.length;
141        
142        while(i--) {
143          if(MT.util.checkNodeType(arguments[i])==='element') {
144            arguments[i].style.visibility = 'visible';
145          } else {
146            MT.core.getEl(arguments[i]).style.visibility = 'visible';
147          }
148        }
149      },
150      
151      hide : function () {
152        var i = arguments.length;
153        while(i--) {
154          if(MT.util.checkNodeType(arguments[i])==='element') {
155            arguments[i].style.visibility = 'hidden';
156          } else {
157            MT.core.getEl(arguments[i]).style.visibility = 'hidden';
158          }
159        }
160      }
161    },
162    
163    toggleDisplay : {
164      show : function () {
165        var i = arguments.length;
166        while(i--) {
167          if(MT.util.checkNodeType(arguments[i])==='element') {
168            arguments[i].style.display = '';
169          } else {
170            MT.core.getEl(arguments[i]).style.display = '';
171          }
172        }
173      },
174      
175      hide : function () {
176        var i = arguments.length;
177        while(i--) {
178          if(MT.util.checkNodeType(arguments[i])==='element') {
179            arguments[i].style.display = 'none';
180          } else {
181            MT.core.getEl(arguments[i]).style.display = 'none';
182          }
183        }
184      }
185    },
186    
187    
188    findDefiningParent : function (origin) {
189      if(MT.util.checkNodeType(origin)==='element') {
190        for(var node=origin.parentNode;node.parentNode;node=node.parentNode) {
191          if((node.hasAttribute && node.hasAttribute('id')) || node.getAttribute('id')) {
192            return node;
193          }
194        }
195      }
196      return false;
197    },
198    
199    
200    checkNodeType : function (obj) {
201      if (obj && obj.nodeName){
202        switch (obj.nodeType) {
203          case 1: return 'element';
204          case 3: return (/\S/).test(obj.nodeValue) ? 'textnode' : 'whitespace';
205        }
206      }
207    }
208  }
209}();
210
211
212(function () {
213  var M = MT.core,
214      c = MT.cons,
215      u = MT.util,
216      cache,
217      isLoading,
218      direction,
219      currentComments,
220      commentAnchor,
221      commentArrId,
222      commentsPerPage,
223      commentsTotalPages,
224      loadingIcon,
225      pageNum,
226      commentsOffset,
227      totalComments,
228      entryID,
229      commentContentDiv,
230      topNav,
231      nav,
232      currentCommentsSpan,
233      topCurrentCommentsSpan;
234            
235  M.addEvent(window,'load',_init);
236  
237  /**
238   * Initializes the class
239   * 
240   * @return void
241   */
242  function _init () {
243    if(!MT.entryCommentCount) {
244      return;
245    }
246    
247    _initializeVariables();
248    _setCommentOffset(false);
249    _checkForAnchor();
250		_setCurrentComments();
251    _toggleNavLinks();
252    _initializeEvents();
253  }
254  
255  
256  function _initializeVariables() {
257    cache = {};
258    isLoading = false;
259    commentAnchor = '';
260    commentArrId = '';
261    commentsPerPage = MT.commentsPerPage || 50;
262    currentComments = '';
263    direction = 'ascend';
264    entryID = MT.entryID;
265    totalComments = MT.entryCommentCount;
266    commentsTotalPages = Math.ceil(totalComments / commentsPerPage);
267    pageNum = 1;
268    
269    loadingIcon = "<img title='Loading...' src='../../../mt-static/images/indicator.white.gif' alt='Loading' />";
270    
271    commentContentDiv = M.getEl("comments-content");
272    topNav = M.getEl("top-comment-nav");
273    nav = M.getEl("comment-nav");
274    
275    currentCommentsSpan = M.getEl("current-comments");
276    topCurrentCommentsSpan = M.getEl("top-current-comments");
277  }
278  
279  function _initializeEvents() {
280    if (commentsPerPage < totalComments) {
281      M.addEvent(nav,'click',_handleEvents);
282      M.addEvent(topNav,'click',_handleEvents);
283    }
284  }
285  
286  
287  function _checkForAnchor() {
288    var found = String(window.location.hash).match( /comment-(\d{1,6})/ );
289		
290		if (found) {
291		  M.log(c.DEBUG,found);
292			if (!M.getEl(found[0]).hasOwnProperty('className')) {
293				if (_findIdMatch(found[1])) {
294    			pageNum = Math.floor(commentArrId / commentsPerPage) + 1;
295    			M.log(c.DEBUG,'Comment Array Id: ' + commentArrId);
296    			M.log(c.DEBUG,'Comments Per Page: ' + commentsPerPage);
297    			M.log(c.DEBUG,'Page Number: ' + pageNum);
298    			M.log(c.DEBUG,'Comment Offset: ' + _getCommentOffset());
299    			_updateComments();
300    		}
301			}
302		}
303  }
304  
305  
306  function _setCommentOffset() {
307    commentsOffset = commentsPerPage * (pageNum-1);
308  }
309  
310  
311  function _getCommentOffset() {
312    return commentsOffset;
313  }
314  
315  
316  function _handleEvents (e) {
317    var origin = e.target || e.srcElement,
318        parentId;
319        
320    // stupid IE
321    origin = origin.id && M.getEl(origin.id) || false;
322
323    if(origin) {
324      parentId = u.checkNodeType(origin.parentNode)==='element' && origin.parentNode.getAttribute('id') && origin.parentNode.id;
325    } else {
326      return false;
327    }
328    
329    switch(origin.nodeName) {
330      case 'A':
331        switch (parentId) {
332          case 'prev-comments':
333          case 'top-prev-comments':
334            if(e.preventDefault) {
335              e.preventDefault();
336            } else {
337              e.returnValue =	false;
338            }
339            if(!isLoading) {
340              _previousPage();
341            }
342            break;
343          case 'next-comments':
344          case 'top-next-comments':
345            if(e.preventDefault) {
346              e.preventDefault();
347            } else {
348              e.returnValue =	false;
349            }
350            if(!isLoading) {
351              _nextPage();
352            }
353            break;
354        }
355        break;
356    }
357  }
358  
359  
360  function _toggleNavLinks () {
361    M.log(c.DEBUG,M.getEl('top-prev-comments'));
362    if(pageNum <= commentsTotalPages && pageNum !== 1) {
363      u.toggleVisibility.show('prev-comments');
364      u.toggleVisibility.show('top-prev-comments');
365    }
366    
367    if(pageNum >= 1 && pageNum !== commentsTotalPages) {
368      u.toggleVisibility.show('next-comments');
369      u.toggleVisibility.show('top-next-comments');
370    }
371    
372    if(pageNum===1 || nav.style.visibility==='hidden') {
373      u.toggleVisibility.hide('prev-comments');
374      u.toggleVisibility.hide('top-prev-comments');
375    }
376    
377    if(pageNum===commentsTotalPages || nav.style.visibility==='hidden') {
378      u.toggleVisibility.hide('next-comments');
379      u.toggleVisibility.hide('top-next-comments');
380    }
381  }
382  
383  
384  function _nextPage () {
385    if(pageNum < commentsTotalPages) {
386      pageNum++;
387      _updateComments();
388    }
389  }
390  
391  
392  function _previousPage() {
393    if(pageNum > 1) {
394      pageNum--;
395      _updateComments();
396    }
397  }
398  
399  
400  function _findIdMatch (id) {
401    var len = MT.commentIds.length;
402    
403  	while (len--) {
404  		if (MT.commentIds[len] == id) {
405  			commentAnchor = "comment-" + id;
406  			commentArrId = len;
407  			return true;
408  		}
409  	}
410  	
411  	return false;
412  }
413  
414  
415  function _setCurrentComments() {
416    var commentsOnPage = pageNum != commentsTotalPages ? commentsOffset + commentsPerPage : totalComments;
417    
418    _setCurrentCommentsContent([commentsOffset+1," - ",commentsOnPage].join(''));
419  }
420  
421  
422  function _setCurrentCommentsContent(currentCommentsHTML) {
423    currentCommentsSpan.innerHTML = currentCommentsHTML;
424    topCurrentCommentsSpan.innerHTML = currentCommentsHTML;
425  }
426  
427  
428  function _setCommentContent(commentHTML) {
429    commentContentDiv.innerHTML = commentHTML;
430  }
431  
432  
433  function _updateComments() {
434    var comments, jsonUrl;
435    isLoading = true;
436    _setCurrentCommentsContent(loadingIcon);
437    _setCommentOffset();
438    
439    jsonUrl = [
440        "http://www.montrealites.ca/cgi-bin/mt/hLaSx4qew0QL-q1sS7eTgQ2wIg-c0mn3mts.cgi?__mode=comment_listing&direction=",
441        direction,
442        "&entry_id=",
443        entryID,
444        "&limit=",
445        commentsPerPage,
446        "&offset=",
447        _getCommentOffset()
448      ].join('');
449  	
450  	if (!commentAnchor) {
451      commentAnchor = "comments-content";
452    }
453    
454    if(cache.hasOwnProperty(jsonUrl)) {
455      _refreshComments(cache[jsonUrl]);
456      isLoading = false;
457    } else {
458      M.connect(jsonUrl,c.JSON,function (json) {
459        cache[jsonUrl] = json.comments;
460    	  _refreshComments(json.comments);
461    	  isLoading = false;
462      });
463    }
464  }
465  
466  
467  function _refreshComments(commentData) {
468    _setCommentContent(commentData);
469    _setCurrentComments();
470    window.location.hash = 'reset';
471    window.location.hash = commentAnchor;
472    _toggleNavLinks();
473  }
474})();
475
476
477function mtRelativeDate(ts, fds) {
478    var now = new Date();
479    var ref = ts;
480    var delta = Math.floor((now.getTime() - ref.getTime()) / 1000);
481
482    var str;
483    if (delta < 60) {
484        str = 'moments ago';
485    } else if (delta <= 86400) {
486        // less than 1 day
487        var hours = Math.floor(delta / 3600);
488        var min = Math.floor((delta % 3600) / 60);
489        if (hours == 1)
490            str = '1 hour ago';
491        else if (hours > 1)
492            str = '2 hours ago'.replace(/2/, hours);
493        else if (min == 1)
494            str = '1 minute ago';
495        else
496            str = '2 minutes ago'.replace(/2/, min);
497    } else if (delta <= 604800) {
498        // less than 1 week
499        var days = Math.floor(delta / 86400);
500        var hours = Math.floor((delta % 86400) / 3600);
501        if (days == 1)
502            str = '1 day ago';
503        else if (days > 1)
504            str = '2 days ago'.replace(/2/, days);
505        else if (hours == 1)
506            str = '1 hour ago';
507        else
508            str = '2 hours ago'.replace(/2/, hours);
509    }
510    return str ? str : fds;
511}
512
513
514function mtEditLink(entry_id, author_id) {
515    
516    
517}
518
519
520function mtCommentFormOnFocus() {
521    // if CAPTCHA is enabled, this causes the captcha image to be
522    // displayed if it hasn't been already.
523    mtShowCaptcha();
524}
525
526
527var mtCaptchaVisible = false;
528function mtShowCaptcha() {
529    var u = mtGetUser();
530    if ( u && u.is_authenticated ) return;
531    if (mtCaptchaVisible) return;
532    var div = document.getElementById('comments-open-captcha');
533    if (div) {
534        div.innerHTML = '';
535        mtCaptchaVisible = true;
536    }
537}
538
539
540
541var is_preview;
542var user;
543
544function mtSetUser(u) {
545    if (u) {
546        // persist this
547        user = u;
548        mtSaveUser();
549        // sync up user greeting
550        mtFireEvent('usersignin');
551    }
552}
553
554
555function mtEscapeJS(s) {
556    s = s.replace(/'/g, "&apos;");
557    return s;
558}
559
560
561function mtUnescapeJS(s) {
562    s = s.replace(/&apos;/g, "'");
563    return s;
564}
565
566
567function mtBakeUserCookie(u) {
568    var str = "";
569    if (u.name) str += "name:'" + mtEscapeJS(u.name) + "';";
570    if (u.url) str += "url:'" + mtEscapeJS(u.url) + "';";
571    if (u.email) str += "email:'" + mtEscapeJS(u.email) + "';";
572    if (u.is_authenticated) str += "is_authenticated:'1';";
573    if (u.profile) str += "profile:'" + mtEscapeJS(u.profile) + "';";
574    if (u.userpic) str += "userpic:'" + mtEscapeJS(u.userpic) + "';";
575    if (u.sid) str += "sid:'" + mtEscapeJS(u.sid) + "';";
576    str += "is_trusted:'" + (u.is_trusted ? "1" : "0") + "';";
577    str += "is_author:'" + (u.is_author ? "1" : "0") + "';";
578    str += "is_banned:'" + (u.is_banned ? "1" : "0") + "';";
579    str += "can_post:'" + (u.can_post ? "1" : "0") + "';";
580    str += "can_comment:'" + (u.can_comment ? "1" : "0") + "';";
581    str = str.replace(/;$/, '');
582    return str;
583}
584
585
586function mtUnbakeUserCookie(s) {
587    if (!s) return;
588
589    var u = {};
590    var m;
591    while (m = s.match(/^((name|url|email|is_authenticated|profile|userpic|sid|is_trusted|is_author|is_banned|can_post|can_comment):'([^']+?)';?)/)) {
592        s = s.substring(m[1].length);
593        if (m[2].match(/^(is|can)_/)) // boolean fields
594            u[m[2]] = m[3] == '1' ? true : false;
595        else
596            u[m[2]] = mtUnescapeJS(m[3]);
597    }
598    if (u.is_authenticated) {
599        u.is_anonymous = false;
600    } else {
601        u.is_anonymous = true;
602        u.can_post = false;
603        u.is_author = false;
604        u.is_banned = false;
605        u.is_trusted = false;
606    }
607    return u;
608}
609
610
611function mtGetUser() {
612    if (!user) {
613        var cookie = mtGetCookie(mtCookieName);
614        if (!cookie) return;
615        user = mtUnbakeUserCookie(cookie);
616        if (! user) {
617            user = {};
618            user.is_anonymous = true;
619            user.can_post = false;
620            user.is_author = false;
621            user.is_banned = false;
622            user.is_trusted = false;
623        }
624    }
625    return user;
626}
627
628
629var mtFetchedUser = false;
630
631function mtFetchUser(cb) {
632    if (!cb) cb = 'mtSetUser';
633    if ( ( cb == 'mtSetUser' ) && mtGetUser() ) {
634        var url = document.URL;
635        url = url.replace(/#.+$/, '');
636        url += '#comments-open';
637        location.href = url;
638    } else {
639        // we aren't using AJAX for this, since we may have to request
640        // from a different domain. JSONP to the rescue.
641        mtFetchedUser = true;
642        var u = mtGetUser();
643        var script = document.createElement('script');
644        var ts = new Date().getTime();
645        script.src = 'http://www.montrealites.ca/cgi-bin/mt/hLaSx4qew0QL-q1sS7eTgQ2wIg-c0mn3mts.cgi?__mode=userinfo&blog_id=125&jsonp=' + cb + '&ts=' + ts + '&sid=' + u.sid;
646        (document.getElementsByTagName('head'))[0].appendChild(script);
647    }
648}
649
650function mtVerifySession(cb) {
651    if (!cb) return;
652    var script = document.createElement('script');
653    var ts = new Date().getTime();
654    var u = mtGetUser();
655    script.src = 'http://www.montrealites.ca/cgi-bin/mt/hLaSx4qew0QL-q1sS7eTgQ2wIg-c0mn3mts.cgi?__mode=verify_session&blog_id=125&jsonp=' + cb + '&ts=' + ts + '&sid=' + u.sid;
656    (document.getElementsByTagName('head'))[0].appendChild(script);
657}
658
659
660
661function mtRememberMeOnClick(b) {
662    if (!b.checked)
663        mtClearUser(b.form);
664    return true;
665}
666
667
668
669var mtRequestSubmitted = false;
670function mtCommentOnSubmit(f) {
671    if (!mtRequestSubmitted) {
672        mtRequestSubmitted = true;
673
674        if (f.armor)
675            f.armor.value = '99078c7fe09d3963958aeebeba6ea9ce3ee626ce';
676        if (f.bakecookie && f.bakecookie.checked)
677            mtSaveUser(f);
678
679        // disable submit buttons
680        if (f.preview_button) f.preview_button.disabled = true;
681        if (f.post) f.post.disabled = true;
682
683        var u = mtGetUser();
684        if ( !is_preview && ( u && u.is_authenticated ) ) {
685            // validate session; then submit
686            mtVerifySession('mtCommentSessionVerify');
687            return false;
688        }
689
690        return true;
691    }
692    return false;
693}
694
695function mtCommentSessionVerify(app_user) {
696    var f = document['comments_form'];
697    if ( app_user && app_user.verified ) {
698        f.submit();
699    } else {
700        alert('Your session has expired. Please sign in again to comment.');
701        mtClearUser();
702        mtFireEvent('usersignin');
703
704        mtShow('comments-form');
705        mtHide('comments-open-footer');
706
707    }
708}
709
710function mtUserOnLoad() {
711    var u = mtGetUser();
712
713    // if the user is authenticated, hide the 'anonymous' fields
714    // and any captcha input if already shown
715    if ( document.getElementById('comments-form')) {
716        if ( u && u.is_authenticated ) {
717            mtShow('comments-form');
718            mtHide('comments-open-data');
719            if (mtCaptchaVisible)
720                mtHide('comments-open-captcha');
721        } else {
722
723            mtHide('comments-form');
724
725        }
726        if ( u && u.is_banned )
727            mtHide('comments-form');
728
729        // if we're previewing a comment, make sure the captcha
730        // field is visible
731        if (is_preview)
732            mtShowCaptcha();
733        else
734            mtShowGreeting();
735
736        // populate anonymous comment fields if user is cookied as anonymous
737        var cf = document['comments_form'];
738        if (cf) {
739            if (u && u.is_anonymous) {
740                if (u.email) cf.email.value = u.email;
741                if (u.name) cf.author.value = u.name;
742                if (u.url) cf.url.value = u.url;
743                if (cf.bakecookie)
744                    cf.bakecookie.checked = u.name || u.email;
745            } else {
746                if (u && u.sid && cf.sid)
747                    cf.sid.value = u.sid;
748            }
749            if (cf.post && cf.post.disabled)
750                cf.post.disabled = false;
751            if (cf.preview_button && cf.preview_button.disabled)
752                cf.preview_button.disabled = false;
753            mtRequestSubmitted = false;
754        }
755    }
756}
757
758
759
760
761function mtEntryOnLoad() {
762    var cf = document['comments_form'];
763    if (cf && cf.preview) cf.preview.value = '';
764    mtHide('trackbacks-info');
765    
766    mtFireEvent('usersignin');
767}
768
769function mtEntryOnUnload() {
770    if (mtRequestSubmitted) {
771        var cf = document['comments_form'];
772        if (cf) {
773            if (cf.post && cf.post.disabled)
774                cf.post.disabled = false;
775            if (cf.preview_button && cf.preview_button.disabled)
776                cf.preview_button.disabled = false;
777        }
778        mtRequestSubmitted = false;
779    }
780    return true;
781}
782
783mtAttachEvent('usersignin', mtUserOnLoad);
784
785
786
787function mtSignIn() {
788    var doc_url = document.URL;
789    doc_url = doc_url.replace(/#.+/, '');
790    var url = 'http://www.montrealites.ca/cgi-bin/mt/mt-cp.cgi?__mode=login&blog_id=125';
791    if (is_preview) {
792        if ( document['comments_form'] ) {
793            var entry_id = document['comments_form'].entry_id.value;
794            url += '&entry_id=' + entry_id;
795        } else {
796            url += '&return_url=http%3A%2F%2Fwww.montrealites.ca%2Fmontrealites_words%2F';
797        }
798    } else {
799        url += '&return_url=' + encodeURIComponent(doc_url);
800    }
801    mtClearUser();
802    location.href = url;
803}
804
805function mtSignInOnClick(sign_in_element) {
806    var el;
807    if (sign_in_element) {
808        // display throbber
809        el = document.getElementById(sign_in_element);
810        if (!el)  // legacy MT 4.x element id
811            el = document.getElementById('comment-form-external-auth');
812    }
813    if (el)
814        el.innerHTML = 'Signing in... <span class="status-indicator">&nbsp;</span>';
815
816    mtClearUser(); // clear any 'anonymous' user cookie to allow sign in
817    mtSignIn();
818    return false;
819}
820
821function mtSetUserOrLogin(u) {
822    if (u && u.is_authenticated) {
823        mtSetUser(u);
824    } else {
825        // user really isn't logged in; so let's do this!
826        mtSignIn();
827    }
828}
829
830
831function mtSignOut(entry_id) {
832    mtClearUser();
833    var doc_url = document.URL;
834    doc_url = doc_url.replace(/#.+/, '');
835    var url = 'http://www.montrealites.ca/cgi-bin/mt/mt-cp.cgi?__mode=logout&static=0&blog_id=125';
836    if (is_preview) {
837        if ( document['comments_form'] ) {
838            var entry_id = document['comments_form'].entry_id.value;
839            url += '&entry_id=' + entry_id;
840        } else {
841            url += '&return_url=http%3A%2F%2Fwww.montrealites.ca%2Fmontrealites_words%2F';
842        }
843    } else {
844        url += '&return_url=' + encodeURIComponent(doc_url);
845    }
846    location.href = url;
847}
848
849
850function mtSignOutOnClick() {
851    mtSignOut();
852    return false;
853}
854
855
856
857function mtShowGreeting() {
858
859    var reg_reqd = true;
860
861    var cf = document['comments_form'];
862    if (!cf) return;
863
864    var el = document.getElementById('comment-greeting');
865    if (!el)  // legacy MT 4.x element id
866        el = document.getElementById('comment-form-external-auth');
867    if (!el) return;
868
869    var eid = cf.entry_id;
870    var entry_id;
871    if (eid) entry_id = eid.value;
872
873    var phrase;
874    var u = mtGetUser();
875
876    if ( u && u.is_authenticated ) {
877        if ( u.is_banned ) {
878            phrase = 'You do not have permission to comment on this blog. (\<a href=\"javas\cript:void(0);\" onclick=\"return mtSignOutOnClick();\"\>sign out\<\/a\>)';
879        } else {
880            var user_link;
881            if ( u.is_author ) {
882                user_link = '<a href="http://www.montrealites.ca/cgi-bin/mt/hLaSx4qew0QL-q1sS7eTgQ2wIg-c0mn3mts.cgi?__mode=edit_profile&blog_id=125&return_url=' + encodeURIComponent( location.href );
883                user_link += '">' + u.name + '</a>';
884            } else {
885                // registered user, but not a user with posting rights
886                if (u.url)
887                    user_link = '<a href="' + u.url + '">' + u.name + '</a>';
888                else
889                    user_link = u.name;
890            }
891            // TBD: supplement phrase with userpic if one is available.
892            phrase = 'Thanks for signing in, __NAME__. (\<a href=\"javas\cript:void(0)\" onclick=\"return mtSignOutOnClick();\"\>sign out\<\/a\>)';
893            phrase = phrase.replace(/__NAME__/, user_link);
894        }
895    } else {
896        if (reg_reqd) {
897            phrase = '\<a href=\"javas\cript:void(0)\" onclick=\"return mtSignInOnClick(\'comment-greeting\')\"\>Sign in\<\/a\> to comment.';
898        } else {
899            phrase = '\<a href=\"javas\cript:void(0)\" onclick=\"return mtSignInOnClick(\'comment-greeting\')\"\>Sign in\<\/a\> to comment, or comment anonymously.';
900        }
901    }
902    el.innerHTML = phrase;
903
904}
905
906
907
908function mtReplyCommentOnClick(parent_id, author) {
909    mtShow('comment-form-reply');
910
911    var checkbox = document.getElementById('comment-reply');
912    var label = document.getElementById('comment-reply-label');
913    var text = document.getElementById('comment-text');
914
915    // Populate label with new values
916    var reply_text = 'Replying to \<a href=\"#comment-__PARENT__\" onclick=\"location.href=this.href; return false\"\>comment from __AUTHOR__\<\/a\>';
917    reply_text = reply_text.replace(/__PARENT__/, parent_id);
918    reply_text = reply_text.replace(/__AUTHOR__/, author);
919    label.innerHTML = reply_text;
920
921    checkbox.value = parent_id; 
922    checkbox.checked = true;
923    try {
924        // text field may be hidden
925        text.focus();
926    } catch(e) {
927    }
928
929    mtSetCommentParentID();
930}
931
932
933function mtSetCommentParentID() {
934    var checkbox = document.getElementById('comment-reply');
935    var parent_id_field = document.getElementById('comment-parent-id');
936    if (!checkbox || !parent_id_field) return;
937
938    var pid = 0;
939    if (checkbox.checked == true)
940        pid = checkbox.value;
941    parent_id_field.value = pid;
942}
943
944
945function mtSaveUser(f) {
946    // We can't reliably store the user cookie during a preview.
947    if (is_preview) return;
948
949    var u = mtGetUser();
950
951    if (f && (!u || u.is_anonymous)) {
952        if ( !u ) {
953            u = {};
954            u.is_authenticated = false;
955            u.can_comment = true;
956            u.is_author = false;
957            u.is_banned = false;
958            u.is_anonymous = true;
959            u.is_trusted = false;
960        }
961        if (f.author != undefined) u.name = f.author.value;
962        if (f.email != undefined) u.email = f.email.value;
963        if (f.url != undefined) u.url = f.url.value;
964    }
965
966    if (!u) return;
967
968    var cache_period = mtCookieTimeout * 1000;
969
970    // cache anonymous user info for a long period if the
971    // user has requested to be remembered
972    if (u.is_anonymous && f && f.bakecookie && f.bakecookie.checked)
973        cache_period = 365 * 24 * 60 * 60 * 1000;
974
975    var now = new Date();
976    mtFixDate(now);
977    now.setTime(now.getTime() + cache_period);
978
979    var cmtcookie = mtBakeUserCookie(u);
980    mtSetCookie(mtCookieName, cmtcookie, now, mtCookiePath, mtCookieDomain,
981        location.protocol == 'https:');
982}
983
984
985function mtClearUser() {
986    user = null;
987    mtDeleteCookie(mtCookieName, mtCookiePath, mtCookieDomain,
988        location.protocol == 'https:');
989}
990
991
992function mtSetCookie(name, value, expires, path, domain, secure) {
993    if (domain && ( domain.match(/^\.?localhost$/) || domain.match(/^[0-9\.]+$/) ))
994        domain = null;
995    var curCookie = name + "=" + encodeURIComponent(value) +
996        (expires ? "; expires=" + expires.toGMTString() : "") +
997        (path ? "; path=" + path : "") +
998        (domain ? "; domain=" + domain : "") +
999        (secure ? "; secure" : "");
1000    document.cookie = curCookie;
1001}
1002
1003
1004function mtGetCookie(name) {
1005    var prefix = name + '=';
1006    var c = document.cookie;
1007    var cookieStartIndex = c.indexOf(prefix);
1008    if (cookieStartIndex == -1)
1009        return '';
1010    var cookieEndIndex = c.indexOf(";", cookieStartIndex + prefix.length);
1011    if (cookieEndIndex == -1)
1012        cookieEndIndex = c.length;
1013    var this_cookie = c.substring(cookieStartIndex + prefix.length, cookieEndIndex);
1014    if ( this_cookie.match(/\%u/i) ) {
1015        return '';
1016    }
1017    return decodeURIComponent( this_cookie );
1018}
1019
1020
1021function mtDeleteCookie(name, path, domain, secure) {
1022    if (mtGetCookie(name)) {
1023        if (domain && domain.match(/^\.?localhost$/))
1024            domain = null;
1025        document.cookie = name + "=" +
1026            (path ? "; path=" + path : "") +
1027            (domain ? "; domain=" + domain : "") +
1028            (secure ? "; secure" : "") +
1029            "; expires=Thu, 01-Jan-70 00:00:01 GMT";
1030    }
1031}
1032
1033function mtFixDate(date) {
1034    var skew = (new Date(0)).getTime();
1035    if (skew > 0)
1036        date.setTime(date.getTime() - skew);
1037}
1038
1039
1040function mtGetXmlHttp() {
1041    if ( !window.XMLHttpRequest ) {
1042        window.XMLHttpRequest = function() {
1043            var types = [
1044                "Microsoft.XMLHTTP",
1045                "MSXML2.XMLHTTP.5.0",
1046                "MSXML2.XMLHTTP.4.0",
1047                "MSXML2.XMLHTTP.3.0",
1048                "MSXML2.XMLHTTP"
1049            ];
1050
1051            for ( var i = 0; i < types.length; i++ ) {
1052                try {
1053                    return new ActiveXObject( types[ i ] );
1054                } catch( e ) {}
1055            }
1056
1057            return undefined;
1058        };
1059    }
1060    if ( window.XMLHttpRequest )
1061        return new XMLHttpRequest();
1062}
1063
1064// BEGIN: fast browser onload init
1065// Modifications by David Davis, DWD
1066// Dean Edwards/Matthias Miller/John Resig
1067// http://dean.edwards.name/weblog/2006/06/again/?full#comment5338
1068
1069function mtInit() {
1070    // quit if this function has already been called
1071    if (arguments.callee.done) return;
1072
1073    // flag this function so we don't do the same thing twice
1074    arguments.callee.done = true;
1075
1076    // kill the timer
1077    // DWD - check against window
1078    if ( window._timer ) clearInterval(window._timer);
1079
1080    // DWD - fire the window onload now, and replace it
1081    if ( window.onload && ( window.onload !== window.mtInit ) ) {
1082        window.onload();
1083        window.onload = function() {};
1084    }
1085
1086
1087    mtInitCommenter();
1088
1089
1090}
1091
1092/* for Mozilla/Opera9 */
1093if (document.addEventListener) {
1094    document.addEventListener("DOMContentLoaded", mtInit, false);
1095}
1096
1097/* for Internet Explorer */
1098/*@cc_on @*/
1099/*@if (@_win32)
1100document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>");
1101var script = document.getElementById("__ie_onload");
1102script.onreadystatechange = function() {
1103    if (this.readyState == "complete") {
1104        mtInit(); // call the onload handler
1105    }
1106};
1107/*@end @*/
1108
1109/* for Safari */
1110if (/WebKit/i.test(navigator.userAgent)) { // sniff
1111    _timer = setInterval(function() {
1112        if (/loaded|complete/.test(document.readyState)) {
1113            mtInit(); // call the onload handler
1114        }
1115    }, 10);
1116}
1117
1118/* for other browsers */
1119window.onload = mtInit;
1120
1121// END: fast browser onload init
1122
1123
1124
1125
1126function mtLoggedIn(ott) {
1127    var script = document.createElement('script');
1128    var ts = new Date().getTime();
1129    script.src = 'http://www.montrealites.ca/cgi-bin/mt/hLaSx4qew0QL-q1sS7eTgQ2wIg-c0mn3mts.cgi?__mode=userinfo&jsonp=mtSaveUserInfo&ott=' + ott;
1130    (document.getElementsByTagName('head'))[0].appendChild(script);
1131}
1132
1133function mtRefreshUserInfo(sid) {
1134    var script = document.createElement('script');
1135    var ts = new Date().getTime();
1136    script.src = 'http://www.montrealites.ca/cgi-bin/mt/hLaSx4qew0QL-q1sS7eTgQ2wIg-c0mn3mts.cgi?__mode=userinfo&jsonp=mtSaveUserInfo&sid=' + sid;
1137    (document.getElementsByTagName('head'))[0].appendChild(script);
1138}
1139
1140function mtSaveUserInfo (u) {
1141    if ( u.error ) {
1142        if ( !user ) {
1143            alert('The sign-in attempt was not successful; please try again.');
1144        }
1145        return;
1146    }
1147    user = null;
1148    var cmtcookie = mtBakeUserCookie(u);
1149    var now = new Date();
1150    var cache_period = mtCookieTimeout * 1000;
1151
1152    // cache anonymous user info for a long period if the
1153    // user has requested to be remembered
1154    mtFixDate(now);
1155    now.setTime(now.getTime() + cache_period);
1156    mtSetCookie(mtCookieName, cmtcookie, now, mtCookiePath, mtCookieDomain,
1157        location.protocol == 'https:');
1158    mtFireEvent('usersignin');
1159}
1160
1161function mtInitCommenter () {
1162    /***
1163     * If request contains a '#_login' or '#_logout' hash, use this to
1164     * also delete the blog-side user cookie, since we're coming back from
1165     * a login, logout or edit profile operation.
1166     */
1167
1168    var hash = ( window.location.hash );
1169    hash.match( /^#_(.*)$/ );
1170    var command = RegExp.$1 || '';
1171    if ( command === 'refresh' ) {
1172        // Back from profile edit screen.
1173        // Reload userinfo with current session ID.
1174        var u = mtGetUser();
1175        user  = null;
1176        mtRefreshUserInfo(u.sid);
1177    }
1178    else if ( command === 'logout' ) {
1179        // clear any logged in state
1180        mtClearUser();
1181        mtFireEvent('usersignin');
1182    }
1183    else if ( command.match( /^login_(.*)$/ ) ) {
1184        var sid = RegExp.$1;
1185        var u = mtGetUser();
1186        if ( u && u.sid && u.sid == sid ) {
1187            mtFireEvent('usersignin');
1188        }
1189        else {
1190            mtLoggedIn(sid);
1191        }
1192    }
1193    else {
1194        mtFireEvent('usersignin');
1195    }
1196}
1197
1198
1199
1200/**
1201 * menuMatic
1202 * @version 0.68.3 (beta)
1203 * @author Jason J. Jaeger | greengeckodesign.com
1204 * @copyright 2008 Jason John Jaeger
1205 * @license MIT-style License
1206 *Permission is hereby granted, free of charge, to any person obtaining a copy
1207 *of this software and associated documentation files (the "Software"), to deal
1208 *in the Software without restriction, including without limitation the rights
1209 *to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1210 *copies of the Software, and to permit persons to whom the Software is
1211 *furnished to do so, subject to the following conditions:
1212 *
1213 *The above copyright notice and this permission notice shall be included in
1214 *all copies or substantial portions of the Software.
1215 *
1216 *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1217 *IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1218 *FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1219 *AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1220 *LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1221 *OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1222 *THE SOFTWARE.
1223 *
1224 *  @changeLog_________________________________________________________________________________
1225 *  
1226 *  Nov  15th 2008
1227 *  JJJ - incremented version to 0.68.3
1228 *  - Fixed bug which occured when ol was used instead of ul.. was because you cannot use selectors such
1229 *  as 'ul, ol' with getParent() and getNext() despite the mootool documentation saying "if Selectors
1230 *  is included, a full CSS selector can be passed."
1231 *  
1232 *  Nov 4th 2008
1233 *  JJJ - incremented version to 0.68.2
1234 *  - Removed extranious comma which threw an error in IE. (Thanks to Robert S. for catching that)
1235 *  - Added check to change the opacity to 99.9 if it is over 99 due to the fact that
1236 *  setting the opaicty to 100% in safari caused misc strange behaviour (thanks to Roland Barker
1237 *  at xnau webdesign for reporting this one)
1238 *  
1239 *  Nov 3rd 2008
1240 *  JJJ - Renamed menuWidth option to stretchMainMenu and made it work much quicker
1241 *  
1242 *  Nov 2nd 2008
1243 *  JJJ - Finished adding several callback functions
1244 *  - Added fixHasLayoutBug option
1245 *  
1246 *  Oct 4th 2008
1247 *  JJJ - Continued to go through code to optimize and clean it up
1248 *  - Added center option
1249 *  
1250 *  Sept 28th 2008
1251 *  JJJ - Incremented version to 0.68
1252 *  - Began optimizing & better commenting code
1253 *  
1254 *  Sept 16th 2008
1255 *  JJJ - Incremented version to 0.67
1256 *  - Specifically set each submenu z-index on show, incrementing each time. Set z-index back to 1 on hide.
1257 *  
1258 *  Sept 14th 2008
1259 *  JJJ - Added bottom edge detection and redirection (if bottom of subsequent submenu is lower than the scrollheight of the body)
1260 *  
1261 *  Sept 10th 2008
1262 *  JJJ - Did a google search for MooMenu and it looks like that name is already being used, so I must reluctently change the name.
1263 *  The name I have chose to move forward with is: MenuMatic.
1264 *  
1265 *  Sept 8th 2008
1266 *  JJJ - Incremented version to 0.66
1267 *  - Fixed IE bug whiched caused some events to be incorrectly attached to parent buttons causing sub submenus to not open
1268 *    correctly when transitions are enabled. This error was introduced in 0.65.
1269 *  
1270 *  August 10th 2008
1271 *  JJJ - Incremented verion to 0.65
1272 *  - Fixed bug with edge detection and redirection
1273 *  - Improved javascript and CSS to assure the submenus will always be on top
1274 *  - Improved edge detection and redirection logic
1275 *  - Added subMenusContainerId option so that if you have multiple MooMenu instances on a page they can be styled differently
1276 *  
1277 *  May 7th 2008
1278 *  JJJ - Incremented version to 0.64
1279 *  - Fixed bug with that occured when firing events
1280 *  
1281 *  April 28th 2008:
1282 *  JJJ - Incremented version to 0.63
1283 *  - Added killDivider option
1284 *  - Removed focus on hover since it adds browser default dotted outline
1285 *  - Changed menuWidth option to use actual width of nav element (so it can be set in css) instead of a width set in javascript
1286 *  
1287 *  Feb 17th 2008:
1288 *  JJJ - Incremented version to 0.62
1289 *  - Added mmbClassName and mmbFocusedClassName options to allow morphing of main menu btns
1290 *  
1291 *  Feb 16th 2008:
1292 *  JJJ - Incremented version to 0.61
1293 *  - Made changes to options syntax.
1294 *  - Improved keyboard accessability to take into account a menu's orientation, and its x and y direction when using arrow keys
1295 *  - Added matchWidthMode option.
1296 *  
1297 *  Feb 14th 2008:
1298 *  JJJ - Incremented version to 0.60
1299 *  - Added menuWidth option to simulate a table based layout for the main menu when the orienation is horizontal
1300 **/
1301
1302
1303/*
1304 * To Do:
1305 *
1306 * 1. Make screencast tutorials
1307 *
1308 * 2. Add more examples to website
1309 *
1310 * 3. Clean up/optimize CSS / make easier to edit
1311 *
1312 * 4. Make online generator
1313 *
1314 * 5. Add rounded corners option
1315 *
1316 * 6. Add drop shadow option
1317 *
1318 * 7. Add sound integration
1319 *
1320 */
1321
1322var MenuMatic = new Class({
1323Implements: Options,
1324options: {
1325        id: 'nav',//the id of the main menu (ul or ol)
1326        subMenusContainerId:'subMenusContainer',//id of the container div that will be generated to hold the submenus
1327
1328//subMenu behavior
1329effect: 'slide & fade',// 'slide', 'fade', 'slide & fade', or  null
1330duration: 600,//duration of the effect in milliseconds
1331physics: Fx.Transitions.Pow.easeOut,//how the effect behaves
1332hideDelay: 1000,//in milliseconds, how long you have after moving your mouse off of the submenus before they dissapear
1333
1334//layout
1335stretchMainMenu:false,//stretch main menu btn widths to fit within the width {set in the css} of the parent UL or OL
1336matchWidthMode:false,//initial submenus match their parent button's width
1337orientation: 'horizontal',//horizontal or vertical
1338direction:{x: 'right',y: 'down' },//for submenus ( relative to the parent button )left or right, up or down
1339tweakInitial:{ x:0, y:0},//if you need to tweak the placement of the initial submenus
1340tweakSubsequent:{ x:0, y:0 },//if you need to tweak the placement of the subsequent submenus
1341center: false,// will attempt to center main nav element
1342
1343//dynamic style
1344opacity: 95,//of the submenus
1345mmbFocusedClassName:null,//main menu button classname, used for morphing to focused state
1346mmbClassName:null,//main menu button classname, used for morphing back to original state
1347killDivider:null,
1348
1349fixHasLayoutBug:false,
1350
1351onHideAllSubMenusNow_begin: (function(){}),
1352onHideAllSubMenusNow_complete: (function(){}),
1353
1354onInit_begin: (function(){}),
1355onInit_complete: (function(){})
1356
1357    },
1358
1359hideAllMenusTimeout:null,
1360allSubMenus:[],
1361subMenuZindex:1,
1362
1363initialize: function(options){
1364//if(Browser.Engine.webkit419){return;}
1365        this.setOptions(options);
1366this.options.onInit_begin();
1367if(this.options.opacity > 99){this.options.opacity = 99.9;}
1368this.options.opacity = this.options.opacity /100;
1369
1370Element.implement({
1371    getId: function(){
1372//If this element does not have an id, give it a unique one
1373        if(!this.id){
1374var uniqueId = this.get('tag') + "-" + $time();
1375while($(uniqueId)){
1376//make sure it is absolutely unique
1377uniqueId = this.get('tag') + "-" + $time();
1378}
1379this.id = uniqueId;
1380}
1381    return this.id;
1382    }
1383});
1384
1385//initialize directions
1386this.options.direction.x = this.options.direction.x.toLowerCase();
1387this.options.direction.y = this.options.direction.y.toLowerCase();
1388if(this.options.direction.x === 'right'){
1389this.options.direction.xInverse = 'left';
1390}else if(this.options.direction.x === 'left'){
1391this.options.direction.xInverse = 'right';
1392}
1393if(this.options.direction.y === 'up'){
1394this.options.direction.yInverse = 'down';
1395}else if(this.options.direction.y === 'down'){
1396this.options.direction.yInverse = 'up';
1397}
1398
1399var links = $(this.options.id).getElements('a');
1400
1401links.each(function(item,index){
1402
1403//store parent links & child menu info
1404item.store('parentLinks', item.getParent().getParents('li').getFirst('a'));
1405
1406item.store('parentLinks',item.retrieve('parentLinks').erase(item.retrieve('parentLinks').getFirst()));
1407item.store('childMenu',item.getNext('ul') || item.getNext('ol'));
1408
1409//determine submenu type
1410theSubMenuType = 'subsequent';
1411
1412//console.log($(this.options.id).getElements('ul, ol'));
1413//console.log(item.getParent(['body ul,ol']));
1414
1415//console.log($(item.getParent('ul') || item.getParent('ol') ));
1416
1417if( $(item.getParent('ul') || item.getParent('ol') ).id === this.options.id){theSubMenuType = 'initial';}
1418item.store('subMenuType',theSubMenuType );
1419
1420//add classes to parents
1421if(theSubMenuType === 'initial' && $(item.getNext('ul') || item.getNext('ol') )){
1422item.addClass('mainMenuParentBtn');
1423}else if($(item.getNext('ul') || item.getNext('ol') )){
1424item.addClass('subMenuParentBtn');
1425}
1426}.bind(this));
1427
1428//rip the submenus apart into separate divs inside of subMenusContainer
1429var subMenusContainer = new Element('div', { 'id': this.options.subMenusContainerId}).inject( $(document.body) ,'bottom');
1430$(this.options.id).getElements('ul, ol').each(function(item,index){
1431new Element('div',{'class': 'smOW'}).inject(subMenusContainer).grab(item);
1432}.bind(this));
1433
1434//set tabindex to -1 so tabbing through links in page does not go through hidden links in submenus container, since arrow keys can be used to navigate through submenus
1435subMenusContainer.getElements('a').set('tabindex','-1');
1436
1437links.each(function(item,index){
1438//only apply to links with subMenus
1439if (!item.retrieve('childMenu')) {return;}
1440
1441//update childMenu pointer to look at smOW DIVs
1442item.store('childMenu', item.retrieve('childMenu').getParent('div'));
1443
1444//add to allSubMenus array
1445this.allSubMenus.include(item.retrieve('childMenu'));
1446
1447//store parentSubMenus
1448item.store('parentSubMenus',item.retrieve('parentLinks').retrieve('childMenu'));
1449
1450//now create the MenuMaticSubMenu class instances
1451var aSubMenu = new MenuMaticSubMenu(this.options,this, item);
1452
1453}.bind(this));
1454
1455//attach event handlers to non-parent main menu buttons
1456var nonParentBtns = $(this.options.id).getElements('a').filter(function(item, index){ return !item.retrieve('childMenu'); });
1457nonParentBtns.each(function(item, index){
1458item.addEvents({
1459'mouseenter': function(e){
1460//e = new Event(e).stop();
1461this.hideAllSubMenusNow();
1462if(this.options.mmbClassName && this.options.mmbFocusedClassName){
1463$(item).retrieve('btnMorph', new Fx.Morph(item, { 'duration':(this.options.duration/2), transition:this.options.physics, link:'cancel'})).start(this.options.mmbFocusedClassName);
1464}
1465}.bind(this),
1466
1467'focus': function(e){
1468//e = new Event(e).stop();
1469this.hideAllSubMenusNow();
1470if(this.options.mmbClassName && this.options.mmbFocusedClassName){
1471$(item).retrieve('btnMorph', new Fx.Morph(item, { 'duration':(this.options.duration/2), transition:this.options.physics, link:'cancel'})).start(this.options.mmbFocusedClassName);
1472}
1473}.bind(this),
1474
1475'mouseleave':function(e){
1476//e = new Event(e).stop();
1477if (this.options.mmbClassName && this.options.mmbFocusedClassName) {
1478$(item).retrieve('btnMorph', new Fx.Morph(item, {'duration': (this.options.duration * 5),transition: this.options.physics,link: 'cancel'})).start(this.options.mmbClassName);
1479}
1480}.bind(this),
1481
1482'blur':function(e){
1483//e = new Event(e).stop();
1484if (this.options.mmbClassName && this.options.mmbFocusedClassName) {
1485$(item).retrieve('btnMorph', new Fx.Morph(item, {'duration': (this.options.duration * 5),transition: this.options.physics,link: 'cancel'})).start(this.options.mmbClassName);
1486}
1487}.bind(this),
1488
1489'keydown' : function(e){
1490    var event = new Event(e);
1491if (e.key === 'up' || e.key === 'down' || e.key === 'left' || e.key === 'right') {e.stop();}
1492
1493if( e.key === 'left' && this.options.orientation === 'horizontal' ||
1494e.key === 'up' && this.options.orientation === 'vertical'){
1495
1496if(item.getParent('li').getPrevious('li')){
1497item.getParent('li').getPrevious('li').getFirst('a').focus();
1498}else{
1499item.getParent('li').getParent().getLast('li').getFirst('a').focus();
1500}
1501}else if(e.key === 'right' && this.options.orientation === 'horizontal' ||
1502 e.key === 'down' && this.options.orientation === 'vertical'){
1503if(item.getParent('li').getNext('li')){
1504item.getParent('li').getNext('li').getFirst('a').focus();
1505}else{
1506item.getParent('li').getParent().getFirst('li').getFirst('a').focus();
1507}
1508}
1509}.bind(this)
1510});
1511}, this);
1512
1513
1514this.stretch();
1515this.killDivider();
1516this.center();
1517this.fixHasLayoutBug();
1518this.options.onInit_complete();
1519    },
1520
1521fixHasLayoutBug:function(){
1522if(Browser.Engine.trident && this.options.fixHasLayoutBug){
1523$(this.options.id).getParents().setStyle('zoom',1);
1524$(this.options.id).setStyle('zoom',1);
1525$(this.options.id).getChildren().setStyle('zoom',1);
1526$(this.options.subMenusContainerId).setStyle('zoom',1);
1527$(this.options.subMenusContainerId).getChildren().setStyle('zoom',1);
1528}
1529},
1530
1531center:function(){
1532if(!this.options.center){return;}
1533$(this.options.id).setStyles({'left':'50%','margin-left': -($(this.options.id).getSize().x/2) });
1534},
1535
1536stretch:function(){
1537//stretch main menu btn widths to fit within the width of the parent UL or OL
1538if(this.options.stretchMainMenu  && this.options.orientation === 'horizontal'){
1539var targetWidth = parseFloat($(this.options.id).getCoordinates().width) ;
1540var totalBtnWidth = 0;
1541var mainBtns = $(this.options.id).getElements('a');
1542mainBtns.setStyles({'padding-left':0,'padding-right':0});
1543mainBtns.each(function(item,index){ totalBtnWidth+= item.getSize().x; }.bind(this));
1544if(targetWidth < totalBtnWidth){return;}
1545var increment = (targetWidth - totalBtnWidth)/ mainBtns.length;
1546mainBtns.each(function(item,index){ item.setStyle('width',item.getSize().x+increment);}.bind(this));
1547mainBtns.getLast().setStyle('width',mainBtns.getLast().getSize().x-1);
1548}
1549},
1550
1551killDivider:function(){
1552if(this.options.killDivider && this.options.killDivider.toLowerCase() === 'first'){
1553$($(this.options.id).getElements('li')[0]).setStyles({'background':'none'});
1554}else if(this.options.killDivider && this.options.killDivider.toLowerCase() === 'last'){
1555$($(this.options.id).getElements('li').getLast()).setStyles({'background':'none'});
1556}
1557},
1558
1559hideAllSubMenusNow: function(){
1560this.options.onHideAllSubMenusNow_begin();
1561$clear(this.hideAllMenusTimeout);
1562$$(this.allSubMenus).fireEvent('hide');
1563this.options.onHideAllSubMenusNow_complete();
1564}
1565
1566});
1567
1568var MenuMaticSubMenu = new Class({
1569Implements: Options,
1570Extends: MenuMatic,
1571    options: {
1572onSubMenuInit_begin: (function(subMenuClass){}),
1573onSubMenuInit_complete: (function(subMenuClass){}),
1574
1575onMatchWidth_begin: (function(subMenuClass){}),
1576onMatchWidth_complete: (function(subMenuClass){}),
1577
1578onHideSubMenu_begin: (function(subMenuClass){}),
1579onHideSubMenu_complete: (function(subMenuClass){}),
1580
1581onHideOtherSubMenus_begin: (function(subMenuClass){}),
1582onHideOtherSubMenus_complete: (function(subMenuClass){}),
1583
1584onHideAllSubMenus_begin: (function(subMenuClass){}),
1585onHideAllSubMenus_complete: (function(subMenuClass){}),
1586
1587onPositionSubMenu_begin: (function(subMenuClass){}),
1588onPositionSubMenu_complete: (function(subMenuClass){}),
1589
1590onShowSubMenu_begin: (function(subMenuClass){}),
1591onShowSubMenu_complete: (function(subMenuClass){})
1592},
1593root:null,
1594btn:null,
1595hidden:true,
1596myEffect:null,
1597
1598initialize: function(options,root,btn){
1599this.setOptions(options);
1600this.root = root;
1601this.btn = btn;
1602this.childMenu = this.btn.retrieve('childMenu');
1603this.subMenuType = this.btn.retrieve('subMenuType');
1604this.childMenu = this.btn.retrieve('childMenu');
1605this.parentSubMenus =  $$(this.btn.retrieve('parentSubMenus'));
1606this.parentLinks =  $$(this.btn.retrieve('parentLinks'));
1607this.parentSubMenu = $(this.parentSubMenus[0]);
1608if(this.parentSubMenu ){this.parentSubMenu =this.parentSubMenu.retrieve('class');}
1609this.childMenu.store('class',this);
1610this.btn.store('class',this);
1611this.childMenu.store('status','closed')
1612
1613this.options.onSubMenuInit_begin(this);
1614
1615//add hide Event
1616this.childMenu.addEvent('hide',function(){this.hideSubMenu();}.bind(this));
1617
1618//add show Event
1619this.childMenu.addEvent('show',function(){this.showSubMenu();}.bind(this));
1620
1621if(this.options.effect){
1622this.myEffect = new Fx.Morph(
1623$(this.childMenu).getFirst(), {duration: this.options.duration, transition: this.options.physics,  link: 'cancel' }
1624);
1625}
1626if(this.options.effect === 'slide' || this.options.effect === 'slide & fade'){
1627if (this.subMenuType == 'initial' && this.options.orientation === 'horizontal' ) {
1628this.childMenu.getFirst().setStyle('margin-top','0' );
1629}else {
1630this.childMenu.getFirst().setStyle('margin-left', '0');
1631}
1632
1633}else if (this.options.effect === 'fade' || this.options.effect === 'slide & fade'){
1634this.childMenu.getFirst().setStyle('opacity',0 );
1635}
1636
1637if (this.options.effect != 'fade' && this.options.effect != 'slide & fade') {
1638this.childMenu.getFirst().setStyle('opacity',this.options.opacity);
1639}
1640
1641
1642//attach event handlers to non-parent sub menu buttons
1643var nonParentBtns = $(this.childMenu).getElements('a').filter(function(item, index){ return !item.retrieve('childMenu'); });
1644nonParentBtns.each(function(item, index){
1645$(item).addClass('subMenuBtn');
1646
1647item.addEvents({
1648'mouseenter': function(e){
1649this.childMenu.fireEvent('show');
1650this.cancellHideAllSubMenus();
1651this.hideOtherSubMenus();
1652}.bind(this),
1653
1654'focus': function(e){
1655this.childMenu.fireEvent('show');
1656this.cancellHideAllSubMenus();
1657this.hideOtherSubMenus();
1658}.bind(this),
1659
1660'mouseleave': function(e){
1661this.cancellHideAllSubMenus();
1662this.hideAllSubMenus();
1663}.bind(this),
1664
1665'blur': function(e){
1666this.cancellHideAllSubMenus();
1667this.hideAllSubMenus();
1668}.bind(this),
1669
1670'keydown' : function(e){
1671    var event = new Event(e);
1672
1673if (e.key === 'up' || e.key === 'down' || e.key === 'left' || e.key === 'right' || e.key === 'tab') {e.stop();}
1674
1675if(e.key === 'up'){
1676if(item.getParent('li').getPrevious('li')){
1677//move focus to the next link up if possible
1678item.getParent('li').getPrevious('li').getFirst('a').focus();
1679}else if(this.options.direction.y ==='down'){
1680//move focus to the parent link
1681this.btn.focus();
1682}else if(this.options.direction.y ==='up'){
1683//move focus to the last link in the subMenu
1684item.getParent('li').getParent().getLast('li').getFirst('a').focus();
1685}
1686}else if(e.key === 'down'){
1687if(item.getParent('li').getNext('li')){
1688//move focus to the next link down if possible
1689item.getParent('li').getNext('li').getFirst('a').focus();
1690}else if(this.options.direction.y ==='down'){
1691//move focus to the first link in the submenu
1692item.getParent('li').getParent().getFirst('li').getFirst('a').focus();
1693}else if(this.options.direction.y ==='up'){
1694//move focus to the parent link
1695this.btn.focus();
1696}
1697}else if(e.key === this.options.direction.xInverse){
1698this.btn.focus();
1699}
1700}.bind(this)
1701});
1702
1703}, this);
1704
1705$(this.btn).removeClass('subMenuBtn');
1706
1707if (this.subMenuType == 'initial') {
1708this.btn.addClass('mainParentBtn');
1709}else{
1710this.btn.addClass('subParentBtn');
1711}
1712
1713//attach event handlers to parent button
1714$(this.btn).addEvents({
1715'mouseenter' : function(e){
1716//e = new Event(e).stop();
1717this.cancellHideAllSubMenus();
1718this.hideOtherSubMenus();
1719this.showSubMenu();
1720if(this.subMenuType === 'initial' && this.options.mmbClassName && this.options.mmbFocusedClassName){
1721$(this.btn).retrieve('btnMorph', new Fx.Morph($(this.btn), { 'duration':(this.options.duration/2), transition:this.options.physics, link:'cancel' })).start(this.options.mmbFocusedClassName);
1722}
1723}.bind(this),
1724
1725'focus' : function(e){
1726//e = new Event(e).stop();
1727this.cancellHideAllSubMenus();
1728this.hideOtherSubMenus();
1729this.showSubMenu();
1730if(this.subMenuType === 'initial' && this.options.mmbClassName && this.options.mmbFocusedClassName){
1731$(this.btn).retrieve('btnMorph', new Fx.Morph($(this.btn), { 'duration':(this.options.duration/2), transition:this.options.physics, link:'cancel' })).start(this.options.mmbFocusedClassName);
1732}
1733}.bind(this),
1734
1735'mouseleave': function(e){
1736//e = new Event(e).stop();
1737this.cancellHideAllSubMenus();
1738this.hideAllSubMenus();
1739}.bind(this),
1740
1741'blur': function(e){
1742//e = new Event(e).stop();
1743this.cancellHideAllSubMenus();
1744this.hideAllSubMenus();
1745}.bind(this),
1746
1747'keydown' : function(e){
1748    e = new Event(e)
1749if (e.key === 'up' || e.key === 'down' || e.key === 'left' || e.key === 'right') {e.stop();}
1750
1751if(!this.parentSubMenu){
1752//main menu parent buttons
1753if(
1754this.options.orientation === 'horizontal' && e.key === this.options.direction.y ||
1755this.options.orientation === 'vertical' && e.key === this.options.direction.x
1756){
1757if(this.options.direction.y ==='down'){
1758//move focus to the first link in the child menu
1759this.childMenu.getFirst().getFirst('li').getFirst('a').focus();
1760}else if(this.options.direction.y ==='up'){
1761//move focus to the first link in the child menu
1762this.childMenu.getFirst().getLast('li').getFirst('a').focus();
1763}
1764}else if(
1765this.options.orientation === 'horizontal' && e.key === 'left' ||
1766this.options.orientation === 'vertical' && e.key === this.options.direction.yInverse
1767){
1768//move focus to the previous link if possible, if not, move focus to the last link in the menu
1769if(this.btn.getParent().getPrevious()){
1770this.btn.getParent().getPrevious().getFirst().focus();
1771}else{
1772this.btn.getParent().getParent().getLast().getFirst().focus();
1773}
1774}else if(
1775this.options.orientation === 'horizontal' && e.key === 'right' ||
1776this.options.orientation === 'vertical' && e.key === this.options.direction.y
1777){
1778//move focus to the next link if possible, if not, move focus to the first link in the menu
1779if (this.btn.getParent().getNext()) {
1780this.btn.getParent().getNext().getFirst().focus();
1781}else{
1782this.btn.getParent().getParent().getFirst().getFirst().focus();
1783}
1784}
1785}else{
1786if(e.key === 'tab'){e.stop();}
1787//submenu parent buttons
1788if (e.key === 'up') {
1789if (this.btn.getParent('li').getPrevious('li')) {
1790//move focus to the next link up
1791this.btn.getParent('li').getPrevious('li').getFirst('a').focus();
1792}else if(this.options.direction.y === 'down'){
1793//move focus to the parent link
1794this.parentSubMenu.btn.focus();
1795}else if(this.options.direction.y === 'up'){
1796//move focus to the bottom link in this submenu
1797this.btn.getParent('li').getParent().getLast('li').getFirst('a').focus();
1798}
1799}else if(e.key === 'down'){
1800if(this.btn.getParent('li').getNext('li')){
1801//move focus to the next link down
1802this.btn.getParent('li').getNext('li').getFirst('a').focus();
1803}else if(this.options.direction.y === 'down'){
1804//move focus to the top link in this submenu
1805this.btn.getParent('li').getParent().getFirst('li').getFirst('a').focus();
1806}else if(this.options.direction.y === 'up'){
1807//move focus to the parent link
1808this.parentSubMenu.btn.focus();
1809}
1810}else if(e.key === this.options.direction.xInverse){
1811this.parentSubMenu.btn.focus();
1812}else if(e.key === this.options.direction.x){
1813if(this.options.direction.y === 'down'){
1814this.childMenu.getFirst().getFirst('li').getFirst('a').focus();
1815}else if(this.options.direction.y === 'up'){
1816//this.childMenu.getFirst().getLast('li').getFirst('a').focus();
1817}
1818}
1819}
1820}.bind(this)
1821});
1822
1823this.options.onSubMenuInit_complete(this);
1824
1825    },
1826
1827matchWidth:function(){
1828if (this.widthMatched || !this.options.matchWidthMode || this.subMenuType === 'subsequent'){return;}
1829this.options.onMatchWidth_begin(this);
1830var parentWidth = this.btn.getCoordinates().width;
1831$(this.childMenu).getElements('a').each(function(item,index){
1832var borderWidth = parseFloat($(this.childMenu).getFirst().getStyle('border-left-width')) + parseFloat($(this.childMenu).getFirst().getStyle('border-right-width'));
1833var paddingWidth = parseFloat(item.getStyle('padding-left')) + parseFloat(item.getStyle('padding-right'));
1834var offset = borderWidth + paddingWidth ;
1835if(parentWidth > item.getCoordinates().width){
1836item.setStyle('width',parentWidth - offset);
1837item.setStyle('margin-right',-borderWidth);
1838}
1839}.bind(this));
1840this.width = this.childMenu.getFirst().getCoordinates().width;
1841this.widthMatched = true;
1842this.options.onMatchWidth_complete(this);
1843},
1844
1845hideSubMenu: function() {
1846if(this.childMenu.retrieve('status') === 'closed'){return;}
1847this.options.onHideSubMenu_begin(this);
1848if (this.subMenuType == 'initial') {
1849if(this.options.mmbClassName && this.options.mmbFocusedClassName){
1850$(this.btn).retrieve('btnMorph', new Fx.Morph($(this.btn), { 'duration':(this.options.duration), transition:this.options.physics, link:'cancel' })).start(this.options.mmbClassName )
1851.chain(function(){
1852$(this.btn).removeClass('mainMenuParentBtnFocused');
1853$(this.btn).addClass('mainMenuParentBtn');
1854}.bind(this));
1855}else{
1856$(this.btn).removeClass('mainMenuParentBtnFocused');
1857$(this.btn).addClass('mainMenuParentBtn');
1858}
1859}else{
1860$(this.btn).removeClass('subMenuParentBtnFocused');
1861$(this.btn).addClass('subMenuParentBtn');
1862}
1863
1864this.childMenu.setStyle('z-index',1);
1865
1866if(this.options.effect && this.options.effect.toLowerCase() === 'slide'){
1867if (this.subMenuType == 'initial' && this.options.orientation === 'horizontal' && this.options.direction.y === 'down') {
1868this.myEffect.start({ 'margin-top': -this.height }).chain(function(){this.childMenu.style.display = "none";}.bind(this));
1869}else if (this.subMenuType == 'initial' && this.options.orientation === 'horizontal' && this.options.direction.y === 'up') {
1870this.myEffect.start({ 'margin-top': this.height }).chain(function(){this.childMenu.style.display = "none";}.bind(this));
1871}else if(this.options.direction.x === 'right'){
1872this.myEffect.start({ 'margin-left': -this.width }).chain(function(){this.childMenu.style.display = "none";}.bind(this));
1873}else if(this.options.direction.x === 'left'){
1874this.myEffect.start({ 'margin-left': this.width }).chain(function(){this.childMenu.style.display = "none";}.bind(this));
1875}
1876}else if(this.options.effect == 'fade'){
1877this.myEffect.start({ 'opacity': 0 }).chain(function(){this.childMenu.style.display = "none";}.bind(this));
1878}else if(this.options.effect == 'slide & fade'){
1879
1880if (this.subMenuType == 'initial' && this.options.orientation === 'horizontal' && this.options.direction.y === 'down') {
1881this.myEffect.start({ 'margin-top': -this.height,opacity:0 }).chain(function(){this.childMenu.style.display = "none";}.bind(this));
1882}else if (this.subMenuType == 'initial' && this.options.orientation === 'horizontal' && this.options.direction.y === 'up') {
1883this.myEffect.start({ 'margin-top': this.height,opacity:0 }).chain(function(){this.childMenu.style.display = "none";}.bind(this));
1884}else if(this.options.direction.x === 'right'){
1885this.myEffect.start({ 'margin-left': -this.width,opacity:0 }).chain(function(){this.childMenu.style.display = "none";}.bind(this));
1886}else if(this.options.direction.x === 'left'){
1887this.myEffect.start({ 'margin-left': this.width, opacity:0 }).chain(function(){this.childMenu.style.display = "none";}.bind(this));
1888}
1889}else{
1890this.childMenu.style.display = "none";
1891}
1892this.childMenu.store('status','closed');
1893this.options.onHideSubMenu_complete(this);
1894},
1895
1896hideOtherSubMenus: function() {
1897this.options.onHideOtherSubMenus_begin(this);
1898//set up otherSubMenus element collection
1899if(!this.btn.retrieve('otherSubMenus')){
1900this.btn.store('otherSubMenus', $$(this.root.allSubMenus.filter(function(item){ return !this.btn.retrieve('parentSubMenus').contains(item) && item != this.childMenu; }.bind(this)) ));
1901}
1902this.parentSubMenus.fireEvent('show');
1903this.btn.retrieve('otherSubMenus').fireEvent('hide');
1904this.options.onHideOtherSubMenus_complete(this);
1905},
1906
1907hideAllSubMenus: function(){
1908this.options.onHideAllSubMenus_begin(this);
1909$clear(this.root.hideAllMenusTimeout);
1910this.root.hideAllMenusTimeout = (function(){
1911$clear(this.hideAllMenusTimeout);
1912$$(this.root.allSubMenus).fireEvent('hide');
1913}).bind(this).delay(this.options.hideDelay);
1914this.options.onHideAllSubMenus_complete(this);
1915},
1916
1917cancellHideAllSubMenus: function(){
1918$clear(this.root.hideAllMenusTimeout);
1919},
1920
1921showSubMenu: function(now){
1922if(this.childMenu.retrieve('status') === 'open'){return;}
1923this.options.onShowSubMenu_begin(this);
1924if (this.subMenuType == 'initial') {
1925$(this.btn).removeClass('mainMenuParentBtn');
1926$(this.btn).addClass('mainMenuParentBtnFocused');
1927}else{
1928$(this.btn).removeClass('subMenuParentBtn');
1929$(this.btn).addClass('subMenuParentBtnFocused');
1930}
1931this.root.subMenuZindex++;
1932this.childMenu.setStyles({'display':'block','visibility':'hidden','z-index':this.root.subMenuZindex});
1933
1934if(!this.width || !this.height ){
1935this.width = this.childMenu.getFirst().getCoordinates().width;
1936this.height = this.childMenu.getFirst().getCoordinates().height;
1937this.childMenu.setStyle('height',this.height,'border');
1938if(this.options.effect === 'slide' || this.options.effect === 'slide & fade'){
1939if (this.subMenuType == 'initial' && this.options.orientation === 'horizontal' ) {
1940this.childMenu.getFirst().setStyle('margin-top','0' );
1941if(this.options.direction.y === 'down'){
1942this.myEffect.set({ 'margin-top': - this.height });
1943}else if(this.options.direction.y === 'up'){
1944this.myEffect.set({ 'margin-top': this.height });
1945}
1946}else {
1947if(this.options.direction.x === 'left'){
1948this.myEffect.set({ 'margin-left': this.width });
1949}else{
1950this.myEffect.set({ 'margin-left': -this.width });
1951}
1952}
1953}
1954}
1955this.matchWidth();
1956this.positionSubMenu();
1957
1958if(this.options.effect === 'slide' ){
1959this.childMenu.setStyles({'display':'block','visibility':'visible'});
1960if (this.subMenuType === 'initial' && this.options.orientation === 'horizontal') {
1961if(now){
1962this.myEffect.set({ 'margin-top': 0 }).chain(function(){this.showSubMenuComplete();}.bind(this));
1963}else{
1964this.myEffect.start({ 'margin-top': 0 }).chain(function(){this.showSubMenuComplete();}.bind(this));
1965}
1966}else{
1967if (now) {
1968this.myEffect.set({ 'margin-left': 0 }).chain(function(){this.showSubMenuComplete();}.bind(this));
1969}else{
1970this.myEffect.start({ 'margin-left': 0 }).chain(function(){this.showSubMenuComplete();}.bind(this));
1971}
1972}
1973}else if(this.options.effect === 'fade' ){
1974if (now) {
1975this.myEffect.set({'opacity': this.options.opacity}).chain(function(){this.showSubMenuComplete();}.bind(this));
1976}else{
1977this.myEffect.start({'opacity': this.options.opacity}).chain(function(){this.showSubMenuComplete();}.bind(this));
1978}
1979}else if(this.options.effect == 'slide & fade'){
1980this.childMenu.setStyles({'display':'block','visibility':'visible'});
1981this.childMenu.getFirst().setStyles({'left':0});
1982if (this.subMenuType === 'initial' && this.options.orientation === 'horizontal') {
1983if (now) {
1984this.myEffect.set({ 'margin-top': 0, 'opacity': this.options.opacity }).chain(function(){this.showSubMenuComplete();}.bind(this));
1985}else{
1986this.myEffect.start({ 'margin-top': 0, 'opacity': this.options.opacity }).chain(function(){this.showSubMenuComplete();}.bind(this));
1987}
1988}else{
1989if (now) {
1990if (this.options.direction.x === 'right') {
1991this.myEffect.set({ 'margin-left': 0, 'opacity': this.options.opacity }).chain(function(){this.showSubMenuComplete();}.bind(this));
1992}else if (this.options.direction.x === 'left') {
1993this.myEffect.set({ 'margin-left': 0, 'opacity': this.options.opacity }).chain(function(){this.showSubMenuComplete();}.bind(this));
1994}
1995}else{
1996if (this.options.direction.x === 'right') {
1997this.myEffect.set({ 'margin-left': -this.width, 'opacity': this.options.opacity });
1998this.myEffect.start({ 'margin-left': 0, 'opacity': this.options.opacity }).chain(function(){this.showSubMenuComplete();}.bind(this));
1999}else if (this.options.direction.x === 'left') {
2000this.myEffect.start({ 'margin-left': 0, 'opacity': this.options.opacity }).chain(function(){this.showSubMenuComplete();}.bind(this));
2001}
2002}
2003}
2004}else{
2005this.childMenu.setStyles({'display':'block','visibility':'visible'}).chain(function(){this.showSubMenuComplete(this);}.bind(this));
2006}
2007this.childMenu.store('status','open');
2008
2009},
2010
2011showSubMenuComplete:function(){
2012this.options.onShowSubMenu_complete(this);
2013},
2014
2015positionSubMenu: function(){
2016this.options.onPositionSubMenu_begin(this);
2017this.childMenu.setStyle('width',this.width) ;
2018this.childMenu.getFirst().setStyle('width',this.width) ;
2019
2020//if any parent has bounced off a viewport edge, inherit that new di
2020rection
2021if (this.subMenuType === 'subsequent') {
2022if(this.parentSubMenu && this.options.direction.x != this.parentSubMenu.options.direction.x){
2023if(this.parentSubMenu.options.direction.x === 'left' && this.options.effect && this.options.effect.contains('slide')){
2024this.myEffect.set({ 'margin-left': this.width });
2025}
2026}
2027this.options.direction.x = this.parentSubMenu.options.direction.x;
2028this.options.direction.xInverse = this.parentSubMenu.options.direction.xInverse;
2029this.options.direction.y = this.parentSubMenu.options.direction.y;
2030this.options.direction.yInverse = this.parentSubMenu.options.direction.yInverse;
2031}
2032
2033var top;
2034var overlap
2035if(this.subMenuType == 'initial'){
2036if(this.options.direction.y === 'up'){
2037if(this.options.orientation === 'vertical'){
2038top = this.btn.getCoordinates().bottom - this.height + this.options.tweakInitial.y ;
2039}else{
2040top = this.btn.getCoordinates().top - this.height + this.options.tweakInitial.y ;
2041}
2042this.childMenu.style.top = top+ 'px';
2043}else if(this.options.orientation == 'horizontal'){
2044this.childMenu.style.top = this.btn.getCoordinates().bottom + this.options.tweakInitial.y + 'px';
2045}else if(this.options.orientation == 'vertical'){
2046top = this.btn.getPosition().y + this.options.tweakInitial.y ;
2047if((top + this.childMenu.getSize().y) >= $(document.body).getScrollSize().y){
2048overlap = (top + this.childMenu.getSize().y) - $(document.body).getScrollSize().y  ;
2049top = top - overlap - 20;
2050}
2051this.childMenu.style.top = top+ 'px';
2052}
2053if(this.options.orientation == 'horizontal'){
2054this.childMenu.style.left = this.btn.getPosition().x + this.options.tweakInitial.x + 'px';
2055}else if(this.options.direction.x == 'left'){
2056this.childMenu.style.left = this.btn.getPosition().x - this.childMenu.getCoordinates().width + this.options.tweakInitial.x + 'px';
2057}else if(this.options.direction.x == 'right'){
2058this.childMenu.style.left = this.btn.getCoordinates().right + this.options.tweakInitial.x + 'px';
2059}
2060}else if(this.subMenuType == 'subsequent'){
2061
2062if(this.options.direction.y === 'down'){
2063if( (this.btn.getCoordinates().top + this.options.tweakSubsequent.y+ this.childMenu.getSize().y) >= $(document.body).getScrollSize().y ){
2064overlap =  (this.btn.getCoordinates().top + this.options.tweakSubsequent.y+ this.childMenu.getSize().y) -$(document.body).getScrollSize().y  ;
2065this.childMenu.style.top = (this.btn.getCoordinates().top + this.options.tweakSubsequent.y) - overlap - 20+ 'px';
2066}else{
2067this.childMenu.style.top = this.btn.getCoordinates().top + this.options.tweakSubsequent.y + 'px';
2068}
2069}else if(this.options.direction.y === 'up'){
2070if((this.btn.getCoordinates().bottom - this.height + this.options.tweakSubsequent.y)< 1){
2071this.options.direction.y = 'down';
2072this.options.direction.yInverse = 'up';
2073this.childMenu.style.top = this.btn.getCoordinates().top + this.options.tweakSubsequent.y + 'px';
2074}else{
2075this.childMenu.style.top = this.btn.getCoordinates().bottom - this.height + this.options.tweakSubsequent.y + 'px';
2076}
2077}
2078if(this.options.direction.x == 'left'){
2079this.childMenu.style.left = this.btn.getCoordinates().left - this.childMenu.getCoordinates().width + this.options.tweakSubsequent.x + 'px';
2080
2081if( this.childMenu.getPosition().x < 0){
2082this.options.direction.x = 'right';
2083this.options.direction.xInverse = 'left';
2084this.childMenu.style.left = this.btn.getPosition().x + this.btn.getCoordinates().width + this.options.tweakSubsequent.x + 'px';
2085
2086if(this.options.effect === 'slide' || this.options.effect === 'slide & fade'){
2087this.myEffect.set({ 'margin-left': -this.width, 'opacity': this.options.opacity });
2088}
2089}
2090}else if(this.options.direction.x == 'right'){
2091this.childMenu.style.left = this.btn.getCoordinates().right + this.options.tweakSubsequent.x + 'px';
2092var smRight = this.childMenu.getCoordinates().right;
2093var viewportRightEdge = document.getCoordinates().width + window.getScroll().x;
2094if( smRight > viewportRightEdge ){
2095this.options.direction.x = 'left';
2096this.options.direction.xInverse = 'right';
2097this.childMenu.style.left = this.btn.getCoordinates().left - this.childMenu.getCoordinates().width + this.options.tweakSubsequent.x + 'px'
2098if (this.options.effect === 'slide' || this.options.effect === 'slide & fade') {
2099this.myEffect.set({'margin-left': this.width,'opacity': this.options.opacity});
2100}
2101}
2102}
2103}
2104this.options.onPositionSubMenu_complete(this);
2105}
2106});
2107/*
2108---
2109
2110name: Core
2111
2112description: The core of MooTools, contains all the base functions and the Native and Hash implementations. Required by all the other scripts.
2113
2114license: MIT-style license.
2115
2116copyright: Copyright (c) 2006-2008 [Valerio Proietti](http://mad4milk.net/).
2117
2118authors: The MooTools production team (http://mootools.net/developers/)
2119
2120inspiration:
2121  - Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)
2122  - Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php)
2123
2124provides: [MooTools, Native, Hash.base, Array.each, $util]
2125
2126...
2127*/
2128
2129var MooTools = {
2130'version': '1.2.5',
2131'build': '008d8f0f2fcc2044e54fdd3635341aaab274e757'
2132};
2133
2134var Native = function(options){
2135options = options || {};
2136var name = options.name;
2137var legacy = options.legacy;
2138var protect = options.protect;
2139var methods = options.implement;
2140var generics = options.generics;
2141var initialize = options.initialize;
2142var afterImplement = options.afterImplement || function(){};
2143var object = initialize || legacy;
2144generics = generics !== false;
2145
2146object.constructor = Native;
2147object.$family = {name: 'native'};
2148if (legacy && initialize) object.prototype = legacy.prototype;
2149object.prototype.constructor = object;
2150
2151if (name){
2152var family = name.toLowerCase();
2153object.prototype.$family = {name: family};
2154Native.typize(object, family);
2155}
2156
2157var add = function(obj, name, method, force){
2158if (!protect || force || !obj.prototype[name]) obj.prototype[name] = method;
2159if (generics) Native.genericize(obj, name, protect);
2160afterImplement.call(obj, name, method);
2161return obj;
2162};
2163
2164object.alias = function(a1, a2, a3){
2165if (typeof a1 == 'string'){
2166var pa1 = this.prototype[a1];
2167if ((a1 = pa1)) return add(this, a2, a1, a3);
2168}
2169for (var a in a1) this.alias(a, a1[a], a2);
2170return this;
2171};
2172
2173object.implement = function(a1, a2, a3){
2174if (typeof a1 == 'string') return add(this, a1, a2, a3);
2175for (var p in a1) add(this, p, a1[p], a2);
2176return this;
2177};
2178
2179if (methods) object.implement(methods);
2180
2181return object;
2182};
2183
2184Native.genericize = function(object, property, check){
2185if ((!check || !object[property]) && typeof object.prototype[property] == 'function') object[property] = function(){
2186var args = Array.prototype.slice.call(arguments);
2187return object.prototype[property].apply(args.shift(), args);
2188};
2189};
2190
2191Native.implement = function(objects, properties){
2192for (var i = 0, l = objects.length; i < l; i++) objects[i].implement(properties);
2193};
2194
2195Native.typize = function(object, family){
2196if (!object.type) object.type = function(item){
2197return ($type(item) === family);
2198};
2199};
2200
2201(function(){
2202var natives = {'Array': Array, 'Date': Date, 'Function': Function, 'Number': Number, 'RegExp': RegExp, 'String': String};
2203for (var n in natives) new Native({name: n, initialize: natives[n], protect: true});
2204
2205var types = {'boolean': Boolean, 'native': Native, 'object': Object};
2206for (var t in types) Native.typize(types[t], t);
2207
2208var generics = {
2209'Array': ["concat", "indexOf", "join", "lastIndexOf", "pop", "push", "reverse", "shift", "slice", "sort", "splice", "toString", "unshift", "valueOf"],
2210'String': ["charAt", "charCodeAt", "concat", "indexOf", "lastIndexOf", "match", "replace", "search", "slice", "split", "substr", "substring", "toLowerCase", "toUpperCase", "valueOf"]
2211};
2212for (var g in generics){
2213for (var i = generics[g].length; i--;) Native.genericize(natives[g], generics[g][i], true);
2214}
2215})();
2216
2217var Hash = new Native({
2218
2219name: 'Hash',
2220
2221initialize: function(object){
2222if ($type(object) == 'hash') object = $unlink(object.getClean());
2223for (var key in object) this[key] = object[key];
2224return this;
2225}
2226
2227});
2228
2229Hash.implement({
2230
2231forEach: function(fn, bind){
2232for (var key in this){
2233if (this.hasOwnProperty(key)) fn.call(bind, this[key], key, this);
2234}
2235},
2236
2237getClean: function(){
2238var clean = {};
2239for (var key in this){
2240if (this.hasOwnProperty(key)) clean[key] = this[key];
2241}
2242return clean;
2243},
2244
2245getLength: function(){
2246var length = 0;
2247for (var key in this){
2248if (this.hasOwnProperty(key)) length++;
2249}
2250return length;
2251}
2252
2253});
2254
2255Hash.alias('forEach', 'each');
2256
2257Array.implement({
2258
2259forEach: function(fn, bind){
2260for (var i = 0, l = this.length; i < l; i++) fn.call(bind, this[i], i, this);
2261}
2262
2263});
2264
2265Array.alias('forEach', 'each');
2266
2267function $A(iterable){
2268if (iterable.item){
2269var l = iterable.length, array = new Array(l);
2270while (l--) array[l] = iterable[l];
2271return array;
2272}
2273return Array.prototype.slice.call(iterable);
2274};
2275
2276function $arguments(i){
2277return function(){
2278return arguments[i];
2279};
2280};
2281
2282function $chk(obj){
2283return !!(obj || obj === 0);
2284};
2285
2286function $clear(timer){
2287clearTimeout(timer);
2288clearInterval(timer);
2289return null;
2290};
2291
2292function $defined(obj){
2293return (obj != undefined);
2294};
2295
2296function $each(iterable, fn, bind){
2297var type = $type(iterable);
2298((type == 'arguments' || type == 'collection' || type == 'array') ? Array : Hash).each(iterable, fn, bind);
2299};
2300
2301function $empty(){};
2302
2303function $extend(original, extended){
2304for (var key in (extended || {})) original[key] = extended[key];
2305return original;
2306};
2307
2308function $H(object){
2309return new Hash(object);
2310};
2311
2312function $lambda(value){
2313return ($type(value) == 'function') ? value : function(){
2314return value;
2315};
2316};
2317
2318function $merge(){
2319var args = Array.slice(arguments);
2320args.unshift({});
2321return $mixin.apply(null, args);
2322};
2323
2324function $mixin(mix){
2325for (var i = 1, l = arguments.length; i < l; i++){
2326var object = arguments[i];
2327if ($type(object) != 'object') continue;
2328for (var key in object){
2329var op = object[key], mp = mix[key];
2330mix[key] = (mp && $type(op) == 'object' && $type(mp) == 'object') ? $mixin(mp, op) : $unlink(op);
2331}
2332}
2333return mix;
2334};
2335
2336function $pick(){
2337for (var i = 0, l = arguments.length; i < l; i++){
2338if (arguments[i] != undefined) return arguments[i];
2339}
2340return null;
2341};
2342
2343function $random(min, max){
2344return Math.floor(Math.random() * (max - min + 1) + min);
2345};
2346
2347function $splat(obj){
2348var type = $type(obj);
2349return (type) ? ((type != 'array' && type != 'arguments') ? [obj] : obj) : [];
2350};
2351
2352var $time = Date.now || function(){
2353return +new Date;
2354};
2355
2356function $try(){
2357for (var i = 0, l = arguments.length; i < l; i++){
2358try {
2359return arguments[i]();
2360} catch(e){}
2361}
2362return null;
2363};
2364
2365function $type(obj){
2366if (obj == undefined) return false;
2367if (obj.$family) return (obj.$family.name == 'number' && !isFinite(obj)) ? false : obj.$family.name;
2368if (obj.nodeName){
2369switch (obj.nodeType){
2370case 1: return 'element';
2371case 3: return (/\S/).test(obj.nodeValue) ? 'textnode' : 'whitespace';
2372}
2373} else if (typeof obj.length == 'number'){
2374if (obj.callee) return 'arguments';
2375else if (obj.item) return 'collection';
2376}
2377return typeof obj;
2378};
2379
2380function $unlink(object){
2381var unlinked;
2382switch ($type(object)){
2383case 'object':
2384unlinked = {};
2385for (var p in object) unlinked[p] = $unlink(object[p]);
2386break;
2387case 'hash':
2388unlinked = new Hash(object);
2389break;
2390case 'array':
2391unlinked = [];
2392for (var i = 0, l = object.length; i < l; i++) unlinked[i] = $unlink(object[i]);
2393break;
2394default: return object;
2395}
2396return unlinked;
2397};
2398
2399
2400/*
2401---
2402
2403name: Array
2404
2405description: Contains Array Prototypes like each, contains, and erase.
2406
2407license: MIT-style license.
2408
2409requires: [$util, Array.each]
2410
2411provides: Array
2412
2413...
2414*/
2415
2416Array.implement({
2417
2418every: function(fn, bind){
2419for (var i = 0, l = this.length; i < l; i++){
2420if (!fn.call(bind, this[i], i, this)) return false;
2421}
2422return true;
2423},
2424
2425filter: function(fn, bind){
2426var results = [];
2427for (var i = 0, l = this.length; i < l; i++){
2428if (fn.call(bind, this[i], i, this)) results.push(this[i]);
2429}
2430return results;
2431},
2432
2433clean: function(){
2434return this.filter($defined);
2435},
2436
2437indexOf: function(item, from){
2438var len = this.length;
2439for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++){
2440if (this[i] === item) return i;
2441}
2442return -1;
2443},
2444
2445map: function(fn, bind){
2446var results = [];
2447for (var i = 0, l = this.length; i < l; i++) results[i] = fn.call(bind, this[i], i, this);
2448return results;
2449},
2450
2451some: function(fn, bind){
2452for (var i = 0, l = this.length; i < l; i++){
2453if (fn.call(bind, this[i], i, this)) return true;
2454}
2455return false;
2456},
2457
2458associate: function(keys){
2459var obj = {}, length = Math.min(this.length, keys.length);
2460for (var i = 0; i < length; i++) obj[keys[i]] = this[i];
2461return obj;
2462},
2463
2464link: function(object){
2465var result = {};
2466for (var i = 0, l = this.length; i < l; i++){
2467for (var key in object){
2468if (object[key](this[i])){
2469result[key] = this[i];
2470delete object[key];
2471break;
2472}
2473}
2474}
2475return result;
2476},
2477
2478contains: function(item, from){
2479return this.indexOf(item, from) != -1;
2480},
2481
2482extend: function(array){
2483for (var i = 0, j = array.length; i < j; i++) this.push(array[i]);
2484return this;
2485},
2486
2487getLast: function(){
2488return (this.length) ? this[this.length - 1] : null;
2489},
2490
2491getRandom: function(){
2492return (this.length) ? this[$random(0, this.length - 1)] : null;
2493},
2494
2495include: function(item){
2496if (!this.contains(item)) this.push(item);
2497return this;
2498},
2499
2500combine: function(array){
2501for (var i = 0, l = array.length; i < l; i++) this.include(array[i]);
2502return this;
2503},
2504
2505erase: function(item){
2506for (var i = this.length; i--; i){
2507if (this[i] === item) this.splice(i, 1);
2508}
2509return this;
2510},
2511
2512empty: function(){
2513this.length = 0;
2514return this;
2515},
2516
2517flatten: function(){
2518var array = [];
2519for (var i = 0, l = this.length; i < l; i++){
2520var type = $type(this[i]);
2521if (!type) continue;
2522array = array.concat((type == 'array' || type == 'collection' || type == 'arguments') ? Array.flatten(this[i]) : this[i]);
2523}
2524return array;
2525},
2526
2527hexToRgb: function(array){
2528if (this.length != 3) return null;
2529var rgb = this.map(function(value){
2530if (value.length == 1) value += value;
2531return value.toInt(16);
2532});
2533return (array) ? rgb : 'rgb(' + rgb + ')';
2534},
2535
2536rgbToHex: function(array){
2537if (this.length < 3) return null;
2538if (this.length == 4 && this[3] == 0 && !array) return 'transparent';
2539var hex = [];
2540for (var i = 0; i < 3; i++){
2541var bit = (this[i] - 0).toString(16);
2542hex.push((bit.length == 1) ? '0' + bit : bit);
2543}
2544return (array) ? hex : '#' + hex.join('');
2545}
2546
2547});
2548
2549
2550/*
2551---
2552
2553name: String
2554
2555description: Contains String Prototypes like camelCase, capitalize, test, and toInt.
2556
2557license: MIT-style license.
2558
2559requires: Native
2560
2561provides: String
2562
2563...
2564*/
2565
2566String.implement({
2567
2568test: function(regex, params){
2569return ((typeof regex == 'string') ? new RegExp(regex, params) : regex).test(this);
2570},
2571
2572contains: function(string, separator){
2573return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : this.indexOf(string) > -1;
2574},
2575
2576trim: function(){
2577return this.replace(/^\s+|\s+$/g, '');
2578},
2579
2580clean: function(){
2581return this.replace(/\s+/g, ' ').trim();
2582},
2583
2584camelCase: function(){
2585return this.replace(/-\D/g, function(match){
2586return match.charAt(1).toUpperCase();
2587});
2588},
2589
2590hyphenate: function(){
2591return this.replace(/[A-Z]/g, function(match){
2592return ('-' + match.charAt(0).toLowerCase());
2593});
2594},
2595
2596capitalize: function(){
2597return this.replace(/\b[a-z]/g, function(match){
2598return match.toUpperCase();
2599});
2600},
2601
2602escapeRegExp: function(){
2603return this.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
2604},
2605
2606toInt: function(base){
2607return parseInt(this, base || 10);
2608},
2609
2610toFloat: function(){
2611return parseFloat(this);
2612},
2613
2614hexToRgb: function(array){
2615var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
2616return (hex) ? hex.slice(1).hexToRgb(array) : null;
2617},
2618
2619rgbToHex: function(array){
2620var rgb = this.match(/\d{1,3}/g);
2621return (rgb) ? rgb.rgbToHex(array) : null;
2622},
2623
2624stripScripts: function(option){
2625var scripts = '';
2626var text = this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(){
2627scripts += arguments[1] + '\n';
2628return '';
2629});
2630if (option === true) $exec(scripts);
2631else if ($type(option) == 'function') option(scripts, text);
2632return text;
2633},
2634
2635substitute: function(object, regexp){
2636return this.replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){
2637if (match.charAt(0) == '\\') return match.slice(1);
2638return (object[name] != undefined) ? object[name] : '';
2639});
2640}
2641
2642});
2643
2644
2645/*
2646---
2647
2648name: Function
2649
2650description: Contains Function Prototypes like create, bind, pass, and delay.
2651
2652license: MIT-style license.
2653
2654requires: [Native, $util]
2655
2656provides: Function
2657
2658...
2659*/
2660
2661try {
2662delete Function.prototype.bind;
2663} catch(e){}
2664
2665Function.implement({
2666
2667extend: function(properties){
2668for (var property in properties) this[property] = properties[property];
2669return this;
2670},
2671
2672create: function(options){
2673var self = this;
2674options = options || {};
2675return function(event){
2676var args = options.arguments;
2677args = (args != undefined) ? $splat(args) : Array.slice(arguments, (options.event) ? 1 : 0);
2678if (options.event) args = [event || window.event].extend(args);
2679var returns = function(){
2680return self.apply(options.bind || null, args);
2681};
2682if (options.delay) return setTimeout(returns, options.delay);
2683if (options.periodical) return setInterval(returns, options.periodical);
2684if (options.attempt) return $try(returns);
2685return returns();
2686};
2687},
2688
2689run: function(args, bind){
2690return this.apply(bind, $splat(args));
2691},
2692
2693pass: function(args, bind){
2694return this.create({bind: bind, arguments: args});
2695},
2696
2697bind: function(bind, args){
2698return this.create({bind: bind, arguments: args});
2699},
2700
2701bindWithEvent: function(bind, args){
2702return this.create({bind: bind, arguments: args, event: true});
2703},
2704
2705attempt: function(args, bind){
2706return this.create({bind: bind, arguments: args, attempt: true})();
2707},
2708
2709delay: function(delay, bind, args){
2710return this.create({bind: bind, arguments: args, delay: delay})();
2711},
2712
2713periodical: function(periodical, bind, args){
2714return this.create({bind: bind, arguments: args, periodical: periodical})();
2715}
2716
2717});
2718
2719
2720/*
2721---
2722
2723name: Number
2724
2725description: Contains Number Prototypes like limit, round, times, and ceil.
2726
2727license: MIT-style license.
2728
2729requires: [Native, $util]
2730
2731provides: Number
2732
2733...
2734*/
2735
2736Number.implement({
2737
2738limit: function(min, max){
2739return Math.min(max, Math.max(min, this));
2740},
2741
2742round: function(precision){
2743precision = Math.pow(10, precision || 0);
2744return Math.round(this * precision) / precision;
2745},
2746
2747times: function(fn, bind){
2748for (var i = 0; i < this; i++) fn.call(bind, i, this);
2749},
2750
2751toFloat: function(){
2752return parseFloat(this);
2753},
2754
2755toInt: function(base){
2756return parseInt(this, base || 10);
2757}
2758
2759});
2760
2761Number.alias('times', 'each');
2762
2763(function(math){
2764var methods = {};
2765math.each(function(name){
2766if (!Number[name]) methods[name] = function(){
2767return Math[name].apply(null, [this].concat($A(arguments)));
2768};
2769});
2770Number.implement(methods);
2771})(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']);
2772
2773
2774/*
2775---
2776
2777name: Hash
2778
2779description: Contains Hash Prototypes. Provides a means for overcoming the JavaScript practical impossibility of extending native Objects.
2780
2781license: MIT-style license.
2782
2783requires: Hash.base
2784
2785provides: Hash
2786
2787...
2788*/
2789
2790Hash.implement({
2791
2792has: Object.prototype.hasOwnProperty,
2793
2794keyOf: function(value){
2795for (var key in this){
2796if (this.hasOwnProperty(key) && this[key] === value) return key;
2797}
2798return null;
2799},
2800
2801hasValue: function(value){
2802return (Hash.keyOf(this, value) !== null);
2803},
2804
2805extend: function(properties){
2806Hash.each(properties || {}, function(value, key){
2807Hash.set(this, key, value);
2808}, this);
2809return this;
2810},
2811
2812combine: function(properties){
2813Hash.each(properties || {}, function(value, key){
2814Hash.include(this, key, value);
2815}, this);
2816return this;
2817},
2818
2819erase: function(key){
2820if (this.hasOwnProperty(key)) delete this[key];
2821return this;
2822},
2823
2824get: function(key){
2825return (this.hasOwnProperty(key)) ? this[key] : null;
2826},
2827
2828set: function(key, value){
2829if (!this[key] || this.hasOwnProperty(key)) this[key] = value;
2830return this;
2831},
2832
2833empty: function(){
2834Hash.each(this, function(value, key){
2835delete this[key];
2836}, this);
2837return this;
2838},
2839
2840include: function(key, value){
2841if (this[key] == undefined) this[key] = value;
2842return this;
2843},
2844
2845map: function(fn, bind){
2846var results = new Hash;
2847Hash.each(this, function(value, key){
2848results.set(key, fn.call(bind, value, key, this));
2849}, this);
2850return results;
2851},
2852
2853filter: function(fn, bind){
2854var results = new Hash;
2855Hash.each(this, function(value, key){
2856if (fn.call(bind, value, key, this)) results.set(key, value);
2857}, this);
2858return results;
2859},
2860
2861every: function(fn, bind){
2862for (var key in this){
2863if (this.hasOwnProperty(key) && !fn.call(bind, this[key], key)) return false;
2864}
2865return true;
2866},
2867
2868some: function(fn, bind){
2869for (var key in this){
2870if (this.hasOwnProperty(key) && fn.call(bind, this[key], key)) return true;
2871}
2872return false;
2873},
2874
2875getKeys: function(){
2876var keys = [];
2877Hash.each(this, function(value, key){
2878keys.push(key);
2879});
2880return keys;
2881},
2882
2883getValues: function(){
2884var values = [];
2885Hash.each(this, function(value){
2886values.push(value);
2887});
2888return values;
2889},
2890
2891toQueryString: function(base){
2892var queryString = [];
2893Hash.each(this, function(value, key){
2894if (base) key = base + '[' + key + ']';
2895var result;
2896switch ($type(value)){
2897case 'object': result = Hash.toQueryString(value, key); break;
2898case 'array':
2899var qs = {};
2900value.each(function(val, i){
2901qs[i] = val;
2902});
2903result = Hash.toQueryString(qs, key);
2904break;
2905default: result = key + '=' + encodeURIComponent(value);
2906}
2907if (value != undefined) queryString.push(result);
2908});
2909
2910return queryString.join('&');
2911}
2912
2913});
2914
2915Hash.alias({keyOf: 'indexOf', hasValue: 'contains'});
2916
2917
2918/*
2919---
2920
2921name: Class
2922
2923description: Contains the Class Function for easily creating, extending, and implementing reusable Classes.
2924
2925license: MIT-style license.
2926
2927requires: [$util, Native, Array, String, Function, Number, Hash]
2928
2929provides: Class
2930
2931...
2932*/
2933
2934function Class(params){
2935
2936if (params instanceof Function) params = {initialize: params};
2937
2938var newClass = function(){
2939Object.reset(this);
2940if (newClass._prototyping) return this;
2941this._current = $empty;
2942var value = (this.initialize) ? this.initialize.apply(this, arguments) : this;
2943delete this._current; delete this.caller;
2944return value;
2945}.extend(this);
2946
2947newClass.implement(params);
2948
2949newClass.constructor = Class;
2950newClass.prototype.constructor = newClass;
2951
2952return newClass;
2953
2954};
2955
2956Function.prototype.protect = function(){
2957this._protected = true;
2958return this;
2959};
2960
2961Object.reset = function(object, key){
2962
2963if (key == null){
2964for (var p in object) Object.reset(object, p);
2965return object;
2966}
2967
2968delete object[key];
2969
2970switch ($type(object[key])){
2971case 'object':
2972var F = function(){};
2973F.prototype = object[key];
2974var i = new F;
2975object[key] = Object.reset(i);
2976break;
2977case 'array': object[key] = $unlink(object[key]); break;
2978}
2979
2980return object;
2981
2982};
2983
2984new Native({name: 'Class', initialize: Class}).extend({
2985
2986instantiate: function(F){
2987F._prototyping = true;
2988var proto = new F;
2989delete F._prototyping;
2990return proto;
2991},
2992
2993wrap: function(self, key, method){
2994if (method._origin) method = method._origin;
2995
2996return function(){
2997if (method._protected && this._current == null) throw new Error('The method "' + key + '" cannot be called.');
2998var caller = this.caller, current = this._current;
2999this.caller = current; this._current = arguments.callee;
3000var result = method.apply(this, arguments);
3001this._current = current; this.caller = caller;
3002return result;
3003}.extend({_owner: self, _origin: method, _name: key});
3004
3005}
3006
3007});
3008
3009Class.implement({
3010
3011implement: function(key, value){
3012
3013if ($type(key) == 'object'){
3014for (var p in key) this.implement(p, key[p]);
3015return this;
3016}
3017
3018var mutator = Class.Mutators[key];
3019
3020if (mutator){
3021value = mutator.call(this, value);
3022if (value == null) return this;
3023}
3024
3025var proto = this.prototype;
3026
3027switch ($type(value)){
3028
3029case 'function':
3030if (value._hidden) return this;
3031proto[key] = Class.wrap(this, key, value);
3032break;
3033
3034case 'object':
3035var previous = proto[key];
3036if ($type(previous) == 'object') $mixin(previous, value);
3037else proto[key] = $unlink(value);
3038break;
3039
3040case 'array':
3041proto[key] = $unlink(value);
3042break;
3043
3044default: proto[key] = value;
3045
3046}
3047
3048return this;
3049
3050}
3051
3052});
3053
3054Class.Mutators = {
3055
3056Extends: function(parent){
3057
3058this.parent = parent;
3059this.prototype = Class.instantiate(parent);
3060
3061this.implement('parent', function(){
3062var name = this.caller._name, previous = this.caller._owner.parent.prototype[name];
3063if (!previous) throw new Error('The method "' + name + '" has no parent.');
3064return previous.apply(this, arguments);
3065}.protect());
3066
3067},
3068
3069Implements: function(items){
3070$splat(items).each(function(item){
3071if (item instanceof Function) item = Class.instantiate(item);
3072this.implement(item);
3073}, this);
3074
3075}
3076
3077};
3078
3079
3080/*
3081---
3082
3083name: Class.Extras
3084
3085description: Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks.
3086
3087license: MIT-style license.
3088
3089requires: Class
3090
3091provides: [Chain, Events, Options, Class.Extras]
3092
3093...
3094*/
3095
3096var Chain = new Class({
3097
3098$chain: [],
3099
3100chain: function(){
3101this.$chain.extend(Array.flatten(arguments));
3102return this;
3103},
3104
3105callChain: function(){
3106return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false;
3107},
3108
3109clearChain: function(){
3110this.$chain.empty();
3111return this;
3112}
3113
3114});
3115
3116var Events = new Class({
3117
3118$events: {},
3119
3120addEvent: function(type, fn, internal){
3121type = Events.removeOn(type);
3122if (fn != $empty){
3123this.$events[type] = this.$events[type] || [];
3124this.$events[type].include(fn);
3125if (internal) fn.internal = true;
3126}
3127return this;
3128},
3129
3130addEvents: function(events){
3131for (var type in events) this.addEvent(type, events[type]);
3132return this;
3133},
3134
3135fireEvent: function(type, args, delay){
3136type = Events.removeOn(type);
3137if (!this.$events || !this.$events[type]) return this;
3138this.$events[type].each(function(fn){
3139fn.create({'bind': this, 'delay': delay, 'arguments': args})();
3140}, this);
3141return this;
3142},
3143
3144removeEvent: function(type, fn){
3145type = Events.removeOn(type);
3146if (!this.$events[type]) return this;
3147if (!fn.internal) this.$events[type].erase(fn);
3148return this;
3149},
3150
3151removeEvents: function(events){
3152var type;
3153if ($type(events) == 'object'){
3154for (type in events) this.removeEvent(type, events[type]);
3155return this;
3156}
3157if (events) events = Events.removeOn(events);
3158for (type in this.$events){
3159if (events && events != type) continue;
3160var fns = this.$events[type];
3161for (var i = fns.length; i--; i) this.removeEvent(type, fns[i]);
3162}
3163return this;
3164}
3165
3166});
3167
3168Events.removeOn = function(string){
3169return string.replace(/^on([A-Z])/, function(full, first){
3170return first.toLowerCase();
3171});
3172};
3173
3174var Options = new Class({
3175
3176setOptions: function(){
3177this.options = $merge.run([this.options].extend(arguments));
3178if (!this.addEvent) return this;
3179for (var option in this.options){
3180if ($type(this.options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue;
3181this.addEvent(option, this.options[option]);
3182delete this.options[option];
3183}
3184return this;
3185}
3186
3187});
3188
3189
3190/*
3191---
3192
3193name: Browser
3194
3195description: The Browser Core. Contains Browser initialization, Window and Document, and the Browser Hash.
3196
3197license: MIT-style license.
3198
3199requires: [Native, $util]
3200
3201provides: [Browser, Window, Document, $exec]
3202
3203...
3204*/
3205
3206var Browser = $merge({
3207
3208Engine: {name: 'unknown', version: 0},
3209
3210Platform: {name: (window.orientation != undefined) ? 'ipod' : (navigator.platform.match(/mac|win|linux/i) || ['other'])[0].toLowerCase()},
3211
3212Features: {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)},
3213
3214Plugins: {},
3215
3216Engines: {
3217
3218presto: function(){
3219return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925));
3220},
3221
3222trident: function(){
3223return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4);
3224},
3225
3226webkit: function(){
3227return (navigator.taintEnabled) ? false : ((Browser.Features.xpath) ? ((Browser.Features.query) ? 525 : 420) : 419);
3228},
3229
3230gecko: function(){
3231return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19 : 18);
3232}
3233
3234}
3235
3236}, Browser || {});
3237
3238Browser.Platform[Browser.Platform.name] = true;
3239
3240Browser.detect = function(){
3241
3242for (var engine in this.Engines){
3243var version = this.Engines[engine]();
3244if (version){
3245this.Engine = {name: engine, version: version};
3246this.Engine[engine] = this.Engine[engine + version] = true;
3247break;
3248}
3249}
3250
3251return {name: engine, version: version};
3252
3253};
3254
3255Browser.detect();
3256
3257Browser.Request = function(){
3258return $try(function(){
3259return new XMLHttpRequest();
3260}, function(){
3261return new ActiveXObject('MSXML2.XMLHTTP');
3262}, function(){
3263return new ActiveXObject('Microsoft.XMLHTTP');
3264});
3265};
3266
3267Browser.Features.xhr = !!(Browser.Request());
3268
3269Browser.Plugins.Flash = (function(){
3270var version = ($try(function(){
3271return navigator.plugins['Shockwave Flash'].description;
3272}, function(){
3273return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
3274}) || '0 r0').match(/\d+/g);
3275return {version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0};
3276})();
3277
3278function $exec(text){
3279if (!text) return text;
3280if (window.execScript){
3281window.execScript(text);
3282} else {
3283var script = document.createElement('script');
3284script.setAttribute('type', 'text/javascript');
3285script[(Browser.Engine.webkit && Browser.Engine.version < 420) ? 'innerText' : 'text'] = text;
3286document.head.appendChild(script);
3287document.head.removeChild(script);
3288}
3289return text;
3290};
3291
3292Native.UID = 1;
3293
3294var $uid = (Browser.Engine.trident) ? function(item){
3295return (item.uid || (item.uid = [Native.UID++]))[0];
3296} : function(item){
3297return item.uid || (item.uid = Native.UID++);
3298};
3299
3300var Window = new Native({
3301
3302name: 'Window',
3303
3304legacy: (Browser.Engine.trident) ? null: window.Window,
3305
3306initialize: function(win){
3307$uid(win);
3308if (!win.Element){
3309win.Element = $empty;
3310if (Browser.Engine.webkit) win.document.createElement("iframe"); //fixes safari 2
3311win.Element.prototype = (Browser.Engine.webkit) ? window["[[DOMElement.prototype]]"] : {};
3312}
3313win.document.window = win;
3314return $extend(win, Window.Prototype);
3315},
3316
3317afterImplement: function(property, value){
3318window[property] = Window.Prototype[property] = value;
3319}
3320
3321});
3322
3323Window.Prototype = {$family: {name: 'window'}};
3324
3325new Window(window);
3326
3327var Document = new Native({
3328
3329name: 'Document',
3330
3331legacy: (Browser.Engine.trident) ? null: window.Document,
3332
3333initialize: function(doc){
3334$uid(doc);
3335doc.head = doc.getElementsByTagName('head')[0];
3336doc.html = doc.getElementsByTagName('html')[0];
3337if (Browser.Engine.trident && Browser.Engine.version <= 4) $try(function(){
3338doc.execCommand("BackgroundImageCache", false, true);
3339});
3340if (Browser.Engine.trident) doc.window.attachEvent('onunload', function(){
3341doc.window.detachEvent('onunload', arguments.callee);
3342doc.head = doc.html = doc.window = null;
3343});
3344return $extend(doc, Document.Prototype);
3345},
3346
3347afterImplement: function(property, value){
3348document[property] = Document.Prototype[property] = value;
3349}
3350
3351});
3352
3353Document.Prototype = {$family: {name: 'document'}};
3354
3355new Document(document);
3356
3357
3358/*
3359---
3360
3361name: Element
3362
3363description: One of the most important items in MooTools. Contains the dollar function, the dollars function, and an handful of cross-browser, time-saver methods to let you easily work with HTML Elements.
3364
3365license: MIT-style license.
3366
3367requires: [Window, Document, Array, String, Function, Number, Hash]
3368
3369provides: [Element, Elements, $, $$, Iframe]
3370
3371...
3372*/
3373
3374var Element = new Native({
3375
3376name: 'Element',
3377
3378legacy: window.Element,
3379
3380initialize: function(tag, props){
3381var konstructor = Element.Constructors.get(tag);
3382if (konstructor) return konstructor(props);
3383if (typeof tag == 'string') return document.newElement(tag, props);
3384return document.id(tag).set(props);
3385},
3386
3387afterImplement: function(key, value){
3388Element.Prototype[key] = value;
3389if (Array[key]) return;
3390Elements.implement(key, function(){
3391var items = [], elements = true;
3392for (var i = 0, j = this.length; i < j; i++){
3393var returns = this[i][key].apply(this[i], arguments);
3394items.push(returns);
3395if (elements) elements = ($type(returns) == 'element');
3396}
3397return (elements) ? new Elements(items) : items;
3398});
3399}
3400
3401});
3402
3403Element.Prototype = {$family: {name: 'element'}};
3404
3405Element.Constructors = new Hash;
3406
3407var IFrame = new Native({
3408
3409name: 'IFrame',
3410
3411generics: false,
3412
3413initialize: function(){
3414var params = Array.link(arguments, {properties: Object.type, iframe: $defined});
3415var props = params.properties || {};
3416var iframe = document.id(params.iframe);
3417var onload = props.onload || $empty;
3418delete props.onload;
3419props.id = props.name = $pick(props.id, props.name, iframe ? (iframe.id || iframe.name) : 'IFrame_' + $time());
3420iframe = new Element(iframe || 'iframe', props);
3421var onFrameLoad = function(){
3422var host = $try(function(){
3423return iframe.contentWindow.location.host;
3424});
3425if (!host || host == window.location.host){
3426var win = new Window(iframe.contentWindow);
3427new Document(iframe.contentWindow.document);
3428$extend(win.Element.prototype, Element.Prototype);
3429}
3430onload.call(iframe.contentWindow, iframe.contentWindow.document);
3431};
3432var contentWindow = $try(function(){
3433return iframe.contentWindow;
3434});
3435((contentWindow && contentWindow.document.body) || window.frames[props.id]) ? onFrameLoad() : iframe.addListener('load', onFrameLoad);
3436return iframe;
3437}
3438
3439});
3440
3441var Elements = new Native({
3442
3443initialize: function(elements, options){
3444options = $extend({ddup: true, cash: true}, options);
3445elements = elements || [];
3446if (options.ddup || options.cash){
3447var uniques = {}, returned = [];
3448for (var i = 0, l = elements.length; i < l; i++){
3449var el = document.id(elements[i], !options.cash);
3450if (options.ddup){
3451if (uniques[el.uid]) continue;
3452uniques[el.uid] = true;
3453}
3454if (el) returned.push(el);
3455}
3456elements = returned;
3457}
3458return (options.cash) ? $extend(elements, this) : elements;
3459}
3460
3461});
3462
3463Elements.implement({
3464
3465filter: function(filter, bind){
3466if (!filter) return this;
3467return new Elements(Array.filter(this, (typeof filter == 'string') ? function(item){
3468return item.match(filter);
3469} : filter, bind));
3470}
3471
3472});
3473
3474(function(){
3475
3476/*<ltIE8>*/
3477var createElementAcceptsHTML;
3478try {
3479var x = document.createElement('<input name=x>');
3480createElementAcceptsHTML = (x.name == 'x');
3481} catch(e){}
3482
3483var escapeQuotes = function(html){
3484return ('' + html).replace(/&/g,'&amp;').replace(/"/g,'&quot;');
3485};
3486/*</ltIE8>*/
3487
3488Document.implement({
3489
3490newElement: function(tag, props){
3491if (props && props.checked != null) props.defaultChecked = props.checked;
3492/*<ltIE8>*/// Fix for readonly name and type properties in IE < 8
3493if (createElementAcceptsHTML && props){
3494tag = '<' + tag;
3495if (props.name) tag += ' name="' + escapeQuotes(props.name) + '"';
3496if (props.type) tag += ' type="' + escapeQuotes(props.type) + '"';
3497tag += '>';
3498delete props.name;
3499delete props.type;
3500}
3501/*</ltIE8>*/
3502return this.id(this.createElement(tag)).set(props);
3503},
3504
3505newTextNode: function(text){
3506return this.createTextNode(text);
3507},
3508
3509getDocument: function(){
3510return this;
3511},
3512
3513getWindow: function(){
3514return this.window;
3515},
3516
3517id: (function(){
3518
3519var types = {
3520
3521string: function(id, nocash, doc){
3522id = doc.getElementById(id);
3523return (id) ? types.element(id, nocash) : null;
3524},
3525
3526element: function(el, nocash){
3527$uid(el);
3528if (!nocash && !el.$family && !(/^object|embed$/i).test(el.tagName)){
3529var proto = Element.Prototype;
3530for (var p in proto) el[p] = proto[p];
3531};
3532return el;
3533},
3534
3535object: function(obj, nocash, doc){
3536if (obj.toElement) return types.element(obj.toElement(doc), nocash);
3537return null;
3538}
3539
3540};
3541
3542types.textnode = types.whitespace = types.window = types.document = $arguments(0);
3543
3544return function(el, nocash, doc){
3545if (el && el.$family && el.uid) return el;
3546var type = $type(el);
3547return (types[type]) ? types[type](el, nocash, doc || document) : null;
3548};
3549
3550})()
3551
3552});
3553
3554})();
3555
3556if (window.$ == null) Window.implement({
3557$: function(el, nc){
3558return document.id(el, nc, this.document);
3559}
3560});
3561
3562Window.implement({
3563
3564$$: function(selector){
3565if (arguments.length == 1 && typeof selector == 'string') return this.document.getElements(selector);
3566var elements = [];
3567var args = Array.flatten(arguments);
3568for (var i = 0, l = args.length; i < l; i++){
3569var item = args[i];
3570switch ($type(item)){
3571case 'element': elements.push(item); break;
3572case 'string': elements.extend(this.document.getElements(item, true));
3573}
3574}
3575return new Elements(elements);
3576},
3577
3578getDocument: function(){
3579return this.document;
3580},
3581
3582getWindow: function(){
3583return this;
3584}
3585
3586});
3587
3588Native.implement([Element, Document], {
3589
3590getElement: function(selector, nocash){
3591return document.id(this.getElements(selector, true)[0] || null, nocash);
3592},
3593
3594getElements: function(tags, nocash){
3595tags = tags.split(',');
3596var elements = [];
3597var ddup = (tags.length > 1);
3598tags.each(function(tag){
3599var partial = this.getElementsByTagName(tag.trim());
3600(ddup) ? elements.extend(partial) : elements = partial;
3601}, this);
3602return new Elements(elements, {ddup: ddup, cash: !nocash});
3603}
3604
3605});
3606
3607(function(){
3608
3609var collected = {}, storage = {};
3610var props = {input: 'checked', option: 'selected', textarea: (Browser.Engine.webkit && Browser.Engine.version < 420) ? 'innerHTML' : 'value'};
3611
3612var get = function(uid){
3613return (storage[uid] || (storage[uid] = {}));
3614};
3615
3616var clean = function(item, retain){
3617if (!item) return;
3618var uid = item.uid;
3619if (retain !== true) retain = false;
3620if (Browser.Engine.trident){
3621if (item.clearAttributes){
3622var clone = retain && item.cloneNode(false);
3623item.clearAttributes();
3624if (clone) item.mergeAttributes(clone);
3625} else if (item.removeEvents){
3626item.removeEvents();
3627}
3628if ((/object/i).test(item.tagName)){
3629for (var p in item){
3630if (typeof item[p] == 'function') item[p] = $empty;
3631}
3632Element.dispose(item);
3633}
3634}
3635if (!uid) return;
3636collected[uid] = storage[uid] = null;
3637};
3638
3639var purge = function(){
3640Hash.each(collected, clean);
3641if (Browser.Engine.trident) $A(document.getElementsByTagName('object')).each(clean);
3642if (window.CollectGarbage) CollectGarbage();
3643collected = storage = null;
3644};
3645
3646var walk = function(element, walk, start, match, all, nocash){
3647var el = element[start || walk];
3648var elements = [];
3649while (el){
3650if (el.nodeType == 1 && (!match || Element.match(el, match))){
3651if (!all) return document.id(el, nocash);
3652elements.push(el);
3653}
3654el = el[walk];
3655}
3656return (all) ? new Elements(elements, {ddup: false, cash: !nocash}) : null;
3657};
3658
3659var attributes = {
3660'html': 'innerHTML',
3661'class': 'className',
3662'for': 'htmlFor',
3663'defaultValue': 'defaultValue',
3664'text': (Browser.Engine.trident || (Browser.Engine.webkit && Browser.Engine.version < 420)) ? 'innerText' : 'textContent'
3665};
3666var bools = ['compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked', 'disabled', 'readonly', 'multiple', 'selected', 'noresize', 'defer'];
3667var camels = ['value', 'type', 'defaultValue', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan', 'frameBorder', 'maxLength', 'readOnly', 'rowSpan', 'tabIndex', 'useMap'];
3668
3669bools = bools.associate(bools);
3670
3671Hash.extend(attributes, bools);
3672Hash.extend(attributes, camels.associate(camels.map(String.toLowerCase)));
3673
3674var inserters = {
3675
3676before: function(context, element){
3677if (element.parentNode) element.parentNode.insertBefore(context, element);
3678},
3679
3680after: function(context, element){
3681if (!element.parentNode) return;
3682var next = element.nextSibling;
3683(next) ? element.parentNode.insertBefore(context, next) : element.parentNode.appendChild(context);
3684},
3685
3686bottom: function(context, element){
3687element.appendChild(context);
3688},
3689
3690top: function(context, element){
3691var first = element.firstChild;
3692(first) ? element.insertBefore(context, first) : element.appendChild(context);
3693}
3694
3695};
3696
3697inserters.inside = inserters.bottom;
3698
3699Hash.each(inserters, function(inserter, where){
3700
3701where = where.capitalize();
3702
3703Element.implement('inject' + where, function(el){
3704inserter(this, document.id(el, true));
3705return this;
3706});
3707
3708Element.implement('grab' + where, function(el){
3709inserter(document.id(el, true), this);
3710return this;
3711});
3712
3713});
3714
3715Element.implement({
3716
3717set: function(prop, value){
3718switch ($type(prop)){
3719case 'object':
3720for (var p in prop) this.set(p, prop[p]);
3721break;
3722case 'string':
3723var property = Element.Properties.get(prop);
3724(property && property.set) ? property.set.apply(this, Array.slice(arguments, 1)) : this.setProperty(prop, value);
3725}
3726return this;
3727},
3728
3729get: function(prop){
3730var property = Element.Properties.get(prop);
3731return (property && property.get) ? property.get.apply(this, Array.slice(arguments, 1)) : this.getProperty(prop);
3732},
3733
3734erase: function(prop){
3735var property = Element.Properties.get(prop);
3736(property && property.erase) ? property.erase.apply(this) : this.removeProperty(prop);
3737return this;
3738},
3739
3740setProperty: function(attribute, value){
3741var key = attributes[attribute];
3742if (value == undefined) return this.removeProperty(attribute);
3743if (key && bools[attribute]) value = !!value;
3744(key) ? this[key] = value : this.setAttribute(attribute, '' + value);
3745return this;
3746},
3747
3748setProperties: function(attributes){
3749for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]);
3750return this;
3751},
3752
3753getProperty: function(attribute){
3754var key = attributes[attribute];
3755var value = (key) ? this[key] : this.getAttribute(attribute, 2);
3756return (bools[attribute]) ? !!value : (key) ? value : value || null;
3757},
3758
3759getProperties: function(){
3760var args = $A(arguments);
3761return args.map(this.getProperty, this).associate(args);
3762},
3763
3764removeProperty: function(attribute){
3765var key = attributes[attribute];
3766(key) ? this[key] = (key && bools[attribute]) ? false : '' : this.removeAttribute(attribute);
3767return this;
3768},
3769
3770removeProperties: function(){
3771Array.each(arguments, this.removeProperty, this);
3772return this;
3773},
3774
3775hasClass: function(className){
3776return this.className.contains(className, ' ');
3777},
3778
3779addClass: function(className){
3780if (!this.hasClass(className)) this.className = (this.className + ' ' + className).clean();
3781return this;
3782},
3783
3784removeClass: function(className){
3785this.className = this.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)'), '$1');
3786return this;
3787},
3788
3789toggleClass: function(className){
3790return this.hasClass(className) ? this.removeClass(className) : this.addClass(className);
3791},
3792
3793adopt: function(){
3794Array.flatten(arguments).each(function(element){
3795element = document.id(element, true);
3796if (element) this.appendChild(element);
3797}, this);
3798return this;
3799},
3800
3801appendText: function(text, where){
3802return this.grab(this.getDocument().newTextNode(text), where);
3803},
3804
3805grab: function(el, where){
3806inserters[where || 'bottom'](document.id(el, true), this);
3807return this;
3808},
3809
3810inject: function(el, where){
3811inserters[where || 'bottom'](this, document.id(el, true));
3812return this;
3813},
3814
3815replaces: function(el){
3816el = document.id(el, true);
3817el.parentNode.replaceChild(this, el);
3818return this;
3819},
3820
3821wraps: function(el, where){
3822el = document.id(el, true);
3823return this.replaces(el).grab(el, where);
3824},
3825
3826getPrevious: function(match, nocash){
3827return walk(this, 'previousSibling', null, match, false, nocash);
3828},
3829
3830getAllPrevious: function(match, nocash){
3831return walk(this, 'previousSibling', null, match, true, nocash);
3832},
3833
3834getNext: function(match, nocash){
3835return walk(this, 'nextSibling', null, match, false, nocash);
3836},
3837
3838getAllNext: function(match, nocash){
3839return walk(this, 'nextSibling', null, match, true, nocash);
3840},
3841
3842getFirst: function(match, nocash){
3843return walk(this, 'nextSibling', 'firstChild', match, false, nocash);
3844},
3845
3846getLast: function(match, nocash){
3847return walk(this, 'previousSibling', 'lastChild', match, false, nocash);
3848},
3849
3850getParent: function(match, nocash){
3851return walk(this, 'parentNode', null, match, false, nocash);
3852},
3853
3854getParents: function(match, nocash){
3855return walk(this, 'parentNode', null, match, true, nocash);
3856},
3857
3858getSiblings: function(match, nocash){
3859return this.getParent().getChildren(match, nocash).erase(this);
3860},
3861
3862getChildren: function(match, nocash){
3863return walk(this, 'nextSibling', 'firstChild', match, true, nocash);
3864},
3865
3866getWindow: function(){
3867return this.ownerDocument.window;
3868},
3869
3870getDocument: function(){
3871return this.ownerDocument;
3872},
3873
3874getElementById: function(id, nocash){
3875var el = this.ownerDocument.getElementById(id);
3876if (!el) return null;
3877for (var parent = el.parentNode; parent != this; parent = parent.parentNode){
3878if (!parent) return null;
3879}
3880return document.id(el, nocash);
3881},
3882
3883getSelected: function(){
3884return new Elements($A(this.options).filter(function(option){
3885return option.selected;
3886}));
3887},
3888
3889getComputedStyle: function(property){
3890if (this.currentStyle) return this.currentStyle[property.camelCase()];
3891var computed = this.getDocument().defaultView.getComputedStyle(this, null);
3892return (computed) ? computed.getPropertyValue([property.hyphenate()]) : null;
3893},
3894
3895toQueryString: function(){
3896var queryString = [];
3897this.getElements('input, select, textarea', true).each(function(el){
3898if (!el.name || el.disabled || el.type == 'submit' || el.type == 'reset' || el.type == 'file') return;
3899var value = (el.tagName.toLowerCase() == 'select') ? Element.getSelected(el).map(function(opt){
3900return opt.value;
3901}) : ((el.type == 'radio' || el.type == 'checkbox') && !el.checked) ? null : el.value;
3902$splat(value).each(function(val){
3903if (typeof val != 'undefined') queryString.push(el.name + '=' + encodeURIComponent(val));
3904});
3905});
3906return queryString.join('&');
3907},
3908
3909clone: function(contents, keepid){
3910contents = contents !== false;
3911var clone = this.cloneNode(contents);
3912var clean = function(node, element){
3913if (!keepid) node.removeAttribute('id');
3914if (Browser.Engine.trident){
3915node.clearAttributes();
3916node.mergeAttributes(element);
3917node.removeAttribute('uid');
3918if (node.options){
3919var no = node.options, eo = element.options;
3920for (var j = no.length; j--;) no[j].selected = eo[j].selected;
3921}
3922}
3923var prop = props[element.tagName.toLowerCase()];
3924if (prop && element[prop]) node[prop] = element[prop];
3925};
3926
3927if (contents){
3928var ce = clone.getElementsByTagName('*'), te = this.getElementsByTagName('*');
3929for (var i = ce.length; i--;) clean(ce[i], te[i]);
3930}
3931
3932clean(clone, this);
3933return document.id(clone);
3934},
3935
3936destroy: function(){
3937Element.empty(this);
3938Element.dispose(this);
3939clean(this, true);
3940return null;
3941},
3942
3943empty: function(){
3944$A(this.childNodes).each(function(node){
3945Element.destroy(node);
3946});
3947return this;
3948},
3949
3950dispose: function(){
3951return (this.parentNode) ? this.parentNode.removeChild(this) : this;
3952},
3953
3954hasChild: function(el){
3955el = document.id(el, true);
3956if (!el) return false;
3957if (Browser.Engine.webkit && Browser.Engine.version < 420) return $A(this.getElementsByTagName(el.tagName)).contains(el);
3958return (this.contains) ? (this != el && this.contains(el)) : !!(this.compareDocumentPosition(el) & 16);
3959},
3960
3961match: function(tag){
3962return (!tag || (tag == this) || (Element.get(this, 'tag') == tag));
3963}
3964
3965});
3966
3967Native.implement([Element, Window, Document], {
3968
3969addListener: function(type, fn){
3970if (type == 'unload'){
3971var old = fn, self = this;
3972fn = function(){
3973self.removeListener('unload', fn);
3974old();
3975};
3976} else {
3977collected[this.uid] = this;
3978}
3979if (this.addEventListener) this.addEventListener(type, fn, false);
3980else this.attachEvent('on' + type, fn);
3981return this;
3982},
3983
3984removeListener: function(type, fn){
3985if (this.removeEventListener) this.removeEventListener(type, fn, false);
3986else this.detachEvent('on' + type, fn);
3987return this;
3988},
3989
3990retrieve: function(property, dflt){
3991var storage = get(this.uid), prop = storage[property];
3992if (dflt != undefined && prop == undefined) prop = storage[property] = dflt;
3993return $pick(prop);
3994},
3995
3996store: function(property, value){
3997var storage = get(this.uid);
3998storage[property] = value;
3999return this;
4000},
4001
4002eliminate: function(property){
4003var storage = get(this.uid);
4004delete storage[property];
4005return this;
4006}
4007
4008});
4009
4010window.addListener('unload', purge);
4011
4012})();
4013
4014Element.Properties = new Hash;
4015
4016Element.Properties.style = {
4017
4018set: function(style){
4019this.style.cssText = style;
4020},
4021
4022get: function(){
4023return this.style.cssText;
4024},
4025
4026erase: function(){
4027this.style.cssText = '';
4028}
4029
4030};
4031
4032Element.Properties.tag = {
4033
4034get: function(){
4035return this.tagName.toLowerCase();
4036}
4037
4038};
4039
4040Element.Properties.html = (function(){
4041var wrapper = document.createElement('div');
4042
4043var translations = {
4044table: [1, '<table>', '</table>'],
4045select: [1, '<select>', '</select>'],
4046tbody: [2, '<table><tbody>', '</tbody></table>'],
4047tr: [3, '<table><tbody><tr>', '</tr></tbody></table>']
4048};
4049translations.thead = translations.tfoot = translations.tbody;
4050
4051var html = {
4052set: function(){
4053var html = Array.flatten(arguments).join('');
4054var wrap = Browser.Engine.trident && translations[this.get('tag')];
4055if (wrap){
4056var first = wrapper;
4057first.innerHTML = wrap[1] + html + wrap[2];
4058for (var i = wrap[0]; i--;) first = first.firstChild;
4059this.empty().adopt(first.childNodes);
4060} else {
4061this.innerHTML = html;
4062}
4063}
4064};
4065
4066html.erase = html.set;
4067
4068return html;
4069})();
4070
4071if (Browser.Engine.webkit && Browser.Engine.version < 420) Element.Properties.text = {
4072get: function(){
4073if (this.innerText) return this.innerText;
4074var temp = this.ownerDocument.newElement('div', {html: this.innerHTML}).inject(this.ownerDocument.body);
4075var text = temp.innerText;
4076temp.destroy();
4077return text;
4078}
4079};
4080
4081
4082/*
4083---
4084
4085name: Element.Dimensions
4086
4087description: Contains methods to work with size, scroll, or positioning of Elements and the window object.
4088
4089license: MIT-style license.
4090
4091credits:
4092  - Element positioning based on the [qooxdoo](http://qooxdoo.org/) code and smart browser fixes, [LGPL License](http://www.gnu.org/licenses/lgpl.html).
4093  - Viewport dimensions based on [YUI](http://developer.yahoo.com/yui/) code, [BSD License](http://developer.yahoo.com/yui/license.html).
4094
4095requires: Element
4096
4097provides: Element.Dimensions
4098
4099...
4100*/
4101
4102(function(){
4103
4104Element.implement({
4105
4106scrollTo: function(x, y){
4107if (isBody(this)){
4108this.getWindow().scrollTo(x, y);
4109} else {
4110this.scrollLeft = x;
4111this.scrollTop = y;
4112}
4113return this;
4114},
4115
4116getSize: function(){
4117if (isBody(this)) return this.getWindow().getSize();
4118return {x: this.offsetWidth, y: this.offsetHeight};
4119},
4120
4121getScrollSize: function(){
4122if (isBody(this)) return this.getWindow().getScrollSize();
4123return {x: this.scrollWidth, y: this.scrollHeight};
4124},
4125
4126getScroll: function(){
4127if (isBody(this)) return this.getWindow().getScroll();
4128return {x: this.scrollLeft, y: this.scrollTop};
4129},
4130
4131getScrolls: function(){
4132var element = this, position = {x: 0, y: 0};
4133while (element && !isBody(element)){
4134position.x += element.scrollLeft;
4135position.y += element.scrollTop;
4136element = element.parentNode;
4137}
4138return position;
4139},
4140
4141getOffsetParent: function(){
4142var element = this;
4143if (isBody(element)) return null;
4144if (!Browser.Engine.trident) return element.offsetParent;
4145while ((element = element.parentNode) && !isBody(element)){
4146if (styleString(element, 'position') != 'static') return element;
4147}
4148return null;
4149},
4150
4151getOffsets: function(){
4152if (this.getBoundingClientRect){
4153var bound = this.getBoundingClientRect(),
4154html = document.id(this.getDocument().documentElement),
4155htmlScroll = html.getScroll(),
4156elemScrolls = this.getScrolls(),
4157elemScroll = this.getScroll(),
4158isFixed = (styleString(this, 'position') == 'fixed');
4159
4160return {
4161x: bound.left.toInt() + elemScrolls.x - elemScroll.x + ((isFixed) ? 0 : htmlScroll.x) - html.clientLeft,
4162y: bound.top.toInt()  + elemScrolls.y - elemScroll.y + ((isFixed) ? 0 : htmlScroll.y) - html.clientTop
4163};
4164}
4165
4166var element = this, position = {x: 0, y: 0};
4167if (isBody(this)) return position;
4168
4169while (element && !isBody(element)){
4170position.x += element.offsetLeft;
4171position.y += element.offsetTop;
4172
4173if (Browser.Engine.gecko){
4174if (!borderBox(element)){
4175position.x += leftBorder(element);
4176position.y += topBorder(element);
4177}
4178var parent = element.parentNode;
4179if (parent && styleString(parent, 'overflow') != 'visible'){
4180position.x += leftBorder(parent);
4181position.y += topBorder(parent);
4182}
4183} else if (element != this && Browser.Engine.webkit){
4184position.x += leftBorder(element);
4185position.y += topBorder(element);
4186}
4187
4188element = element.offsetParent;
4189}
4190if (Browser.Engine.gecko && !borderBox(this)){
4191position.x -= leftBorder(this);
4192position.y -= topBorder(this);
4193}
4194return position;
4195},
4196
4197getPosition: function(relative){
4198if (isBody(this)) return {x: 0, y: 0};
4199var offset = this.getOffsets(),
4200scroll = this.getScrolls();
4201var position = {
4202x: offset.x - scroll.x,
4203y: offset.y - scroll.y
4204};
4205var relativePosition = (relative && (relative = document.id(relative))) ? relative.getPosition() : {x: 0, y: 0};
4206return {x: position.x - relativePosition.x, y: position.y - relativePosition.y};
4207},
4208
4209getCoordinates: function(element){
4210if (isBody(this)) return this.getWindow().getCoordinates();
4211var position = this.getPosition(element),
4212size = this.getSize();
4213var obj = {
4214left: position.x,
4215top: position.y,
4216width: size.x,
4217height: size.y
4218};
4219obj.right = obj.left + obj.width;
4220obj.bottom = obj.top + obj.height;
4221return obj;
4222},
4223
4224computePosition: function(obj){
4225return {
4226left: obj.x - styleNumber(this, 'margin-left'),
4227top: obj.y - styleNumber(this, 'margin-top')
4228};
4229},
4230
4231setPosition: function(obj){
4232return this.setStyles(this.computePosition(obj));
4233}
4234
4235});
4236
4237
4238Native.implement([Document, Window], {
4239
4240getSize: function(){
4241if (Browser.Engine.presto || Browser.Engine.webkit){
4242var win = this.getWindow();
4243return {x: win.innerWidth, y: win.innerHeight};
4244}
4245var doc = getCompatElement(this);
4246return {x: doc.clientWidth, y: doc.clientHeight};
4247},
4248
4249getScroll: function(){
4250var win = this.getWindow(), doc = getCompatElement(this);
4251return {x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop};
4252},
4253
4254getScrollSize: function(){
4255var doc = getCompatElement(this), min = this.getSize();
4256return {x: Math.max(doc.scrollWidth, min.x), y: Math.max(doc.scrollHeight, min.y)};
4257},
4258
4259getPosition: function(){
4260return {x: 0, y: 0};
4261},
4262
4263getCoordinates: function(){
4264var size = this.getSize();
4265return {top: 0, left: 0, bottom: size.y, right: size.x, height: size.y, width: size.x};
4266}
4267
4268});
4269
4270// private methods
4271
4272var styleString = Element.getComputedStyle;
4273
4274function styleNumber(element, style){
4275return styleString(element, style).toInt() || 0;
4276};
4277
4278function borderBox(element){
4279return styleString(element, '-moz-box-sizing') == 'border-box';
4280};
4281
4282function topBorder(element){
4283return styleNumber(element, 'border-top-width');
4284};
4285
4286function leftBorder(element){
4287return styleNumber(element, 'border-left-width');
4288};
4289
4290function isBody(element){
4291return (/^(?:body|html)$/i).test(element.tagName);
4292};
4293
4294function getCompatElement(element){
4295var doc = element.getDocument();
4296return (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
4297};
4298
4299})();
4300
4301//aliases
4302Element.alias('setPosition', 'position'); //compatability
4303
4304Native.implement([Window, Document, Element], {
4305
4306getHeight: function(){
4307return this.getSize().y;
4308},
4309
4310getWidth: function(){
4311return this.getSize().x;
4312},
4313
4314getScrollTop: function(){
4315return this.getScroll().y;
4316},
4317
4318getScrollLeft: function(){
4319return this.getScroll().x;
4320},
4321
4322getScrollHeight: function(){
4323return this.getScrollSize().y;
4324},
4325
4326getScrollWidth: function(){
4327return this.getScrollSize().x;
4328},
4329
4330getTop: function(){
4331return this.getPosition().y;
4332},
4333
4334getLeft: function(){
4335return this.getPosition().x;
4336}
4337
4338});
4339
4340
4341/*
4342---
4343
4344name: Event
4345
4346description: Contains the Event Class, to make the event object cross-browser.
4347
4348license: MIT-style license.
4349
4350requires: [Window, Document, Hash, Array, Function, String]
4351
4352provides: Event
4353
4354...
4355*/
4356
4357var Event = new Native({
4358
4359name: 'Event',
4360
4361initialize: function(event, win){
4362win = win || window;
4363var doc = win.document;
4364event = event || win.event;
4365if (event.$extended) return event;
4366this.$extended = true;
4367var type = event.type;
4368var target = event.target || event.srcElement;
4369while (target && target.nodeType == 3) target = target.parentNode;
4370
4371if (type.test(/key/)){
4372var code = event.which || event.keyCode;
4373var key = Event.Keys.keyOf(code);
4374if (type == 'keydown'){
4375var fKey = code - 111;
4376if (fKey > 0 && fKey < 13) key = 'f' + fKey;
4377}
4378key = key || String.fromCharCode(code).toLowerCase();
4379} else if (type.match(/(click|mouse|menu)/i)){
4380doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
4381var page = {
4382x: event.pageX || event.clientX + doc.scrollLeft,
4383y: event.pageY || event.clientY + doc.scrollTop
4384};
4385var client = {
4386x: (event.pageX) ? event.pageX - win.pageXOffset : event.clientX,
4387y: (event.pageY) ? event.pageY - win.pageYOffset : event.clientY
4388};
4389if (type.match(/DOMMouseScroll|mousewheel/)){
4390var wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
4391}
4392var rightClick = (event.which == 3) || (event.button == 2);
4393var related = null;
4394if (type.match(/over|out/)){
4395switch (type){
4396case 'mouseover': related = event.relatedTarget || event.fromElement; break;
4397case 'mouseout': related = event.relatedTarget || event.toElement;
4398}
4399if (!(function(){
4400while (related && related.nodeType == 3) related = related.parentNode;
4401return true;
4402}).create({attempt: Browser.Engine.gecko})()) related = false;
4403}
4404}
4405
4406return $extend(this, {
4407event: event,
4408type: type,
4409
4410page: page,
4411client: client,
4412rightClick: rightClick,
4413
4414wheel: wheel,
4415
4416relatedTarget: related,
4417target: target,
4418
4419code: code,
4420key: key,
4421
4422shift: event.shiftKey,
4423control: event.ctrlKey,
4424alt: event.altKey,
4425meta: event.metaKey
4426});
4427}
4428
4429});
4430
4431Event.Keys = new Hash({
4432'enter': 13,
4433'up': 38,
4434'down': 40,
4435'left': 37,
4436'right': 39,
4437'esc': 27,
4438'space': 32,
4439'backspace': 8,
4440'tab': 9,
4441'delete': 46
4442});
4443
4444Event.implement({
4445
4446stop: function(){
4447return this.stopPropagation().preventDefault();
4448},
4449
4450stopPropagation: function(){
4451if (this.event.stopPropagation) this.event.stopPropagation();
4452else this.event.cancelBubble = true;
4453return this;
4454},
4455
4456preventDefault: function(){
4457if (this.event.preventDefault) this.event.preventDefault();
4458else this.event.returnValue = false;
4459return this;
4460}
4461
4462});
4463
4464
4465/*
4466---
4467
4468name: Element.Event
4469
4470description: Contains Element methods for dealing with events. This file also includes mouseenter and mouseleave custom Element Events.
4471
4472license: MIT-style license.
4473
4474requires: [Element, Event]
4475
4476provides: Element.Event
4477
4478...
4479*/
4480
4481Element.Properties.events = {set: function(events){
4482this.addEvents(events);
4483}};
4484
4485Native.implement([Element, Window, Document], {
4486
4487addEvent: function(type, fn){
4488var events = this.retrieve('events', {});
4489events[type] = events[type] || {'keys': [], 'values': []};
4490if (events[type].keys.contains(fn)) return this;
4491events[type].keys.push(fn);
4492var realType = type, custom = Element.Events.get(type), condition = fn, self = this;
4493if (custom){
4494if (custom.onAdd) custom.onAdd.call(this, fn);
4495if (custom.condition){
4496condition = function(event){
4497if (custom.condition.call(this, event)) return fn.call(this, event);
4498return true;
4499};
4500}
4501realType = custom.base || realType;
4502}
4503var defn = function(){
4504return fn.call(self);
4505};
4506var nativeEvent = Element.NativeEvents[realType];
4507if (nativeEvent){
4508if (nativeEvent == 2){
4509defn = function(event){
4510event = new Event(event, self.getWindow());
4511if (condition.call(self, event) === false) event.stop();
4512};
4513}
4514this.addListener(realType, defn);
4515}
4516events[type].values.push(defn);
4517return this;
4518},
4519
4520removeEvent: function(type, fn){
4521var events = this.retrieve('events');
4522if (!events || !events[type]) return this;
4523var pos = events[type].keys.indexOf(fn);
4524if (pos == -1) return this;
4525events[type].keys.splice(pos, 1);
4526var value = events[type].values.splice(pos, 1)[0];
4527var custom = Element.Events.get(type);
4528if (custom){
4529if (custom.onRemove) custom.onRemove.call(this, fn);
4530type = custom.base || type;
4531}
4532return (Element.NativeEvents[type]) ? this.removeListener(type, value) : this;
4533},
4534
4535addEvents: function(events){
4536for (var event in events) this.addEvent(event, events[event]);
4537return this;
4538},
4539
4540removeEvents: function(events){
4541var type;
4542if ($type(events) == 'object'){
4543for (type in events) this.removeEvent(type, events[type]);
4544return this;
4545}
4546var attached = this.retrieve('events');
4547if (!attached) return this;
4548if (!events){
4549for (type in attached) this.removeEvents(type);
4550this.eliminate('events');
4551} else if (attached[events]){
4552while (attached[events].keys[0]) this.removeEvent(events, attached[events].keys[0]);
4553attached[events] = null;
4554}
4555return this;
4556},
4557
4558fireEvent: function(type, args, delay){
4559var events = this.retrieve('events');
4560if (!events || !events[type]) return this;
4561events[type].keys.each(function(fn){
4562fn.create({'bind': this, 'delay': delay, 'arguments': args})();
4563}, this);
4564return this;
4565},
4566
4567cloneEvents: function(from, type){
4568from = document.id(from);
4569var fevents = from.retrieve('events');
4570if (!fevents) return this;
4571if (!type){
4572for (var evType in fevents) this.cloneEvents(from, evType);
4573} else if (fevents[type]){
4574fevents[type].keys.each(function(fn){
4575this.addEvent(type, fn);
4576}, this);
4577}
4578return this;
4579}
4580
4581});
4582
4583// IE9
4584try {
4585if (typeof HTMLElement != 'undefined')
4586HTMLElement.prototype.fireEvent = Element.prototype.fireEvent;
4587} catch(e){}
4588
4589Element.NativeEvents = {
4590click: 2, dblclick: 2, mouseup: 2, mousedown: 2, contextmenu: 2, //mouse buttons
4591mousewheel: 2, DOMMouseScroll: 2, //mouse wheel
4592mouseover: 2, mouseout: 2, mousemove: 2, selectstart: 2, selectend: 2, //mouse movement
4593keydown: 2, keypress: 2, keyup: 2, //keyboard
4594focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, //form elements
4595load: 1, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1, readystatechange: 1, //window
4596error: 1, abort: 1, scroll: 1 //misc
4597};
4598
4599(function(){
4600
4601var $check = function(event){
4602var related = event.relatedTarget;
4603if (related == undefined) return true;
4604if (related === false) return false;
4605return ($type(this) != 'document' && related != this && related.prefix != 'xul' && !this.hasChild(related));
4606};
4607
4608Element.Events = new Hash({
4609
4610mouseenter: {
4611base: 'mouseover',
4612condition: $check
4613},
4614
4615mouseleave: {
4616base: 'mouseout',
4617condition: $check
4618},
4619
4620mousewheel: {
4621base: (Browser.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel'
4622}
4623
4624});
4625
4626})();
4627
4628
4629/*
4630---
4631
4632name: Element.Style
4633
4634description: Contains methods for interacting with the styles of Elements in a fashionable way.
4635
4636license: MIT-style license.
4637
4638requires: Element
4639
4640provides: Element.Style
4641
4642...
4643*/
4644
4645Element.Properties.styles = {set: function(styles){
4646this.setStyles(styles);
4647}};
4648
4649Element.Properties.opacity = {
4650
4651set: function(opacity, novisibility){
4652if (!novisibility){
4653if (opacity == 0){
4654if (this.style.visibility != 'hidden') this.style.visibility = 'hidden';
4655} else {
4656if (this.style.visibility != 'visible') this.style.visibility = 'visible';
4657}
4658}
4659if (!this.currentStyle || !this.currentStyle.hasLayout) this.style.zoom = 1;
4660if (Browser.Engine.trident) this.style.filter = (opacity == 1) ? '' : 'alpha(opacity=' + opacity * 100 + ')';
4661this.style.opacity = opacity;
4662this.store('opacity', opacity);
4663},
4664
4665get: function(){
4666return this.retrieve('opacity', 1);
4667}
4668
4669};
4670
4671Element.implement({
4672
4673setOpacity: function(value){
4674return this.set('opacity', value, true);
4675},
4676
4677getOpacity: function(){
4678return this.get('opacity');
4679},
4680
4681setStyle: function(property, value){
4682switch (property){
4683case 'opacity': return this.set('opacity', parseFloat(value));
4684case 'float': property = (Browser.Engine.trident) ? 'styleFloat' : 'cssFloat';
4685}
4686property = property.camelCase();
4687if ($type(value) != 'string'){
4688var map = (Element.Styles.get(property) || '@').split(' ');
4689value = $splat(value).map(function(val, i){
4690if (!map[i]) return '';
4691return ($type(val) == 'number') ? map[i].replace('@', Math.round(val)) : val;
4692}).join(' ');
4693} else if (value == String(Number(value))){
4694value = Math.round(value);
4695}
4696this.style[property] = value;
4697return this;
4698},
4699
4700getStyle: function(property){
4701switch (property){
4702case 'opacity': return this.get('opacity');
4703case 'float': property = (Browser.Engine.trident) ? 'styleFloat' : 'cssFloat';
4704}
4705property = property.camelCase();
4706var result = this.style[property];
4707if (!$chk(result)){
4708result = [];
4709for (var style in Element.ShortStyles){
4710if (property != style) continue;
4711for (var s in Element.ShortStyles[style]) result.push(this.getStyle(s));
4712return result.join(' ');
4713}
4714result = this.getComputedStyle(property);
4715}
4716if (result){
4717result = String(result);
4718var color = result.match(/rgba?\([\d\s,]+\)/);
4719if (color) result = result.replace(color[0], color[0].rgbToHex());
4720}
4721if (Browser.Engine.presto || (Browser.Engine.trident && !$chk(parseInt(result, 10)))){
4722if (property.test(/^(height|width)$/)){
4723var values = (property == 'width') ? ['left', 'right'] : ['top', 'bottom'], size = 0;
4724values.each(function(value){
4725size += this.getStyle('border-' + value + '-width').toInt() + this.getStyle('padding-' + value).toInt();
4726}, this);
4727return this['offset' + property.capitalize()] - size + 'px';
4728}
4729if ((Browser.Engine.presto) && String(result).test('px')) return result;
4730if (property.test(/(border(.+)Width|margin|padding)/)) return '0px';
4731}
4732return result;
4733},
4734
4735setStyles: function(styles){
4736for (var style in styles) this.setStyle(style, styles[style]);
4737return this;
4738},
4739
4740getStyles: function(){
4741var result = {};
4742Array.flatten(arguments).each(function(key){
4743result[key] = this.getStyle(key);
4744}, this);
4745return result;
4746}
4747
4748});
4749
4750Element.Styles = new Hash({
4751left: '@px', top: '@px', bottom: '@px', right: '@px',
4752width: '@px', height: '@px', maxWidth: '@px', maxHeight: '@px', minWidth: '@px', minHeight: '@px',
4753backgroundColor: 'rgb(@, @, @)', backgroundPosition: '@px @px', color: 'rgb(@, @, @)',
4754fontSize: '@px', letterSpacing: '@px', lineHeight: '@px', clip: 'rect(@px @px @px @px)',
4755margin: '@px @px @px @px', padding: '@px @px @px @px', border: '@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)',
4756borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', borderColor: 'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)',
4757zIndex: '@', 'zoom': '@', fontWeight: '@', textIndent: '@px', opacity: '@'
4758});
4759
4760Element.ShortStyles = {margin: {}, padding: {}, border: {}, borderWidth: {}, borderStyle: {}, borderColor: {}};
4761
4762['Top', 'Right', 'Bottom', 'Left'].each(function(direction){
4763var Short = Element.ShortStyles;
4764var All = Element.Styles;
4765['margin', 'padding'].each(function(style){
4766var sd = style + direction;
4767Short[style][sd] = All[sd] = '@px';
4768});
4769var bd = 'border' + direction;
4770Short.border[bd] = All[bd] = '@px @ rgb(@, @, @)';
4771var bdw = bd + 'Width', bds = bd + 'Style', bdc = bd + 'Color';
4772Short[bd] = {};
4773Short.borderWidth[bdw] = Short[bd][bdw] = All[bdw] = '@px';
4774Short.borderStyle[bds] = Short[bd][bds] = All[bds] = '@';
4775Short.borderColor[bdc] = Short[bd][bdc] = All[bdc] = 'rgb(@, @, @)';
4776});
4777
4778
4779/*
4780---
4781
4782name: Fx
4783
4784description: Contains the basic animation logic to be extended by all other Fx Classes.
4785
4786license: MIT-style license.
4787
4788requires: [Chain, Events, Options]
4789
4790provides: Fx
4791
4792...
4793*/
4794
4795var Fx = new Class({
4796
4797Implements: [Chain, Events, Options],
4798
4799options: {
4800/*
4801onStart: $empty,
4802onCancel: $empty,
4803onComplete: $empty,
4804*/
4805fps: 50,
4806unit: false,
4807duration: 500,
4808link: 'ignore'
4809},
4810
4811initialize: function(options){
4812this.subject = this.subject || this;
4813this.setOptions(options);
4814this.options.duration = Fx.Durations[this.options.duration] || this.options.duration.toInt();
4815var wait = this.options.wait;
4816if (wait === false) this.options.link = 'cancel';
4817},
4818
4819getTransition: function(){
4820return function(p){
4821return -(Math.cos(Math.PI * p) - 1) / 2;
4822};
4823},
4824
4825step: function(){
4826var time = $time();
4827if (time < this.time + this.options.duration){
4828var delta = this.transition((time - this.time) / this.options.duration);
4829this.set(this.compute(this.from, this.to, delta));
4830} else {
4831this.set(this.compute(this.from, this.to, 1));
4832this.complete();
4833}
4834},
4835
4836set: function(now){
4837return now;
4838},
4839
4840compute: function(from, to, delta){
4841return Fx.compute(from, to, delta);
4842},
4843
4844check: function(){
4845if (!this.timer) return true;
4846switch (this.options.link){
4847case 'cancel': this.cancel(); return true;
4848case 'chain': this.chain(this.caller.bind(this, arguments)); return false;
4849}
4850return false;
4851},
4852
4853start: function(from, to){
4854if (!this.check(from, to)) return this;
4855this.from = from;
4856this.to = to;
4857this.time = 0;
4858this.transition = this.getTransition();
4859this.startTimer();
4860this.onStart();
4861return this;
4862},
4863
4864complete: function(){
4865if (this.stopTimer()) this.onComplete();
4866return this;
4867},
4868
4869cancel: function(){
4870if (this.stopTimer()) this.onCancel();
4871return this;
4872},
4873
4874onStart: function(){
4875this.fireEvent('start', this.subject);
4876},
4877
4878onComplete: function(){
4879this.fireEvent('complete', this.subject);
4880if (!this.callChain()) this.fireEvent('chainComplete', this.subject);
4881},
4882
4883onCancel: function(){
4884this.fireEvent('cancel', this.subject).clearChain();
4885},
4886
4887pause: function(){
4888this.stopTimer();
4889return this;
4890},
4891
4892resume: function(){
4893this.startTimer();
4894return this;
4895},
4896
4897stopTimer: function(){
4898if (!this.timer) return false;
4899this.time = $time() - this.time;
4900this.timer = $clear(this.timer);
4901return true;
4902},
4903
4904startTimer: function(){
4905if (this.timer) return false;
4906this.time = $time() - this.time;
4907this.timer = this.step.periodical(Math.round(1000 / this.options.fps), this);
4908return true;
4909}
4910
4911});
4912
4913Fx.compute = function(from, to, delta){
4914return (to - from) * delta + from;
4915};
4916
4917Fx.Durations = {'short': 250, 'normal': 500, 'long': 1000};
4918
4919
4920/*
4921---
4922
4923name: Fx.CSS
4924
4925description: Contains the CSS animation logic. Used by Fx.Tween, Fx.Morph, Fx.Elements.
4926
4927license: MIT-style license.
4928
4929requires: [Fx, Element.Style]
4930
4931provides: Fx.CSS
4932
4933...
4934*/
4935
4936Fx.CSS = new Class({
4937
4938Extends: Fx,
4939
4940//prepares the base from/to object
4941
4942prepare: function(element, property, values){
4943values = $splat(values);
4944var values1 = values[1];
4945if (!$chk(values1)){
4946values[1] = values[0];
4947values[0] = element.getStyle(property);
4948}
4949var parsed = values.map(this.parse);
4950return {from: parsed[0], to: parsed[1]};
4951},
4952
4953//parses a value into an array
4954
4955parse: function(value){
4956value = $lambda(value)();
4957value = (typeof value == 'string') ? value.split(' ') : $splat(value);
4958return value.map(function(val){
4959val = String(val);
4960var found = false;
4961Fx.CSS.Parsers.each(function(parser, key){
4962if (found) return;
4963var parsed = parser.parse(val);
4964if ($chk(parsed)) found = {value: parsed, parser: parser};
4965});
4966found = found || {value: val, parser: Fx.CSS.Parsers.String};
4967return found;
4968});
4969},
4970
4971//computes by a from and to prepared objects, using their parsers.
4972
4973compute: function(from, to, delta){
4974var computed = [];
4975(Math.min(from.length, to.length)).times(function(i){
4976computed.push({value: from[i].parser.compute(from[i].value, to[i].value, delta), parser: from[i].parser});
4977});
4978computed.$family = {name: 'fx:css:value'};
4979return computed;
4980},
4981
4982//serves the value as settable
4983
4984serve: function(value, unit){
4985if ($type(value) != 'fx:css:value') value = this.parse(value);
4986var returned = [];
4987value.each(function(bit){
4988returned = returned.concat(bit.parser.serve(bit.value, unit));
4989});
4990return returned;
4991},
4992
4993//renders the change to an element
4994
4995render: function(element, property, value, unit){
4996element.setStyle(property, this.serve(value, unit));
4997},
4998
4999//searches inside the page css to find the values for a selector
5000
5001search: function(selector){
5002if (Fx.CSS.Cache[selector]) return Fx.CSS.Cache[selector];
5003var to = {};
5004Array.each(document.styleSheets, function(sheet, j){
5005var href = sheet.href;
5006if (href && href.contains('://') && !href.contains(document.domain)) return;
5007var rules = sheet.rules || sheet.cssRules;
5008Array.each(rules, function(rule, i){
5009if (!rule.style) return;
5010var selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/, function(m){
5011return m.toLowerCase();
5012}) : null;
5013if (!selectorText || !selectorText.test('^' + selector + '$')) return;
5014Element.Styles.each(function(value, style){
5015if (!rule.style[style] || Element.ShortStyles[style]) return;
5016value = String(rule.style[style]);
5017to[style] = (value.test(/^rgb/)) ? value.rgbToHex() : value;
5018});
5019});
5020});
5021return Fx.CSS.Cache[selector] = to;
5022}
5023
5024});
5025
5026Fx.CSS.Cache = {};
5027
5028Fx.CSS.Parsers = new Hash({
5029
5030Color: {
5031parse: function(value){
5032if (value.match(/^#[0-9a-f]{3,6}$/i)) return value.hexToRgb(true);
5033return ((value = value.match(/(\d+),\s*(\d+),\s*(\d+)/))) ? [value[1], value[2], value[3]] : false;
5034},
5035compute: function(from, to, delta){
5036return from.map(function(value, i){
5037return Math.round(Fx.compute(from[i], to[i], delta));
5038});
5039},
5040serve: function(value){
5041return value.map(Number);
5042}
5043},
5044
5045Number: {
5046parse: parseFloat,
5047compute: Fx.compute,
5048serve: function(value, unit){
5049return (unit) ? value + unit : value;
5050}
5051},
5052
5053String: {
5054parse: $lambda(false),
5055compute: $arguments(1),
5056serve: $arguments(0)
5057}
5058
5059});
5060
5061
5062/*
5063---
5064
5065name: Fx.Morph
5066
5067description: Formerly Fx.Styles, effect to transition any number of CSS properties for an element using an object of rules, or CSS based selector rules.
5068
5069license: MIT-style license.
5070
5071requires: Fx.CSS
5072
5073provides: Fx.Morph
5074
5075...
5076*/
5077
5078Fx.Morph = new Class({
5079
5080Extends: Fx.CSS,
5081
5082initialize: function(element, options){
5083this.element = this.subject = document.id(element);
5084this.parent(options);
5085},
5086
5087set: function(now){
5088if (typeof now == 'string') now = this.search(now);
5089for (var p in now) this.render(this.element, p, now[p], this.options.unit);
5090return this;
5091},
5092
5093compute: function(from, to, delta){
5094var now = {};
5095for (var p in from) now[p] = this.parent(from[p], to[p], delta);
5096return now;
5097},
5098
5099start: function(properties){
5100if (!this.check(properties)) return this;
5101if (typeof properties == 'string') properties = this.search(properties);
5102var from = {}, to = {};
5103for (var p in properties){
5104var parsed = this.prepare(this.element, p, properties[p]);
5105from[p] = parsed.from;
5106to[p] = parsed.to;
5107}
5108return this.parent(from, to);
5109}
5110
5111});
5112
5113Element.Properties.morph = {
5114
5115set: function(options){
5116var morph = this.retrieve('morph');
5117if (morph) morph.cancel();
5118return this.eliminate('morph').store('morph:options', $extend({link: 'cancel'}, options));
5119},
5120
5121get: function(options){
5122if (options || !this.retrieve('morph')){
5123if (options || !this.retrieve('morph:options')) this.set('morph', options);
5124this.store('morph', new Fx.Morph(this, this.retrieve('morph:options')));
5125}
5126return this.retrieve('morph');
5127}
5128
5129};
5130
5131Element.implement({
5132
5133morph: function(props){
5134this.get('morph').start(props);
5135return this;
5136}
5137
5138});
5139
5140
5141/*
5142---
5143
5144name: Fx.Transitions
5145
5146description: Contains a set of advanced transitions to be used with any of the Fx Classes.
5147
5148license: MIT-style license.
5149
5150credits: Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>, modified and optimized to be used with MooTools.
5151
5152requires: Fx
5153
5154provides: Fx.Transitions
5155
5156...
5157*/
5158
5159Fx.implement({
5160
5161getTransition: function(){
5162var trans = this.options.transition || Fx.Transitions.Sine.easeInOut;
5163if (typeof trans == 'string'){
5164var data = trans.split(':');
5165trans = Fx.Transitions;
5166trans = trans[data[0]] || trans[data[0].capitalize()];
5167if (data[1]) trans = trans['ease' + data[1].capitalize() + (data[2] ? data[2].capitalize() : '')];
5168}
5169return trans;
5170}
5171
5172});
5173
5174Fx.Transition = function(transition, params){
5175params = $splat(params);
5176return $extend(transition, {
5177easeIn: function(pos){
5178return transition(pos, params);
5179},
5180easeOut: function(pos){
5181return 1 - transition(1 - pos, params);
5182},
5183easeInOut: function(pos){
5184return (pos <= 0.5) ? transition(2 * pos, params) / 2 : (2 - transition(2 * (1 - pos), params)) / 2;
5185}
5186});
5187};
5188
5189Fx.Transitions = new Hash({
5190
5191linear: $arguments(0)
5192
5193});
5194
5195Fx.Transitions.extend = function(transitions){
5196for (var transition in transitions) Fx.Transitions[transition] = new Fx.Transition(transitions[transition]);
5197};
5198
5199Fx.Transitions.extend({
5200
5201Pow: function(p, x){
5202return Math.pow(p, x[0] || 6);
5203},
5204
5205Expo: function(p){
5206return Math.pow(2, 8 * (p - 1));
5207},
5208
5209Circ: function(p){
5210return 1 - Math.sin(Math.acos(p));
5211},
5212
5213Sine: function(p){
5214return 1 - Math.sin((1 - p) * Math.PI / 2);
5215},
5216
5217Back: function(p, x){
5218x = x[0] || 1.618;
5219return Math.pow(p, 2) * ((x + 1) * p - x);
5220},
5221
5222Bounce: function(p){
5223var value;
5224for (var a = 0, b = 1; 1; a += b, b /= 2){
5225if (p >= (7 - 4 * a) / 11){
5226value = b * b - Math.pow((11 - 6 * a - 11 * p) / 4, 2);
5227break;
5228}
5229}
5230return value;
5231},
5232
5233Elastic: function(p, x){
5234return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x[0] || 1) / 3);
5235}
5236
5237});
5238
5239['Quad', 'Cubic', 'Quart', 'Quint'].each(function(transition, i){
5240Fx.Transitions[transition] = new Fx.Transition(function(p){
5241return Math.pow(p, [i + 2]);
5242});
5243});
5244
5245
5246/*
5247---
5248
5249name: Fx.Tween
5250
5251description: Formerly Fx.Style, effect to transition any CSS property for an element.
5252
5253license: MIT-style license.
5254
5255requires: Fx.CSS
5256
5257provides: [Fx.Tween, Element.fade, Element.highlight]
5258
5259...
5260*/
5261
5262Fx.Tween = new Class({
5263
5264Extends: Fx.CSS,
5265
5266initialize: function(element, options){
5267this.element = this.subject = document.id(element);
5268this.parent(options);
5269},
5270
5271set: function(property, now){
5272if (arguments.length == 1){
5273now = property;
5274property = this.property || this.options.property;
5275}
5276this.render(this.element, property, now, this.options.unit);
5277return this;
5278},
5279
5280start: function(property, from, to){
5281if (!this.check(property, from, to)) return this;
5282var args = Array.flatten(arguments);
5283this.property = this.options.property || args.shift();
5284var parsed = this.prepare(this.element, this.property, args);
5285return this.parent(parsed.from, parsed.to);
5286}
5287
5288});
5289
5290Element.Properties.tween = {
5291
5292set: function(options){
5293var tween = this.retrieve('tween');
5294if (tween) tween.cancel();
5295return this.eliminate('tween').store('tween:options', $extend({link: 'cancel'}, options));
5296},
5297
5298get: function(options){
5299if (options || !this.retrieve('tween')){
5300if (options || !this.retrieve('tween:options')) this.set('tween', options);
5301this.store('tween', new Fx.Tween(this, this.retrieve('tween:options')));
5302}
5303return this.retrieve('tween');
5304}
5305
5306};
5307
5308Element.implement({
5309
5310tween: function(property, from, to){
5311this.get('tween').start(arguments);
5312return this;
5313},
5314
5315fade: function(how){
5316var fade = this.get('tween'), o = 'opacity', toggle;
5317how = $pick(how, 'toggle');
5318switch (how){
5319case 'in': fade.start(o, 1); break;
5320case 'out': fade.start(o, 0); break;
5321case 'show': fade.set(o, 1); break;
5322case 'hide': fade.set(o, 0); break;
5323case 'toggle':
5324var flag = this.retrieve('fade:flag', this.get('opacity') == 1);
5325fade.start(o, (flag) ? 0 : 1);
5326this.store('fade:flag', !flag);
5327toggle = true;
5328break;
5329default: fade.start(o, arguments);
5330}
5331if (!toggle) this.eliminate('fade:flag');
5332return this;
5333},
5334
5335highlight: function(start, end){
5336if (!end){
5337end = this.retrieve('highlight:original', this.getStyle('background-color'));
5338end = (end == 'transparent') ? '#fff' : end;
5339}
5340var tween = this.get('tween');
5341tween.start('background-color', start || '#ffff88', end).chain(function(){
5342this.setStyle('background-color', this.retrieve('highlight:original'));
5343tween.callChain();
5344}.bind(this));
5345return this;
5346}
5347
5348});
5349
5350
5351/*
5352---
5353
5354name: Request
5355
5356description: Powerful all purpose Request Class. Uses XMLHTTPRequest.
5357
5358license: MIT-style license.
5359
5360requires: [Element, Chain, Events, Options, Browser]
5361
5362provides: Request
5363
5364...
5365*/
5366
5367var Request = new Class({
5368
5369Implements: [Chain, Events, Options],
5370
5371options: {/*
5372onRequest: $empty,
5373onComplete: $empty,
5374onCancel: $empty,
5375onSuccess: $empty,
5376onFailure: $empty,
5377onException: $empty,*/
5378url: '',
5379data: '',
5380headers: {
5381'X-Requested-With': 'XMLHttpRequest',
5382'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
5383},
5384async: true,
5385format: false,
5386method: 'post',
5387link: 'ignore',
5388isSuccess: null,
5389emulation: true,
5390urlEncoded: true,
5391encoding: 'utf-8',
5392evalScripts: false,
5393evalResponse: false,
5394noCache: false
5395},
5396
5397initialize: function(options){
5398this.xhr = new Browser.Request();
5399this.setOptions(options);
5400this.options.isSuccess = this.options.isSuccess || this.isSuccess;
5401this.headers = new Hash(this.options.headers);
5402},
5403
5404onStateChange: function(){
5405if (this.xhr.readyState != 4 || !this.running) return;
5406this.running = false;
5407this.status = 0;
5408$try(function(){
5409this.status = this.xhr.status;
5410}.bind(this));
5411this.xhr.onreadystatechange = $empty;
5412if (this.options.isSuccess.call(this, this.status)){
5413this.response = {text: this.xhr.responseText, xml: this.xhr.responseXML};
5414this.success(this.response.text, this.response.xml);
5415} else {
5416this.response = {text: null, xml: null};
5417this.failure();
5418}
5419},
5420
5421isSuccess: function(){
5422return ((this.status >= 200) && (this.status < 300));
5423},
5424
5425processScripts: function(text){
5426if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) return $exec(text);
5427return text.stripScripts(this.options.evalScripts);
5428},
5429
5430success: function(text, xml){
5431this.onSuccess(this.processScripts(text), xml);
5432},
5433
5434onSuccess: function(){
5435this.fireEvent('complete', arguments).fireEvent('success', arguments).callChain();
5436},
5437
5438failure: function(){
5439this.onFailure();
5440},
5441
5442onFailure: function(){
5443this.fireEvent('complete').fireEvent('failure', this.xhr);
5444},
5445
5446setHeader: function(name, value){
5447this.headers.set(name, value);
5448return this;
5449},
5450
5451getHeader: function(name){
5452return $try(function(){
5453return this.xhr.getResponseHeader(name);
5454}.bind(this));
5455},
5456
5457check: function(){
5458if (!this.running) return true;
5459switch (this.options.link){
5460case 'cancel': this.cancel(); return true;
5461case 'chain': this.chain(this.caller.bind(this, arguments)); return false;
5462}
5463return false;
5464},
5465
5466send: function(options){
5467if (!this.check(options)) return this;
5468this.running = true;
5469
5470var type = $type(options);
5471if (type == 'string' || type == 'element') options = {data: options};
5472
5473var old = this.options;
5474options = $extend({data: old.data, url: old.url, method: old.method}, options);
5475var data = options.data, url = String(options.url), method = options.method.toLowerCase();
5476
5477switch ($type(data)){
5478case 'element': data = document.id(data).toQueryString(); break;
5479case 'object': case 'hash': data = Hash.toQueryString(data);
5480}
5481
5482if (this.options.format){
5483var format = 'format=' + this.options.format;
5484data = (data) ? format + '&' + data : format;
5485}
5486
5487if (this.options.emulation && !['get', 'post'].contains(method)){
5488var _method = '_method=' + method;
5489data = (data) ? _method + '&' + data : _method;
5490method = 'post';
5491}
5492
5493if (this.options.urlEncoded && method == 'post'){
5494var encoding = (this.options.encoding) ? '; charset=' + this.options.encoding : '';
5495this.headers.set('Content-type', 'application/x-www-form-urlencoded' + encoding);
5496}
5497
5498if (this.options.noCache){
5499var noCache = 'noCache=' + new Date().getTime();
5500data = (data) ? noCache + '&' + data : noCache;
5501}
5502
5503var trimPosition = url.lastIndexOf('/');
5504if (trimPosition > -1 && (trimPosition = url.indexOf('#')) > -1) url = url.substr(0, trimPosition);
5505
5506if (data && method == 'get'){
5507url = url + (url.contains('?') ? '&' : '?') + data;
5508data = null;
5509}
5510
5511this.xhr.open(method.toUpperCase(), url, this.options.async);
5512
5513this.xhr.onreadystatechange = this.onStateChange.bind(this);
5514
5515this.headers.each(function(value, key){
5516try {
5517this.xhr.setRequestHeader(key, value);
5518} catch (e){
5519this.fireEvent('exception', [key, value]);
5520}
5521}, this);
5522
5523this.fireEvent('request');
5524this.xhr.send(data);
5525if (!this.options.async) this.onStateChange();
5526return this;
5527},
5528
5529cancel: function(){
5530if (!this.running) return this;
5531this.running = false;
5532this.xhr.abort();
5533this.xhr.onreadystatechange = $empty;
5534this.xhr = new Browser.Request();
5535this.fireEvent('cancel');
5536return this;
5537}
5538
5539});
5540
5541(function(){
5542
5543var methods = {};
5544['get', 'post', 'put', 'delete', 'GET', 'POST', 'PUT', 'DELETE'].each(function(method){
5545methods[method] = function(){
5546var params = Array.link(arguments, {url: String.type, data: $defined});
5547return this.send($extend(params, {method: method}));
5548};
5549});
5550
5551Request.implement(methods);
5552
5553})();
5554
5555Element.Properties.send = {
5556
5557set: function(options){
5558var send = this.retrieve('send');
5559if (send) send.cancel();
5560return this.eliminate('send').store('send:options', $extend({
5561data: this, link: 'cancel', method: this.get('method') || 'post', url: this.get('action')
5562}, options));
5563},
5564
5565get: function(options){
5566if (options || !this.retrieve('send')){
5567if (options || !this.retrieve('send:options')) this.set('send', options);
5568this.store('send', new Request(this.retrieve('send:options')));
5569}
5570return this.retrieve('send');
5571}
5572
5573};
5574
5575Element.implement({
5576
5577send: function(url){
5578var sender = this.get('send');
5579sender.send({data: this, url: url || sender.options.url});
5580return this;
5581}
5582
5583});
5584
5585
5586/*
5587---
5588
5589name: Request.HTML
5590
5591description: Extends the basic Request Class with additional methods for interacting with HTML responses.
5592
5593license: MIT-style license.
5594
5595requires: [Request, Element]
5596
5597provides: Request.HTML
5598
5599...
5600*/
5601
5602Request.HTML = new Class({
5603
5604Extends: Request,
5605
5606options: {
5607update: false,
5608append: false,
5609evalScripts: true,
5610filter: false
5611},
5612
5613processHTML: function(text){
5614var match = text.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
5615text = (match) ? match[1] : text;
5616
5617var container = new Element('div');
5618
5619return $try(function(){
5620var root = '<root>' + text + '</root>', doc;
5621if (Browser.Engine.trident){
5622doc = new ActiveXObject('Microsoft.XMLDOM');
5623doc.async = false;
5624doc.loadXML(root);
5625} else {
5626doc = new DOMParser().parseFromString(root, 'text/xml');
5627}
5628root = doc.getElementsByTagName('root')[0];
5629if (!root) return null;
5630for (var i = 0, k = root.childNodes.length; i < k; i++){
5631var child = Element.clone(root.childNodes[i], true, true);
5632if (child) container.grab(child);
5633}
5634return container;
5635}) || container.set('html', text);
5636},
5637
5638success: function(text){
5639var options = this.options, response = this.response;
5640
5641response.html = text.stripScripts(function(script){
5642response.javascript = script;
5643});
5644
5645var temp = this.processHTML(response.html);
5646
5647response.tree = temp.childNodes;
5648response.elements = temp.getElements('*');
5649
5650if (options.filter) response.tree = response.elements.filter(options.filter);
5651if (options.update) document.id(options.update).empty().set('html', response.html);
5652else if (options.append) document.id(options.append).adopt(temp.getChildren());
5653if (options.evalScripts) $exec(response.javascript);
5654
5655this.onSuccess(response.tree, response.elements, response.html, response.javascript);
5656}
5657
5658});
5659
5660Element.Properties.load = {
5661
5662set: function(options){
5663var load = this.retrieve('load');
5664if (load) load.cancel();
5665return this.eliminate('load').store('load:options', $extend({data: this, link: 'cancel', update: this, method: 'get'}, options));
5666},
5667
5668get: function(options){
5669if (options || ! this.retrieve('load')){
5670if (options || !this.retrieve('load:options')) this.set('load', options);
5671this.store('load', new Request.HTML(this.retrieve('load:options')));
5672}
5673return this.retrieve('load');
5674}
5675
5676};
5677
5678Element.implement({
5679
5680load: function(){
5681this.get('load').send(Array.link(arguments, {data: Object.type, url: String.type}));
5682return this;
5683}
5684
5685});
5686
5687
5688/*
5689---
5690
5691name: JSON
5692
5693description: JSON encoder and decoder.
5694
5695license: MIT-style license.
5696
5697see: <http://www.json.org/>
5698
5699requires: [Array, String, Number, Function, Hash]
5700
5701provides: JSON
5702
5703...
5704*/
5705
5706var JSON = new Hash(this.JSON && {
5707stringify: JSON.stringify,
5708parse: JSON.parse
5709}).extend({
5710
5711$specialChars: {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'},
5712
5713$replaceChars: function(chr){
5714return JSON.$specialChars[chr] || '\\u00' + Math.floor(chr.charCodeAt() / 16).toString(16) + (chr.charCodeAt() % 16).toString(16);
5715},
5716
5717encode: function(obj){
5718switch ($type(obj)){
5719case 'string':
5720return '"' + obj.replace(/[\x00-\x1f\\"]/g, JSON.$replaceChars) + '"';
5721case 'array':
5722return '[' + String(obj.map(JSON.encode).clean()) + ']';
5723case 'object': case 'hash':
5724var string = [];
5725Hash.each(obj, function(value, key){
5726var json = JSON.encode(value);
5727if (json) string.push(JSON.encode(key) + ':' + json);
5728});
5729return '{' + string + '}';
5730case 'number': case 'boolean': return String(obj);
5731case false: return 'null';
5732}
5733return null;
5734},
5735
5736decode: function(string, secure){
5737if ($type(string) != 'string' || !string.length) return null;
5738if (secure && !(/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(string.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''))) return null;
5739return eval('(' + string + ')');
5740}
5741
5742});
5743
5744
5745/*
5746---
5747
5748name: Request.JSON
5749
5750description: Extends the basic Request Class with additional methods for sending and receiving JSON data.
5751
5752license: MIT-style license.
5753
5754requires: [Request, JSON]
5755
5756provides: [Request.JSON]
5757
5758...
5759*/
5760
5761Request.JSON = new Class({
5762
5763Extends: Request,
5764
5765options: {
5766secure: true
5767},
5768
5769initialize: function(options){
5770this.parent(options);
5771this.headers.extend({'Accept': 'application/json', 'X-Request': 'JSON'});
5772},
5773
5774success: function(text){
5775this.response.json = JSON.decode(text, this.options.secure);
5776this.onSuccess(this.response.json, text);
5777}
5778
5779});
5780
5781
5782/*
5783---
5784
5785name: Cookie
5786
5787description: Class for creating, reading, and deleting browser Cookies.
5788
5789license: MIT-style license.
5790
5791credits: Based on the functions by Peter-Paul Koch (http://quirksmode.org).
5792
5793requires: Options
5794
5795provides: Cookie
5796
5797...
5798*/
5799
5800var Cookie = new Class({
5801
5802Implements: Options,
5803
5804options: {
5805path: false,
5806domain: false,
5807duration: false,
5808secure: false,
5809document: document
5810},
5811
5812initialize: function(key, options){
5813this.key = key;
5814this.setOptions(options);
5815},
5816
5817write: function(value){
5818value = encodeURIComponent(value);
5819if (this.options.domain) value += '; domain=' + this.options.domain;
5820if (this.options.path) value += '; path=' + this.options.path;
5821if (this.options.duration){
5822var date = new Date();
5823date.setTime(date.getTime() + this.options.duration * 24 * 60 * 60 * 1000);
5824value += '; expires=' + date.toGMTString();
5825}
5826if (this.options.secure) value += '; secure';
5827this.options.document.cookie = this.key + '=' + value;
5828return this;
5829},
5830
5831read: function(){
5832var value = this.options.document.cookie.match('(?:^|;)\\s*' + this.key.escapeRegExp() + '=([^;]*)');
5833return (value) ? decodeURIComponent(value[1]) : null;
5834},
5835
5836dispose: function(){
5837new Cookie(this.key, $merge(this.options, {duration: -1})).write('');
5838return this;
5839}
5840
5841});
5842
5843Cookie.write = function(key, value, options){
5844return new Cookie(key, options).write(value);
5845};
5846
5847Cookie.read = function(key){
5848return new Cookie(key).read();
5849};
5850
5851Cookie.dispose = function(key, options){
5852return new Cookie(key, options).dispose();
5853};
5854
5855
5856/*
5857---
5858
5859name: DomReady
5860
5861description: Contains the custom event domready.
5862
5863license: MIT-style license.
5864
5865requires: Element.Event
5866
5867provides: DomReady
5868
5869...
5870*/
5871
5872Element.Events.domready = {
5873
5874onAdd: function(fn){
5875if (Browser.loaded) fn.call(this);
5876}
5877
5878};
5879
5880(function(){
5881
5882var domready = function(){
5883if (Browser.loaded) return;
5884Browser.loaded = true;
5885window.fireEvent('domready');
5886document.fireEvent('domready');
5887};
5888
5889window.addEvent('load', domready);
5890
5891if (Browser.Engine.trident){
5892var temp = document.createElement('div');
5893(function(){
5894($try(function(){
5895temp.doScroll(); // Technique by Diego Perini
5896return document.id(temp).inject(document.body).set('html', 'temp').dispose();
5897})) ? domready() : arguments.callee.delay(50);
5898})();
5899} else if (Browser.Engine.webkit && Browser.Engine.version < 525){
5900(function(){
5901(['loaded', 'complete'].contains(document.readyState)) ? domready() : arguments.callee.delay(50);
5902})();
5903} else {
5904document.addEvent('DOMContentLoaded', domready);
5905}
5906
5907})();
5908
5909
5910/*
5911---
5912
5913name: Selectors
5914
5915description: Adds advanced CSS-style querying capabilities for targeting HTML Elements. Includes pseudo selectors.
5916
5917license: MIT-style license.
5918
5919requires: Element
5920
5921provides: Selectors
5922
5923...
5924*/
5925
5926Native.implement([Document, Element], {
5927
5928getElements: function(expression, nocash){
5929expression = expression.split(',');
5930var items, local = {};
5931for (var i = 0, l = expression.length; i < l; i++){
5932var selector = expression[i], elements = Selectors.Utils.search(this, selector, local);
5933if (i != 0 && elements.item) elements = $A(elements);
5934items = (i == 0) ? elements : (items.item) ? $A(items).concat(elements) : items.concat(elements);
5935}
5936return new Elements(items, {ddup: (expression.length > 1), cash: !nocash});
5937}
5938
5939});
5940
5941Element.implement({
5942
5943match: function(selector){
5944if (!selector || (selector == this)) return true;
5945var tagid = Selectors.Utils.parseTagAndID(selector);
5946var tag = tagid[0], id = tagid[1];
5947if (!Selectors.Filters.byID(this, id) || !Selectors.Filters.byTag(this, tag)) return false;
5948var parsed = Selectors.Utils.parseSelector(selector);
5949return (parsed) ? Selectors.Utils.filter(this, parsed, {}) : true;
5950}
5951
5952});
5953
5954var Selectors = {Cache: {nth: {}, parsed: {}}};
5955
5956Selectors.RegExps = {
5957id: (/#([\w-]+)/),
5958tag: (/^(\w+|\*)/),
5959quick: (/^(\w+|\*)$/),
5960splitter: (/\s*([+>~\s])\s*([a-zA-Z#.*:\[])/g),
5961combined: (/\.([\w-]+)|\[(\w+)(?:([!*^$~|]?=)(["']?)([^\4]*?)\4)?\]|:([\w-]+)(?:\(["']?(.*?)?["']?\)|$)/g)
5962};
5963
5964Selectors.Utils = {
5965
5966chk: function(item, uniques){
5967if (!uniques) return true;
5968var uid = $uid(item);
5969if (!uniques[uid]) return uniques[uid] = true;
5970return false;
5971},
5972
5973parseNthArgument: function(argument){
5974if (Selectors.Cache.nth[argument]) return Selectors.Cache.nth[argument];
5975var parsed = argument.match(/^([+-]?\d*)?([a-z]+)?([+-]?\d*)?$/);
5976if (!parsed) return false;
5977var inta = parseInt(parsed[1], 10);
5978var a = (inta || inta === 0) ? inta : 1;
5979var special = parsed[2] || false;
5980var b = parseInt(parsed[3], 10) || 0;
5981if (a != 0){
5982b--;
5983while (b < 1) b += a;
5984while (b >= a) b -= a;
5985} else {
5986a = b;
5987special = 'index';
5988}
5989switch (special){
5990case 'n': parsed = {a: a, b: b, special: 'n'}; break;
5991case 'odd': parsed = {a: 2, b: 0, special: 'n'}; break;
5992case 'even': parsed = {a: 2, b: 1, special: 'n'}; break;
5993case 'first': parsed = {a: 0, special: 'index'}; break;
5994case 'last': parsed = {special: 'last-child'}; break;
5995case 'only': parsed = {special: 'only-child'}; break;
5996default: parsed = {a: (a - 1), special: 'index'};
5997}
5998
5999return Selectors.Cache.nth[argument] = parsed;
6000},
6001
6002parseSelector: function(selector){
6003if (Selectors.Cache.parsed[selector]) return Selectors.Cache.parsed[selector];
6004var m, parsed = {classes: [], pseudos: [], attributes: []};
6005while ((m = Selectors.RegExps.combined.exec(selector))){
6006var cn = m[1], an = m[2], ao = m[3], av = m[5], pn = m[6], pa = m[7];
6007if (cn){
6008parsed.classes.push(cn);
6009} else if (pn){
6010var parser = Selectors.Pseudo.get(pn);
6011if (parser) parsed.pseudos.push({parser: parser, argument: pa});
6012else parsed.attributes.push({name: pn, operator: '=', value: pa});
6013} else if (an){
6014parsed.attributes.push({name: an, operator: ao, value: av});
6015}
6016}
6017if (!parsed.classes.length) delete parsed.classes;
6018if (!parsed.attributes.length) delete parsed.attributes;
6019if (!parsed.pseudos.length) delete parsed.pseudos;
6020if (!parsed.classes && !parsed.attributes && !parsed.pseudos) parsed = null;
6021return Selectors.Cache.parsed[selector] = parsed;
6022},
6023
6024parseTagAndID: function(selector){
6025var tag = selector.match(Selectors.RegExps.tag);
6026var id = selector.match(Selectors.RegExps.id);
6027return [(tag) ? tag[1] : '*', (id) ? id[1] : false];
6028},
6029
6030filter: function(item, parsed, local){
6031var i;
6032if (parsed.classes){
6033for (i = parsed.classes.length; i--; i){
6034var cn = parsed.classes[i];
6035if (!Selectors.Filters.byClass(item, cn)) return false;
6036}
6037}
6038if (parsed.attributes){
6039for (i = parsed.attributes.length; i--; i){
6040var att = parsed.attributes[i];
6041if (!Selectors.Filters.byAttribute(item, att.name, att.operator, att.value)) return false;
6042}
6043}
6044if (parsed.pseudos){
6045for (i = parsed.pseudos.length; i--; i){
6046var psd = parsed.pseudos[i];
6047if (!Selectors.Filters.byPseudo(item, psd.parser, psd.argument, local)) return false;
6048}
6049}
6050return true;
6051},
6052
6053getByTagAndID: function(ctx, tag, id){
6054if (id){
6055var item = (ctx.getElementById) ? ctx.getElementById(id, true) : Element.getElementById(ctx, id, true);
6056return (item && Selectors.Filters.byTag(item, tag)) ? [item] : [];
6057} else {
6058return ctx.getElementsByTagName(tag);
6059}
6060},
6061
6062search: function(self, expression, local){
6063var splitters = [];
6064
6065var selectors = expression.trim().replace(Selectors.RegExps.splitter, function(m0, m1, m2){
6066splitters.push(m1);
6067return ':)' + m2;
6068}).split(':)');
6069
6070var items, filtered, item;
6071
6072for (var i = 0, l = selectors.length; i < l; i++){
6073
6074var selector = selectors[i];
6075
6076if (i == 0 && Selectors.RegExps.quick.test(selector)){
6077items = self.getElementsByTagName(selector);
6078continue;
6079}
6080
6081var splitter = splitters[i - 1];
6082
6083var tagid = Selectors.Utils.parseTagAndID(selector);
6084var tag = tagid[0], id = tagid[1];
6085
6086if (i == 0){
6087items = Selectors.Utils.getByTagAndID(self, tag, id);
6088} else {
6089var uniques = {}, found = [];
6090for (var j = 0, k = items.length; j < k;
6090 j++) found = Selectors.Getters[splitter](found, items[j], tag, id, uniques);
6091items = found;
6092}
6093
6094var parsed = Selectors.Utils.parseSelector(selector);
6095
6096if (parsed){
6097filtered = [];
6098for (var m = 0, n = items.length; m < n; m++){
6099item = items[m];
6100if (Selectors.Utils.filter(item, parsed, local)) filtered.push(item);
6101}
6102items = filtered;
6103}
6104
6105}
6106
6107return items;
6108
6109}
6110
6111};
6112
6113Selectors.Getters = {
6114
6115' ': function(found, self, tag, id, uniques){
6116var items = Selectors.Utils.getByTagAndID(self, tag, id);
6117for (var i = 0, l = items.length; i < l; i++){
6118var item = items[i];
6119if (Selectors.Utils.chk(item, uniques)) found.push(item);
6120}
6121return found;
6122},
6123
6124'>': function(found, self, tag, id, uniques){
6125var children = Selectors.Utils.getByTagAndID(self, tag, id);
6126for (var i = 0, l = children.length; i < l; i++){
6127var child = children[i];
6128if (child.parentNode == self && Selectors.Utils.chk(child, uniques)) found.push(child);
6129}
6130return found;
6131},
6132
6133'+': function(found, self, tag, id, uniques){
6134while ((self = self.nextSibling)){
6135if (self.nodeType == 1){
6136if (Selectors.Utils.chk(self, uniques) && Selectors.Filters.byTag(self, tag) && Selectors.Filters.byID(self, id)) found.push(self);
6137break;
6138}
6139}
6140return found;
6141},
6142
6143'~': function(found, self, tag, id, uniques){
6144while ((self = self.nextSibling)){
6145if (self.nodeType == 1){
6146if (!Selectors.Utils.chk(self, uniques)) break;
6147if (Selectors.Filters.byTag(self, tag) && Selectors.Filters.byID(self, id)) found.push(self);
6148}
6149}
6150return found;
6151}
6152
6153};
6154
6155Selectors.Filters = {
6156
6157byTag: function(self, tag){
6158return (tag == '*' || (self.tagName && self.tagName.toLowerCase() == tag));
6159},
6160
6161byID: function(self, id){
6162return (!id || (self.id && self.id == id));
6163},
6164
6165byClass: function(self, klass){
6166return (self.className && self.className.contains && self.className.contains(klass, ' '));
6167},
6168
6169byPseudo: function(self, parser, argument, local){
6170return parser.call(self, argument, local);
6171},
6172
6173byAttribute: function(self, name, operator, value){
6174var result = Element.prototype.getProperty.call(self, name);
6175if (!result) return (operator == '!=');
6176if (!operator || value == undefined) return true;
6177switch (operator){
6178case '=': return (result == value);
6179case '*=': return (result.contains(value));
6180case '^=': return (result.substr(0, value.length) == value);
6181case '$=': return (result.substr(result.length - value.length) == value);
6182case '!=': return (result != value);
6183case '~=': return result.contains(value, ' ');
6184case '|=': return result.contains(value, '-');
6185}
6186return false;
6187}
6188
6189};
6190
6191Selectors.Pseudo = new Hash({
6192
6193// w3c pseudo selectors
6194
6195checked: function(){
6196return this.checked;
6197},
6198
6199empty: function(){
6200return !(this.innerText || this.textContent || '').length;
6201},
6202
6203not: function(selector){
6204return !Element.match(this, selector);
6205},
6206
6207contains: function(text){
6208return (this.innerText || this.textContent || '').contains(text);
6209},
6210
6211'first-child': function(){
6212return Selectors.Pseudo.index.call(this, 0);
6213},
6214
6215'last-child': function(){
6216var element = this;
6217while ((element = element.nextSibling)){
6218if (element.nodeType == 1) return false;
6219}
6220return true;
6221},
6222
6223'only-child': function(){
6224var prev = this;
6225while ((prev = prev.previousSibling)){
6226if (prev.nodeType == 1) return false;
6227}
6228var next = this;
6229while ((next = next.nextSibling)){
6230if (next.nodeType == 1) return false;
6231}
6232return true;
6233},
6234
6235'nth-child': function(argument, local){
6236argument = (argument == undefined) ? 'n' : argument;
6237var parsed = Selectors.Utils.parseNthArgument(argument);
6238if (parsed.special != 'n') return Selectors.Pseudo[parsed.special].call(this, parsed.a, local);
6239var count = 0;
6240local.positions = local.positions || {};
6241var uid = $uid(this);
6242if (!local.positions[uid]){
6243var self = this;
6244while ((self = self.previousSibling)){
6245if (self.nodeType != 1) continue;
6246count ++;
6247var position = local.positions[$uid(self)];
6248if (position != undefined){
6249count = position + count;
6250break;
6251}
6252}
6253local.positions[uid] = count;
6254}
6255return (local.positions[uid] % parsed.a == parsed.b);
6256},
6257
6258// custom pseudo selectors
6259
6260index: function(index){
6261var element = this, count = 0;
6262while ((element = element.previousSibling)){
6263if (element.nodeType == 1 && ++count > index) return false;
6264}
6265return (count == index);
6266},
6267
6268even: function(argument, local){
6269return Selectors.Pseudo['nth-child'].call(this, '2n+1', local);
6270},
6271
6272odd: function(argument, local){
6273return Selectors.Pseudo['nth-child'].call(this, '2n', local);
6274},
6275
6276selected: function(){
6277return this.selected;
6278},
6279
6280enabled: function(){
6281return (this.disabled === false);
6282}
6283
6284});
6285
6286
6287/*
6288---
6289
6290name: Swiff
6291
6292description: Wrapper for embedding SWF movies. Supports External Interface Communication.
6293
6294license: MIT-style license.
6295
6296credits: Flash detection & Internet Explorer + Flash Player 9 fix inspired by SWFObject.
6297
6298requires: [Options, $util]
6299
6300provides: Swiff
6301
6302...
6303*/
6304
6305var Swiff = new Class({
6306
6307Implements: [Options],
6308
6309options: {
6310id: null,
6311height: 1,
6312width: 1,
6313container: null,
6314properties: {},
6315params: {
6316quality: 'high',
6317allowScriptAccess: 'always',
6318wMode: 'transparent',
6319swLiveConnect: true
6320},
6321callBacks: {},
6322vars: {}
6323},
6324
6325toElement: function(){
6326return this.object;
6327},
6328
6329initialize: function(path, options){
6330this.instance = 'Swiff_' + $time();
6331
6332this.setOptions(options);
6333options = this.options;
6334var id = this.id = options.id || this.instance;
6335var container = document.id(options.container);
6336
6337Swiff.CallBacks[this.instance] = {};
6338
6339var params = options.params, vars = options.vars, callBacks = options.callBacks;
6340var properties = $extend({height: options.height, width: options.width}, options.properties);
6341
6342var self = this;
6343
6344for (var callBack in callBacks){
6345Swiff.CallBacks[this.instance][callBack] = (function(option){
6346return function(){
6347return option.apply(self.object, arguments);
6348};
6349})(callBacks[callBack]);
6350vars[callBack] = 'Swiff.CallBacks.' + this.instance + '.' + callBack;
6351}
6352
6353params.flashVars = Hash.toQueryString(vars);
6354if (Browser.Engine.trident){
6355properties.classid = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000';
6356params.movie = path;
6357} else {
6358properties.type = 'application/x-shockwave-flash';
6359properties.data = path;
6360}
6361var build = '<object id="' + id + '"';
6362for (var property in properties) build += ' ' + property + '="' + properties[property] + '"';
6363build += '>';
6364for (var param in params){
6365if (params[param]) build += '<param name="' + param + '" value="' + params[param] + '" />';
6366}
6367build += '</object>';
6368this.object = ((container) ? container.empty() : new Element('div')).set('html', build).firstChild;
6369},
6370
6371replaces: function(element){
6372element = document.id(element, true);
6373element.parentNode.replaceChild(this.toElement(), element);
6374return this;
6375},
6376
6377inject: function(element){
6378document.id(element, true).appendChild(this.toElement());
6379return this;
6380},
6381
6382remote: function(){
6383return Swiff.remote.apply(Swiff, [this.toElement()].extend(arguments));
6384}
6385
6386});
6387
6388Swiff.CallBacks = {};
6389
6390Swiff.remote = function(obj, fn){
6391var rs = obj.CallFunction('<invoke name="' + fn + '" returntype="javascript">' + __flash__argumentsToXML(arguments, 2) + '</invoke>');
6392return eval(rs);
6393};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.