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;
61 var enumerables = true;
62 for (var i in {toString: 1}) enumerables = null;
63 if (enumerables) enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'constructor'];
65 var hasOwnProperty = Object.prototype.hasOwnProperty;
66 function objectKeys(object, alsoPrototypeChain){
68 for (var k in object){
69 if (alsoPrototypeChain) keys.push(k);
70 else hasOwnProperty.call(object, k) && keys.push(k);
73 if (enumerables) for (var i = enumerables.length; i--;){
75 if (hasOwnProperty.call(object, k)) keys.push(k);
81 // Function overloading
83 var Function = this.Function;
85 Function.prototype.overloadSetter = function(usePlural){
87 return function(a, b){
88 if (a == null) return this;
89 if (usePlural || typeof a != 'string'){
90 var keys = objectKeys(a, true), k;
91 for (var i = 0; k = keys[i]; i++) self.call(this, k, a[k]);
93 self.call(this, a, b);
99 Function.prototype.overloadGetter = function(usePlural){
103 if (typeof a != 'string') args = a;
104 else if (arguments.length > 1) args = arguments;
105 else if (usePlural) args = [a];
108 for (var i = 0; i < args.length; i++) result[args[i]] = self.call(this, args[i]);
110 result = self.call(this, a);
116 Function.prototype.extend = function(key, value){
120 Function.prototype.implement = function(key, value){
121 this.prototype[key] = value;
126 var slice = Array.prototype.slice;
128 Function.from = function(item){
129 return (typeOf(item) == 'function') ? item : function(){
134 Array.from = function(item){
135 if (item == null) return [];
136 return (Type.isEnumerable(item) && typeof item != 'string') ? (typeOf(item) == 'array') ? item : slice.call(item) : [item];
139 Number.from = function(item){
140 var number = parseFloat(item);
141 return isFinite(number) ? number : null;
144 String.from = function(item){
158 this.$protected = true;
166 var Type = this.Type = function(name, object){
168 var lower = name.toLowerCase();
169 var typeCheck = function(item){
170 return (typeOf(item) == lower);
173 Type['is' + name] = typeCheck;
175 object.prototype.$family = (function(){
179 object.type = typeCheck;
184 if (object == null) return null;
187 object.$constructor = Type;
188 object.prototype.$constructor = object;
193 var toString = Object.prototype.toString;
195 Type.isEnumerable = function(item){
196 return (item != null && typeof item.length == 'number' && toString.call(item) != '[object Function]' );
201 var hooksOf = function(object){
202 var type = typeOf(object.prototype);
203 return hooks[type] || (hooks[type] = []);
206 var implement = function(name, method){
207 if (method && method.$hidden) return;
209 var hooks = hooksOf(this);
211 for (var i = 0; i < hooks.length; i++){
213 if (typeOf(hook) == 'type') implement.call(hook, name, method);
214 else hook.call(this, name, method);
217 var previous = this.prototype[name];
218 if (previous == null || !previous.$protected) this.prototype[name] = method;
220 if (this[name] == null && typeOf(method) == 'function') extend.call(this, name, function(item){
221 return method.apply(item, slice.call(arguments, 1));
225 var extend = function(name, method){
226 if (method && method.$hidden) return;
227 var previous = this[name];
228 if (previous == null || !previous.$protected) this[name] = method;
233 implement: implement.overloadSetter(),
235 extend: extend.overloadSetter(),
237 alias: function(name, existing){
238 implement.call(this, name, this.prototype[existing]);
241 mirror: function(hook){
242 hooksOf(this).push(hook);
248 new Type('Type', Type);
252 var force = function(name, object, methods){
253 var isType = (object != Object),
254 prototype = object.prototype;
256 if (isType) object = new Type(name, object);
258 for (var i = 0, l = methods.length; i < l; i++){
259 var key = methods[i],
260 generic = object[key],
261 proto = prototype[key];
263 if (generic) generic.protect();
264 if (isType && proto) object.implement(key, proto.protect());
268 var methodsEnumerable = prototype.propertyIsEnumerable(methods[0]);
269 object.forEachMethod = function(fn){
270 if (!methodsEnumerable) for (var i = 0, l = methods.length; i < l; i++){
271 fn.call(prototype, prototype[methods[i]], methods[i]);
273 for (var key in prototype) fn.call(prototype, prototype[key], key);
280 force('String', String, [
281 'charAt', 'charCodeAt', 'concat', 'contains', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search',
282 'slice', 'split', 'substr', 'substring', 'trim', 'toLowerCase', 'toUpperCase'
284 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice',
285 'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight', 'contains'
286 ])('Number', Number, [
287 'toExponential', 'toFixed', 'toLocaleString', 'toPrecision'
288 ])('Function', Function, [
289 'apply', 'call', 'bind'
290 ])('RegExp', RegExp, [
292 ])('Object', Object, [
293 'create', 'defineProperty', 'defineProperties', 'keys',
294 'getPrototypeOf', 'getOwnPropertyDescriptor', 'getOwnPropertyNames',
295 'preventExtensions', 'isExtensible', 'seal', 'isSealed', 'freeze', 'isFrozen'
296 ])('Date', Date, ['now']);
298 Object.extend = extend.overloadSetter();
300 Date.extend('now', function(){
304 new Type('Boolean', Boolean);
306 // fixes NaN returning as Number
308 Number.prototype.$family = function(){
309 return isFinite(this) ? 'number' : 'null';
314 Number.extend('random', function(min, max){
315 return Math.floor(Math.random() * (max - min + 1) + min);
318 // forEach, each, keys
323 forEach: function(fn, bind){
324 for (var i = 0, l = this.length; i < l; i++){
325 if (i in this) fn.call(bind, this[i], i, this);
330 each: function(fn, bind){
331 Array.forEach(this, fn, bind);
341 forEach: function(object, fn, bind){
342 Object.keys(object).forEach(function(key){
343 fn.call(bind, object[key], key, object);
349 Object.each = Object.forEach;
352 // Array & Object cloning, Object merging and appending
354 var cloneOf = function(item){
355 switch (typeOf(item)){
356 case 'array': return item.clone();
357 case 'object': return Object.clone(item);
358 default: return item;
362 Array.implement('clone', function(){
363 var i = this.length, clone = new Array(i);
364 while (i--) clone[i] = cloneOf(this[i]);
368 var mergeOne = function(source, key, current){
369 switch (typeOf(current)){
371 if (typeOf(source[key]) == 'object') Object.merge(source[key], current);
372 else source[key] = Object.clone(current);
374 case 'array': source[key] = current.clone(); break;
375 default: source[key] = current;
382 merge: function(source, k, v){
383 if (typeOf(k) == 'string') return mergeOne(source, k, v);
384 for (var i = 1, l = arguments.length; i < l; i++){
385 var object = arguments[i];
386 for (var key in object) mergeOne(source, key, object[key]);
391 clone: function(object){
393 for (var key in object) clone[key] = cloneOf(object[key]);
397 append: function(original){
398 for (var i = 1, l = arguments.length; i < l; i++){
399 var extended = arguments[i] || {};
400 for (var key in extended) original[key] = extended[key];
409 ['Object', 'WhiteSpace', 'TextNode', 'Collection', 'Arguments'].each(function(name){
415 var UID = Date.now();
417 String.extend('uniqueID', function(){
418 return (UID++).toString(36);
423 var Hash = this.Hash = new Type('Hash', function(object){
424 if (typeOf(object) == 'hash') object = Object.clone(object.getClean());
425 for (var key in object) this[key] = object[key];
431 forEach: function(fn, bind){
432 Object.forEach(this, fn, bind);
435 getClean: function(){
437 for (var key in this){
438 if (this.hasOwnProperty(key)) clean[key] = this[key];
443 getLength: function(){
445 for (var key in this){
446 if (this.hasOwnProperty(key)) length++;
453 Hash.alias('each', 'forEach');
455 Object.type = Type.isObject;
457 var Native = this.Native = function(properties){
458 return new Type(properties.name, properties.initialize);
461 Native.type = Type.type;
463 Native.implement = function(objects, methods){
464 for (var i = 0; i < objects.length; i++) objects[i].implement(methods);
468 var arrayType = Array.type;
469 Array.type = function(item){
470 return instanceOf(item, Array) || arrayType(item);
473 this.$A = function(item){
474 return Array.from(item).slice();
477 this.$arguments = function(i){
483 this.$chk = function(obj){
484 return !!(obj || obj === 0);
487 this.$clear = function(timer){
489 clearInterval(timer);
493 this.$defined = function(obj){
494 return (obj != null);
497 this.$each = function(iterable, fn, bind){
498 var type = typeOf(iterable);
499 ((type == 'arguments' || type == 'collection' || type == 'array' || type == 'elements') ? Array : Object).each(iterable, fn, bind);
502 this.$empty = function(){};
504 this.$extend = function(original, extended){
505 return Object.append(original, extended);
508 this.$H = function(object){
509 return new Hash(object);
512 this.$merge = function(){
513 var args = Array.slice(arguments);
515 return Object.merge.apply(null, args);
518 this.$lambda = Function.from;
519 this.$mixin = Object.merge;
520 this.$random = Number.random;
521 this.$splat = Array.from;
522 this.$time = Date.now;
524 this.$type = function(object){
525 var type = typeOf(object);
526 if (type == 'elements') return 'array';
527 return (type == 'null') ? false : type;
530 this.$unlink = function(object){
531 switch (typeOf(object)){
532 case 'object': return Object.clone(object);
533 case 'array': return Array.clone(object);
534 case 'hash': return new Hash(object);
535 default: return object;