<commit_before>/**
@module ember-metal
*/

/*
  JavaScript (before ES6) does not have a Map implementation. Objects,
  which are often used as dictionaries, may only have Strings as keys.

  Because Ember has a way to get a unique identifier for every object
  via `Ember.guidFor`, we can implement a performant Map with arbitrary
  keys. Because it is commonly used in low-level bookkeeping, Map is
  implemented as a pure JavaScript object for performance.

  This implementation follows the current iteration of the ES6 proposal for
  maps (http://wiki.ecmascript.org/doku.php?id=harmony:simple_maps_and_sets),
  with one exception:  as we do not have the luxury of in-VM iteration, we implement a
  forEach method for iteration.

  Map is mocked out to look like an Ember object, so you can do
  `Ember.Map.create()` for symmetry with other Ember classes.
*/

import { guidFor } from "ember-metal/utils";
import { indexOf } from "ember-metal/array";
import { create } from "ember-metal/platform";
import { deprecateProperty } from "ember-metal/deprecate_property";

function missingFunction(fn) {
  throw new TypeError('' + Object.prototype.toString.call(fn) + " is not a function");
}

function missingNew(name) {
  throw new TypeError("Constructor " + name + "requires 'new'");
}

function copyNull(obj) {
  var output = create(null);

  for (var prop in obj) {
    // hasOwnPropery is not needed because obj is Object.create(null);
    output[prop] = obj[prop];
  }

  return output;
}

function copyMap(original, newObject) {
  var keys = original.keys.copy();
  var values = copyNull(original.values);

  newObject.keys = keys;
  newObject.values = values;
  newObject.size = original.size;

  return newObject;
}

/**
  This class is used internally by Ember and Ember Data.
  Please do not use it at this time. We plan to clean it up
  and add many tests soon.

  @class OrderedSet
  @namespace Ember
  @constructor
  @private
*/
function OrderedSet() {

  if (this instanceof OrderedSet) {
    this.clear();
    this._silenceRemoveDeprecation = false;
  } else {
    missingNew("OrderedSet");
  }
}

/**
  @method create
  @static
  @return {Ember.OrderedSet}
*/
OrderedSet.create = function() {
  var Constructor = this;

  return new Constructor();
};

OrderedSet.prototype = {
  constructor: OrderedSet,
  /**
    @method clear
  */
  clear: function() {
    this.presenceSet = create(null);
    this.list = [];
    this.size = 0;
  },

  /**
    @method add
    @param obj
    @param guid (optional, and for internal use)
    @return {Ember.OrderedSet}
  */
  add: function(obj, _guid) {
    var guid = _guid || guidFor(obj);
    var presenceSet = this.presenceSet;
    var list = this.list;

    if (presenceSet[guid] !== true) {
      presenceSet[guid] = true;
      this.size = list.push(obj);
    }

    return this;
  },

  /**
    @deprecated

    @method remove
    @param obj
    @param _guid (optional and for internal use only)
    @return {Boolean}
  */
  remove: function(obj, _guid) {
    Ember.deprecate('Calling `OrderedSet.prototype.remove` has been deprecated, please use `OrderedSet.prototype.delete` instead.', this._silenceRemoveDeprecation);

    return this.delete(obj, _guid);
  },

  /**
    @method delete
    @param obj
    @param _guid (optional and for internal use only)
    @return {Boolean}
  */
  delete: function(obj, _guid) {
    var guid = _guid || guidFor(obj);
    var presenceSet = this.presenceSet;
    var list = this.list;

    if (presenceSet[guid] === true) {
      delete presenceSet[guid];
      var index = indexOf.call(list, obj);
      if (index > -1) {
        list.splice(index, 1);
      }
      this.size = list.length;
      return true;
    } else {
      return false;
    }
  },

  /**
    @method isEmpty
    @return {Boolean}
  */
  isEmpty: function() {
    return this.size === 0;
  },

  /**
    @method has
    @param obj
    @return {Boolean}
  */
  has: function(obj) {
    if (this.size === 0) { return false; }

    var guid = guidFor(obj);
    var presenceSet = this.presenceSet;

    return presenceSet[guid] === true;
  },

  /**
    @method forEach
    @param {Function} fn
    @param self
  */
  forEach: function(fn /*, thisArg*/) {
    if (typeof fn !== 'function') {
      missingFunction(fn);
    }

    if (this.size === 0) { return; }

    var list = this.list;
    var length = arguments.length;
    var i;

    if (length === 2) {
      for (i = 0; i < list.length; i++) {
        fn.call(arguments[1], list[i]);
      }
    } else {
      for (i = 0; i < list.length; i++) {
        fn(list[i]);
      }
    }
  },

  /**
    @method toArray
    @return {Array}
  */
  toArray: function() {
    return this.list.slice();
  },

  /**
    @method copy
    @return {Ember.OrderedSet}
  */
  copy: function() {
    var Constructor = this.constructor;
    var set = new Constructor();

    set._silenceRemoveDeprecation = this._silenceRemoveDeprecation;
    set.presenceSet = copyNull(this.presenceSet);
    set.list = this.toArray();
    set.size = this.size;

    return set;
  }
};

deprecateProperty(OrderedSet.prototype, 'length', 'size');

/**
  A Map stores values indexed by keys. Unlike JavaScript's
  default Objects, the keys of a Map can be any JavaScript
  object.

  Internally, a Map has two data structures:

  1. `keys`: an OrderedSet of all of the existing keys
  2. `values`: a JavaScript Object indexed by the `Ember.guidFor(key)`

  When a key/value pair is added for the first time, we
  add the key to the `keys` OrderedSet, and create or
  replace an entry in `values`. When an entry is deleted,
  we delete its entry in `keys` and `values`.

  @class Map
  @namespace Ember
  @private
  @constructor
*/
function Map() {
  if (this instanceof this.constructor) {
    this.keys = OrderedSet.create();
    this.keys._silenceRemoveDeprecation = true;
    this.values = create(null);
    this.size = 0;
  } else {
    missingNew("OrderedSet");
  }
}

Ember.Map = Map;

/**
  @method create
  @static
*/
Map.create = function() {
  var Constructor = this;
  return new Constructor();
};

Map.prototype = {
  constructor: Map,

  /**
    This property will change as the number of objects in the map changes.

    @property size
    @type number
    @default 0
  */
  size: 0,

  /**
    Retrieve the value associated with a given key.

    @method get
    @param {*} key
    @return {*} the value associated with the key, or `undefined`
  */
  get: function(key) {
    if (this.size === 0) { return; }

    var values = this.values;
    var guid = guidFor(key);

    return values[guid];
  },

  /**
    Adds a value to the map. If a value for the given key has already been
    provided, the new value will replace the old value.

    @method set
    @param {*} key
    @param {*} value
    @return {Ember.Map}
  */
  set: function(key, value) {
    var keys = this.keys;
    var values = this.values;
    var guid = guidFor(key);

    // ensure we don't store -0
    var k = key === -0 ? 0 : key;

    keys.add(k, guid);

    values[guid] = value;

    this.size = keys.size;

    return this;
  },

  /**
    @deprecated see delete
    Removes a value from the map for an associated key.

    @method remove
    @param {*} key
    @return {Boolean} true if an item was removed, false otherwise
  */
  remove: function(key) {
    Ember.deprecate('Calling `Map.prototype.remove` has been deprecated, please use `Map.prototype.delete` instead.');

    return this.delete(key);
  },

  /**
    Removes a value from the map for an associated key.

    @method delete
    @param {*} key
    @return {Boolean} true if an item was removed, false otherwise
  */
  delete: function(key) {
    if (this.size === 0) { return false; }
    // don't use ES6 "delete" because it will be annoying
    // to use in browsers that are not ES6 friendly;
    var keys = this.keys;
    var values = this.values;
    var guid = guidFor(key);

    if (keys.delete(key, guid)) {
      delete values[guid];
      this.size = keys.size;
      return true;
    } else {
      return false;
    }
  },

  /**
    Check whether a key is present.

    @method has
    @param {*} key
    @return {Boolean} true if the item was present, false otherwise
  */
  has: function(key) {
    return this.keys.has(key);
  },

  /**
    Iterate over all the keys and values. Calls the function once
    for each key, passing in value, key, and the map being iterated over,
    in that order.

    The keys are guaranteed to be iterated over in insertion order.

    @method forEach
    @param {Function} callback
    @param {*} self if passed, the `this` value inside the
      callback. By default, `this` is the map.
  */
  forEach: function(callback /*, thisArg*/) {
    if (typeof callback !== 'function') {
      missingFunction(callback);
    }

    if (this.size === 0) { return; }

    var length = arguments.length;
    var map = this;
    var cb, thisArg;

    if (length === 2) {
      thisArg = arguments[1];
      cb = function(key) {
        callback.call(thisArg, map.get(key), key, map);
      };
    } else {
      cb = function(key) {
        callback(map.get(key), key, map);
      };
    }

    this.keys.forEach(cb);
  },

  /**
    @method clear
  */
  clear: function() {
    this.keys.clear();
    this.values = create(null);
    this.size = 0;
  },

  /**
    @method copy
    @return {Ember.Map}
  */
  copy: function() {
    return copyMap(this, new Map());
  }
};

deprecateProperty(Map.prototype, 'length', 'size');

/**
  @class MapWithDefault
  @namespace Ember
  @extends Ember.Map
  @private
  @constructor
  @param [options]
    @param {*} [options.defaultValue]
*/
function MapWithDefault(options) {
  this._super$constructor();
  this.defaultValue = options.defaultValue;
}

/**
  @method create
  @static
  @param [options]
    @param {*} [options.defaultValue]
  @return {Ember.MapWithDefault|Ember.Map} If options are passed, returns
    `Ember.MapWithDefault` otherwise returns `Ember.Map`
*/
MapWithDefault.create = function(options) {
  if (options) {
    return new MapWithDefault(options);
  } else {
    return new Map();
  }
};

MapWithDefault.prototype = create(Map.prototype);
MapWithDefault.prototype.constructor = MapWithDefault;
MapWithDefault.prototype._super$constructor = Map;
MapWithDefault.prototype._super$get = Map.prototype.get;

/**
  Retrieve the value associated with a given key.

  @method get
  @param {*} key
  @return {*} the value associated with the key, or the default value
*/
MapWithDefault.prototype.get = function(key) {
  var hasValue = this.has(key);

  if (hasValue) {
    return this._super$get(key);
  } else {
    var defaultValue = this.defaultValue(key);
    this.set(key, defaultValue);
    return defaultValue;
  }
};

/**
  @method copy
  @return {Ember.MapWithDefault}
*/
MapWithDefault.prototype.copy = function() {
  var Constructor = this.constructor;
  return copyMap(this, new Constructor({
    defaultValue: this.defaultValue
  }));
};

export default Map;

export {
  OrderedSet,
  Map,
  MapWithDefault
};
<commit_msg>Make Ember.Map keys and values private.<commit_after>/**
@module ember-metal
*/

