Merge pull request #2789 from SergioCrisostomo/upgrade-karma-sauce-launcher
[mootools.git] / Source / Core / Core.js
blobc7bed355fa24b2a09be9f3d3d167b83db67356d5
1 /*
2 ---
4 name: Core
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/)
14 inspiration:
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]
20 ...
22 /*! MooTools: the javascript framework. license: MIT-style license. copyright: Copyright (c) 2006-2015 [Valerio Proietti](http://mad4milk.net/).*/
23 (function(){
25 this.MooTools = {
26         version: '1.6.1-dev',
27         build: '%build%'
30 // typeOf, instanceOf
32 var typeOf = this.typeOf = function(item){
33         if (item == null) return 'null';
34         if (item.$family != null) return item.$family();
36         if (item.nodeName){
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';
42         }
44         return typeof item;
47 var instanceOf = this.instanceOf = function(item, object){
48         if (item == null) return false;
49         var constructor = item.$constructor || item.constructor;
50         while (constructor){
51                 if (constructor === object) return true;
52                 constructor = constructor.parent;
53         }
54         /*<ltIE8>*/
55         if (!item.hasOwnProperty) return false;
56         /*</ltIE8>*/
57         return item instanceof object;
60 var hasOwnProperty = Object.prototype.hasOwnProperty;
62 /*<ltIE8>*/
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]);
72         }
74 /*</ltIE8>*/
76 // Function overloading
78 var Function = this.Function;
80 Function.prototype.overloadSetter = function(usePlural){
81         var self = this;
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]);
86                         /*<ltIE8>*/
87                         forEachObjectEnumberableKey(a, self, this);
88                         /*</ltIE8>*/
89                 } else {
90                         self.call(this, a, b);
91                 }
92                 return this;
93         };
96 Function.prototype.overloadGetter = function(usePlural){
97         var self = this;
98         return function(a){
99                 var args, result;
100                 if (typeof a != 'string') args = a;
101                 else if (arguments.length > 1) args = arguments;
102                 else if (usePlural) args = [a];
103                 if (args){
104                         result = {};
105                         for (var i = 0; i < args.length; i++) result[args[i]] = self.call(this, args[i]);
106                 } else {
107                         result = self.call(this, a);
108                 }
109                 return result;
110         };
113 Function.prototype.extend = function(key, value){
114         this[key] = value;
115 }.overloadSetter();
117 Function.prototype.implement = function(key, value){
118         this.prototype[key] = value;
119 }.overloadSetter();
121 // From
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(){
132                 return item;
133         };
137 Number.convert = function(item){
138         var number = parseFloat(item);
139         return isFinite(number) ? number : null;
142 String.convert = function(item){
143         return item + '';
146 /*<1.5compat>*/
147 Array.from = Array.convert;
148 /*</1.5compat>*/
150 Function.from = Function.convert;
151 Number.from = Number.convert;
152 String.from = String.convert;
154 // hide, protect
156 Function.implement({
158         hide: function(){
159                 this.$hidden = true;
160                 return this;
161         },
163         protect: function(){
164                 this.$protected = true;
165                 return this;
166         }
170 // Type
172 var Type = this.Type = function(name, object){
173         if (name){
174                 var lower = name.toLowerCase();
175                 var typeCheck = function(item){
176                         return (typeOf(item) == lower);
177                 };
179                 Type['is' + name] = typeCheck;
180                 if (object != null){
181                         object.prototype.$family = (function(){
182                                 return lower;
183                         }).hide();
184                         //<1.2compat>
185                         object.type = typeCheck;
186                         //</1.2compat>
187                 }
188         }
190         if (object == null) return null;
192         object.extend(this);
193         object.$constructor = Type;
194         object.prototype.$constructor = object;
196         return 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]' );
205 var hooks = {};
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++){
218                 var hook = hooks[i];
219                 if (typeOf(hook) == 'type') implement.call(hook, name, method);
220                 else hook.call(this, name, method);
221         }
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));
228         });
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;
237 Type.implement({
239         implement: implement.overloadSetter(),
241         extend: extend.overloadSetter(),
243         alias: function(name, existing){
244                 implement.call(this, name, this.prototype[existing]);
245         }.overloadSetter(),
247         mirror: function(hook){
248                 hooksOf(this).push(hook);
249                 return this;
250         }
254 new Type('Type', Type);
256 // Default Types
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());
271         }
273         if (isType){
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]);
278                         }
279                         for (var key in prototype) fn.call(prototype, prototype[key], key);
280                 };
281         }
283         return force;
286 force('String', String, [
287         'charAt', 'charCodeAt', 'concat', 'contains', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search',
288         'slice', 'split', 'substr', 'substring', 'trim', 'toLowerCase', 'toUpperCase'
289 ])('Array', Array, [
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, [
297         'exec', 'test'
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(){
307         return +(new Date);
310 new Type('Boolean', Boolean);
312 // fixes NaN returning as Number
314 Number.prototype.$family = function(){
315         return isFinite(this) ? 'number' : 'null';
316 }.hide();
318 // Number.random
320 Number.extend('random', function(min, max){
321         return Math.floor(Math.random() * (max - min + 1) + min);
324 // forEach, each, keys
326 Array.implement({
328         /*<!ES5>*/
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);
332                 }
333         },
334         /*</!ES5>*/
336         each: function(fn, bind){
337                 Array.forEach(this, fn, bind);
338                 return this;
339         }
343 Object.extend({
345         keys: function(object){
346                 var keys = [];
347                 for (var k in object){
348                         if (hasOwnProperty.call(object, k)) keys.push(k);
349                 }
350                 /*<ltIE8>*/
351                 forEachObjectEnumberableKey(object, function(k){
352                         keys.push(k);
353                 });
354                 /*</ltIE8>*/
355                 return keys;
356         },
358         forEach: function(object, fn, bind){
359                 Object.keys(object).forEach(function(key){
360                         fn.call(bind, object[key], key, object);
361                 });
362         }
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;
376         }
379 Array.implement('clone', function(){
380         var i = this.length, clone = new Array(i);
381         while (i--) clone[i] = cloneOf(this[i]);
382         return clone;
385 var mergeOne = function(source, key, current){
386         switch (typeOf(current)){
387                 case 'object':
388                         if (typeOf(source[key]) == 'object') Object.merge(source[key], current);
389                         else source[key] = Object.clone(current);
390                         break;
391                 case 'array': source[key] = current.clone(); break;
392                 default: source[key] = current;
393         }
394         return source;
397 Object.extend({
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]);
404                 }
405                 return source;
406         },
408         clone: function(object){
409                 var clone = {};
410                 for (var key in object) clone[key] = cloneOf(object[key]);
411                 return clone;
412         },
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];
418                 }
419                 return original;
420         }
424 // Object-less types
426 ['Object', 'WhiteSpace', 'TextNode', 'Collection', 'Arguments'].each(function(name){
427         new Type(name);
430 // Unique ID
432 var UID = Date.now();
434 String.extend('uniqueID', function(){
435         return (UID++).toString(36);
438 //<1.2compat>
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];
443         return this;
446 Hash.implement({
448         forEach: function(fn, bind){
449                 Object.forEach(this, fn, bind);
450         },
452         getClean: function(){
453                 var clean = {};
454                 for (var key in this){
455                         if (this.hasOwnProperty(key)) clean[key] = this[key];
456                 }
457                 return clean;
458         },
460         getLength: function(){
461                 var length = 0;
462                 for (var key in this){
463                         if (this.hasOwnProperty(key)) length++;
464                 }
465                 return length;
466         }
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);
482         return Native;
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){
495         return function(){
496                 return arguments[i];
497         };
500 this.$chk = function(obj){
501         return !!(obj || obj === 0);
504 this.$clear = function(timer){
505         clearTimeout(timer);
506         clearInterval(timer);
507         return null;
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);
531         args.unshift({});
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;
553         }
556 //</1.2compat>
558 })();