<commit_before>/**
 * Top-level Pangaea namespace
 *
 * @namespace pan
 * @class {Object}
 */
var pan = pan || {};
(function () {
	"use strict";

	/**
	 * @namespace FLAGS
	 * @desc Contains debug-related flags
	 */
	pan.FLAGS = {
		DRAW_FPS: 1,
		DRAW_HIT_REGIONS: 0,
		LOG_KEY_INPUT: 0
	};

	/**
	 * @object pan.core
	 * @desc
	 */
	pan.core = {
		start: function () {
			pan.core.then = Date.now();
		},
		frame: function () {
			pan.core.delta();
			pan.core.update();
			pan.core.render();
			pan.core.animationFrame = window.requestAnimationFrame(pan.core.frame);
		},
		delta: function () {
			pan.core.now = Date.now();
			pan.core.delta = (pan.core.now - pan.core.then);
			pan.core.then = pan.core.now;
		},
		update: function () {
			// update state
		},
		render: function () {
			// draw to canvas
		}
	};

	//
	// augmentation
	//

	// Setup convenient prototypal inheritance function
	// Example: newObject = Object.create(oldObject);
	// http://javascript.crockford.com/prototypal.html
	if (typeof Object.create !== 'function') {
		Object.create = function (o) {
			function F() {}
			F.prototype = o;
			return new F();
		};
	}

	/**
	 * Returns a number whose value is limited to the given range.
	 *
	 * Example: limit the output of this computation to between 0 and 255
	 * (x * 255).clamp(0, 255)
	 *
	 * @param {Number} min The lower boundary of the output range
	 * @param {Number} max The upper boundary of the output range
	 * @returns A number in the range [min, max]
	 * @type Number
	 */
	Number.prototype.clamp = function (min, max) {
		return Math.min(Math.max(this, min), max);
	};

	/**
	 * Draws an ellipse to the canvas 2d rendering context.
	 *
	 * @param {number} centerX The x coordinate of the center point of the ellipse.
	 * @param {number} centerY The y coordinate of the center point of the ellipse.
	 * @param {number} width The semi-major axis of the ellipse.
	 * @param {number} height The semi-minor axis of the ellipse.
	 */
	CanvasRenderingContext2D.prototype.ellipse = function (centerX, centerY, width, height) {

		this.moveTo(centerX, centerY - (height / 2)); // A1

		this.bezierCurveTo(centerX + (width / 2), centerY - (height / 2), // C1
			centerX + (width / 2), centerY + (height / 2), // C2
			centerX, centerY + (height / 2)); // A2

		this.bezierCurveTo(centerX - (width / 2), centerY + (height / 2), // C3
			centerX - (width / 2), centerY - (height / 2), // C4
			centerX, centerY - (height / 2)); // A1
	};
}());

// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
// requestAnimationFrame polyfill by Erik Mller. Fixes from Paul Irish and Tino Zijdel.
// MIT license
(function () {
	"use strict";

    var lastTime = 0, vendors = ['ms', 'moz', 'webkit', 'o'], x, currTime, timeToCall, id;
    for (x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
        window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
        window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame'];
    }
    if (!window.requestAnimationFrame) {
        window.requestAnimationFrame = function (callback, element) {
            currTime = new Date().getTime();
            timeToCall = Math.max(0, 16 - (currTime - lastTime));
            id = window.setTimeout(function () {
				callback(currTime + timeToCall);
			}, timeToCall);
            lastTime = currTime + timeToCall;
            return id;
        };
	}
    if (!window.cancelAnimationFrame) {
        window.cancelAnimationFrame = function (id) {
            clearTimeout(id);
        };
	}
}());
<commit_msg>tweak<commit_after>/**
 * Top-level Pangaea namespace
 *
 * @namespace pan
 * @class {Object}
 */
var pan = pan || {};
(function () {
	"use strict";

	/**
	 * @namespace FLAGS
	 * @desc Contains debug-related flags
	 */
	pan.FLAGS = {
		DRAW_FPS: 1,
		DRAW_HIT_REGIONS: 0,
		LOG_KEY_INPUT: 0
	};

	/**
	 * @object pan.core
	 * @desc
	 */
	pan.core = {
		start: function () {
			pan.core.last = Date.now();
		},
		frame: function () {
			pan.core.delta();
			pan.core.update();
			pan.core.render();
			pan.core.animationFrame = window.requestAnimationFrame(pan.core.frame);
		},
		delta: function () {
			pan.core.now = Date.now();
			pan.core.delta = (pan.core.now - pan.core.last);
			pan.core.last = pan.core.now;
		},
		update: function () {
			// update state
		},
		render: function () {
			// draw to canvas
		}
	};

	//
	// augmentation
	//

	// Setup convenient prototypal inheritance function
	// Example: newObject = Object.create(oldObject);
	// http://javascript.crockford.com/prototypal.html
	if (typeof Object.create !== 'function') {
		Object.create = function (o) {
			function F() {}
			F.prototype = o;
			return new F();
		};
	}

	/**
	 * Returns a number whose value is limited to the given range.
	 *
	 * Example: limit the output of this computation to between 0 and 255
	 * (x * 255).clamp(0, 255)
	 *
	 * @param {Number} min The lower boundary of the output range
	 * @param {Number} max The upper boundary of the output range
	 * @returns A number in the range [min, max]
	 * @type Number
	 */
	Number.prototype.clamp = function (min, max) {
		return Math.min(Math.max(this, min), max);
	};

	/**
	 * Draws an ellipse to the canvas 2d rendering context.
	 *
	 * @param {number} centerX The x coordinate of the center point of the ellipse.
	 * @param {number} centerY The y coordinate of the center point of the ellipse.
	 * @param {number} width The semi-major axis of the ellipse.
	 * @param {number} height The semi-minor axis of the ellipse.
	 */
	CanvasRenderingContext2D.prototype.ellipse = function (centerX, centerY, width, height) {

		this.moveTo(centerX, centerY - (height / 2)); // A1

		this.bezierCurveTo(centerX + (width / 2), centerY - (height / 2), // C1
			centerX + (width / 2), centerY + (height / 2), // C2
			centerX, centerY + (height / 2)); // A2

		this.bezierCurveTo(centerX - (width / 2), centerY + (height / 2), // C3
			centerX - (width / 2), centerY - (height / 2), // C4
			centerX, centerY - (height / 2)); // A1
	};
}());

// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
// requestAnimationFrame polyfill by Erik Mller. Fixes from Paul Irish and Tino Zijdel.
// MIT license
(function () {
	"use strict";

    var lastTime = 0, vendors = ['ms', 'moz', 'webkit', 'o'], x, currTime, timeToCall, id;
    for (x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
        window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
        window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame'];
    }
    if (!window.requestAnimationFrame) {
        window.requestAnimationFrame = function (callback, element) {
            currTime = new Date().getTime();
            timeToCall = Math.max(0, 16 - (currTime - lastTime));
            id = window.setTimeout(function () {
				callback(currTime + timeToCall);
			}, timeToCall);
            lastTime = currTime + timeToCall;
            return id;
        };
	}
    if (!window.cancelAnimationFrame) {
        window.cancelAnimationFrame = function (id) {
            clearTimeout(id);
        };
	}
}());
<|endoftext|>