/*
  JavaScript (before ES6) does not have a Map implementation. Objects,
  which are often used as dictionaries, may only have Strings as keys.

  Because Ember has a way to get a unique identifier for every object
  via `Ember.guidFor`, we can implement a performant Map with arbitrary
  keys. Because it is commonly used in low-level bookkeeping, Map is
  implemented as a pure JavaScript object for performance.

  This implementation follows the current iteration of the ES6 proposal for
  maps (http://wiki.ecmascript.org/doku.php?id=harmony:simple_maps_and_sets),
  with one exception:  as we do not have the luxury of in-VM iteration, we implement a
  forEach method for iteration.

  Map is mocked out to look like an Ember object, so you can do
  `Ember.Map.create()` for symmetry with other Ember classes.
*/

import { guidFor } from "ember-metal/utils";
import { indexOf } from "ember-metal/array";
import { create } from "ember-metal/platform";
import { deprecateProperty } from "ember-metal/deprecate_property";

function missingFunction(fn) {
  throw new TypeError('' + Object.prototype.toString.call(fn) + " is not a function");
}

function missingNew(name) {
  throw new TypeError("Constructor " + name + "requires 'new'");
}

function copyNull(obj) {
  var output = create(null);

  for (var prop in obj) {
    // hasOwnPropery is not needed because obj is Object.create(null);
    output[prop] = obj[prop];
  }

  return output;
}

function copyMap(original, newObject) {
  var keys = original._keys.copy();
  var values = copyNull(original._values);

  newObject._keys = keys;
  newObject._values = values;
  newObject.size = original.size;

  return newObject;
}

/**
  This class is used internally by Ember and Ember Data.
  Please do not use it at this time. We plan to clean it up
  and add many tests soon.

  @class OrderedSet
  @namespace Ember
  @constructor
  @private
*/
function OrderedSet() {

  if (this instanceof OrderedSet) {
    this.clear();
    this._silenceRemoveDeprecation = false;
  } else {
    missingNew("OrderedSet");
  }
}

/**
  @method create
  @static
  @return {Ember.OrderedSet}
*/
OrderedSet.create = function() {
  var Constructor = this;

  return new Constructor();
};

OrderedSet.prototype = {
  constructor: OrderedSet,
  /**
    @method clear
  */
  clear: function() {
    this.presenceSet = create(null);
    this.list = [];
    this.size = 0;
  },

  /**
    @method add
    @param obj
    @param guid (optional, and for internal use)
    @return {Ember.OrderedSet}
  */
  add: function(obj, _guid) {
    var guid = _guid || guidFor(obj);
    var presenceSet = this.presenceSet;
    var list = this.list;

    if (presenceSet[guid] !== true) {
      presenceSet[guid] = true;
      this.size = list.push(obj);
    }

    return this;
  },

  /**
    @deprecated

    @method remove
    @param obj
    @param _guid (optional and for internal use only)
    @return {Boolean}
  */
  remove: function(obj, _guid) {
    Ember.deprecate('Calling `OrderedSet.prototype.remove` has been deprecated, please use `OrderedSet.prototype.delete` instead.', this._silenceRemoveDeprecation);

    return this.delete(obj, _guid);
  },

  /**
    @method delete
    @param obj
    @param _guid (optional and for internal use only)
    @return {Boolean}
  */
  delete: function(obj, _guid) {
    var guid = _guid || guidFor(obj);
    var presenceSet = this.presenceSet;
    var list = this.list;

    if (presenceSet[guid] === true) {
      delete presenceSet[guid];
      var index = indexOf.call(list, obj);
      if (index > -1) {
        list.splice(index, 1);
      }
      this.size = list.length;
      return true;
    } else {
      return false;
    }
  },

  /**
    @method isEmpty
    @return {Boolean}
  */
  isEmpty: function() {
    return this.size === 0;
  },

  /**
    @method has
    @param obj
    @return {Boolean}
  */
  has: function(obj) {
    if (this.size === 0) { return false; }

    var guid = guidFor(obj);
    var presenceSet = this.presenceSet;

    return presenceSet[guid] === true;
  },

  /**
    @method forEach
    @param {Function} fn
    @param self
  */
  forEach: function(fn /*, thisArg*/) {
    if (typeof fn !== 'function') {
      missingFunction(fn);
    }

    if (this.size === 0) { return; }

    var list = this.list;
    var length = arguments.length;
    var i;

    if (length === 2) {
      for (i = 0; i < list.length; i++) {
        fn.call(arguments[1], list[i]);
      }
    } else {
      for (i = 0; i < list.length; i++) {
        fn(list[i]);
      }
    }
  },

  /**
    @method toArray
    @return {Array}
  */
  toArray: function() {
    return this.list.slice();
  },

  /**
    @method copy
    @return {Ember.OrderedSet}
  */
  copy: function() {
    var Constructor = this.constructor;
    var set = new Constructor();

    set._silenceRemoveDeprecation = this._silenceRemoveDeprecation;
    set.presenceSet = copyNull(this.presenceSet);
    set.list = this.toArray();
    set.size = this.size;

    return set;
  }
};

deprecateProperty(OrderedSet.prototype, 'length', 'size');

/**
  A Map stores values indexed by keys. Unlike JavaScript's
  default Objects, the keys of a Map can be any JavaScript
  object.

  Internally, a Map has two data structures:

  1. `keys`: an OrderedSet of all of the existing keys
  2. `values`: a JavaScript Object indexed by the `Ember.guidFor(key)`

  When a key/value pair is added for the first time, we
  add the key to the `keys` OrderedSet, and create or
  replace an entry in `values`. When an entry is deleted,
  we delete its entry in `keys` and `values`.

  @class Map
  @namespace Ember
  @private
  @constructor
*/
function Map() {
  if (this instanceof this.constructor) {
    this._keys = OrderedSet.create();
    this._keys._silenceRemoveDeprecation = true;
    this._values = create(null);
    this.size = 0;
  } else {
    missingNew("OrderedSet");
  }
}

Ember.Map = Map;

/**
  @method create
  @static
*/
Map.create = function() {
  var Constructor = this;
  return new Constructor();
};

Map.prototype = {
  constructor: Map,

  /**
    This property will change as the number of objects in the map changes.

    @property size
    @type number
    @default 0
  */
  size: 0,

  /**
    Retrieve the value associated with a given key.

    @method get
    @param {*} key
    @return {*} the value associated with the key, or `undefined`
  */
  get: function(key) {
    if (this.size === 0) { return; }

    var values = this._values;
    var guid = guidFor(key);

    return values[guid];
  },

  /**
    Adds a value to the map. If a value for the given key has already been
    provided, the new value will replace the old value.

    @method set
    @param {*} key
    @param {*} value
    @return {Ember.Map}
  */
  set: function(key, value) {
    var keys = this._keys;
    var values = this._values;
    var guid = guidFor(key);

    // ensure we don't store -0
    var k = key === -0 ? 0 : key;

    keys.add(k, guid);

    values[guid] = value;

    this.size = keys.size;

    return this;
  },

  /**
    @deprecated see delete
    Removes a value from the map for an associated key.

    @method remove
    @param {*} key
    @return {Boolean} true if an item was removed, false otherwise
  */
  remove: function(key) {
    Ember.deprecate('Calling `Map.prototype.remove` has been deprecated, please use `Map.prototype.delete` instead.');

    return this.delete(key);
  },

  /**
    Removes a value from the map for an associated key.

    @method delete
    @param {*} key
    @return {Boolean} true if an item was removed, false otherwise
  */
  delete: function(key) {
    if (this.size === 0) { return false; }
    // don't use ES6 "delete" because it will be annoying
    // to use in browsers that are not ES6 friendly;
    var keys = this._keys;
    var values = this._values;
    var guid = guidFor(key);

    if (keys.delete(key, guid)) {
      delete values[guid];
      this.size = keys.size;
      return true;
    } else {
      return false;
    }
  },

  /**
    Check whether a key is present.

    @method has
    @param {*} key
    @return {Boolean} true if the item was present, false otherwise
  */
  has: function(key) {
    return this._keys.has(key);
  },

  /**
    Iterate over all the keys and values. Calls the function once
    for each key, passing in value, key, and the map being iterated over,
    in that order.

    The keys are guaranteed to be iterated over in insertion order.

    @method forEach
    @param {Function} callback
    @param {*} self if passed, the `this` value inside the
      callback. By default, `this` is the map.
  */
  forEach: function(callback /*, thisArg*/) {
    if (typeof callback !== 'function') {
      missingFunction(callback);
    }

    if (this.size === 0) { return; }

    var length = arguments.length;
    var map = this;
    var cb, thisArg;

    if (length === 2) {
      thisArg = arguments[1];
      cb = function(key) {
        callback.call(thisArg, map.get(key), key, map);
      };
    } else {
      cb = function(key) {
        callback(map.get(key), key, map);
      };
    }

    this._keys.forEach(cb);
  },

  /**
    @method clear
  */
  clear: function() {
    this._keys.clear();
    this._values = create(null);
    this.size = 0;
  },

  /**
    @method copy
    @return {Ember.Map}
  */
  copy: function() {
    return copyMap(this, new Map());
  }
};

deprecateProperty(Map.prototype, 'length', 'size');

/**
  @class MapWithDefault
  @namespace Ember
  @extends Ember.Map
  @private
  @constructor
  @param [options]
    @param {*} [options.defaultValue]
*/
function MapWithDefault(options) {
  this._super$constructor();
  this.defaultValue = options.defaultValue;
}

/**
  @method create
  @static
  @param [options]
    @param {*} [options.defaultValue]
  @return {Ember.MapWithDefault|Ember.Map} If options are passed, returns
    `Ember.MapWithDefault` otherwise returns `Ember.Map`
*/
MapWithDefault.create = function(options) {
  if (options) {
    return new MapWithDefault(options);
  } else {
    return new Map();
  }
};

MapWithDefault.prototype = create(Map.prototype);
MapWithDefault.prototype.constructor = MapWithDefault;
MapWithDefault.prototype._super$constructor = Map;
MapWithDefault.prototype._super$get = Map.prototype.get;

/**
  Retrieve the value associated with a given key.

  @method get
  @param {*} key
  @return {*} the value associated with the key, or the default value
*/
MapWithDefault.prototype.get = function(key) {
  var hasValue = this.has(key);

  if (hasValue) {
    return this._super$get(key);
  } else {
    var defaultValue = this.defaultValue(key);
    this.set(key, defaultValue);
    return defaultValue;
  }
};

/**
  @method copy
  @return {Ember.MapWithDefault}
*/
MapWithDefault.prototype.copy = function() {
  var Constructor = this.constructor;
  return copyMap(this, new Constructor({
    defaultValue: this.defaultValue
  }));
};

export default Map;

export {
  OrderedSet,
  Map,
  MapWithDefault
};
<|endoftext|>


<commit_before>/*
 * Optics / option.js
 * copyright (c) 2016 Susisu
 */

/**
 * @module option
 */

"use strict";

function endModule() {
    module.exports = Object.freeze({
        OptionArgument,
        OptionalOptionArgument,
        Option,
        SpecialOption
    });
}

/**
 * An instance of `OptionArgument` represents an argument of option.
 * @static
 */
