diff --git a/lib/bootstrap.js b/lib/bootstrap.js index ce6110b2..210fa3b5 100644 --- a/lib/bootstrap.js +++ b/lib/bootstrap.js @@ -11,12 +11,11 @@ * http://prototypejs.org/ */ -var Base = this.Base = new function() { +var Base = new function() { // Bootstrap scope // Fix __proto__ for browsers where it is not implemented (IE and Opera). var fix = !this.__proto__, - hidden = /^(statics|generics|preserve|beans|enumerable|prototype|__proto__|toString|valueOf)$/, + hidden = /^(statics|generics|preserve|enumerable|beans|prototype|__proto__|toString|valueOf)$/, proto = Object.prototype, - toString = proto.toString, /** * Private function that checks if an object contains a given property. * Naming it 'has' causes problems on Opera when defining @@ -28,6 +27,7 @@ var Base = this.Base = new function() { return name !== '__proto__' && this.hasOwnProperty(name); } : proto.hasOwnProperty, + toString = proto.toString, proto = Array.prototype, isArray = Array.isArray = Array.isArray || function(obj) { return toString.call(obj) === '[object Array]'; @@ -38,6 +38,9 @@ var Base = this.Base = new function() { iter.call(bind, this[i], i, this); }, forIn = function(iter, bind) { + // Do not use Object.keys for iteration as iterators might modify + // the object we're iterating over, making the hasOwnProperty still + // necessary. for (var i in this) if (this.hasOwnProperty(i)) iter.call(bind, this[i], i, this); @@ -109,8 +112,15 @@ var Base = this.Base = new function() { // the property exists (name in dest) and store result in prev prev = preserve || func ? (val && val.get ? name in dest : dest[name]) : null; + // Make generics first, as we might jump out bellow in the + // val !== (src.__proto__ || Object.prototype)[name] check, + // e.g. when explicitely reinjecting Array.prototype methods + // to produce generics of them. if (generics && func && (!preserve || !generics[name])) { generics[name] = function(bind) { + // Do not call Array.slice generic here, as on Safari, + // this seems to confuse scopes (calling another + // generic from generic-producing code). return bind && dest[name].apply(bind, slice.call(arguments, 1)); } @@ -121,6 +131,9 @@ var Base = this.Base = new function() { if (prev && /\bthis\.base\b/.test(val)) { var fromBase = base && base[name] == prev; res = function() { + // Look up the base function each time if we can, + // to reflect changes to the base class after + // inheritance. var tmp = describe(this, 'base'); define(this, 'base', { value: fromBase ? base[name] : prev, configurable: true }); @@ -154,6 +167,7 @@ var Base = this.Base = new function() { && (bean = name.match(/^(get|is)(([A-Z])(.*))$/))) beans.push([ bean[3].toLowerCase() + bean[4], bean[2] ]); } + // No need to look up getter if this is a function already. if (!res || func || !res.get && !res.set) res = { value: res, writable: true }; // Only set/change configurable and enumerable if this field is @@ -233,8 +247,10 @@ var Base = this.Base = new function() { function each(obj, iter, bind, asArray) { try { - (asArray || asArray === undefined && isArray(obj) ? forEach : forIn) - .call(obj, iterator(iter), bind = bind || obj); + if (obj) + (asArray || asArray === undefined && isArray(obj) + ? forEach : forIn).call(obj, iterator(iter), + bind = bind || obj); } catch (e) { if (e !== Base.stop) throw e; } @@ -249,7 +265,7 @@ var Base = this.Base = new function() { // Inject into new ctor object that's passed to inject(), and then returned return inject(function() {}, { - inject: function(src/* , ... */) { + inject: function(src/*, ... */) { if (src) { var proto = this.prototype, base = proto.__proto__ && proto.__proto__.constructor, @@ -261,8 +277,8 @@ var Base = this.Base = new function() { src.preserve, src.generics && this); // Define new static fields as enumerable, and inherit from // base. enumerable is necessary so they can be copied over from - // base, and it does not disturb to be enumerable in the - // constructor. Use the preserve setting in src.preserve for + // base, and it does not harm to have enumerable properties in + // the constructor. Use the preserve setting in src.preserve for // statics too, not their own. inject(this, statics, true, base, src.preserve); } @@ -387,7 +403,7 @@ var Base = this.Base = new function() { * functionality can achieved by using return in the closure. * In prototype, the implementation of $continue also leads to a * huge speed decrease, as the closure is wrapped in another closure - * that does nothing else than handling $continue. + * that does nothing else than handling $continue. */ stop: {} } diff --git a/src/core/Base.js b/src/core/Base.js index 5538f8d8..b842b7a3 100644 --- a/src/core/Base.js +++ b/src/core/Base.js @@ -14,8 +14,9 @@ * All rights reserved. */ -// Extend Base with utility functions used across the library. -Base.inject({ +// Extend Base with utility functions used across the library. Also set +// this.Base, since bootstrap.js ommits that. +this.Base = Base.inject({ clone: function() { return new this.constructor(this);