sketchable.utils.js

/* eslint-env browser */

(function() {
  var cache = [0], expando = 'data' + Date.now();
  function data(elem) {
    var cacheIndex   = elem[expando],
      nextCacheIndex = cache.length;
    if (!cacheIndex) {
      cacheIndex = elem[expando] = nextCacheIndex;
      cache[cacheIndex] = {};
    }
    return cache[cacheIndex];
  };
  /**
   * Add/Read private data to a DOM element.
   * @global
   * @method
   * @param {object} elem - DOM element to bind data to.
   * @return {void}
   * @example
   * var elem = document.getElementById('foo');
   * // Attach private data to element:
   * dataBind(elem).someName = { value: 42 };
   * dataBind(elem)['other-name'] = { value: 43 };
   * // Read private data from element:
   * var some = dataBind(elem).someName;
   * var other = dataBind(elem)['other-name'];
   */
  window.dataBind = data;
})();

/**
 * Event manager.
 * @global
 * @module Event
 */
window.Event = {
  /**
   * Add event to DOM element.
   * @memberof module:Event
   * @param {object|string} elem - DOM element or selector.
   * @param {string} type - Event type.
   * @param {function} fn - Callback.
   * @return {void}
   * @example
   * Event.add(document.getElementById('foo'), 'click', function fooClick(evt) {
   *   // Element was clicked.
   * });
   * Event.add('#foo', 'click', function fooClick(evt) {
   *   // Element was clicked.
   * });
   */
  add: function(elem, type, fn) {
    if (!elem) return false;
    if (typeof elem === 'string') elem = document.querySelector(elem);
    if (elem.addEventListener) { // W3C standard
      elem.addEventListener(type, fn, false);
    } else if (elem.attachEvent) { // Old IE versions
      elem.attachEvent('on'+type, fn);
    } else { // Really old browser
      elem[type+fn] = function() {
        fn(window.event);
      };
    }
  },
  /**
   * Remove event from DOM element.
   * @memberof module:Event
   * @param {object|string} elem - DOM element or selector.
   * @param {string} type - Event type.
   * @param {function} fn - Callback.
   * @return {void}
   * @example
   * // Assuming elemen had the `fooClick` function (see previous example):
   * Event.remove(document.getElementById('foo'), 'click', fooClick);
   * Event.remove('#foo'), 'click', fooClick);
   */
  remove: function(elem, type, fn) {
    if (!elem) return false;
    if (typeof elem === 'string') elem = document.querySelector(elem);
    if (elem.removeEventListener) { // W3C standard
      elem.removeEventListener(type, fn, false);
    } else if (elem.detachEvent) { // Old IE versions
      elem.detachEvent('on'+type, fn);
    } else { // Really old browser
      elem[type+fn] = null;
    }
  },
  /**
   * Determine if an event is a "right click" event.
   * @memberof module:Event
   * @param {object} ev - DOM event.
   * @return {boolean}
   * @example
   * // Assume this function is a click event listener.
   * function clickHandler(evt) {
   *   alert(Event.isRightClick(evt));
   * });
   */
  isRightClick: function(ev) {
    if (!ev) ev = window.event;
    if (ev.which) return ev.which === 3;
    else if (ev.button) return e.button === 2;
    return false;
  },

};

/**
 * A handy method to (deep) extend an object.
 * The input object is modified.
 * @global
 * @param {object} myObj - Input object.
 * @return {object}
 * @example
 * var one = { foo:1, bar: { a:true, b:false } };
 * var two = { bar: { a:false } };
 * // In this case both `ext` and `one` will be the same object.
 * var ext = deepExtend(one, two);
 * // To create a fresh copy, pass in an empty object as first arg.
 * var ext = deepExtend({}, one, two);
 * // Now `ext` is `{ foo:1, bar: { a:false, b:false } }`
 * // and `one` is left intact.
 */
window.deepExtend = function(myObj) {
  myObj = myObj || {};
  for (var i = 1; i < arguments.length; i++) {
    var obj = arguments[i];
    for (var key in obj) {
      if (obj.hasOwnProperty(key)) {
        if (typeof obj[key] === 'object') {
          myObj[key] = deepExtend(myObj[key], obj[key]);
        } else {
          myObj[key] = obj[key];
        }
      }
    }
  }
  return myObj;
};