class OptionArgument {
    /**
     * Creates a new `OptionArgument` instance.
     * @param {string} name A string used in placeholder.
     * @param {Function} reader A function which reads value given as argument.
     * @throws {TypeError} `name` is not a string.
     * @throws {TypeError} `reader` is not a function.
     */
    constructor(name, reader) {
        if (typeof name !== "string") {
            throw new TypeError("name must be a string");
        }
        if (typeof reader !== "function") {
            throw new TypeError("reader must be a function");
        }
        this.name   = name;
        this.reader = reader;
    }

    /**
     * Returns whether the argument is required.
     * This method always returns `true`.
     * @returns {boolean} `true`.
     */
    isRequired() {
        return true;
    }

    /**
     * Returns a string used as a placeholder of the argument of a short-named option.
     * @returns {string} A string used as a placeholder.
     */
    toShortPlaceholder() {
        return this.name;
    }

    /**
     * Returns a string used as a placeholder of the argument of a long-named option.
     * @returns {string} A string used as a placeholder.
     */
    toLongPlaceholder() {
        return `=${this.name}`;
    }

    /**
     * Returns the default value, which is used when the argument is not specified.
     * @throws {Error} No default value for a requried argument.
     */
    getDefaultValue() {
        throw new Error("no default value");
    }
}

/**
 * An instance of `OptionalOptionArgument` represents an optional argument of option.
 * @static
 * @extends {module:option.OptionArgument}
 */
class OptionalOptionArgument extends OptionArgument {
    /**
     * Creates a new `OptionalOptionArgument` instance.
     * @param {string} name A string used in placeholder.
     * @param defaultVal The default value, which is used when the argument is not specified.
     * @param {Function} reader A function which reads value given as argument.
     */
    constructor(name, defaultVal, reader) {
        super(name, reader);
        this.defaultVal = defaultVal;
    }

    /**
     * Returns whether the argument is required.
     * This method always returns `false`.
     * @returns {boolean} `false`.
     */
    isRequired() {
        return false;
    }

    /**
     * Returns a string used as a placeholder of the argument of a short-named option.
     * @returns {string} A string used as a placeholder.
     */
    toShortPlaceholder() {
        return `[${this.name}]`;
    }

    /**
     * Returns a string used as a placeholder of the argument of a long-named option.
     * @returns {string} A string used as a placeholder.
     */
    toLongPlaceholder() {
        return `[=${this.name}]`;
    }

    /**
     * Returns the default value, which is used when the argument is not specified.
     * @returns The default value specified at the constructor.
     */
    getDefaultValue() {
        return this.defaultVal;
    }
}

/**
 * An instance of `Option` represents a command-line option of command.
 * @static
 */
class Option {
    /**
     * Creates an new instance of `Option`
     * @param {(string | undefined)} shortName Short name of the option.
     * @param {(string | undefined)} longName Long name of the option.
     * @param {(module:option.OptionArgument | null)} arg Argument of the option.
     * If `null` is specified, the option has no argument.
     * @param {string} desc Description of the option.
     * @throws {TypeError} `shortName` is not a string nor undefined.
     * @throws {Error} The length of `shortName` is not 1.
     * @throws {TypeError} `longName` is not a string nor undefined.
     * @throws {Error} The length of `longName` is 0.
     * @throws {Error} Both `shortName` and `longName` are undefined.
     * @throws {TypeError} `arg` is not an instance of OptionArgument nor null.
     * @throws {TypeError} `desc` is not a string.
     */
    constructor(shortName, longName, arg, desc) {
        if (typeof shortName !== "string" && typeof shortName !== "undefined") {
            throw new TypeError("shortName must be a string or undefined");
        }
        if (typeof shortName === "string" && shortName.length !== 1) {
            throw new Error("the length of shortName must be 1");
        }
        if (typeof longName !== "string" && typeof longName !== "undefined") {
            throw new TypeError("longName must be a string or undefined")
        }
        if (typeof longName === "string" && longName.length === 0) {
            throw new Error("the length of longName must be greater than 0");
        }
        if (typeof shortName === "undefined" && typeof longName === "undefined") {
            throw new Error("one of shortName or longName must be specified");
        }
        if (!(arg instanceof OptionArgument) && arg !== null) {
            throw new TypeError("arg must be an instance of OptionArgument or null");
        }
        if (typeof desc !== "string") {
            throw new TypeError("desc must be a string");
        }
        this.shortName = shortName;
        this.longName  = longName;
        this.arg       = arg;
        this.desc      = desc;
    }
}

/**
 * An instance of the `SpecialOption` class represents a special option.
 * The action associated to a special option is invoked on parsing command-line arguments,
 * and can interrupt its process.
 * @static
 */
class SpecialOption extends Option {
    /**
     * Creates a new instance of `SpecialOption`.
     * @param {(string | undefined)} shortName Short name of the option.
     * @param {(string | undefined)} longName Long name of the option.
     * @param {(module:option.OptionArgument | null)} arg Argument of the option.
     * If `null` is specified, the option has no argument.
     * @param {string} desc Description of the option.
     * @param {Function} action A function associated to the option.
     */
    constructor(shortName, longName, arg, desc, action) {
        super(shortName, longName, arg, desc);
        this.action = action;
    }

    /**
     * Returns if the option is a special option.
     * This method always returns `true`.
     * @returns {boolean} `true`.
     */
    isSpecial() {
        return true;
    }

    /**
     * Invokes the action associated to the option.
     * @param {module:command.CommandBase} cmd The command by which the option is invoked.
     * @param {module:output.Output} out Output target.
     * @param arg Argument given to the option.
     */
    invoke(cmd, out, arg) {
        return this.action.call(this, cmd, out, arg);
    }
}

endModule();
<commit_msg>Add Option#isSpecial() method<commit_after>/*
 * Optics / option.js
 * copyright (c) 2016 Susisu
 */

/**
 * @module option
 */

"use strict";

function endModule() {
    module.exports = Object.freeze({
        OptionArgument,
        OptionalOptionArgument,
        Option,
        SpecialOption
    });
}

/**
 * An instance of `OptionArgument` represents an argument of option.
 * @static
 */
class OptionArgument {
    /**
     * Creates a new `OptionArgument` instance.
     * @param {string} name A string used in placeholder.
     * @param {Function} reader A function which reads value given as argument.
     * @throws {TypeError} `name` is not a string.
     * @throws {TypeError} `reader` is not a function.
     */
    constructor(name, reader) {
        if (typeof name !== "string") {
            throw new TypeError("name must be a string");
        }
        if (typeof reader !== "function") {
            throw new TypeError("reader must be a function");
        }
        this.name   = name;
        this.reader = reader;
    }

    /**
     * Returns whether the argument is required.
     * This method always returns `true`.
     * @returns {boolean} `true`.
     */
    isRequired() {
        return true;
    }

    /**
     * Returns a string used as a placeholder of the argument of a short-named option.
     * @returns {string} A string used as a placeholder.
     */
    toShortPlaceholder() {
        return this.name;
    }

    /**
     * Returns a string used as a placeholder of the argument of a long-named option.
     * @returns {string} A string used as a placeholder.
     */
    toLongPlaceholder() {
        return `=${this.name}`;
    }

    /**
     * Returns the default value, which is used when the argument is not specified.
     * @throws {Error} No default value for a requried argument.
     */
    getDefaultValue() {
        throw new Error("no default value");
    }
}

/**
 * An instance of `OptionalOptionArgument` represents an optional argument of option.
 * @static
 * @extends {module:option.OptionArgument}
 */
class OptionalOptionArgument extends OptionArgument {
    /**
     * Creates a new `OptionalOptionArgument` instance.
     * @param {string} name A string used in placeholder.
     * @param defaultVal The default value, which is used when the argument is not specified.
     * @param {Function} reader A function which reads value given as argument.
     */
    constructor(name, defaultVal, reader) {
        super(name, reader);
        this.defaultVal = defaultVal;
    }

    /**
     * Returns whether the argument is required.
     * This method always returns `false`.
     * @returns {boolean} `false`.
     */
    isRequired() {
        return false;
    }

    /**
     * Returns a string used as a placeholder of the argument of a short-named option.
     * @returns {string} A string used as a placeholder.
     */
    toShortPlaceholder() {
        return `[${this.name}]`;
    }

    /**
     * Returns a string used as a placeholder of the argument of a long-named option.
     * @returns {string} A string used as a placeholder.
     */
    toLongPlaceholder() {
        return `[=${this.name}]`;
    }

    /**
     * Returns the default value, which is used when the argument is not specified.
     * @returns The default value specified at the constructor.
     */
    getDefaultValue() {
        return this.defaultVal;
    }
}

/**
 * An instance of `Option` represents a command-line option of command.
 * @static
 */
class Option {
    /**
     * Creates an new instance of `Option`
     * @param {(string | undefined)} shortName Short name of the option.
     * @param {(string | undefined)} longName Long name of the option.
     * @param {(module:option.OptionArgument | null)} arg Argument of the option.
     * If `null` is specified, the option has no argument.
     * @param {string} desc Description of the option.
     * @throws {TypeError} `shortName` is not a string nor undefined.
     * @throws {Error} The length of `shortName` is not 1.
     * @throws {TypeError} `longName` is not a string nor undefined.
     * @throws {Error} The length of `longName` is 0.
     * @throws {Error} Both `shortName` and `longName` are undefined.
     * @throws {TypeError} `arg` is not an instance of OptionArgument nor null.
     * @throws {TypeError} `desc` is not a string.
     */
    constructor(shortName, longName, arg, desc) {
        if (typeof shortName !== "string" && typeof shortName !== "undefined") {
            throw new TypeError("shortName must be a string or undefined");
        }
        if (typeof shortName === "string" && shortName.length !== 1) {
            throw new Error("the length of shortName must be 1");
        }
        if (typeof longName !== "string" && typeof longName !== "undefined") {
            throw new TypeError("longName must be a string or undefined")
        }
        if (typeof longName === "string" && longName.length === 0) {
            throw new Error("the length of longName must be greater than 0");
        }
        if (typeof shortName === "undefined" && typeof longName === "undefined") {
            throw new Error("one of shortName or longName must be specified");
        }
        if (!(arg instanceof OptionArgument) && arg !== null) {
            throw new TypeError("arg must be an instance of OptionArgument or null");
        }
        if (typeof desc !== "string") {
            throw new TypeError("desc must be a string");
        }
        this.shortName = shortName;
        this.longName  = longName;
        this.arg       = arg;
        this.desc      = desc;
    }

    /**
     * Returns if the option is a special option.
     * This method always returns `false`.
     * @returns {boolean} `false`.
     */
    isSpecial() {
        return false;
    }
}

/**
 * An instance of the `SpecialOption` class represents a special option.
 * The action associated to a special option is invoked on parsing command-line arguments,
 * and can interrupt its process.
 * @static
 */
class SpecialOption extends Option {
    /**
     * Creates a new instance of `SpecialOption`.
     * @param {(string | undefined)} shortName Short name of the option.
     * @param {(string | undefined)} longName Long name of the option.
     * @param {(module:option.OptionArgument | null)} arg Argument of the option.
     * If `null` is specified, the option has no argument.
     * @param {string} desc Description of the option.
     * @param {Function} action A function associated to the option.
     */
    constructor(shortName, longName, arg, desc, action) {
        super(shortName, longName, arg, desc);
        this.action = action;
    }

