6 description: The heart of MooTools.
8 license: MIT-style license.
10 copyright: Copyright (c) 2006-2014 [Valerio Proietti](http://mad4milk.net/).
12 authors: The MooTools production team (http://mootools.net/developers/)
15 - Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)
16 - Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php)
18 provides: [Core, MooTools, Type, typeOf, instanceOf, Native]
22 /*! MooTools: the javascript framework. license: MIT-style license. copyright: Copyright (c) 2006-2014 [Valerio Proietti](http://mad4milk.net/).*/
32 var typeOf = this.typeOf = function(item){
33 if (item == null) return 'null';
34 if (item.$family != null) return item.$family();
37 if (item.nodeType == 1) return 'element';
38 if (item.nodeType == 3) return (/\S/).test(item.nodeValue) ? 'textnode' : 'whitespace';
39 } else if (typeof item.length == 'number'){
40 if ('callee' in item) return 'arguments';
41 if ('item' in item) return 'collection';
47 var instanceOf = this.instanceOf = function(item, object){
48 if (item == null) return false;
49 var constructor = item.$constructor || item.constructor;
51 if (constructor === object) return true;
52 constructor = constructor.parent;
55 if (!item.hasOwnProperty) return false;
57 return item instanceof object;
60 var hasOwnProperty = Object.prototype.hasOwnProperty;
63 var enumerables = true;
64 for (var i in {toString: 1}) enumerables = null;
65 if (enumerables) enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'constructor'];
66 function forEachObjectEnumberableKey(object, fn, bind) {
67 if (enumerables) for (var i = enumerables.length; i--;){
68 var k = enumerables[i];
69 // signature has key-value, so overloadSetter can directly pass the
70 // method function, without swapping arguments.
71 if (hasOwnProperty.call(object, k)) fn.call(bind, k, object[k]);
76 // Function overloading
78 var Function = this.Function;
80 Function.prototype.overloadSetter = function(usePlural){
82 return function(a, b){
83 if (a == null) return this;
84 if (usePlural || typeof a != 'string'){
85 for (var k in a) self.call(this, k, a[k]);
87 forEachObjectEnumberableKey(a, self, this);
90 self.call(this, a, b);
96 Function.prototype.overloadGetter = function(usePlural){
100 if (typeof a != 'string') args = a;
101 else if (arguments.length > 1) args = arguments;
102 else if (usePlural) args = [a];
105 for (var i = 0; i < args.length; i++) result[args[i]] = self.call(this, args[i]);
107 result = self.call(this, a);
113 Function.prototype.extend = function(key, value){
117 Function.prototype.implement = function(key, value){
118 this.prototype[key] = value;
123 var slice = Array.prototype.slice;
125 Function.from = function(item){
126 return (typeOf(item) == 'function') ? item : function(){
131 Array.from = function(item){
132 if (item == null) return [];
133 return (Type.isEnumerable(item) && typeof item != 'string') ? (typeOf(item) == 'array') ? item : slice.call(item) : [item];
136 Number.from = function(item){
137 var number = parseFloat(item);
138 return isFinite(number) ? number : null;
141 String.from = function(item){
155 this.$protected = true;
163 var Type = this.Type = function(name, object){
165 var lower = name.toLowerCase();
166 var typeCheck = function(item){
167 return (typeOf(item) == lower);
170 Type['is' + name] = typeCheck;
172 object.prototype.$family = (function(){
176 object.type = typeCheck;
181 if (object == null) return null;
184 object.$constructor = Type;
185 object.prototype.$constructor = object;
190 var toString = Object.prototype.toString;
192 Type.isEnumerable = function(item){
193 return (item != null && typeof item.length == 'number' && toString.call(item) != '[object Function]' );
198 var hooksOf = function(object){
199 var type = typeOf(object.prototype);
200 return hooks[type] || (hooks[type] = []);
203 var implement = function(name, method){
204 if (method && method.$hidden) return;
206 var hooks = hooksOf(this);
208 for (var i = 0; i < hooks.length; i++){
210 if (typeOf(hook) == 'type') implement.call(hook, name, method);
211 else hook.call(this, name, method);
214 var previous = this.prototype[name];
215 if (previous == null || !previous.$protected) this.prototype[name] = method;
217 if (this[name] == null && typeOf(method) == 'function') extend.call(this, name, function(item){
218 return method.apply(item, slice.call(arguments, 1));
222 var extend = function(name, method){
223 if (method && method.$hidden) return;
224 var previous = this[name];
225 if (previous == null || !previous.$protected) this[name] = method;
230 implement: implement.overloadSetter(),
232 extend: extend.overloadSetter(),
234 alias: function(name, existing){
235 implement.call(this, name, this.prototype[existing]);
238 mirror: function(hook){
239 hooksOf(this).push(hook);
245 new Type('Type', Type);
249 var force = function(name, object, methods){
250 var isType = (object != Object),
251 prototype = object.prototype;
253 if (isType) object = new Type(name, object);
255 for (var i = 0, l = methods.length; i < l; i++){
256 var key = methods[i],
257 generic = object[key],
258 proto = prototype[key];
260 if (generic) generic.protect();
261 if (isType && proto) object.implement(key, proto.protect());
265 var methodsEnumerable = prototype.propertyIsEnumerable(methods[0]);
266 object.forEachMethod = function(fn){
267 if (!methodsEnumerable) for (var i = 0, l = methods.length; i < l; i++){
268 fn.call(prototype, prototype[methods[i]], methods[i]);
270 for (var key in prototype) fn.call(prototype, prototype[key], key);
277 force('String', String, [
278 'charAt', 'charCodeAt', 'concat', 'contains', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search',
279 'slice', 'split', 'substr', 'substring', 'trim', 'toLowerCase', 'toUpperCase'
281 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice',
282 'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight', 'contains'
283 ])('Number', Number, [
284 'toExponential', 'toFixed', 'toLocaleString', 'toPrecision'
285 ])('Function', Function, [
286 'apply', 'call', 'bind'
287 ])('RegExp', RegExp, [
289 ])('Object', Object, [
290 'create', 'defineProperty', 'defineProperties', 'keys',
291 'getPrototypeOf', 'getOwnPropertyDescriptor', 'getOwnPropertyNames',
292 'preventExtensions', 'isExtensible', 'seal', 'isSealed', 'freeze', 'isFrozen'
293 ])('Date', Date, ['now']);
295 Object.extend = extend.overloadSetter();
297 Date.extend('now', function(){
301 new Type('Boolean', Boolean);
303 // fixes NaN returning as Number
305 Number.prototype.$family = function(){
306 return isFinite(this) ? 'number' : 'null';
311 Number.extend('random', function(min, max){
312 return Math.floor(Math.random() * (max - min + 1) + min);
315 // forEach, each, keys
320 forEach: function(fn, bind){
321 for (var i = 0, l = this.length; i < l; i++){
322 if (i in this) fn.call(bind, this[i], i, this);
327 each: function(fn, bind){
328 Array.forEach(this, fn, bind);
336 keys: function(object){
339 if (hasOwnProperty.call(object, k)) keys.push(k);
342 forEachObjectEnumberableKey(object, function(k){
349 forEach: function(object, fn, bind){
350 Object.keys(object).forEach(function(key){
351 fn.call(bind, object[key], key, object);
357 Object.each = Object.forEach;
360 // Array & Object cloning, Object merging and appending
362 var cloneOf = function(item){
363 switch (typeOf(item)){
364 case 'array': return item.clone();
365 case 'object': return Object.clone(item);
366 default: return item;
370 Array.implement('clone', function(){
371 var i = this.length, clone = new Array(i);
372 while (i--) clone[i] = cloneOf(this[i]);
376 var mergeOne = function(source, key, current){
377 switch (typeOf(current)){
379 if (typeOf(source[key]) == 'object') Object.merge(source[key], current);
380 else source[key] = Object.clone(current);
382 case 'array': source[key] = current.clone(); break;
383 default: source[key] = current;
390 merge: function(source, k, v){
391 if (typeOf(k) == 'string') return mergeOne(source, k, v);
392 for (var i = 1, l = arguments.length; i < l; i++){
393 var object = arguments[i];
394 for (var key in object) mergeOne(source, key, object[key]);
399 clone: function(object){
401 for (var key in object) clone[key] = cloneOf(object[key]);
405 append: function(original){
406 for (var i = 1, l = arguments.length; i < l; i++){
407 var extended = arguments[i] || {};
408 for (var key in extended) original[key] = extended[key];
417 ['Object', 'WhiteSpace', 'TextNode', 'Collection', 'Arguments'].each(function(name){
423 var UID = Date.now();
425 String.extend('uniqueID', function(){
426 return (UID++).toString(36);
431 var Hash = this.Hash = new Type('Hash', function(object){
432 if (typeOf(object) == 'hash') object = Object.clone(object.getClean());
433 for (var key in object) this[key] = object[key];
439 forEach: function(fn, bind){
440 Object.forEach(this, fn, bind);
443 getClean: function(){
445 for (var key in this){
446 if (this.hasOwnProperty(key)) clean[key] = this[key];
451 getLength: function(){
453 for (var key in this){
454 if (this.hasOwnProperty(key)) length++;
461 Hash.alias('each', 'forEach');
463 Object.type = Type.isObject;
465 var Native = this.Native = function(properties){
466 return new Type(properties.name, properties.initialize);
469 Native.type = Type.type;
471 Native.implement = function(objects, methods){
472 for (var i = 0; i < objects.length; i++) objects[i].implement(methods);
476 var arrayType = Array.type;
477 Array.type = function(item){
478 return instanceOf(item, Array) || arrayType(item);
481 this.$A = function(item){
482 return Array.from(item).slice();
485 this.$arguments = function(i){
491 this.$chk = function(obj){
492 return !!(obj || obj === 0);
495 this.$clear = function(timer){
497 clearInterval(timer);
501 this.$defined = function(obj){
502 return (obj != null);
505 this.$each = function(iterable, fn, bind){
506 var type = typeOf(iterable);
507 ((type == 'arguments' || type == 'collection' || type == 'array' || type == 'elements') ? Array : Object).each(iterable, fn, bind);
510 this.$empty = function(){};
512 this.$extend = function(original, extended){
513 return Object.append(original, extended);
516 this.$H = function(object){
517 return new Hash(object);
520 this.$merge = function(){
521 var args = Array.slice(arguments);
523 return Object.merge.apply(null, args);
526 this.$lambda = Function.from;
527 this.$mixin = Object.merge;
528 this.$random = Number.random;
529 this.$splat = Array.from;
530 this.$time = Date.now;
532 this.$type = function(object){
533 var type = typeOf(object);
534 if (type == 'elements') return 'array';
535 return (type == 'null') ? false : type;
538 this.$unlink = function(object){
539 switch (typeOf(object)){
540 case 'object': return Object.clone(object);
541 case 'array': return Array.clone(object);
542 case 'hash': return new Hash(object);
543 default: return object;