<commit_before>  /*!
  * ----------------
  * Treasure Core JS
  * ----------------
  */

  /**
   * Creates a new Treasure logger
   * @constructor Treasure
   * @param {Object} config - Configuration object
   * @param {String} config.database - Database to which the events should get saved
   * @param {String} config.writeKey - Write-only API key for your account
   * @param {String} [config.protocol="auto"] - Protocol to use for url, allowed values: auto, http, https
   * @param {String} [config.requestType="auto"] - Request type for sending events, allowed values: auto, xhr, jsonp
   * @param {String} [config.host="in.treasuredata.com"] - Host to which events get sent
   */
  function Treasure() {
    return _init.apply(this, arguments);
  }

  function _validate(config) {
    if (_isUndefined(config)) {
      throw new Error('Check out our JavaScript SDK Usage Guide: http://docs.treasuredata.com/articles/javascript-sdk/');
    }
    if (_isUndefined(config.database) || _type(config.database) !== 'String' || !config.database.length) {
      throw new Error('Please provide a database');
    }
    if (!(/^[a-z0-9_]{3,255}$/.test(config.database))) {
      throw new Error('Database must be between 3 and 255 characters and must consist only of lower case letters, numbers, and _');
    }
  }

  function _init(config) {
    _validate(config);
    this.configure(config);
  }

  Treasure.prototype.configure = function(config){
    _validate(config);
    config.host = (_isUndefined(config.host)) ? 'in.treasuredata.com' : config.host.replace(/.*?:\/\//g, '');
    config.protocol = _set_protocol(config.protocol);
    config.requestType = _set_request_type(config.requestType);

    this.client = {
      database: config.database,
      writeKey: config.writeKey,
      globals: {},
      endpoint: config.protocol + '://' + config.host,
      requestType: config.requestType
    };

    Treasure.trigger('client', this, config);
    this.trigger('ready');

    return this;
  };

  // Private
  // --------------------------------

  function _extend(target){
    for (var i = 1; i < arguments.length; i++) {
      for (var prop in arguments[i]){
        // if ((target[prop] && _type(target[prop]) == 'Object') && (arguments[i][prop] && _type(arguments[i][prop]) == 'Object')){
        target[prop] = arguments[i][prop];
      }
    }
    return target;
  }

  function _isUndefined(obj) {
    return obj === void 0;
  }

  function _type(obj){
    var text = (obj && obj.constructor) ? obj.constructor.toString() : void 0;
	  return (text) ? text.match(/function (.*)\(/)[1] : 'Null';
  }

  function _each(o, cb, s){
    var n;
    if (!o){
      return 0;
    }
    s = !s ? o : s;
    if (_type(o)==='array'){ // is(o.length)
      // Indexed arrays, needed for Safari
      for (n=0; n<o.length; n++) {
        if (cb.call(s, o[n], n, o) === false){
          return 0;
        }
      }
    } else {
      // Hashtables
      for (n in o){
        if (o.hasOwnProperty(n)) {
          if (cb.call(s, o[n], n, o) === false){
            return 0;
          }
        }
      }
    }
    return 1;
  }

  function _set_protocol(value) {
    value = value || 'auto';
    switch(value) {
      case 'http':
        return 'http';
      case 'https':
        return 'https';
      default:
        return 'http';
    }
  }

  function _set_request_type(value) {
    value = value || 'auto';
    if (value !== 'auto') {
      return value;
    } else {
      if ((_type(XMLHttpRequest)==='Object'||_type(XMLHttpRequest)==='Function') && 'withCredentials' in new XMLHttpRequest()) {
        return 'xhr';
      } else {
        return 'jsonp';
      }
    }
  }

  function _build_url(path) {
    return this.client.endpoint + '/js/v3/event/' + this.client.database + path;
  }

  var _request = {

    xhr: function(method, url, headers, body, apiKey, success, error){
      if (!apiKey) {
        return Treasure.log('Please provide an apikey from https://console.treasuredata.com/users/current');
      }
      var xhr = new XMLHttpRequest();
      xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
          if (xhr.status >= 200 && xhr.status < 300) {
            var response;
            try {
              response = JSON.parse(xhr.responseText);
            } catch (e) {
              Treasure.log('Could not JSON parse HTTP response: ' + xhr.responseText);
              if (error) {
                error(xhr, e);
              }
            }
            if (success && response) {
              success(response);
            }
          } else {
            Treasure.log('HTTP request failed.');
            if (error) {
              error(xhr, null);
            }
          }
        }
      };
      xhr.open(method, url, true);
      if (apiKey) {
        xhr.setRequestHeader('X-TD-WRITE-KEY', apiKey);
      }
      if (body) {
        xhr.setRequestHeader('Content-Type', 'application/json');
      }
      if (headers) {
        for (var headerName in headers) {
          if (headers.hasOwnProperty(headerName)) {
            xhr.setRequestHeader(headerName, headers[headerName]);
          }
        }
      }
      var toSend = body ? JSON.stringify(body) : null;
      xhr.send(toSend);
    },

    jsonp: function(url, apiKey, success, error){
      if (!apiKey) {
        return Treasure.log('Please provide an apikey from https://console.treasuredata.com/users/current');
      }
      if (apiKey && url.indexOf('api_key') < 0) {
        var delimiterChar = url.indexOf('?') > 0 ? '&' : '?';
        url = url + delimiterChar + 'api_key=' + apiKey;
      }

      var callbackName = 'TreasureJSONPCallback' + new Date().getTime();
      while (callbackName in window) {
        callbackName += 'a';
      }
      var loaded = false;
      window[callbackName] = function (response) {
        loaded = true;
        if (success && response) {
          success(response);
        }
        // Remove this from the namespace
        window[callbackName] = undefined;
      };
      url = url + '&callback=' + callbackName;
      var script = document.createElement('script');
      script.id = 'td-jsonp';
      script.src = url;
      document.getElementsByTagName('head')[0].appendChild(script);
      // for early IE w/ no onerror event
      script.onreadystatechange = function() {
        if (loaded === false && this.readyState === 'loaded') {
          loaded = true;
          if (error) {
            error();
          }
        }
      };
      // non-ie, etc
      script.onerror = function() {
        if (loaded === false) { // on IE9 both onerror and onreadystatechange are called
          loaded = true;
          if (error) {
            error();
          }
        }
      };
    }

    // beacon: function(url, apiKey, success, error){
    //   if (apiKey && url.indexOf('api_key') < 0) {
    //     var delimiterChar = url.indexOf('?') > 0 ? '&' : '?';
    //     url = url + delimiterChar + 'api_key=' + apiKey;
    //   }
    //   var loaded = false, img = document.createElement('img');
    //   img.onload = function() {
    //     loaded = true;
    //     if ('naturalHeight' in this) {
    //       if (this.naturalHeight + this.naturalWidth === 0) {
    //         this.onerror(); return;
    //       }
    //     } else if (this.width + this.height === 0) {
    //       this.onerror(); return;
    //     }
    //     if (success) {
    //       success({created: true});
    //     }
    //   };
    //   img.onerror = function() {
    //     loaded = true;
    //     if (error) {
    //       error();
    //     }
    //   };
    //   img.src = url;
    // }
  };

  // -------------------------------
  // Treasure.Events
  // -------------------------------

  var Events = Treasure.Events = {
    on: function(name, callback) {
      this.listeners = this.listeners || {};
      var events = this.listeners[name] || (this.listeners[name] = []);
      events.push({callback: callback});
      return this;
    },
    off: function(name, callback) {
      if (!name && !callback) {
        this.listeners = void 0;
        delete this.listeners;
        return this;
      }
      var events = this.listeners[name] || [];
      for (var i = events.length; i--;) {
        if (callback && callback === events[i].callback) {
          this.listeners[name].splice(i, 1);
        }
        if (!callback || events.length === 0) {
          this.listeners[name] = void 0;
          delete this.listeners[name];
        }
      }
      return this;
    },
    trigger: function(name) {
      if (!this.listeners) {
        return this;
      }
      var args = Array.prototype.slice.call(arguments, 1);
      var events = this.listeners[name] || [];
      for (var i = 0; i < events.length; i++) {
        events[i].callback.apply(this, args);
      }
      return this;
    }
  };
  _extend(Treasure.prototype, Events);
  _extend(Treasure, Events);

  Treasure.loaded = true;

  // Expose utils
  Treasure.utils = {
    each: _each,
    extend: _extend
  };

  Treasure.ready = function(callback){
    if (Treasure.loaded) {
      callback();
    } else {
      Treasure.on('ready', callback);
    }
  };

  Treasure.log = function(message) {
    if (typeof console === 'object') {
      console.log('[Treasure]', message);
    }
  };

  // -------------------------------
  // Treasure.Plugins
  // -------------------------------

  // var Plugins = Treasure.Plugins = {};

<commit_msg>fix(protocol): detect when set to auto<commit_after>  /*!
  * ----------------
  * Treasure Core JS
  * ----------------
  */

  /**
   * Creates a new Treasure logger
   * @constructor Treasure
   * @param {Object} config - Configuration object
   * @param {String} config.database - Database to which the events should get saved
   * @param {String} config.writeKey - Write-only API key for your account
   * @param {String} [config.protocol="auto"] - Protocol to use for url, allowed values: auto, http, https
   * @param {String} [config.requestType="auto"] - Request type for sending events, allowed values: auto, xhr, jsonp
   * @param {String} [config.host="in.treasuredata.com"] - Host to which events get sent
   */
  function Treasure() {
    return _init.apply(this, arguments);
  }

  function _validate(config) {
    if (_isUndefined(config)) {
      throw new Error('Check out our JavaScript SDK Usage Guide: http://docs.treasuredata.com/articles/javascript-sdk/');
    }
    if (_isUndefined(config.database) || _type(config.database) !== 'String' || !config.database.length) {
      throw new Error('Please provide a database');
    }
    if (!(/^[a-z0-9_]{3,255}$/.test(config.database))) {
      throw new Error('Database must be between 3 and 255 characters and must consist only of lower case letters, numbers, and _');
    }
  }

  function _init(config) {
    _validate(config);
    this.configure(config);
  }

  Treasure.prototype.configure = function(config){
    _validate(config);
    config.host = (_isUndefined(config.host)) ? 'in.treasuredata.com' : config.host.replace(/.*?:\/\//g, '');
    config.protocol = _set_protocol(config.protocol);
    config.requestType = _set_request_type(config.requestType);

    this.client = {
      database: config.database,
      writeKey: config.writeKey,
      globals: {},
      endpoint: config.protocol + '://' + config.host,
      requestType: config.requestType
    };

    Treasure.trigger('client', this, config);
    this.trigger('ready');

    return this;
  };

  // Private
  // --------------------------------

  function _extend(target){
    for (var i = 1; i < arguments.length; i++) {
      for (var prop in arguments[i]){
        // if ((target[prop] && _type(target[prop]) == 'Object') && (arguments[i][prop] && _type(arguments[i][prop]) == 'Object')){
        target[prop] = arguments[i][prop];
      }
    }
    return target;
  }

  function _isUndefined(obj) {
    return obj === void 0;
  }

  function _type(obj){
    var text = (obj && obj.constructor) ? obj.constructor.toString() : void 0;
	  return (text) ? text.match(/function (.*)\(/)[1] : 'Null';
  }

  function _each(o, cb, s){
    var n;
    if (!o){
      return 0;
    }
    s = !s ? o : s;
    if (_type(o)==='array'){ // is(o.length)
      // Indexed arrays, needed for Safari
      for (n=0; n<o.length; n++) {
        if (cb.call(s, o[n], n, o) === false){
          return 0;
        }
      }
    } else {
      // Hashtables
      for (n in o){
        if (o.hasOwnProperty(n)) {
          if (cb.call(s, o[n], n, o) === false){
            return 0;
          }
        }
      }
    }
    return 1;
  }

  function _set_protocol(value) {
    value = value || 'auto';
    switch(value) {
      case 'http':
        return 'http';
      case 'https':
        return 'https';
      default:
        return 'https:' === document.location.protocol ? 'https' : 'http';
    }
  }

  function _set_request_type(value) {
    value = value || 'auto';
    if (value !== 'auto') {
      return value;
    } else {
      if ((_type(XMLHttpRequest)==='Object'||_type(XMLHttpRequest)==='Function') && 'withCredentials' in new XMLHttpRequest()) {
        return 'xhr';
      } else {
        return 'jsonp';
      }
    }
  }

  function _build_url(path) {
    return this.client.endpoint + '/js/v3/event/' + this.client.database + path;
  }

  var _request = {

    xhr: function(method, url, headers, body, apiKey, success, error){
      if (!apiKey) {
        return Treasure.log('Please provide an apikey from https://console.treasuredata.com/users/current');
      }
      var xhr = new XMLHttpRequest();
      xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
          if (xhr.status >= 200 && xhr.status < 300) {
            var response;
            try {
              response = JSON.parse(xhr.responseText);
            } catch (e) {
              Treasure.log('Could not JSON parse HTTP response: ' + xhr.responseText);
              if (error) {
                error(xhr, e);
              }
            }
            if (success && response) {
              success(response);
            }
          } else {
            Treasure.log('HTTP request failed.');
            if (error) {
              error(xhr, null);
            }
          }
        }
      };
      xhr.open(method, url, true);
      if (apiKey) {
        xhr.setRequestHeader('X-TD-WRITE-KEY', apiKey);
      }
      if (body) {
        xhr.setRequestHeader('Content-Type', 'application/json');
      }
      if (headers) {
        for (var headerName in headers) {
          if (headers.hasOwnProperty(headerName)) {
            xhr.setRequestHeader(headerName, headers[headerName]);
          }
        }
      }
      var toSend = body ? JSON.stringify(body) : null;
      xhr.send(toSend);
    },

    jsonp: function(url, apiKey, success, error){
      if (!apiKey) {
        return Treasure.log('Please provide an apikey from https://console.treasuredata.com/users/current');
      }
      if (apiKey && url.indexOf('api_key') < 0) {
        var delimiterChar = url.indexOf('?') > 0 ? '&' : '?';
        url = url + delimiterChar + 'api_key=' + apiKey;
      }

      var callbackName = 'TreasureJSONPCallback' + new Date().getTime();
      while (callbackName in window) {
        callbackName += 'a';
      }
      var loaded = false;
      window[callbackName] = function (response) {
        loaded = true;
        if (success && response) {
          success(response);
        }
        // Remove this from the namespace
        window[callbackName] = undefined;
      };
      url = url + '&callback=' + callbackName;
      var script = document.createElement('script');
      script.id = 'td-jsonp';
      script.src = url;
      document.getElementsByTagName('head')[0].appendChild(script);
      // for early IE w/ no onerror event
      script.onreadystatechange = function() {
        if (loaded === false && this.readyState === 'loaded') {
          loaded = true;
          if (error) {
            error();
          }
        }
      };
      // non-ie, etc
      script.onerror = function() {
        if (loaded === false) { // on IE9 both onerror and onreadystatechange are called
          loaded = true;
          if (error) {
            error();
          }
        }
      };
    }

    // beacon: function(url, apiKey, success, error){
    //   if (apiKey && url.indexOf('api_key') < 0) {
    //     var delimiterChar = url.indexOf('?') > 0 ? '&' : '?';
    //     url = url + delimiterChar + 'api_key=' + apiKey;
    //   }
    //   var loaded = false, img = document.createElement('img');
    //   img.onload = function() {
    //     loaded = true;
    //     if ('naturalHeight' in this) {
    //       if (this.naturalHeight + this.naturalWidth === 0) {
    //         this.onerror(); return;
    //       }
    //     } else if (this.width + this.height === 0) {
    //       this.onerror(); return;
    //     }
    //     if (success) {
    //       success({created: true});
    //     }
    //   };
    //   img.onerror = function() {
    //     loaded = true;
    //     if (error) {
    //       error();
    //     }
    //   };
    //   img.src = url;
    // }
  };

  // -------------------------------
  // Treasure.Events
  // -------------------------------

  var Events = Treasure.Events = {
    on: function(name, callback) {
      this.listeners = this.listeners || {};
      var events = this.listeners[name] || (this.listeners[name] = []);
      events.push({callback: callback});
      return this;
    },
    off: function(name, callback) {
      if (!name && !callback) {
        this.listeners = void 0;
        delete this.listeners;
        return this;
      }
      var events = this.listeners[name] || [];
      for (var i = events.length; i--;) {
        if (callback && callback === events[i].callback) {
          this.listeners[name].splice(i, 1);
        }
        if (!callback || events.length === 0) {
          this.listeners[name] = void 0;
          delete this.listeners[name];
        }
      }
      return this;
    },
    trigger: function(name) {
      if (!this.listeners) {
        return this;
      }
      var args = Array.prototype.slice.call(arguments, 1);
      var events = this.listeners[name] || [];
      for (var i = 0; i < events.length; i++) {
        events[i].callback.apply(this, args);
      }
      return this;
    }
  };
  _extend(Treasure.prototype, Events);
  _extend(Treasure, Events);

  Treasure.loaded = true;

  // Expose utils
  Treasure.utils = {
    each: _each,
    extend: _extend
  };

  Treasure.ready = function(callback){
    if (Treasure.loaded) {
      callback();
    } else {
      Treasure.on('ready', callback);
    }
  };

  Treasure.log = function(message) {
    if (typeof console === 'object') {
      console.log('[Treasure]', message);
    }
  };

  // -------------------------------
  // Treasure.Plugins
  // -------------------------------

  // var Plugins = Treasure.Plugins = {};

