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 // Function overloading
62 var Function = this.Function;
64 var enumerables = true;
65 for (var i in {toString: 1}) enumerables = null;
66 if (enumerables) enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'constructor'];
68 Function.prototype.overloadSetter = function(usePlural){
70 return function(a, b){
71 if (a == null) return this;
72 if (usePlural || typeof a != 'string'){
73 for (var k in a) self.call(this, k, a[k]);
74 if (enumerables) for (var i = enumerables.length; i--;){
76 if (a.hasOwnProperty(k)) self.call(this, k, a[k]);
79 self.call(this, a, b);
85 Function.prototype.overloadGetter = function(usePlural){
89 if (typeof a != 'string') args = a;
90 else if (arguments.length > 1) args = arguments;
91 else if (usePlural) args = [a];
94 for (var i = 0; i < args.length; i++) result[args[i]] = self.call(this, args[i]);
96 result = self.call(this, a);
102 Function.prototype.extend = function(key, value){
106 Function.prototype.implement = function(key, value){
107 this.prototype[key] = value;
112 var slice = Array.prototype.slice;
114 Function.from = function(item){
115 return (typeOf(item) == 'function') ? item : function(){
120 Array.from = function(item){
121 if (item == null) return [];
122 return (Type.isEnumerable(item) && typeof item != 'string') ? (typeOf(item) == 'array') ? item : slice.call(item) : [item];
125 Number.from = function(item){
126 var number = parseFloat(item);
127 return isFinite(number) ? number : null;
130 String.from = function(item){
144 this.$protected = true;
152 var Type = this.Type = function(name, object){
154 var lower = name.toLowerCase();
155 var typeCheck = function(item){
156 return (typeOf(item) == lower);
159 Type['is' + name] = typeCheck;
161 object.prototype.$family = (function(){
165 object.type = typeCheck;
170 if (object == null) return null;
173 object.$constructor = Type;
174 object.prototype.$constructor = object;
179 var toString = Object.prototype.toString;
181 Type.isEnumerable = function(item){
182 return (item != null && typeof item.length == 'number' && toString.call(item) != '[object Function]' );
187 var hooksOf = function(object){
188 var type = typeOf(object.prototype);
189 return hooks[type] || (hooks[type] = []);
192 var implement = function(name, method){
193 if (method && method.$hidden) return;
195 var hooks = hooksOf(this);
197 for (var i = 0; i < hooks.length; i++){
199 if (typeOf(hook) == 'type') implement.call(hook, name, method);
200 else hook.call(this, name, method);
203 var previous = this.prototype[name];
204 if (previous == null || !previous.$protected) this.prototype[name] = method;
206 if (this[name] == null && typeOf(method) == 'function') extend.call(this, name, function(item){
207 return method.apply(item, slice.call(arguments, 1));
211 var extend = function(name, method){
212 if (method && method.$hidden) return;
213 var previous = this[name];
214 if (previous == null || !previous.$protected) this[name] = method;
219 implement: implement.overloadSetter(),
221 extend: extend.overloadSetter(),
223 alias: function(name, existing){
224 implement.call(this, name, this.prototype[existing]);
227 mirror: function(hook){
228 hooksOf(this).push(hook);
234 new Type('Type', Type);
238 var force = function(name, object, methods){
239 var isType = (object != Object),
240 prototype = object.prototype;
242 if (isType) object = new Type(name, object);
244 for (var i = 0, l = methods.length; i < l; i++){
245 var key = methods[i],
246 generic = object[key],
247 proto = prototype[key];
249 if (generic) generic.protect();
250 if (isType && proto) object.implement(key, proto.protect());
254 var methodsEnumerable = prototype.propertyIsEnumerable(methods[0]);
255 object.forEachMethod = function(fn){
256 if (!methodsEnumerable) for (var i = 0, l = methods.length; i < l; i++){
257 fn.call(prototype, prototype[methods[i]], methods[i]);
259 for (var key in prototype) fn.call(prototype, prototype[key], key);
266 force('String', String, [
267 'charAt', 'charCodeAt', 'concat', 'contains', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search',
268 'slice', 'split', 'substr', 'substring', 'trim', 'toLowerCase', 'toUpperCase'
270 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice',
271 'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight'
272 ])('Number', Number, [
273 'toExponential', 'toFixed', 'toLocaleString', 'toPrecision'
274 ])('Function', Function, [
275 'apply', 'call', 'bind'
276 ])('RegExp', RegExp, [
278 ])('Object', Object, [
279 'create', 'defineProperty', 'defineProperties', 'keys',
280 'getPrototypeOf', 'getOwnPropertyDescriptor', 'getOwnPropertyNames',
281 'preventExtensions', 'isExtensible', 'seal', 'isSealed', 'freeze', 'isFrozen'
282 ])('Date', Date, ['now']);
284 Object.extend = extend.overloadSetter();
286 Date.extend('now', function(){
290 new Type('Boolean', Boolean);
292 // fixes NaN returning as Number
294 Number.prototype.$family = function(){
295 return isFinite(this) ? 'number' : 'null';
300 Number.extend('random', function(min, max){
301 return Math.floor(Math.random() * (max - min + 1) + min);
306 var hasOwnProperty = Object.prototype.hasOwnProperty;
307 Object.extend('forEach', function(object, fn, bind){
308 for (var key in object){
309 if (hasOwnProperty.call(object, key)) fn.call(bind, object[key], key, object);
313 Object.each = Object.forEach;
318 forEach: function(fn, bind){
319 for (var i = 0, l = this.length; i < l; i++){
320 if (i in this) fn.call(bind, this[i], i, this);
325 each: function(fn, bind){
326 Array.forEach(this, fn, bind);
332 // Array & Object cloning, Object merging and appending
334 var cloneOf = function(item){
335 switch (typeOf(item)){
336 case 'array': return item.clone();
337 case 'object': return Object.clone(item);
338 default: return item;
342 Array.implement('clone', function(){
343 var i = this.length, clone = new Array(i);
344 while (i--) clone[i] = cloneOf(this[i]);
348 var mergeOne = function(source, key, current){
349 switch (typeOf(current)){
351 if (typeOf(source[key]) == 'object') Object.merge(source[key], current);
352 else source[key] = Object.clone(current);
354 case 'array': source[key] = current.clone(); break;
355 default: source[key] = current;
362 merge: function(source, k, v){
363 if (typeOf(k) == 'string') return mergeOne(source, k, v);
364 for (var i = 1, l = arguments.length; i < l; i++){
365 var object = arguments[i];
366 for (var key in object) mergeOne(source, key, object[key]);
371 clone: function(object){
373 for (var key in object) clone[key] = cloneOf(object[key]);
377 append: function(original){
378 for (var i = 1, l = arguments.length; i < l; i++){
379 var extended = arguments[i] || {};
380 for (var key in extended) original[key] = extended[key];
389 ['Object', 'WhiteSpace', 'TextNode', 'Collection', 'Arguments'].each(function(name){
395 var UID = Date.now();
397 String.extend('uniqueID', function(){
398 return (UID++).toString(36);
403 var Hash = this.Hash = new Type('Hash', function(object){
404 if (typeOf(object) == 'hash') object = Object.clone(object.getClean());
405 for (var key in object) this[key] = object[key];
411 forEach: function(fn, bind){
412 Object.forEach(this, fn, bind);
415 getClean: function(){
417 for (var key in this){
418 if (this.hasOwnProperty(key)) clean[key] = this[key];
423 getLength: function(){
425 for (var key in this){
426 if (this.hasOwnProperty(key)) length++;
433 Hash.alias('each', 'forEach');
435 Object.type = Type.isObject;
437 var Native = this.Native = function(properties){
438 return new Type(properties.name, properties.initialize);
441 Native.type = Type.type;
443 Native.implement = function(objects, methods){
444 for (var i = 0; i < objects.length; i++) objects[i].implement(methods);
448 var arrayType = Array.type;
449 Array.type = function(item){
450 return instanceOf(item, Array) || arrayType(item);
453 this.$A = function(item){
454 return Array.from(item).slice();
457 this.$arguments = function(i){
463 this.$chk = function(obj){
464 return !!(obj || obj === 0);
467 this.$clear = function(timer){
469 clearInterval(timer);
473 this.$defined = function(obj){
474 return (obj != null);
477 this.$each = function(iterable, fn, bind){
478 var type = typeOf(iterable);
479 ((type == 'arguments' || type == 'collection' || type == 'array' || type == 'elements') ? Array : Object).each(iterable, fn, bind);
482 this.$empty = function(){};
484 this.$extend = function(original, extended){
485 return Object.append(original, extended);
488 this.$H = function(object){
489 return new Hash(object);
492 this.$merge = function(){
493 var args = Array.slice(arguments);
495 return Object.merge.apply(null, args);
498 this.$lambda = Function.from;
499 this.$mixin = Object.merge;
500 this.$random = Number.random;
501 this.$splat = Array.from;
502 this.$time = Date.now;
504 this.$type = function(object){
505 var type = typeOf(object);
506 if (type == 'elements') return 'array';
507 return (type == 'null') ? false : type;
510 this.$unlink = function(object){
511 switch (typeOf(object)){
512 case 'object': return Object.clone(object);
513 case 'array': return Array.clone(object);
514 case 'hash': return new Hash(object);
515 default: return object;