6 description: The heart of MooTools.
8 license: MIT-style license.
10 copyright: Copyright (c) 2006-2015 [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-2015 [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 Array.convert = function(item){
126 if (item == null) return [];
127 return (Type.isEnumerable(item) && typeof item != 'string') ? (typeOf(item) == 'array') ? item : slice.call(item) : [item];
130 Function.convert = function(item){
131 return (typeOf(item) == 'function') ? item : function(){
137 Number.convert = function(item){
138 var number = parseFloat(item);
139 return isFinite(number) ? number : null;
142 String.convert = function(item){
147 Array.from = Array.convert;
150 Function.from = Function.convert;
151 Number.from = Number.convert;
152 String.from = String.convert;
164 this.$protected = true;
172 var Type = this.Type = function(name, object){
174 var lower = name.toLowerCase();
175 var typeCheck = function(item){
176 return (typeOf(item) == lower);
179 Type['is' + name] = typeCheck;
181 object.prototype.$family = (function(){
185 object.type = typeCheck;
190 if (object == null) return null;
193 object.$constructor = Type;
194 object.prototype.$constructor = object;
199 var toString = Object.prototype.toString;
201 Type.isEnumerable = function(item){
202 return (item != null && typeof item.length == 'number' && toString.call(item) != '[object Function]' );
207 var hooksOf = function(object){
208 var type = typeOf(object.prototype);
209 return hooks[type] || (hooks[type] = []);
212 var implement = function(name, method){
213 if (method && method.$hidden) return;
215 var hooks = hooksOf(this);
217 for (var i = 0; i < hooks.length; i++){
219 if (typeOf(hook) == 'type') implement.call(hook, name, method);
220 else hook.call(this, name, method);
223 var previous = this.prototype[name];
224 if (previous == null || !previous.$protected) this.prototype[name] = method;
226 if (this[name] == null && typeOf(method) == 'function') extend.call(this, name, function(item){
227 return method.apply(item, slice.call(arguments, 1));
231 var extend = function(name, method){
232 if (method && method.$hidden) return;
233 var previous = this[name];
234 if (previous == null || !previous.$protected) this[name] = method;
239 implement: implement.overloadSetter(),
241 extend: extend.overloadSetter(),
243 alias: function(name, existing){
244 implement.call(this, name, this.prototype[existing]);
247 mirror: function(hook){
248 hooksOf(this).push(hook);
254 new Type('Type', Type);
258 var force = function(name, object, methods){
259 var isType = (object != Object),
260 prototype = object.prototype;
262 if (isType) object = new Type(name, object);
264 for (var i = 0, l = methods.length; i < l; i++){
265 var key = methods[i],
266 generic = object[key],
267 proto = prototype[key];
269 if (generic) generic.protect();
270 if (isType && proto) object.implement(key, proto.protect());
274 var methodsEnumerable = prototype.propertyIsEnumerable(methods[0]);
275 object.forEachMethod = function(fn){
276 if (!methodsEnumerable) for (var i = 0, l = methods.length; i < l; i++){
277 fn.call(prototype, prototype[methods[i]], methods[i]);
279 for (var key in prototype) fn.call(prototype, prototype[key], key);
286 force('String', String, [
287 'charAt', 'charCodeAt', 'concat', 'contains', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search',
288 'slice', 'split', 'substr', 'substring', 'trim', 'toLowerCase', 'toUpperCase'
290 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice',
291 'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight', 'contains'
292 ])('Number', Number, [
293 'toExponential', 'toFixed', 'toLocaleString', 'toPrecision'
294 ])('Function', Function, [
295 'apply', 'call', 'bind'
296 ])('RegExp', RegExp, [
298 ])('Object', Object, [
299 'create', 'defineProperty', 'defineProperties', 'keys',
300 'getPrototypeOf', 'getOwnPropertyDescriptor', 'getOwnPropertyNames',
301 'preventExtensions', 'isExtensible', 'seal', 'isSealed', 'freeze', 'isFrozen'
302 ])('Date', Date, ['now']);
304 Object.extend = extend.overloadSetter();
306 Date.extend('now', function(){
310 new Type('Boolean', Boolean);
312 // fixes NaN returning as Number
314 Number.prototype.$family = function(){
315 return isFinite(this) ? 'number' : 'null';
320 Number.extend('random', function(min, max){
321 return Math.floor(Math.random() * (max - min + 1) + min);
324 // forEach, each, keys
329 forEach: function(fn, bind){
330 for (var i = 0, l = this.length; i < l; i++){
331 if (i in this) fn.call(bind, this[i], i, this);
336 each: function(fn, bind){
337 Array.forEach(this, fn, bind);
345 keys: function(object){
347 for (var k in object){
348 if (hasOwnProperty.call(object, k)) keys.push(k);
351 forEachObjectEnumberableKey(object, function(k){
358 forEach: function(object, fn, bind){
359 Object.keys(object).forEach(function(key){
360 fn.call(bind, object[key], key, object);
366 Object.each = Object.forEach;
369 // Array & Object cloning, Object merging and appending
371 var cloneOf = function(item){
372 switch (typeOf(item)){
373 case 'array': return item.clone();
374 case 'object': return Object.clone(item);
375 default: return item;
379 Array.implement('clone', function(){
380 var i = this.length, clone = new Array(i);
381 while (i--) clone[i] = cloneOf(this[i]);
385 var mergeOne = function(source, key, current){
386 switch (typeOf(current)){
388 if (typeOf(source[key]) == 'object') Object.merge(source[key], current);
389 else source[key] = Object.clone(current);
391 case 'array': source[key] = current.clone(); break;
392 default: source[key] = current;
399 merge: function(source, k, v){
400 if (typeOf(k) == 'string') return mergeOne(source, k, v);
401 for (var i = 1, l = arguments.length; i < l; i++){
402 var object = arguments[i];
403 for (var key in object) mergeOne(source, key, object[key]);
408 clone: function(object){
410 for (var key in object) clone[key] = cloneOf(object[key]);
414 append: function(original){
415 for (var i = 1, l = arguments.length; i < l; i++){
416 var extended = arguments[i] || {};
417 for (var key in extended) original[key] = extended[key];
426 ['Object', 'WhiteSpace', 'TextNode', 'Collection', 'Arguments'].each(function(name){
432 var UID = Date.now();
434 String.extend('uniqueID', function(){
435 return (UID++).toString(36);
440 var Hash = this.Hash = new Type('Hash', function(object){
441 if (typeOf(object) == 'hash') object = Object.clone(object.getClean());
442 for (var key in object) this[key] = object[key];
448 forEach: function(fn, bind){
449 Object.forEach(this, fn, bind);
452 getClean: function(){
454 for (var key in this){
455 if (this.hasOwnProperty(key)) clean[key] = this[key];
460 getLength: function(){
462 for (var key in this){
463 if (this.hasOwnProperty(key)) length++;
470 Hash.alias('each', 'forEach');
472 Object.type = Type.isObject;
474 var Native = this.Native = function(properties){
475 return new Type(properties.name, properties.initialize);
478 Native.type = Type.type;
480 Native.implement = function(objects, methods){
481 for (var i = 0; i < objects.length; i++) objects[i].implement(methods);
485 var arrayType = Array.type;
486 Array.type = function(item){
487 return instanceOf(item, Array) || arrayType(item);
490 this.$A = function(item){
491 return Array.convert(item).slice();
494 this.$arguments = function(i){
500 this.$chk = function(obj){
501 return !!(obj || obj === 0);
504 this.$clear = function(timer){
506 clearInterval(timer);
510 this.$defined = function(obj){
511 return (obj != null);
514 this.$each = function(iterable, fn, bind){
515 var type = typeOf(iterable);
516 ((type == 'arguments' || type == 'collection' || type == 'array' || type == 'elements') ? Array : Object).each(iterable, fn, bind);
519 this.$empty = function(){};
521 this.$extend = function(original, extended){
522 return Object.append(original, extended);
525 this.$H = function(object){
526 return new Hash(object);
529 this.$merge = function(){
530 var args = Array.slice(arguments);
532 return Object.merge.apply(null, args);
535 this.$lambda = Function.convert;
536 this.$mixin = Object.merge;
537 this.$random = Number.random;
538 this.$splat = Array.convert;
539 this.$time = Date.now;
541 this.$type = function(object){
542 var type = typeOf(object);
543 if (type == 'elements') return 'array';
544 return (type == 'null') ? false : type;
547 this.$unlink = function(object){
548 switch (typeOf(object)){
549 case 'object': return Object.clone(object);
550 case 'array': return Array.clone(object);
551 case 'hash': return new Hash(object);
552 default: return object;