<|endoftext|>


<commit_before>(function( window, $ ) {
  var $window, $doc;

  $window = $(window);
  $doc = $( document.documentElement );

  window.componentNamespace = window.componentNamespace || window;

  Function.prototype.bind = function( context ) {
    var self = this;

    return function() {
      self.apply( context, arguments );
    };
  };

  var Tau = window.componentNamespace.Tau = function( element ) {
    var startIndex;

    this.element = element;
    this.$element = $( element );
    this.$initial = this.$element.find( "img" );
    this.$loading = this.$element.find( ".loading" );

    // make sure the initial image stays visible after enhance
    this.$initial.addClass( "focused" );

    // hide all other images
    this.$element.addClass( "enhanced" );

    // create the rest of the images
    this.createImages();

    // set the initial index and image
    this.goto( 0 );

    // start the automatic rotation
    this.autoRotate();

    // setup the event bindings for touch drag and mouse drag rotation
    this.bind();
  };

  Tau.autoRotateDelay = 64;
  Tau.verticalScrollRatio = 4;

  Tau.prototype.change = function( delta ) {
    this.goto( this.index + delta );
  };

  Tau.prototype.goto = function( index ) {
    var $next, normalizedIndex;

    index = index % this.$images.length;

    // stay within the bounds of the array
    normalizedIndex = (this.$images.length + index) % this.$images.length;

    // set the next image that's going to be shown/focused
    $next = this.$images.eq( normalizedIndex );

    // skip this action if the desired image isn't loaded yet
    // TODO do something fancier here instead of just throwing up hands
    if( !$next[0].tauImageLoaded ) {
      this.showLoading();
      return;
    }

    // record the updated index only after advancing is possible
    this.index = normalizedIndex;

    // hide the old focused image
    if( this.$current ) {
      this.$current.removeClass( "focused" );
    }

    // record the current focused image and make it visible
    this.$current = $next;

    // show the new focused image
    this.$current.addClass( "focused" );
  };

  // TODO transplant the attributes from the initial image
  Tau.prototype.createImages = function() {
    var src, frames, $new, boundImageLoaded;

    // avoid doing rebinding in a tight loop
    boundImageLoaded = this.imageLoaded.bind( this );

    src = this.$initial.attr( "data-src-template" );
    frames = parseInt( this.$initial.attr( "data-frames" ), 10 );

    // mark the initial image as loaded
    this.markImageLoaded( this.$initial[0] );

    for( var i = 2; i <= frames; i++) {
      $new = $( "" );

      // record when each image has loaded
      $new.bind( "load", boundImageLoaded );

      this.$element.append( $new );
    }

    this.$images = this.$element.find( "img" );
  };

  Tau.prototype.imageLoaded = function( event ) {
    this.markImageLoaded( event.target );
    this.hideLoading();
  };

  Tau.prototype.markImageLoaded = function( element ) {
    element.tauImageLoaded = true;
  };

  Tau.prototype.bind = function() {
    this.$element.bind( "mousedown touchstart", this.track.bind(this) );
  };

  Tau.prototype.autoRotate = function() {
    this.autoInterval = setInterval(function() {
      this.change( 1 );
    }.bind(this), Tau.autoRotateDelay);
  };

  Tau.prototype.stopAutoRotate = function() {
    clearInterval( this.autoInterval );
  };

  Tau.prototype.track = function( event ) {
    var point;

    // prevent dragging behavior for mousedown
    if( event.type === "mousedown"  ){
      event.preventDefault();
    }

    if( this.tracking ) {
      return;
    }

    $doc.one( "mouseup", this.release.bind(this) );
    $doc.one( "touchend", this.release.bind(this) );

    this.tracking = true;

    this.prevPoint = undefined;
    this.prevTime = undefined;

    this.cursorGrab();

    // calculate/store how many pixels makes for an image switch
    this.rotateThreshold = $doc[0].clientWidth / this.$images.length;

    // record the x for threshold calculations
    point = this.getPoint( event );
    this.downX = point.x;
    this.downY = point.y;
    this.downIndex = this.index;

    $doc.bind( "mousemove", this.rotateEvent.bind(this) );
    $doc.bind( "touchmove", this.rotateEvent.bind(this) );
  };

  Tau.prototype.release = function( event ) {
    var distance, time, point, velocity;

    if( !this.prevPoint || !this.prevPrevPoint ) {
      return;
    }

    distance = this.prevPoint.x - this.prevPrevPoint.x;
    time = this.prevTime - this.prevPrevTime;
    velocity = distance / (time/20);

    // console.log( "distance: " + distance );
    // console.log( "time: " + time );
    console.log( "initial velocity: " + velocity );

    var timeout = setInterval(function() {
      if( !this.prevPoint || !this.prevPrevPoint ) {
        clearInterval(timeout);
        return;
      }

      var point = {
        x: this.prevPoint.x + velocity,
        y: this.prevPoint.y
      };

      this.rotate( point );

      if( velocity > 0 ){
        velocity = velocity - 3;

        if( velocity <= 0 ){
          clearInterval(timeout);
        }
      } else {
        velocity = velocity + 2;

        if( velocity >= 0 ){
          clearInterval(timeout);
        }
      }
    }.bind(this), 20);

    this.cursorRelease();

    // TODO sort out why shoestring borks when unbinding with a string split list
    $doc.unbind( "mousemove", this.rotateEvent.bind(this) );
    $doc.unbind( "touchmove", this.rotateEvent.bind(this) );

    this.tracking = false;
  };

  Tau.prototype.cursorGrab = function() {
    $doc.addClass( "grabbing" );
  };

  Tau.prototype.cursorRelease = function() {
    $doc.removeClass( "grabbing" );
  };

  Tau.prototype.showLoading = function() {
    this.$loading.attr( "style" , "display: block" );
  };

  Tau.prototype.hideLoading = function() {
    this.$loading.attr( "style" , "display: none" );
  };

  Tau.prototype.getPoint = function( event ) {
    var touch = event.touches || (event.originalEvent && event.originalEvent.touches);

    if( touch ){
      return {
        x: touch[0].pageX,
        y: touch[0].pageY
      };
    }

    return {
      x: event.pageX,
      y: event.pageY
    };
  };

  Tau.prototype.rotateEvent = function( event ) {
    if( this.rotate(this.getPoint(event)) ){
      event.preventDefault();
    };
  };

  Tau.prototype.rotate = function( point ) {
    var deltaX, deltaY, point;

    point = point || this.getPoint( event );

    deltaX = point.x - this.downX;
    deltaY = point.y - this.downY;

    // if the movement on the Y dominates X then skip and allow scroll
    if( Math.abs(deltaY) / Math.abs(deltaX) >= Tau.verticalScrollRatio ) {
      return false;
    }

    // NOTE works better on mousedown, here allows autorotate to continue on scroll though
    this.stopAutoRotate();

    this.prevPrevTime = this.prevTime;
    this.prevPrevPoint = this.prevPoint;

    // record the most recent drag point for decel on release
    this.prevTime = Date.now();
    this.prevPoint = point;

    // NOTE to reverse the spin direction add the delta/thresh to the downIndex
    // NOTE it might be better to prevent anyway for slow drags across the image
    if( Math.abs(deltaX) >= this.rotateThreshold ) {
      this.goto( this.downIndex - Math.round(deltaX / this.rotateThreshold) );
      return true;
    }
  };
})(this, jQuery);
<commit_msg>further rotate cleanup<commit_after>(function( window, $ ) {
  var $window, $doc;

  $window = $(window);
  $doc = $( document.documentElement );

  window.componentNamespace = window.componentNamespace || window;

  Function.prototype.bind = function( context ) {
    var self = this;

    return function() {
      self.apply( context, arguments );
    };
  };

  var Tau = window.componentNamespace.Tau = function( element ) {
    var startIndex;

    this.element = element;
    this.$element = $( element );
    this.$initial = this.$element.find( "img" );
    this.$loading = this.$element.find( ".loading" );

    // make sure the initial image stays visible after enhance
    this.$initial.addClass( "focused" );

    // hide all other images
    this.$element.addClass( "enhanced" );

    // create the rest of the images
    this.createImages();

    // set the initial index and image
    this.goto( 0 );

    // start the automatic rotation
    this.autoRotate();

    // setup the event bindings for touch drag and mouse drag rotation
    this.bind();
  };

  Tau.autoRotateDelay = 64;
  Tau.verticalScrollRatio = 4;

  Tau.prototype.change = function( delta ) {
    this.goto( this.index + delta );
  };

  Tau.prototype.goto = function( index ) {
    var $next, normalizedIndex;

    index = index % this.$images.length;

    // stay within the bounds of the array
    normalizedIndex = (this.$images.length + index) % this.$images.length;

    // set the next image that's going to be shown/focused
    $next = this.$images.eq( normalizedIndex );

    // skip this action if the desired image isn't loaded yet
    // TODO do something fancier here instead of just throwing up hands
    if( !$next[0].tauImageLoaded ) {
      this.showLoading();
      return;
    }

    // record the updated index only after advancing is possible
    this.index = normalizedIndex;

    // hide the old focused image
    if( this.$current ) {
      this.$current.removeClass( "focused" );
    }

    // record the current focused image and make it visible
    this.$current = $next;

    // show the new focused image
    this.$current.addClass( "focused" );
  };

  // TODO transplant the attributes from the initial image
  Tau.prototype.createImages = function() {
    var src, frames, $new, boundImageLoaded;

    // avoid doing rebinding in a tight loop
    boundImageLoaded = this.imageLoaded.bind( this );

    src = this.$initial.attr( "data-src-template" );
    frames = parseInt( this.$initial.attr( "data-frames" ), 10 );

    // mark the initial image as loaded
    this.markImageLoaded( this.$initial[0] );

    for( var i = 2; i <= frames; i++) {
      $new = $( "" );

      // record when each image has loaded
      $new.bind( "load", boundImageLoaded );

      this.$element.append( $new );
    }

    this.$images = this.$element.find( "img" );
  };

  Tau.prototype.imageLoaded = function( event ) {
    this.markImageLoaded( event.target );
    this.hideLoading();
  };

  Tau.prototype.markImageLoaded = function( element ) {
    element.tauImageLoaded = true;
  };

  Tau.prototype.bind = function() {
    this.$element.bind( "mousedown touchstart", this.track.bind(this) );
  };

  Tau.prototype.autoRotate = function() {
    this.autoInterval = setInterval(function() {
      this.change( 1 );
    }.bind(this), Tau.autoRotateDelay);
  };

  Tau.prototype.stopAutoRotate = function() {
    clearInterval( this.autoInterval );
  };

  Tau.prototype.track = function( event ) {
    var point;

    // prevent dragging behavior for mousedown
    if( event.type === "mousedown"  ){
      event.preventDefault();
    }

    if( this.tracking ) {
      return;
    }

    $doc.one( "mouseup", this.release.bind(this) );
    $doc.one( "touchend", this.release.bind(this) );

    this.tracking = true;

    this.prevPoint = undefined;
    this.prevTime = undefined;

    this.cursorGrab();

    // calculate/store how many pixels makes for an image switch
    this.rotateThreshold = $doc[0].clientWidth / this.$images.length;

    // record the x for threshold calculations
    point = this.getPoint( event );
    this.downX = point.x;
    this.downY = point.y;
    this.downIndex = this.index;

    $doc.bind( "mousemove", this.rotateEvent.bind(this) );
    $doc.bind( "touchmove", this.rotateEvent.bind(this) );
  };

  Tau.prototype.release = function( event ) {
    var distance, time, point, velocity;

    if( !this.prevPoint || !this.prevPrevPoint ) {
      return;
    }

    distance = this.prevPoint.x - this.prevPrevPoint.x;
    time = this.prevTime - this.prevPrevTime;
    velocity = distance / (time/20);

    // console.log( "distance: " + distance );
    // console.log( "time: " + time );
    console.log( "initial velocity: " + velocity );

    var timeout = setInterval(function() {
      if( !this.prevPoint || !this.prevPrevPoint ) {
        clearInterval(timeout);
        return;
      }

      var point = {
        x: this.prevPoint.x + velocity,
        y: this.prevPoint.y
      };

      this.rotate( point );

      if( velocity > 0 ){
        velocity = velocity - 3;

        if( velocity <= 0 ){
          clearInterval(timeout);
        }
      } else {
        velocity = velocity + 2;

        if( velocity >= 0 ){
          clearInterval(timeout);
        }
      }
    }.bind(this), 20);

    this.cursorRelease();

    // TODO sort out why shoestring borks when unbinding with a string split list
    $doc.unbind( "mousemove", this.rotateEvent.bind(this) );
    $doc.unbind( "touchmove", this.rotateEvent.bind(this) );

    this.tracking = false;
  };

  Tau.prototype.cursorGrab = function() {
    $doc.addClass( "grabbing" );
  };

  Tau.prototype.cursorRelease = function() {
    $doc.removeClass( "grabbing" );
  };

  Tau.prototype.showLoading = function() {
    this.$loading.attr( "style" , "display: block" );
  };

  Tau.prototype.hideLoading = function() {
    this.$loading.attr( "style" , "display: none" );
  };

  Tau.prototype.getPoint = function( event ) {
    var touch = event.touches || (event.originalEvent && event.originalEvent.touches);

    if( touch ){
      return {
        x: touch[0].pageX,
        y: touch[0].pageY
      };
    }

    return {
      x: event.pageX,
      y: event.pageY
    };
  };

  Tau.prototype.rotateEvent = function( event ) {
    // NOTE it might be better to prevent when the rotation returns anything BUT false
    //      so that slow drags still get the scroll prevented
    if( this.rotate(this.getPoint(event)) ){
      event.preventDefault();
    };
  };

  Tau.prototype.rotate = function( point ) {
    var deltaX, deltaY;

    deltaX = point.x - this.downX;
    deltaY = point.y - this.downY;

    // if the movement on the Y dominates X then skip and allow scroll
    if( Math.abs(deltaY) / Math.abs(deltaX) >= Tau.verticalScrollRatio ) {
      return false;
    }

    // NOTE works better on mousedown, here allows autorotate to continue on scroll though
    this.stopAutoRotate();

    this.prevPrevTime = this.prevTime;
    this.prevPrevPoint = this.prevPoint;

    // record the most recent drag point for decel on release
    this.prevTime = Date.now();
    this.prevPoint = point;

    // NOTE to reverse the spin direction add the delta/thresh to the downIndex
    if( Math.abs(deltaX) >= this.rotateThreshold ) {
      this.goto( this.downIndex - Math.round(deltaX / this.rotateThreshold) );
      return true;
    }
  };
})(this, jQuery);
<|endoftext|>


