/** * Bootstrap JavaScript Library * (c) 2006 - 2010 Juerg Lehni, http://scratchdisk.com/ * * Bootstrap is released under the MIT license * http://bootstrap-js.net/ * * Inspirations: * http://dean.edwards.name/weblog/2006/03/base/ * http://dev.helma.org/Wiki/JavaScript+Inheritance+Sugar/ * http://prototypejs.org/ * http://mootools.net/ * * Some code in this file is based on Mootools.net and adapted to the * architecture of Bootstrap, with added changes in design and architecture * where deemeded necessary. * See http://www.bootstrap-js.net/wiki/MootoolsDifferences */ new function() { var fix = !this.__proto__ && [Function, Number, Boolean, String, Array, Date, RegExp]; if (fix) for (var i in fix) fix[i].prototype.__proto__ = fix[i].prototype; function has(obj, name) { return (!fix || name != '__proto__') && obj.hasOwnProperty(name); } var _define = Object.defineProperty, _describe = Object.getOwnPropertyDescriptor; function define(obj, name, desc) { if (_define) try { delete obj[name]; return _define(obj, name, desc); } catch (e) {} if ((desc.get || desc.set) && obj.__defineGetter__) { if (desc.get) obj.__defineGetter__(name, desc.get); if (desc.set) obj.__defineSetter__(name, desc.set); } else { obj[name] = desc.value; } return obj; } function describe(obj, name) { if (_describe) try { return _describe(obj, name); } catch (e) {} var get = obj.__lookupGetter__ && obj.__lookupGetter__(name); return get ? { enumerable: true, configurable: true, get: get, set: obj.__lookupSetter__(name) } : has(obj, name) ? { enumerable: true, configurable: true, writable: true, value: obj[name] } : null; } function inject(dest, src, enumerable, base, generics) { var beans; function field(name, val, dontCheck, generics) { if (!val) val = (val = describe(src, name)) && (val.get ? val : val.value); var type = typeof val, func = type == 'function', res = val, prev = dest[name], bean, part; if (generics && func && (!src.preserve || !generics[name])) generics[name] = function(bind) { return bind && dest[name].apply(bind, Array.prototype.slice.call(arguments, 1)); } if ((dontCheck || val !== undefined && has(src, name)) && (!prev || !src.preserve)) { if (func) { if (prev && /\bthis\.base\b/.test(val)) { var fromBase = base && base[name] == prev; res = (function() { var tmp = describe(this, 'base'); define(this, 'base', { value: fromBase ? base[name] : prev, configurable: true }); try { return val.apply(this, arguments); } finally { tmp ? define(this, 'base', tmp) : delete this.base; } }).pretend(val); } if (beans && (bean = name.match(/^(get|is)(([A-Z])(.*))$/))) beans.push([ bean[3].toLowerCase() + bean[4], bean[2] ]); } if (!res || func || !res.get && !res.set) res = { value: res, writable: true }; if ((describe(dest, name) || { configurable: true }).configurable) { res.configurable = true; res.enumerable = enumerable; } define(dest, name, res); } } if (src) { beans = src.beans && []; for (var name in src) if (has(src, name) && !/^(statics|generics|preserve|beans|prototype|__proto__|toString|valueOf)$/.test(name)) field(name, null, true, generics); field('toString'); field('valueOf'); for (var i = 0, l = beans && beans.length; i < l; i++) try { var bean = beans[i], part = bean[1]; field(bean[0], { get: dest['get' + part] || dest['is' + part], set: dest['set' + part] }, true); } catch (e) {} } } function extend(obj) { function ctor(dont) { if (fix) define(this, '__proto__', { value: obj }); if (this.initialize && dont !== ctor.dont) return this.initialize.apply(this, arguments); } ctor.prototype = obj; ctor.toString = function() { return (this.prototype.initialize || function() {}).toString(); } return ctor; } inject(Function.prototype, { inject: function(src) { if (src) { var proto = this.prototype, base = proto.__proto__ && proto.__proto__.constructor; inject(proto, src, false, base && base.prototype, src.generics && this); inject(this, src.statics, true, base); } for (var i = 1, l = arguments.length; i < l; i++) this.inject(arguments[i]); return this; }, extend: function(src) { var proto = new this(this.dont), ctor = extend(proto); define(proto, 'constructor', { value: ctor, writable: true, configurable: true }); ctor.dont = {}; inject(ctor, this, true); return arguments.length ? this.inject.apply(ctor, arguments) : ctor; }, pretend: function(fn) { this.toString = function() { return fn.toString(); } this.valueOf = function() { return fn.valueOf(); } return this; } }); function each(obj, iter, bind) { return obj ? (typeof obj.length == 'number' ? Array : Hash).prototype.each.call(obj, iter, bind) : bind; } Base = Object.extend({ has: function(name) { return has(this, name); }, each: function(iter, bind) { return each(this, iter, bind); }, inject: function() { for (var i = 0, l = arguments.length; i < l; i++) inject(this, arguments[i]); return this; }, extend: function() { var res = new (extend(this)); return res.inject.apply(res, arguments); }, statics: { has: has, each: each, define: define, describe: describe, type: function(obj) { return (obj || obj === 0) && ( obj._type || obj.nodeName && ( obj.nodeType == 1 && 'element' || obj.nodeType == 3 && 'textnode' || obj.nodeType == 9 && 'document') || obj.location && obj.frames && obj.history && 'window' || typeof obj) || null; }, check: function(obj) { return !!(obj || obj === 0); }, pick: function() { for (var i = 0, l = arguments.length; i < l; i++) if (arguments[i] !== undefined) return arguments[i]; return null; }, iterator: function(iter) { return !iter ? function(val) { return val } : typeof iter != 'function' ? function(val) { return val == iter } : iter; }, stop: {} } }, { generics: true, debug: function() { return /^(string|number|function|regexp)$/.test(Base.type(this)) ? this : Base.each(this, function(val, key) { this.push(key + ': ' + val); }, []).join(', '); }, clone: function() { return Base.each(this, function(val, i) { this[i] = val; }, new this.constructor()); }, toQueryString: function() { return Base.each(this, function(val, key) { this.push(key + '=' + encodeURIComponent(val)); }, []).join('&'); } }); } $each = Base.each; $type = Base.type; $check = Base.check; $pick = Base.pick; $stop = $break = Base.stop; Enumerable = { generics: true, preserve: true, findEntry: function(iter, bind) { var that = this, iter = Base.iterator(iter), ret = null; Base.each(this, function(val, key) { var res = iter.call(bind, val, key, that); if (res) { ret = { key: key, value: val, result: res }; throw Base.stop; } }); return ret; }, find: function(iter, bind) { var entry = this.findEntry(iter, bind); return entry && entry.result; }, contains: function(iter) { return !!this.findEntry(iter); }, remove: function(iter, bind) { var entry = this.findEntry(iter, bind); if (entry) { delete this[entry.key]; return entry.value; } }, filter: function(iter, bind) { var that = this; return Base.each(this, function(val, i) { if (iter.call(bind, val, i, that)) this[this.length] = val; }, []); }, map: function(iter, bind) { var that = this; return Base.each(this, function(val, i) { this[this.length] = iter.call(bind, val, i, that); }, []); }, every: function(iter, bind) { var that = this; return this.find(function(val, i) { return !iter.call(this, val, i, that); }, bind || null) == null; }, some: function(iter, bind) { return this.find(iter, bind || null) != null; }, collect: function(iter, bind) { var that = this, iter = Base.iterator(iter); return Base.each(this, function(val, i) { val = iter.call(bind, val, i, that); if (val != null) this[this.length] = val; }, []); }, max: function(iter, bind) { var that = this, iter = Base.iterator(iter); return Base.each(this, function(val, i) { val = iter.call(bind, val, i, that); if (val >= (this.max || val)) this.max = val; }, {}).max; }, min: function(iter, bind) { var that = this, iter = Base.iterator(iter); return Base.each(this, function(val, i) { val = iter.call(bind, val, i, that); if (val <= (this.min || val)) this.min = val; }, {}).min; }, pluck: function(prop) { return this.map(function(val) { return val[prop]; }); }, sortBy: function(iter, bind) { var that = this, iter = Base.iterator(iter); return this.map(function(val, i) { return { value: val, compare: iter.call(bind, val, i, that) }; }, bind).sort(function(left, right) { var a = left.compare, b = right.compare; return a < b ? -1 : a > b ? 1 : 0; }).pluck('value'); }, toArray: function() { return this.map(function(value) { return value; }); } }; Hash = Base.extend(Enumerable, { generics: true, initialize: function(arg) { if (typeof arg == 'string') { for (var i = 0, l = arguments.length; i < l; i += 2) this[arguments[i]] = arguments[i + 1]; } else { this.append.apply(this, arguments); } return this; }, each: function(iter, bind) { var bind = bind || this, iter = Base.iterator(iter); try { for (var i in this) if (this.hasOwnProperty(i)) iter.call(bind, this[i], i, this); } catch (e) { if (e !== Base.stop) throw e; } return bind; }, append: function() { for (var i = 0, l = arguments.length; i < l; i++) { var obj = arguments[i]; for (var key in obj) if (Base.has(obj, key)) this[key] = obj[key]; } return this; }, merge: function() { return Array.each(arguments, function(obj) { Base.each(obj, function(val, key) { this[key] = Base.type(this[key]) == 'object' ? Hash.prototype.merge.call(this[key], val) : Base.type(val) == 'object' ? Base.clone(val) : val; }, this); }, this); }, getKeys: function() { return Hash.getKeys(this); }, getValues: Enumerable.toArray, getSize: function() { return this.each(function() { this.size++; }, { size: 0 }).size; }, statics: { create: function(obj) { return arguments.length == 1 && obj.constructor == Hash ? obj : Hash.prototype.initialize.apply(new Hash(), arguments); }, getKeys: Object.keys || function(obj) { return Hash.map(function(val, key) { return key; }); } } }); $H = Hash.create; Array.inject({ generics: true, preserve: true, _type: 'array', forEach: function(iter, bind) { for (var i = 0, l = this.length; i < l; i++) iter.call(bind, this[i], i, this); }, indexOf: function(obj, i) { i = i || 0; if (i < 0) i = Math.max(0, this.length + i); for (var l = this.length; i < l; i++) if (this[i] == obj) return i; return -1; }, lastIndexOf: function(obj, i) { i = i != null ? i : this.length - 1; if (i < 0) i = Math.max(0, this.length + i); for (; i >= 0; i--) if (this[i] == obj) return i; return -1; }, filter: function(iter, bind) { var res = []; for (var i = 0, l = this.length; i < l; i++) { var val = this[i]; if (iter.call(bind, val, i, this)) res[res.length] = val; } return res; }, map: function(iter, bind) { var res = new Array(this.length); for (var i = 0, l = this.length; i < l; i++) res[i] = iter.call(bind, this[i], i, this); return res; }, every: function(iter, bind) { for (var i = 0, l = this.length; i < l; i++) if (!iter.call(bind, this[i], i, this)) return false; return true; }, some: function(iter, bind) { for (var i = 0, l = this.length; i < l; i++) if (iter.call(bind, this[i], i, this)) return true; return false; }, reduce: function(fn, value) { var i = 0; if (arguments.length < 2 && this.length) value = this[i++]; for (var l = this.length; i < l; i++) value = fn.call(null, value, this[i], i, this); return value; } }, Enumerable, { generics: true, beans: true, each: function(iter, bind) { try { Array.prototype.forEach.call(this, Base.iterator(iter), bind = bind || this); } catch (e) { if (e !== Base.stop) throw e; } return bind; }, collect: function(iter, bind) { var that = this; return this.each(function(val, i) { if ((val = iter.call(bind, val, i, that)) != null) this[this.length] = val; }, []); }, findEntry: function(iter, bind) { if (typeof iter != 'function') { var i = this.indexOf(iter); return i == -1 ? null : { key: i, value: iter, result: iter }; } return Enumerable.findEntry.call(this, iter, bind); }, remove: function(iter, bind) { var entry = this.findEntry(iter, bind); if (entry) { this.splice(entry.key, 1); return entry.value; } }, toArray: function() { return Array.prototype.slice.call(this); }, clone: function() { return this.toArray(); }, clear: function() { this.length = 0; }, compact: function() { return this.filter(function(value) { return value != null; }); }, append: function(items) { for (var i = 0, l = items.length; i < l; i++) this[this.length++] = items[i]; return this; }, associate: function(obj) { if (!obj) obj = this; else if (typeof obj == 'function') obj = this.map(obj); if (obj.length != null) { var that = this; return Base.each(obj, function(name, index) { this[name] = that[index]; if (index == that.length) throw Base.stop; }, {}); } else { obj = Hash.append({}, obj); return Array.each(this, function(val) { var type = Base.type(val); Base.each(obj, function(hint, name) { if (hint == 'any' || type == hint) { this[name] = val; delete obj[name]; throw Base.stop; } }, this); }, {}); } }, flatten: function() { return Array.each(this, function(val) { if (val != null && val.flatten) this.append(val.flatten()); else this.push(val); }, []); }, swap: function(i, j) { var tmp = this[j]; this[j] = this[i]; this[i] = tmp; return tmp; }, shuffle: function() { var res = this.clone(); var i = this.length; while (i--) res.swap(i, Math.rand(i + 1)); return res; }, pick: function() { return this[Math.rand(this.length)]; }, getFirst: function() { return this[0]; }, getLast: function() { return this[this.length - 1]; } }, new function() { function combine(subtract) { return function(items) { var res = new this.constructor(); for (var i = this.length - 1; i >= 0; i--) if (subtract == !Array.find(items, this[i])) res.push(this[i]); return res; } } return { subtract: combine(true), intersect: combine(false) } }); Array.inject(new function() { var proto = Array.prototype, fields = ['push','pop','shift','unshift','sort', 'reverse','join','slice','splice','forEach','indexOf','lastIndexOf', 'filter','map','every','some','reduce','concat'].each(function(name) { this[name] = proto[name]; }, { generics: true, preserve: true }); Array.inject(fields); Hash.append(fields, proto, { clear: function() { for (var i = 0, l = this.length; i < l; i++) delete this[i]; this.length = 0; }, concat: function(list) { return Browser.WEBKIT ? new Array(this.length + list.length).append(this).append(list) : Array.concat(this, list); }, toString: proto.join, length: 0 }); return { statics: { create: function(obj) { if (obj == null) return []; if (obj.toArray) return obj.toArray(); if (typeof obj.length == 'number') return Array.prototype.slice.call(obj); return [obj]; }, convert: function(obj) { return Base.type(obj) == 'array' ? obj : Array.create(obj); }, extend: function(src) { var ret = Base.extend(fields, src); ret.extend = Function.extend; return ret; } } }; }); $A = Array.create; Function.inject(new function() { function timer(set) { return function(delay, bind, args) { var func = this.wrap(bind, args); if (delay === undefined) return func(); var timer = set(func, delay); func.clear = function() { clearTimeout(timer); clearInterval(timer); }; return func; }; } return { generics: true, preserve: true, delay: timer(setTimeout), periodic: timer(setInterval), bind: function(bind) { var that = this, slice = Array.prototype.slice, args = arguments.length > 1 ? slice.call(arguments, 1) : null; return function() { return that.apply(bind, args ? arguments.length > 0 ? args.concat(slice.call(arguments)) : args : arguments); } }, wrap: function(bind, args) { var that = this; return function() { return that.apply(bind, args || arguments); } } } }); Number.inject({ _type: 'number', limit: function(min, max) { return Math.min(max, Math.max(min, this)); }, times: function(func, bind) { for (var i = 0; i < this; i++) func.call(bind, i); return bind || this; }, toInt: function(base) { return parseInt(this, base || 10); }, toFloat: function() { return parseFloat(this); }, toPaddedString: function(length, base, prefix) { var str = this.toString(base || 10); return (prefix || '0').times(length - str.length) + str; } }); String.inject({ _type: 'string', test: function(exp, param) { return new RegExp(exp, param || '').test(this); }, toArray: function() { return this ? this.split(/\s+/) : []; }, toInt: Number.prototype.toInt, toFloat: Number.prototype.toFloat, camelize: function(separator) { return this.replace(separator ? new RegExp('[' + separator + '](\\w)', 'g') : /-(\w)/g, function(all, chr) { return chr.toUpperCase(); }); }, uncamelize: function(separator) { separator = separator || ' '; return this.replace(/[a-z][A-Z0-9]|[0-9][a-zA-Z]|[A-Z]{2}[a-z]/g, function(match) { return match.charAt(0) + separator + match.substring(1); }); }, hyphenate: function(separator) { return this.uncamelize(separator || '-').toLowerCase(); }, capitalize: function() { return this.replace(/\b[a-z]/g, function(match) { return match.toUpperCase(); }); }, escapeRegExp: function() { return this.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1'); }, trim: function(exp) { exp = exp ? '[' + exp + ']' : '\\s'; return this.replace(new RegExp('^' + exp + '+|' + exp + '+$', 'g'), ''); }, clean: function() { return this.replace(/\s{2,}/g, ' ').trim(); }, contains: function(string, sep) { return (sep ? (sep + this + sep).indexOf(sep + string + sep) : this.indexOf(string)) != -1; }, times: function(count) { return count < 1 ? '' : new Array(count + 1).join(this); }, isHtml: function() { return /^[^<]*(<(.|\s)+>)[^>]*$/.test(this); } }); RegExp.inject({ _type: 'regexp' }); Date.inject({ statics: { SECOND: 1000, MINUTE: 60000, HOUR: 3600000, DAY: 86400000, WEEK: 604800000, MONTH: 2592000000, YEAR: 31536000000, now: Date.now || function() { return +new Date(); } } }); Math.rand = function(first, second) { return second == undefined ? Math.rand(0, first) : Math.floor(Math.random() * (second - first) + first); } Array.inject({ hexToRgb: function(toArray) { if (this.length >= 3) { var rgb = []; for (var i = 0; i < 3; i++) rgb.push((this[i].length == 1 ? this[i] + this[i] : this[i]).toInt(16)); return toArray ? rgb : 'rgb(' + rgb.join(',') + ')'; } }, rgbToHex: function(toArray) { if (this.length >= 3) { if (this.length == 4 && this[3] == 0 && !toArray) 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 toArray ? hex : '#' + hex.join(''); } }, rgbToHsb: function() { var r = this[0], g = this[1], b = this[2]; var hue, saturation, brightness; var max = Math.max(r, g, b), min = Math.min(r, g, b); var delta = max - min; brightness = max / 255; saturation = (max != 0) ? delta / max : 0; if (saturation == 0) { hue = 0; } else { var rr = (max - r) / delta; var gr = (max - g) / delta; var br = (max - b) / delta; if (r == max) hue = br - gr; else if (g == max) hue = 2 + rr - br; else hue = 4 + gr - rr; hue /= 6; if (hue < 0) hue++; } return [Math.round(hue * 360), Math.round(saturation * 100), Math.round(brightness * 100)]; }, hsbToRgb: function() { var br = Math.round(this[2] / 100 * 255); if (this[1] == 0) { return [br, br, br]; } else { var hue = this[0] % 360; var f = hue % 60; var p = Math.round((this[2] * (100 - this[1])) / 10000 * 255); var q = Math.round((this[2] * (6000 - this[1] * f)) / 600000 * 255); var t = Math.round((this[2] * (6000 - this[1] * (60 - f))) / 600000 * 255); switch (Math.floor(hue / 60)) { case 0: return [br, t, p]; case 1: return [q, br, p]; case 2: return [p, br, t]; case 3: return [p, q, br]; case 4: return [t, p, br]; case 5: return [br, p, q]; } } } }); String.inject({ hexToRgb: function(toArray) { var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/); return hex && hex.slice(1).hexToRgb(toArray); }, rgbToHex: function(toArray) { var rgb = this.match(/\d{1,3}/g); return rgb && rgb.rgbToHex(toArray); } }); Json = function(JSON) { var special = { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', "'" : "\\'", '\\': '\\\\' }; return { encode: JSON ? function(obj, properties) { return JSON.stringify(obj, properties || Browser.TRIDENT && function(key, value) { return key == '__proto__' ? undefined : value; }); } : function(obj, properties) { if (Base.type(properties) == 'array') { properties = properties.each(function(val) { this[val] = true; }, {}); } switch (Base.type(obj)) { case 'string': return '"' + obj.replace(/[\x00-\x1f\\"]/g, function(chr) { return special[chr] || '\\u' + chr.charCodeAt(0).toPaddedString(4, 16); }) + '"'; case 'array': return '[' + obj.collect(function(val) { return Json.encode(val, properties); }) + ']'; case 'object': case 'hash': return '{' + Hash.collect(obj, function(val, key) { if (!properties || properties[key]) { val = Json.encode(val, properties); if (val !== undefined) return Json.encode(key) + ':' + val; } }) + '}'; case 'function': return undefined; default: return obj + ''; } return null; }, decode: JSON ? function(str, secure) { try { return JSON.parse(str); } catch (e) { return null; } } : function(str, secure) { try { return Base.type(str) == 'string' && (str = str.trim()) && (!secure || /^[\],:{}\s]*$/.test( str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@") .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]") .replace(/(?:^|:|,)(?:\s*\[)+/g, ""))) ? (new Function('return ' + str))() : null; } catch (e) { return null; } } }; }(this.JSON); Browser = new function() { var name = window.orientation != undefined ? 'ipod' : (navigator.platform.match(/mac|win|linux|nix/i) || ['other'])[0].toLowerCase(); var fields = { PLATFORM: name, XPATH: !!document.evaluate, QUERY: !!document.querySelector }; fields[name.toUpperCase()] = true; function getVersion(prefix, min, max) { var ver = (new RegExp(prefix + '([\\d.]+)', 'i').exec(navigator.userAgent) || [0, '0'])[1].split('.'); return (ver.slice(0, min).join('') + '.' + ver.slice(min, max || ver.length).join('')).toFloat(); } var engines = { presto: function() { return !window.opera ? false : getVersion('Presto/', 2) || getVersion('Opera[/ ]', 1); }, trident: function() { return !window.ActiveXObject ? false : getVersion('MSIE ', 1); }, webkit: function() { return navigator.taintEnabled ? false : getVersion('WebKit/', 1, 2); }, gecko: function() { return !document.getBoxObjectFor && window.mozInnerScreenX == null ? false : getVersion('rv:', 2); } }; for (var engine in engines) { var version = engines[engine](); if (version) { fields.ENGINE = engine; fields.VERSION = version; engine = engine.toUpperCase(); fields[engine] = true; fields[(engine + version).replace(/\./g, '')] = true; break; } } fields.log = function() { if (!Browser.TRIDENT && window.console && console.log) console.log.apply(console, arguments); else (window.console && console.log || window.opera && opera.postError || alert)(Array.join(arguments, ' ')); } return fields; }; DomNodes = Array.extend(new function() { var unique = 0; return { initialize: function(nodes) { this._unique = unique++; this.append(nodes && nodes.length != null && !nodes.nodeType ? nodes : arguments); }, push: function() { this.append(arguments); return this.length; }, append: function(items) { for (var i = 0, l = items.length; i < l; i++) { var el = items[i]; if ((el = el && (el._wrapper || DomNode.wrap(el))) && el._unique != this._unique) { el._unique = this._unique; this[this.length++] = el; } } return this; }, toNode: function() { return this; }, statics: { inject: function(src) { var proto = this.prototype; this.base(Base.each(src || {}, function(val, key) { if (typeof val == 'function') { var func = val, prev = proto[key]; var count = func.length, prevCount = prev && prev.length; val = function() { var args = arguments, values; if (prev && args.length == prevCount || (args.length > count && args.length <= prevCount)) return prev.apply(this, args); this.each(function(obj) { var ret = (obj[key] || func).apply(obj, args); if (ret !== undefined && ret != obj) { values = values || (DomNode.isNode(ret) ? new obj._collection() : []); values.push(ret); } }); return values || this; } } this[key] = val; }, {})); for (var i = 1, l = arguments.length; i < l; i++) this.inject(arguments[i]); return this; } } }; }); DomNode = Base.extend(new function() { var nodes = []; var tags = {}, classes = {}, classCheck, unique = 0; function dispose(force) { for (var i = nodes.length - 1; i >= 0; i--) { var el = nodes[i]; if (force || (!el || el != window && el != document && (!el.parentNode || !el.offsetParent))) { if (el) { var obj = el._wrapper; if (obj && obj.finalize) obj.finalize(); el._wrapper = el._unique = null; } if (!force) nodes.splice(i, 1); } } } function inject(src) { src = src || {}; (src._methods || []).each(function(name) { src[name] = function(arg) { var ret = this.$[name] && this.$[name](arg); return ret === undefined ? this : ret; } }); (src._properties || []).each(function(name) { var part = name.capitalize(), prop = name.toLowerCase(); src['get' + part] = function() { return this.getProperty(prop); } src['set' + part] = function(value) { return this.setProperty(prop, value); } }); delete src._methods; delete src._properties; return Function.inject.call(this, src); } function getConstructor(el) { var match; return classCheck && el.className && (match = el.className.match(classCheck)) && match[2] && classes[match[2]] || el.tagName && tags[el.tagName] || el.className !== undefined && HtmlElement || el.nodeType == 1 && DomElement || el.nodeType == 3 && DomTextNode || el.nodeType == 9 && (el.documentElement.nodeName.toLowerCase() == 'html' && HtmlDocument || DomDocument) || el.location && el.frames && el.history && DomWindow || DomNode; } var dont = {}; return { beans: true, _type: 'node', _collection: DomNodes, _initialize: true, initialize: function(el, props, doc) { if (!el) return null; if (this._tag && Base.type(el) == 'object') { props = el; el = this._tag; } if (typeof(el) == 'string') { el = DomElement.create(el, props, doc); } else if (el._wrapper) { return el._wrapper; } if (props === dont) { props = null; } else { var ctor = getConstructor(el); if (ctor != this.constructor) return new ctor(el, props); } this.$ = el; try { el._wrapper = this; nodes[nodes.length] = el; } catch (e) {} if (props) this.set(props); }, statics: { inject: function(src) { if (src) { var proto = this.prototype, that = this; src.statics = Base.each(src, function(val, name) { if (typeof val == 'function' && !this[name] && !that[name]) { this[name] = function(el, param1, param2) { if (el) try { proto.$ = el.$ || el; return proto[name](param1, param2); } finally { delete proto.$; } } } }, src.statics || {}); inject.call(this, src); delete src.toString; proto._collection.inject(src); } for (var i = 1, l = arguments.length; i < l; i++) this.inject(arguments[i]); return this; }, extend: function(src) { var ret = this.base(); var init = src.initialize; if (init) src.initialize = function(el, props) { var ret = this._initialize && this.base(el, props); if (ret) return ret; init.apply(this, arguments); } inject.call(ret, src); if (ret.prototype._collection == this.prototype._collection) ret.inject = inject; if (src) { if (src._tag) tags[src._tag.toLowerCase()] = tags[src._tag.toUpperCase()] = ret; if (src._class) { classes[src._class] = ret; classCheck = new RegExp('(^|\\s)(' + Base.each(classes, function(val, name) { this.push(name); }, []).join('|') + ')(\\s|$)'); if (!src._lazy && src.initialize) Browser.document.addEvent('domready', function() { this.getElements('.' + src._class); }); } } return ret; }, wrap: function(el) { return el ? typeof el == 'string' ? DomElement.get(el) : el._wrapper || el._collection && el || new (getConstructor(el))(el, dont) : null; }, unwrap: function(el) { return el && el.$ || el; }, unique: function(el) { if (!el._unique) { nodes.push(el); el._unique = ++unique; } }, isNode: function(obj) { return /^(element|node|textnode|document)$/.test( typeof obj == 'string' ? obj : Base.type(obj)); }, dispose: function() { dispose(true); } } } }); DomNode.inject(new function() { var bools = ['compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked', 'disabled', 'readonly', 'multiple', 'selected', 'noresize', 'defer' ].associate(); var properties = Hash.append({ text: Browser.TRIDENT || Browser.WEBKIT && Browser.VERSION < 420 || Browser.PRESTO && Browser.VERSION < 9 ? function(node) { return node.$.innerText !== undefined ? 'innerText' : 'nodeValue' } : 'textContent', html: 'innerHTML', 'class': 'className', className: 'className', 'for': 'htmlFor' }, [ 'value', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan', 'frameBorder', 'maxLength', 'readOnly', 'rowSpan', 'tabIndex', 'selectedIndex', 'useMap', 'width', 'height' ].associate(function(name) { return name.toLowerCase(); }), bools); var clones = { input: 'checked', option: 'selected', textarea: Browser.WEBKIT && Browser.VERSION < 420 ? 'innerHTML' : 'value' }; function handle(that, prefix, name, value) { var ctor = that.__proto__.constructor; var handlers = ctor.handlers = ctor.handlers || { get: {}, set: {} }; var list = handlers[prefix]; var fn = name == 'events' && prefix == 'set' ? that.addEvents : list[name]; if (fn === undefined) fn = list[name] = that[prefix + name.capitalize()] || null; return fn ? fn[Base.type(value) == 'array' ? 'apply' : 'call'](that, value) : that[prefix + 'Property'](name, value); } function toNodes(elements) { var els = Base.type(elements) == 'array' ? elements : Array.create(arguments); var created = els.find(function(el) { return !DomNode.isNode(el); }); var result = els.toNode(this.getDocument()); return { array: result ? (Base.type(result) == 'array' ? result : [result]) : [], result: created && result }; } var fields = { beans: true, _properties: ['text'], set: function(name, value) { switch (Base.type(name)) { case 'string': return handle(this, 'set', name, value); case 'object': return Base.each(name, function(value, key) { handle(this, 'set', key, value); }, this); } return this; }, get: function(name) { return handle(this, 'get', name); }, getDocument: function() { return DomNode.wrap(this.$.ownerDocument); }, getWindow: function() { return this.getDocument().getWindow(); }, getPreviousNode: function() { return DomNode.wrap(this.$.previousSibling); }, getNextNode: function() { return DomNode.wrap(this.$.nextSibling); }, getFirstNode: function() { return DomNode.wrap(this.$.firstChild); }, getLastNode: function() { return DomNode.wrap(this.$.lastChild); }, getParentNode: function() { return DomNode.wrap(this.$.parentNode); }, getChildNodes: function() { return new DomNodes(this.$.childNodes); }, hasChildNodes: function() { return this.$.hasChildNodes(); }, appendChild: function(el) { if (el = DomNode.wrap(el)) { var text = Browser.TRIDENT && el.$.text; if (text) el.$.text = ''; this.$.appendChild(el.$); if (text) el.$.text = text; } return this; }, appendChildren: function() { return Array.flatten(arguments).each(function(el) { this.appendChild($(DomNode.wrap(el))); }, this); }, appendText: function(text) { return this.injectBottom(this.getDocument().createTextNode(text)); }, prependText: function(text) { return this.injectTop(this.getDocument().createTextNode(text)); }, remove: function() { if (this.$.parentNode) this.$.parentNode.removeChild(this.$); return this; }, removeChild: function(el) { el = DomNode.wrap(el); this.$.removeChild(el.$); return el; }, removeChildren: function() { var nodes = this.getChildNodes(); nodes.remove(); return nodes; }, replaceWith: function(el) { if (this.$.parentNode) { el = toNodes.apply(this, arguments); var els = el.array; if (els.length > 0) this.$.parentNode.replaceChild(els[0].$, this.$); for (var i = els.length - 1; i > 0; i--) els[i].insertAfter(els[0]); return el.result; } return null; }, wrap: function() { var el = this.injectBefore.apply(this, arguments), last; do { last = el; el = el.getFirst(); } while(el); last.appendChild(this); return last; }, clone: function(contents) { var clone = this.$.cloneNode(!!contents); function clean(left, right) { if (Browser.TRIDENT) { left.clearAttributes(); left.mergeAttributes(right); left.removeAttribute('_wrapper'); left.removeAttribute('_unique'); if (left.options) for (var l = left.options, r = right.options, i = l.length; i--;) l[i].selected = r[i].selected; } var name = clones[right.tagName.toLowerCase()]; if (name && right[name]) left[name] = right[name]; if (contents) for (var l = left.childNodes, r = right.childNodes, i = l.length; i--;) clean(l[i], r[i]); } clean(clone, this.$); return DomNode.wrap(clone); }, hasProperty: function(name) { var key = properties[name]; key = key && typeof key == 'function' ? key(this) : key; return key ? this.$[key] !== undefined : this.$.hasAttribute(name); }, getProperty: function(name) { var key = properties[name], value; key = key && typeof key == 'function' ? key(this) : key; var value = key ? this.$[key] : this.$.getAttribute(name); return bools[name] ? !!value : value; }, setProperty: function(name, value) { var key = properties[name], defined = value !== undefined; key = key && typeof key == 'function' ? key(this) : key; if (key && bools[name]) value = value || !defined ? true : false; else if (!defined) return this.removeProperty(name); key ? this.$[key] = value : this.$.setAttribute(name, value); return this; }, removeProperty: function(name) { var key = properties[name], bool = key && bools[name]; key = key && typeof key == 'function' ? key(this) : key; key ? this.$[key] = bool ? false : '' : this.$.removeAttribute(name); return this; }, getProperties: function() { var props = {}; for (var i = 0; i < arguments.length; i++) props[arguments[i]] = this.getProperty(arguments[i]); return props; }, setProperties: function(src) { return Base.each(src, function(value, name) { this.setProperty(name, value); }, this); }, removeProperties: function() { return Array.each(arguments, this.removeProperty, this); } }; var inserters = { before: function(source, dest) { if (source && dest && dest.$.parentNode) { var text = Browser.TRIDENT && dest.$.text; if (text) dest.$.text = ''; dest.$.parentNode.insertBefore(source.$, dest.$); if (text) dest.$.text = text; } }, after: function(source, dest) { if (source && dest && dest.$.parentNode) { var next = dest.$.nextSibling; if (next) source.insertBefore(next); else dest.getParent().appendChild(source); } }, bottom: function(source, dest) { if (source && dest) dest.appendChild(source); }, top: function(source, dest) { if (source && dest) { var first = dest.$.firstChild; if (first) source.insertBefore(first); else dest.appendChild(source); } } }; inserters.inside = inserters.bottom; Base.each(inserters, function(inserter, name) { var part = name.capitalize(); fields['insert' + part] = function(el) { el = toNodes.apply(this, arguments); for (var i = 0, list = el.array, l = list.length; i < l; i++) inserter(i == 0 ? this : this.clone(true), list[i]); return el.result || this; } fields['inject' + part] = function(el) { el = toNodes.apply(this, arguments); for (var i = 0, list = el.array, l = list.length; i < l; i++) inserter(list[i], this); return el.result || this; } }); return fields; }); DomElements = DomNodes.extend(); DomElement = DomNode.extend({ beans: true, _type: 'element', _collection: DomElements, statics: { get: function(selector, root) { return (root && DomNode.wrap(root) || Browser.document).getElement(selector); }, getAll: function(selector, root) { return (root && DomNode.wrap(root) || Browser.document).getElements(selector); }, create: function(tag, props, doc) { if (Browser.TRIDENT && props) { ['name', 'type', 'checked'].each(function(key) { if (props[key]) { tag += ' ' + key + '="' + props[key] + '"'; if (key != 'checked') delete props[key]; } }); tag = '<' + tag + '>'; } return (DomElement.unwrap(doc) || document).createElement(tag); }, isAncestor: function(el, parent) { return !el ? false : el.ownerDocument == parent ? true : Browser.WEBKIT && Browser.VERSION < 420 ? Array.contains(parent.getElementsByTagName(el.tagName), el) : parent.contains ? parent != el && parent.contains(el) : !!(parent.compareDocumentPosition(el) & 16) } } }); DomElement.inject(new function() { function walk(el, walk, start, match, all) { var elements = all && new el._collection(); el = el.$[start || walk]; while (el) { if (el.nodeType == 1 && (!match || DomElement.match(el, match))) { if (!all) return DomNode.wrap(el); elements.push(el); } el = el[walk]; } return elements; } return { beans: true, _properties: ['id'], getTag: function() { return (this.$.tagName || '').toLowerCase(); }, getPrevious: function(match) { return walk(this, 'previousSibling', null, match); }, getAllPrevious: function(match) { return walk(this, 'previousSibling', null, match, true); }, getNext: function(match) { return walk(this, 'nextSibling', null, match); }, getAllNext: function(match) { return walk(this, 'nextSibling', null, match, true); }, getFirst: function(match) { return walk(this, 'nextSibling', 'firstChild', match); }, getLast: function(match) { return walk(this, 'previousSibling', 'lastChild', match); }, hasChild: function(match) { return DomNode.isNode(match) ? DomElement.isAncestor(DomElement.unwrap(match), this.$) : !!this.getFirst(match); }, getParent: function(match) { return walk(this, 'parentNode', null, match); }, getParents: function(match) { return walk(this, 'parentNode', null, match, true); }, hasParent: function(match) { return DomNode.isNode(match) ? DomElement.isAncestor(this.$, DomElement.unwrap(match)) : !!this.getParent(match); }, getChildren: function(match) { return walk(this, 'nextSibling', 'firstChild', match, true); }, hasChildren: function(match) { return !!this.getChildren(match).length; }, toString: function() { return (this.$.tagName || this._type).toLowerCase() + (this.$.id ? '#' + this.$.id : ''); }, toNode: function() { return this; } }; }); $ = DomElement.get; $$ = DomElement.getAll; DomTextNode = DomNode.extend({ _type: 'textnode' }); DomDocument = DomElement.extend({ beans: true, _type: 'document', initialize: function() { if (Browser.TRIDENT && Browser.VERSION < 7) try { this.$.execCommand('BackgroundImageCache', false, true); } catch (e) {} }, createElement: function(tag, props) { return DomNode.wrap(DomElement.create(tag, props, this.$)).set(props); }, createTextNode: function(text) { return $(this.$.createTextNode(text)); }, getDocument: function() { return this; }, getWindow: function() { return DomNode.wrap(this.$.defaultView || this.$.parentWindow); }, open: function() { this.$.open(); }, close: function() { this.$.close(); }, write: function(markup) { this.$.write(markup); }, writeln: function(markup) { this.$.writeln(markup); } }); Window = DomWindow = DomElement.extend({ beans: true, _type: 'window', _initialize: false, _methods: ['close', 'alert', 'prompt', 'confirm', 'blur', 'focus', 'reload'], getDocument: function() { return DomNode.wrap(this.$.document); }, getWindow: function() { return this; }, initialize: function(param) { var win; if (param.location && param.frames && param.history) { win = this.base(param) || this; } else { if (typeof param == 'string') param = { url: param }; (['toolbar','menubar','location','status','resizable','scrollbars']).each(function(key) { param[key] = param[key] ? 1 : 0; }); if (param.width && param.height) { if (param.left == null) param.left = Math.round( Math.max(0, (screen.width - param.width) / 2)); if (param.top == null) param.top = Math.round( Math.max(0, (screen.height - param.height) / 2 - 40)); } var str = Base.each(param, function(val, key) { if (!/^(focus|confirm|url|name)$/.test(key)) this.push(key + '=' + (val + 0)); }, []).join(); win = this.base(window.open(param.url, param.name.replace(/\s+|\.+|-+/gi, ''), str)) || this; if (win && param.focus) win.focus(); } return ['location', 'frames', 'history'].each(function(key) { this[key] = this.$[key]; }, win); } }); DomElement.inject(new function() { function cumulate(name, parent, iter) { var left = name + 'Left', top = name + 'Top'; return function(that) { var cur, next = that, x = 0, y = 0; do { cur = next; x += cur.$[left] || 0; y += cur.$[top] || 0; } while((next = DomNode.wrap(cur.$[parent])) && (!iter || iter(cur, next))) return { x: x, y: y }; } } function bounds(fields, offset) { return function(values) { var vals = /^(object|array)$/.test(Base.type(values)) ? values : arguments; if (offset) { if (vals.x) vals.left = vals.x; if (vals.y) vals.top = vals.y; } var i = 0; return fields.each(function(name) { var val = vals.length ? vals[i++] : vals[name]; if (val != null) this.setStyle(name, val); }, this); } } function body(that) { return that.getTag() == 'body'; } var getAbsolute = cumulate('offset', 'offsetParent', Browser.WEBKIT ? function(cur, next) { return next.$ != document.body || cur.getStyle('position') != 'absolute'; } : null, true); var getPositioned = cumulate('offset', 'offsetParent', function(cur, next) { return next.$ != document.body && !/^(relative|absolute)$/.test(next.getStyle('position')); }); var getScrollOffset = cumulate('scroll', 'parentNode'); var fields = { beans: true, getSize: function() { return body(this) ? this.getWindow().getSize() : { width: this.$.offsetWidth, height: this.$.offsetHeight }; }, getOffset: function(relative) { if (body(this)) return this.getWindow().getOffset(); if (relative && !DomNode.isNode(relative)) return getPositioned(this); var off = getAbsolute(this); if (relative) { var rel = getAbsolute(DomNode.wrap(relative)); off = { x: off.x - rel.x, y: off.y - rel.y }; } return off; }, getScrollOffset: function() { return body(this) ? this.getWindow().getScrollOffset() : getScrollOffset(this); }, getScrollSize: function() { return body(this) ? this.getWindow().getScrollSize() : { width: this.$.scrollWidth, height: this.$.scrollHeight }; }, getBounds: function(relative) { if (body(this)) return this.getWindow().getBounds(); var off = this.getOffset(relative), el = this.$; return { left: off.x, top: off.y, right: off.x + el.offsetWidth, bottom: off.y + el.offsetHeight, width: el.offsetWidth, height: el.offsetHeight }; }, setBounds: bounds(['left', 'top', 'width', 'height', 'clip'], true), setOffset: bounds(['left', 'top'], true), setSize: bounds(['width', 'height', 'clip']), setScrollOffset: function(x, y) { if (body(this)) { this.getWindow().setScrollOffset(x, y); } else { var off = typeof x == 'object' ? x : { x: x, y: y }; this.$.scrollLeft = off.x; this.$.scrollTop = off.y; } return this; }, scrollTo: function(x, y) { return this.setScrollOffset(x, y); }, contains: function(pos) { var bounds = this.getBounds(); return pos.x >= bounds.left && pos.x < bounds.right && pos.y >= bounds.top && pos.y < bounds.bottom; }, isVisible: function(fully) { var win = this.getWindow(), top = win.getScrollOffset().y, bottom = top + win.getSize().height, bounds = this.getBounds(); return (bounds.height > 0 || bounds.width > 0) && (bounds.top >= top && bounds.bottom <= bottom || (fully && bounds.top <= top && bounds.bottom >= bottom) || !fully && (bounds.top <= top && bounds.bottom >= top || bounds.top <= bottom && bounds.bottom >= bottom)); } }; ['left', 'top', 'right', 'bottom', 'width', 'height'].each(function(name) { var part = name.capitalize(); fields['get' + part] = function() { return this.$['offset' + part]; }; fields['set' + part] = function(value) { this.$.style[name] = isNaN(value) ? value : value + 'px'; }; }); return fields; }); [DomDocument, DomWindow].each(function(ctor) { ctor.inject(this); }, { beans: true, getSize: function() { if (Browser.PRESTO || Browser.WEBKIT) { var win = this.getWindow().$; return { width: win.innerWidth, height: win.innerHeight }; } var doc = this.getCompatElement(); return { width: doc.clientWidth, height: doc.clientHeight }; }, getScrollOffset: function() { var win = this.getWindow().$, doc = this.getCompatElement(); return { x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop }; }, getScrollSize: function() { var doc = this.getCompatElement(), min = this.getSize(); return { width: Math.max(doc.scrollWidth, min.width), height: Math.max(doc.scrollHeight, min.height) }; }, getOffset: function() { return { x: 0, y: 0 }; }, getBounds: function() { var size = this.getSize(); return { left: 0, top: 0, right: size.width, bottom: size.height, width: size.width, height: size.height }; }, setScrollOffset: function(x, y) { var off = typeof x == 'object' ? x : { x: x, y: y }; this.getWindow().$.scrollTo(off.x, off.y); return this; }, getElementAt: function(pos, exclude) { var el = this.getDocument().getElement('body'); while (true) { var max = -1; var ch = el.getFirst(); while (ch) { if (ch.contains(pos) && ch != exclude) { var z = ch.$.style.zIndex.toInt() || 0; if (z >= max) { el = ch; max = z; } } ch = ch.getNext(); } if (max < 0) break; } return el; }, getCompatElement: function() { var doc = this.getDocument(); return doc.getElement(!doc.$.compatMode || doc.$.compatMode == 'CSS1Compat' ? 'html' : 'body').$; } }); DomEvent = Base.extend(new function() { var keys = { '8': 'backspace', '13': 'enter', '27': 'escape', '32': 'space', '37': 'left', '38': 'up', '39': 'right', '40': 'down', '46': 'delete' }; function hover(name, type) { return { type: type, listener: function(event) { if (event.relatedTarget != this && !this.hasChild(event.relatedTarget)) this.fireEvent(name, [event]); } } } return { initialize: function(event) { this.event = event = event || window.event; this.type = event.type; this.target = DomNode.wrap(event.target || event.srcElement); if (this.target && this.target.$.nodeType == 3) this.target = this.target.getParentNode(); this.shift = event.shiftKey; this.control = event.ctrlKey; this.alt = event.altKey; this.meta = event.metaKey; if (/^(mousewheel|DOMMouseScroll)$/.test(this.type)) { this.wheel = event.wheelDelta ? event.wheelDelta / (window.opera ? -120 : 120) : - (event.detail || 0) / 3; } else if (/^key/.test(this.type)) { this.code = event.which || event.keyCode; this.key = keys[this.code] || String.fromCharCode(this.code).toLowerCase(); } else if (/^mouse|^click$/.test(this.type)) { this.page = { x: event.pageX || event.clientX + document.documentElement.scrollLeft, y: event.pageY || event.clientY + document.documentElement.scrollTop }; this.client = { x: event.pageX ? event.pageX - window.pageXOffset : event.clientX, y: event.pageY ? event.pageY - window.pageYOffset : event.clientY }; var offset = this.target.getOffset(); this.offset = { x: this.page.x - offset.x, y: this.page.y - offset.y } this.rightClick = event.which == 3 || event.button == 2; if (/^mouse(over|out)$/.test(this.type)) this.relatedTarget = DomNode.wrap(event.relatedTarget || this.type == 'mouseout' ? event.toElement : event.fromElement); } }, stop: function() { this.stopPropagation(); this.preventDefault(); return this; }, stopPropagation: function() { if (this.event.stopPropagation) this.event.stopPropagation(); else this.event.cancelBubble = true; this.stopped = true; return this; }, preventDefault: function() { if (this.event.preventDefault) this.event.preventDefault(); else this.event.returnValue = false; return this; }, statics: { events: new Hash({ mouseenter: hover('mouseenter', 'mouseover'), mouseleave: hover('mouseleave', 'mouseout'), mousewheel: { type: Browser.GECKO ? 'DOMMouseScroll' : 'mousewheel' }, domready: function(func) { var win = this.getWindow(), doc = this.getDocument(); if (Browser.loaded) { func.call(this); } else if (!doc.onDomReady) { doc.onDomReady = function() { if (!Browser.loaded) { Browser.loaded = true; doc.fireEvent('domready'); win.fireEvent('domready'); } } if (Browser.TRIDENT) { var temp = doc.createElement('div'); (function() { try { temp.$.doScroll('left'); temp.insertBottom(DomElement.get('body')).setHtml('temp').remove(); doc.onDomReady(); } catch (e) { arguments.callee.delay(50); } }).delay(0); } else if (Browser.WEBKIT && Browser.VERSION < 525) { (function() { /^(loaded|complete)$/.test(doc.$.readyState) ? doc.onDomReady() : arguments.callee.delay(50); })(); } else { win.addEvent('load', doc.onDomReady); doc.addEvent('DOMContentLoaded', doc.onDomReady); } } } }), add: function(events) { this.events.append(events); } } }; }); DomElement.inject(new function() { function callEvent(fire) { return function(type, args, delay) { var entries = (this.events || {})[type]; if (entries) { var event = args && args[0]; if (event) args[0] = event.event ? event : new DomEvent(event); entries.each(function(entry) { entry[fire ? 'func' : 'bound'].delay(delay, this, args); }, this); } return !!entries; } } return { addEvent: function(type, func) { this.events = this.events || {}; var entries = this.events[type] = this.events[type] || []; if (func && !entries.find(function(entry) { return entry.func == func })) { var listener = func, name = type, pseudo = DomEvent.events[type]; if (pseudo) { if (typeof pseudo == 'function') pseudo = pseudo.call(this, func); listener = pseudo && pseudo.listener || listener; name = pseudo && pseudo.type; } var that = this, bound = function(event) { if (event || window.event) event = event && event.event ? event : new DomEvent(event); if (listener.call(that, event) === false && event) event.stop(); }; if (name) { if (this.$.addEventListener) { this.$.addEventListener(name, bound, false); } else if (this.$.attachEvent) { this.$.attachEvent('on' + name, bound); } } entries.push({ func: func, name: name, bound: bound }); } return this; }, removeEvent: function(type, func) { var entries = (this.events || {})[type], entry; if (func && entries) { if (entry = entries.remove(function(entry) { return entry.func == func })) { var name = entry.name, pseudo = DomEvent.events[type]; if (pseudo && pseudo.remove) pseudo.remove.call(this, func); if (name) { if (this.$.removeEventListener) { this.$.removeEventListener(name, entry.bound, false); } else if (this.$.detachEvent) { this.$.detachEvent('on' + name, entry.bound); } } } } return this; }, addEvents: function(events) { return Base.each(events || [], function(fn, type) { this.addEvent(type, fn); }, this); }, removeEvents: function(type) { if (this.events) { if (type) { (this.events[type] || []).each(function(fn) { this.removeEvent(type, fn); }, this); delete this.events[type]; } else { Base.each(this.events, function(ev, type) { this.removeEvents(type); }, this); this.events = null; } } return this; }, fireEvent: callEvent(true), triggerEvent: callEvent(false), finalize: function() { this.removeEvents(); } }; }); DomEvent.add(new function() { var object, last; function dragStart(event) { if (object != this) { event.type = 'dragstart'; last = event.page; this.fireEvent('dragstart', [event]); if (!event.stopped) { event.stop(); var doc = this.getDocument(); doc.addEvent('mousemove', drag); doc.addEvent('mouseup', dragEnd); object = this; } } } function drag(event) { event.type = 'drag'; event.delta = { x: event.page.x - last.x, y: event.page.y - last.y } last = event.page; object.fireEvent('drag', [event]); event.preventDefault(); } function dragEnd(event) { if (object) { event.type = 'dragend'; object.fireEvent('dragend', [event]); event.preventDefault(); var doc = object.getDocument(); doc.removeEvent('mousemove', drag); doc.removeEvent('mouseup', dragEnd); object = null; } } return { dragstart: { type: 'mousedown', listener: dragStart }, drag: { type: 'mousedown', listener: dragStart }, dragend: {} }; }); DomElement.inject(new function() { var XPATH= 0, FILTER = 1; var methods = [{ getParam: function(items, separator, context, params) { var str = context.namespaceURI ? 'xhtml:' + params.tag : params.tag; if (separator && (separator = DomElement.separators[separator])) str = separator[XPATH] + str; for (var i = params.pseudos.length; i--;) { var pseudo = params.pseudos[i]; str += pseudo.handler[XPATH](pseudo.argument); } if (params.id) str += '[@id="' + params.id + '"]'; for (var i = params.classes.length; i--;) str += '[contains(concat(" ", @class, " "), " ' + params.classes[i] + ' ")]'; for (var i = params.attributes.length; i--;) { var attribute = params.attributes[i]; var operator = DomElement.operators[attribute[1]]; if (operator) str += operator[XPATH](attribute[0], attribute[2]); else str += '[@' + attribute[0] + ']'; } items.push(str); return items; }, getElements: function(items, elements, context) { function resolver(prefix) { return prefix == 'xhtml' ? 'http://www.w3.org/1999/xhtml' : false; } var res = (context.ownerDocument || context).evaluate('.//' + items.join(''), context, resolver, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null); for (var i = 0, l = res.snapshotLength; i < l; i++) elements.push(res.snapshotItem(i)); } }, { getParam: function(items, separator, context, params, data) { var found = []; var tag = params.tag; if (separator && (separator = DomElement.separators[separator])) { separator = separator[FILTER]; var uniques = {}; function add(item) { if (!item._unique) DomNode.unique(item); if (!uniques[item._unique] && match(item, params, data)) { uniques[item._unique] = true; found.push(item); return true; } } for (var i = 0, l = items.length; i < l; i++) separator(items[i], params, add); if (params.clearTag) params.tag = params.clearTag = null; return found; } if (params.id) { var el = (context.ownerDocument || context).getElementById(params.id); params.id = null; return el && DomElement.isAncestor(el, context) && match(el, params, data) ? [el] : null; } else { if (!items.length) { items = context.getElementsByTagName(tag); params.tag = null; } for (var i = 0, l = items.length; i < l; i++) if (match(items[i], params, data)) found.push(items[i]); } return found; }, getElements: function(items, elements, context) { elements.append(items); } }]; function parse(selector) { var params = { tag: '*', id: null, classes: [], attributes: [], pseudos: [] }; selector.replace(/:([^:(]+)*(?:\((["']?)(.*?)\2\))?|\[([\w-]+)(?:([!*^$~|]?=)(["']?)(.*?)\6)?\]|\.[\w-]+|#[\w-]+|\w+|\*/g, function(part) { switch (part.charAt(0)) { case '.': params.classes.push(part.slice(1)); break; case '#': params.id = part.slice(1); break; case '[': params.attributes.push([arguments[4], arguments[5], arguments[7]]); break; case ':': var handler = DomElement.pseudos[arguments[1]]; if (!handler) { params.attributes.push([arguments[1], arguments[3] ? '=' : '', arguments[3]]); break; } params.pseudos.push({ name: arguments[1], argument: handler && handler.parser ? (handler.parser.apply ? handler.parser(arguments[3]) : handler.parser) : arguments[3], handler: handler.handler || handler }); break; default: params.tag = part; } return ''; }); return params; } function match(el, params, data) { if (params.id && params.id != el.id) return false; if (params.tag && params.tag != '*' && params.tag != (el.tagName || '').toLowerCase()) return false; for (var i = params.classes.length; i--;) if (!el.className || !el.className.contains(params.classes[i], ' ')) return false; var proto = DomElement.prototype; for (var i = params.attributes.length; i--;) { var attribute = params.attributes[i]; proto.$ = el; var val = proto.getProperty(attribute[0]); if (!val) return false; var operator = DomElement.operators[attribute[1]]; operator = operator && operator[FILTER]; if (operator && (!val || !operator(val, attribute[2]))) return false; } for (var i = params.pseudos.length; i--;) { var pseudo = params.pseudos[i]; if (!pseudo.handler[FILTER](el, pseudo.argument, data)) return false; } return true; } function filter(items, selector, context, elements, data) { var method = methods[!Browser.XPATH || items.length || typeof selector == 'string' && selector.contains('option[') ? FILTER : XPATH]; var separators = []; selector = selector.trim().replace(/\s*([+>~\s])[a-zA-Z#.*\s]/g, function(match) { if (match.charAt(2)) match = match.trim(); separators.push(match.charAt(0)); return ':)' + match.charAt(1); }).split(':)'); for (var i = 0, l = selector.length; i < l; i++) { var params = parse(selector[i]); if (!params) return elements; var next = method.getParam(items, separators[i - 1], context, params, data); if (!next) break; items = next; } method.getElements(items, elements, context); return elements; } return { beans: true, getElements: function(selectors, nowrap) { var elements = nowrap ? [] : new this._collection(); selectors = !selectors ? ['*'] : typeof selectors == 'string' ? selectors.split(',') : selectors.length != null ? selectors : [selectors]; for (var i = 0, l = selectors.length; i < l; i++) { var selector = selectors[i]; if (Base.type(selector) == 'element') elements.push(selector); else filter([], selector, this.$, elements, {}); } return elements; }, getElement: function(selector) { var el, type = Base.type(selector), match; if (type == 'window') { el = selector; } else { if (type == 'string' && (match = selector.match(/^#?([\w-]+)$/))) el = this.getDocument().$.getElementById(match[1]); else if (DomNode.isNode(type)) el = DomElement.unwrap(selector); if (el && el != this.$ && !DomElement.isAncestor(el, this.$)) el = null; if (!el) el = this.getElements(selector, true)[0]; } return DomNode.wrap(el); }, hasElement: function(selector) { return !!this.getElement(selector); }, match: function(selector) { return !selector || match(this.$, parse(selector), {}); }, filter: function(elements, selector) { return filter(elements, selector, this.$, new this._collection(), {}); }, statics: { match: function(el, selector) { return !selector || match(DomElement.unwrap(el), parse(selector), {}); } } }; }); DomElement.separators = { '~': [ '/following-sibling::', function(item, params, add) { while (item = item.nextSibling) if (item.nodeType == 1 && add(item)) break; } ], '+': [ '/following-sibling::*[1]/self::', function(item, params, add) { while (item = item.nextSibling) { if (item.nodeType == 1) { add(item); break; } } } ], '>': [ '/', function(item, params, add) { var children = item.childNodes; for (var i = 0, l = children.length; i < l; i++) if (children[i].nodeType == 1) add(children[i]); } ], ' ': [ '//', function(item, params, add) { var children = item.getElementsByTagName(params.tag); params.clearTag = true; for (var i = 0, l = children.length; i < l; i++) add(children[i]); } ] }; DomElement.operators = new function() { function contains(sep) { return [ function(a, v) { return '[contains(' + (sep ? 'concat("' + sep + '", @' + a + ', "' + sep + '")' : '@' + a) + ', "' + sep + v + sep + '")]'; }, function(a, v) { return a.contains(v, sep); } ] } return { '=': [ function(a, v) { return '[@' + a + '="' + v + '"]'; }, function(a, v) { return a == v; } ], '^=': [ function(a, v) { return '[starts-with(@' + a + ', "' + v + '")]'; }, function(a, v) { return a.substring(0, v.length) == v; } ], '$=': [ function(a, v) { return '[substring(@' + a + ', string-length(@' + a + ') - ' + v.length + ' + 1) = "' + v + '"]'; }, function(a, v) { return a.substring(a.length - v.length) == v; } ], '!=': [ function(a, v) { return '[@' + a + '!="' + v + '"]'; }, function(a, v) { return a != v; } ], '*=': contains(''), '|=': contains('-'), '~=': contains(' ') }; }; DomElement.pseudos = new function() { var nthChild = [ function(argument) { switch (argument.special) { case 'n': return '[count(preceding-sibling::*) mod ' + argument.a + ' = ' + argument.b + ']'; case 'first': return '[count(preceding-sibling::*) = 0]'; case 'last': return '[count(following-sibling::*) = 0]'; case 'only': return '[not(preceding-sibling::* or following-sibling::*)]'; case 'index': return '[count(preceding-sibling::*) = ' + argument.a + ']'; } }, function(el, argument, data) { var count = 0; switch (argument.special) { case 'n': data.indices = data.indices || {}; if (!data.indices[el._unique]) { var children = el.parentNode.childNodes; for (var i = 0, l = children.length; i < l; i++) { var child = children[i]; if (child.nodeType == 1) { if (!child._unique) DomNode.unique(item); data.indices[child._unique] = count++; } } } return data.indices[el._unique] % argument.a == argument.b; case 'first': while (el = el.previousSibling) if (el.nodeType == 1) return false; return true; case 'last': while (el = el.nextSibling) if (el.nodeType == 1) return false; return true; case 'only': var prev = el; while(prev = prev.previousSibling) if (prev.nodeType == 1) return false; var next = el; while (next = next.nextSibling) if (next.nodeType == 1) return false; return true; case 'index': while (el = el.previousSibling) if (el.nodeType == 1 && ++count > argument.a) return false; return true; } return false; } ]; function contains(caseless) { var abc = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; return [ function(argument) { return '[contains(' + (caseless ? 'translate(text(), "' + abc + '", "' + abc.toLowerCase() + '")' : 'text()') + ', "' + (caseless && argument ? argument.toLowerCase() : argument) + '")]'; }, function(el, argument) { if (caseless && argument) argument = argument.toLowerCase(); var nodes = el.childNodes; for (var i = nodes.length - 1; i >= 0; i--) { var child = nodes[i]; if (child.nodeName && child.nodeType == 3 && (caseless ? child.nodeValue.toLowerCase() : child.nodeValue).contains(argument)) return true; } return false; } ]; } return { 'nth-child': { parser: function(argument) { var match = argument ? argument.match(/^([+-]?\d*)?([a-z]+)?([+-]?\d*)?$/) : [null, 1, 'n', 0]; if (!match) return null; var i = parseInt(match[1]), a = isNaN(i) ? 1 : i, special = match[2], b = parseInt(match[3]) || 0; if (a != 0) { b--; while (b < 1) b += a; while (b >= a) b -= a; } else { a = b; special = 'index'; } switch (special) { case 'n': return { a: a, b: b, special: 'n' }; case 'odd': return { a: 2, b: 0, special: 'n' }; case 'even': return { a: 2, b: 1, special: 'n' }; case 'first': return { special: 'first' }; case 'last': return { special: 'last' }; case 'only': return { special: 'only' }; default: return { a: a - 1, special: 'index' }; } }, handler: nthChild }, 'even': { parser: { a: 2, b: 1, special: 'n' }, handler: nthChild }, 'odd': { parser: { a: 2, b: 0, special: 'n' }, handler: nthChild }, 'first-child': { parser: { special: 'first' }, handler: nthChild }, 'last-child': { parser: { special: 'last' }, handler: nthChild }, 'only-child': { parser: { special: 'only' }, handler: nthChild }, 'enabled': [ function() { return '[not(@disabled)]'; }, function(el) { return !el.disabled; } ], 'empty': [ function() { return '[not(node())]'; }, function(el) { return !(el.innerText || el.textContent || '').length; } ], 'contains': contains(false), 'contains-caseless': contains(true) }; }; HtmlElements = DomElements.extend(); HtmlElement = DomElement.extend({ _collection: HtmlElements }); HtmlElement.inject({ beans: true, _properties: ['html'], getClass: function() { return this.$.className; }, setClass: function(cls) { this.$.className = cls; }, modifyClass: function(name, add) { if (!this.hasClass(name) ^ !add) this.$.className = (add ? this.$.className + ' ' + name : this.$.className.replace(name, '')).clean(); return this; }, addClass: function(name) { return this.modifyClass(name, true); }, removeClass: function(name) { return this.modifyClass(name, false); }, toggleClass: function(name) { return this.modifyClass(name, !this.hasClass(name)); }, hasClass: function(name) { return this.$.className.contains(name, ' '); } }); Array.inject({ toNode: function(doc) { doc = DomNode.wrap(doc || document); var elements = new HtmlElements(); for (var i = 0; i < this.length;) { var value = this[i++], element = null, type = Base.type(value); if (type == 'string') { var props = /^(object|hash)$/.test(Base.type(this[i])) && this[i++]; element = value.isHtml() ? value.toNode(doc).set(props) : doc.createElement(value, props); if (Base.type(this[i]) == 'array') element.injectBottom(this[i++].toNode(doc)); } else if (DomNode.isNode(type)) { element = value; } else if (value && value.toNode) { element = value.toNode(doc); } if (element) elements[Base.type(element) == 'array' ? 'append' : 'push'](element); } return elements.length == 1 ? elements[0] : elements; } }); String.inject({ toNode: function(doc) { var doc = doc || document, elements; if (this.isHtml()) { var str = this.trim().toLowerCase(); var div = DomElement.unwrap(doc).createElement('div'); var wrap = !str.indexOf('', ''] || !str.indexOf('', ''] || (!str.indexOf('', ''] || !str.indexOf('', ''] || (!str.indexOf('', ''] || !str.indexOf('', ''] || [0,'','']; div.innerHTML = wrap[1] + this + wrap[2]; while (wrap[0]--) div = div.firstChild; if (Browser.TRIDENT) { var els = []; if (!str.indexOf('' && str.indexOf('= 0 ; --i) { var el = els[i]; if (el.nodeName.toLowerCase() == 'tbody' && !el.childNodes.length) el.parentNode.removeChild(el); } } elements = new HtmlElements(div.childNodes); } else { elements = DomNode.wrap(doc).getElements(this); } return elements.length == 1 ? elements[0] : elements; } }); HtmlDocument = DomDocument.extend({ _collection: HtmlElements }); HtmlElement.inject(new function() { var styles = { all: { width: '@px', height: '@px', left: '@px', top: '@px', right: '@px', bottom: '@px', color: 'rgb(@, @, @)', backgroundColor: 'rgb(@, @, @)', backgroundPosition: '@px @px', fontSize: '@px', letterSpacing: '@px', lineHeight: '@px', textIndent: '@px', margin: '@px @px @px @px', padding: '@px @px @px @px', border: '@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)', borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', borderColor: 'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)', clip: 'rect(@px, @px, @px, @px)', opacity: '@' }, part: { 'border': {}, 'borderWidth': {}, 'borderStyle': {}, 'borderColor': {}, 'margin': {}, 'padding': {} } }; ['Top', 'Right', 'Bottom', 'Left'].each(function(dir) { ['margin', 'padding'].each(function(style) { var sd = style + dir; styles.part[style][sd] = styles.all[sd] = '@px'; }); var bd = 'border' + dir; styles.part.border[bd] = styles.all[bd] = '@px @ rgb(@, @, @)'; var bdw = bd + 'Width', bds = bd + 'Style', bdc = bd + 'Color'; styles.part[bd] = {}; styles.part.borderWidth[bdw] = styles.part[bd][bdw] = '@px'; styles.part.borderStyle[bds] = styles.part[bd][bds] = '@'; styles.part.borderColor[bdc] = styles.part[bd][bdc] = 'rgb(@, @, @)'; }); Base.each(styles.all, function(val, name) { this[name] = val.split(' '); }); var fields = { beans: true, getComputedStyle: function(name) { if (this.$.currentStyle) return this.$.currentStyle[name.camelize()]; var style = this.getWindow().$.getComputedStyle(this.$, null); return style ? style.getPropertyValue(name.hyphenate()) : null; }, getStyle: function(name) { if (name === undefined) return this.getStyles(); if (name == 'opacity') { var op = this.opacity; return op || op == 0 ? op : this.getVisibility() ? 1 : 0; } var el = this.$; name = name.camelize(); var style = el.style[name]; if (!Base.check(style)) { if (styles.part[name]) { style = Hash.map(styles.part[name], function(val, key) { return this.getStyle(key); }, this); return style.every(function(val) { return val == style[0]; }) ? style[0] : style.join(' '); } style = this.getComputedStyle(name); } if (name == 'visibility') return /^(visible|inherit(|ed))$/.test(style); var color = style && style.match(/rgb[a]?\([\d\s,]+\)/); if (color) return style.replace(color[0], color[0].rgbToHex()); if (Browser.PRESTO || Browser.TRIDENT && isNaN(parseInt(style))) { if (/^(width|height)$/.test(name)) { var size = 0; (name == 'width' ? ['left', 'right'] : ['top', 'bottom']).each(function(val) { size += this.getStyle('border-' + val + '-width').toInt() + this.getStyle('padding-' + val).toInt(); }, this); return this.$['offset' + name.capitalize()] - size + 'px'; } if (Browser.PRESTO && /px/.test(style)) return style; if (/border(.+)[wW]idth|margin|padding/.test(name)) return '0px'; } return style; }, setStyle: function(name, value) { if (value === undefined) return this.setStyles(name); var el = this.$; switch (name) { case 'float': name = Browser.TRIDENT ? 'styleFloat' : 'cssFloat'; break; case 'clip': if (value == true) value = [0, el.offsetWidth, el.offsetHeight, 0]; break; default: name = name.camelize(); } var type = Base.type(value); if (value != undefined && type != 'string') { var parts = styles.all[name] || ['@'], index = 0; value = (type == 'array' ? value.flatten() : [value]).map(function(val) { var part = parts[index++]; if (!part) throw Base.stop; return Base.type(val) == 'number' ? part.replace('@', name == 'opacity' ? val : Math.round(val)) : val; }).join(' '); } switch (name) { case 'visibility': if (!isNaN(value)) value = !!value.toInt() + ''; value = value == 'true' && 'visible' || value == 'false' && 'hidden' || value; break; case 'opacity': this.opacity = value = parseFloat(value); this.setStyle('visibility', !!value); if (!value) value = 1; if (!el.currentStyle || !el.currentStyle.hasLayout) el.style.zoom = 1; if (Browser.TRIDENT) el.style.filter = value > 0 && value < 1 ? 'alpha(opacity=' + value * 100 + ')' : ''; el.style.opacity = value; return this; } el.style[name] = value; return this; }, getStyles: function() { return arguments.length ? Array.each(arguments, function(name) { this[name] = that.getStyle(name); }, {}) : this.$.style.cssText; }, setStyles: function(styles) { switch (Base.type(styles)) { case 'object': Base.each(styles, function(style, name) { if (style !== undefined) this.setStyle(name, style); }, this); break; case 'string': this.$.style.cssText = styles; } return this; } }; ['opacity', 'color', 'background', 'visibility', 'clip', 'zIndex', 'border', 'margin', 'padding', 'display'].each(function(name) { var part = name.capitalize(); fields['get' + part] = function() { return this.getStyle(name); }; fields['set' + part] = function(value) { return this.setStyle(name, arguments.length > 1 ? Array.create(arguments) : value); }; }); return fields; }); HtmlElement.inject({ beans: true, getFormElements: function() { return this.getElements(['input', 'select', 'textarea']); }, getValue: function(name) { var el = this.getElement(name); return el && el.getValue && el.getValue(); }, setValue: function(name, val) { var el = this.getElement(name); if (!el) el = this.injectTop('input', { type: 'hidden', id: name, name: name }); return el.setValue(val); }, getValues: function() { return this.getFormElements().each(function(el) { var name = el.getName(), value = el.getValue(); if (name && value !== undefined && !el.getDisabled()) this[name] = value; }, new Hash()); }, setValues: function(values) { return Base.each(values, function(val, name) { this.setValue(name, val); }, this); }, toQueryString: function() { return Base.toQueryString(this.getValues()); } }); HtmlForm = HtmlElement.extend({ beans: true, _tag: 'form', _properties: ['action', 'method', 'target'], _methods: ['submit'], blur: function() { return this.getFormElements().each(function(el) { el.blur(); }, this); }, enable: function(enable) { return this.getFormElements().each(function(el) { el.enable(enable); }, this); } }); HtmlFormElement = HtmlElement.extend({ beans: true, _properties: ['name', 'disabled'], _methods: ['focus', 'blur'], enable: function(enable) { var disabled = !enable && enable !== undefined; if (disabled) this.$.blur(); this.$.disabled = disabled; return this; } }); HtmlInput = HtmlFormElement.extend({ beans: true, _tag: 'input', _properties: ['type', 'checked', 'defaultChecked', 'readOnly', 'maxLength'], _methods: ['click'], getValue: function() { if (this.$.checked && /^(checkbox|radio)$/.test(this.$.type) || /^(hidden|text|password|button|search)$/.test(this.$.type)) return this.$.value; }, setValue: function(val) { if (/^(checkbox|radio)$/.test(this.$.type)) this.$.checked = this.$.value == val; else this.$.value = val != null ? val : ''; return this; } }); HtmlTextArea = HtmlFormElement.extend({ beans: true, _tag: 'textarea', _properties: ['value'] }); HtmlSelect = HtmlFormElement.extend({ beans: true, _tag: 'select', _properties: ['type', 'selectedIndex'], getOptions: function() { return this.getElements('option'); }, getSelected: function() { return this.getElements('option[selected]'); }, setSelected: function(values) { this.$.selectedIndex = -1; if (values) { Array.each(values.length != null ? values : [values], function(val) { val = DomElement.unwrap(val); if (val != null) this.getElements('option[value="' + (val.value || val) + '"]').setProperty('selected', true); }, this); } return this; }, getValue: function() { return this.getSelected().getProperty('value'); }, setValue: function(values) { return this.setSelected(values); } }); HtmlOption = HtmlFormElement.extend({ beans: true, _tag: 'option', _properties: ['text', 'value', 'selected', 'defaultSelected', 'index'] }); HtmlFormElement.inject({ setSelection: function(start, end) { var sel = end == undefined ? start : { start: start, end: end }; this.focus(); if (this.$.setSelectionRange) { this.$.setSelectionRange(sel.start, sel.end); } else { var value = this.getValue(); var len = value.substring(sel.start, sel.end).replace(/\r/g, '').length; var pos = value.substring(0, sel.start).replace(/\r/g, '').length; var range = this.$.createTextRange(); range.collapse(true); range.moveEnd('character', pos + len); range.moveStart('character', pos); range.select(); } return this; }, getSelection: function() { if (this.$.selectionStart !== undefined) { return { start: this.$.selectionStart, end: this.$.selectionEnd }; } else { this.focus(); var pos = { start: 0, end: 0 }; var range = this.getDocument().$.selection.createRange(); var dup = range.duplicate(); if (this.$.type == 'text') { pos.start = 0 - dup.moveStart('character', -100000); pos.end = pos.start + range.text.length; } else { var value = this.getValue(); dup.moveToElementText(this.$); dup.setEndPoint('StartToEnd', range); pos.end = value.length - dup.text.length; dup.setEndPoint('StartToStart', range); pos.start = value.length - dup.text.length; } return pos; } }, getSelectedText: function() { var range = this.getSelection(); return this.getValue().substring(range.start, range.end); }, replaceSelectedText: function(value, select) { var range = this.getSelection(), current = this.getValue(); var top = this.$.scrollTop, height = this.$.scrollHeight; this.setValue(current.substring(0, range.start) + value + current.substring(range.end, current.length)); if (top != null) this.$.scrollTop = top + this.$.scrollHeight - height; return select || select == undefined ? this.setSelection(range.start, range.start + value.length) : this.setCaret(range.start + value.length); }, getCaret: function() { return this.getSelection().start; }, setCaret: function(pos) { return this.setSelection(pos, pos); } }); HtmlImage = HtmlElement.extend({ beans: true, _tag: 'img', _properties: ['src', 'alt', 'title'] }); $document = Browser.document = DomNode.wrap(document); $window = Browser.window = DomNode.wrap(window).addEvent('unload', DomNode.dispose); Chain = { chain: function(fn) { (this._chain = this._chain || []).push(fn); return this; }, callChain: function() { if (this._chain && this._chain.length) this._chain.shift().apply(this, arguments); return this; }, clearChain: function() { this._chain = []; return this; } }; Callback = { addEvent: function(type, fn) { var ref = this.events = this.events || {}; ref = ref[type] = ref[type] || []; if (!ref.find(function(val) { return val == fn })) ref.push(fn); return this; }, addEvents: function(events) { return Base.each((events || []), function(fn, type) { this.addEvent(type, fn); }, this); }, fireEvent: function(type, args, delay) { return (this.events && this.events[type] || []).each(function(fn) { fn.delay(delay, this, args); }, this); }, removeEvent: function(type, fn) { if (this.events && this.events[type]) this.events[type].remove(function(val) { return fn == val; }); return this; }, setOptions: function(opts) { return (this.options = Hash.create(this.options, opts)).each(function(val, i) { if (typeof val == 'function' && (i = i.match(/^on([A-Z]\w*)/))) this.addEvent(i[1].toLowerCase(), val); }, this); }, statics: { inject: function() { var proto = this.prototype, options = proto.options; this.base.apply(this, arguments); if (proto.options != options) proto.options = Hash.merge({}, options, proto.options); return this; } } }; Request = Base.extend(Chain, Callback, new function() { var unique = 0; function createRequest(that) { if (!that.transport) that.transport = window.XMLHttpRequest && new XMLHttpRequest() || Browser.TRIDENT && new ActiveXObject('Microsoft.XMLHTTP'); } function createFrame(that) { var id = 'request_' + unique++, load = that.onFrameLoad.bind(that); var div = DomElement.get('body').injectBottom('div', { styles: { position: 'absolute', width: 0, height: 0, top: 0, marginLeft: '-10000px' } }, [ 'iframe', { name: id, id: id, events: { load: load, readystatechange: load } } ] ); that.frame = { id: id, div: div, iframe: window.frames[id] || document.getElementById(id), element: DomElement.get(id) }; div.offsetWidth; } return { options: { headers: { 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*' }, method: 'post', async: true, urlEncoded: true, encoding: 'utf-8', emulation: true, secure: false }, initialize: function() { var params = Array.associate(arguments, { url: 'string', options: 'object', handler: 'function' }); this.setOptions(params.options); if (params.handler) this.addEvent('complete', params.handler); if (this.options.update) this.options.type = 'html'; this.headers = new Hash(this.options.headers); if (this.options.type == 'json') { this.setHeader('Accept', 'application/json'); this.setHeader('X-Request', 'JSON'); } if (this.options.urlEncoded && /^(post|put)$/.test(this.options.method)) { this.setHeader('Content-Type', 'application/x-www-form-urlencoded' + (this.options.encoding ? '; charset=' + this.options.encoding : '')); } this.headers.append(this.options.headers); }, onStateChange: function() { if (this.transport.readyState == 4 && this.running) { this.running = false; this.status = 0; try { this.status = this.transport.status; delete this.transport.onreadystatechange; } catch (e) {} if (!this.status || this.status >= 200 && this.status < 300) { this.success(this.transport.responseText, this.transport.responseXML); } else { this.fireEvent('complete').fireEvent('failure'); } } }, onFrameLoad: function() { var frame = this.frame && this.frame.iframe, loc = frame && frame.location, doc = frame && (frame.contentDocument || frame.contentWindow || frame).document; if (this.running && frame && loc && (!loc.href || loc.href.indexOf(this.url) != -1) && /^(loaded|complete|undefined)$/.test(doc.readyState)) { this.running = false; var html = this.options.type == 'html', area = !html && doc.getElementsByTagName('textarea')[0]; var text = doc && (area && area.value || doc.body && (html && doc.body.innerHTML || doc.body.textContent || doc.body.innerText)) || ''; this.frame.element.setProperty('src', ''); this.success(text); if (!this.options.link) { var div = this.frame.div; div.insertBottom(DomElement.get('body')); div.remove.delay(5000, div); this.frame = null; } } }, success: function(text, xml) { var args; switch (this.options.type) { case 'html': var match = text.match(/]*>([\u0000-\uffff]*?)<\/body>/i); var stripped = this.stripScripts(match ? match[1] : text); if (this.options.update) DomElement.get(this.options.update).setHtml(stripped.html); if (this.options.evalScripts) this.executeScript(stripped.script); args = [ stripped.html, text ]; break; case 'json': args = [ Json.decode(text, this.options.secure), text ]; break; default: args = [ this.processScripts(text), xml ] } this.fireEvent('complete', args) .fireEvent('success', args) .callChain(); }, stripScripts: function(html) { var script = ''; html = html.replace(/]*>([\u0000-\uffff]*?)<\/script>/gi, function() { script += arguments[1] + '\n'; return ''; }); return { html: html, script: script }; }, processScripts: function(text) { if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) { this.executeScript(text); return text; } else { var stripped = this.stripScripts(text); if (this.options.evalScripts) this.executeScript(stripped.script); return stripped.html; } }, executeScript: function(script) { if (window.execScript) { window.execScript(script); } else { DomElement.get('head').injectBottom('script', { type: 'text/javascript', text: script }).remove(); } }, setHeader: function(name, value) { this.headers[name] = value; return this; }, getHeader: function(name) { try { if (this.transport) return this.transport.getResponseHeader(name); } catch (e) {} return null; }, send: function() { var params = Array.associate(arguments, { url: 'string', options: 'object', handler: 'function' }); var opts = params.options ? Hash.merge(params.options, this.options) : this.options; if (params.handler) this.addEvent('complete', function() { params.handler.apply(this, arguments); this.removeEvent('complete', arguments.callee); }); if (this.running) { switch (opts.link) { case 'cancel': this.cancel(); break; case 'chain': this.chain(this.send.wrap(this, arguments)); default: return this; } } var data = opts.data || ''; var url = params.url || opts.url; switch (Base.type(data)) { case 'element': data = DomNode.wrap(data); if (data.getTag() != 'form' || !data.hasElement('input[type=file]')) data = data.toQueryString(); break; case 'object': data = Base.toQueryString(data); break; default: data = data.toString(); } var string = typeof data == 'string', method = opts.method; if (opts.emulation && /^(put|delete)$/.test(method)) { if (string) data += '&_method=' + method; else data.setValue('_method', method); method = 'post'; } if (string && !this.options.iframe) { createRequest(this); if (!this.transport) { if (!this.frame) createFrame(this); method = 'get'; } } else if (!this.frame) { createFrame(this); } if (string && data && method == 'get') { url += (url.contains('?') ? '&' : '?') + data; data = null; } this.running = true; this.url = url; if (this.frame) { var form = !string && data; if (form) { form.set({ target: this.frame.id, action: url, method: method, enctype: method == 'get' ? 'application/x-www-form-urlencoded' : 'multipart/form-data', 'accept-charset': opts.encoding || '' }).submit(); } else { this.frame.element.setProperty('src', url); } } else if (this.transport) { try { this.transport.open(method.toUpperCase(), url, opts.async); this.transport.onreadystatechange = this.onStateChange.bind(this); new Hash(this.headers, opts.headers).each(function(header, name) { try{ this.transport.setRequestHeader(name, header); } catch (e) { this.fireEvent('exception', [e, name, header]); } }, this); this.fireEvent('request'); this.transport.send(data); if (!opts.async) this.onStateChange(); } catch (e) { this.fireEvent('failure', [e]); } } return this; }, cancel: function() { if (this.running) { this.running = false; if (this.transport) { this.transport.abort(); this.transport.onreadystatechange = null; this.transport = null; } else if (this.frame) { this.frame.div.remove(); this.frame = null; } this.fireEvent('cancel'); } return this; } }; }); HtmlForm.inject({ send: function(url) { if (!this.sender) this.sender = new Request({ link: 'cancel' }); this.sender.send({ url: url || this.getProperty('action'), data: this, method: this.getProperty('method') || 'post' }); } }); HtmlElement.inject({ load: function() { if (!this.loader) this.loader = new Request({ link: 'cancel', update: this, method: 'get' }); this.loader.send(Array.associate(arguments, { data: 'object', url: 'string' })); return this; } }); Asset = new function() { function getProperties(props) { return props ? Hash.create(props).each(function(val, key) { if (/^on/.test(key)) delete this[key]; }) : {}; } function createMultiple(type, sources, options) { var props = getProperties(options), count = 0; options = options || {}; return sources.each(function(src) { props.onLoad = function() { if (options.onProgress) options.onProgress(src); if (++count == sources.length && options.onComplete) options.onComplete(); } this.push(Asset[type](src, props)); }, new HtmlElements()); } return { script: function(src, props) { var script = DomElement.get('head').injectBottom('script', Hash.merge({ events: { load: props.onLoad && function() { if (!this.loaded) { this.loaded = true; props.onLoad.call(this); } }, readystatechange: function() { if (/loaded|complete/.test(this.$.readyState)) this.fireEvent('load'); } }, src: src }, getProperties(props))); if (Browser.WEBKIT && Browser.VERSION < 420) new Request({ url: src, method: 'get' }).addEvent('success', function() { script.fireEvent('load', [], 1); }).send(); return script; }, stylesheet: function(src, props) { return new HtmlElement('link', new Hash({ rel: 'stylesheet', media: 'screen', type: 'text/css', href: src }, props)).insertInside(DomElement.get('head')); }, image: function(src, props) { props = props || {}; var image = new Image(); image.src = src; var element = new HtmlElement('img', { src: src }); ['load', 'abort', 'error'].each(function(type) { var name = 'on' + type.capitalize(); if (props[name]) element.addEvent(type, function() { this.removeEvent(type, arguments.callee); props[name].call(this); }); }); if (image.width && image.height) element.fireEvent('load', [], 1); return element.setProperties(getProperties(props)); }, scripts: function(sources, options) { return createMultiple('script', sources, options); }, stylesheets: function(sources, options) { return createMultiple('stylesheet', sources, options); }, images: function(sources, options) { return createMultiple('image', sources, options); } } }; Cookie = { set: function(name, value, expires, path) { document.cookie = name + '=' + encodeURIComponent(value) + (expires ? ';expires=' + expires.toGMTString() : '') + ';path=' + (path || '/'); }, get: function(name) { var res = document.cookie.match('(?:^|;)\\s*' + name + '=([^;]*)'); if (res) return decodeURIComponent(res[1]); }, remove: function(name) { this.set(name, '', -1); } }; Fx = Base.extend(Chain, Callback, { options: { fps: 50, unit: false, duration: 500, wait: true, transition: function(p) { return -(Math.cos(Math.PI * p) - 1) / 2; } }, initialize: function(element, options) { this.element = DomElement.get(element); this.setOptions(options); }, step: function() { var time = Date.now(); if (time < this.time + this.options.duration) { this.delta = this.options.transition((time - this.time) / this.options.duration); this.update(this.get()); } else { this.stop(true); this.update(this.to); this.fireEvent('complete', [this.element]).callChain(); } }, set: function(to) { this.update(to); this.fireEvent('set', [this.element]); return this; }, get: function() { return this.compute(this.from, this.to); }, compute: function(from, to) { return (to - from) * this.delta + from; }, start: function(from, to) { if (!this.options.wait) this.stop(); else if (this.timer) return this; this.from = from; this.to = to; this.time = Date.now(); if (!this.slave) { this.timer = this.step.periodic(Math.round(1000 / this.options.fps), this); this.fireEvent('start', [this.element]); } this.step(); return this; }, stop: function(end) { if (this.timer) { this.timer = this.timer.clear(); if (!end) this.fireEvent('cancel', [this.element]).clearChain(); } return this; } }); Fx.Scroll = Fx.extend({ options: { offset: { x: 0, y: 0 }, wheelStops: true }, initialize: function(element, options) { this.base(element, options); if (this.options.wheelStops) { var stop = this.stop.bind(this), stopper = this.element; this.addEvent('start', function() { stopper.addEvent('mousewheel', stop); }, true); this.addEvent('complete', function() { stopper.removeEvent('mousewheel', stop); }, true); } }, update: function(x, y) { var now = Array.flatten(arguments); this.element.setScrollOffset(now[0], now[1]); }, get: function() { var now = []; for (var i = 0; i < 2; i++) now.push(this.compute(this.from[i], this.to[i])); return now; }, start: function(x, y) { var offsetSize = this.element.getSize(), scrollSize = this.element.getScrollSize(), scroll = this.element.getScrollOffset(), values = { x: x, y: y }; var lookup = { x: 'width', y: 'height' }; for (var i in values) { var s = lookup[i]; var max = scrollSize[s] - offsetSize[s]; if (Base.check(values[i])) values[i] = Base.type(values[i]) == 'number' ? values[i].limit(0, max) : max; else values[i] = scroll[i]; values[i] += this.options.offset[i]; } return this.base([scroll.x, scroll.y], [values.x, values.y]); }, toTop: function() { return this.start(false, 0); }, toLeft: function() { return this.start(0, false); }, toRight: function() { return this.start('right', false); }, toBottom: function() { return this.start(false, 'bottom'); }, toElement: function(el, options) { var el = DomElement.get(el), offset = el.getOffset(), current = el.getWindow().getScrollOffset(); return this.start( !options || options.x ? offset.x : current.x, !options || options.y ? offset.y : current.y); } }); Fx.SmoothScroll = Fx.Scroll.extend({ initialize: function(options, context) { context = DomElement.get(context || document); var doc = context.getDocument(), win = context.getWindow(); this.base(doc, options); var links = this.options.links ? $$(this.options.links) : $$('a', context); var loc = win.location.href.match(/^[^#]*/)[0] + '#'; links.each(function(link) { if (link.$.href.indexOf(loc) != 0) return; var hash = link.$.href.substring(loc.length); var anchor = hash && DomElement.get('#' + hash, context); if (anchor) { link.addEvent('click', function(event) { this.toElement(anchor); var props = anchor.getProperties('name', 'id'); anchor.removeProperties('name', 'id'); win.location.hash = hash; anchor.setProperties(props); event.stop(); }.bind(this)); } }, this); } }); Fx.CSS = new function() { var parsers = new Hash({ color: { match: function(value) { if (value.match(/^#[0-9a-f]{3,6}$/i)) return value.hexToRgb(true); return ((value = value.match(/(\d+),\s*(\d+),\s*(\d+)/))) ? [value[1], value[2], value[3]] : false; }, compute: function(from, to, fx) { return from.map(function(value, i) { return Math.round(fx.compute(value, to[i])); }); }, get: function(value) { return value.map(Number); } }, number: { match: function(value) { return parseFloat(value); }, compute: function(from, to, fx) { return fx.compute(from, to); }, get: function(value, unit) { return (unit) ? value + unit : value; } } }); return { start: function(element, property, values) { values = Array.convert(values); if (!Base.check(values[1])) values = [ element.getStyle(property), values[0] ]; var parsed = values.map(Fx.CSS.set); return { from: parsed[0], to: parsed[1] }; }, set: function(value) { return Array.convert(value).map(function(val) { val = val + ''; var res = parsers.find(function(parser, key) { var value = parser.match(val); if (Base.check(value)) return { value: value, parser: parser }; }) || { value: val, parser: { compute: function(from, to) { return to; } } }; return res; }); }, compute: function(from, to, fx) { return from.map(function(obj, i) { return { value: obj.parser.compute(obj.value, to[i].value, fx), parser: obj.parser }; }); }, get: function(now, unit) { return now.reduce(function(prev, cur) { var get = cur.parser.get; return prev.concat(get ? get(cur.value, unit) : cur.value); }, []); } } }; Fx.Style = Fx.extend({ initialize: function(element, property, options) { this.base(element, options); this.property = property; }, hide: function() { return this.set(0); }, get: function() { return Fx.CSS.compute(this.from, this.to, this); }, set: function(to) { return this.base(Fx.CSS.set(to)); }, start: function(from, to) { if (this.timer && this.options.wait) return this; var parsed = Fx.CSS.start(this.element, this.property, [from, to]); return this.base(parsed.from, parsed.to); }, update: function(val) { this.element.setStyle(this.property, Fx.CSS.get(val, this.options.unit)); } }); HtmlElement.inject({ effect: function(prop, opts) { return new Fx.Style(this, prop, opts); } }); Fx.Styles = Fx.extend({ get: function() { var that = this; return Base.each(this.from, function(from, key) { this[key] = Fx.CSS.compute(from, that.to[key], that); }, {}); }, set: function(to) { return this.base(Base.each(to, function(val, key) { this[key] = Fx.CSS.set(val); }, {})); }, start: function(obj) { if (this.timer && this.options.wait) return this; var from = {}, to = {}; Base.each(obj, function(val, key) { var parsed = Fx.CSS.start(this.element, key, val); from[key] = parsed.from; to[key] = parsed.to; }, this); return this.base(from, to); }, update: function(val) { Base.each(val, function(val, key) { this.element.setStyle(key, Fx.CSS.get(val, this.options.unit)); }, this); } }); HtmlElement.inject({ effects: function(opts) { return new Fx.Styles(this, opts); } }); Fx.Elements = Fx.extend({ initialize: function(elements, options) { this.base(null, options); this.elements = DomElement.getAll(elements); }, start: function(obj) { if (this.timer && this.options.wait) return this; this.effects = {}; function start(that, key, val) { var fx = that.effects[key] = new Fx.Styles(that.elements[key], that.options); fx.slave = true; fx.start(val); } Base.each(obj, function(val, key) { if (key == '*') { this.elements.each(function(el, key) { start(this, key, val); }, this); } else if (isNaN(parseInt(key))) { var els = DomElement.getAll(key); this.elements.append(els); els.each(function(el) { start(this, this.elements.indexOf(el), val); }, this); } else { start(this, key, val); } }, this); return this.base(); }, set: function(to) { }, update: function(to) { Base.each(this.effects, function(fx) { fx.step(); }); } }); Fx.Transitions = new Base().inject({ inject: function(src) { return this.base(Base.each(src, function(func, name) { func.In = func; func.Out = function(pos) { return 1 - func(1 - pos); } func.InOut = function(pos) { return pos <= 0.5 ? func(2 * pos) / 2 : (2 - func(2 * (1 - pos))) / 2; } })); }, Linear: function(p) { return p; } }); Fx.Transitions.inject({ Pow: function(p, x) { return Math.pow(p, x[0] || 6); }, Expo: function(p) { return Math.pow(2, 8 * (p - 1)); }, Circ: function(p) { return 1 - Math.sin(Math.acos(p)); }, Sine: function(p) { return 1 - Math.sin((1 - p) * Math.PI / 2); }, Back: function(p, x) { x = x[0] || 1.618; return Math.pow(p, 2) * ((x + 1) * p - x); }, Bounce: function(p) { var value; for (var a = 0, b = 1; 1; a += b, b /= 2) { if (p >= (7 - 4 * a) / 11) { value = - Math.pow((11 - 6 * a - 11 * p) / 4, 2) + b * b; break; } } return value; }, Elastic: function(p, x) { return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x[0] || 1) / 3); } }); Fx.Transitions.inject(['Quad', 'Cubic', 'Quart', 'Quint'].each(function(name, i) { this[name] = function(p) { return Math.pow(p, i + 2); } }, {}));