    /**
     * Returns if the option is a special option.
     * This method always returns `true`.
     * @returns {boolean} `true`.
     */
    isSpecial() {
        return true;
    }

    /**
     * Invokes the action associated to the option.
     * @param {module:command.CommandBase} cmd The command by which the option is invoked.
     * @param {module:output.Output} out Output target.
     * @param arg Argument given to the option.
     */
    invoke(cmd, out, arg) {
        return this.action.call(this, cmd, out, arg);
    }
}

endModule();
<|endoftext|>


<commit_before>/*
Copyright 2019 New Vector Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import FixedDistributor from "./fixed";

// const allowWhitespace = true;
const handleHeight = 1;

function log() {
}

function clamp(height, min, max) {
    //log(`clamping ${height} between ${min} and ${max}`);
    if (height > max) return max;
    if (height < min) return min;
    return height;
}

export class Layout {
    constructor(applyHeight, initialSizes, collapsedState) {
        this._applyHeight = applyHeight;
        this._sections = [];
        this._collapsedState = Object.assign({}, collapsedState);
        this._availableHeight = 0;
        // heights stored by section section id
        this._sectionHeights = Object.assign({}, initialSizes);
        // in-progress heights, while dragging. Committed on mouse-up.
        this._heights = [];
    }

    setAvailableHeight(newSize) {
        this._availableHeight = newSize;
        // needs more work
        this._applyNewSize();
    }

    expandSection(id, height) {
        this._collapsedState[id] = false;
        this._applyNewSize();
        this.openHandle(id).setHeight(height).finish();
    }

    collapseSection(id) {
        this._collapsedState[id] = true;
        this._applyNewSize();
    }

    // [{id, count}]
    update(sections, availableHeight) {
        if (Number.isFinite(availableHeight)) {
            this._availableHeight = availableHeight;
        }
        const totalHeight = this._getAvailableHeight();
        this._sections.forEach((section, i) => {
            if (!this._sectionHeights.hasOwnProperty(section.id)) {
                this._sectionHeights[section.id] = clamp(
                    totalHeight / this._sections.length,
                    this._getMinHeight(i),
                    this._getMaxHeight(i),
                );
            }
        });
        this._sections = sections;
        this._applyNewSize();
    }

    openHandle(id) {
        const index = this._getSectionIndex(id);
        //log(`openHandle resolved ${id} to ${index}`);
        return new Handle(this, index, this._sectionHeights[id]);
    }

    _getAvailableHeight() {
        const nonCollapsedSectionCount = this._sections.reduce((count, section) => {
            const collapsed = this._collapsedState[section.id];
            return count + (collapsed ? 0 : 1);
        }, 0);
        return this._availableHeight - ((nonCollapsedSectionCount - 1) * handleHeight);
    }

    _applyNewSize() {
        const newHeight = this._getAvailableHeight();
        const currHeight = this._sections.reduce((sum, section) => {
            return sum + this._sectionHeights[section.id];
        }, 0);
        const offset = newHeight - currHeight;
        this._heights = this._sections.map((section) => this._sectionHeights[section.id]);
        const sections = this._sections.map((_, i) => i);
        this._applyOverflow(-offset, sections, true);
        this._applyHeights();
        this._commitHeights();
    }

    _getSectionIndex(id) {
        return this._sections.findIndex((s) => s.id === id);
    }

    _getMaxHeight(i) {
        const section = this._sections[i];
        const collapsed = this._collapsedState[section.id];

        if (collapsed) {
            return this._sectionHeight(0);
        } else {
            return 100000;
            // return this._sectionHeight(section.count);
        }
    }

    _sectionHeight(count) {
        return 36 + (count === 0 ? 0 : 4 + (count * 34));
    }

    _getMinHeight(i) {
        const section = this._sections[i];
        const collapsed = this._collapsedState[section.id];
        const maxItems = collapsed ? 0 : 1;
        // log("_getMinHeight", i, section);
        return this._sectionHeight(Math.min(section.count, maxItems));
    }

    _applyOverflow(overflow, sections, blend) {
        //log("applyOverflow", overflow, sections);
        // take the given overflow amount, and applies it to the given sections.
        // calls itself recursively until it has distributed all the overflow
        // or run out of unclamped sections.

        const unclampedSections = [];

        let overflowPerSection = blend ? (overflow / sections.length) : overflow;
        for (const i of sections) {
            const newHeight = clamp(
                this._heights[i] - overflowPerSection,
                this._getMinHeight(i),
                this._getMaxHeight(i),
            );
            if (newHeight == this._heights[i] - overflowPerSection) {
                unclampedSections.push(i);
            }
            // when section is growing, overflow increases?
            // 100 -= 200 - 300
            // 100 -= -100
            // 200
            overflow -= this._heights[i] - newHeight;
            // console.log(`this._heights[${i}] (${this._heights[i]}) - newHeight (${newHeight}) = ${this._heights[i] - newHeight}`);
            // console.log(`changing ${this._heights[i]} to ${newHeight}`);
            this._heights[i] = newHeight;
            // console.log(`for section ${i} overflow is ${overflow}`);
            if (!blend) {
                overflowPerSection = overflow;
                if (Math.abs(overflow) < 1.0) break;
            }
        }

        if (Math.abs(overflow) > 1.0 && unclampedSections.length > 0) {
            // we weren't able to distribute all the overflow so recurse and try again
            // log("recursing with", overflow, unclampedSections);
            overflow = this._applyOverflow(overflow, unclampedSections, blend);
        }

        return overflow;
    }

    _rebalanceAbove(anchor, overflowAbove) {
        if (Math.abs(overflowAbove) > 1.0) {
            // log(`trying to rebalance upstream with ${overflowAbove}`);
            const sections = [];
            for (let i = anchor - 1; i >= 0; i--) {
                sections.push(i);
            }
            overflowAbove = this._applyOverflow(overflowAbove, sections);
        }
        return overflowAbove;
    }

    _rebalanceBelow(anchor, overflowBelow) {
        if (Math.abs(overflowBelow) > 1.0) {
            // log(`trying to rebalance downstream with ${overflowBelow}`);
            const sections = [];
            for (let i = anchor + 1; i < this._sections.length; i++) {
                sections.push(i);
            }
            overflowBelow = this._applyOverflow(overflowBelow, sections);
            //log(`rebalanced downstream with ${overflowBelow}`);
        }
        return overflowBelow;
    }

    // @param offset the amount the anchor is moved from what is stored in _sectionHeights, positive if downwards
    // if we're clamped, return the offset we should be clamped at.
    _relayout(anchor = 0, offset = 0, clamped = false) {
        this._heights = this._sections.map((section) => this._sectionHeights[section.id]);
        // are these the amounts the items above/below shrank/grew and need to be relayouted?
        let overflowAbove;
        let overflowBelow;
        const maxHeight = this._getMaxHeight(anchor);
        const minHeight = this._getMinHeight(anchor);
        // new height > max ?
        if (this._heights[anchor] + offset > maxHeight) {
            // we're pulling downwards and clamped
            // overflowAbove = minus how much are we above max height?
            overflowAbove = (maxHeight - this._heights[anchor]) - offset;
            overflowBelow = offset;
            // log(`pulling downwards clamped at max: ${overflowAbove} ${overflowBelow}`);
        } else if (this._heights[anchor] + offset < minHeight) { // new height < min?
            // we're pulling upwards and clamped
            // overflowAbove = ??? (offset is negative here, so - offset will add)
            overflowAbove = (minHeight - this._heights[anchor]) - offset;
            overflowBelow = offset;
            // log(`pulling upwards clamped at min: ${overflowAbove} ${overflowBelow}`);
        } else {
            overflowAbove = 0;
            overflowBelow = offset;
            // log(`resizing the anchor: ${overflowAbove} ${overflowBelow}`);
        }
        this._heights[anchor] = clamp(this._heights[anchor] + offset, minHeight, maxHeight);

        // these are reassigned the amount of overflow that could not be rebalanced
        // meaning we dragged the handle too far and it can't follow the cursor anymore
        overflowAbove = this._rebalanceAbove(anchor, overflowAbove);
        overflowBelow = this._rebalanceBelow(anchor, overflowBelow);

        if (!clamped) { // to avoid risk of infinite recursion
            // clamp to avoid overflowing or underflowing the page
            if (Math.abs(overflowAbove) > 1.0) {
                // log(`clamping with overflowAbove ${overflowAbove}`);
                // here we do the layout again with offset - the amount of space we took too much
                this._relayout(anchor, offset + overflowAbove, true);
                return offset + overflowAbove;
            }

            if (Math.abs(overflowBelow) > 1.0) {
                // here we do the layout again with offset - the amount of space we took too much
                // log(`clamping with overflowBelow ${overflowBelow}`);
                this._relayout(anchor, offset - overflowBelow, true);
                return offset - overflowBelow;
            }
        }

        this._applyHeights();
        return undefined;
    }

    _applyHeights() {
        log("updating layout, heights are now", this._heights);
        // apply the heights
        for (let i = 0; i < this._sections.length; i++) {
            const section = this._sections[i];
            this._applyHeight(section.id, this._heights[i]);
        }
    }

    _commitHeights() {
        this._sections.forEach((section, i) => {
            this._sectionHeights[section.id] = this._heights[i];
        });
    }
}

class Handle {
    constructor(layout, anchor, height) {
        this._layout = layout;
        this._anchor = anchor;
        this._initialHeight = height;
    }

    setHeight(height) {
        this._layout._relayout(this._anchor, height - this._initialHeight);
        return this;
    }

    finish() {
        this._layout._commitHeights();
        return this;
    }
}

export class Distributor extends FixedDistributor {
    constructor(item, cfg) {
        super(item);
        const layout = cfg.layout;
        this._handle = layout.openHandle(item.id);
    }

    finish() {
        this._handle.finish();
    }

    resize(height) {
        this._handle.setHeight(height);
    }
}
<commit_msg>assign default height after assigning new sections<commit_after>/*
Copyright 2019 New Vector Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import FixedDistributor from "./fixed";

// const allowWhitespace = true;
const handleHeight = 1;

function log() {
}

function clamp(height, min, max) {
    //log(`clamping ${height} between ${min} and ${max}`);
    if (height > max) return max;
    if (height < min) return min;
    return height;
}

export class Layout {
    constructor(applyHeight, initialSizes, collapsedState) {
        this._applyHeight = applyHeight;
        this._sections = [];
        this._collapsedState = Object.assign({}, collapsedState);
        this._availableHeight = 0;
        // heights stored by section section id
        this._sectionHeights = Object.assign({}, initialSizes);
        // in-progress heights, while dragging. Committed on mouse-up.
        this._heights = [];
    }

    setAvailableHeight(newSize) {
        this._availableHeight = newSize;
        // needs more work
        this._applyNewSize();
    }

    expandSection(id, height) {
        this._collapsedState[id] = false;
        this._applyNewSize();
        this.openHandle(id).setHeight(height).finish();
    }

    collapseSection(id) {
        this._collapsedState[id] = true;
        this._applyNewSize();
    }

    // [{id, count}]
    update(sections, availableHeight) {
        if (Number.isFinite(availableHeight)) {
            this._availableHeight = availableHeight;
        }
        this._sections = sections;
        const totalHeight = this._getAvailableHeight();
        this._sections.forEach((section, i) => {
            if (!this._sectionHeights[section.id]) {
                this._sectionHeights[section.id] = clamp(
                    totalHeight / this._sections.length,
                    this._getMinHeight(i),
                    this._getMaxHeight(i),
                );
            }
        });
        this._applyNewSize();
    }

    openHandle(id) {
        const index = this._getSectionIndex(id);
        //log(`openHandle resolved ${id} to ${index}`);
        return new Handle(this, index, this._sectionHeights[id]);
    }

    _getAvailableHeight() {
        const nonCollapsedSectionCount = this._sections.reduce((count, section) => {
            const collapsed = this._collapsedState[section.id];
            return count + (collapsed ? 0 : 1);
        }, 0);
        return this._availableHeight - ((nonCollapsedSectionCount - 1) * handleHeight);
    }

    _applyNewSize() {
        const newHeight = this._getAvailableHeight();
        const currHeight = this._sections.reduce((sum, section) => {
            return sum + this._sectionHeights[section.id];
        }, 0);
        const offset = newHeight - currHeight;
        this._heights = this._sections.map((section) => this._sectionHeights[section.id]);
        const sections = this._sections.map((_, i) => i);
        this._applyOverflow(-offset, sections, true);
        this._applyHeights();
        this._commitHeights();
    }

    _getSectionIndex(id) {
        return this._sections.findIndex((s) => s.id === id);
    }

    _getMaxHeight(i) {
        const section = this._sections[i];
        const collapsed = this._collapsedState[section.id];

        if (collapsed) {
            return this._sectionHeight(0);
        } else {
            return 100000;
            // return this._sectionHeight(section.count);
        }
    }

    _sectionHeight(count) {
        return 36 + (count === 0 ? 0 : 4 + (count * 34));
    }

    _getMinHeight(i) {
        const section = this._sections[i];
        const collapsed = this._collapsedState[section.id];
        const maxItems = collapsed ? 0 : 1;
        // log("_getMinHeight", i, section);
        return this._sectionHeight(Math.min(section.count, maxItems));
    }

    _applyOverflow(overflow, sections, blend) {
        //log("applyOverflow", overflow, sections);
        // take the given overflow amount, and applies it to the given sections.
        // calls itself recursively until it has distributed all the overflow
        // or run out of unclamped sections.

        const unclampedSections = [];

        let overflowPerSection = blend ? (overflow / sections.length) : overflow;
        for (const i of sections) {
            const newHeight = clamp(
                this._heights[i] - overflowPerSection,
                this._getMinHeight(i),
                this._getMaxHeight(i),
            );
            if (newHeight == this._heights[i] - overflowPerSection) {
                unclampedSections.push(i);
            }
            // when section is growing, overflow increases?
            // 100 -= 200 - 300
            // 100 -= -100
            // 200
            overflow -= this._heights[i] - newHeight;
            // console.log(`this._heights[${i}] (${this._heights[i]}) - newHeight (${newHeight}) = ${this._heights[i] - newHeight}`);
            // console.log(`changing ${this._heights[i]} to ${newHeight}`);
            this._heights[i] = newHeight;
            // console.log(`for section ${i} overflow is ${overflow}`);
            if (!blend) {
                overflowPerSection = overflow;
                if (Math.abs(overflow) < 1.0) break;
            }
        }

        if (Math.abs(overflow) > 1.0 && unclampedSections.length > 0) {
            // we weren't able to distribute all the overflow so recurse and try again
            // log("recursing with", overflow, unclampedSections);
            overflow = this._applyOverflow(overflow, unclampedSections, blend);
        }

        return overflow;
    }

    _rebalanceAbove(anchor, overflowAbove) {
        if (Math.abs(overflowAbove) > 1.0) {
            // log(`trying to rebalance upstream with ${overflowAbove}`);
            const sections = [];
            for (let i = anchor - 1; i >= 0; i--) {
                sections.push(i);
            }
            overflowAbove = this._applyOverflow(overflowAbove, sections);
        }
        return overflowAbove;
    }

    _rebalanceBelow(anchor, overflowBelow) {
        if (Math.abs(overflowBelow) > 1.0) {
            // log(`trying to rebalance downstream with ${overflowBelow}`);
            const sections = [];
            for (let i = anchor + 1; i < this._sections.length; i++) {
                sections.push(i);
            }
            overflowBelow = this._applyOverflow(overflowBelow, sections);
            //log(`rebalanced downstream with ${overflowBelow}`);
        }
        return overflowBelow;
    }

    // @param offset the amount the anchor is moved from what is stored in _sectionHeights, positive if downwards
    // if we're clamped, return the offset we should be clamped at.
    _relayout(anchor = 0, offset = 0, clamped = false) {
        this._heights = this._sections.map((section) => this._sectionHeights[section.id]);
        // are these the amounts the items above/below shrank/grew and need to be relayouted?
        let overflowAbove;
        let overflowBelow;
        const maxHeight = this._getMaxHeight(anchor);
        const minHeight = this._getMinHeight(anchor);
        // new height > max ?
        if (this._heights[anchor] + offset > maxHeight) {
            // we're pulling downwards and clamped
            // overflowAbove = minus how much are we above max height?
            overflowAbove = (maxHeight - this._heights[anchor]) - offset;
            overflowBelow = offset;
            // log(`pulling downwards clamped at max: ${overflowAbove} ${overflowBelow}`);
        } else if (this._heights[anchor] + offset < minHeight) { // new height < min?
            // we're pulling upwards and clamped
            // overflowAbove = ??? (offset is negative here, so - offset will add)
            overflowAbove = (minHeight - this._heights[anchor]) - offset;
            overflowBelow = offset;
            // log(`pulling upwards clamped at min: ${overflowAbove} ${overflowBelow}`);
        } else {
            overflowAbove = 0;
            overflowBelow = offset;
            // log(`resizing the anchor: ${overflowAbove} ${overflowBelow}`);
        }
        this._heights[anchor] = clamp(this._heights[anchor] + offset, minHeight, maxHeight);

        // these are reassigned the amount of overflow that could not be rebalanced
        // meaning we dragged the handle too far and it can't follow the cursor anymore
        overflowAbove = this._rebalanceAbove(anchor, overflowAbove);
        overflowBelow = this._rebalanceBelow(anchor, overflowBelow);

        if (!clamped) { // to avoid risk of infinite recursion
            // clamp to avoid overflowing or underflowing the page
            if (Math.abs(overflowAbove) > 1.0) {
                // log(`clamping with overflowAbove ${overflowAbove}`);
                // here we do the layout again with offset - the amount of space we took too much
                this._relayout(anchor, offset + overflowAbove, true);
                return offset + overflowAbove;
            }

            if (Math.abs(overflowBelow) > 1.0) {
                // here we do the layout again with offset - the amount of space we took too much
                // log(`clamping with overflowBelow ${overflowBelow}`);
                this._relayout(anchor, offset - overflowBelow, true);
                return offset - overflowBelow;
            }
        }

        this._applyHeights();
        return undefined;
    }

    _applyHeights() {
        log("updating layout, heights are now", this._heights);
        // apply the heights
        for (let i = 0; i < this._sections.length; i++) {
            const section = this._sections[i];
            this._applyHeight(section.id, this._heights[i]);
        }
    }

    _commitHeights() {
        this._sections.forEach((section, i) => {
            this._sectionHeights[section.id] = this._heights[i];
        });
    }
}

class Handle {
    constructor(layout, anchor, height) {
        this._layout = layout;
        this._anchor = anchor;
        this._initialHeight = height;
    }

    setHeight(height) {
        this._layout._relayout(this._anchor, height - this._initialHeight);
        return this;
    }

    finish() {
        this._layout._commitHeights();
        return this;
    }
}

export class Distributor extends FixedDistributor {
    constructor(item, cfg) {
        super(item);
        const layout = cfg.layout;
        this._handle = layout.openHandle(item.id);
    }

    finish() {
        this._handle.finish();
    }

    resize(height) {
        this._handle.setHeight(height);
    }
}
<|endoftext|>


<commit_before>/* eslint-disable max-statements */
// View for location