<commit_before>// Copyright (c) 2013
// All Rights Reserved
// https://github.com/msecret/aronnax
// Licensed MIT

/**
 * @file time Holds the game loop and any other timing functionality
 */

define('aronnax/Core',
  ['aronnax/Base',
    'aronnax/Logger',
    'aronnax/util',
    'aronnax/Config',
    'aronnax/shims/requestAnimationFrame'],
  function(Base, Logger, util, config, requestAnimationFrame) {
    "use strict";

    var _log = Logger.getLog('aronnax.Time');

    var Core = Base.create(Object.prototype, 'Core', {
      fps: {
        value: config.fps || 60
      },
      millisecondsPerFrame: {
        value: 1000 / config.fps
      },
      previousTime: {
        writable: true
      },
      lag: {
        writable: true
      },
      frame: {
        value: 0,
        writable: true
      },
      isRunning: {
        value: false,
        writable: true
      },
      requestId: {
        writable: true
      },

      tick: function() {
        var currentTime = (new Date()).getTime(),
            elapsed;

        elapsed = currentTime - this.previousTime;
        this.previousTime = currentTime;
        this.lag += elapsed;

        while (this.lag >= this.millisecondsPerFrame) {
          this.update();
          this.lag -= this.millisecondsPerFrame;
        }
        this.draw(this.lag / this.millisecondsPerFrame);
        this.frame++;
      },

      run: function () {
        if (!this.isRunning) {
          this.launchLoop();
          this.isRunning = true;
        }
      },

      launchLoop: function() {
        var runner,
            self = this;

        this.previousTime = (new Date()).getTime();
        this.lag = 0;

        runner = function() {
          self.tick();
          if (self.isRunning) {
            self.requestId = requestAnimationFrame(runner);
          }
        };
        runner();
      },

      stop: function() {
        this.isRunning = false;
      },

      update: function() {
        _log.log('update');
        if (this.frame > 100) {
          this.stop();
        }
      },

      draw: function() { _log.log('draw'); }

    });

    return Core;
 });

<commit_msg>Added documentation to core<commit_after>// Copyright (c) 2013
// All Rights Reserved
// https://github.com/msecret/aronnax
// Licensed MIT

