Source: WKcClass.js

/*jslint es5: true, plusplus: true, white: true, indent: 2, maxlen: 90 */
/*global $wk$ */
/**
 *  @author    Wolfgang Kowarschick
 *  @copyright 2012-2013, Wolfgang Kowarschick
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted under the terms of the 
 *  Creative Commons License Attribution-NonCommercial-ShareAlike 3.0 Unported    
 *  (CC BY-NC-SA 3.0: http://creativecommons.org/licenses/by-nc-sa/3.0/). 
 */

 
////////////////////////////////////////////////////////////////////////////////
// Class "$wk$.WKcClass"
////////////////////////////////////////////////////////////////////////////////

$wk$(
function($)
{ "use strict";

   var i = 0,
       mixin               = $.mixin,
       pkg                 = $.pkg,
       moduleAdd           = $.moduleAdd,
       l_dummy             = {},
       l_prototype,
       l_wrapper, 
       l_class,
       l_class_props       = ["classFullName", "classID", "classPath", "parent"],
       ANNOTATION_CLASS    = "cls",
       ANNOTATION_PROPERTY = "prop", 
       ANNOTATION_METHOD   = "meth";
      
  /**
   *  Temporarily defines a <code>$super$</code> method, 
   *  before a method is invoked.
   *  
   *  @private
   *  @function
   *  @see http://ejohn.org/blog/simple-javascript-inheritance/#postcomment 
   */
  function f_wrapper(p_key, p_f)
  { return function() 
           { var l_super = this.$super$, l_result;
             this.$super$ = this.parent[p_key];
             l_result = p_f.apply(this, arguments);
             this.$super$ = l_super;
             return l_result;
           };
  }
  
  /**
   *  Sets the standard class properties.
   * 
   *  @private
   *  @function 
   */
  function f_class_properties(p_class, p_class_prop_values)
  { var l_key = null,
        l_prototype = p_class.prototype;
    p_class.fullName = p_class_prop_values[0];
    p_class.id       = p_class_prop_values[1];
    p_class.path     = p_class_prop_values[2];
  
    l_prototype.constructor = p_class;
  
    for (l_key in p_class_prop_values)
    { if (p_class_prop_values.hasOwnProperty(l_key))
      { Object.defineProperty(l_prototype,
                              l_class_props[l_key],
                              { configurable: false,
                                enumerable:   false,
                                value:        p_class_prop_values[l_key],
                                writable:     false
                              }
                             );
      }
    }
  }
  
  /** 
   * The meta class <code>WKcClass</code> can be used to create classes.
   * 
   * @class
   * @name $wk$.WKcClass
   * 
   * @param {String} [p_args.fullName]    The full name of the class: 
   *                                      path + "." + id. 
   * @param {String} [p_args.id]          A unique identifier of the class.
   * @param {String} [p_args.path]        The path, where the class is stored.
   * @param {Object} [p_args.methods]     The methods of the class
   *                                      (incl. real getters  and setters).
   * @param {Object} [p_args.observers]   The observer methods of the class;
   *                                      <code>this</code> denotes the
   *                                      object itself.
   * @param {Class}  [p_args.inherit]     A superclass, the methods
   *                                      of which are inherited.
   * @param {Class|Array} 
   *                 [p_args.mixin]       A class or an array of classes, the
   *                                      methods of which are directly copied
   *                                      into the prototype object of the 
   *                                      new class.
   * @param {Object} [p_args.staticMethods]  
   *                                      The static methods of the class
   *                                      (incl. real getters  and setters).
   * @param {Object} [p_args.staticObservers]  
   *                                      The static observer methods of the 
   *                                      class. <code>this</code> denotes 
   *                                      the class itself.
   * @param {Class|Array} 
   *                 [p_args.staticMixin] A class or an array of classes, the 
   *                                      class methods of which are directly
   *                                      copied into the new class object.
   * @returns {Object} A new class (object).
   */
  l_class =
  function(p_args) 
  { p_args = p_args || {};
    var l_max, 
        f_aux, 
        l_key              = null,
        l_full_id          = p_args.fullName || "ANONYMOUS",
        l_id               =    p_args.id 
                             || l_full_id.match("[^.]*$")[0],
        l_path             =    p_args.path
                             || (   p_args.fullName
                                 && l_full_id.substring(0,  l_full_id.length 
                                                          - l_id.length 
                                                          - 1
                                                        )
                                ),
        l_methods          = p_args.methods,
        l_observers        = p_args.observers,
        f_observers        = function(p_observers, p_object, p_this)
                             { var l_key = null;
                               for (l_key in p_observers)
                               { if (p_observers.hasOwnProperty(l_key))
                                 { p_object[l_key]
                                     = p_observers[l_key].bind(p_this); 
                                 }
                               }
                             },
        l_inherit          = p_args.inherit,
        l_mixin            = p_args.mixin,
        l_static_methods   = p_args.staticMethods,
        l_static_observers = p_args.staticObservers,
        l_static_mixin     = p_args.staticMixin,
        Class              = null;

    // Replace, if necessary, the name of the super class 
    // by the super class itself.
    if (typeof l_inherit === "string")
    { l_inherit = pkg(l_inherit, false); }
    
    // TBD: Check that all observer methods are of type "Function".

    // Auxiliary function used by the next two statements.
    f_aux = function(p_class)
            { if (p_class && p_class.$__wk_observers__$)
              {  if (!l_observers)
                 { l_observers = p_class.$__wk_observers__$; }
                 else
                 { mixin(p_class.$__wk_observers__$, l_observers); }
              }
            };
          
    // Mixin observers of the super class.
    f_aux(l_inherit);
    
    // Mixin observers of the mixin classes.
    if (l_mixin)
    { if (l_mixin instanceof Array)
      { for (i = 0, l_max = l_mixin.length; i < l_max; i++)
        { f_aux(l_mixin[i]); }
      }
      else
      { f_aux(l_mixin); }
    }

    // The new class. 
    Class = 
      function(p_test, p_args) 
      { if (!(this instanceof Class))
        { return new Class(l_dummy, arguments); }
          
        var l_proto,
            l_this = this;
        
        if (l_observers)
        { l_proto = Object.create(Class.prototype);
          l_this = Object.create(l_proto);
          f_observers(l_observers, l_proto, l_this);  
        }
        
        if (this.init)
        { this.init.apply(l_this, (p_test===l_dummy)?p_args||[]:arguments); }
           
        return l_this;
      };
      
    // Store observer methods for inheriting them (by mixin!) in subclasses.
    if (l_observers)
    { Object.defineProperty
      (Class, "$__wk_observers__$", { enumarable:   false,
                                      configurable: false,
                                      writable:     false,
                                      value:        l_observers,
                                    }
      );
    }
      
    if (l_id && !p_args.fullName)
    { l_full_id = ((l_path && l_path.length > 0) 
                   ? (l_path + ".")
                   : ""
                  ) + l_id; 
    }
    
    if (p_args.fullName && p_args.fullName !== l_full_id)
    { throw new Error(  "full name (" 
                      + p_args.fullName 
                      + ") != path + name ("
                      + l_full_id
                      + ")"); 
    }
    
    // Create the prototyp object of the new class ...
    l_prototype = Class.prototype 
                = Object.create((l_inherit || Object).prototype);

    // ... and add some attributes:
    f_class_properties(Class, [l_full_id, l_id, l_path, 
                               (l_inherit || Object).prototype
                              ]
                      );

    // Add the user defined methods to the prototype object.
    mixin(l_methods, l_prototype);

    // Add the methods of all classes that are to be copied into the prototype
    // object.
    if (l_mixin)
    { if (l_mixin instanceof Array)
      { for (i = 0, l_max = l_mixin.length; i < l_max; i++)
        { mixin(l_mixin[i].prototype, l_prototype); }
      }
      else
      { mixin(l_mixin.prototype, l_prototype);
      }
    }

    // Add the init method to the prototype object.
    l_prototype.init =
         (l_methods && l_methods.init) 
      || (l_inherit && l_inherit.prototype.init)
      || l_prototype.init
      || null;

    // If the new class inherits methods from a super class,
    // add SUPER method to each method that overrides an inherited method.
    if (l_inherit)
    { for (l_key in l_prototype)
      { if (l_prototype.hasOwnProperty(l_key))
        { f_aux = l_prototype[l_key];
          if (   f_aux instanceof Function 
              && l_inherit.prototype[l_key]
              && l_key !== "constructor"
             )
          { l_wrapper = l_prototype[l_key] = f_wrapper(l_key, f_aux);
            l_wrapper.$original$ = f_aux;
          }
        }
      }
    }
    
    // Copy the static methods into the class object itself.
    mixin(l_static_methods, Class);
    
    // Add the methods of all classes that are to be copied into the class
    // object.
    if (l_static_mixin)
    { if (l_static_mixin instanceof Array)
      { for (i = 0, l_max = l_static_mixin.length; i < l_max; i++)
        { mixin(l_static_mixin[i], Class); }
      }
      else
      { mixin(l_static_mixin, Class); }
    }

    // Add the static observers.
    if (l_static_observers)
    { f_observers(l_static_observers, Class, Class); }
    
    // Initialize the class object.
    if (Class.init)
    { Class.init(); }
    
    // Store the class object within the subpackage stated.
    if (l_path !== undefined && l_path !== null)
    { pkg(l_path)[l_id] = Class; }
    
    return Class;
  };

  /**
   *  @method
   *  
   *  @param {Object} p_obj An arbitray object.
   * 
   *  @returns {String} The class name of <code>p_obj</code> or 
   *                   <code>undefined</code> if it's an instrinsic object.
   *                   
   *  @see {@link http://blog.magnetiq.com/post/514962277/finding-out-class-names-of-javascript-objects}
   */
  l_class.className
  = function(p_obj)
    { var l_constructor, l_arr;

      if (p_obj && p_obj.constructor)
      { l_constructor = p_obj.constructor;
        if (l_constructor.className)
        { return l_constructor.className; }
        if ( l_constructor.toString) 
        { l_arr = l_constructor.toString().match(/function\s*(\w+)/);
          if (l_arr && l_arr.length === 2)
          { return l_arr[1]; }
        }
      }
      return undefined;
    };

  f_class_properties(l_class, ["$wk$.WKcClass", "WKcClass", "$wk$"]);

  pkg("$wk$").WKcClass = l_class;
  
  moduleAdd({name:   "$wk$.WKcClass",
             module: { WKcClass: l_class },
 //          init:   function(p_c) 
 //                  { console.log("$wk$.WKcClass ready"); p_c(); },      
            });
});

////////////////////////////////////////////////////////////////////////////////
// End of Class "$wk$.WKcClass"
////////////////////////////////////////////////////////////////////////////////
W. Kowarschick © 2013 (CC BY-NC-SA 3.0: http://creativecommons.org/licenses/by-nc-sa/3.0/)
Documentation generated by JSDoc 3.3.0-dev on Sat Oct 05 2013 15:14:39 GMT+0200 (MESZ) using the DocStrap template.