var emitter = require('component-emitter');

var locations = require('../../stores/locations');

//var getEntryView = require('./lib/getEntryView');
var NameView = require('./Name');
var PlacesView = require('./Places');
var GeomView = require('./Geom');
var TagsView = require('./Tags');
var FormsView = require('./Forms');
var RemoveView = require('./Remove');
var EntriesView = require('./Entries');
var EventsView = require('./Events');

// Templates
var locationTemplate = require('./template.ejs');

module.exports = function (id) {
  // Parameters
  //   id
  //     location id
  //
  // Emits
  //   removed
  //     when model emits "removed"

  // Init
  var self = this;
  emitter(self);

  // State
  var _location;
  var nameView, placesView, geomView, tagsView, formsView;
  var entriesView, eventsView, removeView;

  // Public methods

  self.bind = function ($mount) {

    // Loading
    $mount.html(locationTemplate());

    var $loading = $('#tresdb-location-loading');

    // Fetch location before rendering.
    locations.get(id, function (err, loc) {

      $loading.addClass('hidden');

      if (err) {
        console.error(err);
        return;
      }

      // Set state
      _location = loc;

      nameView = new NameView(_location);
      placesView = new PlacesView(_location);
      geomView = new GeomView(_location);
      tagsView = new TagsView(_location);
      formsView = new FormsView(_location);
      entriesView = new EntriesView(_location.getEntries());
      eventsView = new EventsView(_location.getEvents());
      removeView = new RemoveView(_location);

      nameView.bind($('#tresdb-location-name'));
      placesView.bind($('#tresdb-location-places'));
      geomView.bind($('#tresdb-location-geom'));
      tagsView.bind($('#tresdb-location-tags'));
      formsView.bind($('#tresdb-location-forms'));
      entriesView.bind($('#tresdb-location-entries'));
      eventsView.bind($('#tresdb-location-events'));
      removeView.bind($('#tresdb-location-remove'));

      // Listen possible changes in the location.

      // Inform parents that view model is removed and view should be closed.
      _location.on('location_removed', function () {
        self.unbind();
        self.emit('removed');
      });

      // Enable tooltips. See http://getbootstrap.com/javascript/#tooltips
      $('[data-toggle="tooltip"]').tooltip();

      // Inform view is ready
      self.emit('idle', _location);

    });
  };  // end bind

  self.unbind = function () {

    if (_location) {
      nameView.unbind();
      placesView.unbind();
      geomView.unbind();
      tagsView.unbind();
      formsView.unbind();
      entriesView.unbind();
      eventsView.unbind();
      removeView.unbind();
      _location.off();
    }
    _location = null;
  };

};
<commit_msg>client: location entries: open external links in new tabs<commit_after>/* eslint-disable max-statements */
// View for location

var emitter = require('component-emitter');