/**
 * @file time Holds the game loop and any other timing functionality
 */

define('aronnax/Core',
  ['aronnax/Base',
    'aronnax/Logger',
    'aronnax/util',
    'aronnax/Config',
    'aronnax/shims/requestAnimationFrame'],
  function(Base, Logger, util, config, requestAnimationFrame) {
    "use strict";

    var _log = Logger.getLog('aronnax.Core');

    var Core = Base.create(Object.prototype, 'Core', {
      /**
       * The frames per second the game should run at defaults to 60
       * @type Number
       */
      fps: {
        value: config.fps || 60
      },
      /**
       * The number of milliseconds for each frame should be based on
       * the fps
       * @type Number
       */
      millisecondsPerFrame: {
        value: 1000 / config.fps
      },
      /**
       * The number of milliseconds for each frame should be based on
       * the fps
       * @type Date
       */
      previousTime: {
        writable: true
      },
      /**
       * The lag since the last loop completion, used to determine whether
       * to update more until the next draw.
       * @type Boolean
       */
      lag: {
        writable: true
      },
      /**
       * The total frames run since the first call.
       * @type Number
       */
      frame: {
        value: 0,
        writable: true
      },
      /**
       * Whether the game is currently running or not, used to actually
       * stop the loop.
       * @type Boolean
       */
      isRunning: {
        value: false,
        writable: true
      },
      /**
       * The ID of the animation frame, returned from requestAnimationFrame.
       * @type Number
       */
      requestId: {
        writable: true
      },

      /**
       * Moves the game forward by one tick. Will call update continually if
       * lag is larger or equal to milliseconds per frame. Will call draw
       * at the end of every loop and will update total frames.
       */
      tick: function() {
        var currentTime = (new Date()).getTime(),
            elapsed;

        elapsed = currentTime - this.previousTime;
        this.previousTime = currentTime;
        this.lag += elapsed;

        while (this.lag >= this.millisecondsPerFrame) {
          this.update();
          this.lag -= this.millisecondsPerFrame;
        }
        this.draw(this.lag / this.millisecondsPerFrame);
        this.frame++;
      },

      /**
       * Runs the game if not already running, runs the loop with launchLoop.
       */
      run: function () {
        if (!this.isRunning) {
          this.launchLoop();
          this.isRunning = true;
        }
      },

      /**
       * Creates a loop that runs tick by request animation frames. Will
       * check the isRunning boolean to see if it should continue running.
       * Initializes previousTime and lag.
       */
      launchLoop: function() {
        var runner,
            self = this;

        this.previousTime = (new Date()).getTime();
        this.lag = 0;

        runner = function() {
          self.tick();
          if (self.isRunning) {
            self.requestId = requestAnimationFrame(runner);
          }
        };
        runner();
      },

      /**
       * Stops the game by changing the isRunning variable.
       */
      stop: function() {
        this.isRunning = false;
      },

      /**
       * Update function should be overwritten
       */
      update: function() {
        _log.log('update');
        if (this.frame > 100) {
          this.stop();
        }
      },

      /**
       * Draw function should be overwritten
       */
      draw: function() { _log.log('draw'); }

    });

    return Core;
 });

<|endoftext|>


<commit_before>'use strict';

import colors from 'colors';
import createAdminApi from './adminApi';
import assign from 'object-assign';
import kongState from './kongState';
import {
    noop,
    createApi,
    removeApi,
    updateApi,
    addApiPlugin,
    removeApiPlugin,
    updateApiPlugin,
    createConsumer,
    removeConsumer,
    addConsumerCredentials,
    updateConsumerCredentials,
    removeConsumerCredentials
} from './actions';

export default async function execute(config, adminApi) {
    const actions = [
        ...apis(config.apis),
        ...consumers(config.consumers)
    ];

    return actions
        .map(_bindWorldState(adminApi))
        .reduce((promise, action) => promise.then(_executeActionOnApi(action, adminApi)), Promise.resolve(''));
}

export function apis(apis = []) {
    return apis.reduce((actions, api) => [...actions, _api(api), ..._apiPlugins(api)], []);
};

export function plugins(apiName, plugins) {
    return plugins.reduce((actions, plugin) => [...actions, _plugin(apiName, plugin)], []);
}

export function consumers(consumers = []) {
    return consumers.reduce((calls, consumer) => [...calls, _consumer(consumer), ..._consumerCredentials(consumer)], []);
}

export function credentials(username, credentials) {
    return credentials.reduce((actions, credential) => [...actions, _consumerCredential(username, credential)], []);
}

function _executeActionOnApi(action, adminApi) {
    return async () => {
        const params = await action();

        if (params.noop) {
            return Promise.resolve('No-op');
        }

        return adminApi
            .requestEndpoint(params.endpoint, params)
            .then(response => {
                console.info(
                    `\n${params.method.blue}`,
                    response.ok ? ('' + response.status).bold.green : ('' + response.status).bold.red,
                    adminApi.router(params.endpoint).blue,
                    "\n",
                    params.body ? params.body : ''
                );

                if (!response.ok) {
                    if (params.endpoint.name == 'consumer' && params.method == 'DELETE') {
                        console.log('Bug in Kong throws error, Consumer has still been removed will continue'.bold.green);

                        return response;
                    }

                    return response.text()
                        .then(content => {
                            throw new Error(`${response.statusText}\n${content}`);
                        });
                } else {
                    response.text()
                        .then(content => {
                            console.info(`Response status ${response.statusText}:`.green, "\n", JSON.parse(content));
                        });
                }

                return response;
            });
        }
}

function _bindWorldState(adminApi) {
    return f => async () => {
        const state = await kongState(adminApi);
        return f(_createWorld(state));
    }
}

function _createWorld({apis, consumers}) {
    const world = {
        hasApi: apiName => apis.some(api => api.name === apiName),
        getApi: apiName => {
            const api = apis.find(api => api.name === apiName);

            if (!api) {
                throw new Error(`Unable to find api ${apiName}`);
            }

            return api;
        },
        getPlugin: (apiName, pluginName) => {
            const plugin = world.getApi(apiName).plugins.find(plugin => plugin.name == pluginName);

            if (!plugin) {
                throw new Error(`Unable to find plugin ${pluginName}`);
            }

            return plugin;
        },
        getPluginId: (apiName, pluginName) => {
            return world.getPlugin(apiName, pluginName).id;
        },
        getPluginAttributes: (apiName, pluginName) => {
            return world.getPlugin(apiName, pluginName).config;
        },
        hasPlugin: (apiName, pluginName) => {
            return apis.some(api => api.name === apiName && api.plugins.some(plugin => plugin.name == pluginName));
        },
        hasConsumer: (username) => {
            return consumers.some(consumer => consumer.username === username);
        },
        hasConsumerCredential: (username, name, attributes) => {
            switch(name) {
                case 'oauth2': {
                    return consumers.some(
                        c => c.username === username
                        && c.credentials.oauth2.some(oa => oa.client_id == attributes.client_id));
                }
                case 'key-auth': {
                    return consumers.some(
                        c => c.username === username
                        && c.credentials.keyAuth.some(k => k.key == attributes.key));
                }
                case 'jwt': {
                    return consumers.some(
                        c => c.username === username
                        && c.credentials.jwt.some(k => k.key == attributes.key));
                }
                case 'basic-auth': {
                    return consumers.some(
                            c => c.username === username &&
                            c.credentials.basicAuth.some(k => k.username == attributes.username)
                    );
                }
            }

            throw new Error(`Unknown credential "${name}"`);
        },
        getConsumerCredentialId: (username, name, attributes) => {
            const consumer = consumers.find(c => c.username === username);

            if (!consumer) {
                throw new Error(`Unable to find consumer ${username}`);
            }

            const credential = extractCredentialId(consumer.credentials, name, attributes);

            if (!credential) {
                throw new Error(`Unable to find credential`);
            }

            return credential.id;
        },

        isApiUpToDate: (api) => {
            let current = world.getApi(api.name);

            let different = Object.keys(api.attributes).filter(key => {
                return api.attributes[key] !== current[key];
            });

            return different.length == 0;
        },

        isApiPluginUpToDate: (apiName, plugin) => {
            let current = world.getPluginAttributes(apiName, plugin.name);

            // TODO keys can be in form of "config.somefoo: value"
            let different = Object.keys(plugin.attributes.config).filter(key => {
                return JSON.stringify(plugin.attributes.config[key]) !== JSON.stringify(current[key]);
            });

            return different.length === 0;
        }
    };

    return world;
}

function extractCredentialId(credentials, name, attributes) {
    switch (name) {
        case 'oauth2': return credentials.oauth2.find(oa => oa.client_id == attributes.client_id);
        case 'key-auth': return credentials.keyAuth.find(k => k.key == attributes.key);
        case 'jwt': return credentials.jwt.find(k => k.key == attributes.key);
        case 'basic-auth': return credentials.basicAuth.find(k => k.username == attributes.username);
    }

    throw new Error(`Unknown credential "${name}"`);
}

function _api(api) {
    validateEnsure(api.ensure);

    return world => {
        if (api.ensure == 'removed') {
            return world.hasApi(api.name) ? removeApi(api.name) : noop();
        }

        if (world.hasApi(api.name)) {
            if (world.isApiUpToDate(api)) {
                console.log("api", `${api.name}`.bold, "is up-to-date");

                return noop();
            }

            return updateApi(api.name, api.attributes);
        }

        return createApi(api.name, api.attributes);
    };
}

function _apiPlugins(api) {
    return api.plugins && api.ensure != 'removed' ? plugins(api.name, api.plugins) : [];
}

function validateEnsure(ensure) {
    if (!ensure) {
        return;
    }

    if (['removed', 'present'].indexOf(ensure) === -1) {
        throw new Error(`Invalid ensure "${ensure}"`);
    }
}

function _plugin(apiName, plugin) {
    validateEnsure(plugin.ensure);

    return world => {
        if (plugin.ensure == 'removed') {
            if (world.hasPlugin(apiName, plugin.name)) {
                return removeApiPlugin(apiName, world.getPluginId(apiName, plugin.name));
            }

            return noop();
        }

        if (world.hasPlugin(apiName, plugin.name)) {
            if (world.isApiPluginUpToDate(apiName, plugin)) {
                console.log("  - plugin", `${plugin.name}`.bold, "is up-to-date".green);

                return noop();
            }

            return updateApiPlugin(apiName, world.getPluginId(apiName, plugin.name), plugin.attributes);
        }

        return addApiPlugin(apiName, plugin.name, plugin.attributes);
    }
}

