      1 /**
      2  * Hilo
      3  * Copyright 2015 alibaba.com
      4  * Licensed under the MIT License
      5  */
      6
      7 /**
      8  * Create Example Class:
      9  * <pre>
     10  * var Bird = Hilo.Class.create({
     11  *     Extends: Animal,
     12  *     Mixes: EventMixin,
     13  *     constructor: function(name){
     14  *         this.name = name;
     15  *     },
     16  *     fly: function(){
     17  *         console.log('I am flying');
     18  *     },
     19  *     Statics: {
     20  *         isBird: function(bird){
     21  *             return bird instanceof Bird;
     22  *         }
     23  *     }
     24  * });
     25  *
     26  * var swallow = new Bird('swallow');
     27  * swallow.fly();
     28  * Bird.isBird(swallow);
     29  * </pre>
     30  * @namespace Class Class is created to aid the developer.
     31  * @static
     32  * @module hilo/core/Class
     33  */
     34 var Class = (function(){
     35
     36 /**
     37  * Create a class based on the parameters, properties and methods specified.
     38  * @param {Object} properties Properties and methods to create the class.
     39  * <ul>
     40  * <li><b>Extends</b> - Designed to inherit the parent class.</li>
     41  * <li><b>Mixes</b> - Specifies mixed member collection object.</li>
     42  * <li><b>Statics</b> - Static property or method specified class.</li>
     43  * <li><b>constructor</b> -  The constructor of specified class.</li>
     44  * <li>Other members of the property or method to create the class.</li>
     45  * </ul>
     46  * @returns {Object} Create classes.
     47  */
     48 var create = function(properties){
     49     properties = properties || {};
     50     var clazz = properties.hasOwnProperty('constructor') ? properties.constructor : function(){};
     51     implement.call(clazz, properties);
     52     return clazz;
     53 };
     54
     55 /**
     56  * @private
     57  */
     58 var implement = function(properties){
     59     var proto = {}, key, value;
     60     for(key in properties){
     61         value = properties[key];
     62         if(classMutators.hasOwnProperty(key)){
     63             classMutators[key].call(this, value);
     64         }else{
     65             proto[key] = value;
     66         }
     67     }
     68
     69     mix(this.prototype, proto);
     70 };
     71
     72 var classMutators = /** @ignore */{
     73     Extends: function(parent){
     74         var existed = this.prototype, proto = createProto(parent.prototype);
     75         //inherit static properites
     76         mix(this, parent);
     77         //keep existed properties
     78         mix(proto, existed);
     79         //correct constructor
     80         proto.constructor = this;
     81         //prototype chaining
     82         this.prototype = proto;
     83         //shortcut to parent's prototype
     84         this.superclass = parent.prototype;
     85     },
     86
     87     Mixes: function(items){
     88         items instanceof Array || (items = [items]);
     89         var proto = this.prototype, item;
     90
     91         while(item = items.shift()){
     92             mix(proto, item.prototype || item);
     93         }
     94     },
     95
     96     Statics: function(properties){
     97         mix(this, properties);
     98     }
     99 };
    100
    101 /**
    102  * @private
    103  */
    104 var createProto = (function(){
    105     if(Object.__proto__){
    106         return function(proto){
    107             return {__proto__: proto};
    108         };
    109     }else{
    110         var Ctor = function(){};
    111         return function(proto){
    112             Ctor.prototype = proto;
    113             return new Ctor();
    114         };
    115     }
    116 })();
    117
    118 /**
    119  * Mixed property or method.
    120  * @param {Object} target Mixed audiences.
    121  * @param {Object} source The source whose methods and properties are to be mixed. It can support multiple source parameters.
    122  * @returns {Object} Mixed audiences.
    123  */
    124 var mix = function(target){
    125     for(var i = 1, len = arguments.length; i < len; i++){
    126         var source  = arguments[i], defineProps;
    127         for(var key in source){
    128             var prop = source[key];
    129             if(prop && typeof prop === 'object'){
    130                 if(prop.value !== undefined || typeof prop.get === 'function' || typeof prop.set === 'function'){
    131                     defineProps = defineProps || {};
    132                     defineProps[key] = prop;
    133                     continue;
    134                 }
    135             }
    136             target[key] = prop;
    137         }
    138         if(defineProps) defineProperties(target, defineProps);
    139     }
    140
    141     return target;
    142 };
    143
    144 var defineProperty, defineProperties;
    145 try{
    146     defineProperty = Object.defineProperty;
    147     defineProperties = Object.defineProperties;
    148     defineProperty({}, '$', {value:0});
    149 }catch(e){
    150     if('__defineGetter__' in Object){
    151         defineProperty = function(obj, prop, desc){
    152             if('value' in desc) obj[prop] = desc.value;
    153             if('get' in desc) obj.__defineGetter__(prop, desc.get);
    154             if('set' in desc) obj.__defineSetter__(prop, desc.set);
    155             return obj;
    156         };
    157         defineProperties = function(obj, props){
    158             for(var prop in props){
    159                 if(props.hasOwnProperty(prop)){
    160                     defineProperty(obj, prop, props[prop]);
    161                 }
    162             }
    163             return obj;
    164         };
    165     }
    166 }
    167
    168 return {create:create, mix:mix};
    169
    170 })();
    171