var locations = require('../../stores/locations');

//var getEntryView = require('./lib/getEntryView');
var NameView = require('./Name');
var PlacesView = require('./Places');
var GeomView = require('./Geom');
var TagsView = require('./Tags');
var FormsView = require('./Forms');
var RemoveView = require('./Remove');
var EntriesView = require('./Entries');
var EventsView = require('./Events');

// Templates
var locationTemplate = require('./template.ejs');

module.exports = function (id) {
  // Parameters
  //   id
  //     location id
  //
  // Emits
  //   removed
  //     when model emits "removed"

  // Init
  var self = this;
  emitter(self);

  // State
  var _location;
  var nameView, placesView, geomView, tagsView, formsView;
  var entriesView, eventsView, removeView;

  // Public methods

  self.bind = function ($mount) {

    // Loading
    $mount.html(locationTemplate());

    var $loading = $('#tresdb-location-loading');

    // Fetch location before rendering.
    locations.get(id, function (err, loc) {

      $loading.addClass('hidden');

      if (err) {
        console.error(err);
        return;
      }

      // Set state
      _location = loc;

      nameView = new NameView(_location);
      placesView = new PlacesView(_location);
      geomView = new GeomView(_location);
      tagsView = new TagsView(_location);
      formsView = new FormsView(_location);
      entriesView = new EntriesView(_location.getEntries());
      eventsView = new EventsView(_location.getEvents());
      removeView = new RemoveView(_location);

      nameView.bind($('#tresdb-location-name'));
      placesView.bind($('#tresdb-location-places'));
      geomView.bind($('#tresdb-location-geom'));
      tagsView.bind($('#tresdb-location-tags'));
      formsView.bind($('#tresdb-location-forms'));
      entriesView.bind($('#tresdb-location-entries'));
      eventsView.bind($('#tresdb-location-events'));
      removeView.bind($('#tresdb-location-remove'));

      // Listen possible changes in the location.

      // Inform parents that view model is removed and view should be closed.
      _location.on('location_removed', function () {
        self.unbind();
        self.emit('removed');
      });

      // Enable tooltips. See http://getbootstrap.com/javascript/#tooltips
      $('[data-toggle="tooltip"]').tooltip();

      // Command all external links in entries to open a new tab.
      // See http://stackoverflow.com/a/4425214/638546
      $('#tresdb-location-entries a').filter(function () {
        return this.hostname !== window.location.hostname;
      }).attr('target', '_blank');

      // Inform view is ready
      self.emit('idle', _location);

    });
  };  // end bind

  self.unbind = function () {

    if (_location) {
      nameView.unbind();
      placesView.unbind();
      geomView.unbind();
      tagsView.unbind();
      formsView.unbind();
      entriesView.unbind();
      eventsView.unbind();
      removeView.unbind();
      _location.off();
    }
    _location = null;
  };

};
<|endoftext|>


<commit_before>if (Meteor.isClient) {
  Meteor.startup(function() {
    Meteor.subscribe('Sensors');
    Meteor.subscribe('Measurements');
  });

  var prev = 0;
  var prevTimeStamp = 0;
  var timer = Chronos.createTimer({ granularity : "second" });
  timer.start();

  Template.sensor.data = function () {
    console.log('sensor.data')
    var data = Sensors.findOne({  _sensorId: 'woonkamer' }) || {};
    var now = data['_currentTime'] = +timer.getTime();

    if (data._timestamp != prevTimeStamp ) {
      prevTimeStamp = data._timestamp;
      var updated = true;
    }

    var timeDiff = now - prev;
    data['_timeDiff'] = timeDiff;
    data['timeAgo'] = Math.round(timeDiff/1000) + "s ago" ;

    // timeDiff < 20000
    //   ? Math.round(timeDiff/1000) + "s ago"
    //   : moment(prev).fromNow();

    var color = mapTo(timeDiff, 0, 60000, 0, 255); //Math.max(255 * (timeDiff / 60000));
    var alpha = 1 - mapTo(timeDiff, 0, 60000, 0, 0.5);
    data['_color'] = "rgba(" +Math.round(color)+ ",0,0, "+alpha+")";

    console.log(now, timeDiff);
    if (updated)
      prev = now;
    return data;
  };

  // Template.history.rendered = function() {
  //   var canvas = this.findAll('canvas')[0];
  //   var ctx = canvas.getContext('2d');
  //   // console.log('rendered', canvas);

  //   Deps.autorun(function() {
  //     var filtered = Measurements
  //     .find({ temperature : { $ne : null} }, { limit: 30})
  //     .fetch();

  //     var data = {
  //       labels : filtered.map(function(f,i) { return i; }),
  //       datasets : [{
  //         fillColor : "rgba(220,220,220,0.5)",
  //         strokeColor : "rgba(220,220,220,1)",
  //         pointColor : "rgba(220,220,220,1)",
  //         pointStrokeColor : "#fff",
  //         data : filtered.map(function(f) { return f.temperature; }),
  //       }]
  //     };

  //     console.time('chart');
  //     var myNewChart = new Chart(ctx).Bar(data, {
  //       animation: false,
  //       scaleOverride: true,
  //       scaleSteps: 10,
  //       scaleStepWidth: 1,
  //       scaleStartValue: 15
  //     });
  //     console.timeEnd('chart');

  //     //console.log(filtered);
  //   });
  // }
}

function mapTo (v, s0,s1, t0,t1) {
  var v_new = t1 * (v / s1)
  return Math.max(t0, Math.min(t1, v_new));
}

if (Meteor.isServer) {
  Meteor.startup(function() {
    // Measurements.remove({});
    // Sensors.remove({});

    Meteor.publish('Sensors', function() {
      return Sensors.find({});
    });

    Meteor.publish('Measurements', function() {
      return Measurements.find({});
    });
  });

  Meteor.methods({
    measurement : function(sensorId, timestamp, data) {
      console.log("measurement data received: [" + sensorId + "][" + timestamp + "]", data);

      Measurements.insert({
        _sensorId : sensorId,
        _timestamp : timestamp,
        temperature : data.temperature,
        humidity : data.humidity
      });

      if (data.temperature)
        Sensors.upsert({ _sensorId : sensorId }, {
          $set : {
            _timestamp : timestamp,
            temperature : data.temperature,
          }
        });

      if (data.humidity)
        Sensors.upsert({ _sensorId : sensorId }, {
          $set : {
            _timestamp : timestamp,
            humidity : data.humidity
          }
        });
    }
  })
}
Sensors = new Meteor.Collection("sensors");
Measurements = new Meteor.Collection("measurements");
<commit_msg>fix 'last updated' initial state<commit_after>if (Meteor.isClient) {
  Meteor.startup(function() {
    Meteor.subscribe('Sensors');
    Meteor.subscribe('Measurements');
  });

  var prev = null;
  var prevTimeStamp = null;
  var timer = Chronos.createTimer({ granularity : "second" });
  timer.start();

  Template.sensor.data = function () {
    // console.log('sensor.data')
    var data = Sensors.findOne({  _sensorId: 'woonkamer' }) || {};
    var now = data['_currentTime'] = +timer.getTime();

    if (data._timestamp != prevTimeStamp ) {
      prevTimeStamp = data._timestamp;
      if (prev === null)
        prev = prevTimeStamp;
      else
        var updated = true;
    }

    var delta = now - prev;
    data['_delta'] = delta;
    data['timeAgo'] = delta < 20000
      ? Math.round(delta/1000) + "s ago"
      : moment(now - delta).fromNow();

    var color = mapTo(delta, 0, 60000, 0, 255); //Math.max(255 * (delta / 60000));
    var alpha = 1 - mapTo(delta, 0, 60000, 0, 0.5);
    data['_color'] = "rgba(" +Math.round(color)+ ",0,0, "+alpha+")";

    if (updated)
      prev = now;
    return data;
  };

  // Template.history.rendered = function() {
  //   var canvas = this.findAll('canvas')[0];
  //   var ctx = canvas.getContext('2d');
  //   // console.log('rendered', canvas);

  //   Deps.autorun(function() {
  //     var filtered = Measurements
  //     .find({ temperature : { $ne : null} }, { limit: 30})
  //     .fetch();

  //     var data = {
  //       labels : filtered.map(function(f,i) { return i; }),
  //       datasets : [{
  //         fillColor : "rgba(220,220,220,0.5)",
  //         strokeColor : "rgba(220,220,220,1)",
  //         pointColor : "rgba(220,220,220,1)",
  //         pointStrokeColor : "#fff",
  //         data : filtered.map(function(f) { return f.temperature; }),
  //       }]
  //     };

  //     console.time('chart');
  //     var myNewChart = new Chart(ctx).Bar(data, {
  //       animation: false,
  //       scaleOverride: true,
  //       scaleSteps: 10,
  //       scaleStepWidth: 1,
  //       scaleStartValue: 15
  //     });
  //     console.timeEnd('chart');

  //     //console.log(filtered);
  //   });
  // }
}

function mapTo (v, s0,s1, t0,t1) {
  var v_new = t1 * (v / s1)
  return Math.max(t0, Math.min(t1, v_new));
}

if (Meteor.isServer) {
  Meteor.startup(function() {
    // Measurements.remove({});
    // Sensors.remove({});

    Meteor.publish('Sensors', function() {
      return Sensors.find({});
    });

    Meteor.publish('Measurements', function() {
      return Measurements.find({});
    });
  });

  Meteor.methods({
    measurement : function(sensorId, timestamp, data) {
      console.log("measurement data received: [" + sensorId + "][" + timestamp + "]", data);

      Measurements.insert({
        _sensorId : sensorId,
        _timestamp : timestamp,
        temperature : data.temperature,
        humidity : data.humidity
      });

      if (data.temperature)
        Sensors.upsert({ _sensorId : sensorId }, {
          $set : {
            _timestamp : timestamp,
            temperature : data.temperature,
          }
        });

      if (data.humidity)
        Sensors.upsert({ _sensorId : sensorId }, {
          $set : {
            _timestamp : timestamp,
            humidity : data.humidity
          }
        });
    }
  })
}
Sensors = new Meteor.Collection("sensors");
Measurements = new Meteor.Collection("measurements");
<|endoftext|>


<commit_before>if(!(['test','development'].indexOf(process.env.NODE_ENV || 'development') > -1 )){
  console.error('Can\'t execute database preparation on non-development database, could be dangerous!');
  return;
}

var mongoose = require('mongoose'),
  fs = require('fs'),
  $q = require('q'),
  async = require('async'),
  config = require('./config/config');

// define fixtures order
var fixtures = [
  'events'
];

// Load models
var modelsPath = __dirname + '/app/models';
fs.readdirSync(modelsPath).forEach(function (file) {
  if (file.indexOf('.js') >= 0) {
    require(modelsPath + '/' + file);
  }
});

//connect to db
mongoose.connect(config.db);
console.log('using database: '+ config.db);
var db = mongoose.connection;
db.on('error', function () {
  throw new Error('unable to connect to database at ' + config.db);
});

//prepare database before inserting fixtures
var dbprepared = $q.defer();
db.on('open', function(){
  console.log('open');
  mongoose.connection.db.dropDatabase(function(err){
    if(err){
      dbprepared.reject(err);
    }else{
      dbprepared.resolve();
    }
  });
});