function _isApiPluginUpToDate(world, apiName, plugin) {
    let current = world.getPluginAttributes(apiName, plugin.name);

    // TODO keys can be in form of "config.somefoo: value"
    let different = Object.keys(plugin.attributes.config).filter(key => {
        return JSON.stringify(plugin.attributes.config[key]) !== JSON.stringify(current[key]);
    });

    return different.length === 0;
}

function _consumer(consumer) {
    validateEnsure(consumer.ensure);
    validateConsumer(consumer);

    return world => {
        if (consumer.ensure == 'removed') {
            if (world.hasConsumer(consumer.username)) {
                return removeConsumer(consumer.username);
            }

            return noop();
        }

        if (!world.hasConsumer(consumer.username)) {
            return createConsumer(consumer.username);
        }

        return noop();
    }

    let _credentials = [];

    if (consumer.credentials && consumer.ensure != 'removed') {
        _credentials = consumerCredentials(consumer.username, consumer.credentials);
    }

    return [consumerAction, ..._credentials];
}

function validateConsumer({username}) {
    if (!username) {
        throw new Error("Consumer username must be specified");
    }
}

function _consumerCredentials(consumer) {
    if (!consumer.credentials || consumer.ensure == 'removed') {
        return [];
    }

    return credentials(consumer.username, consumer.credentials);
}

function _consumerCredential(username, credential) {
    validateEnsure(credential.ensure);

    return world => {
        if (credential.ensure == 'removed') {
            if (world.hasConsumerCredential(username, credential.name, credential.attributes)) {
                const credentialId = world.getConsumerCredentialId(username, credential.name, credential.attributes);

                return removeConsumerCredentials(username, credential.name, credentialId);
            }

            return noop();
        }

        if (world.hasConsumerCredential(username, credential.name, credential.attributes)) {
            const credentialId = world.getConsumerCredentialId(username, credential.name, credential.attributes);

            return updateConsumerCredentials(username, credential.name, credentialId, credential.attributes);
        }

        return addConsumerCredentials(username, credential.name, credential.attributes);
    }
}
<commit_msg>Fix when api plugin has no attributes<commit_after>'use strict';

import colors from 'colors';
import createAdminApi from './adminApi';
import assign from 'object-assign';
import kongState from './kongState';
import {
    noop,
    createApi,
    removeApi,
    updateApi,
    addApiPlugin,
    removeApiPlugin,
    updateApiPlugin,
    createConsumer,
    removeConsumer,
    addConsumerCredentials,
    updateConsumerCredentials,
    removeConsumerCredentials
} from './actions';

export default async function execute(config, adminApi) {
    const actions = [
        ...apis(config.apis),
        ...consumers(config.consumers)
    ];

    return actions
        .map(_bindWorldState(adminApi))
        .reduce((promise, action) => promise.then(_executeActionOnApi(action, adminApi)), Promise.resolve(''));
}

export function apis(apis = []) {
    return apis.reduce((actions, api) => [...actions, _api(api), ..._apiPlugins(api)], []);
};

export function plugins(apiName, plugins) {
    return plugins.reduce((actions, plugin) => [...actions, _plugin(apiName, plugin)], []);
}

export function consumers(consumers = []) {
    return consumers.reduce((calls, consumer) => [...calls, _consumer(consumer), ..._consumerCredentials(consumer)], []);
}

export function credentials(username, credentials) {
    return credentials.reduce((actions, credential) => [...actions, _consumerCredential(username, credential)], []);
}

function _executeActionOnApi(action, adminApi) {
    return async () => {
        const params = await action();

        if (params.noop) {
            return Promise.resolve('No-op');
        }

        return adminApi
            .requestEndpoint(params.endpoint, params)
            .then(response => {
                console.info(
                    `\n${params.method.blue}`,
                    response.ok ? ('' + response.status).bold.green : ('' + response.status).bold.red,
                    adminApi.router(params.endpoint).blue,
                    "\n",
                    params.body ? params.body : ''
                );

                if (!response.ok) {
                    if (params.endpoint.name == 'consumer' && params.method == 'DELETE') {
                        console.log('Bug in Kong throws error, Consumer has still been removed will continue'.bold.green);

                        return response;
                    }

                    return response.text()
                        .then(content => {
                            throw new Error(`${response.statusText}\n${content}`);
                        });
                } else {
                    response.text()
                        .then(content => {
                            console.info(`Response status ${response.statusText}:`.green, "\n", JSON.parse(content));
                        });
                }

                return response;
            });
        }
}

function _bindWorldState(adminApi) {
    return f => async () => {
        const state = await kongState(adminApi);
        return f(_createWorld(state));
    }
}

function _createWorld({apis, consumers}) {
    const world = {
        hasApi: apiName => apis.some(api => api.name === apiName),
        getApi: apiName => {
            const api = apis.find(api => api.name === apiName);

            if (!api) {
                throw new Error(`Unable to find api ${apiName}`);
            }

            return api;
        },
        getPlugin: (apiName, pluginName) => {
            const plugin = world.getApi(apiName).plugins.find(plugin => plugin.name == pluginName);

            if (!plugin) {
                throw new Error(`Unable to find plugin ${pluginName}`);
            }

            return plugin;
        },
        getPluginId: (apiName, pluginName) => {
            return world.getPlugin(apiName, pluginName).id;
        },
        getPluginAttributes: (apiName, pluginName) => {
            return world.getPlugin(apiName, pluginName).config;
        },
        hasPlugin: (apiName, pluginName) => {
            return apis.some(api => api.name === apiName && api.plugins.some(plugin => plugin.name == pluginName));
        },
        hasConsumer: (username) => {
            return consumers.some(consumer => consumer.username === username);
        },
        hasConsumerCredential: (username, name, attributes) => {
            switch(name) {
                case 'oauth2': {
                    return consumers.some(
                        c => c.username === username
                        && c.credentials.oauth2.some(oa => oa.client_id == attributes.client_id));
                }
                case 'key-auth': {
                    return consumers.some(
                        c => c.username === username
                        && c.credentials.keyAuth.some(k => k.key == attributes.key));
                }
                case 'jwt': {
                    return consumers.some(
                        c => c.username === username
                        && c.credentials.jwt.some(k => k.key == attributes.key));
                }
                case 'basic-auth': {
                    return consumers.some(
                            c => c.username === username &&
                            c.credentials.basicAuth.some(k => k.username == attributes.username)
                    );
                }
            }

            throw new Error(`Unknown credential "${name}"`);
        },
        getConsumerCredentialId: (username, name, attributes) => {
            const consumer = consumers.find(c => c.username === username);

            if (!consumer) {
                throw new Error(`Unable to find consumer ${username}`);
            }

            const credential = extractCredentialId(consumer.credentials, name, attributes);

            if (!credential) {
                throw new Error(`Unable to find credential`);
            }

            return credential.id;
        },

        isApiUpToDate: (api) => {
            let current = world.getApi(api.name);

            let different = Object.keys(api.attributes).filter(key => {
                return api.attributes[key] !== current[key];
            });

            return different.length == 0;
        },

        isApiPluginUpToDate: (apiName, plugin) => {
            let current = world.getPluginAttributes(apiName, plugin.name);

            if (false == plugin.hasOwnProperty('attributes')) {
                // of a plugin has no attributes, and its been added then it is up to date
                return true;
            }

            // TODO keys can be in form of "config.somefoo: value"
            let different = Object.keys(plugin.attributes.config).filter(key => {
                return JSON.stringify(plugin.attributes.config[key]) !== JSON.stringify(current[key]);
            });

            return different.length === 0;
        }
    };

    return world;
}

function extractCredentialId(credentials, name, attributes) {
    switch (name) {
        case 'oauth2': return credentials.oauth2.find(oa => oa.client_id == attributes.client_id);
        case 'key-auth': return credentials.keyAuth.find(k => k.key == attributes.key);
        case 'jwt': return credentials.jwt.find(k => k.key == attributes.key);
        case 'basic-auth': return credentials.basicAuth.find(k => k.username == attributes.username);
    }

    throw new Error(`Unknown credential "${name}"`);
}

function _api(api) {
    validateEnsure(api.ensure);

    return world => {
        if (api.ensure == 'removed') {
            return world.hasApi(api.name) ? removeApi(api.name) : noop();
        }

        if (world.hasApi(api.name)) {
            if (world.isApiUpToDate(api)) {
                console.log("api", `${api.name}`.bold, "is up-to-date");

                return noop();
            }

            return updateApi(api.name, api.attributes);
        }

        return createApi(api.name, api.attributes);
    };
}

function _apiPlugins(api) {
    return api.plugins && api.ensure != 'removed' ? plugins(api.name, api.plugins) : [];
}

function validateEnsure(ensure) {
    if (!ensure) {
        return;
    }

    if (['removed', 'present'].indexOf(ensure) === -1) {
        throw new Error(`Invalid ensure "${ensure}"`);
    }
}

function _plugin(apiName, plugin) {
    validateEnsure(plugin.ensure);

    return world => {
        if (plugin.ensure == 'removed') {
            if (world.hasPlugin(apiName, plugin.name)) {
                return removeApiPlugin(apiName, world.getPluginId(apiName, plugin.name));
            }

            return noop();
        }

        if (world.hasPlugin(apiName, plugin.name)) {
            if (world.isApiPluginUpToDate(apiName, plugin)) {
                console.log("  - plugin", `${plugin.name}`.bold, "is up-to-date".green);

                return noop();
            }

            return updateApiPlugin(apiName, world.getPluginId(apiName, plugin.name), plugin.attributes);
        }

        return addApiPlugin(apiName, plugin.name, plugin.attributes);
    }
}

function _isApiPluginUpToDate(world, apiName, plugin) {
    let current = world.getPluginAttributes(apiName, plugin.name);

    // TODO keys can be in form of "config.somefoo: value"
    let different = Object.keys(plugin.attributes.config).filter(key => {
        return JSON.stringify(plugin.attributes.config[key]) !== JSON.stringify(current[key]);
    });

    return different.length === 0;
}

