javascripts/right-src.js in right-rails-0.3.2 vs javascripts/right-src.js in right-rails-0.4.0

- old
+ new

@@ -2,20 +2,21 @@ * RightJS - the right javascript framework * * The library released under terms of the MIT license * Visit http://rightjs.org for more details * + * Custom build with options: no-olds + * * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. */ - /** * The framework description object * * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> */ var RightJS = { - version: "1.4.3", + version: "1.5.0", modules: ["core", "form", "cookie", "xhr", "fx"] }; /** * this object will contain info about the current browser @@ -154,37 +155,31 @@ * * @param mixed value * @return boolean check result */ function isHash(value) { - return typeof(value) == 'object' && value !== null && value.constructor === Object; + return typeof(value) === 'object' && value !== null && value.constructor === Object; }; -// Konqueror 3 patch -if (navigator.userAgent.indexOf('Konqueror/3') != -1) { - eval(isHash.toString().replace(';', '&&!(arguments[0] instanceof HTMLElement);')); -} - - /** * checks if the given value is a function * * @param mixed value * @return boolean check result */ function isFunction(value) { - return typeof(value) == 'function'; + return typeof(value) === 'function'; }; /** * checks if the given value is a string * * @param mixed value * @return boolean check result */ function isString(value) { - return typeof(value) == 'string'; + return typeof(value) === 'string'; }; /** * checks if the given value is an array * @@ -200,11 +195,11 @@ * * @param mixed value to check * @return boolean check result */ function isNumber(value) { - return typeof(value) == 'number'; + return typeof(value) === 'number'; }; /** * checks if the given value is an element * @@ -232,16 +227,16 @@ * @return Array list */ var $A = (function(slice) { return function (it) { try { - var a = slice.call(it); + return slice.call(it); } catch(e) { for (var a=[], i=0, length = it.length; i < length; i++) a[i] = it[i]; + return a; } - return a; }; })(Array.prototype.slice); /** * shortcut to instance new elements @@ -259,12 +254,11 @@ * * @param String element id or Element to extend * @return Element or null */ function $(element) { - var element = typeof(element) == 'string' ? document.getElementById(element) : element; - return Browser.OLD ? Element.prepare(element) : element; + return typeof(element) === 'string' ? document.getElementById(element) : element; }; /** * searches for elements in the document which matches the given css-rule * @@ -289,17 +283,15 @@ * generates an unique id for an object * * @param Object object * @return Integer uniq id */ -var $uid = (function() { - var _UID = 1; - +var $uid = (function(UID) { return function(item) { - return item.uid || (item.uid = _UID++); + return item.uid || (item.uid = UID++); }; -})(); +})(1); /** * The Object class extentions * @@ -536,11 +528,11 @@ var guess_callback = function(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])) { + if (array.length !== 0 && isFunction(array[0][attr])) { callback = function(object) { return object[attr].apply(object, args); }; } else { callback = function(object) { return object[attr]; }; } } else { @@ -731,14 +723,14 @@ merge: function() { for (var copy = this.clone(), arg, i=0, length = arguments.length; i < length; i++) { arg = arguments[i]; if (isArray(arg)) { for (var j=0; j < arg.length; j++) { - if (copy.indexOf(arg[j]) == -1) + if (copy.indexOf(arg[j]) === -1) copy.push(arg[j]); } - } else if (copy.indexOf(arg) == -1) { + } else if (copy.indexOf(arg) === -1) { copy.push(arg); } } return copy; }, @@ -786,11 +778,11 @@ * .... * @return boolean check result */ includes: function() { for (var i=0, length = arguments.length; i < length; i++) - if (this.indexOf(arguments[i]) == -1) + if (this.indexOf(arguments[i]) === -1) return false; return true; }, /** @@ -924,11 +916,11 @@ * * @return String the extracted stcripts */ extractScripts: function() { var scripts = ''; - this.stripScripts(function(s,t) { scripts = s; }); + this.stripScripts(function(s) { scripts = s; }); return scripts; }, /** * evals all the scripts in the string @@ -978,11 +970,11 @@ * * @param String string * @return boolean check result */ includes: function(string) { - return this.indexOf(string) != -1; + return this.indexOf(string) !== -1; }, /** * checks if the string starts with the given substring * @@ -990,12 +982,12 @@ * @param boolean ignore the letters case * @return boolean check result */ startsWith: function(string, ignorecase) { var start_str = this.substr(0, string.length); - return ignorecase ? start_str.toLowerCase() == string.toLowerCase() : - start_str == string; + return ignorecase ? start_str.toLowerCase() === string.toLowerCase() : + start_str === string; }, /** * checks if the string ends with the given substring * @@ -1003,12 +995,12 @@ * @param boolean ignore the letters case * @return boolean check result */ endsWith: function(string, ignorecase) { var end_str = this.substring(this.length - string.length); - return ignorecase ? end_str.toLowerCase() == string.toLowerCase() : - end_str == string; + return ignorecase ? end_str.toLowerCase() === string.toLowerCase() : + end_str === string; }, /** * converts the string to an integer value * @param Integer base @@ -1038,88 +1030,123 @@ * 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> */ -$ext(Function.prototype, { +$ext(Function.prototype, (function() { + // creating a local reference to the method for a faster access + var _A = Array.prototype.slice; + +return { /** * 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 && !defined(arguments[0])) return this; - - var _method = this, args = $A(arguments), scope = args.shift(); + if (arguments.length < 2 && !arguments[0]) return this; + + var slice = _A, args = slice.call(arguments), scope = args.shift(), func = this; return function() { - return _method.apply(scope, args.concat($A(arguments))); + return func.apply(scope, (args.length !== 0 || arguments.length !== 0) ? args.concat(slice.call(arguments)) : args); }; }, - + /** * binds the function as an event listener to the given scope object * * @param Object scope * @param mixed optional curry (left) argument * ....... * @return Function binded function */ bindAsEventListener: function() { - var _method = this, args = $A(arguments), scope = args.shift(); + var slice = _A, args = slice.call(arguments), scope = args.shift(), func = this; return function(event) { - return _method.apply(scope, [event || window.event].concat(args).concat($A(arguments))); + return func.apply(scope, [event || window.event].concat(args).concat(slice.call(arguments))); }; }, - + /** * allows you to put some curry in your cookery * * @param mixed value to curry * .... - * @return Function carried function + * @return Function curried function */ curry: function() { - return this.bind.apply(this, [this].concat($A(arguments))); + return this.bind.apply(this, [this].concat(_A.call(arguments))); }, /** + * The right side curry feature + * + * @param mixed value to curry + * .... + * @return Function curried function + */ + rcurry: function() { + var curry = _A.call(arguments), func = this; + return function() { + return func.apply(func, _A.call(arguments).concat(curry)); + } + }, + + /** * delays the function execution * * @param Integer delay ms * @param mixed value to curry * ..... * @return Integer timeout marker */ delay: function() { - var args = $A(arguments), timeout = args.shift(); + var args = _A.call(arguments), timeout = args.shift(); var timer = new Number(window.setTimeout(this.bind.apply(this, [this].concat(args)), timeout)); - - timer['cancel'] = function() { window.clearTimeout(this); }; - + + timer.cancel = function() { window.clearTimeout(this); }; + return timer; }, - + /** * creates a periodical execution of the function with the given timeout * * @param Integer delay ms * @param mixed value to curry * ... * @return Ineger interval marker */ periodical: function() { - var args = $A(arguments), timeout = args.shift(); + var args = _A.call(arguments), timeout = args.shift(); var timer = new Number(window.setInterval(this.bind.apply(this, [this].concat(args)), timeout)); - - timer['stop'] = function() { window.clearInterval(this); }; - + + timer.stop = function() { window.clearInterval(this); }; + return timer; + }, + + /** + * Chains the given function after the current one + * + * @param Function the next function + * @param mixed optional value to curry + * ...... + * @return Function chained function + */ + chain: function() { + var args = _A.call(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: @@ -1156,12 +1183,17 @@ abs: function() { return Math.abs(this); }, - round: function() { - return Math.round(this); + round: function(base) { + if (base) { + var base = Math.pow(10, base); + return Math.round(this * base) / base; + } else { + return Math.round(this); + } }, ceil: function() { return Math.ceil(this); }, @@ -1203,106 +1235,98 @@ * * Copyright (C) 2008 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> */ var Class = function() { var args = $A(arguments), properties = args.pop() || {}, parent = args.pop(); - + // if only the parent class has been specified - if (arguments.length == 1 && isFunction(properties)) { + 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); + $ext(klass, Class.Methods).inherit(parent); - // handling the parent class assign - Class.Util.catchSuper(klass, parent); - klass.prototype.constructor = klass; // <- don't put it lower + // catching the injections + $w('extend include').each(function(name) { + if (properties[name]) { + var modules = properties[name]; + klass[name].apply(klass, isArray(modules) ? modules : [modules]); + delete(properties[name]); + } + }); - // handling the inlinde extends and includes - Class.Util.catchExtends(klass, properties); - Class.Util.catchIncludes(klass, properties); - - klass.include(properties); - - return klass; + return klass.include(properties); }; /** - * This module contains some utils which hepls handling new classes definition + * This method gets through a list of the object its class and all the ancestors + * and finds a hash named after property, used for configuration purposes with + * the Observer and Options modules * - * Copyright (C) 2008 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> + * NOTE: this method will look for capitalized and uppercased versions of the + * property name + * + * @param Object a class instance + * @param String property name + * @return Object hash or null if nothing found */ -Class.Util = { +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]}); + + 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> + */ +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 clean_module = function(module, what) { + return Object.without.apply(Object, [module].concat(what == 'e' ? extend : include)); + }; + +return { /** - * handles the class superclass catching up + * Makes the class get inherited from another one * - * @param Function class - * @param Class superclass - * @return void + * @param Object another class + * @return Class this */ - catchSuper: function(klass, parent) { - if (parent && defined(parent.prototype)) { - klass.parent = parent; + inherit: function(parent) { + // handling the parent class assign + if (parent && parent.prototype) { var s_klass = function() {}; s_klass.prototype = parent.prototype; - klass.prototype = new s_klass; + this.prototype = new s_klass; + this.parent = parent; } - - klass.ancestors = []; + + // collecting the list of ancestors + this.ancestors = []; while (parent) { - klass.ancestors.push(parent); + this.ancestors.push(parent); parent = parent.parent; } + + return this.prototype.constructor = this; }, - - /** - * handles the inline extendings on class definitions - * - * @param Function class - * @param Object user's properties - * @return void - */ - catchExtends: function(klass, properties) { - if (properties['extend']) { - var exts = properties['extend']; - - klass.extend.apply(klass, isArray(exts) ? exts : [exts]); - delete(properties['extend']); - } - }, - - /** - * handles the inline includes of the class definitions - * - * @param Function class - * @param Object user's properties - * @return void - */ - catchIncludes: function(klass, properties) { - if (properties['include']) { - var includes = properties['include']; - klass.include.apply(klass, isArray(includes) ? includes : [includes]); - delete(properties['include']); - } - } -}; - -/** - * 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 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> - */ -Class.Methods = { /** * this method will extend the class-level with the given objects * * NOTE: this method _WILL_OVERWRITE_ the existing itercecting entries * @@ -1314,61 +1338,56 @@ * @param Object module to extend * .... * @return Class the klass */ extend: function() { - var filter = ['prototype', 'name', 'parent', 'extend', 'include']; - for (var i=0; i < arguments.length; i++) { - if (isHash(arguments[i])) { - for (var key in arguments[i]) { - if (!filter.includes(key)) { - this[key] = arguments[i][key]; - } - } - } - } - + $A(arguments).filter(isHash).each(function(module) { + var callback = module.selfExtended || module.self_extended; + + $ext(this, clean_module(module, 'e')); + + if (callback) callback.call(module, this); + }, this); + return this; }, - + /** * extends the class prototype with the given objects * NOTE: this method _WILL_OVERWRITE_ the existing itercecting entries * NOTE: this method _WILL_NOT_OVERWRITE_ the 'klass' attribute of the klass.prototype * * @param Object module to include * .... * @return Class the klass */ include: function() { - for (var i=0; i < arguments.length; i++) { - if (isHash(arguments[i])) { - for (var key in arguments[i]) { - if (key != 'klass' && key != 'constructor') { - - // handling the super methods - var ancestor = this.ancestors.first(function(klass) { return isFunction(klass.prototype[key]); }); - - if (ancestor) { - (function(name, method, $super) { - this.prototype[name] = function() { - this.$super = $super; - - return method.apply(this, arguments); - }; - }).call(this, key, arguments[i][key], ancestor.prototype[key]); - } else { - this.prototype[key] = arguments[i][key]; - } - - } - } + var ancestors = this.ancestors.map('prototype'), ancestor; + + $A(arguments).filter(isHash).each(function(module) { + var callback = module.selfIncluded || module.self_included; + module = clean_module(module, 'i'); + + for (var key in module) { + ancestor = ancestors.first(function(proto) { return isFunction(proto[key]); }); + + this.prototype[key] = !ancestor ? module[key] : + (function(name, method, super_method) { + return function() { + this.$super = super_method; + + return method.apply(this, arguments); + }; + })(key, module[key], ancestor[key]); } - } + + if (callback) callback.call(module, this); + }, this); + return this; } -}; +}})(); /** * This is a simple mix-in module to be included in other classes * * Basically it privdes the <tt>setOptions</tt> method which processes @@ -1386,30 +1405,38 @@ * * @param Object options * @return Object current instance */ setOptions: function(options) { - var names = $w('OPTIONS Options options'), - objects = [this, this.constructor].concat(this.constructor.ancestors), - OPTIONS = objects.map(function(object) { - return names.map(function(name) { return object[name]; }); - }).flatten().first(function(i) { return !!i; }); + var options = this.options = Object.merge(Class.findSet(this, 'options'), options); - this.options = Object.merge({}, OPTIONS, options); - // hooking up the observer options if (isFunction(this.on)) { var match; - for (var key in this.options) { - if (match = key.match(/on([A-Z][a-z]+)/)) { - this.on(match[1].toLowerCase(), this.options[key]); - delete(this.options[key]); + for (var key in options) { + if (match = key.match(/on([A-Z][A-Za-z]+)/)) { + this.on(match[1].toLowerCase(), options[key]); + delete(options[key]); } } } return this; + }, + + /** + * Cuts of an options hash from the end of the arguments list + * assigns them using the #setOptions method and then + * returns the list of other arguments as an Array instance + * + * @param mixed iterable + * @return Array of the arguments + */ + cutOptions: function(args) { + var args = $A(args); + this.setOptions(isHash(args.last()) ? args.pop() : {}); + return args; } }; /** * standard Observer class. @@ -1429,18 +1456,12 @@ * general constructor * * @param Object options */ initialize: function(options) { - this.setOptions(options); - - // catching up the event shortucts - var ancestor, shorts = this.EVENTS || this.constructor.EVENTS || - ((ancestor = this.constructor.ancestors.first('EVENTS')) ? - ancestor.EVENTS : null); - - Observer.createShortcuts(this, shorts); + this.setOptions(options); + Observer.createShortcuts(this, Class.findSet(this, 'events')); }, /** * starts observing an event * @@ -1450,44 +1471,45 @@ * observe(Object events_hash); * * @return Observer self */ observe: function() { - var args = $A(arguments), event = args.shift(); + var args = Array.prototype.slice.call(arguments), event = args.shift(); - if (!event.trim) { // <- not a string + if (typeof(event) === 'string') { + if (this.$listeners === undefined) this.$listeners = []; + + var callback = args.shift(); + switch (typeof callback) { + case "string": + callback = this[callback]; + + case "function": + var hash = { e: event, f: callback, a: args }; + this.$listeners.push(hash); + break; + + default: + if (isArray(callback)) { + callback.each(function(params) { + this.observe.apply(this, [event].concat( + isArray(params) ? params : [params] + ).concat(args)); + }, this); + } + } + + } else { + // assuming it's a hash of key-value pairs for (var name in event) { this.observe.apply(this, [name].concat( isArray(event[name]) ? event[name] : [event[name]] ).concat(args)); } } - if (!this.$listeners) this.$listeners = []; - var callback = args.shift(); - switch (typeof callback) { - case "string": - callback = this[callback]; - - case "function": - var hash = { e: event, f: callback, a: args }; - this.$listeners.push(hash); - - if (this.$o && this.$o.add) this.$o.add.call(this, hash); - - break; - - default: - if (isArray(callback)) { - callback.each(function(params) { - this.observe.apply(this, [event].concat( - isArray(params) ? params : [params] - ).concat(args)); - }, this); - } - } return this; }, /** @@ -1528,16 +1550,12 @@ if (this.$listeners) { if (!isString(event)) { callback = event; event = null; } if (isString(callback)) callback = this[callback]; this.$listeners = this.$listeners.filter(function(i) { - var result = (event && callback) ? (i.e != event || i.f != callback) : - (event ? i.e != event : i.f != callback); - - if (!result && this.$o && this.$o.remove) this.$o.remove.call(this, i); - - return result; + return (event && callback) ? (i.e !== event || i.f !== callback) : + (event ? i.e !== event : i.f !== callback); }, this); } return this; }, @@ -1551,11 +1569,11 @@ * @param String event name * @return Array of listeners */ listeners: function(event) { return (this.$listeners || []).filter(function(i) { - return !event || i.e == event; + return !event || i.e === event; }).map(function(i) { return i.f; }).uniq(); }, /** * initiates the event handling @@ -1567,14 +1585,11 @@ */ fire: function() { var args = $A(arguments), event = args.shift(); (this.$listeners || []).each(function(i) { - if (i.e == event) { - (this.$o && this.$o.fire) ? this.$o.fire.call(this, event, args, i) : - i.f.apply(this, i.a.concat(args)); - } + if (i.e === event) i.f.apply(this, i.a.concat(args)); }, this); return this; }, @@ -1586,11 +1601,11 @@ * @param Array optional events list to build shortcuts * @return Object extended object */ create: function(object, events) { $ext(object, Object.without(this.prototype, 'initialize', 'setOptions'), true); - return this.createShortcuts(object, events || object['EVENTS']); + return this.createShortcuts(object, events || Class.findSet(object, 'events')); }, /** * builds shortcut methods to wire/fire events on the object * @@ -1688,24 +1703,24 @@ * @param String event name * @return String fixed event name */ cleanName: function(name) { name = name.toLowerCase(); - name = name.startsWith('on') ? name.slice(2) : name; - name = name == 'rightclick' ? 'contextmenu' : name; + name = name.substr(0,2) === 'on' ? name.slice(2) : name; + name = name === 'rightclick' ? 'contextmenu' : name; return name; }, /** * returns a real, browser specific event name * * @param String clean unified name * @return String real name */ realName: function(name) { - if (Browser.Gecko && name == 'mousewheel') name = 'DOMMouseScroll'; - if (Browser.Konqueror && name == 'contextmenu') name = 'rightclick'; + if (Browser.Gecko && name === 'mousewheel') name = 'DOMMouseScroll'; + if (Browser.Konqueror && name === 'contextmenu') name = 'rightclick'; return name; }, /** * Registers some additional event extendsions @@ -1782,100 +1797,82 @@ }); /** * The DOM Element unit handling * - * Credits: - * The basic principles of the elements extending are originated from - * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson - * * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> */ -window.Element = new Class(window.Element, { +self.Element = (function(old_Element) { + + var new_Element = function(tag, options) { + var element = document.createElement(tag), options = options || {}; + + 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); } + + 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; + } + + // 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); + + +$ext(Element, { /** - * basic constructor + * registeres the methods on the custom element methods list + * will add them to prototype and will generate a non extensive static mirror + * + * USAGE: + * Element.addMethods({ + * foo: function(bar) {} + * }); * - * @param String tag name - * @param Object new element options - * @return Element object + * $(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 */ - initialize: function(tag_name, options) { - if (Browser.IE && tag_name == 'input' && options && options.checked) { - tag_name = '<input checked="true"/>'; + addMethods: 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) {} } - var element = $(document.createElement(tag_name)), options = options || {}; - - 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']); } - - return element.set(options); + return this; }, - extend: { - Methods: {}, // DO NOT Extend this object manually unless you need it, use Element#addMethods - - /** - * IE browsers manual elements extending - * - * @param Element - * @return Element - */ - prepare: function(element) { - if (element && element.tagName && !element.set) { - $ext(element, Element.Methods, true); - - if (self['Form']) { - switch(element.tagName) { - case 'FORM': - Form.ext(element); - break; - - case 'INPUT': - case 'SELECT': - case 'BUTTON': - case 'TEXTAREA': - Form.Element.ext(element); - break; - } - } - } - return element; - }, - - /** - * registeres the methods on the custom element methods list - * will add them to prototype and will generate a non extensive static mirror - * - * USAGE: - * Element.addMethods({ - * 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 - */ - addMethods: 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) {} - } - - return this; - } - } + Methods: {} // DO NOT Extend this object manually unless you really need it, use Element#addMethods }); /** * The DOM Element unit structures handling module * @@ -1927,18 +1924,10 @@ prev: function(css_rule) { return this.prevSiblings(css_rule).first(); }, -// those two are moved to the Selector unit definition -// first: Element.Methods.querySelector, -// select: Element.Methods.querySelectorAll, - - match: function(css_rule) { - return new Selector(css_rule).match(this); - }, - /** * removes the elemnt out of this parent node * * @return Element self */ @@ -1971,24 +1960,31 @@ } else { var scripts = ''; position = isString(position) ? position.toLowerCase() : 'bottom'; if (isString(content)) { - content = content.stripScripts(function(s, h) { scripts = s; }); + content = content.stripScripts(function(s) { scripts = s; }); } Element.insertions[position](this, content.tagName ? content : Element.insertions.createFragment.call( (position == 'bottom' || position == 'top' || !this.parentNode) ? this : this.parentNode, content ) ); - $eval(scripts); + if (scripts) $eval(scripts); } return this; }, + /** + * Inserts the element inside the given one at the given position + * + * @param mixed destination element reference + * @param String optional position + * @return Element this + */ insertTo: function(element, position) { $(element).insert(this, position); return this; }, @@ -2008,12 +2004,13 @@ * @param mixed content (a String, an Element or a list of elements) * @return Element self */ update: function(content) { if (isString(content)) { - this.innerHTML = content.stripScripts(); - content.evalScripts(); + var scripts = ''; + this.innerHTML = content.stripScripts(function(s) { scripts = s; }); + if (scripts) $eval(scripts); } else { this.clean().insert(content); } return this; }, @@ -2060,19 +2057,19 @@ * @param String pointer attribute name * @param String optional css-atom rule * @return Array found elements */ rCollect: function(attr, css_rule) { - var node = this, nodes = []; + var node = this, result = []; while ((node = node[attr])) { - if (node.tagName && (!css_rule || new Selector(css_rule).match(node))) { - nodes.push(Browser.OLD ? Element.prepare(node) : node); + if (node.tagName && (!css_rule || $(node).match(css_rule))) { + result.push(node); } } - - return nodes; + if (Browser.OLD) result.forEach(Element.prepare); + return result; } }); // list of insertions handling functions // NOTE: each of the methods will be called in the contects of the current element @@ -2189,13 +2186,13 @@ var c_key; for (var key in hash) { c_key = key.indexOf('-') != -1 ? key.camelize() : key; - if (key == 'opacity') { + if (key === 'opacity') { this.setOpacity(hash[key]); - } else if (key == 'float') { + } else if (key === 'float') { c_key = Browser.IE ? 'styleFloat' : 'cssFloat'; } this.style[c_key] = hash[key]; } @@ -2207,18 +2204,16 @@ * handles the opacity setting * * @param Float opacity value between 0 and 1 * @return Element self */ - setOpacity: function(value) { - var key = 'opacity'; - if (Browser.IE) { - key = 'filter'; - value = 'alpha(opacity='+ value * 100 +')'; - } - this.style[key] = value; + setOpacity: Browser.IE ? function(value) { + this.style.filter = 'alpha(opacity='+ value * 100 +')'; return this; + } : function(value) { + this.style.opacity = value; + return this; }, /** * returns style of the element * @@ -2246,34 +2241,22 @@ var value, key = key.camelize(); switch (key) { case 'opacity': value = !Browser.IE ? style[key] : - (((style['filter'] || '').match(/opacity=(\d+)/i) || ['', '100'])[1].toInt() / 100)+''; + ((/opacity=(\d+)/i.exec(style.filter || '') || ['', '100'])[1].toInt() / 100)+''; break; case 'float': - key = Browser.IE ? 'styleFloat' : 'cssFloat'; + key = Browser.IE ? 'styleFloat' : 'cssFloat'; default: - if (style[key]) { - value = style[key]; - } else { - var values = $w('top right bottom left').map(function(name) { - var tokens = key.underscored().split('_'); tokens.splice(1, 0, name); - return style[tokens.join('_').camelize()]; - }).uniq(); - - if (values.length == 1) { - value = values[0]; - } - } + value = style[key]; // Opera returns named colors with quotes - if (value && Browser.Opera && /color/.test(key)) { - var match = value.match(/"(.+?)"/); - value = match ? match[1] : value; + if (Browser.Opera && /color/i.test(key) && value) { + value = value.replace(/"/g, ''); } } return value ? value : null; }, @@ -2283,11 +2266,11 @@ * * @param String class name * @return boolean check result */ hasClass: function(name) { - return (' '+this.className+' ').indexOf(' '+name+' ') != -1; + return (' '+this.className+' ').indexOf(' '+name+' ') !== -1; }, /** * sets the whole class-name string for the element * @@ -2304,12 +2287,13 @@ * * @param String class name * @return Element self */ addClass: function(name) { - if ((' '+this.className+' ').indexOf(' '+name+' ') == -1) { - this.className += (this.className ? ' ' : '') + name; + var testee = ' '+this.className+' '; + if (testee.indexOf(' '+name+' ') === -1) { + this.className += (testee === ' ' ? '' : ' ') + name; } return this; }, /** @@ -2363,13 +2347,18 @@ * @param mixed attribute value * @return Element self */ set: function(hash, value) { if (value) { var val = {}; val[hash] = value; hash = val; } - - for (var key in hash) + + for (var key in hash) { + // some attributes are not available as properties + if (this[key] === undefined) { + this.setAttribute(key, ''+hash[key]); + } this[key] = hash[key]; + } return this; }, /** @@ -2377,11 +2366,11 @@ * * @param String attr name * @return mixed value */ get: function(name) { - var value = this.getAttribute(name) || this[name]; + var value = this[name] || this.getAttribute(name); return value == '' ? null : value; }, /** * checks if the element has that attribute @@ -2480,57 +2469,61 @@ * responsible for the dimensions and positions getting/setting * * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-il> */ Element.addMethods({ - + /** + * Returns the element sizes as a hash + * + * @return Object {x: NNN, y: NNN} + */ sizes: function() { return { x: this.offsetWidth, y: this.offsetHeight }; }, + /** + * Returns the element absolute position + * + * NOTE: see the konq.js file for the manual version of the method + * + * @return Object {x: NNN, y: NNN} + */ position: function() { - var dims = this.dimensions(); - return { x: dims.left, y: dims.top }; + var rect = this.getBoundingClientRect(), doc = this.ownerDocument.documentElement, scrolls = window.scrolls(); + + return { + x: rect.left + scrolls.x - doc.clientLeft, + y: rect.top + scrolls.y - doc.clientTop + }; }, + /** + * Returns the element scrolls + * + * @return Object {x: NNN, y: NNN} + */ scrolls: function() { return { x: this.scrollLeft, y: this.scrollTop }; }, /** * returns the element dimensions hash * * @return Object dimensions (top, left, width, height, scrollLeft, scrollTop) */ dimensions: function() { - var left = 0, top = 0; + var sizes = this.sizes(); + var scrolls = this.scrolls(); + var position = this.position(); - if (this.getBoundingClientRect) { - var rect = this.getBoundingClientRect(), doc = this.ownerDocument.documentElement, scrolls = window.scrolls(); - - left = rect.left + scrolls.x - doc.clientLeft; - top = rect.top + scrolls.y - doc.clientTop; - } else { - // Manual version - left = this.offsetLeft; - top = this.offsetTop; - - if (this.getStyle('position') != 'absolute') { - var body = this.ownerDocument.body, html = body.parentNode; - - left += body.offsetLeft + html.offsetLeft; - top += body.offsetTop + html.offsetTop; - } - } - return { - top: top, - left: left, - width: this.sizes().x, - height: this.sizes().y, - scrollLeft: this.scrolls().x, - scrollTop: this.scrolls().y + top: position.y, + left: position.x, + width: sizes.x, + height: sizes.y, + scrollLeft: scrolls.x, + scrollTop: scrolls.y }; }, /** * sets the width of the element in pixels @@ -2639,43 +2632,46 @@ Element.addMethods((function() { var observer = Observer.create({}, $w('click rightclick contextmenu mousedown mouseup mouseover mouseout mousemove keypress keydown keyup') ); - observer.$o = { - add: function(hash) { - var callback = hash.f, args = hash.a; - hash.e = Event.cleanName(hash.e); - hash.n = Event.realName(hash.e); + // + // 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);'+ - hash.w = function() { - Event.ext(arguments[0]); - return callback.apply(this, $A(arguments).concat(args)); - }; + '$2.w=function(){Event.ext(arguments[0]);'+ + 'return $2.f.apply(this,$A(arguments).concat($2.a));};'+( - if (this.addEventListener) { - this.addEventListener(hash.n, hash.w, false); - } else { - hash.w = hash.w.bind(this); - this.attachEvent('on'+ hash.n, hash.w); - } - }, - - remove: function(hash) { - if (this.removeEventListener) { - this.removeEventListener(hash.n, hash.w, false); - } else { - this.detachEvent('on'+ hash.n, hash.w); - } - }, - - fire: function(name, args, hash) { - var event = new Event(name, args.shift()); - hash.f.apply(this, [event].concat(hash.a).concat(args)); - } - }; + self.attachEvent ? + '$2.w=$2.w.bind(this);this.attachEvent("on"+$2.n,$2.w);' : + 'this.addEventListener($2.n,$2.w,false);' + ) + )+ + '})').f; + 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; + + + 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; + // a simple events terminator method to be hooked like // this.onClick('stopEvent'); observer.stopEvent = function(e) { e.stop(); }; $ext(window, observer); @@ -2684,603 +2680,80 @@ return observer; })()); /** - * The DOM elements selection handling class + * The DOM elements selection handling * - * Credits: - * The naming principles of the unit are inspired by - * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson + * 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-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> */ - -// checking, monkeying and hooking the native css-selectors interface -// IE8 W3C -[document, (Element.parent || self['HTMLElement'] || {}.constructor).prototype].each(function(object, i) { - var old_selector = object.querySelector; - var old_selector_all = object.querySelectorAll; - - // the native selectors checking/monkeying - var selectors = {}; - if (!old_selector) selectors.querySelector = function(css_rule) { - return new Selector(css_rule).first(this); - }; - if (!old_selector_all) selectors.querySelectorAll = function(css_rule) { - return new Selector(css_rule).select(this); - }; - - // RightJS version of the selectors - selectors.first = old_selector ? i ? function(css_rule) { - return this.querySelector(this.tagName + ' ' + (css_rule || '*')); - } : function(css_rule) { - return this.querySelector(css_rule || '*'); - } : selectors.querySelector; - - selectors.select = old_selector_all ? i ? function(css_rule) { - return $A(this.querySelectorAll(this.tagName + ' ' + (css_rule || '*'))); - } : function(css_rule) { - return $A(this.querySelectorAll(css_rule || '*')); - } : selectors.querySelectorAll; - - return i ? Element.addMethods(selectors) : $ext(object, selectors); -}); - - -var Selector = new Class({ - extend: { - cache: {} - }, - +Element.addMethods((function() { /** - * constructor - * - * @param String css rule definition - * @return void + * 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 */ - initialize: function(css_rule) { - var cached = isString(css_rule) ? Selector.cache[css_rule] : css_rule; - if (cached) return cached; - Selector.cache[css_rule] = this; - - this.cssRule = css_rule || '*'; - - var strategy = 'Manual'; - if (this.cssRule.includes(',')) { - strategy = 'Multiple'; - } - - this.strategy = new Selector[strategy](this.cssRule); - }, + var stub_rule = function(css_rule, tag) { + return css_rule ? css_rule.replace(/(^|,)/g, '$1'+ tag + ' ') : '*'; + }; +return { /** - * selects the first matching element which is a sub node of the given element - * and matches the selector's css-rule + * Extracts the first element matching the css-rule, + * or just any first element if no css-rule was specified * - * @param Element element - * @return Element matching element or null if nothing found + * @param String css-rule + * @return Element matching node or null */ - first: Browser.OLD ? function(element) { - var element = this.strategy.first(element); - return element ? $(element) : null; - } : function(element) { - return this.strategy.first(element); + first: function(css_rule) { + return this.querySelector(stub_rule(css_rule, this.tagName)); }, /** - * select all the subnodes of the element which are matching the rule + * Selects a list of matching nodes, or all the descendant nodes if no css-rule provided * - * @param Element element - * @return Array list of found nodes + * @param String css-rule + * @return Array of elements */ - select: Browser.OLD ? function(element) { - return this.strategy.select(element).map(Element.prepare); - } : function(element) { - return this.strategy.select(element); + select: function(css_rule) { + return $A(this.querySelectorAll(stub_rule(css_rule, this.tagName))); }, /** - * checks if the element matches the rule + * checks if the element matches this css-rule * - * @param Element element + * @param String css-rule * @return Boolean check result */ - match: function(element) { - return this.strategy.match(element); - } -}); - - -/** - * this class represent a simple css-definition atom unit - * - * the main purpose is to organize the simpliest case of css-rule match for the manual matcher. - * - * Credits: - * Some functionality and principles are inspired by css-selectors in - * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti - * - * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> - */ -Selector.Atom = new Class({ - id: null, - tag: '*', - classes: [], - pseudo: null, - pseudoValue: null, - attrs: {}, - - rel: ' ', // relations with the previous atom - - ID_RE: /#([\w\-_]+)/, - TAG_RE: /^[\w\*]+/, - CLASS_RE: /\.([\w\-\._]+)/, - PSEUDO_RE: /:([\w\-]+)(\((.+?)\))*$/, - ATTRS_RE: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/, - - /** - * constructor - * - * @param String css-definition - * @param String relation with the previous atom - * @return void - */ - initialize: function(css_rule, rel) { - css_rule = css_rule.trim(); - this.rel = rel || ' '; - this.hasNonTagMatcher = !/^[a-z\*]+$/.test(css_rule); + match: function(css_rule) { + if (!css_rule || css_rule == '*') return true; - // NOTE! dont change the order of the atom parsing, there might be collisions - this.attrs = {}; - while((m = css_rule.match(this.ATTRS_RE))) { - this.attrs[m[1]] = { op: m[2], value: m[5] || m[6] }; - css_rule = css_rule.replace(m[0], ''); - } + var fake, result, parent, parents = this.parents(); - if ((m = css_rule.match(this.PSEUDO_RE))) { - this.pseudo = m[1]; - this.pseudoValue = m[3] == '' ? null : m[3]; - css_rule = css_rule.replace(m[0], ''); - } else { - this.pseudo = null; - this.pseudoValue = null; - } + parent = parents.length ? parents.last() : fake = $E('div').insert(this); + result = parent.select(css_rule).include(this); - this.id = (css_rule.match(this.ID_RE) || [1, null])[1]; - this.tag = (css_rule.match(this.TAG_RE) || '*').toString().toUpperCase(); - this.classes = (css_rule.match(this.CLASS_RE) || [1, ''])[1].split('.').without(''); + if (fake) { this.remove(); } - this.buildMatch(); - }, - - /** - * cecks if the node matches the atom - * - * @param Element element - * @return Boolean check result - */ - match: null, // this method is dinamically generated depend on the situation - -// protected - - // building the match method for the particular case - buildMatch: function() { - var matchers = []; - - if (this.id) matchers.push('matchId'); - if (this.tag != '*') matchers.push('matchTag'); - if (this.classes.length) matchers.push('matchClass'); - if (!Object.empty(this.attrs)) matchers.push('matchAttrs'); - if (this.pseudo) matchers.push('matchPseudo'); - - if (matchers.length == 1) { - this.match = this[matchers[0]]; - } else if (matchers.length) { - var length = matchers.length; - this.match = function(element) { - for (var i=0; i < length; i++) - if (!this[matchers[i]](element)) - return false; - return true; - } - } else { - this.match = function() { return true; } - } - }, - - matchId: function(element) { - return element.id == this.id; - }, - - matchTag: function(element) { - return element.tagName == this.tag; - }, - - matchClass: function(element) { - if (element.className) { - var names = element.className.split(' '); - if (names.length == 1) { - return this.classes.indexOf(names[0]) != -1; - } else { - for (var i=0, length = this.classes.length; i < length; i++) - if (names.indexOf(this.classes[i]) == -1) - return false; - - return true; - } - } - return false; - }, - - matchAttrs: function(element) { - var matches = true; - for (var key in this.attrs) { - matches &= this.matchAttr(element, key, this.attrs[key]['op'], this.attrs[key]['value']); - } - return matches; - }, - - matchAttr: function(element, name, operator, value) { - var attr = element.getAttribute(name) || ''; - switch(operator) { - case '=': return attr == value; - case '*=': return attr.includes(value); - case '^=': return attr.startsWith(value); - case '$=': return attr.endsWith(value); - case '~=': return attr.split(' ').includes(value); - case '|=': return attr.split('-').includes(value); - default: return attr != ''; - } - return false; - }, - - matchPseudo: function(element) { - return this.pseudoMatchers[this.pseudo].call(element, this.pseudoValue, this.pseudoMatchers); - }, - - /** - * W3C pseudo matchers - * - * NOTE: methods of the module will be called in a context of an element - */ - pseudoMatchers: { - checked: function() { - return this.checked; - }, - - disabled: function() { - return this.disabled; - }, - - empty: function() { - return !(this.innerText || this.innerHTML || this.textContent || '').length; - }, - - 'first-child': function(tag_name) { - var node = this; - while ((node = node.previousSibling)) { - if (node.tagName && (!tag_name || node.tagName == tag_name)) { - return false; - } - } - return true; - }, - - 'first-of-type': function() { - return arguments[1]['first-child'].call(this, this.tagName); - }, - - 'last-child': function(tag_name) { - var node = this; - while ((node = node.nextSibling)) { - if (node.tagName && (!tag_name || node.tagName == tag_name)) { - return false; - } - } - return true; - }, - - 'last-of-type': function() { - return arguments[1]['last-child'].call(this, this.tagName); - }, - - 'only-child': function(tag_name, matchers) { - return matchers['first-child'].call(this, tag_name) - && matchers['last-child'].call(this, tag_name); - }, - - 'only-of-type': function() { - return arguments[1]['only-child'].call(this, this.tagName, arguments[1]); - }, - - 'nth-child': function(number, matchers, tag_name) { - if (!matchers.hasParent(this)) return false; - number = number.toLowerCase(); - - if (number == 'n') return true; - - if (number.includes('n')) { - // parsing out the matching expression - var a = b = 0; - if (m = number.match(/^([+-]?\d*)?n([+-]?\d*)?$/)) { - a = m[1] == '-' ? -1 : parseInt(m[1], 10) || 1; - b = parseInt(m[2], 10) || 0; - } - - // getting the element index - var index = 1, node = this; - while ((node = node.previousSibling)) { - if (node.tagName && (!tag_name || node.tagName == tag_name)) index++; - } - - return (index - b) % a == 0 && (index - b) / a >= 0; - - } else { - return matchers['index'].call(this, number.toInt() - 1, matchers, tag_name); - } - }, - - 'nth-of-type': function(number) { - return arguments[1]['nth-child'].call(this, number, arguments[1], this.tagName); - }, - -// protected - index: function(number, matchers, tag_name) { - number = isString(number) ? number.toInt() : number; - var node = this, count = 0; - while ((node = node.previousSibling)) { - if (node.tagName && (!tag_name || node.tagName == tag_name) && ++count > number) return false; - } - return count == number; - }, - - // checking if the element has a parent node - // the '-----fake' parent is a temporary context for the element - // just of the matching process - hasParent: function(element) { - return element.parentNode && element.parentNode.id != '-----fake'; - } + return result; } -}); +}})()); -/** - * represents a manual (virtual) selector strategy - * - * Credits: - * Some principles were inspired by - * - 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-ilc-om> - */ -Selector.Manual = new Class({ - ATOMS_SPLIT_RE: /(\s*([~>+ ])\s*)(?![^\s\)\]]*(\)|\]))/, - - /** - * constructor - * - * @param String css-rule - */ - initialize: function(css_rule) { - var css_rule = css_rule.trim(); - this.cssRule = css_rule; - - this.atoms = []; - - var relation = null, match = null; - - while (match = css_rule.match(this.ATOMS_SPLIT_RE)) { - separator_pos = css_rule.indexOf(match[0]); - this.atoms.push(new Selector.Atom(css_rule.substring(0, separator_pos), relation)); - - relation = match[2]; // <- puts the current relation to the next atom - - // chopping off the first atom of the rule - css_rule = css_rule.substr(separator_pos+(match[1].length==1 ? 1 : match[1].length-1)).trim(); - } - this.atoms.push(new Selector.Atom(css_rule, relation)); +// document-level hooks +$ext(document, { + first: function(css_rule) { + return this.querySelector(css_rule || '*'); }, - - /** - * searches for the first matching subnode - * - * @param Element base node - * @return Element matching element or null if nothing found - */ - first: function(node) { - return this.select(node).first(); - }, - - /** - * selects all the matching subnodes - * - * @param Element base node - * @return Array found nodes - */ - select: function(node) { - var founds, atom, index, sub_founds; - - for (var i=0, i_length = this.atoms.length; i < i_length; i++) { - atom = this.atoms[i]; - if (i == 0) { - founds = this.find[atom.rel](node, atom); - - } else { - if (i > 1) founds = this.uniq(founds); - - for (var j=0; j < founds.length; j++) { - sub_founds = this.find[atom.rel](founds[j], atom); - - sub_founds.unshift(1); // <- nuke the parent node out of the list - sub_founds.unshift(j); // <- position to insert the subresult - - founds.splice.apply(founds, sub_founds); - - j += sub_founds.length - 3; - } - } - } - - return this.atoms.length > 1 ? this.uniq(founds) : founds; - }, - - /** - * checks if the node matches the rule - * - * @param Element node to check - * @return boolean check result - */ - match: function(element) { - // if there's more than one atom, we match the element in a context - if (!this.atoms || this.atoms.length > 1) { - if (element.parentNode) { - // searching for the top parent node - // NOTE: don't use the Element.parents in here to avoid annecessary elements extending - var p = element, parent; - while ((p = p.parentNode)) parent = p; - } else { - // putting the element in a temporary context so we could test it - var parent = document.createElement('div'), parent_is_fake = true; - parent.id = '-----fake'; // <- this id is used in the manual 'match' method, - // to determine if the element originally had no parent node - parent.appendChild(element); - } - - var match = this.select(parent).includes(element); - if (parent_is_fake) parent.removeChild(element); - } else { - // if there's just one atom, we simple match against it. - var match = this.atoms[0].match(element); - } - - return match; - }, -// protected - uniq: function(elements) { - var uniq = [], uids = [], uid; - for (var i=0, length = elements.length; i < length; i++) { - uid = $uid(elements[i]); - if (!uids[uid]) { - uniq.push(elements[i]); - uids[uid] = true; - } - } - - return uniq; - }, - - find: { - /** - * search for any descendant nodes - */ - ' ': function(element, atom) { - var founds = $A(element.getElementsByTagName(atom.tag)); - if (atom.hasNonTagMatcher) { - var matching = []; - for (var i=0, length = founds.length; i < length; i++) { - if (atom.match(founds[i])) - matching.push(founds[i]); - } - return matching; - } - return founds; - }, - - /** - * search for immidate descendant nodes - */ - '>': function(element, atom) { - var node = element.firstChild, matched = []; - while (node) { - if (atom.match(node)) { - matched.push(node); - } - node = node.nextSibling; - } - return matched; - }, - - /** - * search for immiate sibling nodes - */ - '+': function(element, atom) { - while ((element = element.nextSibling)) { - if (element.tagName) { - return atom.match(element) ? [element] : []; - } - } - return []; - }, - - /** - * search for late sibling nodes - */ - '~': function(element, atom) { - var founds = []; - while ((element = element.nextSibling)) { - if (atom.match(element)) - founds.push(element); - } - return founds; - } - } - -}); - -/** - * represents a complex, multi ruled select strategy - * - * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> - */ -Selector.Multiple = new Class({ - - /** - * constructor - * - * @param String css-rule - */ - initialize: function(css_rule) { - this.cssRule = css_rule; - this.selectors = css_rule.split(',').map(function(rule) { - return rule.blank() ? null : new Selector.Manual(rule); - }).compact(); - }, - - /** - * searches for the first matching subnode - * - * @param Element base node - * @return Element matching element or null if nothing found - */ - first: function(node) { - return this.selectors.map('first', node).first(function(i) { return !!i;}); - }, - - /** - * selects all the matching subnodes - * - * @param Element base node - * @return Array found nodes - */ - select: function(node) { - return this.selectors.map('select', node, null).flatten().uniq(); - }, - - /** - * checks if the node matches the rule - * - * @param Element node to check - * @return boolean check result - */ - match: function(node) { - return this.selectors.some('match', node) || !this.selectors.length; + select: function(css_rule) { + return $A(this.querySelectorAll(css_rule || '*')); } }); - /** * the window object extensions * * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-il> */ @@ -3346,25 +2819,15 @@ */ [window, document].each(function(object) { Observer.createShortcuts(object, ['ready']); var ready = object.ready.bind(object); - if (Browser.IE) { - var tmp = $E('div'); + // IE and Konqueror browsers + if (document.readyState !== undefined) { (function() { - var loaded = false; - try { - document.body.appendChild(tmp); - tmp.remove(); - loaded = true; - } catch(e) { arguments.callee.delay(50);} - if (loaded) ready(); + ['loaded','complete'].includes(document.readyState) ? ready() : arguments.callee.delay(50); })(); - } else if (document['readyState'] !== undefined) { - (function() { - $w('loaded complete').includes(document.readyState) ? ready() : arguments.callee.delay(50); - })(); } else { document.addEventListener('DOMContentLoaded', ready, false); } }); @@ -3376,51 +2839,44 @@ * The basic principles of the module are inspired by * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson * * Copyright (C) 2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> */ -var Form = new Class(Element, { +var Form = function(options) { + var options = options || {}, remote = options['remote'], + form = new Element('form', Object.without(options, 'remote')); + + if (remote) form.remotize(); + + return form; +}; + +$ext(Form, { /** - * generic forms creation constructor + * IE browsers manual elements extending * - * @param Object form options + * @param Element form + * @return Form element */ - initialize: function(options) { - var options = options || {}, remote = options['remote'], - form = this.$super('form', Object.without(options, 'remote')); - - if (remote) form.remotize(); - - return form; + ext: function(element) { + return $ext(element, this.Methods); }, - extend: { - /** - * IE browsers manual elements extending - * - * @param Element form - * @return Form element - */ - ext: function(element) { - return $ext(element, this.Methods); - }, + Methods: {}, + + /** + * Extends the form functionality + * + * @param Object methods hash + * @return void + */ + addMethods: function(methods, dont_overwrite) { + $ext(Form.Methods, methods, dont_overwrite); - Methods: {}, - - /** - * Extends the form functionality - * - * @param Object methods hash - * @return void - */ - addMethods: function(methods, dont_overwrite) { - $ext(Form.Methods, methods, dont_overwrite); - - try { // trying to extend the form element prototype - $ext(HTMLFormElement.prototype, methods, dont_overwrite); - } catch(e) {} - } + try { // trying to extend the form element prototype + $ext(HTMLFormElement.prototype, methods, dont_overwrite); + } catch(e) {} } }); Form.addMethods({ /** @@ -3779,11 +3235,11 @@ // default options Options: { headers: { 'X-Requested-With': 'XMLHttpRequest', - 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*' + 'Accept': 'text/javascript,text/html,application/xml,text/xml,*/*' }, method: 'post', encoding: 'utf-8', async: true, evalScripts: false, @@ -3791,11 +3247,12 @@ evalJSON: true, secureJSON: true, urlEncoded: true, spinner: null, spinnerFx: 'fade', - params: null + params: null, + iframed: false }, /** * Shortcut to initiate and send an XHR in a single call * @@ -3864,25 +3321,24 @@ * * @param Object options * @return Xhr self */ send: function(params) { - var add_params = {}, url = this.url; + var add_params = {}, url = this.url, method = this.method.toLowerCase(); - var method = this.method.toUpperCase(); - if (['PUT', 'DELETE'].includes(method)) { - add_params['_method'] = method.toLowerCase(); - method = 'POST'; + if (method == 'put' || method == 'delete') { + add_params['_method'] = method; + method = 'post'; } var data = this.prepareData(this.params, this.prepareParams(params), add_params); - if (this.urlEncoded && method == 'POST' && !this.headers['Content-type']) { - this.setHeader('Content-type', 'application/x-www-form-urlencoded; charset='+this.encoding); + if (this.urlEncoded && method == 'post' && !this.headers['Content-type']) { + this.setHeader('Content-type', 'application/x-www-form-urlencoded;charset='+this.encoding); } - if (method == 'GET') { + if (method == 'get') { url += (url.includes('?') ? '&' : '?') + data; data = null; } this.xhr = this.createXhr(); @@ -4005,16 +3461,13 @@ return eval("("+this.text+")"); }, // initializes the request callbacks initCallbacks: function() { - // creating an automatical spinner handling - this.on('create', 'showSpinner').on('complete', 'hideSpinner').on('cancel', 'hideSpinner'); + // connecting basic callbacks + this.on('success', 'tryScripts').on('create', 'showSpinner').on('complete', 'hideSpinner').on('cancel', 'hideSpinner'); - // response scripts evaluation, should be before the global xhr callbacks - this.on('success', 'tryScripts'); - // wiring the global xhr callbacks Xhr.EVENTS.each(function(name) { this.on(name, function() { Xhr.fire(name, this, this.xhr); }); }, this); }, @@ -4044,17 +3497,17 @@ hideSpinner: function() { if (this.Options.spinner) $(this.Options.spinner).hide(this.Options.spinnerFx, {duration: 100}); } }); -Xhr.on('create', function() { +Xhr.onCreate(function() { this.counter++; this.showSpinner(); -}).on('complete', function() { +}).onComplete(function() { this.counter--; if (this.counter < 1) this.hideSpinner(); -}).on('cancel', function() { +}).onCancel(function() { this.counter--; if (this.counter < 1) this.hideSpinner(); }); @@ -4213,11 +3666,11 @@ 'long': 800 }, // default options Options: { - fps: Browser.IE ? 40 : 60, + fps: Browser.IE || Browser.Opera ? 35 : 60, duration: 'normal', transition: 'Sin', queue: true }, @@ -4250,11 +3703,11 @@ * * @param Object options */ initialize: function(element, options) { this.$super(options); - this.element = $(element); + this.element = this.fxee = $(element); }, /** * starts the transition * @@ -4262,12 +3715,13 @@ */ start: function() { if (this.queue(arguments)) return this; this.prepare.apply(this, arguments); - this.transition = Fx.Transitions[this.options.transition] || this.options.transition; - var duration = Fx.Durations[this.options.duration] || this.options.duration; + var options = this.options, + duration = Fx.Durations[options.duration] || options.duration; + this.transition = Fx.Transitions[options.transition] || options.transition; this.steps = (duration / 1000 * this.options.fps).ceil(); this.number = 1; return this.fire('start', this).startTimer(); @@ -4309,32 +3763,22 @@ return this.startTimer(); }, // protected // dummy method, should be implemented in a subclass - prepare: function() {}, + prepare: function(values) {}, - // dummy method, should implement the actual things happenning - render: function(value) {}, + // dummy method, processes the element properties + render: function(delta) {}, // the periodically called method // NOTE: called outside of the instance scope! - step: function($this) { - if ($this.steps >= $this.number) { - $this.render($this.transition($this.number / $this.steps)); - - $this.number ++; - } else { - $this.finish(); - } + step: function(that) { + if (that.number > that.steps) that.finish(); + else that.render(that.transition(that.number ++ / that.steps)); }, - - // calculates the current value - calc: function(start, end, delata) { - return start + (end - start) * delta; - }, - + startTimer: function() { this.timer = this.step.periodical((1000 / this.options.fps).round(), this); return this; }, @@ -4347,33 +3791,29 @@ // 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.$chained) { - delete(this['$chained']); - return false; - } + if (this.$ch) return this.$ch = false; var uid = $uid(this.element), chain; - if (!Fx.$chains) Fx.$chains = {}; - if (!Fx.$chains[uid]) Fx.$chains[uid] = []; - chain = Fx.$chains[uid]; + Fx.$ch = Fx.$ch || []; + chain = (Fx.$ch[uid] = Fx.$ch[uid] || []); if (this.options.queue) chain.push([args, this]); this.next = function() { var next = chain.shift(); next = chain[0]; if (next) { - next[1].$chained = true; + next[1].$ch = true; next[1].start.apply(next[1], next[0]); } return this; }; - return chain[0][1] !== this && this.options.queue; + return this.options.queue && chain[0][1] !== this; }, next: function() { return this; } @@ -4428,15 +3868,15 @@ * converts a #XXX or rgb(X, X, X) sring into standard #XXXXXX color string * * @return String hex color */ toHex: function() { - var match = this.match(/^#(\w)(\w)(\w)$/); + var match = /^#(\w)(\w)(\w)$/.exec(this); if (match) { match = "#"+ match[1]+match[1]+match[2]+match[2]+match[3]+match[3]; - } else if (match = this.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/)) { + } else if (match = /^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/.exec(this)) { match = "#"+ match.slice(1).map(function(bit) { bit = (bit-0).toString(16); return bit.length == 1 ? '0'+bit : bit; }).join(''); } else { @@ -4451,11 +3891,11 @@ * * @param boolean flag if need an array * @return String rgb(R,G,B) or Array [R,G,B] */ toRgb: function(array) { - var match = (this.toHex()||'').match(/#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})/i); + var match = /#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})/i.exec(this.toHex()||''); if (match) { match = match.slice(1).map('toInt', 16); match = array ? match : match.toRgb(); } @@ -4471,192 +3911,227 @@ * 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> */ -Fx.Morph = new Class(Fx, { - -// protected +Fx.Morph = new Class(Fx, (function() { + // a list of common style names to compact the code a bit + var Top = 'Top', Left = 'Left', Right = 'Right', Bottom = 'Bottom', Color = 'Color', Style = 'Style', + Width = 'Width', Bg = 'background', Border = 'border', Pos = 'Position', BgColor = Bg + Color, + BdStyle = Border + Style, BdColor = Border + Color, BdWidth = Border + Width; - /** - * starts the effect - * - * @param mixed an Object with an end style or a string with the end class-name(s) - * @return Fx this - */ + + // adds variants to the style names list + var add_variants = function(keys, key, variants) { + for (var i=0; i < variants.length; i++) + keys.push(key + variants[i]); + }; + + // parses the style hash into a processable format + var parse_style = function(values) { + var result = {}, re = /[\d\.\-]+/g, m; + + for (var key in values) { + m = values[key].match(re); + var value = m.map('toFloat'); + value.t = values[key].split(re); + if (/^\d/.test(values[key]) && value.t[0] !== '') value.t.unshift(''); + for (var i=0; i < value.length; i++) { + value.t.splice(i*2+1, 0, value[i]); + } + result[key] = value; + } + + return result; + }; + +return { + +// protected + + // parepares the effect prepare: function(style) { - this.endStyle = this._findStyle(style); - this.startStyle = this._getStyle(this.element, Object.keys(this.endStyle)); + var keys = this._styleKeys(style), + before = this._cloneStyle(this.element, keys), + after = this._endStyle(style, keys); - this._cleanStyles(); + this._cleanStyles(before, after); - return this.$super(); + this.before = parse_style(before); + this.after = parse_style(after); }, render: function(delta) { - var value, start, end; - - for (var key in this.endStyle) { - start = this.startStyle[key]; - end = this.endStyle[key]; - - if (typeof(start) == 'number') { - // handling floats like opacity - value = start + (end - start) * delta; - - } else if(start.length == 2) { - // handling usual sizes with dimensions - value = (start[0] + (end[0] - start[0]) * delta) + end[1]; - - } else if(start.length == 3) { - // calculating colors - value = end.map(function(value, i) { - return start[i] + (value - start[i]) * delta; - }).toRgb(); - } + var before, after, value, style = this.element.style; + for (var key in this.after) { + before = this.before[key]; + after = this.after[key]; - if (key == 'opacity') { - this.element.setOpacity(value); - } else { - this.element.style[key] = value; + for (var i=0; i < after.length; i++) { + value = before[i] + (after[i] - before[i]) * delta; + if (after.t[0] === 'rgb(') value = Math.round(value); + after.t[i*2+1] = ''+value; } + style[key] = after.t.join(''); } }, -// private - - // finds the style definition by a css-selector string - _findStyle: function(style) { - // a dummy node to calculate the end styles - var element = this._dummy().setStyle(style); + /** + * Returns a hash of the end style + * + * @param Object style + * @return Object end style + */ + _endStyle: function(style, keys) { + var parent = this.element.parentNode, + dummy = $(this.element.cloneNode(true)).setStyle(style); + + // swapping the element with the dummy and getting the new styles + if (parent) parent.replaceChild(dummy, this.element); + var after = this._cloneStyle(dummy, keys); + if (parent) parent.replaceChild(this.element, dummy); - // grabbing the computed styles - var element_styles = element.computedStyles(); - var this_element_styles = this.element.computedStyles(); + return after; + }, + + /** + * Fast styles cloning + * + * @param Element element + * @param Array style keys + * @return Hash of styles + */ + _cloneStyle: function(element, keys) { + for (var i=0, len = keys.length, style = element.computedStyles(), clean = {}; i < len; i++) + clean[keys[i]] = style[keys[i]]; - // grabbing the element style - var end_style = this._getStyle(element, Object.keys(style), element_styles); - - // assigning the border style if the end style has a border - var border_style = element_styles.borderTopStyle, element_border_style = this_element_styles.borderTopStyle; - if (border_style != element_border_style) { - if (element_border_style == 'none') { - this.element.style.borderWidth = '0px'; - } - this.element.style.borderStyle = border_style; - if (this._transp(this_element_styles.borderTopColor)) { - this.element.style.borderColor = this_element_styles.color; - } - } - - element.remove(); - - return end_style; + return clean; }, - // creates a dummy element to work with - _dummy: function() { - // a container for the styles extraction element - var container = Fx.Morph.$c = (Fx.Morph.$c || $E('div', {style: "visibility:hidden;float:left;height:0;width:0"})); - if (this.element.parentNode) this.element.parentNode.insertBefore(container, this.element); + /** + * creates an appropriate style-keys list out of the user styles + * + * @param Object the style hash + * @return Array of clean style keys list + */ + _styleKeys: function(style) { + var keys = [], border_types = [Style, Color, Width], directions = [Top, Left, Right, Bottom]; + + for (var key in style) { + if (key.startsWith(Border)) + for (var i=0; i < border_types.length; i++) + for (var 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']); + else if (key === 'opacity' && Browser.IE) + keys.push('filter'); + else + keys.push(key); + } - return $(this.element.cloneNode(false)).insertTo(container); + return keys; }, - // grabs computed styles with the given keys out of the element - _getStyle: function(element, keys, styles) { - var style = {}, styles = styles || element.computedStyles(), name; - if (isString(keys)) { name = keys, keys = [keys]; } + /** + * cleans up and optimizies the styles + * + * @param Object before + * @param Object after + * @return void + */ + _cleanStyles: function(before, after) { + var remove = []; - for (var i=0; i < keys.length; i++) { - var key = keys[i].camelize(); + for (var key in after) { + // getting directional options together so they were processed faster + if (key.includes(Top)) { + var top = key, + left = key.replace(Top, Left), + right = key.replace(Top, Right), + bottom = key.replace(Top, Bottom), + common = key.replace(Top, ''); + + if (after[top] === after[left] && after[top] === after[right] && after[top] === after[bottom] && + before[top] === before[left] && before[top] === before[right] && before[top] === before[bottom] + ) { + after[common] = after[top]; + before[common] = before[top]; + + remove = remove.concat([top, left, right, bottom]); + } + } - // keys preprocessing - if (key == 'background') key = 'backgroundColor'; - else if (key == 'border') { - key = 'borderWidth'; - keys.splice(i+1, 0, 'borderColor'); // inserting the border color as the next unit + // checking the height/width options + if (key === Width || key === 'height') { + if (before[key] == 'auto') before[key] = this.element['offset'+key.capitalize()] + 'px'; } + } + + // IE opacity filter fix + if (after.filter && !before.filter) before.filter = 'alpha(opacity=100)'; + + // adjusting the border style + if (before[BdStyle] != after[BdStyle]) { + var style = this.element.style; - // getting the actual style - style[key] = element._getStyle(styles, key); + if (before[BdStyle] == 'none') { + style[BdWidth] = '0px'; + } - // Opera returns named colors as quoted strings - if (Browser.Opera && /color/i.test(key)) style[key] = style[key].replace(/'|"/g, ''); + style[BdStyle] = after[BdStyle]; + if (this._transp(before[BdColor])) { + style[BdColor] = this.element.getStyle(Color); + } + } + + // cleaing up the list + for (var 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, ''); + } + + if (!this._transp(after[key])) after[key] = after[key].toRgb(); + if (!this._transp(before[key])) before[key] = before[key].toRgb(); + + if (!after[key] || !before[key]) after[key] = before[key] = ''; + } - // getting the real color if it's a transparent - if (this._transp(style[key])) style[key] = this._getBGColor(element); + // filling up the missing sizes + if (/\d/.test(after[key]) && !/\d/.test(before[key])) before[key] = after[key].replace(/[\d\.\-]+/g, '0'); - // getting the real width and height if they not set or set as 'auto' - if (!style[key] || style[key] == 'auto') { - style[key] = key == 'width' ? element.offsetWidth + 'px' : - key == 'height' ? element.offsetHeight + 'px' : ''; + // removing unprocessable keys + if (after[key] === before[key] || remove.includes(key) || !/\d/.test(before[key]) || !/\d/.test(after[key])) { + delete(after[key]); + delete(before[key]); } } - - return name ? style[name] : style; }, // looking for the visible background color of the element _getBGColor: function(element) { return [element].concat(element.parents()).map(function(node) { - var bg = node.getStyle('backgroundColor'); + var bg = node.getStyle(BgColor); return (bg && !this._transp(bg)) ? bg : null; - }, this).compact().first() || 'rgb(255,255,255)'; + }, this).compact().first() || '#FFF'; }, - // prepares the style values to be processed correctly - _cleanStyles: function() { - var end = this.endStyle, start = this.startStyle; - - // filling up missing styles - for (var key in end) { - if (start[key] === '' && /^[\d\.\-]+[a-z]+$/.test(end[key])) { - start[key] = '0px'; - } - } - - [end, start].each(this._cleanStyle, this); - - // removing duplications between start and end styles - for (var key in end) { - if (!defined(start[key]) || (end[key] instanceof Array ? end[key].join() === start[key].join() : end[key] === start[key])) { - delete(end[key]); - delete(start[key]); - } - } - }, - // cleans up a style object - _cleanStyle: function(style) { - var match; - for (var key in style) { - style[key] = String(style[key]); - - if (/color/i.test(key)) { - // preparing the colors - style[key] = style[key].toRgb(true); - if (!style[key]) delete(style[key]); - } else if (/^[\d\.]+$/.test(style[key])) { - // preparing numberic values - style[key] = style[key].toFloat(); - } else if (match = style[key].match(/^([\d\.\-]+)([a-z]+)$/i)) { - // preparing values with dimensions - style[key] = [match[1].toFloat(), match[2]]; - - } else { - delete(style[key]); - } - } - }, - // checks if the color is transparent _transp: function(color) { - return color == 'transparent' || color == 'rgba(0, 0, 0, 0)'; + return color === 'transparent' || color === 'rgba(0, 0, 0, 0)'; } -}); + +}})()); + + /** * the elements hightlighting effect * * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> */ @@ -4751,19 +4226,19 @@ }, this); this.element.style.overflow = 'hidden'; this.onFinish('_getBack').onCancel('_getBack'); - return this.$super(this._endStyle(this.options.direction)); + return this.$super(this._getStyle(this.options.direction)); }, _getBack: function() { this.element.setStyle(this.styles); }, // calculates the final style - _endStyle: function(direction) { + _getStyle: function(direction) { var style = {}, sizes = this.sizes, margin_left = (this.styles.marginLeft || '0').toFloat(), margin_top = (this.styles.marginTop || '0').toFloat(); if (this.how == 'out') { @@ -4815,10 +4290,31 @@ return this.$super({opacity: typeof(how) == 'number' ? how : this.how == 'in' ? 1 : 0}); } }); /** + * A smooth scrolling visual effect + * + * Copyright (C) 2009 Nikolay V. Nemshilov aka St. + */ +Fx.Scroll = new Class(Fx, { + prepare: function(value) { + this.before = {}; + this.after = value; + + if (value.x != undefined) this.before.x = this.element.scrollLeft; + if (value.y != undefined) this.before.y = this.element.scrollTop; + }, + + render: function(delta) { + for (var key in this.after) { + this.element['scroll' + (key === 'x' ? 'Left' : 'Top')] = this.before[key] + (this.after[key] - this.before[key]) * delta; + } + } +}); + +/** * This block contains additional Element shortcuts for effects easy handling * * Credits: * Some ideas are inspired by * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti @@ -4826,11 +4322,11 @@ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om> */ Element.addMethods((function(methods) { var old_hide = methods.hide, old_show = methods.show, - old_resize = methods.resize; + old_scroll = methods.scrollTo; return { /** * hides the element with given visual effect @@ -4851,35 +4347,10 @@ show: function(fx, options) { return fx ? this.fx(fx, ['in', options], old_show) : old_show.call(this); }, /** - * resizes the element using the Morph visual effect - * - * @param Integer width - * @param Integer height - * @param Object options - */ - resize: function(width, height, options) { - if (isHash(width)) { - height = width.y; - width = width.x; - } - if (options) { - var style = {}; - if (isNumber(height)) style.height = height+'px'; - if (isNumber(width)) style.width = width +'px'; - - if (!isHash(options)) options = {duration: options}; - - return this.fx('morph', [style, options]); - } else { - return old_resize.call(this, width, height); - } - }, - - /** * runs the Fx.Morth effect to the given style * * @param Object style or a String class names * @param Object optional effect options * @return Element self @@ -4919,10 +4390,34 @@ */ slide: function() { return this.fx('slide', arguments); }, + /** + * Starts the smooth scrolling effect + * + * @param Object {x: NNN, y: NNN} where to scroll + * @param Object fx-options + * @return Element this + */ + scroll: function(value, options) { + return this.fx('scroll', [value, options||{}]); + }, + + /** + * wraps the old scroll to be able to run it with fxes + * + * If you send two hashes then will start a smooth scrolling + * otherwise will just jump over with the usual method + * + * @return Element this + */ + scrollTo: function(value, options) { + return isHash(options) ? this.scroll(value, options) : old_scroll.apply(this, arguments); + }, + + // protected // runs an Fx on the element fx: function(name, args, on_finish) { var args = $A(args).compact(), options = {}; @@ -4935,5 +4430,25 @@ return this; } }})(Element.Methods)); +/** + * The old browsers support patch loading script + * will be included in the core file when it's built + * with the no-olds option + * + * 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. + */ +if (!document.querySelector) { + (function() { + var rigth_src_re = /(\/right)([^\/]+)$/; + var 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>'); + })(); +} + \ No newline at end of file