//after db is prepared
dbprepared.promise.then(
  function(){
    console.log('Import fixtures');
    var fixturePath = __dirname + '/fixtures';

    //load fixtures in order
    async.eachSeries(fixtures,
      function(file, callback){
        console.log('- Fixture: ' + file);
        var fixture = require(fixturePath + '/' + file + '.js');
        if(!('then' in fixture)){
          callback('Fixture made no promise: ' + file);
        }else{
          fixture.then(
            function(){
              callback()
            }, function(error){
              callback('"' + error + '" on fixture: ' + file);
            }
          );
        }
      }, function(err){
        if(!err){
          console.log('Import finished successfully');
          process.exit(0);
        }else{
          console.error('An error occurred: '+ err);
          process.exit(1);
        }
        db.close();
      }
    );
  }, function(err){
    console.log('An error occurred while preparing the database: ' + (err.collection?' On Collection: "'+err.collection+'"':'') + (err.error?' With Error: '+err.error:''));
  }
);
<commit_msg>fix: deny prepare-db in production env<commit_after>if(!(['test','development'].indexOf(process.env.NODE_ENV || 'development') > -1 )){
  console.error('Can\'t execute database preparation on non-development database, could be dangerous!');
  process.exit(1);
}

var mongoose = require('mongoose'),
  fs = require('fs'),
  $q = require('q'),
  async = require('async'),
  config = require('./config/config');

// define fixtures order
var fixtures = [
  'events'
];

// Load models
var modelsPath = __dirname + '/app/models';
fs.readdirSync(modelsPath).forEach(function (file) {
  if (file.indexOf('.js') >= 0) {
    require(modelsPath + '/' + file);
  }
});

//connect to db
mongoose.connect(config.db);
console.log('using database: '+ config.db);
var db = mongoose.connection;
db.on('error', function () {
  throw new Error('unable to connect to database at ' + config.db);
});

//prepare database before inserting fixtures
var dbprepared = $q.defer();
db.on('open', function(){
  console.log('open');
  mongoose.connection.db.dropDatabase(function(err){
    if(err){
      dbprepared.reject(err);
    }else{
      dbprepared.resolve();
    }
  });
});

//after db is prepared
dbprepared.promise.then(
  function(){
    console.log('Import fixtures');
    var fixturePath = __dirname + '/fixtures';

    //load fixtures in order
    async.eachSeries(fixtures,
      function(file, callback){
        console.log('- Fixture: ' + file);
        var fixture = require(fixturePath + '/' + file + '.js');
        if(!('then' in fixture)){
          callback('Fixture made no promise: ' + file);
        }else{
          fixture.then(
            function(){
              callback()
            }, function(error){
              callback('"' + error + '" on fixture: ' + file);
            }
          );
        }
      }, function(err){
        if(!err){
          console.log('Import finished successfully');
          process.exit(0);
        }else{
          console.error('An error occurred: '+ err);
          process.exit(1);
        }
        db.close();
      }
    );
  }, function(err){
    console.log('An error occurred while preparing the database: ' + (err.collection?' On Collection: "'+err.collection+'"':'') + (err.error?' With Error: '+err.error:''));
  }
);
<|endoftext|>


<commit_before>module.exports = [
    'gl_Position'
  , 'gl_PointSize'
  , 'gl_ClipVertex'
  , 'gl_FragCoord'
  , 'gl_FrontFacing'
  , 'gl_FragColor'
  , 'gl_FragData'
  , 'gl_FragDepth'
  , 'gl_Color'
  , 'gl_SecondaryColor'
  , 'gl_Normal'
  , 'gl_Vertex'
  , 'gl_MultiTexCoord0'
  , 'gl_MultiTexCoord1'
  , 'gl_MultiTexCoord2'
  , 'gl_MultiTexCoord3'
  , 'gl_MultiTexCoord4'
  , 'gl_MultiTexCoord5'
  , 'gl_MultiTexCoord6'
  , 'gl_MultiTexCoord7'
  , 'gl_FogCoord'
  , 'gl_MaxLights'
  , 'gl_MaxClipPlanes'
  , 'gl_MaxTextureUnits'
  , 'gl_MaxTextureCoords'
  , 'gl_MaxVertexAttribs'
  , 'gl_MaxVertexUniformComponents'
  , 'gl_MaxVaryingFloats'
  , 'gl_MaxVertexTextureImageUnits'
  , 'gl_MaxCombinedTextureImageUnits'
  , 'gl_MaxTextureImageUnits'
  , 'gl_MaxFragmentUniformComponents'
  , 'gl_MaxDrawBuffers'
  , 'gl_ModelViewMatrix'
  , 'gl_ProjectionMatrix'
  , 'gl_ModelViewProjectionMatrix'
  , 'gl_TextureMatrix'
  , 'gl_NormalMatrix'
  , 'gl_ModelViewMatrixInverse'
  , 'gl_ProjectionMatrixInverse'
  , 'gl_ModelViewProjectionMatrixInverse'
  , 'gl_TextureMatrixInverse'
  , 'gl_ModelViewMatrixTranspose'
  , 'gl_ProjectionMatrixTranspose'
  , 'gl_ModelViewProjectionMatrixTranspose'
  , 'gl_TextureMatrixTranspose'
  , 'gl_ModelViewMatrixInverseTranspose'
  , 'gl_ProjectionMatrixInverseTranspose'
  , 'gl_ModelViewProjectionMatrixInverseTranspose'
  , 'gl_TextureMatrixInverseTranspose'
  , 'gl_NormalScale'
  , 'gl_DepthRangeParameters'
  , 'gl_DepthRange'
  , 'gl_ClipPlane'
  , 'gl_PointParameters'
  , 'gl_Point'
  , 'gl_MaterialParameters'
  , 'gl_FrontMaterial'
  , 'gl_BackMaterial'
  , 'gl_LightSourceParameters'
  , 'gl_LightSource'
  , 'gl_LightModelParameters'
  , 'gl_LightModel'
  , 'gl_LightModelProducts'
  , 'gl_FrontLightModelProduct'
  , 'gl_BackLightModelProduct'
  , 'gl_LightProducts'
  , 'gl_FrontLightProduct'
  , 'gl_BackLightProduct'
  , 'gl_FogParameters'
  , 'gl_Fog'
  , 'gl_TextureEnvColor'
  , 'gl_EyePlaneS'
  , 'gl_EyePlaneT'
  , 'gl_EyePlaneR'
  , 'gl_EyePlaneQ'
  , 'gl_ObjectPlaneS'
  , 'gl_ObjectPlaneT'
  , 'gl_ObjectPlaneR'
  , 'gl_ObjectPlaneQ'
  , 'gl_FrontColor'
  , 'gl_BackColor'
  , 'gl_FrontSecondaryColor'
  , 'gl_BackSecondaryColor'
  , 'gl_TexCoord'
  , 'gl_FogFragCoord'
  , 'gl_Color'
  , 'gl_SecondaryColor'
  , 'gl_TexCoord'
  , 'gl_FogFragCoord'
  , 'gl_PointCoord'
  , 'radians'
  , 'degrees'
  , 'sin'
  , 'cos'
  , 'tan'
  , 'asin'
  , 'acos'
  , 'atan'
  , 'pow'
  , 'exp'
  , 'log'
  , 'exp2'
  , 'log2'
  , 'sqrt'
  , 'inversesqrt'
  , 'abs'
  , 'sign'
  , 'floor'
  , 'ceil'
  , 'fract'
  , 'mod'
  , 'min'
  , 'max'
  , 'clamp'
  , 'mix'
  , 'step'
  , 'smoothstep'
  , 'length'
  , 'distance'
  , 'dot'
  , 'cross'
  , 'normalize'
  , 'faceforward'
  , 'reflect'
  , 'refract'
  , 'matrixCompMult'
  , 'lessThan'
  , 'lessThanEqual'
  , 'greaterThan'
  , 'greaterThanEqual'
  , 'equal'
  , 'notEqual'
  , 'any'
  , 'all'
  , 'not'
  , 'texture2D'
  , 'texture2DProj'
  , 'texture2DLod'
  , 'texture2DProjLod'
  , 'textureCube'
  , 'textureCubeLod'
  , 'dFdx'
  , 'dFdy'
]
<commit_msg>Fixes #5 and sort builtins to avoid duplicates<commit_after>module.exports = [
  // Keep this list sorted
  'abs'
  , 'acos'
  , 'all'
  , 'any'
  , 'asin'
  , 'atan'
  , 'ceil'
  , 'clamp'
  , 'cos'
  , 'cross'
  , 'dFdx'
  , 'dFdy'
  , 'degrees'
  , 'distance'
  , 'dot'
  , 'equal'
  , 'exp'
  , 'exp2'
  , 'faceforward'
  , 'floor'
  , 'fract'
  , 'gl_BackColor'
  , 'gl_BackLightModelProduct'
  , 'gl_BackLightProduct'
  , 'gl_BackMaterial'
  , 'gl_BackSecondaryColor'
  , 'gl_ClipPlane'
  , 'gl_ClipVertex'
  , 'gl_Color'
  , 'gl_DepthRange'
  , 'gl_DepthRangeParameters'
  , 'gl_EyePlaneQ'
  , 'gl_EyePlaneR'
  , 'gl_EyePlaneS'
  , 'gl_EyePlaneT'
  , 'gl_Fog'
  , 'gl_FogCoord'
  , 'gl_FogFragCoord'
  , 'gl_FogParameters'
  , 'gl_FragColor'
  , 'gl_FragCoord'
  , 'gl_FragData'
  , 'gl_FragDepth'
  , 'gl_FrontColor'
  , 'gl_FrontFacing'
  , 'gl_FrontLightModelProduct'
  , 'gl_FrontLightProduct'
  , 'gl_FrontMaterial'
  , 'gl_FrontSecondaryColor'
  , 'gl_LightModel'
  , 'gl_LightModelParameters'
  , 'gl_LightModelProducts'
  , 'gl_LightProducts'
  , 'gl_LightSource'
  , 'gl_LightSourceParameters'
  , 'gl_MaterialParameters'
  , 'gl_MaxClipPlanes'
  , 'gl_MaxCombinedTextureImageUnits'
  , 'gl_MaxDrawBuffers'
  , 'gl_MaxFragmentUniformComponents'
  , 'gl_MaxLights'
  , 'gl_MaxTextureCoords'
  , 'gl_MaxTextureImageUnits'
  , 'gl_MaxTextureUnits'
  , 'gl_MaxVaryingFloats'
  , 'gl_MaxVertexAttribs'
  , 'gl_MaxVertexTextureImageUnits'
  , 'gl_MaxVertexUniformComponents'
  , 'gl_ModelViewMatrix'
  , 'gl_ModelViewMatrixInverse'
  , 'gl_ModelViewMatrixInverseTranspose'
  , 'gl_ModelViewMatrixTranspose'
  , 'gl_ModelViewProjectionMatrix'
  , 'gl_ModelViewProjectionMatrixInverse'
  , 'gl_ModelViewProjectionMatrixInverseTranspose'
  , 'gl_ModelViewProjectionMatrixTranspose'
  , 'gl_MultiTexCoord0'
  , 'gl_MultiTexCoord1'
  , 'gl_MultiTexCoord2'
  , 'gl_MultiTexCoord3'
  , 'gl_MultiTexCoord4'
  , 'gl_MultiTexCoord5'
  , 'gl_MultiTexCoord6'
  , 'gl_MultiTexCoord7'
  , 'gl_Normal'
  , 'gl_NormalMatrix'
  , 'gl_NormalScale'
  , 'gl_ObjectPlaneQ'
  , 'gl_ObjectPlaneR'
  , 'gl_ObjectPlaneS'
  , 'gl_ObjectPlaneT'
  , 'gl_Point'
  , 'gl_PointCoord'
  , 'gl_PointParameters'
  , 'gl_PointSize'
  , 'gl_Position'
  , 'gl_ProjectionMatrix'
  , 'gl_ProjectionMatrixInverse'
  , 'gl_ProjectionMatrixInverseTranspose'
  , 'gl_ProjectionMatrixTranspose'
  , 'gl_SecondaryColor'
  , 'gl_TexCoord'
  , 'gl_TextureEnvColor'
  , 'gl_TextureMatrix'
  , 'gl_TextureMatrixInverse'
  , 'gl_TextureMatrixInverseTranspose'
  , 'gl_TextureMatrixTranspose'
  , 'gl_Vertex'
  , 'greaterThan'
  , 'greaterThanEqual'
  , 'inversesqrt'
  , 'length'
  , 'lessThan'
  , 'lessThanEqual'
  , 'log'
  , 'log2'
  , 'matrixCompMult'
  , 'max'
  , 'min'
  , 'mix'
  , 'mod'
  , 'normalize'
  , 'not'
  , 'notEqual'
  , 'pow'
  , 'radians'
  , 'reflect'
  , 'refract'
  , 'sign'
  , 'sin'
  , 'smoothstep'
  , 'sqrt'
  , 'step'
  , 'tan'
  , 'texture2D'
  , 'texture2DLod'
  , 'texture2DProj'
  , 'texture2DProjLod'
  , 'textureCube'
  , 'textureCubeLod'
]
<|endoftext|>