function _consumer(consumer) {
    validateEnsure(consumer.ensure);
    validateConsumer(consumer);

    return world => {
        if (consumer.ensure == 'removed') {
            if (world.hasConsumer(consumer.username)) {
                return removeConsumer(consumer.username);
            }

            return noop();
        }

        if (!world.hasConsumer(consumer.username)) {
            return createConsumer(consumer.username);
        }

        return noop();
    }

    let _credentials = [];

    if (consumer.credentials && consumer.ensure != 'removed') {
        _credentials = consumerCredentials(consumer.username, consumer.credentials);
    }

    return [consumerAction, ..._credentials];
}

function validateConsumer({username}) {
    if (!username) {
        throw new Error("Consumer username must be specified");
    }
}

function _consumerCredentials(consumer) {
    if (!consumer.credentials || consumer.ensure == 'removed') {
        return [];
    }

    return credentials(consumer.username, consumer.credentials);
}

function _consumerCredential(username, credential) {
    validateEnsure(credential.ensure);

    return world => {
        if (credential.ensure == 'removed') {
            if (world.hasConsumerCredential(username, credential.name, credential.attributes)) {
                const credentialId = world.getConsumerCredentialId(username, credential.name, credential.attributes);

                return removeConsumerCredentials(username, credential.name, credentialId);
            }

            return noop();
        }

        if (world.hasConsumerCredential(username, credential.name, credential.attributes)) {
            const credentialId = world.getConsumerCredentialId(username, credential.name, credential.attributes);

            return updateConsumerCredentials(username, credential.name, credentialId, credential.attributes);
        }

        return addConsumerCredentials(username, credential.name, credential.attributes);
    }
}
<|endoftext|>


