From: <die...@us...> - 2010-09-01 17:32:31
|
Revision: 2995 http://openutils.svn.sourceforge.net/openutils/?rev=2995&view=rev Author: diego_schivo Date: 2010-09-01 17:32:22 +0000 (Wed, 01 Sep 2010) Log Message: ----------- MEDIA-166 Style the "sort by" select: javascript replacement Modified Paths: -------------- trunk/openutils-mgnlmedia/src/main/resources/mgnl-resources/media/css/folderView.css trunk/openutils-mgnlmedia/src/main/resources/net/sourceforge/openutils/mgnlmedia/media/pages/MediaFolderViewPage.html Added Paths: ----------- trunk/openutils-mgnlmedia/src/main/resources/mgnl-resources/media/css/images/myselect.gif trunk/openutils-mgnlmedia/src/main/resources/mgnl-resources/media/css/images/select-left.png trunk/openutils-mgnlmedia/src/main/resources/mgnl-resources/media/css/images/select-right.png trunk/openutils-mgnlmedia/src/main/resources/mgnl-resources/media/css/mooSelecta.css trunk/openutils-mgnlmedia/src/main/resources/mgnl-resources/media/js/mooSelecta.js trunk/openutils-mgnlmedia/src/main/resources/mgnl-resources/media/js/mootools-1.2.4-core.js trunk/openutils-mgnlmedia/src/main/resources/mgnl-resources/media/js/mootools-1.2.4.4-more.js Modified: trunk/openutils-mgnlmedia/src/main/resources/mgnl-resources/media/css/folderView.css =================================================================== --- trunk/openutils-mgnlmedia/src/main/resources/mgnl-resources/media/css/folderView.css 2010-09-01 15:09:21 UTC (rev 2994) +++ trunk/openutils-mgnlmedia/src/main/resources/mgnl-resources/media/css/folderView.css 2010-09-01 17:32:22 UTC (rev 2995) @@ -78,6 +78,10 @@ background-position: 0 -80px; } +#navigation select#sorting { + width: 150px; +} + .voice a,.voice a:visited,.voice a:hover { text-decoration: none; color: #333; Added: trunk/openutils-mgnlmedia/src/main/resources/mgnl-resources/media/css/images/myselect.gif =================================================================== (Binary files differ) Property changes on: trunk/openutils-mgnlmedia/src/main/resources/mgnl-resources/media/css/images/myselect.gif ___________________________________________________________________ Added: svn:mime-type + image/gif Added: trunk/openutils-mgnlmedia/src/main/resources/mgnl-resources/media/css/images/select-left.png =================================================================== (Binary files differ) Property changes on: trunk/openutils-mgnlmedia/src/main/resources/mgnl-resources/media/css/images/select-left.png ___________________________________________________________________ Added: svn:mime-type + image/png Added: trunk/openutils-mgnlmedia/src/main/resources/mgnl-resources/media/css/images/select-right.png =================================================================== (Binary files differ) Property changes on: trunk/openutils-mgnlmedia/src/main/resources/mgnl-resources/media/css/images/select-right.png ___________________________________________________________________ Added: svn:mime-type + image/png Added: trunk/openutils-mgnlmedia/src/main/resources/mgnl-resources/media/css/mooSelecta.css =================================================================== --- trunk/openutils-mgnlmedia/src/main/resources/mgnl-resources/media/css/mooSelecta.css (rev 0) +++ trunk/openutils-mgnlmedia/src/main/resources/mgnl-resources/media/css/mooSelecta.css 2010-09-01 17:32:22 UTC (rev 2995) @@ -0,0 +1,132 @@ +body{ background-color:#FFF;font-family:Verdana;font-size:12px;line-height:1.2} + +div.formLeft { + width: 170px; + padding-top: 4px; + float: left; + text-align: right; + padding-right: 5px; +} + +div.formLeft label.orange_title { + float: right; +} + +div.formLeft label.orange_title { + float: right; +} +div.formRight { + float: left; + width: 370px; + min-height: 25px; +} +.left { + float: left; +} +.cur { + _cursor: hand; + cursor: pointer; +} + +.orange_title { + color: #f60; + font-weight: bold; + font-size: 14px; + display: inline; + float: left; + padding-bottom: 4px; +} + +div.selectaTrigger { + font-weight: bold; + height: 20px; + background: url(images/myselect.gif) no-repeat top right; + border-left: 1px solid #000; + padding-right: 30px; + padding-left: 5px; + padding-top: 5px; + overflow: hidden; +} + +select.selecta { + height: 25px; +} + +div.selectaWrapper { + border: 1px solid #000; + border-top: 0; + position: absolute; + z-index: 10000; + background: white; + overflow: hidden; + overflow-y: auto; +} +div.selectaOption { + padding: 4px; + padding-bottom: 2px; + padding-top: 2px; + border-bottom: 1px solid #eee; + clear: both; +} +div.selectaOptionSelected { + background: #ffffcf; +} +div.selectaOptionOver { + background: yellow; +} + +div.selecta2 { + background: url(images/select-right.png) no-repeat right center; + font-weight: bold; + height: 18px; + padding-top: 3px; + padding-left: 4px; + padding-right: 20px; + overflow: hidden; + color: white; +} + +div.selecta2Wrapper { + border: 1px solid #b6b7bf; + border-top: 0px; + padding: 2px; + background: #f2f2f2; + overflow: hidden; + overflow-y: auto; + position: absolute; +} + +div.selecta2Option { + padding: 2px; + padding-left: 4px; + + clear: both; + background: #fff; + color: #555; + font-weight: bold; +} + +div.selecta2OptionSelected { + background: #666; + color: #ffffcf; + +} +div.selecta2OptionOver { + background: #444; + color: #ffffcf; +} + + + +.shadowy { + -moz-box-shadow: 1px 1px 2px #000; + -webkit-box-shadow: 1px 1px 2px #000; + box-shadow: 1px 1px 2px #000; +} + +#debug { + background: red; + color: yellow; + padding: 5px; + margin: 5px; +} \ No newline at end of file Property changes on: trunk/openutils-mgnlmedia/src/main/resources/mgnl-resources/media/css/mooSelecta.css ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + Author Date Id Revision Added: svn:eol-style + native Added: trunk/openutils-mgnlmedia/src/main/resources/mgnl-resources/media/js/mooSelecta.js =================================================================== --- trunk/openutils-mgnlmedia/src/main/resources/mgnl-resources/media/js/mooSelecta.js (rev 0) +++ trunk/openutils-mgnlmedia/src/main/resources/mgnl-resources/media/js/mooSelecta.js 2010-09-01 17:32:22 UTC (rev 2995) @@ -0,0 +1,424 @@ +/* +--- +description: mooSelecta, select element styling replacement + +license: + MIT-style + +authors: +- Dimitar Christoff + +requires: + core/1.2.4: '*' + +provides: [Element] + +... +*/ + +var mooSelecta = new Class({ + + version: 1.2, + + updated: "29/03/2010 15:47:49", + + + Implements: [Options,Events], + + // default options + // don't change these here but on the instance (unless you want to) + options: { + selector: "selecta", // class / selector for selects to convert + triggerClass: "selectaTrigger", // class of the replacement div + triggerPadding: 30+5, // compensate for left/right padding of text + triggerBeforeImage: "", // advanced styling of trigger like a round image + triggerBeforeImageWidth: 0, // need to compensate width + triggerBeforeImageHeight: 0, // and know height. + wrapperClass: "selectaWrapper", // popup wrapper class + wrapperWidthAdjustment: 0, // you can add or substract to width if not matching, use +/- value + wrapperShadow: "shadowy", // extra class applied to wrapper, like one with box-shadow + wrapperHeight: 0, // maximum allowed height for dropdown before it scrolls + optionClass: "selectaOption", // base class of indivdual options + optionClassSelected: "selectaOptionSelected", // pre-selected value class + optionClassOver: "selectaOptionOver", // onmouseover option class + allowTextSelect: false, // experimental to stop accdiental text selection + // these are keycodes that correspond to alpha numerics on most ISO keyboards for index lookups of options + allowedKeyboardCodes: [48,49,50,51,52,53,54,55,56,57,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90], + useClickListener: true // binds click events to check for clicks away from dropdown. + }, + + Binds: ["_bindClickListener"], + + // internal hashed collection of managed selects + selects: {}, + + // hash that references options per select for keycode lookups + optionList: {}, + + // false or contains a pointer to the last select that has opened the menu + focused: false, + + initialize: function(options) { + // start everything. + this.setOptions(options); + + // locate and apply selects to all required ones. + var selects = $$('select.'+this.options.selector); + + if (!selects.length) + return "nothing to do, selector came up empty!"; + + selects.each(this.replaceSelect.bind(this)); + + // convert object to hash + this.selects = new Hash(this.selects); + + // bind mouseclicks and keytyping + this.bindListeners(); + }, + + replaceSelect: function(el) { + // public method that replaces selects + var el = document.id(el); // adds uid. + + if (!el) return; + + // gets existing element's width to use + var width = el.getSize().x; + + // default selected to go into wrapper + var selectedOption = el.getElements("option").filter(function(op) { + return op.getProperty("selected"); + }); + + // clean up old instances. + if (el.retrieve("triggerElement")) + el.retrieve("triggerElement").dispose(); + if (el.retrieve("wrapper")) + el.retrieve("wrapper").dispose(); + + + // build the top visible element + el.store("triggerElement", new Element("div", { + "class": this.options.triggerClass, + styles: { + width: width - this.options.triggerPadding + } + }).inject(el, "after").addClass("cur")); // cur is a class that changes cursor to a clicky one. + + // re-adjust width after the trigger has been done so wrapper can match it. + width = el.retrieve("triggerElement").getSize().x - 2 - this.options.triggerBeforeImageWidth + this.options.wrapperWidthAdjustment; + + // build the menu wrapper + + // if we have an image to pre-pend, add it. + if (this.options.triggerBeforeImage.length) { + new Element("div", { + styles: { + float: "left", + position: (Browser.Engine.trident4) ? "absolute" : "relative", + background: "url("+this.options.triggerBeforeImage+") no-repeat", + width: this.options.triggerBeforeImageWidth, + height: this.options.triggerBeforeImageHeight + } + }).inject(el.retrieve("triggerElement"), "before"); + } + + // create the options wrapper + el.store("wrapper", new Element("div", { + "class": this.options.wrapperClass, + styles: { + width: width, + zIndex: 10000 + } + }).inject(el.retrieve("triggerElement"), "after").addClass(this.options.wrapperShadow)); + + // now hide the original selects off-screen + // this is so the tab indexing and by-label focus works and hands us back contol. + el.set({ + styles: { + position: "absolute", + left: -1000 + }, + events: { + focus: function() { + if (this.focused) + this._hideOptions(); + + this.focused = el; + this._toggleOptions(el); + + }.bind(this), + blur: function(e) { + if (this.focused == el) + this._toggleOptions(el); + }.bind(this) + } + }); + + // handle labels so they don't interfere by launching a semi-full event. + var lbl = document.getElement("label[for="+el.get("id")+"]"); + if (el.get("id") && lbl) { + lbl.addEvent("click", function(e) { + new Event(e).stop(); + el.fireEvent("focus"); + }); + } + + + // get all options and port them to wrapper + el.getElements('option').each(function(option) { + var selected = false; + if (option.getProperty("selected")) { + el.retrieve("triggerElement").set("html", option.get("text")); + selected = true; + } + this._addOption(option, el, selected); + }, this); + + // figure out height of wrapper and reduce if needed + if (this.options.wrapperHeight) { // if greater than 0 care about this + var height = el.retrieve("wrapper").getSize().y; + if (height > this.options.wrapperHeight) { + el.retrieve("wrapper").setStyles({ + height: this.options.wrapperHeight + }); + } + } + + // hide the menu by default. + el.retrieve("wrapper").setStyle("display", "none"); + + // attach a click event to trigger element + el.retrieve("triggerElement").addEvents({ + click: function(e) { + new Event(e).stop(); + // toggler, click on opened closes it. + el.fireEvent((this.focused == el) ? "blur" : "focus"); + }.bind(this) + }); + + // export the managed select to the hash + if (el.uid && el) + this.selects[el.uid] = el; + + }, // end .replaceSelect(); + + bindListeners: function() { + // setup valrious click / key events + + if (this.options.useClickListener) + document.addEvent("click", this._bindClickListener); + + document.addEvents({ + // keyboard listener + keydown: function(e) { + var e = new Event(e); + + // if no menu is currently open, don't do anything. + if (!this.focused) + return; + + switch(e.code) { + case 40: // down arrow option navigation + new Event(e).stop(); + // ops should really be cached outside here + var ops = this.focused.retrieve("wrapper").getElements("div."+this.options.optionClass), done = false; + + ops.each(function(el, i) { + if (ops.length-1 == i || done) + return; + + if (el.hasClass(this.options.optionClassSelected)) { + ops.removeClass(this.options.optionClassOver); + ops[i+1].addClass(this.options.optionClassSelected).addClass(this.options.optionClassOver); + el.removeClass(this.options.optionClassSelected); + done = true; + } + }, this); + + + break; + case 38: // up arrow option navigation + new Event(e).stop(); + var ops = this.focused.retrieve("wrapper").getElements("div."+this.options.optionClass), done = false; + + ops.each(function(el, i) { + if (done) + return; + + if (el.hasClass(this.options.optionClassSelected)) { + if (i > 0) { + ops.removeClass(this.options.optionClassOver); + ops[i-1].addClass(this.options.optionClassSelected).addClass(this.options.optionClassOver); + el.removeClass(this.options.optionClassSelected); + } + done = true; + } + }, this); + + + break; + case 13: // enter + new Event(e).stop(); + this.focused.retrieve("wrapper").getElements("div."+this.options.optionClassSelected).fireEvent("click"); + break; + case 9: // tabbed out, blur auto... + this._hideOptions(this.focused); + break; + case 34: + case 35: + // go to last option via pgdn or end + new Event(e).stop(); + var old = this.focused; + this.focused.retrieve("wrapper").getElements("div."+this.options.optionClass).getLast().fireEvent("click"); + old.fireEvent("focus"); + + break; + case 33: + case 36: + // go to first option via pgup or home + new Event(e).stop(); + var old = this.focused; + this.focused.retrieve("wrapper").getElement("div."+this.options.optionClass).fireEvent("click"); + old.fireEvent("focus"); + + break; + default: + // the "other" keys. + + var old = this.focused, ops = this.focused.retrieve("wrapper").getElements("div."+this.options.optionClass); + + // is is alpha numeric allowed? + if (this.options.allowedKeyboardCodes.contains(e.code)) { + // loop through current option texts array cache for matches + var matchingKeys = []; + var selected = false; + + var applicable = this.optionList["k"+this.focused.uid].filter(function(el, index) { + if (ops[index].hasClass(this.options.optionClassSelected)) selected = index; + var match = el.indexOf(e.key) == 0; + if (match) + matchingKeys.push(index); + return match; + }, this); + + if (applicable.length) { + if (!matchingKeys.contains(selected)) { + selected = matchingKeys[0]; + } + else { + if (ops[selected+1] && matchingKeys.contains(selected+1)) { + selected++; + } + else { + selected = matchingKeys[0]; + } + + } + + ops[selected].fireEvent("click"); + old.fireEvent("focus"); + done = true; + } + } + else { + // do nothing or disable comment to see other keys you may like to bind. + // console.log(e.code, e.key); + } + break; + } + }.bind(this) + }); + }, // end .bindListeners() + + _bindClickListener: function(e) { + // listens for client clicks away from triggers and closes like real selects do when user loses interest + var e = new Event(e); + + // using a collection which saves a click on an element that's not extended with .hasClass + if ($$(e.target).hasClass(this.options.triggerClass).contains(false)) { + this._hideOptions(); + } + }, + + _addOption: function(option, el, selected) { + // internal method called by replaceSelect that adds each option as a div within the wrapper + var text = option.get("text").trim(); + if (!text.length) + text = " "; + + // store options relevant to element uid. + var oldList = this.optionList["k" + el.uid] || []; + oldList.push(text.toLowerCase()); + var tempObj = {}; + tempObj["k" + el.uid] = oldList; + $extend(this.optionList, tempObj); + // end store + + var opDiv = new Element("div", { + "class": this.options.optionClass, + html: text, + events: { + mouseenter: function() { + opDiv.addClass(this.options.optionClassOver); + }.bind(this), + mouseleave: function() { + opDiv.removeClass(this.options.optionClassOver); + }.bind(this), + click: function(e) { + if (e && e.type && e.stop) + e.stop(); + + // menu stuff visual + el.retrieve("wrapper").getChildren().removeClass(this.options.optionClassSelected); + opDiv.addClass(this.options.optionClassSelected); + el.retrieve("triggerElement").set("html", opDiv.get("text")); + + // now handle change in the real select + el.set("value", opDiv.retrieve("value")).fireEvent("change", e); + this._toggleOptions(el); + }.bind(this) + } + }).store("value", option.get("value")).inject(el.retrieve("wrapper")).addClass("cur"); + + if (selected) + opDiv.addClass(this.options.optionClassSelected); + + }, + + _toggleOptions: function(el) { + // toggles visibility on click + var vis = el.retrieve("wrapper").getStyle("display"); + el.retrieve("wrapper").setStyle("display", (vis == "none") ? "block" : "none").getChildren().removeClass(this.options.optionClassOver); + this.focused = (vis != "none") ? false : el; + + // scroll to selected from .toElement in core but w/o a fx.slide instance + var parent = el.retrieve("wrapper").getPosition(this.options.overflown); + var target = el.retrieve("wrapper").getElement("div." + this.options.optionClassSelected).getPosition(this.options.overflown); + el.retrieve("wrapper").scrollTo(target.x - parent.x, target.y - parent.y); + this._clearSelection(); + }, + + _hideOptions: function() { + // private called on cleanup / away click + this.selects.getValues().each(function(el) { + if (el.retrieve("wrapper").getStyle("display") != "none") + el.fireEvent("blur"); + el.retrieve("wrapper").setStyle("display", "none"); + el.focused = false; + }); + }, + + _clearSelection: function() { + // removes document selection + if (this.options.allowTextSelect || Browser.Engine.trident4) // not sure how IE6 does this + return; + + if (document.selection && document.selection.empty) { + document.selection.empty(); + } else if (window.getSelection) { + window.getSelection().removeAllRanges(); + } + } +}); // endClass Property changes on: trunk/openutils-mgnlmedia/src/main/resources/mgnl-resources/media/js/mooSelecta.js ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + Author Date Id Revision Added: svn:eol-style + native Added: trunk/openutils-mgnlmedia/src/main/resources/mgnl-resources/media/js/mootools-1.2.4-core.js =================================================================== --- trunk/openutils-mgnlmedia/src/main/resources/mgnl-resources/media/js/mootools-1.2.4-core.js (rev 0) +++ trunk/openutils-mgnlmedia/src/main/resources/mgnl-resources/media/js/mootools-1.2.4-core.js 2010-09-01 17:32:22 UTC (rev 2995) @@ -0,0 +1,4329 @@ +/* +--- + +script: Core.js + +description: The core of MooTools, contains all the base functions and the Native and Hash implementations. Required by all the other scripts. + +license: MIT-style license. + +copyright: Copyright (c) 2006-2008 [Valerio Proietti](http://mad4milk.net/). + +authors: The MooTools production team (http://mootools.net/developers/) + +inspiration: +- 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) +- Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php) + +provides: [Mootools, Native, Hash.base, Array.each, $util] + +... +*/ + +var MooTools = { + 'version': '1.2.4', + 'build': '0d9113241a90b9cd5643b926795852a2026710d4' +}; + +var Native = function(options){ + options = options || {}; + var name = options.name; + var legacy = options.legacy; + var protect = options.protect; + var methods = options.implement; + var generics = options.generics; + var initialize = options.initialize; + var afterImplement = options.afterImplement || function(){}; + var object = initialize || legacy; + generics = generics !== false; + + object.constructor = Native; + object.$family = {name: 'native'}; + if (legacy && initialize) object.prototype = legacy.prototype; + object.prototype.constructor = object; + + if (name){ + var family = name.toLowerCase(); + object.prototype.$family = {name: family}; + Native.typize(object, family); + } + + var add = function(obj, name, method, force){ + if (!protect || force || !obj.prototype[name]) obj.prototype[name] = method; + if (generics) Native.genericize(obj, name, protect); + afterImplement.call(obj, name, method); + return obj; + }; + + object.alias = function(a1, a2, a3){ + if (typeof a1 == 'string'){ + var pa1 = this.prototype[a1]; + if ((a1 = pa1)) return add(this, a2, a1, a3); + } + for (var a in a1) this.alias(a, a1[a], a2); + return this; + }; + + object.implement = function(a1, a2, a3){ + if (typeof a1 == 'string') return add(this, a1, a2, a3); + for (var p in a1) add(this, p, a1[p], a2); + return this; + }; + + if (methods) object.implement(methods); + + return object; +}; + +Native.genericize = function(object, property, check){ + if ((!check || !object[property]) && typeof object.prototype[property] == 'function') object[property] = function(){ + var args = Array.prototype.slice.call(arguments); + return object.prototype[property].apply(args.shift(), args); + }; +}; + +Native.implement = function(objects, properties){ + for (var i = 0, l = objects.length; i < l; i++) objects[i].implement(properties); +}; + +Native.typize = function(object, family){ + if (!object.type) object.type = function(item){ + return ($type(item) === family); + }; +}; + +(function(){ + var natives = {'Array': Array, 'Date': Date, 'Function': Function, 'Number': Number, 'RegExp': RegExp, 'String': String}; + for (var n in natives) new Native({name: n, initialize: natives[n], protect: true}); + + var types = {'boolean': Boolean, 'native': Native, 'object': Object}; + for (var t in types) Native.typize(types[t], t); + + var generics = { + 'Array': ["concat", "indexOf", "join", "lastIndexOf", "pop", "push", "reverse", "shift", "slice", "sort", "splice", "toString", "unshift", "valueOf"], + 'String': ["charAt", "charCodeAt", "concat", "indexOf", "lastIndexOf", "match", "replace", "search", "slice", "split", "substr", "substring", "toLowerCase", "toUpperCase", "valueOf"] + }; + for (var g in generics){ + for (var i = generics[g].length; i--;) Native.genericize(natives[g], generics[g][i], true); + } +})(); + +var Hash = new Native({ + + name: 'Hash', + + initialize: function(object){ + if ($type(object) == 'hash') object = $unlink(object.getClean()); + for (var key in object) this[key] = object[key]; + return this; + } + +}); + +Hash.implement({ + + forEach: function(fn, bind){ + for (var key in this){ + if (this.hasOwnProperty(key)) fn.call(bind, this[key], key, this); + } + }, + + getClean: function(){ + var clean = {}; + for (var key in this){ + if (this.hasOwnProperty(key)) clean[key] = this[key]; + } + return clean; + }, + + getLength: function(){ + var length = 0; + for (var key in this){ + if (this.hasOwnProperty(key)) length++; + } + return length; + } + +}); + +Hash.alias('forEach', 'each'); + +Array.implement({ + + forEach: function(fn, bind){ + for (var i = 0, l = this.length; i < l; i++) fn.call(bind, this[i], i, this); + } + +}); + +Array.alias('forEach', 'each'); + +function $A(iterable){ + if (iterable.item){ + var l = iterable.length, array = new Array(l); + while (l--) array[l] = iterable[l]; + return array; + } + return Array.prototype.slice.call(iterable); +}; + +function $arguments(i){ + return function(){ + return arguments[i]; + }; +}; + +function $chk(obj){ + return !!(obj || obj === 0); +}; + +function $clear(timer){ + clearTimeout(timer); + clearInterval(timer); + return null; +}; + +function $defined(obj){ + return (obj != undefined); +}; + +function $each(iterable, fn, bind){ + var type = $type(iterable); + ((type == 'arguments' || type == 'collection' || type == 'array') ? Array : Hash).each(iterable, fn, bind); +}; + +function $empty(){}; + +function $extend(original, extended){ + for (var key in (extended || {})) original[key] = extended[key]; + return original; +}; + +function $H(object){ + return new Hash(object); +}; + +function $lambda(value){ + return ($type(value) == 'function') ? value : function(){ + return value; + }; +}; + +function $merge(){ + var args = Array.slice(arguments); + args.unshift({}); + return $mixin.apply(null, args); +}; + +function $mixin(mix){ + for (var i = 1, l = arguments.length; i < l; i++){ + var object = arguments[i]; + if ($type(object) != 'object') continue; + for (var key in object){ + var op = object[key], mp = mix[key]; + mix[key] = (mp && $type(op) == 'object' && $type(mp) == 'object') ? $mixin(mp, op) : $unlink(op); + } + } + return mix; +}; + +function $pick(){ + for (var i = 0, l = arguments.length; i < l; i++){ + if (arguments[i] != undefined) return arguments[i]; + } + return null; +}; + +function $random(min, max){ + return Math.floor(Math.random() * (max - min + 1) + min); +}; + +function $splat(obj){ + var type = $type(obj); + return (type) ? ((type != 'array' && type != 'arguments') ? [obj] : obj) : []; +}; + +var $time = Date.now || function(){ + return +new Date; +}; + +function $try(){ + for (var i = 0, l = arguments.length; i < l; i++){ + try { + return arguments[i](); + } catch(e){} + } + return null; +}; + +function $type(obj){ + if (obj == undefined) return false; + if (obj.$family) return (obj.$family.name == 'number' && !isFinite(obj)) ? false : obj.$family.name; + if (obj.nodeName){ + switch (obj.nodeType){ + case 1: return 'element'; + case 3: return (/\S/).test(obj.nodeValue) ? 'textnode' : 'whitespace'; + } + } else if (typeof obj.length == 'number'){ + if (obj.callee) return 'arguments'; + else if (obj.item) return 'collection'; + } + return typeof obj; +}; + +function $unlink(object){ + var unlinked; + switch ($type(object)){ + case 'object': + unlinked = {}; + for (var p in object) unlinked[p] = $unlink(object[p]); + break; + case 'hash': + unlinked = new Hash(object); + break; + case 'array': + unlinked = []; + for (var i = 0, l = object.length; i < l; i++) unlinked[i] = $unlink(object[i]); + break; + default: return object; + } + return unlinked; +}; + + +/* +--- + +script: Browser.js + +description: The Browser Core. Contains Browser initialization, Window and Document, and the Browser Hash. + +license: MIT-style license. + +requires: +- /Native +- /$util + +provides: [Browser, Window, Document, $exec] + +... +*/ + +var Browser = $merge({ + + Engine: {name: 'unknown', version: 0}, + + Platform: {name: (window.orientation != undefined) ? 'ipod' : (navigator.platform.match(/mac|win|linux/i) || ['other'])[0].toLowerCase()}, + + Features: {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)}, + + Plugins: {}, + + Engines: { + + presto: function(){ + return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); + }, + + trident: function(){ + return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4); + }, + + webkit: function(){ + return (navigator.taintEnabled) ? false : ((Browser.Features.xpath) ? ((Browser.Features.query) ? 525 : 420) : 419); + }, + + gecko: function(){ + return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19 : 18); + } + + } + +}, Browser || {}); + +Browser.Platform[Browser.Platform.name] = true; + +Browser.detect = function(){ + + for (var engine in this.Engines){ + var version = this.Engines[engine](); + if (version){ + this.Engine = {name: engine, version: version}; + this.Engine[engine] = this.Engine[engine + version] = true; + break; + } + } + + return {name: engine, version: version}; + +}; + +Browser.detect(); + +Browser.Request = function(){ + return $try(function(){ + return new XMLHttpRequest(); + }, function(){ + return new ActiveXObject('MSXML2.XMLHTTP'); + }, function(){ + return new ActiveXObject('Microsoft.XMLHTTP'); + }); +}; + +Browser.Features.xhr = !!(Browser.Request()); + +Browser.Plugins.Flash = (function(){ + var version = ($try(function(){ + return navigator.plugins['Shockwave Flash'].description; + }, function(){ + return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version'); + }) || '0 r0').match(/\d+/g); + return {version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0}; +})(); + +function $exec(text){ + if (!text) return text; + if (window.execScript){ + window.execScript(text); + } else { + var script = document.createElement('script'); + script.setAttribute('type', 'text/javascript'); + script[(Browser.Engine.webkit && Browser.Engine.version < 420) ? 'innerText' : 'text'] = text; + document.head.appendChild(script); + document.head.removeChild(script); + } + return text; +}; + +Native.UID = 1; + +var $uid = (Browser.Engine.trident) ? function(item){ + return (item.uid || (item.uid = [Native.UID++]))[0]; +} : function(item){ + return item.uid || (item.uid = Native.UID++); +}; + +var Window = new Native({ + + name: 'Window', + + legacy: (Browser.Engine.trident) ? null: window.Window, + + initialize: function(win){ + $uid(win); + if (!win.Element){ + win.Element = $empty; + if (Browser.Engine.webkit) win.document.createElement("iframe"); //fixes safari 2 + win.Element.prototype = (Browser.Engine.webkit) ? window["[[DOMElement.prototype]]"] : {}; + } + win.document.window = win; + return $extend(win, Window.Prototype); + }, + + afterImplement: function(property, value){ + window[property] = Window.Prototype[property] = value; + } + +}); + +Window.Prototype = {$family: {name: 'window'}}; + +new Window(window); + +var Document = new Native({ + + name: 'Document', + + legacy: (Browser.Engine.trident) ? null: window.Document, + + initialize: function(doc){ + $uid(doc); + doc.head = doc.getElementsByTagName('head')[0]; + doc.html = doc.getElementsByTagName('html')[0]; + if (Browser.Engine.trident && Browser.Engine.version <= 4) $try(function(){ + doc.execCommand("BackgroundImageCache", false, true); + }); + if (Browser.Engine.trident) doc.window.attachEvent('onunload', function(){ + doc.window.detachEvent('onunload', arguments.callee); + doc.head = doc.html = doc.window = null; + }); + return $extend(doc, Document.Prototype); + }, + + afterImplement: function(property, value){ + document[property] = Document.Prototype[property] = value; + } + +}); + +Document.Prototype = {$family: {name: 'document'}}; + +new Document(document); + + +/* +--- + +script: Array.js + +description: Contains Array Prototypes like each, contains, and erase. + +license: MIT-style license. + +requires: +- /$util +- /Array.each + +provides: [Array] + +... +*/ + +Array.implement({ + + every: function(fn, bind){ + for (var i = 0, l = this.length; i < l; i++){ + if (!fn.call(bind, this[i], i, this)) return false; + } + return true; + }, + + filter: function(fn, bind){ + var results = []; + for (var i = 0, l = this.length; i < l; i++){ + if (fn.call(bind, this[i], i, this)) results.push(this[i]); + } + return results; + }, + + clean: function(){ + return this.filter($defined); + }, + + indexOf: function(item, from){ + var len = this.length; + for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++){ + if (this[i] === item) return i; + } + return -1; + }, + + map: function(fn, bind){ + var results = []; + for (var i = 0, l = this.length; i < l; i++) results[i] = fn.call(bind, this[i], i, this); + return results; + }, + + some: function(fn, bind){ + for (var i = 0, l = this.length; i < l; i++){ + if (fn.call(bind, this[i], i, this)) return true; + } + return false; + }, + + associate: function(keys){ + var obj = {}, length = Math.min(this.length, keys.length); + for (var i = 0; i < length; i++) obj[keys[i]] = this[i]; + return obj; + }, + + link: function(object){ + var result = {}; + for (var i = 0, l = this.length; i < l; i++){ + for (var key in object){ + if (object[key](this[i])){ + result[key] = this[i]; + delete object[key]; + break; + } + } + } + return result; + }, + + contains: function(item, from){ + return this.indexOf(item, from) != -1; + }, + + extend: function(array){ + for (var i = 0, j = array.length; i < j; i++) this.push(array[i]); + return this; + }, + + getLast: function(){ + return (this.length) ? this[this.length - 1] : null; + }, + + getRandom: function(){ + return (this.length) ? this[$random(0, this.length - 1)] : null; + }, + + include: function(item){ + if (!this.contains(item)) this.push(item); + return this; + }, + + combine: function(array){ + for (var i = 0, l = array.length; i < l; i++) this.include(array[i]); + return this; + }, + + erase: function(item){ + for (var i = this.length; i--; i){ + if (this[i] === item) this.splice(i, 1); + } + return this; + }, + + empty: function(){ + this.length = 0; + return this; + }, + + flatten: function(){ + var array = []; + for (var i = 0, l = this.length; i < l; i++){ + var type = $type(this[i]); + if (!type) continue; + array = array.concat((type == 'array' || type == 'collection' || type == 'arguments') ? Array.flatten(this[i]) : this[i]); + } + return array; + }, + + hexToRgb: function(array){ + if (this.length != 3) return null; + var rgb = this.map(function(value){ + if (value.length == 1) value += value; + return value.toInt(16); + }); + return (array) ? rgb : 'rgb(' + rgb + ')'; + }, + + rgbToHex: function(array){ + if (this.length < 3) return null; + if (this.length == 4 && this[3] == 0 && !array) return 'transparent'; + var hex = []; + for (var i = 0; i < 3; i++){ + var bit = (this[i] - 0).toString(16); + hex.push((bit.length == 1) ? '0' + bit : bit); + } + return (array) ? hex : '#' + hex.join(''); + } + +}); + + +/* +--- + +script: Function.js + +description: Contains Function Prototypes like create, bind, pass, and delay. + +license: MIT-style license. + +requires: +- /Native +- /$util + +provides: [Function] + +... +*/ + +Function.implement({ + + extend: function(properties){ + for (var property in properties) this[property] = properties[property]; + return this; + }, + + create: function(options){ + var self = this; + options = options || {}; + return function(event){ + var args = options.arguments; + args = (args != undefined) ? $splat(args) : Array.slice(arguments, (options.event) ? 1 : 0); + if (options.event) args = [event || window.event].extend(args); + var returns = function(){ + return self.apply(options.bind || null, args); + }; + if (options.delay) return setTimeout(returns, options.delay); + if (options.periodical) return setInterval(returns, options.periodical); + if (options.attempt) return $try(returns); + return returns(); + }; + }, + + run: function(args, bind){ + return this.apply(bind, $splat(args)); + }, + + pass: function(args, bind){ + return this.create({bind: bind, arguments: args}); + }, + + bind: function(bind, args){ + return this.create({bind: bind, arguments: args}); + }, + + bindWithEvent: function(bind, args){ + return this.create({bind: bind, arguments: args, event: true}); + }, + + attempt: function(args, bind){ + return this.create({bind: bind, arguments: args, attempt: true})(); + }, + + delay: function(delay, bind, args){ + return this.create({bind: bind, arguments: args, delay: delay})(); + }, + + periodical: function(periodical, bind, args){ + return this.create({bind: bind, arguments: args, periodical: periodical})(); + } + +}); + + +/* +--- + +script: Number.js + +description: Contains Number Prototypes like limit, round, times, and ceil. + +license: MIT-style license. + +requires: +- /Native +- /$util + +provides: [Number] + +... +*/ + +Number.implement({ + + limit: function(min, max){ + return Math.min(max, Math.max(min, this)); + }, + + round: function(precision){ + precision = Math.pow(10, precision || 0); + return Math.round(this * precision) / precision; + }, + + times: function(fn, bind){ + for (var i = 0; i < this; i++) fn.call(bind, i, this); + }, + + toFloat: function(){ + return parseFloat(this); + }, + + toInt: function(base){ + return parseInt(this, base || 10); + } + +}); + +Number.alias('times', 'each'); + +(function(math){ + var methods = {}; + math.each(function(name){ + if (!Number[name]) methods[name] = function(){ + return Math[name].apply(null, [this].concat($A(arguments))); + }; + }); + Number.implement(methods); +})(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']); + + +/* +--- + +script: String.js + +description: Contains String Prototypes like camelCase, capitalize, test, and toInt. + +license: MIT-style license. + +requires: +- /Native + +provides: [String] + +... +*/ + +String.implement({ + + test: function(regex, params){ + return ((typeof regex == 'string') ? new RegExp(regex, params) : regex).test(this); + }, + + contains: function(string, separator){ + return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : this.indexOf(string) > -1; + }, + + trim: function(){ + return this.replace(/^\s+|\s+$/g, ''); + }, + + clean: function(){ + return this.replace(/\s+/g, ' ').trim(); + }, + + camelCase: function(){ + return this.replace(/-\D/g, function(match){ + return match.charAt(1).toUpperCase(); + }); + }, + + hyphenate: function(){ + return this.replace(/[A-Z]/g, function(match){ + return ('-' + match.charAt(0).toLowerCase()); + }); + }, + + capitalize: function(){ + return this.replace(/\b[a-z]/g, function(match){ + return match.toUpperCase(); + }); + }, + + escapeRegExp: function(){ + return this.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1'); + }, + + toInt: function(base){ + return parseInt(this, base || 10); + }, + + toFloat: function(){ + return parseFloat(this); + }, + + hexToRgb: function(array){ + var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/); + return (hex) ? hex.slice(1).hexToRgb(array) : null; + }, + + rgbToHex: function(array){ + var rgb = this.match(/\d{1,3}/g); + return (rgb) ? rgb.rgbToHex(array) : null; + }, + + stripScripts: function(option){ + var scripts = ''; + var text = this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(){ + scripts += arguments[1] + '\n'; + return ''; + }); + if (option === true) $exec(scripts); + else if ($type(option) == 'function') option(scripts, text); + return text; + }, + + substitute: function(object, regexp){ + return this.replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){ + if (match.charAt(0) == '\\') return match.slice(1); + return (object[name] != undefined) ? object[name] : ''; + }); + } + +}); + + +/* +--- + +script: Hash.js + +description: Contains Hash Prototypes. Provides a means for overcoming the JavaScript practical impossibility of extending native Objects. + +license: MIT-style license. + +requires: +- /Hash.base + +provides: [Hash] + +... +*/ + +Hash.implement({ + + has: Object.prototype.hasOwnProperty, + + keyOf: function(value){ + for (var key in this){ + if (this.hasOwnProperty(key) && this[key] === value) return key; + } + return null; + }, + + hasValue: function(value){ + return (Hash.keyOf(this, value) !== null); + }, + + extend: function(properties){ + Hash.each(properties || {}, function(value, key){ + Hash.set(this, key, value); + }, this); + return this; + }, + + combine: function(properties){ + Hash.each(properties || {}, function(value, key){ + Hash.include(this, key, value); + }, this); + return this; + }, + + erase: function(key){ + if (this.hasOwnProperty(key)) delete this[key]; + return this; + }, + + get: function(key){ + return (this.hasOwnProperty(key)) ? this[key] : null; + }, + + set: function(key, value){ + if (!this[key] || this.hasOwnProperty(key)) this[key] = value; + return this; + }, + + empty: function(){ + Hash.each(this, function(value, key){ + delete this[key]; + }, this); + return this; + }, + + include: function(key, value){ + if (this[key] == undefined) this[key] = value; + return this; + }, + + map: function(fn, bind){ + var results = new Hash; + Hash.each(this, function(value, key){ + results.set(key, fn.call(bind, value, key, this)); + }, this); + return results; + }, + + filter: function(fn, bind){ + var results = new Hash; + Hash.each(this, function(value, key){ + if (fn.call(bind, value, key, this)) results.set(key, value); + }, this); + return results; + }, + + every: function(fn, bind){ + for (var key in this){ + if (this.hasOwnProperty(key) && !fn.call(bind, this[key], key)) return false; + } + return true; + }, + + some: function(fn, bind){ + for (var key in this){ + if (this.hasOwnProperty(key) && fn.call(bind, this[key], key)) return true; + } + return false; + }, + + getKeys: function(){ + var keys = []; + Hash.each(this, function(value, key){ + keys.push(key); + }); + return keys; + }, + + getValues: function(){ + var values = []; + Hash.each(this, function(value){ + values.push(value); + }); + return values; + }, + + toQueryString: function(base){ + var queryString = []; + Hash.each(this, function(value, key){ + if (base) key = base + '[' + key + ']'; + var result; + switch ($type(value)){ + case 'object': result = Hash.toQueryString(value, key); break; + case 'array': + var qs = {}; + value.each(function(val, i){ + qs[i] = val; + }); + result = Hash.toQueryString(qs, key); + break; + default: result = key + '=' + encodeURIComponent(value); + } + if (value != undefined) queryString.push(result); + }); + + return queryString.join('&'); + } + +}); + +Hash.alias({keyOf: 'indexOf', hasValue: 'contains'}); + + +/* +--- + +script: Event.js + +description: Contains the Event Class, to make the event object cross-browser. + +license: MIT-style license. + +requires: +- /Window +- /Document +- /Hash +- /Array +- /Function +- /String + +provides: [Event] + +... +*/ + +var Event = new Native({ + + name: 'Event', + + initialize: function(event, win){ + win = win || window; + var doc = win.document; + event = event || win.event; + if (event.$extended) return event; + this.$extended = true; + var type = event.type; + var target = event.target || event.srcElement; + while (target && target.nodeType == 3) target = target.parentNode; + + if (type.test(/key/)){ + var code = event.which || event.keyCode; + var key = Event.Keys.keyOf(code); + if (type == 'keydown'){ + var fKey = code - 111; + if (fKey > 0 && fKey < 13) key = 'f' + fKey; + } + key = key || String.fromCharCode(code).toLowerCase(); + } else if (type.match(/(click|mouse|menu)/i)){ + doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body; + var page = { + x: event.pageX || event.clientX + doc.scrollLeft, + y: event.pageY || event.clientY + doc.scrollTop + }; + var client = { + x: (event.pageX) ? event.pageX - win.pageXOffset : event.clientX, + y: (event.pageY) ? event.pageY - win.pageYOffset : event.clientY + }; + if (type.match(/DOMMouseScroll|mousewheel/)){ + var wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3; + } + var rightClick = (event.which == 3) || (event.button == 2); + var related = null; + if (type.match(/over|out/)){ + switch (type){ + case 'mouseover': related = event.relatedTarget || event.fromElement; break; + case 'mouseout': related = event.relatedTarget || event.toElement; + } + if (!(function(){ + while (related && related.nodeType == 3) related = related.parentNode; + return true; + }).create({attempt: Browser.Engine.gecko})()) related = false; + } + } + + return $extend(this, { + event: event, + type: type, + + page: page, + client: client, + rightClick: rightClick, + + wheel: wheel, + + relatedTarget: related, + target: target, + + code: code, + key: key, + + shift: event.shiftKey, + control: event.ctrlKey, + alt: event.altKey, + meta: event.metaKey + }); + } + +}); + +Event.Keys = new Hash({ + 'enter': 13, + 'up': 38, + 'down': 40, + 'left': 37, + 'right': 39, + 'esc': 27, + 'space': 32, + 'backspace': 8, + 'tab': 9, + 'delete': 46 +}); + +Event.implement({ + + stop: function(){ + return this.stopPropagation().preventDefault(); + }, + + stopPropagation: function(){ + if (this.event.stopPropagation) this.event.stopPropagation(); + else this.event.cancelBubble = true; + return this; + }, + + preventDefault: function(){ + if (this.event.preventDefault) this.event.preventDefault(); + else this.event.returnValue = false; + return this; + } + +}); + + +/* +--- + +script: Class.js + +description: Contains the Class Function for easily creating, extending, and implementing reusable Classes. + +license: MIT-style license. + +requires: +- /$util +- /Native +- /Array +- /String +- /Function +- /Number +- /Hash + +provides: [Class] + +... +*/ + +function Class(params){ + + if (params instanceof Function) params = {initialize: params}; + + var newClass = function(){ + Object.reset(this); + if (newClass._prototyping) return this; + this._current = $empty; + var value = (this.initialize) ? this.initialize.apply(this, arguments) : this; + delete this._current; delete this.caller; + return value; + }.extend(this); + + newClass.implement(params); + + newClass.constructor = Class; + newClass.prototype.constructor = newClass; + + return newClass; + +}; + +Function.prototype.protect = function(){ + this._protected = true; + return this; +}; + +Object.reset = function(object, key){ + + if (key == null){ + for (var p in object) Object.reset(object, p); + return object; + } + + delete object[key]; + + switch ($type(object[key])){ + case 'object': + var F = function(){}; + F.prototype = object[key]; + var i = new F; + object[key] = Object.reset(i); + break; + case 'array': object[key] = $unlink(object[key]); break; + } + + return object; + +}; + +new Native({name: 'Class', initialize: Class}).extend({ + + instantiate: function(F){ + F._prototyping = true; + var proto = new F; + delete F._prototyping; + return proto; + }, + + wrap: function(self, key, method){ + if (method._origin) method = method._origin; + + return function(){ + if (method._protected && this._current == null) throw new Error('The method "' + key + '" cannot be called.'); + var caller = this.caller, current = this._current; + this.caller = current; this._current = arguments.callee; + var result = method.apply(this, arguments); + this._current = current; this.caller = caller; + return result; + }.extend({_owner: self, _origin: method, _name: key}); + + } + +}); + +Class.implement({ + + implement: function(key, value){ + + if ($type(key) == 'object'){ + for (var p in key) this.implement(p, key[p]); + return this; + } + + var mutator = Class.Mutators[key]; + + if (mutator){ + value = mutator.call(this, value); + if (value == null) return this; + } + + var proto = this.prototype; + + switch ($type(value)){ + + case 'function': + if (value._hidden) return this; + proto[key] = Class.wrap(this, key, value); + break; + + case 'object': + var previous = proto[key]; + if ($type(previous) == 'object') $mixin(previous, value); + else proto[key] = $unlink(value); + break; + + case 'array': + proto[key] = $unlink(value); + break; + + default: proto[key] = value; + + } + + return this; + + } + +}); + +Class.Mutators = { + + Extends: function(parent){ + + this.parent = parent; + this.prototype = Class.instantiate(parent); + + this.implement('parent', function(){ + var name = this.caller._name, previous = this.caller._owner.parent.prototype[name]; + if (!previous) throw new Error('The method "' + name + '" has no parent.'); + return previous.apply(this, arguments); + }.protect()); + + }, + + Implements: function(items){ + $splat(items).each(function(item){ + if (item instanceof Function) item = Class.instantiate(item); + this.implement(item); + }, this); + + } + +}; + + +/* +--- + +script: Class.Extras.js + +description: Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks. + +license: MIT-style license. + +requires: +- /Class + +provides: [Chain, Events, Options] + +... +*/ + +var Chain = new Class({ + + $chain: [], + + chain: function(){ + this.$chain.extend(Array.flatten(arguments)); + return this; + }, + + callChain: function(){ + return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false; + }, + + clearChain: function(){ + this.$chain.empty(); + return this; + } + +}); + +var Events = new Class({ + + $events: {}, + + addEvent: function(type, fn, internal){ + type = Events.removeOn(type); + if (fn != $empty){ + this.$events[type] = this.$events[type] || []; + this.$events[type].include(fn); + if (internal) fn.internal = true; + } + return this; + }, + + addEvents: function(events){ + for (var type in events) this.addEvent(type, events[type]); + return this; + }, + + fireEvent: function(type, args, delay){ + type = Events.removeOn(type); + if (!this.$events || !this.$events[type]) return this; + this.$events[type].each(function(fn){ + fn.create({'bind': this, 'delay': delay, 'arguments': args})(); + }, this); + return this; + }, + + removeEvent: function(type, fn){ + type = Events.removeOn(type); + if (!this.$events[type]) return this; + if (!fn.internal) this.$events[type].erase(fn); + return this; + }, + + removeEvents: function(events){ + var type; + if ($type(events) == 'object'){ + for (type in events) this.removeEvent(type, events[type]); + return this; + } + if (events) events = Events.removeOn(events); + for (type in this.$events){ + if (events && events != type) continue; + var fns = this.$events[type]; + for (var i = fns.length; i--; i) this.removeEvent(type, fns[i]); + } + return this; + } + +}); + +Events.removeOn = function(string){ + return string.replace(/^on([A-Z])/, function(full, first){ + return first.toLowerCase(); + }); +}; + +var Options = new Class({ + + setOptions: function(){ + this.options = $merge.run([this.options].extend(arguments)); + if (!this.addEvent) return this; + for (var option in this.options){ + if ($type(this.options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue; + this.addEvent(option, this.options[option]); + delete this.options[option]; + } + return this; + } + +}); + + +/* +--- + +script: Element.js + +description: 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. + +license: MIT-style license. + +requires: +- /Window +- /Document +- /Array +- /String +- /Function +- /Number +- /Hash + +provides: [Element, Elements, $, $$, Iframe] + +... +*/ + +var Element = new Native({ + + name: 'Element', + + legacy: window.Element, + + initialize: function(tag, props){ + var konstructor = Element.Constructors.get(tag); + if (konstructor) return konstructor(props); + if (typeof tag == 'string') return document.newElement(tag, props); + return document.id(tag).set(props); + }, + + afterImplement: function(key, value){ + Element.Prototype[key] = value; + if (Array[key]) return; + Elements.implement(key, function(){ + var items = [], elements = true; + for (var i = 0, j = this.length; i < j; i++){ + var returns = this[i][key].apply(this[i], arguments); + items.push(returns); + if (elements) elements = ($type(returns) == 'element'); + } + return (elements) ? new Elements(items) : items; + }); + } + +}); + +Element.Prototype = {$family: {name: 'element'}}; + +Element.Constructors = new Hash; + +var IFrame = new Native({ + + name: 'IFrame', + + generics: false, + + initialize: function(){ + var params = Array.link(arguments, {properties: Object.type, iframe: $defined}); + var props = params.properties || {}; + var iframe = document.id(params.iframe); + var onload = props.onload || $empty; + delete props.onload; + props.id = props.name = $pick(props.id, props.name, iframe ? (iframe.id || iframe.name) : 'IFrame_' + $time()); + iframe = new Element(iframe || 'iframe', props); + var onFrameLoad = function(){ + var host = $try(function(){ + return iframe.contentWindow.location.host; + }); + if (!host || host == window.location.host){ + var win = new Window(iframe.contentWindow); + new Document(iframe.contentWindow.document); + $extend(win.Element.prototype, Element.Prototype); + } + onload.call(iframe.contentWindow, iframe.contentWindow.document); + }; + var contentWindow = $try(function(){ + return iframe.contentWindow; + }); + ((contentWindow && contentWindow.document.body) || window.frames[props.id]) ? onFrameLoad() : iframe.addListener('load', onFrameLoad); + return iframe; + } + +}); + +var Elements = new Native({ + + initialize: function(elements, options){ + options = $extend({ddup: true, cash: true}, options); + elements = elements || []; + if (options.ddup || options.cash){ + var uniques = {}, returned = []; + for (var i = 0, l = elements.length; i < l; i++){ + var el = document.id(elements[i], !options.cash); + if (options.ddup){ + if (uniques[el.uid]) continue; + uniques[el.uid] = true; + } + if (el) returned.push(el); + } + elements = returned; + } + return (options.cash) ? $extend(elements, this) : elements; + } + +}); + +Elements.implement({ + + filter: function(filter, bind){ + if (!filter) return this; + return new Elements(Array.filter(this, (typeof filter == 'string') ? function(item){ + return item.match(filter); + } : filter, bind)); + } + +}); + +Document.implement({ + + newElement: function(tag, props){ + if (Browser.Engine.trident && props){ + ['name', 'type', 'checked'].each(function(attribute){ + if (!props[attribute]) return; + tag += ' ' + attribute + '="' + props[attribute] + '"'; + if (attribute != 'checked') delete props[attribute]; + }); + tag = '<' + tag + '>'; + } + return document.id(this.createElement(ta... [truncated message content] |