<commit_before>/*!
 * a2 suite
 * Copyright(c) 2012 Matti Savolainen <matti@applifier.com>
 * MIT Licensed
 */

var errors = require('../errors');

function TokenService(server) {
  this.server = server;
  this.sserializer = server.serializer;
  this.store = server.store.namespace('token');
}

/**
 * expose server
 * @type {TokenService}
 */
module.exports = TokenService;

TokenService.prototype.exchangeGrantForToken = function(grant, responseType, callback) {
  var self = this;
  if(grant.used == true && responseType == "code") {
    return callback(new Error('Authorization grant already used for token.'), null);
  }

  self.createAccessToken(grant, function(err, grant) {
    return callback(err, grant);
  });
};

/**
 * generateAccessToken
 *
 * Encrypts client_id, user_id, ttl and scopes into the token.
 *
 * @param grant {Object}  the grant object with client_id, user_id and scopes.
 * @param ttl {Integer} Token time to live in seconds.
 * @api public
 * @return {Object} access_token object.
 */
TokenService.prototype.generateAccessToken = function(grant, ttl) {
  var self = this;
  ttl = ttl || 3600;
  var data = [grant.user_id, grant.client_id, Date.now(), ttl, grant.scope, 'access_token'];
  var token = self.sserializer.stringify(data);
  return {
      created_at: Math.floor(Date.now()/1000)
    , access_token: token
    , expires_in: ttl
    , token_type: "Bearer"
  };
};

/**
 * generateRefreshToken
 *
 * Generates a verifiable refresh token with user_id, client_id, scopes and TTL
 *
 * @param grant {Object}  the grant object with client_id, user_id and scopes.
 * @param ttl {Integer} Time to live for the refresh token, 1 month by default.
 * @api public
 * @return {String} refresh token string
 */
TokenService.prototype.generateRefreshToken = function(grant, ttl) {
  var self = this;
  ttl = ttl || 3600*24*31;
  var data = [grant.user_id, grant.client_id, Date.now(), ttl, grant.scope, 'refresh_token'];
  var token = self.sserializer.stringify(data);
  return token;
};

/**
 * createAccessToken
 *
 *  creates an access token based on a grant
 *
 * @param grant {Object} the grant object with client_id, user_id and scopes.
 * @api public
 * @param callback {Function} the callback function, invoked with (err, tokenData)
 */
TokenService.prototype.createAccessToken = function(grant, callback) {
  var token, self = this;
  grant.access_token = self.generateAccessToken(grant);
  grant.refresh_token = self.generateRefreshToken(grant);
  var refreshTokenKey = 'refresh_token:'+grant.refresh_token;
  grant.used = 1;
  self.server.authorizationService.saveGrant(grant, function(err, grant) {
    // Save ref to the grant with the refresh_token and make it expire in 1 month.
    self.store.set(refreshTokenKey, grant.key, function(err, data) {
      return callback(err, grant);
    });
  });
};

TokenService.prototype.load = function(token, callback) {
  var obj, info =  this.sserializer.parse(token);
  if(!info) {
    return callback(null, null);
  }

  obj = {
      user_id: info[0]
    , client_id: info[1]
    , created_at: info[2]
    , ttl: info[3]
    , scope: info[4]
    , type: info[5]
  };
  if(callback) {
    return callback(null, obj);
  }
  return obj;
};

TokenService.prototype.createOrFind = function(userId, clientId, extra, callback) {
  var self = this;
  if(callback === undefined) {
    callback = extra;
    extra = {};
  }
  var data = [userId, clientId, Date.now()+9999999, extra];
  var token = self.sserializer.stringify(data);
  return callback(null, token);
};

TokenService.prototype.validateToken = function(token, type, scope, callback) {
  var self = this;
  if(callback == undefined) {
    callback =  scope;
    scope = undefined;
  }

  function validate(tokenData) {
    // cant use refresh_token as access_token
    if(tokenData.type && tokenData.type != type) {
      return callback(null, false);
    }
    var now = Math.floor(Date.now()/1000);
    var expiresAt = Number(tokenData.created_at) + Number(tokenData.ttl);
    // is it expired.
   // TODO: enable later.
   /* if(now > expiresAt) {
      return callback(null, false);
    }*/
    return callback(null, true);
  }

  if(typeof token == 'string') {
    self.load(token, function(err, tokenData) {
      if(err || !tokenData) {
        return callback(err, tokenData);
      }
      return validate(tokenData);
    });
  } else {
    return validate(token);
  }
};<commit_msg>Update token.js<commit_after>/*!
 * a2 suite
 * Copyright(c) 2012 Matti Savolainen <matti@applifier.com>
 * MIT Licensed
 */

var errors = require('../errors');

function TokenService(server) {
  this.server = server;
  this.sserializer = server.serializer;
  this.store = server.store.namespace('token');
}

/**
 * expose server
 * @type {TokenService}
 */
module.exports = TokenService;

TokenService.prototype.exchangeGrantForToken = function(grant, responseType, callback) {
  var self = this;
  if(grant.used == true && responseType == "code") {
    return callback(new Error('Authorization grant already used for token.'), null);
  }

  self.createAccessToken(grant, function(err, grant) {
    return callback(err, grant);
  });
};

/**
 * generateAccessToken
 *
 * Encrypts client_id, user_id, ttl and scopes into the token.
 *
 * @param grant {Object}  the grant object with client_id, user_id and scopes.
 * @param ttl {Integer} Token time to live in seconds.
 * @api public
 * @return {Object} access_token object.
 */
TokenService.prototype.generateAccessToken = function(grant, ttl) {
  var self = this;
  ttl = ttl || 3600;
  var data = [grant.user_id, grant.client_id, Date.now(), ttl, grant.scope, 'access_token'];
  var token = self.sserializer.stringify(data);
  return {
      created_at: Math.floor(Date.now()/1000)
    , access_token: token
    , expires_in: ttl
    , token_type: "Bearer"
  };
};

/**
 * generateRefreshToken
 *
 * Generates a verifiable refresh token with user_id, client_id, scopes and TTL
 *
 * @param grant {Object}  the grant object with client_id, user_id and scopes.
 * @param ttl {Integer} Time to live for the refresh token, 1 month by default.
 * @api public
 * @return {String} refresh token string
 */
TokenService.prototype.generateRefreshToken = function(grant, ttl) {
  var self = this;
  ttl = ttl || 3600*24*31;
  var data = [grant.user_id, grant.client_id, Date.now(), ttl, grant.scope, 'refresh_token'];
  var token = self.sserializer.stringify(data);
  return token;
};

/**
 * createAccessToken
 *
 *  creates an access token based on a grant
 *
 * @param grant {Object} the grant object with client_id, user_id and scopes.
 * @api public
 * @param callback {Function} the callback function, invoked with (err, tokenData)
 */
TokenService.prototype.createAccessToken = function(grant, callback) {
  var token, self = this;
  grant.access_token = self.generateAccessToken(grant);
  grant.refresh_token = self.generateRefreshToken(grant);
  var refreshTokenKey = 'refresh_token:'+grant.refresh_token;
  grant.used = 1;
  self.server.authorizationService.saveGrant(grant, function(err, grant) {
    // Save ref to the grant with the refresh_token and make it expire in 1 month.
    self.store.set(refreshTokenKey, grant.key, function(err, data) {
      return callback(err, grant);
    });
  });
};

TokenService.prototype.load = function(token, callback) {
  var obj, info =  this.sserializer.parse(token);
  if(!info) {
    return callback ? callback(null, null) : false;
  }

  obj = {
      user_id: info[0]
    , client_id: info[1]
    , created_at: info[2]
    , ttl: info[3]
    , scope: info[4]
    , type: info[5]
  };
  if(callback) {
    return callback(null, obj);
  }
  return obj;
};

TokenService.prototype.createOrFind = function(userId, clientId, extra, callback) {
  var self = this;
  if(callback === undefined) {
    callback = extra;
    extra = {};
  }
  var data = [userId, clientId, Date.now()+9999999, extra];
  var token = self.sserializer.stringify(data);
  return callback(null, token);
};

TokenService.prototype.validateToken = function(token, type, scope, callback) {
  var self = this;
  if(callback == undefined) {
    callback =  scope;
    scope = undefined;
  }

  function validate(tokenData) {
    // cant use refresh_token as access_token
    if(tokenData.type && tokenData.type != type) {
      return callback(null, false);
    }
    var now = Math.floor(Date.now()/1000);
    var expiresAt = Number(tokenData.created_at) + Number(tokenData.ttl);
    // is it expired.
   // TODO: enable later.
   /* if(now > expiresAt) {
      return callback(null, false);
    }*/
    return callback(null, true);
  }

  if(typeof token == 'string') {
    self.load(token, function(err, tokenData) {
      if(err || !tokenData) {
        return callback(err, tokenData);
      }
      return validate(tokenData);
    });
  } else {
    return validate(token);
  }
};
<|endoftext|>