<commit_before>(function (window, document, ajl) {
"use strict";

// ----------------------------------------------------------------------------
// SmoothScroll: ページ内リンクをスムーズにスクロールする
// ----------------------------------------------------------------------------
// TODO: this.defaults.vの改名
//       フォーカスの移動
ajl.SmoothScroll = function (elem, options) {
    this.elem = elem;
    this.targetId = null;
    this.targetElem = null;
    this.start = 0;
    this.dest = 0;
    this.direction = null;
    this.defaults = {
        excludeCond: /tab_/,
        paddingTop: 0,
        v: 20,    // The value which influences speed.
        moveFocus: false,    // 試験実装中のため
        pagetopId: "header"
    };

    this.options = ajl.util.deepExtend({}, this.defaults, options);
};

ajl.SmoothScroll.prototype = {
    startScroll: function () {
        var documentHeight = document.documentElement.scrollHeight,
            viewportHeight = document.documentElement.clientHeight;

        this.start = window.pageYOffset || document.documentElement.scrollTop;
        this.dest = this.targetElem.offsetTop - this.options.paddingTop;

        if (documentHeight - viewportHeight < this.dest) {
            this.dest = documentHeight - viewportHeight;
        } else if (this.dest === 0) {
            this.dest = 1;
        }

        this.direction = (this.dest - this.start > 0) ? "down" : "up";
        this.doScroll();
    },

    doScroll: function () {
        var moveY;

        if (this.direction === "up" && this.start > this.dest) {
            moveY = Math.floor(this.start - (this.start - this.dest) / this.options.v - 1);
            if (moveY <= 1) {
                moveY = 1;
            }
        } else if (this.direction === "down" && this.dest > this.start) {
            moveY = Math.ceil(this.start + (this.dest - this.start) / this.options.v + 1);
        } else {
            if (this.options.moveFocus) {
                if (this.targetId === this.options.pagetopId) {
                    this.pagetopIdsChildNodes.focus();
                } else {
                    this.targetElem.focus();
                }
            }

            return;
        }

        window.scrollTo(0, moveY);
        this.start = moveY;
        window.requestAnimationFrame(ajl.util.proxy(this, this.doScroll));
    },

    init: function () {
        this.targetId = this.elem.getAttribute("href").replace(/(https?:\/\/[a-zA-Z0-9\.%\/]+)?\#/, "");
        this.targetElem = document.getElementById(this.targetId);

        if (!this.options.excludeCond.test(this.targetId) && this.targetElem) {
            if (this.options.moveFocus) {
                if (this.targetId === this.options.pagetopId) {
                    this.pagetopIdsChildNodes = this.targetElem.children[0];
                    this.pagetopIdsChildNodes.setAttribute("tabindex", "-1");
                } else {
                    this.targetElem.setAttribute("tabindex", "-1");
                }
            }

            ajl.event.add(this.elem, "click", ajl.util.proxy(this, function (e) {
                if (e.preventDefault) {
                    e.preventDefault();
                } else if (typeof window.attachEvent === "object") {
                    event.returnValue = false;
                }

                this.startScroll();
            }), false);
        }
    }
};

ajl.util.createRunner("SmoothScroll");

}(window, document, ajl));
<commit_msg>SmoothScrollが完了しないバグを修正<commit_after>(function (window, document, ajl) {
"use strict";

// ----------------------------------------------------------------------------
// SmoothScroll: ページ内リンクをスムーズにスクロールする
// ----------------------------------------------------------------------------
// TODO: this.defaults.vの改名
//       フォーカスの移動
ajl.SmoothScroll = function (elem, options) {
    this.elem = elem;
    this.targetId = null;
    this.targetElem = null;
    this.start = 0;
    this.dest = 0;
    this.direction = null;
    this.defaults = {
        excludeCond: /tab_/,
        paddingTop: 0,
        v: 20,    // The value which influences speed.
        moveFocus: false,    // 試験実装中のため
        pagetopId: "header"
    };

    this.options = ajl.util.deepExtend({}, this.defaults, options);
};

ajl.SmoothScroll.prototype = {
    startScroll: function () {
        var documentHeight = document.documentElement.scrollHeight,
            viewportHeight = document.documentElement.clientHeight;

        this.start = window.pageYOffset || document.documentElement.scrollTop;
        this.dest = this.targetElem.offsetTop - this.options.paddingTop;

        if (documentHeight - viewportHeight < this.dest) {
            this.dest = documentHeight - viewportHeight;
        } else if (this.dest <= 0) {
            this.dest = 1;
        }

        this.direction = (this.dest - this.start > 0) ? "down" : "up";
        this.doScroll();
    },

    doScroll: function () {
        var moveY;

        if (this.direction === "up" && this.start > this.dest) {
            moveY = Math.floor(this.start - (this.start - this.dest) / this.options.v - 1);
            if (moveY <= 1) {
                moveY = 1;
            }
        } else if (this.direction === "down" && this.dest > this.start) {
            moveY = Math.ceil(this.start + (this.dest - this.start) / this.options.v + 1);
        } else {
            if (this.options.moveFocus) {
                if (this.targetId === this.options.pagetopId) {
                    this.pagetopIdsChildNodes.focus();
                } else {
                    this.targetElem.focus();
                }
            }

            return;
        }

        window.scrollTo(0, moveY);
        this.start = moveY;

        if (moveY > this.dest) {
            window.requestAnimationFrame(ajl.util.proxy(this, this.doScroll));
        } else {
            return;
        }
    },

    init: function () {
        this.targetId = this.elem.getAttribute("href").replace(/(https?:\/\/[a-zA-Z0-9\.%\/]+)?\#/, "");
        this.targetElem = document.getElementById(this.targetId);

        if (!this.options.excludeCond.test(this.targetId) && this.targetElem) {
            if (this.options.moveFocus) {
                if (this.targetId === this.options.pagetopId) {
                    this.pagetopIdsChildNodes = this.targetElem.children[0];
                    this.pagetopIdsChildNodes.setAttribute("tabindex", "-1");
                } else {
                    this.targetElem.setAttribute("tabindex", "-1");
                }
            }

            ajl.event.add(this.elem, "click", ajl.util.proxy(this, function (e) {
                if (e.preventDefault) {
                    e.preventDefault();
                } else if (typeof window.attachEvent === "object") {
                    event.returnValue = false;
                }

                this.startScroll();
            }), false);
        }
    }
};

ajl.util.createRunner("SmoothScroll");

}(window, document, ajl));
<|endoftext|>


<commit_before>/*\
 * $
 [ object ]
 * Global namespace for using Kimbo functions
\*/

/*\
 * $(…)
 * Kimbo object collection.
 * All methods called from a Kimbo collection affects all elements in it.
\*/
function Kimbo(selector, context) {
  var elem, match;

  if (!Kimbo.isKimbo(this)) {
    return new Kimbo(selector, context);
  }

  // no selector, return empty Kimbo object
  if (!selector) {
    return this;
  }

  // already a dom element
  if (selector.nodeType) {
    this[0] = selector;
    this.length = 1;
    return this;
  }

  // asume a css selector, query the dom
  if (typeof selector === 'string') {

    match = r_id.exec(selector);

    // handle $('#id');
    if (match && match[1]) {
      elem = document.getElementById(match[1]);

      if (elem) {
        this[0] = elem;
        this.length = 1;
      }

      return this;
    }

    // if no context find it on document
    // or on the specified Kimbo context
    if (!context || context.kimbo) {
      return (context || rootContext).find(selector);

    // create new Kimbo object with the specified context
    } else if (context) {
      return Kimbo(context).find(selector);
    }

  // is a function, call it when DOM is ready
  } else if (Kimbo.isFunction(selector)) {
    return rootContext.ready(selector);
  }

  // handle kimbo object, plain objects or other objects
  return Kimbo.makeArray(selector, this);
}

var push = Array.prototype.push,
  slice = Array.prototype.slice,
  filter = Array.prototype.filter,
  document = window.document,
  rootContext;

Kimbo.fn = Kimbo.prototype = {

  /*\
   * $(…).length
   [ property ]
   * The length of the current Kimbo collection.
   = (number) Integer representing the lenght of the current collection.
   > Usage
   * Having these paragraphs:
   |
one

   |
two

   |
three

   * Grab them and check for the length property:
   | $('p').length; // 2
  \*/
  length: 0,

  /*\
   * $(…).ready
   [ method ]
   * Execute a callback after de DOM is completely loaded
   > Parameters
   - callback (function) A function to call after de DOM is ready
   = (object) The original element
   > Usage
   | $(document).ready(function () {
   |   console.log('the DOM is loaded!');
   | });
   * Or using the shortcut
   | $(function () {
   |   console.log('the DOM is loaded!);
   | });
  \*/
  ready: function (callback) {
    // first check if already loaded
    if (/complete|interactive/.test(document.readyState)) {
      callback.call(document);

    // listen when it loads
    } else {
      document.addEventListener('DOMContentLoaded', callback, false);
    }

    return rootContext;
  },

  /*\
   * $(…).get
   [ method ]
   * Retrieve native DOM elements
   > Parameters
   - index (number) #optional A zero-based integer indicating which element to retrieve, supports going backwards with negative index.
   = (array|object) An array of the native DOM elements or the specified element by index matched by Kimbo.
   > Usage
   |

   |

   |

   |

   * Get standard array of elements
   | $('li').get(); // [
,
]
   * Get the first element
   | $('li').get(0); //

   * Get the last element
   | $('li').get(-1); //

  \*/
  get: function (index) {
    return (!arguments.length) ? slice.call(this) : (index < 0 ? this[this.length + index] : Kimbo(this[index]));
  },

  // needed to have an array-like object
  splice: Array.prototype.slice
};

/*\
 * $.forEach
 [ method ]
 * Iterator function that can be used to seamlessly iterate over both objects and arrays.
 > Parameters
 - obj (object) Object or array to iterate
 - callback (function) Function that will be executed on each iteration
 = (object) The original array or object
 > Usage
 | // iterating array
 | $.forEach(['a', 'b', 'c'], function (index, value) {
 |   alert(index + ': ' + value);
 | });
 |
 | // iterating object
 | $.forEach({name: 'Denis', surname: 'Ciccale'}, function (key, value) {
 |   alert(key + ': ' + value);
 | });
\*/
Kimbo.forEach = function (obj, callback) {
  var i,
    l = obj.length,
    isObj = l === undefined || typeof obj === 'function';

  if (isObj) {
    for (i in obj) {
      if (obj.hasOwnProperty(i) && callback.call(obj[i], i, obj[i], obj) === false) {
        break;
      }
    }
  } else {
    for (i = 0; i < l; i++) {
      if (callback.call(obj[i], obj[i], i, obj) === false) {
        break;
      }
    }
  }

  // return original obj
  return obj;
};

/*\
 * $.extend
 [ method ]
 * Merge the contents of two or more objects together into the first object.
 > Parameters
 - target (object|boolean) Object that will receive the merged properties if objects are passed, or target will extend Kimbo object. If target is `true` the passed objects will be recursively merged.
 - objectN (object) #optional One or more objects with additional properties.
 = (object) The extended target or a new copy if target is an empty object.
 > Usage
 * When passing two or more objects, all properties will be merged into the target object.
 | var obj1 = { msg: 'hi', info: { from: 'Denis' }};
 | var obj2 = { msg: 'Hi!', info: { time: '22:00PM' }};
 |
 | // merge obj1 into obj2
 | $.extend(obj1, obj2);
 |
 | // now obj1 is equal to:
 | { msg: 'Hi!', info: { time: '22:00PM' }}
 * If an empty target object is passed, none of the other objects will be directly modified
 | // pass an empty target
 | var obj3 = $.extend({}, obj1, obj);
 * To do a recursive merge, pass true as first argument, then the objects to merge
 | $.extend(true, obj1, obj2);
 | // obj1 will be:
 | { msg: 'Hi!', info: { from: 'Denis', time: '22:00PM' }}
\*/
Kimbo.extend = Kimbo.fn.extend = function () {
  var objs = arguments,
    target = objs[0] || {},
    deep = (target === true),
    cut = 1;

  // check for deep copy
  if (deep) {
    target = objs[1] || {};
    cut = 2;

  // extend Kimbo itself if only one argument is passed
  } else if (objs.length === 1) {
    target = this;
    cut = 0;
  }

  // make an array from the arguments
  // removing unnecessary objects
  objs = slice.call(objs, cut);

  // loop through the objects
  Kimbo.forEach(objs, function (source) {
    // populate target from source
    Kimbo.forEach(source, function (key, value) {
      var src;

      if (deep && (Kimbo.isObject(value) || Kimbo.isArray(value))) {
        src = target[key];
        target[key] = Kimbo.extend(deep, src, value);

      } else if (value !== undefined) {
        target[key] = value;
      }
    });
  });

  return target;
};
<commit_msg>only test if t letter is present to know if DOM is ready, for complete and interactive readiness states<commit_after>/*\
 * $
 [ object ]
 * Global namespace for using Kimbo functions
\*/

/*\
 * $(…)
 * Kimbo object collection.
 * All methods called from a Kimbo collection affects all elements in it.
\*/
function Kimbo(selector, context) {
  var elem, match;

  if (!Kimbo.isKimbo(this)) {
    return new Kimbo(selector, context);
  }

  // no selector, return empty Kimbo object
  if (!selector) {
    return this;
  }

  // already a dom element
  if (selector.nodeType) {
    this[0] = selector;
    this.length = 1;
    return this;
  }

  // asume a css selector, query the dom
  if (typeof selector === 'string') {

    match = r_id.exec(selector);

    // handle $('#id');
    if (match && match[1]) {
      elem = document.getElementById(match[1]);

      if (elem) {
        this[0] = elem;
        this.length = 1;
      }

      return this;
    }

    // if no context find it on document
    // or on the specified Kimbo context
    if (!context || context.kimbo) {
      return (context || rootContext).find(selector);

    // create new Kimbo object with the specified context
    } else if (context) {
      return Kimbo(context).find(selector);
    }

  // is a function, call it when DOM is ready
  } else if (Kimbo.isFunction(selector)) {
    return rootContext.ready(selector);
  }

  // handle kimbo object, plain objects or other objects
  return Kimbo.makeArray(selector, this);
}

var push = Array.prototype.push,
  slice = Array.prototype.slice,
  filter = Array.prototype.filter,
  document = window.document,
  rootContext;

Kimbo.fn = Kimbo.prototype = {

  /*\
   * $(…).length
   [ property ]
   * The length of the current Kimbo collection.
   = (number) Integer representing the lenght of the current collection.
   > Usage
   * Having these paragraphs:
   |
one

   |
two

   |
three

   * Grab them and check for the length property:
   | $('p').length; // 2
  \*/
  length: 0,

  /*\
   * $(…).ready
   [ method ]
   * Execute a callback after de DOM is completely loaded
   > Parameters
   - callback (function) A function to call after de DOM is ready
   = (object) The original element
   > Usage
   | $(document).ready(function () {
   |   console.log('the DOM is loaded!');
   | });
   * Or using the shortcut
   | $(function () {
   |   console.log('the DOM is loaded!);
   | });
  \*/
  ready: function (callback) {
    // first check if already loaded
    if (/t/.test(document.readyState)) {
      callback.call(document);

    // listen when it loads
    } else {
      document.addEventListener('DOMContentLoaded', callback, false);
    }

    return rootContext;
  },

  /*\
   * $(…).get
   [ method ]
   * Retrieve native DOM elements
   > Parameters
   - index (number) #optional A zero-based integer indicating which element to retrieve, supports going backwards with negative index.
   = (array|object) An array of the native DOM elements or the specified element by index matched by Kimbo.
   > Usage
   |

   |

   |

   |

   * Get standard array of elements
   | $('li').get(); // [
,
]
   * Get the first element
   | $('li').get(0); //

   * Get the last element
   | $('li').get(-1); //

  \*/
  get: function (index) {
    return (!arguments.length) ? slice.call(this) : (index < 0 ? this[this.length + index] : Kimbo(this[index]));
  },

  // needed to have an array-like object
  splice: Array.prototype.slice
};

/*\
 * $.forEach
 [ method ]
 * Iterator function that can be used to seamlessly iterate over both objects and arrays.
 > Parameters
 - obj (object) Object or array to iterate
 - callback (function) Function that will be executed on each iteration
 = (object) The original array or object
 > Usage
 | // iterating array
 | $.forEach(['a', 'b', 'c'], function (index, value) {
 |   alert(index + ': ' + value);
 | });
 |
 | // iterating object
 | $.forEach({name: 'Denis', surname: 'Ciccale'}, function (key, value) {
 |   alert(key + ': ' + value);
 | });
\*/
Kimbo.forEach = function (obj, callback) {
  var i,
    l = obj.length,
    isObj = l === undefined || typeof obj === 'function';

  if (isObj) {
    for (i in obj) {
      if (obj.hasOwnProperty(i) && callback.call(obj[i], i, obj[i], obj) === false) {
        break;
      }
    }
  } else {
    for (i = 0; i < l; i++) {
      if (callback.call(obj[i], obj[i], i, obj) === false) {
        break;
      }
    }
  }

  // return original obj
  return obj;
};

/*\
 * $.extend
 [ method ]
 * Merge the contents of two or more objects together into the first object.
 > Parameters
 - target (object|boolean) Object that will receive the merged properties if objects are passed, or target will extend Kimbo object. If target is `true` the passed objects will be recursively merged.
 - objectN (object) #optional One or more objects with additional properties.
 = (object) The extended target or a new copy if target is an empty object.
 > Usage
 * When passing two or more objects, all properties will be merged into the target object.
 | var obj1 = { msg: 'hi', info: { from: 'Denis' }};
 | var obj2 = { msg: 'Hi!', info: { time: '22:00PM' }};
 |
 | // merge obj1 into obj2
 | $.extend(obj1, obj2);
 |
 | // now obj1 is equal to:
 | { msg: 'Hi!', info: { time: '22:00PM' }}
 * If an empty target object is passed, none of the other objects will be directly modified
 | // pass an empty target
 | var obj3 = $.extend({}, obj1, obj);
 * To do a recursive merge, pass true as first argument, then the objects to merge
 | $.extend(true, obj1, obj2);
 | // obj1 will be:
 | { msg: 'Hi!', info: { from: 'Denis', time: '22:00PM' }}
\*/
Kimbo.extend = Kimbo.fn.extend = function () {
  var objs = arguments,
    target = objs[0] || {},
    deep = (target === true),
    cut = 1;

  // check for deep copy
  if (deep) {
    target = objs[1] || {};
    cut = 2;

  // extend Kimbo itself if only one argument is passed
  } else if (objs.length === 1) {
    target = this;
    cut = 0;
  }

  // make an array from the arguments
  // removing unnecessary objects
  objs = slice.call(objs, cut);

  // loop through the objects
  Kimbo.forEach(objs, function (source) {
    // populate target from source
    Kimbo.forEach(source, function (key, value) {
      var src;

      if (deep && (Kimbo.isObject(value) || Kimbo.isArray(value))) {
        src = target[key];
        target[key] = Kimbo.extend(deep, src, value);

      } else if (value !== undefined) {
        target[key] = value;
      }
    });
  });

  return target;
};
<|endoftext|>