Fixes #1116 - `select.get('value')` on select elements where the <option> elements...
[mootools.git] / Source / Core / Core.js
blobd8214a0b616e59dd938a6b499add94230e25aa45
1 /*
2 ---
4 name: Core
6 description: The heart of MooTools.
8 license: MIT-style license.
10 copyright: Copyright (c) 2006-2010 [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 ...
23 (function(){
25 this.MooTools = {
26         version: '1.3.3dev',
27         build: '%build%'
30 // typeOf, instanceOf
32 var typeOf = this.typeOf = function(item){
33         if (item == null) return 'null';
34         if (item.$family) 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 (item.callee) 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         return item instanceof object;
57 // Function overloading
59 var Function = this.Function;
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 Function.prototype.overloadSetter = function(usePlural){
66         var self = this;
67         return function(a, b){
68                 if (a == null) return this;
69                 if (usePlural || typeof a != 'string'){
70                         for (var k in a) self.call(this, k, a[k]);
71                         if (enumerables) for (var i = enumerables.length; i--;){
72                                 k = enumerables[i];
73                                 if (a.hasOwnProperty(k)) self.call(this, k, a[k]);
74                         }
75                 } else {
76                         self.call(this, a, b);
77                 }
78                 return this;
79         };
82 Function.prototype.overloadGetter = function(usePlural){
83         var self = this;
84         return function(a){
85                 var args, result;
86                 if (usePlural || typeof a != 'string') args = a;
87                 else if (arguments.length > 1) args = arguments;
88                 if (args){
89                         result = {};
90                         for (var i = 0; i < args.length; i++) result[args[i]] = self.call(this, args[i]);
91                 } else {
92                         result = self.call(this, a);
93                 }
94                 return result;
95         };
98 Function.prototype.extend = function(key, value){
99         this[key] = value;
100 }.overloadSetter();
102 Function.prototype.implement = function(key, value){
103         this.prototype[key] = value;
104 }.overloadSetter();
106 // From
108 var slice = Array.prototype.slice;
110 Function.from = function(item){
111         return (typeOf(item) == 'function') ? item : function(){
112                 return item;
113         };
116 Array.from = function(item){
117         if (item == null) return [];
118         return (Type.isEnumerable(item) && typeof item != 'string') ? (typeOf(item) == 'array') ? item : slice.call(item) : [item];
121 Number.from = function(item){
122         var number = parseFloat(item);
123         return isFinite(number) ? number : null;
126 String.from = function(item){
127         return item + '';
130 // hide, protect
132 Function.implement({
134         hide: function(){
135                 this.$hidden = true;
136                 return this;
137         },
139         protect: function(){
140                 this.$protected = true;
141                 return this;
142         }
146 // Type
148 var Type = this.Type = function(name, object){
149         if (name){
150                 var lower = name.toLowerCase();
151                 var typeCheck = function(item){
152                         return (typeOf(item) == lower);
153                 };
155                 Type['is' + name] = typeCheck;
156                 if (object != null){
157                         object.prototype.$family = (function(){
158                                 return lower;
159                         }).hide();
160                         //<1.2compat>
161                         object.type = typeCheck;
162                         //</1.2compat>
163                 }
164         }
166         if (object == null) return null;
168         object.extend(this);
169         object.$constructor = Type;
170         object.prototype.$constructor = object;
172         return object;
175 var toString = Object.prototype.toString;
177 Type.isEnumerable = function(item){
178         return (item != null && typeof item.length == 'number' && toString.call(item) != '[object Function]' );
181 var hooks = {};
183 var hooksOf = function(object){
184         var type = typeOf(object.prototype);
185         return hooks[type] || (hooks[type] = []);
188 var implement = function(name, method){
189         if (method && method.$hidden) return;
191         var hooks = hooksOf(this);
193         for (var i = 0; i < hooks.length; i++){
194                 var hook = hooks[i];
195                 if (typeOf(hook) == 'type') implement.call(hook, name, method);
196                 else hook.call(this, name, method);
197         }
198         
199         var previous = this.prototype[name];
200         if (previous == null || !previous.$protected) this.prototype[name] = method;
202         if (this[name] == null && typeOf(method) == 'function') extend.call(this, name, function(item){
203                 return method.apply(item, slice.call(arguments, 1));
204         });
207 var extend = function(name, method){
208         if (method && method.$hidden) return;
209         var previous = this[name];
210         if (previous == null || !previous.$protected) this[name] = method;
213 Type.implement({
215         implement: implement.overloadSetter(),
217         extend: extend.overloadSetter(),
219         alias: function(name, existing){
220                 implement.call(this, name, this.prototype[existing]);
221         }.overloadSetter(),
223         mirror: function(hook){
224                 hooksOf(this).push(hook);
225                 return this;
226         }
230 new Type('Type', Type);
232 // Default Types
234 var force = function(name, object, methods){
235         var isType = (object != Object),
236                 prototype = object.prototype;
238         if (isType) object = new Type(name, object);
240         for (var i = 0, l = methods.length; i < l; i++){
241                 var key = methods[i],
242                         generic = object[key],
243                         proto = prototype[key];
245                 if (generic) generic.protect();
247                 if (isType && proto){
248                         delete prototype[key];
249                         prototype[key] = proto.protect();
250                 }
251         }
253         if (isType) object.implement(prototype);
255         return force;
258 force('String', String, [
259         'charAt', 'charCodeAt', 'concat', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search',
260         'slice', 'split', 'substr', 'substring', 'trim', 'toLowerCase', 'toUpperCase'
261 ])('Array', Array, [
262         'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice',
263         'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight'
264 ])('Number', Number, [
265         'toExponential', 'toFixed', 'toLocaleString', 'toPrecision'
266 ])('Function', Function, [
267         'apply', 'call', 'bind'
268 ])('RegExp', RegExp, [
269         'exec', 'test'
270 ])('Object', Object, [
271         'create', 'defineProperty', 'defineProperties', 'keys',
272         'getPrototypeOf', 'getOwnPropertyDescriptor', 'getOwnPropertyNames',
273         'preventExtensions', 'isExtensible', 'seal', 'isSealed', 'freeze', 'isFrozen'
274 ])('Date', Date, ['now']);
276 Object.extend = extend.overloadSetter();
278 Date.extend('now', function(){
279         return +(new Date);
282 new Type('Boolean', Boolean);
284 // fixes NaN returning as Number
286 Number.prototype.$family = function(){
287         return isFinite(this) ? 'number' : 'null';
288 }.hide();
290 // Number.random
292 Number.extend('random', function(min, max){
293         return Math.floor(Math.random() * (max - min + 1) + min);
296 // forEach, each
298 var hasOwnProperty = Object.prototype.hasOwnProperty;
299 Object.extend('forEach', function(object, fn, bind){
300         for (var key in object){
301                 if (hasOwnProperty.call(object, key)) fn.call(bind, object[key], key, object);
302         }
305 Object.each = Object.forEach;
307 Array.implement({
309         forEach: function(fn, bind){
310                 for (var i = 0, l = this.length; i < l; i++){
311                         if (i in this) fn.call(bind, this[i], i, this);
312                 }
313         },
315         each: function(fn, bind){
316                 Array.forEach(this, fn, bind);
317                 return this;
318         }
322 // Array & Object cloning, Object merging and appending
324 var cloneOf = function(item){
325         switch (typeOf(item)){
326                 case 'array': return item.clone();
327                 case 'object': return Object.clone(item);
328                 default: return item;
329         }
332 Array.implement('clone', function(){
333         var i = this.length, clone = new Array(i);
334         while (i--) clone[i] = cloneOf(this[i]);
335         return clone;
338 var mergeOne = function(source, key, current){
339         switch (typeOf(current)){
340                 case 'object':
341                         if (typeOf(source[key]) == 'object') Object.merge(source[key], current);
342                         else source[key] = Object.clone(current);
343                 break;
344                 case 'array': source[key] = current.clone(); break;
345                 default: source[key] = current;
346         }
347         return source;
350 Object.extend({
352         merge: function(source, k, v){
353                 if (typeOf(k) == 'string') return mergeOne(source, k, v);
354                 for (var i = 1, l = arguments.length; i < l; i++){
355                         var object = arguments[i];
356                         for (var key in object) mergeOne(source, key, object[key]);
357                 }
358                 return source;
359         },
361         clone: function(object){
362                 var clone = {};
363                 for (var key in object) clone[key] = cloneOf(object[key]);
364                 return clone;
365         },
367         append: function(original){
368                 for (var i = 1, l = arguments.length; i < l; i++){
369                         var extended = arguments[i] || {};
370                         for (var key in extended) original[key] = extended[key];
371                 }
372                 return original;
373         }
377 // Object-less types
379 ['Object', 'WhiteSpace', 'TextNode', 'Collection', 'Arguments'].each(function(name){
380         new Type(name);
383 // Unique ID
385 var UID = Date.now();
387 String.extend('uniqueID', function(){
388         return (UID++).toString(36);
391 //<1.2compat>
393 var Hash = this.Hash = new Type('Hash', function(object){
394         if (typeOf(object) == 'hash') object = Object.clone(object.getClean());
395         for (var key in object) this[key] = object[key];
396         return this;
399 Hash.implement({
401         forEach: function(fn, bind){
402                 Object.forEach(this, fn, bind);
403         },
405         getClean: function(){
406                 var clean = {};
407                 for (var key in this){
408                         if (this.hasOwnProperty(key)) clean[key] = this[key];
409                 }
410                 return clean;
411         },
413         getLength: function(){
414                 var length = 0;
415                 for (var key in this){
416                         if (this.hasOwnProperty(key)) length++;
417                 }
418                 return length;
419         }
423 Hash.alias('each', 'forEach');
425 Object.type = Type.isObject;
427 var Native = this.Native = function(properties){
428         return new Type(properties.name, properties.initialize);
431 Native.type = Type.type;
433 Native.implement = function(objects, methods){
434         for (var i = 0; i < objects.length; i++) objects[i].implement(methods);
435         return Native;
438 var arrayType = Array.type;
439 Array.type = function(item){
440         return instanceOf(item, Array) || arrayType(item);
443 this.$A = function(item){
444         return Array.from(item).slice();
447 this.$arguments = function(i){
448         return function(){
449                 return arguments[i];
450         };
453 this.$chk = function(obj){
454         return !!(obj || obj === 0);
457 this.$clear = function(timer){
458         clearTimeout(timer);
459         clearInterval(timer);
460         return null;
463 this.$defined = function(obj){
464         return (obj != null);
467 this.$each = function(iterable, fn, bind){
468         var type = typeOf(iterable);
469         ((type == 'arguments' || type == 'collection' || type == 'array' || type == 'elements') ? Array : Object).each(iterable, fn, bind);
472 this.$empty = function(){};
474 this.$extend = function(original, extended){
475         return Object.append(original, extended);
478 this.$H = function(object){
479         return new Hash(object);
482 this.$merge = function(){
483         var args = Array.slice(arguments);
484         args.unshift({});
485         return Object.merge.apply(null, args);
488 this.$lambda = Function.from;
489 this.$mixin = Object.merge;
490 this.$random = Number.random;
491 this.$splat = Array.from;
492 this.$time = Date.now;
494 this.$type = function(object){
495         var type = typeOf(object);
496         if (type == 'elements') return 'array';
497         return (type == 'null') ? false : type;
500 this.$unlink = function(object){
501         switch (typeOf(object)){
502                 case 'object': return Object.clone(object);
503                 case 'array': return Array.clone(object);
504                 case 'hash': return new Hash(object);
505                 default: return object;
506         }
509 //</1.2compat>
511 })();