javascripts/right-src.js in right-rails-0.4.4 vs javascripts/right-src.js in right-rails-0.5.0

- old
+ new

@@ -1,52 +1,30 @@ /** * RightJS, http://rightjs.org * Released under the MIT license * - * Custom build with options: no-olds - * - * Copyright (C) 2008-2009 Nikolay Nemshilov + * Copyright (C) 2008-2010 Nikolay Nemshilov */ /** * The framework description object * - * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ -var RightJS = { - version: "1.5.3", - modules: ["core", "form", "cookie", "xhr", "fx"] +RightJS = { + version: "1.5.6", + modules: ["core", "dom", "form", "cookie", "xhr", "fx"] }; /** - * this object will contain info about the current browser - * - * Copyright (C) 2008 Nikolay V. Nemshilov aka St. <nemshilov#gma-il> - */ -var Browser = (function(agent) { - return { - IE: !!(window.attachEvent && !window.opera), - Opera: !!window.opera, - WebKit: agent.indexOf('AppleWebKit/') > -1, - Gecko: agent.indexOf('Gecko') > -1 && agent.indexOf('KHTML') < 0, - MobileSafari: !!agent.match(/Apple.*Mobile.*Safari/), - Konqueror: agent.indexOf('Konqueror') > -1, - - // marker for the browsers which don't give access to the HTMLElement unit - OLD: agent.indexOf('MSIE 6') > -1 || agent.indexOf('MSIE 7') > -1, - IE8: agent.indexOf('MSIE 8') > -1 - } -})(navigator.userAgent); - -/** * There are some util methods * * Credits: * Some of the functionality and names are inspired or copied from * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti * - * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-il> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ /** * extends the first object with the keys and values of the second one * @@ -57,14 +35,14 @@ * @param Object source object * @param Boolean flag if the function should not overwrite intersecting values * @return Objecte extended destination object */ function $ext(dest, src, dont_overwrite) { - var src = src || {}; + var src = src || {}, key; - for (var key in src) - if (dont_overwrite === undefined || dest[key] === undefined) + for (key in src) + if (dont_overwrite !== true || typeof(dest[key]) === 'undefined') dest[key] = src[key]; return dest; }; @@ -83,11 +61,11 @@ return arguments[i](); } catch(e) {} } }; -/** +/** !#server * evals the given javascript text in the context of the current window * * @param String javascript * @return void */ @@ -140,28 +118,14 @@ * * @param mixed value * @return boolean check result */ function defined(value) { - return value !== undefined; + return typeof(value) !== 'undefined'; }; -/** - * checks if the given value is a hash-like object - * - * @param mixed value - * @return boolean check result - */ -function isHash(value) { - return typeof(value) === 'object' && value !== null && value.constructor === Object; -}; -// Opera 10.00 and Konqueror 3 heed some extra care with that -if (isHash(document.createElement('p'))) { - eval(isHash.toString().replace(';', '&&!(arguments[0] instanceof HTMLElement);')); -} - /** * checks if the given value is a function * * @param mixed value * @return boolean check result @@ -178,19 +142,10 @@ */ function isString(value) { return typeof(value) === 'string'; }; -/** - * checks if the given value is an array - * - * @param mixed value to check - * @return boolean check result - */ -function isArray(value) { - return value instanceof Array; -}; /** * checks if the given value is a number * * @param mixed value to check @@ -198,70 +153,52 @@ */ function isNumber(value) { return typeof(value) === 'number'; }; -/** +/** !#server * checks if the given value is an element * * @param mixed value to check * @return boolean check result */ function isElement(value) { return value && value.tagName; }; -/** +/** !#server * checks if the given value is a DOM-node * * @param mixed value to check * @return boolean check result */ function isNode(value) { return value && value.nodeType; }; -/** - * converts any iterables into an array - * - * @param Object iterable - * @return Array list - */ -var $A = (function(slice) { - return function (it) { - try { - return slice.call(it); - } catch(e) { - for (var a=[], i=0, length = it.length; i < length; i++) - a[i] = it[i]; - return a; - } - }; -})(Array.prototype.slice); - -/** +/** !#server * shortcut to instance new elements * * @param String tag name * @param object options * @return Element instance */ function $E(tag_name, options) { return new Element(tag_name, options); }; -/** +/** !#server * searches an element by id and/or extends it with the framework extentions * * @param String element id or Element to extend * @return Element or null */ function $(element) { return typeof(element) === 'string' ? document.getElementById(element) : element; }; -/** +/** !#server * searches for elements in the document which matches the given css-rule * * @param String css-rule * @return Array matching elements list */ @@ -275,44 +212,106 @@ * @param String string * @return Array of words */ function $w(string) { return string.trim().split(/\s+/); -} +}; -/** - * generates an unique id for an object - * - * @param Object object - * @return Integer uniq id - */ -var $uid = (function(UID) { - return function(item) { +// we need to generate those functions in an anonymous scope +(function() { + var to_s = Object.prototype.toString, slice = Array.prototype.slice, UID = 1; + + /** + * checks if the given value is a hash-like object + * + * @param mixed value + * @return boolean check result + */ + isHash = function(value) { + return to_s.call(value) === '[object Object]'; + }; + + /** !#server + * Internet Explorer needs some additional mumbo-jumbo in here + */ + if (isHash(document.documentElement)) { + isHash = function(value) { + return to_s.call(value) === '[object Object]' && + value !== null && typeof(value) !== 'undefined' && + typeof(value.hasOwnProperty) !== 'undefined'; + }; + } + + /** + * checks if the given value is an array + * + * @param mixed value to check + * @return boolean check result + */ + isArray = function(value) { + return to_s.call(value) === '[object Array]'; + }; + + /** + * converts any iterables into an array + * + * @param Object iterable + * @return Array list + */ + $A = function (it) { + try { + return slice.call(it); + } catch(e) { + for (var a=[], i=0, length = it.length; i < length; i++) + a[i] = it[i]; + return a; + } + }; + + /** + * generates an unique id for an object + * + * @param Object object + * @return Integer uniq id + */ + $uid = function(item) { return item.uid || (item.uid = UID++); }; -})(1); + + /** + * Generating methods for native units extending + */ + for (var i=0, natives = [Array, Function, Number, String, Date, RegExp]; i < natives.length; i++) { + natives[i].include = function(module, dont_overwrite) { + $ext(this.prototype, module, dont_overwrite); + return this; + }; + } +})(); + + /** * The Object class extentions * * Credits: * Some functionality is inspired by * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson * - * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-il> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ $ext(Object, { /** * extracts the list of the attribute names of the given object * * @param Object object * @return Array keys list */ keys: function(object) { - var keys = []; - for (var key in object) + var keys = [], key; + for (key in object) keys.push(key); return keys; }, /** @@ -320,12 +319,12 @@ * * @param Object object * @return Array values list */ values: function(object) { - var values = []; - for (var key in object) + var values = [], key; + for (key in object) values.push(object[key]); return values; }, /** @@ -348,13 +347,13 @@ * @param String key-name to exclude * ..... * @return Object filtered copy */ without: function() { - var filter = $A(arguments), object = filter.shift(), copy = {}; + var filter = $A(arguments), object = filter.shift(), copy = {}, key; - for (var key in object) + for (key in object) if (!filter.includes(key)) copy[key] = object[key]; return copy; }, @@ -369,13 +368,14 @@ * @param String key name to exclude * ..... * @return Object filtered copy */ only: function() { - var filter = $A(arguments), object = filter.shift(), copy = {}; + var filter = $A(arguments), object = filter.shift(), copy = {}, + i=0, length = filter.length; - for (var i=0, length = filter.length; i < length; i++) { + for (; i < length; i++) { if (defined(object[filter[i]])) copy[filter[i]] = object[filter[i]]; } return copy; @@ -393,12 +393,12 @@ * @param Object mixing * ...... * @return Object merged object */ merge: function() { - var object = {}; - for (var i=0, length = arguments.length; i < length; i++) { + var object = {}, i=0, length = arguments.length; + for (; i < length; i++) { if (isHash(arguments[i])) { $ext(object, arguments[i]); } } return object; @@ -409,26 +409,26 @@ * * @param Object object * @return String query */ toQueryString: function(object) { - var tokens = []; - for (var key in object) { + var tokens = [], key; + for (key in object) { tokens.push(key+'='+encodeURIComponent(object[key])) } return tokens.join('&'); } -}); +}, true); /** * here are the starndard Math object extends * * Credits: * The idea of random mehtod is taken from * - Ruby (http://www.ruby-lang.org) Copyright (C) Yukihiro Matsumoto * - * Copyright (C) 2008 Nikolay V. Nemshilov aka St. <nemshilov#gma-il> + * Copyright (C) 2008 Nikolay V. Nemshilov */ $ext(Math, { /** * the standard random method replacement, to make it more useful * @@ -460,13 +460,13 @@ * Credits: * Some of the functionality is inspired by * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson * - Ruby (http://www.ruby-lang.org) Copyright (C) Yukihiro Matsumoto * - * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-il> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ -$ext(Array.prototype, (function(A_proto) { +(function(A_proto) { // JavaScript 1.6 methods recatching up or faking var for_each = A_proto.forEach || function(callback, scope) { for (var i=0, length = this.length; i < length; i++) callback.call(scope, this[i], i, this); @@ -501,33 +501,33 @@ return false; } return true; }; - var first = function(callback, scope) { + function first(callback, scope) { for (var i=0, length = this.length; i < length; i++) { if (callback.call(scope, this[i], i, this)) return this[i]; } - return undefined; + return this._$u; // <- undefined, see #191 }; - var last = function(callback, scope) { + function last(callback, scope) { for (var i=this.length-1; i > -1; i--) { if (callback.call(scope, this[i], i, this)) return this[i]; } - return undefined; + return this._$u; // <- undefined, see #191 }; // // RightJS callbacks magick preprocessing // // prepares a correct callback function - var guess_callback = function(args, array) { + function guess_callback(args, array) { var callback = args[0], args = A_proto.slice.call(args, 1), scope = array; if (isString(callback)) { var attr = callback; if (array.length && isFunction(array[0][attr])) { @@ -541,17 +541,22 @@ return [callback, scope]; }; // calls the given method with preprocessing the arguments - var call_method = function(func, scope, args) { + function call_method(func, scope, args) { try { return func.apply(scope, guess_callback(args, scope)); } catch(e) { if (!(e instanceof Break)) throw(e); } }; -return { + // checks the value as a boolean + function boolean_check(i) { + return !!i; + }; + +Array.include({ /** * IE fix * returns the index of the value in the array * * @param mixed value @@ -682,23 +687,23 @@ * * @param Function optional callback for checks * @param Object optional scope for the callback * @return boolean check result */ - some: function() { - return call_method(some, this, arguments.length ? arguments : [function(i) { return !!i; }]); + some: function(value) { + return call_method(some, this, value ? arguments : [boolean_check]); }, /** * checks if all the array elements are logically true * * @param Function optional callback for checks * @param Object optional scope for the callback * @return Boolean check result */ - every: function() { - return call_method(every, this, arguments.length ? arguments : [function(i) { return !!i; }]); + every: function(value) { + return call_method(every, this, value ? arguments : [boolean_check]); }, /** * applies the given lambda to each element in the array * @@ -719,14 +724,14 @@ * @param Array to merge * .................... * @return Array new merged */ merge: function() { - for (var copy = this.clone(), arg, i=0, length = arguments.length; i < length; i++) { + for (var copy = this.clone(), arg, i=0, j, length = arguments.length; i < length; i++) { arg = arguments[i]; if (isArray(arg)) { - for (var j=0; j < arg.length; j++) { + for (j=0; j < arg.length; j++) { if (copy.indexOf(arg[j]) == -1) copy.push(arg[j]); } } else if (copy.indexOf(arg) == -1) { copy.push(arg); @@ -756,11 +761,11 @@ * returns a copy of the array whithout any null or undefined values * * @return Array filtered version */ compact: function() { - return this.without(null, undefined); + return this.without(null, this._$u); // <- this._u === undefined, see #191 }, /** * returns a copy of the array which contains only the unique values * @@ -803,14 +808,13 @@ * Shuffles the array items in a random order * * @return Array shuffled version */ shuffle: function() { - var shuff = this.clone(); + var shuff = this.clone(), j, x, i = shuff.length; - for (var j, x, i = shuff.length; i; - j = Math.random(i-1), x = shuff[--i], shuff[i] = shuff[j], shuff[j] = x); + for (; i; j = Math.random(i-1), x = shuff[--i], shuff[i] = shuff[j], shuff[j] = x); return shuff; }, /** @@ -829,30 +833,34 @@ } }).sort(function(a, b) { return a.value > b.value ? 1 : a.value < b.value ? -1 : 0; }).map('item'); } -}})(Array.prototype)); +}); -$alias(Array.prototype, { +$alias(A_proto, { include: 'includes', all: 'every', any: 'some' }); +})(Array.prototype); + + + /** * The String class extentions * * Credits: * Some of the functionality inspired by * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson * The trim function taken from work of Steven Levithan * - http://blog.stevenlevithan.com/archives/faster-trim-javascript * - * Copyright (C) 2008-2010 Nikolay V. Nemshilov aka St. <nemshilov#gma-il> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ -$ext(String.prototype, { +String.include({ /** * checks if the string is an empty string * * @return boolean check result */ @@ -923,11 +931,11 @@ * evals all the scripts in the string * * @return String self (unchanged version with scripts still in their place) */ evalScripts: function() { - $eval(this.extractScripts()); + this.stripScripts(true); return this; }, /** * converts underscored or dasherized string to a camelized one @@ -1025,31 +1033,25 @@ * * Credits: * Some of the functionality inspired by * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson * - * Copyright (C) 2008 Nikolay V. Nemshilov aka St. <nemshilov#gma-il> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ -$ext(Function.prototype, (function() { - // creating a local reference to the method for a faster access - var _A = Array.prototype.slice; - -return { +Function.include({ /** * binds the function to be executed in the given scope * * @param Object scope * @param mixed optional curry (left) argument * .... * @return Function binded function */ bind: function() { - if (arguments.length < 2 && !arguments[0]) return this; - - var slice = _A, args = slice.call(arguments), scope = args.shift(), func = this; + var args = $A(arguments), scope = args.shift(), func = this; return function() { - return func.apply(scope, (args.length != 0 || arguments.length != 0) ? args.concat(slice.call(arguments)) : args); + return func.apply(scope, (args.length || arguments.length) ? args.concat($A(arguments)) : args); }; }, /** * binds the function as an event listener to the given scope object @@ -1058,13 +1060,13 @@ * @param mixed optional curry (left) argument * ....... * @return Function binded function */ bindAsEventListener: function() { - var slice = _A, args = slice.call(arguments), scope = args.shift(), func = this; + var args = $A(arguments), scope = args.shift(), func = this; return function(event) { - return func.apply(scope, [event || window.event].concat(args).concat(slice.call(arguments))); + return func.apply(scope, [event || window.event].concat(args).concat($A(arguments))); }; }, /** * allows you to put some curry in your cookery @@ -1072,24 +1074,24 @@ * @param mixed value to curry * .... * @return Function curried function */ curry: function() { - return this.bind.apply(this, [this].concat(_A.call(arguments))); + return this.bind.apply(this, [this].concat($A(arguments))); }, /** * The right side curry feature * * @param mixed value to curry * .... * @return Function curried function */ rcurry: function() { - var curry = _A.call(arguments), func = this; + var curry = $A(arguments), func = this; return function() { - return func.apply(func, _A.call(arguments).concat(curry)); + return func.apply(func, $A(arguments).concat(curry)); } }, /** * delays the function execution @@ -1098,12 +1100,12 @@ * @param mixed value to curry * ..... * @return Integer timeout marker */ delay: function() { - var args = _A.call(arguments), timeout = args.shift(); - var timer = new Number(window.setTimeout(this.bind.apply(this, [this].concat(args)), timeout)); + var args = $A(arguments), timeout = args.shift(), + timer = new Number(window.setTimeout(this.bind.apply(this, [this].concat(args)), timeout)); timer.cancel = function() { window.clearTimeout(this); }; return timer; }, @@ -1115,12 +1117,12 @@ * @param mixed value to curry * ... * @return Ineger interval marker */ periodical: function() { - var args = _A.call(arguments), timeout = args.shift(); - var timer = new Number(window.setInterval(this.bind.apply(this, [this].concat(args)), timeout)); + var args = $A(arguments), timeout = args.shift(), + timer = new Number(window.setInterval(this.bind.apply(this, [this].concat(args)), timeout)); timer.stop = function() { window.clearInterval(this); }; return timer; }, @@ -1132,29 +1134,29 @@ * @param mixed optional value to curry * ...... * @return Function chained function */ chain: function() { - var args = _A.call(arguments), func = args.shift(), current = this; + var args = $A(arguments), func = args.shift(), current = this; return function() { var result = current.apply(current, arguments); func.apply(func, args); return result; }; } -}})()); +}); /** * The Number class extentions * * Credits: * Some methods inspired by * - Ruby (http://www.ruby-lang.org) Copyright (C) Yukihiro Matsumoto * - * Copyright (C) 2008 Nikolay V. Nemshilov aka St. <nemshilov#gma-il> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ -$ext(Number.prototype, { +Number.include({ /** * executes the given callback the given number of times * * @param Function callback * @param Object optional callback execution scope @@ -1205,48 +1207,48 @@ * * Credits: * Inspired by * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson * - * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-il> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ -$ext(RegExp, { - /** - * Escapes the string for safely use as a regular expression - * - * @param String raw string - * @return String escaped string - */ - escape: function(string) { - return String(string).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); - } -}); + + /** + * Escapes the string for safely use as a regular expression + * + * @param String raw string + * @return String escaped string + */ +RegExp.escape = function(string) { + return (''+string).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); +}; + /** * The basic Class unit * * Credits: * The Class unit is inspired by its implementation in * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti * - Ruby (http://www.ruby-lang.org) Copyright (C) Yukihiro Matsumoto * - * Copyright (C) 2008 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ var Class = function() { var args = $A(arguments), properties = args.pop() || {}, parent = args.pop(); + + // basic class object definition + function klass() { + return this.initialize ? this.initialize.apply(this, arguments) : this; + }; // if only the parent class has been specified if (!args.length && !isHash(properties)) { parent = properties; properties = {}; } - - // basic class object definition - var klass = function() { - return this.initialize ? this.initialize.apply(this, arguments) : this; - }; - + // attaching main class-level methods $ext(klass, Class.Methods).inherit(parent); // catching the injections $w('extend include').each(function(name) { @@ -1273,28 +1275,28 @@ * @return Object hash or null if nothing found */ Class.findSet = function(object, property) { var upcased = property.toUpperCase(), capcased = property.capitalize(), candidates = [object, object.constructor].concat(object.constructor.ancestors), - holder = candidates.first(function(o) { return o[upcased] || o[capcased]}); + holder = candidates.first(function(o) { return o && (o[upcased] || o[capcased]) }); return holder ? holder[upcased] || holder[capcased] : null; }; /** * This module contains the methods by which the Class instances * will be extended. It provides basic and standard way to work * with the classes. * - * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ Class.Methods = (function() { - var commons = $w('selfExtended self_extended selfIncluded self_included'); - var extend = commons.concat($w('prototype parent extend include')); - var include = commons.concat(['constructor']); + var commons = $w('selfExtended self_extended selfIncluded self_included'), + extend = commons.concat($w('prototype parent extend include')), + include = commons.concat(['constructor']); - var clean_module = function(module, what) { + function clean_module(module, what) { return Object.without.apply(Object, [module].concat(what == 'e' ? extend : include)); }; return { /** @@ -1392,26 +1394,25 @@ * * Credits: * The idea of the module is inspired by * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti * - * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ -var Options = { +Options = { /** * assigns the options by merging them with the default ones * * @param Object options * @return Object current instance */ setOptions: function(options) { - var options = this.options = Object.merge(Class.findSet(this, 'options'), options); + var options = this.options = Object.merge(Class.findSet(this, 'options'), options), match, key; // hooking up the observer options if (isFunction(this.on)) { - var match; - for (var key in options) { + for (key in options) { if (match = key.match(/on([A-Z][A-Za-z]+)/)) { this.on(match[1].toLowerCase(), options[key]); delete(options[key]); } } @@ -1442,13 +1443,13 @@ * * Credits: * The naming principle is inspired by * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson * - * Copyright (C) 2008-2010 Nikolay V. Nemshilov aka St. <nemshilov#gma-il> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ -var Observer = new Class({ +Observer = new Class({ include: Options, /** * general constructor * @@ -1494,22 +1495,22 @@ this.$listeners.push(hash); break; default: if (isArray(callback)) { - callback.each(function(params) { - this.observe.apply(this, [event].concat( - isArray(params) ? params : [params] + for (var i=0; i < callback.length; i++) { + this.on.apply(this, [event].concat( + isArray(callback[i]) ? callback[i] : [callback[i]] ).concat(args)); - }, this); + } } } } else { // assuming it's a hash of key-value pairs for (var name in event) { - this.observe.apply(this, [name].concat( + this.on.apply(this, [name].concat( isArray(event[name]) ? event[name] : [event[name]] ).concat(args)); } } @@ -1527,21 +1528,17 @@ * observes(String event, Function callback) * * @retun boolean check result */ observes: function(event, callback) { - if (this.$listeners) { - if (!isString(event)) { callback = event; event = null; } - if (isString(callback)) callback = this[callback]; - - return this.$listeners.some(function(i) { - return (event && callback) ? i.e == event && i.f == callback : - event ? i.e == event : i.f == callback; - }); - } + if (!isString(event)) { callback = event; event = null; } + if (isString(callback)) callback = this[callback]; - return false; + return (this.$listeners || []).some(function(i) { + return (event && callback) ? i.e === event && i.f === callback : + event ? i.e === event : i.f === callback; + }); }, /** * stops observing an event or/and function * @@ -1551,15 +1548,19 @@ * stopObserving(String event, Function callback) * * @return Observer self */ stopObserving: function(event, callback) { - if (this.$listeners) { + if (isHash(event)) { + for (var key in event) { + this.stopObserving(key, event[key]); + } + } else { if (!isString(event)) { callback = event; event = null; } if (isString(callback)) callback = this[callback]; - this.$listeners = this.$listeners.filter(function(i) { + this.$listeners = (this.$listeners || []).filter(function(i) { return (event && callback) ? (i.e !== event || i.f !== callback) : (event ? i.e !== event : i.f !== callback); }, this); } @@ -1637,28 +1638,47 @@ $alias(Observer.prototype, { observe: 'on' }); /** * iterators in-callbacks break exception * - * Copyright (C) 2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-il> + * Copyright (C) 2009-2010 Nikolay V. Nemshilov */ -var Break = new Class(Error, { +Break = new Class(Error, { message: "Manual iterator break" }); /** + * this object will contain info about the current browser + * + * Copyright (C) 2008-2010 Nikolay V. Nemshilov + */ +Browser = (function(agent) { + return { + IE: !!(window.attachEvent && !window.opera), + Opera: !!window.opera, + WebKit: agent.indexOf('AppleWebKit/') > -1, + Gecko: agent.indexOf('Gecko') > -1 && agent.indexOf('KHTML') < 0, + MobileSafari: !!agent.match(/Apple.*Mobile.*Safari/), + Konqueror: agent.indexOf('Konqueror') > -1, + + // marker for the browsers which don't give access to the HTMLElement unit + OLD: !!(window.attachEvent && !window.opera) && !document.querySelector + } +})(navigator.userAgent); + +/** * represents some additional functionality for the Event class * * NOTE: there more additional functionality for the Event class in the rightjs-goods project * * Credits: * The additional method names are inspired by * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson * - * Copyright (C) 2008-2010 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ -var Event = new Class(Event, { +Event = new Class(window.Event, { extend: { /** * extends a native object with additional functionality * * @param Event event @@ -1666,29 +1686,29 @@ * @return Event same event but extended */ ext: function(event, bound_element) { if (!event.stop) { $ext(event, this.Methods, true); + } + + if (!event.target && event.srcElement) { + // faking the which button + event.which = event.button == 2 ? 3 : event.button == 4 ? 2 : 1; - if (Browser.IE) { - // faking the which button - event.which = event.button == 2 ? 3 : event.button == 4 ? 2 : 1; - - // faking the mouse position - var scrolls = window.scrolls(); + // faking the mouse position + var scrolls = window.scrolls(); - event.pageX = event.clientX + scrolls.x; - event.pageY = event.clientY + scrolls.y; - - // faking the target property - event.target = event.srcElement || bound_element; - - // faking the relatedTarget, currentTarget and other targets - event.relatedTarget = event[(event.target == event.fromElement ? 'to' : 'from') + 'Element']; - event.currentTarget = bound_element; - event.eventPhase = 3; // bubbling phase - } + event.pageX = event.clientX + scrolls.x; + event.pageY = event.clientY + scrolls.y; + + // faking the target property + event.target = $(event.srcElement) || bound_element; + + // faking the relatedTarget, currentTarget and other targets + event.relatedTarget = event.target === event.fromElement ? $(event.toElement) : event.target; + event.currentTarget = bound_element; + event.eventPhase = 3; // bubbling phase } // Safari bug fix if (event.target && event.target.nodeType == 3) event.target = event.target.parentNode; @@ -1742,11 +1762,11 @@ * Registers some additional event extendsions * * @param Object methods * @return void */ -Event.addMethods = Event.include = function(methods) { +Event.include = function(methods) { $ext(this.Methods, methods); try { // extending the events prototype $ext(Event.parent.prototype, methods, true); } catch(e) {}; @@ -1777,116 +1797,259 @@ /** * custom events unit, used as a mediator for the artificial events handling in the generic observer * - * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ Event.Custom = function(name, options) { this.type = name; this.stop = function() {}; $ext(this, options || {}); }; /** + * This module contains the basic events-delegation feature support + * + * Copyright (C) 2010 Nikolay V. Nemshilov + */ +Event.extend({ + /** + * Creates an event delegation handler + * + * USAGE: + * + * var delegation = Event.delegate({ + * "css_rule_1": function() { do_something_usual(); }, + * "css_rule_2": function() { do_something_another(); }, + * + * // us also can use references by name with or without options + * "css_rule_3": ['addClass', 'that-class'], + * "css_rule_4": 'hide' + * }); + * + * $(element).on('click', delegation); + * + * NOTE: + * your delegation handler will be called in contexts of matching _targets_ + * not in the context of the element where it was attached + * + * @param Object delegation rules + * @return Function delegation handler + */ + delegate: function(options) { + return function(event) { + var target = event.target, css_rule, args, callback; + + for (css_rule in options) { + if ($(this).select(css_rule).include(target)) { + args = options[css_rule]; + args = isArray(args) ? args : [args]; + callback = args[0]; + args = args.slice(1); + + if (isString(callback)) + target[callback].apply(target, args); + else + callback.apply(target, [event].concat(args)); + } + } + }; + }, + + /** + * Creates a document-level events delegations catcher + * + * USAGE: + * Event.behave("ul#main-menu li", "click", function() { alert('clicked'); }); + * Event.behave("ul#main-menu li", "mouseover", "addClass", "hovered"); + * Event.behave("ul#main-menu li", { + * click: function() { alert('clicked'); }, + * mouseover: ['addClass', 'hovered'], + * mouseout: ['removeClass', 'hovered'], + * dblclick: 'hide' + * }); + * + * @param String css-rule + * @param mixed String event name or a Hash of events + * @param mixed Function callback or String method name + * @param mixed optional curried arguments + * @return Object with event handlers description the document.on() function will receive + */ + behave: function(css_rule, options) { + var events = {}, hash = {}, args = $A(arguments).slice(1), + focus = 'focus', blur = 'blur', focus_blur = [focus, blur]; + + if (isString(options)) { + hash[args.shift()] = args; + options = hash; + } + + for (var event in options) { + var hash = {}; hash[css_rule] = options[event]; + + if (Browser.IE) { + // fancy IE browsers have different names for bubbling versions of those events + if (event == focus) event = focus + 'in'; + if (event == blur) event = focus + 'out'; + } + + events[event] = Event.delegate(hash); + + if (focus_blur.include(event) && !Browser.IE) { + // HACK! HACK! HACK! + // by default, method #on uses a non-captive events attachment + // but for focus and blur effects we need the opposite + // so we calling the method directly and pushing the listeners manually + + document.addEventListener(event, events[event], true); + + (document.$listeners = document.$listeners || []).push({ + e: event, f: events[event], a: [] + }); + + } else { + document.on(event, events[event]); + } + } + + return events; + } +}); + + +String.include({ + /** + * A shortcut for document-level events delegation handler attaching + * + * USAGE: + * + * "ul#main-menu li".on("click", function() { alert('clicked'); }); + * "ul#main-menu li".on("mouseover", "addClass", "hovered"); + * "ul#main-menu li".on("mouseout", "removeClass", "hovered"); + * + * // or like that in a shash + * "ul#main-menu li".on({ + * click: function() { alert('clicked'); }, + * mouseover: ['addClass', 'hovered'], + * mouseout: ['removeClass', 'hovered'], + * dblclick: 'hide' + * }); + * + * ... + * @return String this + */ + on: function() { + Event.behave.apply(Event, [''+this].concat($A(arguments))); + return this; + } +}); + +$alias(String.prototype, {behave: 'on'}); + + +/** * The DOM Element unit handling * - * Copyright (C) 2008-2010 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ -self.Element = (function(old_Element) { +Element = (function(old_Element) { - var new_Element = function(tag, options) { - var element = document.createElement(tag), options = options || {}; + // Element constructor options mapper + var options_map = { + id: ['id', 0], + html: ['innerHTML', 0], + 'class': ['className', 0], + style: ['setStyle', 1], + observe: ['on', 1], + on: ['on', 1] + }; + + function new_Element(tag, options) { + var element = document.createElement(tag); - if (options.id) { element.id = options.id; delete(options.id); } - if (options.html) { element.innerHTML = options.html; delete(options.html); } - if (options['class']) { element.className = options['class']; delete(options['class']); } - if (options.style) { element.setStyle(options.style); delete(options.style); } - if (options.observe) { element.observe(options.observe); delete(options.observe); } + if (options) { + for (var key in options) { + if (options_map[key]) { + if (options_map[key][1]) element[options_map[key][0]](options[key]); + else element[options_map[key][0]] = options[key]; + } else { + element.set(key, options[key]); + } + } + } - for (var key in options) // a filter in case there is no keys in the options left - return element.set(options); return element; }; if (Browser.IE) { // // IE browsers have a bug with checked input elements // and we kinda hacking the Element constructor so that // it affected IE browsers only // - new_Element = eval('({f:'+new_Element.toString().replace(/(\((\w+), (\w+)\) \{)/, - '$1if($2=="input"&&$3&&$3.checked)$2="<input checked=true/>";' - )+'})').f; + new_Element = eval('['+new_Element.toString().replace(/(\((\w+),\s*(\w+)\)\s*\{)/, + '$1if($2==="input"&&$3)$2="<input name="+$3.name+" type="+$3.type+($3.checked?" checked":"")+"/>";' + )+']')[0]; } // connecting the old Element instance to the new one for IE browsers if (old_Element) { $ext(new_Element, old_Element); new_Element.parent = old_Element; } return new_Element; -})(self.Element); +})(window.Element); $ext(Element, { /** * registeres the methods on the custom element methods list - * will add them to prototype and will generate a non extensive static mirror + * will add them to prototype and register at the Element.Methods hash * * USAGE: * Element.include({ * foo: function(bar) {} * }); * * $(element).foo(bar); - * Element.foo(element, bar); * * @param Object new methods list * @param Boolean flag if the method should keep the existing methods alive * @return Element the global Element object */ include: function(methods, dont_overwrite) { $ext(this.Methods, methods, dont_overwrite); try { // busting up the basic element prototypes - $ext(HTMLElement.prototype, methods, dont_overwrite); - } catch(e) { - try { // IE8 native element extension - $ext(this.parent.prototype, methods, dont_overwrite); - } catch(e) {} - } + $ext((window.HTMLElement || this.parent).prototype, methods, dont_overwrite); + } catch(e) {} return this; }, Methods: {} // DO NOT Extend this object manually unless you really need it, use Element#include }); -// the old interface alias, NOTE will be nuked -Element.addMethods = Element.include; - /** * The DOM Element unit structures handling module * * NOTE: all the methods will process and return only the Element nodes * all the textual nodes will be skipped * * NOTE: if a css-rule was specified then the result of the method * will be filtered/adjusted depends on the rule * - * the css-rule might be a string or a Selector instance - * * Credits: * The naming principle and most of the names are taken from * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson * The insertions system implementation is inspired by * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti * - * Copyright (C) 2008-2010 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ Element.include({ parent: function(css_rule) { return css_rule ? this.parents(css_rule)[0] : $(this.parentNode); }, @@ -1894,13 +2057,13 @@ parents: function(css_rule) { return this.rCollect('parentNode', css_rule); }, subNodes: function(css_rule) { - var first_child = $(this.firstChild); - return first_child ? (first_child.tagName && (!css_rule || first_child.match(css_rule)) ? [first_child] : [] - ).concat(this.rCollect.call(first_child, 'nextSibling', css_rule)) : []; + return this.select(css_rule).filter(function(element) { + return element.parentNode === this; + }, this); }, siblings: function(css_rule) { return this.prevSiblings(css_rule).reverse().concat(this.nextSiblings(css_rule)); }, @@ -1998,11 +2161,11 @@ * * @param mixed content (a String, an Element or a list of elements) * @return Element self */ update: function(content) { - if (isString(content) || isNumber(content)) { + if ((isString(content) || isNumber(content)) && !Element.insertions.wraps[this.tagName]) { var scripts; this.innerHTML = (''+content).stripScripts(function(s) { scripts = s; }); if (scripts) $eval(scripts); } else { this.clean().insert(content); @@ -2153,11 +2316,11 @@ * Some of the functionality is inspired by * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti * - Dojo (www.dojotoolkit.org) Copyright (C) The Dojo Foundation * - * Copyright (C) 2008-2010 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ Element.include({ /** * assigns styles out of the hash to the element * @@ -2166,31 +2329,32 @@ * @param Object styles list or String style name * @param String style value in case of the first param a string style name * @return Element self */ setStyle: function(hash, value) { - if (value) { var style = {}; style[hash] = value; hash = style; } + var key, c_key, style = {}; + + if (value) { style[hash] = value; hash = style; } else if(isString(hash)) { - var style = {}; hash.split(';').each(function(option) { var els = option.split(':').map('trim'); if (els[0] && els[1]) { style[els[0]] = els[1]; } }); hash = style; } - var c_key; - for (var key in hash) { + + for (key in hash) { c_key = key.indexOf('-') != -1 ? key.camelize() : key; if (key === 'opacity') { if (Browser.IE) { - this.style.filter = 'alpha(opacity='+ value * 100 +')'; + this.style.filter = 'alpha(opacity='+ hash[key] * 100 +')'; } else { - this.style.opacity = value; + this.style.opacity = hash[key]; } } else if (key === 'float') { c_key = Browser.IE ? 'styleFloat' : 'cssFloat'; } @@ -2321,11 +2485,11 @@ * * Credits: * Most of the naming system in the module inspired by * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson * - * Copyright (C) 2008-2010 Nikolay V. Nemshilov aka St. <nemshilov#gma-il> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ Element.include({ /** * sets the element attributes * @@ -2336,11 +2500,11 @@ set: function(hash, value) { if (typeof(hash) === 'string') { var val = {}; val[hash] = value; hash = val; } for (var key in hash) { // some attributes are not available as properties - if (this[key] === undefined) { + if (typeof(this[key]) === 'undefined') { this.setAttribute(key, ''+hash[key]); } this[key] = hash[key]; } @@ -2454,11 +2618,11 @@ /** * this module contains the Element's part of functionality * responsible for the dimensions and positions getting/setting * - * Copyright (C) 2008-2010 Nikolay V. Nemshilov aka St. <nemshilov#gma-il> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ Element.include({ /** * Returns the element sizes as a hash * @@ -2602,22 +2766,23 @@ }, /** * makes the window be scrolled to the element * + * @param Object fx options * @return Element self */ - scrollThere: function() { - window.scrollTo(this); + scrollThere: function(options) { + window.scrollTo(this, options); return this; } }); /** * DOM Element events handling methods * - * Copyright (C) 2008-2010 Nikolay V. Nemshilov aka St. <nemshilov#gma-il> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ Element.include((function() { var observer = Observer.create({}, $w('click rightclick contextmenu mousedown mouseup mouseover mouseout mousemove keypress keydown keyup') ); @@ -2626,42 +2791,39 @@ // HACK HACK HACK // // I'm kinda patching the observer methods manually in here // the reason is in building flat and fast functions // - observer.observe = observer.on = eval('({f:'+ - observer.observe.toString().replace(/(\$listeners\.push\((\w+?)\);)/, '$1'+ - '$2.e=Event.cleanName($2.e);$2.n=Event.realName($2.e);'+ - - '$2.w=function(){var a=$A(arguments),e=($2.r&&$2.r!=="stopEvent")?a.shift():Event.ext(a[0],this);'+ - 'return $2.f.apply(this,a.concat($2.a))};'+( - - self.attachEvent ? - '$2.w=$2.w.bind(this);this.attachEvent("on"+$2.n,$2.w);' : - 'this.addEventListener($2.n,$2.w,false);' - ) - )+ - '})').f; + function hack(name, re, text) { + observer[name] = eval('['+ observer[name].toString().replace(re, text) +']')[0]; + }; - observer.stopObserving = eval('({f:'+ - observer.stopObserving.toString().replace(/(function\s*\((\w+)\)\s*\{\s*)(return\s*)([^}]+)/m, - '$1var r=$4;'+ - 'if(!r)' + (self.attachEvent ? - 'this.detachEvent("on"+$2.n,$2.w);' : - 'this.removeEventListener($2.n,$2.w,false);' - )+'$3 r')+ - '})').f; + hack('on', + /(\$listeners\.push\((\w+?)\);)/, + + '$1$2.e=Event.cleanName($2.e);$2.n=Event.realName($2.e);$2.w=function(){var a=$A(arguments),e=($2.r&&$2.r!=="stopEvent")?a.shift():Event.ext(a[0],this);return $2.f.apply(this,a.concat($2.a))};' + ( + window.attachEvent ? + '$2.w=$2.w.bind(this);this.attachEvent("on"+$2.n,$2.w);' : + 'this.addEventListener($2.n,$2.w,false);' + ) + ); + observer.observe = observer.on; + hack('stopObserving', + /(function\s*\((\w+)\)\s*\{\s*)(return\s*)([^}]+)/m, + '$1var r=$4;if(!r)' + (window.attachEvent ? + 'this.detachEvent("on"+$2.n,$2.w);' : + 'this.removeEventListener($2.n,$2.w,false);' + )+'$3 r' + ); - observer.fire = eval('({f:'+ - observer.fire.toString().replace(/(\w+)\.f\.apply.*?\.concat\((\w+)\)[^}]/, - '$1.f.apply(this,[new Event($1.e,$2.shift())].concat($1.a).concat($2))' - )+ - '})').f; + hack('fire', + /(\w+)\.f\.apply.*?\.concat\((\w+)\)\)/, + '$1.f.apply(this,(($1.r&&$1.r!=="stopEvent")?[]:[new Event($1.e,$2.shift())]).concat($1.a).concat($2))' + ); - // a simple events terminator method to be hooked like - // this.onClick('stopEvent'); + // a simple events terminator method to be hooked like this.onClick('stopEvent'); observer.stopEvent = function(e) { e.stop(); }; $ext(window, observer); $ext(document, observer); @@ -2675,19 +2837,19 @@ * The DOM elements selection handling * * NOTE: this module is just a wrap over the native CSS-selectors feature * see the olds/css.js file for the manual selector code * - * Copyright (C) 2008-2010 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ Element.include((function() { /** * Native css-selectors include the current element into the search context * and as we actually search only inside of the element we add it's tag * as a scope for the search */ - var stub_rule = function(css_rule, tag) { + function stub_rule(css_rule, tag) { return css_rule ? css_rule.replace(/(^|,)/g, '$1'+ tag + ' ') : '*'; }; return { /** @@ -2712,45 +2874,46 @@ }, /** * checks if the element matches this css-rule * + * NOTE: the element should be attached to the page + * * @param String css-rule * @return Boolean check result */ match: function(css_rule) { - if (!css_rule || css_rule == '*') return true; + var result, parent = this.tagName === 'HTML' ? this.ownerDocument : this.parents().last(); - var fake, result, parent, parents = this.parents(); + // if it's a single node putting it into the context + result = $(parent || $E('p').insert(this)).select(css_rule).include(this); - parent = parents.length ? parents.last() : fake = $E('div').insert(this); - result = parent.select(css_rule).include(this); + if (!parent) this.remove(); - if (fake) { this.remove(); } - return result; } }})()); // document-level hooks $ext(document, { first: function(css_rule) { - return this.querySelector(css_rule || '*'); + return this.querySelector(css_rule); }, select: function(css_rule) { - return $A(this.querySelectorAll(css_rule || '*')); + return $A(this.querySelectorAll(css_rule)); } }); + /** * the window object extensions * - * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-il> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ -$ext(self, (function() { - var old_scroll = window.scrollTo; +$ext(window, (function() { + var native_scroll = window.scrollTo; return { /** * returns the inner-sizes of the window * @@ -2780,23 +2943,31 @@ /** * overloading the native scrollTo method to support hashes and element references * * @param mixed number left position, a hash position, element or a string element id * @param number top position + * @param Object fx options * @return window self */ - scrollTo: function(left, top) { + scrollTo: function(left, top, fx_options) { + var left_pos = left, top_pos = top; // moving the values into new vars so they didn't get screwed later on + if(isElement(left) || (isString(left) && $(left))) { left = $(left).position(); } if (isHash(left)) { - top = left.y; - left = left.x; + top_pos = left.y; + left_pos = left.x; } - old_scroll(left, top); + // checking if a smooth scroll was requested + if (isHash(fx_options = fx_options || top) && window.Fx) { + new Fx.Scroll(this, fx_options).start({x: left_pos, y: top_pos}); + } else { + native_scroll(left_pos, top_pos); + } return this; } }; @@ -2807,18 +2978,18 @@ * * Credits: * The basic principles of the module are originated from * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti * - * Copyright (C) 2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> + * Copyright (C) 2009-2010 Nikolay V. Nemshilov */ [window, document].each(function(object) { Observer.createShortcuts(object, ['ready']); var ready = object.fire.bind(object, 'ready'); // IE and Konqueror browsers - if (document.readyState !== undefined) { + if (typeof(document.readyState) !== 'undefined') { (function() { ['loaded','complete'].includes(document.readyState) ? ready() : arguments.callee.delay(50); })(); } else { document.addEventListener('DOMContentLoaded', ready, false); @@ -2831,13 +3002,13 @@ * * Credits: * The basic principles of the module are inspired by * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson * - * Copyright (C) 2009-2010 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> + * Copyright (C) 2009-2010 Nikolay V. Nemshilov */ -var Form = function(options) { +function Form(options) { var options = options || {}, remote = options.remote, form = new Element('form', Object.without(options, 'remote')); if (remote) form.remotize(); @@ -2870,19 +3041,18 @@ $ext(HTMLFormElement.prototype, methods, dont_overwrite); } catch(e) {} } }); -Form.addMethods = Form.include; Form.include({ /** * returns the form elements as an array of extended units * * @return Array of elements */ getElements: function() { - return this.select('input,select,textarea,button'); + return $A(this.elements).map($); }, /** * returns the list of all the input elements on the form * @@ -2971,11 +3141,11 @@ * * Credits: * The basic ideas are taken from * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson * - * Copyright (C) 2009-2010 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> + * Copyright (C) 2009-2010 Nikolay V. Nemshilov */ (function() { // trying to get the input element classes list try { var input_classes = [HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement, HTMLButtonElement]; } catch(e) { var input_classes = []; } @@ -3022,11 +3192,11 @@ _focus: 'focus', _select: 'select' }); }); })(); -Form.Element.addMethods = Form.Element.include; + Form.Element.include({ /** * uniform access to the element values * * @return String element value @@ -3047,11 +3217,11 @@ * @param String value * @return Element this */ setValue: function(value) { if (this.type == 'select-multiple') { - value = (isArray(value) ? value : [value]).map(String); + value = $A(isArray(value) ? value : [value]).map(String); $A(this.getElementsByTagName('option')).each(function(option) { option.selected = value.includes(option.value); }); } else { this.value = value; @@ -3126,13 +3296,13 @@ * * Credits: * Most things in the unit are take from * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti * - * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-il> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ -var Cookie = new Class({ +Cookie = new Class({ include: Options, extend: { // sets the cookie set: function(name, value, options) { @@ -3219,13 +3389,13 @@ * Some of the functionality inspired by * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti * - jQuery (http://jquery.com) Copyright (C) John Resig * - * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-il> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ -var Xhr = new Class(Observer, { +Xhr = new Class(Observer, { extend: { // supported events list EVENTS: $w('success failure complete request cancel create'), // default options @@ -3316,11 +3486,11 @@ * * @param Object options * @return Xhr self */ send: function(params) { - var add_params = {}, url = this.url, method = this.method.toLowerCase(); + var add_params = {}, url = this.url, method = this.method.toLowerCase(), key; if (method == 'put' || method == 'delete') { add_params['_method'] = method; method = 'post'; } @@ -3330,22 +3500,22 @@ if (this.urlEncoded && method == 'post' && !this.headers['Content-type']) { this.setHeader('Content-type', 'application/x-www-form-urlencoded;charset='+this.encoding); } if (method == 'get') { - url += (url.includes('?') ? '&' : '?') + data; + if (data) url += (url.includes('?') ? '&' : '?') + data; data = null; } this.xhr = this.createXhr(); this.fire('create'); this.xhr.open(method, url, this.async); this.xhr.onreadystatechange = this.stateChanged.bind(this); - for (var key in this.headers) { + for (key in this.headers) { this.xhr.setRequestHeader(key, this.headers[key]); } this.xhr.send(data); this.fire('request'); @@ -3446,11 +3616,11 @@ sanitizedJSON: function() { try { return JSON.parse(this.text); } catch(e) { // manual json consistancy check - if (self.JSON || !(/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(this.text.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''))) { + if (window.JSON || !(/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(this.text.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''))) { if (this.secureJSON) { throw "JSON parse error: "+this.text; } else { return null; } @@ -3522,11 +3692,11 @@ * Credits: * Some of the functionality inspired by * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson * - jQuery (http://jquery.com) Copyright (C) John Resig * - * Copyright (C) 2009-2010 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> + * Copyright (C) 2009-2010 Nikolay V. Nemshilov */ Form.include({ /** * sends the form via xhr request * @@ -3578,11 +3748,11 @@ * this module contains the Element unit XHR related extensions * * Credits: * - jQuery (http://jquery.com) Copyright (C) John Resig * - * Copyright (C) 2008-2010 Nikolay V. Nemshilov aka St. <nemshilov#gma-il> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ Element.include({ /** * performs an Xhr request to the given url * and updates the element internals with the responseText @@ -3599,11 +3769,11 @@ /** * This unit presents a fake drop in replacement for the XmlHTTPRequest unit * but works with an iframe targeting in the background * - * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-il> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ Xhr.IFramed = new Class({ /** * constructor * @@ -3636,12 +3806,13 @@ onLoad: function() { this.status = 200; this.readyState = 4; - var doc = window[this.iframe.id].document.documentElement; - this.responseText = doc ? doc.innerHTML : null; + try { + this.responseText = window[this.iframe.id].document.documentElement.innerHTML; + } catch(e) { } this.onreadystatechange(); }, // dummy API methods @@ -3656,13 +3827,13 @@ * * Credits: * The basic principles, structures and naming system are inspired by * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti * - * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ -var Fx = new Class(Observer, { +Fx = new Class(Observer, { extend: { EVENTS: $w('start finish cancel'), // named durations Durations: { @@ -3698,21 +3869,29 @@ }, Lin: function(i) { return i; } - } + }, + + ch: [], // scheduled effects registries + cr: [] // currently running effects registries }, /** * Basic constructor * * @param Object options */ initialize: function(element, options) { this.$super(options); - this.element = $(element); + + if (this.element = element = $(element)) { + var uid = $uid(element); + this.ch = (Fx.ch[uid] = Fx.ch[uid] || []); + this.cr = (Fx.cr[uid] = Fx.cr[uid] || []); + } }, /** * starts the transition * @@ -3727,29 +3906,36 @@ this.transition = Fx.Transitions[options.transition] || options.transition; this.steps = (duration / 1000 * this.options.fps).ceil(); this.number = 1; + if (this.cr) this.cr.push(this); // adding this effect to the list of currently active + return this.fire('start', this).startTimer(); }, /** * finishes the transition * * @return Fx this */ finish: function() { - return this.stopTimer().fire('finish').next(); + return this.stopTimer().unreg().fire('finish').next(); }, /** * interrupts the transition * + * NOTE: + * this method cancels all the scheduled effects + * in the element chain + * * @return Fx this */ cancel: function() { - return this.stopTimer().fire('cancel').next(); + this.ch.clean(); + return this.stopTimer().unreg().fire('cancel'); }, /** * pauses the transition * @@ -3786,59 +3972,62 @@ that.w = false; } that.number ++; } }, - + + // starts the effect timer startTimer: function() { this.timer = this.step.periodical((1000 / this.options.fps).round(), this); return this; }, + // stops the effect timer stopTimer: function() { if (this.timer) { this.timer.stop(); } return this; }, // handles effects queing // should return false if there's no queue and true if there is a queue queue: function(args) { - if (!this.element) return false; - if (this.$ch) return this.$ch = false; + var chain = this.ch, queue = this.options.queue; + + if (!chain || this.$ch) + return this.$ch = false; - var uid = $uid(this.element), chain; - Fx.$ch = Fx.$ch || []; - chain = (Fx.$ch[uid] = Fx.$ch[uid] || []); - - if (this.options.queue) + if (queue) chain.push([args, this]); - this.next = function() { - var next = chain.shift(); next = chain[0]; - if (next) { - next[1].$ch = true; - next[1].start.apply(next[1], next[0]); - } - return this; - }; - - return this.options.queue && chain[0][1] !== this; + return queue && chain[0][1] !== this; }, + // calls for the next effect in the queue next: function() { + var chain = this.ch, next = chain.shift(), next = chain[0]; + if (next) { + next[1].$ch = true; + next[1].start.apply(next[1], next[0]); + } return this; + }, + + // unregisters this effect out of the currently running list + unreg: function() { + var currents = this.cr; + if (currents) currents.splice(currents.indexOf(this), 1); + return this; } - }); /** * There are the String unit extensions for the effects library * - * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-il> + * Copyright (C) 2008-2009 Nikolay V. Nemshilov */ String.COLORS = { maroon: '#800000', red: '#ff0000', orange: '#ffA500', @@ -3857,11 +4046,11 @@ silver: '#c0c0c0', gray: '#808080', brown: '#a52a2a' }; -$ext(String.prototype, { +String.include({ /** * converts a #XXX or rgb(X, X, X) sring into standard #XXXXXX color string * * @return String hex color */ @@ -3905,27 +4094,27 @@ * * Credits: * The idea is inspired by the Morph effect from * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti * - * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ Fx.Morph = new Class(Fx, (function() { // a list of common style names to compact the code a bit var Color = 'Color', Style = 'Style', Width = 'Width', Bg = 'background', Border = 'border', Pos = 'Position', BgColor = Bg + Color, directions = $w('Top Left Right Bottom'); // adds variants to the style names list - var add_variants = function(keys, key, variants) { + function add_variants(keys, key, variants) { for (var i=0; i < variants.length; i++) keys.push(key + variants[i]); }; // adjusts the border-styles - var check_border_styles = function(before, after) { + function check_border_styles(before, after) { for (var i=0; i < 4; i++) { var direction = directions[i], bd_style = Border + direction + Style, bd_width = Border + direction + Width, bd_color = Border + direction + Color; @@ -3944,21 +4133,23 @@ } } }; // parses the style hash into a processable format - var parse_style = function(values) { - var result = {}, re = /[\d\.\-]+/g, m; + function parse_style(values) { + var result = {}, re = /[\d\.\-]+/g, m, key, value, i; - for (var key in values) { + for (key in values) { m = values[key].match(re); - var value = m.map('toFloat'); + value = m.map('toFloat'); value.t = values[key].split(re); value.r = value.t[0] === 'rgb('; - if (value.t[0] === '' || value.r) value.t.shift(); - for (var i=0; i < value.length; i++) { - value.t.splice(i*2, 0, value[i]); + + if (value.t.length == 1) value.t.unshift(''); + + for (i=0; i < value.length; i++) { + value.t.splice(i*2 + 1, 0, value[i]); } result[key] = value; } return result; @@ -3979,23 +4170,22 @@ this.before = parse_style(before); this.after = parse_style(after); }, render: function(delta) { - var before, after, value, style = this.element.style; - for (var key in this.after) { + var before, after, value, style = this.element.style, key, i; + for (key in this.after) { before = this.before[key]; after = this.after[key]; - for (var i=0; i < after.length; i++) { + for (i=0; i < after.length; i++) { value = before[i] + (after[i] - before[i]) * delta; if (after.r) value = Math.round(value); - after.t[i*2] = value; + after.t[i*2 + 1] = value; } - value = after.t.join(''); - if (after.r) value = 'rgb('+value; - style[key] = value; + + style[key] = after.t.join(''); } }, /** * Returns a hash of the end style @@ -4006,13 +4196,13 @@ _endStyle: function(style, keys) { var dummy = $(this.element.cloneNode(true)) .setStyle('position:absolute;z-index:-1;visibility:hidden') .insertTo(this.element, 'before') .setWidth(this.element.sizes().x) - .setStyle(style); + .setStyle(style), - var after = this._cloneStyle(dummy, keys); + after = this._cloneStyle(dummy, keys); dummy.remove(); return after; }, @@ -4036,16 +4226,16 @@ * * @param Object the style hash * @return Array of clean style keys list */ _styleKeys: function(style) { - var keys = [], border_types = [Style, Color, Width]; + var keys = [], border_types = [Style, Color, Width], key, i, j; - for (var key in style) { + for (key in style) { if (key.startsWith(Border)) - for (var i=0; i < border_types.length; i++) - for (var j=0; j < directions.length; j++) + for (i=0; i < border_types.length; i++) + for (j=0; j < directions.length; j++) keys.push(Border + directions[j] + border_types[i]); else if (key == 'margin' || key == 'padding') add_variants(keys, key, directions); else if (key.startsWith(Bg)) add_variants(keys, Bg, [Color, Pos, Pos+'X', Pos+'Y']); @@ -4064,13 +4254,13 @@ * @param Object before * @param Object after * @return void */ _cleanStyles: function(before, after) { - var remove = []; + var remove = [], key; - for (var key in after) { + for (key in after) { // checking the height/width options if ((key == 'width' || key == 'height') && before[key] == 'auto') { before[key] = this.element['offset'+key.capitalize()] + 'px'; } } @@ -4080,11 +4270,11 @@ // adjusting the border style check_border_styles.call(this, before, after); // cleaing up the list - for (var key in after) { + for (key in after) { // proprocessing colors if (after[key] !== before[key] && !remove.includes(key) && /color/i.test(key)) { if (Browser.Opera) { after[key] = after[key].replace(/"/g, ''); before[key] = before[key].replace(/"/g, ''); @@ -4126,11 +4316,11 @@ /** * the elements hightlighting effect * - * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ Fx.Highlight = new Class(Fx.Morph, { extend: { Options: Object.merge(Fx.Options, { color: '#FF8', @@ -4162,11 +4352,11 @@ }); /** * this is a superclass for the bidirectional effects * - * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ Fx.Twin = new Class(Fx.Morph, { /** * hides the element if it meant to be switched off @@ -4197,11 +4387,11 @@ }); /** * the slide effects wrapper * - * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ Fx.Slide = new Class(Fx.Twin, { extend: { Options: Object.merge(Fx.Options, { direction: 'top' @@ -4273,11 +4463,11 @@ }); /** * The opacity effects wrapper * - * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ Fx.Fade = new Class(Fx.Twin, { prepare: function(how) { this.setHow(how); @@ -4289,24 +4479,30 @@ }); /** * A smooth scrolling visual effect * - * Copyright (C) 2009 Nikolay V. Nemshilov aka St. + * Copyright (C) 2009-2010 Nikolay V. Nemshilov */ Fx.Scroll = new Class(Fx, { + + initialize: function(element, options) { + // swapping the actual scrollable when it's the window + this.$super(element.prompt ? element.document[Browser.WebKit ? 'body' : 'documentElement'] : element, options); + }, + prepare: function(value) { this.before = {}; this.after = value; if (defined(value.x)) this.before.x = this.element.scrollLeft; if (defined(value.y)) this.before.y = this.element.scrollTop; }, render: function(delta) { - var before = this.before; - for (var key in before) { + var before = this.before, key; + for (key in before) { this.element['scroll' + (key == 'x' ? 'Left' : 'Top')] = before[key] + (this.after[key] - before[key]) * delta; } } }); @@ -4315,34 +4511,45 @@ * * Credits: * Some ideas are inspired by * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti * - * Copyright (C) 2008-2010 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> + * Copyright (C) 2008-2010 Nikolay V. Nemshilov */ Element.include((function(methods) { var old_hide = methods.hide, old_show = methods.show, old_scroll = methods.scrollTo; return { + /** + * Stops all the visual effects on the element + * + * @return Element this + */ + stop: function() { + (Fx.cr[$uid(this)] || []).each('cancel'); + return this; + }, /** * hides the element with given visual effect * * @param String fx name * @param Object fx options + * @return Element this */ hide: function(fx, options) { return fx ? this.fx(fx, ['out', options]) : old_hide.call(this); }, /** * shows the element with the given visual effect * * @param String fx name * @param Object fx options + * @return Element this */ show: function(fx, options) { return fx ? this.fx(fx, ['in', options]) : old_show.call(this); }, @@ -4435,16 +4642,16 @@ * * Basically it just checks all the script tags on the page * finds the core inclusion tag and uses it's src attribute * to dynamically load the olds patch * - * Copyright (C) 2009 Nikolay V. Nemshilov aka St. + * Copyright (C) 2009-2010 Nikolay V. Nemshilov */ if (!document.querySelector) { (function() { - var rigth_src_re = /(\/right)([^\/]+)$/; - var core_src = $A(document.getElementsByTagName('script')).map('src').compact().first('match', rigth_src_re); + var rigth_src_re = /(^|\/)(right)([^\/]+)$/, + core_src = $A(document.getElementsByTagName('script')).map('src').compact().first('match', rigth_src_re); if (core_src) - document.write('<scr'+'ipt src="'+core_src.replace(rigth_src_re, '$1-olds$2')+'"></scr'+'ipt>'); + document.write('<scr'+'ipt src="'+core_src.replace(rigth_src_re, '$1$2-olds$3')+'"></scr'+'ipt>'); })(); } \ No newline at end of file