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