kane-thornwyrd/etho.js

View on GitHub
index.js

Summary

Maintainability
A
1 hr
Test Coverage
// # etho.js
//
// ## UMD.js wrapper.
(function (root, factory) {
    // AMD.
  if (typeof define === 'function' && define.amd) {
    define([], function () {
        return factory();
    });
    // Commonjs.
  } else if(typeof exports !== 'undefined'){
    exports = module.exports = factory();
    // Browser globals.
  } else {
    factory();
  }
}(this, function () {

// declaring the namespace.
  var etho = {};

  // ### ucfirst
  // Return the string passed as argument with the first letter uppercased.
  //
  // **Exemple**:
  // ```
  //etho.ucfirst('foo');
  //// Foo
  // ```
  etho.ucfirst = function ethoUcfirst(str){
    return str.substr(0,1).toUpperCase() + str.substr(1);
  };



  // ### getType
  // Return the *true* type of anything as a string.
  //
  // It always return the type "lowercase"d for consistency.
  //
  // **Exemples**:
  // ```
  //etho.getType('foo');
  //// "string"
  //etho.getType(3);
  //// "number"
  //etho.getType({});
  //// "object"
  //etho.getType([]);
  //// "array"
  //etho.getType(null);
  //// "null"
  //etho.getType(undefined);
  //// "undefined"
  // ```
  etho.getType = function ethoGetType(thing){
    return Object.prototype.toString.call(thing).match(/^\[object\s+(\w+)\]$/)[1].toLowerCase();
  };

  // ### toArraySliced
  // Try to transform anything into an array and slice it.
  //
  // **Exemples**:
  // ```
  //function test(param){
  //  var args = etho.toArraySliced(arguments);
  //  return param + "|" + args.length;
  //}
  //
  //test('foo')
  ////foo|1
  //test('foo','bar')
  ////foo|2
  // ```
  // ```
  //function test(param){
  //  var args = etho.toArraySliced(arguments, 1);
  //  return param + "|" + args.length;
  //}
  //
  //test('foo')
  ////foo|0
  //test('foo','bar')
  ////foo|1
  // ```
  // ```
  //function test(param, /* foo, bar,.. */ ctx){
  //  var args = etho.toArraySliced(arguments, 1, -1);
  //  return param + "|" + args.length + "|" + ctx;
  //}
  //
  //test('foo', 'bar')
  ////foo|0|bar
  //test('foo', 'baz', 'bar')
  ////foo|1|bar
  // ```
  etho.toArraySliced = function ethoToArraySliced(obj, sliceStart, sliceEnd){
    sliceStart = sliceStart || 0;
    if(etho.isA('undefined', sliceEnd)){
      return Array.prototype.slice.call(obj, sliceStart);
    }else{
      return Array.prototype.slice.call(obj, sliceStart, sliceEnd);
    }
  };


  // ### isA
  // Check the type of anything against the supposed type as a string.
  //
  // **Exemples**:
  // ```
  //etho.isA('string', "foo")
  //// true
  //
  //etho.isA('string', {})
  //// false
  // ```
  etho.isA = function ethoIsA(type, obj){
    return etho.getType(obj) === type;
  };


  // ### shallowCopy
  // __Recursively deep copy__ an `array` or an `object` without any reference left !
  //
  // **Exemple**:
  // ```
  //var foo = {
  //  arr : {
  //    pirate : true,
  //    fakelimb   : [
  //      'leftHand'
  //    ]
  //  }
  //}
  //
  //var bar = etho.shallowCopy(foo)
  //
  //bar.arr.fakelimb.push('eye')
  //
  //foo.arr.fakelimb.length
  ////1
  //
  //bar.arr.fakelimb.length
  ////2
  // ```
  etho.shallowCopy = function ethoShallowCopy(obj){
    var out = {};
    switch(etho.getType(obj)){
      case 'array':
        return etho.toArraySliced(obj);
      case 'object':
        for (var key in obj){
          if(
            etho.isA('array',obj[key]) ||
            etho.isA('object',obj[key])
          ){
            out[key] = etho.shallowCopy(obj[key]);
          }else{
            out[key] = obj[key];
          }
        }
      break;
      default:
        throw new Exception('Wrong Type.');
    };
    return out;
  };


  // ### forEach
  // Run an iterator against each entries of an array or an object.
  //
  // **Exemple**:
  // ```
  //var foo = [1,2,3,4,5,6,7,8,9], out = 0;
  //etho.forEach(foo, function(val, key, list){ out += val; });
  //// out === 45
  // ```
  etho.forEach = function ethoForeach(obj, iterator, context) {

    if (obj === null){ throw new Error('no object to iterate on !');}

    if(typeof context === 'undefined'){ context = this; }

    for (var key in obj) {
      if ({}.hasOwnProperty.call(obj, key)) {
        iterator.call(context, obj[key], key, obj);
      }
    }

    return obj;
  };


  // ### merge
  //
  // Recursively an type-checked merge things. Useful to enforce defaultOptions
  // types in Classes Instances !
  //
  // No parameters are affected, it return a shallowCopy of the first merged with
  // the nexts !
  //
  // **Exemple**:
  // ```
  //var foo = {
  //  arr : {
  //    pirate : false,
  //    fakelimb   : [
  //      'leftHand'
  //    ]
  //  }
  //};
  //
  //var bar = {
  //  arr : {
  //    pirate : true,
  //    fakelimb   : false
  //  }
  //};
  //
  //var result = etho.merge(foo, bar);
  //
  ////result => {arr:{pirate:true,fakelimb:['leftHand']}}
  //
  // ```
  etho.merge = function ethoMerge(target, seed/*, seed*… */){
    var _return = etho.shallowCopy(target);
    if(etho.isA('undefined', seed)){ return _return; }
    var args = etho.toArraySliced(arguments, 1);
    for (var i = 0; i < args.length; i++) {
      var src = args[i];
      for (var prop in _return) {
        if (
          _return.hasOwnProperty(prop) &&
          !etho.isA('function',_return[prop]) &&
          etho.getType(_return[prop]) === etho.getType(src[prop])
        ){
          switch(etho.getType(_return[prop])){
            case 'object':
              _return[prop] = etho.merge(_return[prop], src[prop]);
            break;
            default:
              _return[prop] = src[prop];
          }
        } else { continue; }
      }
    }
    return _return;
  };


  // ### deepAccess
  //
  //With this function you can, using a string path, inside imbricated objects:
  //* Retrieve a value
  //* Alter a value
  //* Execute a method
  //
  // **Exemple**:
  // ```
  //var target = {
  //  foo: {
  //    bar: {
  //      baz: [
  //        'madness'
  //      ]
  //    }
  //  }
  //};
  //
  //deepAccess(target, 'foo.bar.baz').push('sparta');
  ////target → {foo:{bar:{baz:['madness', 'sparta']}}}
  //
  // ```
  etho.deepAccess = function ethoDeepAccess(obj, path){
    if(etho.isA('undefined',obj)) throw new Error('no object to navigate inside !');
    if(etho.isA('string',path)){
      path = path.replace(/\[/g, '.').replace(/\]/g, '').split('.');
    }
    return path.length === 1 ? obj[path.shift()] : etho.deepAccess(obj[path.shift()], path);
  };

  // ### inherit
  //
  //
  // **Exemple**:
  // ```
  // ```
  etho.inherit = function ethoInherit(child, parent) {
    for (var key in parent) {
      if ({}.hasOwnProperty.call(parent, key)){
        child[key] = parent[key];
      }
    }

    var wrapper = function wrapper() { this.constructor = child; }

    wrapper.prototype = parent.prototype;
    child.prototype = new wrapper();
    child.__super__ = parent.prototype;
    return child;
  };


  // ### x
  //
  //
  // **Exemple**:
  // ```
  // ```
  etho.x = function ethoX(classname, customConstructor, parentClass){
    if(etho.isA('undefined', classname)){
      throw new Error('classname is missing !');
    }
    var args = etho.toArraySliced(arguments, 1);

    if(etho.isA('undefined', parentClass)){
      parentClass = function Lambda(){};
    }

    var CHILD = this[classname] = customConstructor || etho.x.minimalConstructor();

    return function protoCreation(proto){

      return (function(parent){
        etho.inherit(CHILD, parent);

        for(var attr in proto){
          if( proto.hasOwnProperty( attr ) ) {
            CHILD.prototype[attr] = proto[attr];
          }
        }
        CHILD.prototype.__type__ = classname;

        var autoSuper = function autoSuper(method){
          if(
            !etho.isA('undefined', CHILD.__super__) &&
            etho.isA('undefined', CHILD.__super__[method])
          ){
            return CHILD.__super__[method];
          }
        };

        for(var oldattr in parent){
          if( parent.hasOwnProperty( oldattr ) ) {
            CHILD.prototype[oldattr] = autoSuper(oldattr);
          }
        }

        return CHILD;

      })(parentClass);
    }
  };


  // ### x.minimalConstructor
  //
  //
  // **Exemple**:
  // ```
  // ```
  etho.x.minimalConstructor = function(){ return function ethoGenericConstructor(options){
    if(this.defaultOptions){
      this.options = etho.merge(this.defaultOptions, options);
    }else{
      this.options = options;
    }
    if( this.init ){ this.init(); }
    if( this.listen ){ this.listen(); }
    return this;
  };};

  return etho;
}));