Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #4060: base.js.3.diff

File base.js.3.diff, 6.9 kB (added by newmanb@stanford.edu, 3 years ago)

A slimmer version of the patch. This does not support method privacy restrictions, but I haven't found anyone who really loved them, and there was a slight performance hit... Also, Dean Edwards won me over to the .extend() syntax for subclassing, and it was easy to implement (four lines), so that's supported now too.

  • base.js

    old new  
    1 var Class = { 
    2   create: function() { 
    3     return function() {  
    4       this.initialize.apply(this, arguments); 
     1var Class = new (function(vocab) { 
     2   
     3  this.AUTHOR = "Ben Newman (newmanb@stanford.edu)"; 
     4 
     5  var $doNotInit = {}; 
     6   
     7  this.create = function(declarations) { 
     8     
     9    var constructor = function() { 
     10      if (arguments[0] !== $doNotInit) { 
     11        this._runTimeDeclarations = {}; 
     12        if (this[vocab['init']]) 
     13          this[vocab['init']].apply(this, arguments); 
     14      } 
     15    }; 
     16 
     17    if (declarations) { 
     18      var ownPrototype = function() { 
     19        for (var name in declarations) 
     20          __attachClassProperty(name, declarations[name], this); 
     21        __ensureDefaults(this, declarations); 
     22        __ensureOwnPrototypeMemory(this, declarations); 
     23      }; 
     24 
     25      var base = declarations.extending || {}; 
     26      delete declarations.extending; 
     27 
     28      var baseInstance = __inherit(ownPrototype, base); 
     29      declarations['getBaseInstance'] = Class.encap(baseInstance); 
     30      declarations[vocab['super']] = __makeSup(); 
     31       
     32      __setupAttachDelete(declarations); 
     33      __inherit(constructor, ownPrototype); 
     34      __enableMimicry(constructor); // experimental 
     35 
     36      constructor['extend'] = function(declarations) { 
     37        declarations['extending'] = constructor; 
     38        return Class.create(declarations); 
     39      } // a la Dean Edwards' Base.js 
    540    } 
    6   } 
    7 
     41     
     42    return constructor; 
     43  }; 
    844 
     45  var __enableMimicry = function(__ctor) { 
     46    var __old = __ctor.prototype; 
     47    __ctor['mimic'] = function(obj) { 
     48      var __new = Object.extend(obj, __old); 
     49      return Object.extend(function() { 
     50        return __ctor.apply(this, arguments); 
     51      }, { 'prototype': 
     52        __ensureDefaults(__new, __old) }); 
     53    }; 
     54  }; 
     55 
     56  var __inherit = function(subclass, superclass) { 
     57    switch (typeof(superclass)) { 
     58      case 'object': default: 
     59        superclass[vocab['init']] = superclass.constructor; 
     60        subclass.prototype = superclass; break; 
     61      case 'function': 
     62        superclass.getInstance = superclass.getInstance || 
     63          Class.encap(new superclass($doNotInit)); 
     64        subclass.prototype = superclass.getInstance(); 
     65    } 
     66    return subclass.prototype; 
     67  }; 
     68 
     69  var __setupAttachDelete = function(declarations) { 
     70    return Object.extend(declarations, { 
     71      'attachMethod': function(fnName, func) { 
     72        if (!this.ownPrototypeHas(fnName)) { 
     73          this._runTimeDeclarations[fnName] = true; 
     74          __attachClassProperty(fnName, func, this); 
     75        } else throw "Can't replace existing methods." 
     76      }, 
     77       
     78      'deleteMethod': function(fnName) { 
     79        if (this._runTimeDeclarations[fnName]) { 
     80          __attachClassProperty(fnName, null, this, true); 
     81          this._runTimeDeclarations[fnName] = false; 
     82        } else throw "Only attachMethod additions can be deleted." 
     83      } 
     84    }); 
     85  }; 
     86   
     87  var __attachClassProperty = function(propName, prop, obj, 
     88                                       /*optional*/ removeInstead) { 
     89    if (removeInstead) { 
     90      if (obj.ownPrototypeHas(propName)) 
     91        delete obj[propName]; 
     92    } else { 
     93      if (typeof(obj[propName] = prop) == 'function') { 
     94        if (typeof(prop.body) != 'function') 
     95          __addStackLogic(obj, propName); 
     96        var __origStr = prop.toString(); 
     97        prop.toString = obj[propName].toString = function() { 
     98          return __origStr.replace(/^\s*function/, propName); 
     99        }; 
     100      } 
     101    } 
     102  }; 
     103   
     104  // special-purpose array object (slightly faster in tests) 
     105  var __callStack = new (function() { 
     106    var stack = []; var index = 0; 
     107    this.push  = function(what) { stack[index++] = what; } 
     108    this.pop   = function()     { return stack[--index]; } 
     109    this.empty = function()     { return index == 0; } 
     110    this.peek  = function()     { return stack[index - 1]; } 
     111  })(); 
     112 
     113  var $empty = ['', null]; 
     114  var __lastOnCallStack = function() { 
     115    if (__callStack.empty()) return $empty; 
     116    else return __callStack.peek(); 
     117  }; 
     118     
     119  var __addStackLogic = function(obj, fnName) { 
     120    switch (fnName) { 
     121      case vocab['super']: case 'valueOf': 
     122      case 'attachMethod': case 'deleteMethod': 
     123      case 'getBaseInstance': return; 
     124      default: 
     125        obj[fnName] = Object.extend(__wrap(obj[fnName], fnName), { 
     126          'body': obj[fnName] // preserve underlying function 
     127        }); 
     128    } 
     129  }; 
     130 
     131  // same as bind(), but remembers callstack contents too 
     132  Function.prototype.grant = function(obj) { 
     133    return __wrap(this, __lastOnCallStack(), obj); 
     134  }; 
     135 
     136  var __wrap = function(func, toPush, base) { 
     137    var isArray = (typeof(toPush) == 'array'); 
     138    if (!isArray) toPush = [toPush]; 
     139     
     140    return function() { 
     141      if (!isArray) toPush.push(this); 
     142      __callStack.push(toPush); 
     143      try { 
     144        var result = 
     145          func.apply(base || this, arguments); 
     146      } catch (ex) { throw ex; } 
     147      finally { __callStack.pop(); } 
     148      return result; 
     149    }; 
     150  }; 
     151 
     152  var __ensureDefaults = function(ownPrototype, declarations) { 
     153    ['toString', 'toLocaleString', 'valueOf'].each(function(prop) { 
     154      __attachClassProperty(prop, declarations[prop], ownPrototype); 
     155    }); 
     156    return ownPrototype; 
     157  }; 
     158 
     159  var __ensureOwnPrototypeMemory = function(ownPrototype, declarations) { 
     160    ownPrototype.ownPrototypeHas = function(property) { 
     161      return (typeof(declarations[property]) != 'undefined' || 
     162              (this._runTimeDeclarations && // present if instantiated 
     163               this._runTimeDeclarations[property])); 
     164    }; 
     165  }; 
     166 
     167  // convenient mechanism for creating accessor functions 
     168  this.encap = function(__val, mutable) { 
     169    if (!mutable) return function() { return __val; } 
     170    else return function(/* optional new value */) { 
     171      if (arguments.length > 0) __val = arguments[0]; 
     172      return __val; 
     173    } 
     174  }; 
     175   
     176  var __makeSup = function() { 
     177    var __platform = false; // where to start lookup 
     178     
     179    return function() { 
     180      var methodName = __lastOnCallStack()[0]; 
     181      var curObj = __platform || this; 
     182 
     183      while ( curObj.ownPrototypeHas && 
     184             !curObj.ownPrototypeHas(methodName) && 
     185              curObj.getBaseInstance) 
     186        curObj = curObj.getBaseInstance(); 
     187      curObj = curObj.getBaseInstance 
     188        ? curObj.getBaseInstance() : {}; 
     189 
     190      var prevBase = __platform; 
     191      __platform = curObj; 
     192      try { var result = 
     193              curObj[methodName].apply(this, arguments); } 
     194      catch (ex) { throw ex; } 
     195      finally { __platform = prevBase; } 
     196      return result; 
     197    }; 
     198  }; 
     199})( // the vocabulary is arbitrary: 
     200{'super': 'sup', 'init': 'initialize'}); 
     201 
     202setTimeout(function() { 
     203  window.Base = Class.create({}); 
     204}, 10); 
     205 
    9206var Abstract = new Object(); 
    10207 
    11208Object.extend = function(destination, source) {