<commit_before>'use strict';

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

/**
 * StateManager
 *
 * Manages states and observers to watch for changes in state.
 */
function StateManager (state, famousNode, Transitionable) {
    this._state = {};
    this._famousNode = famousNode;
    this._famousNodeId = this._famousNode.addComponent(this);
    this._Transitionable = Transitionable;

    // observers
    this._observers = {};                       // fires on change to target state
    this._globalObservers = [];                 // fires on every change to state
    this._globalObserversThatFireOnce = [];     // fires once on triggerGlobalChange()
    this._globalObserversThatFireEvery = [];    // fires on every triggerGlobalChange()

    this._transitionables = {};

    this._currentID = 0;
    this._thenSetPool = {};

    this._operator = operator;

    this._initObservers(state);
    this._setInitialState(state);

    // tell engine to call .onUpdate() function
    this._famousNode.requestUpdate(this._famousNodeId);
}

/**
 * Initializes observers by creating an array for each state key.
 */
StateManager.prototype._initObservers = function _initObservers (state) {
    for (var key in state) this._observers[key] = [];
};

StateManager.prototype._setInitialState = function _setInitialState (state) {
    for (var key in state) this.setState(key, state[key]);
};

/**
 * State getter function.
 */
StateManager.prototype.getState = function getState (key) {
    var target = key[key.length - 1];
    return isArray(key) ? traverse(this._state, key)[target] : this._state[key];
};

/**
 * Alias for .getState()
 */
StateManager.prototype.get = StateManager.prototype.getState;

/**
 * Returns entire state object.
 */
StateManager.prototype.getStateObject = function getStateObject () {
    return this._state;
};

/**
 * State setter function.
 * Accepts calls in the format `setState('key', 'value') or
 * `setState('key', 5, {duration: 1000, curve: 'linear'});
 * (all numeric values can be treated as Transitionables)
 */
StateManager.prototype.setState = function setState (key, value, transition) {
    this._currentID++;

    var keyType = getType(key);
    var valueType = getType(value);
    var previousValue = this.get(key);
    var previousValueType = getType(previousValue);

    var typesDoMatch = valueType === previousValueType;

    if (keyType === 'Object') {
        var object = key;
        transition = value;
        for (var state in object) {
            this.setState(state, object[state], transition);
        }
        return this;
    }

    if (keyType === 'Array') key = JSON.stringify(key);

    switch(valueType) {
        case 'Array':
        case 'Number':
            if (this._state[key]) this._transitionables[key].halt();
            else this._transitionables[key] = new this._Transitionable(value);

            if (!transition) {
                this._transitionables[key].set(value, null, this._checkThenSetPool.bind(this, this._currentID));
                setOnObject(key, value, this._state);
                this._fireObservers(key, value);
            }
            else {
                if (typesDoMatch) {
                    this._setTransitionable(this._currentID, key, previousValue, value, transition);
                    setOnObject(key, previousValue, this._state);
                }
                else {
                    throw new Error('Cannot transition from a ' + previousValueType + ' to a ' + valueType);
                }
            }
            break;
        case 'Function':
            throw new Error('Cannot set state of type: ' + valueType);
        default:
            if (transition) {
                throw new Error('Cannot transition state of type: ' + valueType);
            }
            else {
                setOnObject(key, value, this._state);
                this._fireObservers(key, value);
            }
    }

    this._setLatestStateChange(key, value);

    return this;
};

/**
 * Alias for .setState()
 */
StateManager.prototype.set = StateManager.prototype.setState;

/**
 * Sugar for setting state in a callback.
 */
StateManager.prototype.thenSetState = function thenSetState (key, value, transition) {
    var id = this._currentID;
    var pool = this._thenSetPool;

    if (pool[id] && pool[id].length) id++;
    pool[id] = [key, value, transition];

    return this;
};

/**
 * Alias for .thenSetState()
 */
StateManager.prototype.thenSet = StateManager.prototype.thenSetState;

/**
 * Check the thenQueue and call .setState on latest .then call.
 */
StateManager.prototype._checkThenSetPool = function _checkThenSetPool (id) {
    var thenSetArray = this._thenSetPool[id];

    if (thenSetArray && thenSetArray.length) {
        this.setState(thenSetArray[0], thenSetArray[1], thenSetArray[2]);
    }

    delete this._thenSetPool[id];
};

StateManager.prototype._setTransitionable = function _setTransitionable (id, key, previous, current, transition) {
    transition = transition || {};
    transition.curve = transition.curve || 'linear';
    transition.duration = transition.duration || 0;

    this._transitionables[key]
        .from(previous)
        .to(current, transition.curve, transition.duration, this._checkThenSetPool.bind(this, id));
};

/**
 * Get the key and value associated with the latest change to state.
 * @return {Object} Latest state change
 */
StateManager.prototype.getLatestStateChange = function getLatestStateChange () {
    return this._latestStateChange;
};

/**
 * Resets `_latestStateChange to updated key and value.
 * Used internally by `setState()`.
 * @protected
 */
StateManager.prototype._setLatestStateChange = function _setLatestStateChange (key, value) {
    this._latestStateChange = [key, value];
};

/**
 * Adds an observer to all observables.
 */
StateManager.prototype.subscribe = function subscribe (observer) {
    this._globalObservers.push(observer);
};

/**
 * Removes an observer from all observables.
 */
StateManager.prototype.unsubscribe = function unsubscribe (observer) {
    // Remove from global
    var globalKeys = {
        '_globalObservers': true,
        '_globalObserversThatFireEvery': true
    };

    var index;
    var listenerStack;
    var key;
    for (key in globalKeys) {
        listenerStack = this[key];
        index = listenerStack.indexOf(observer);
        if (index !== -1) {
            listenerStack.splice(index);
        }
    }

    // Remove from all key based observers
    for (key in this._observers) {
        this.unsubscribeFrom(key, observer);
    }
};

/**
 * Adds an observer to a key's list of observables.
 */
StateManager.prototype.subscribeTo = function subscribeTo (key, observer) {
    if (!this._observers.hasOwnProperty(key)) this._observers[key] = [];
    this._observers[key].push(observer);
};

/**
 * Removes an observer from a key's list of observables.
 */
StateManager.prototype.unsubscribeFrom = function unsubscribeFrom (key, observer) {
    if (this._observers.hasOwnProperty(key)) {
        var index = this._observers[key].indexOf(observer);
        if (index !== -1) this._observers[key].splice(index);
    }
};

StateManager.prototype._unsubscribeAllFromKey = function _unsubscribeAllFromKey (key) {
    if (this._observers.hasOwnProperty(key)) this._observers[key] = [];
};

/**
 * Adds an observer that will be fired a single time as soon
 * as triggerGlobalChange is invoked.
 */
StateManager.prototype.subscribeOnce = function subscribeOnce (observer) {
    this._globalObserversThatFireOnce.push(observer);
};

/**
 * Adds an observer that will be fired every time a global change
 * is triggered.
 */
StateManager.prototype.subscribeToGlobalChange = function subscribeToGlobalChange (observer) {
    this._globalObserversThatFireEvery.push(observer);
};

/**
 * Calls set state with current state's value on each state.
 * @param {RegEx} whiteList RegEx defining which keys state should be triggered on.
 *                          If both a whiteList & blackList are passed in, the whiteList's result
 *                          takes precedence over the blackList.
 * @param {RegEx} blackList RegEx defining which keys state should NOT be triggered on.
 */
StateManager.prototype.triggerGlobalChange = function triggerGlobalChange (whiteList, blackList) {
    for (var key in this._state) {
        if (whiteList) {
            if (whiteList.test(key)) this.setState(key, this.getState(key));
        }
        else if (blackList) {
            if (!blackList.test(key)) this.setState(key, this.getState(key));
        }
        else {
            this.setState(key, this.getState(key));
        }
    }

    var i;
    for (i = 0; i < this._globalObservers.length; i++) {
        this._globalObservers[i]();
    }
    for (i = 0; i < this._globalObserversThatFireEvery.length; i++) {
        this._globalObserversThatFireEvery[i]();
    }

    var observer;
    while (this._globalObserversThatFireOnce.length) {
        observer = this._globalObserversThatFireOnce.pop();
        observer();
    }
};

StateManager.prototype._fireObservers = function _fireObservers (key, value) {
    // if stateName is an array (nested state), use first string
    var parsedKey = parse(key);
    if (isArray(parsedKey)) key = parsedKey[0];

    if (this._observers[key]) {
        for (var i = 0; i < this._observers[key].length; i++) {
            this._observers[key][i](key, value);
        }
    }

    for (var j = 0; j < this._globalObservers.length; j++) {
        this._globalObservers[j](key, value);
    }
};

StateManager.prototype.onUpdate = function onUpdate () {
    // keep internal state object in sync with transitionable values
    for (var key in this._transitionables) {
        if (this._transitionables.hasOwnProperty(key)) {
            var t = this._transitionables[key];
            if (t && t.isActive()) {
                setOnObject(key, t.get(), this._state);
                this._fireObservers(key, t.get());
            }
        }
    }

    // Update on each tick
    this._famousNode.requestUpdateOnNextTick(this._famousNodeId);
};

/**
 * Sets properties on an object.
 * Handles traversing nested object setting.
 */
function setOnObject (key, val, object) {
    key = parse(key);

    if (isString(key)) {
        object[key] = val;
    }
    else if (isArray(key)) {
        var targetKey = key[key.length - 1];
        var hostObject = traverse(object, key);
        hostObject[targetKey] = val;
    }
}

/**
 * Returns the host object of nested state.
 */
function traverse (object, path) {
    if (object.hasOwnProperty(path[0])) {
        if (path.length === 1) return object;
        else return traverse(object[path.slice(0, 1)], path.slice(1, path.length));
    }
    else {
        console.error('Incorrect path: ' + path[0]);
    }
}

/**
 * Parses nested state keys.
 * Returns key if not nested.
 */
function parse (key) {
    if (isString(key) && key.indexOf('[') === 0) {
        return JSON.parse(key);
    }
    else {
        return key;
    }
}

/**
 * Returns a Boolean indication whether the state is an array or not.
 */
function isArray (state) {
    return getType(state) === 'Array';
}

/**
 * Returns a Boolean indication whether the state is a string or not.
 */
function isString (state) {
    return getType(state) === 'String';
}

/**
 * Returns a String corresponding to the state's type.
 */
function getType (state) {
    return Object.prototype.toString.call(state).slice(8, -1);
}

module.exports = StateManager;
<commit_msg>fix: Don't set initial state of that state was undefined<commit_after>'use strict';

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

/**
 * StateManager
 *
 * Manages states and observers to watch for changes in state.
 */
function StateManager (state, famousNode, Transitionable) {
    this._state = {};
    this._famousNode = famousNode;
    this._famousNodeId = this._famousNode.addComponent(this);
    this._Transitionable = Transitionable;

    // observers
    this._observers = {};                       // fires on change to target state
    this._globalObservers = [];                 // fires on every change to state
    this._globalObserversThatFireOnce = [];     // fires once on triggerGlobalChange()
    this._globalObserversThatFireEvery = [];    // fires on every triggerGlobalChange()

    this._transitionables = {};

    this._currentID = 0;
    this._thenSetPool = {};

    this._operator = operator;

    this._initObservers(state);
    this._setInitialState(state);

    // tell engine to call .onUpdate() function
    this._famousNode.requestUpdate(this._famousNodeId);
}

/**
 * Initializes observers by creating an array for each state key.
 */
StateManager.prototype._initObservers = function _initObservers (state) {
    for (var key in state) this._observers[key] = [];
};

StateManager.prototype._setInitialState = function _setInitialState (state) {
    for (var key in state) {
        // No point triggering a state set that would be undefined anyway,
        // plus, we get the advantage of being able to declare states that
        // we might not want to set right away
        if (state[key] !== undefined) {
            this.setState(key, state[key]);
        }
    }
};

/**
 * State getter function.
 */
StateManager.prototype.getState = function getState (key) {
    var target = key[key.length - 1];
    return isArray(key) ? traverse(this._state, key)[target] : this._state[key];
};

/**
 * Alias for .getState()
 */
StateManager.prototype.get = StateManager.prototype.getState;

/**
 * Returns entire state object.
 */
StateManager.prototype.getStateObject = function getStateObject () {
    return this._state;
};

/**
 * State setter function.
 * Accepts calls in the format `setState('key', 'value') or
 * `setState('key', 5, {duration: 1000, curve: 'linear'});
 * (all numeric values can be treated as Transitionables)
 */
StateManager.prototype.setState = function setState (key, value, transition) {
    this._currentID++;

    var keyType = getType(key);
    var valueType = getType(value);
    var previousValue = this.get(key);
    var previousValueType = getType(previousValue);

    var typesDoMatch = valueType === previousValueType;

    if (keyType === 'Object') {
        var object = key;
        transition = value;
        for (var state in object) {
            this.setState(state, object[state], transition);
        }
        return this;
    }

    if (keyType === 'Array') key = JSON.stringify(key);

    switch(valueType) {
        case 'Array':
        case 'Number':
            if (this._state[key]) this._transitionables[key].halt();
            else this._transitionables[key] = new this._Transitionable(value);

            if (!transition) {
                this._transitionables[key].set(value, null, this._checkThenSetPool.bind(this, this._currentID));
                setOnObject(key, value, this._state);
                this._fireObservers(key, value);
            }
            else {
                if (typesDoMatch) {
                    this._setTransitionable(this._currentID, key, previousValue, value, transition);
                    setOnObject(key, previousValue, this._state);
                }
                else {
                    throw new Error('Cannot transition from a ' + previousValueType + ' to a ' + valueType);
                }
            }
            break;
        case 'Function':
            throw new Error('Cannot set state of type: ' + valueType);
        default:
            if (transition) {
                throw new Error('Cannot transition state of type: ' + valueType);
            }
            else {
                setOnObject(key, value, this._state);
                this._fireObservers(key, value);
            }
    }

    this._setLatestStateChange(key, value);

    return this;
};

/**
 * Alias for .setState()
 */
StateManager.prototype.set = StateManager.prototype.setState;

/**
 * Sugar for setting state in a callback.
 */
StateManager.prototype.thenSetState = function thenSetState (key, value, transition) {
    var id = this._currentID;
    var pool = this._thenSetPool;

    if (pool[id] && pool[id].length) id++;
    pool[id] = [key, value, transition];

    return this;
};

/**
 * Alias for .thenSetState()
 */
StateManager.prototype.thenSet = StateManager.prototype.thenSetState;

/**
 * Check the thenQueue and call .setState on latest .then call.
 */
StateManager.prototype._checkThenSetPool = function _checkThenSetPool (id) {
    var thenSetArray = this._thenSetPool[id];

    if (thenSetArray && thenSetArray.length) {
        this.setState(thenSetArray[0], thenSetArray[1], thenSetArray[2]);
    }

    delete this._thenSetPool[id];
};

StateManager.prototype._setTransitionable = function _setTransitionable (id, key, previous, current, transition) {
    transition = transition || {};
    transition.curve = transition.curve || 'linear';
    transition.duration = transition.duration || 0;

    this._transitionables[key]
        .from(previous)
        .to(current, transition.curve, transition.duration, this._checkThenSetPool.bind(this, id));
};

/**
 * Get the key and value associated with the latest change to state.
 * @return {Object} Latest state change
 */
StateManager.prototype.getLatestStateChange = function getLatestStateChange () {
    return this._latestStateChange;
};

/**
 * Resets `_latestStateChange to updated key and value.
 * Used internally by `setState()`.
 * @protected
 */
StateManager.prototype._setLatestStateChange = function _setLatestStateChange (key, value) {
    this._latestStateChange = [key, value];
};

/**
 * Adds an observer to all observables.
 */
StateManager.prototype.subscribe = function subscribe (observer) {
    this._globalObservers.push(observer);
};

/**
 * Removes an observer from all observables.
 */
StateManager.prototype.unsubscribe = function unsubscribe (observer) {
    // Remove from global
    var globalKeys = {
        '_globalObservers': true,
        '_globalObserversThatFireEvery': true
    };

    var index;
    var listenerStack;
    var key;
    for (key in globalKeys) {
        listenerStack = this[key];
        index = listenerStack.indexOf(observer);
        if (index !== -1) {
            listenerStack.splice(index);
        }
    }

    // Remove from all key based observers
    for (key in this._observers) {
        this.unsubscribeFrom(key, observer);
    }
};

/**
 * Adds an observer to a key's list of observables.
 */
StateManager.prototype.subscribeTo = function subscribeTo (key, observer) {
    if (!this._observers.hasOwnProperty(key)) this._observers[key] = [];
    this._observers[key].push(observer);
};

/**
 * Removes an observer from a key's list of observables.
 */
StateManager.prototype.unsubscribeFrom = function unsubscribeFrom (key, observer) {
    if (this._observers.hasOwnProperty(key)) {
        var index = this._observers[key].indexOf(observer);
        if (index !== -1) this._observers[key].splice(index);
    }
};

StateManager.prototype._unsubscribeAllFromKey = function _unsubscribeAllFromKey (key) {
    if (this._observers.hasOwnProperty(key)) this._observers[key] = [];
};

/**
 * Adds an observer that will be fired a single time as soon
 * as triggerGlobalChange is invoked.
 */
StateManager.prototype.subscribeOnce = function subscribeOnce (observer) {
    this._globalObserversThatFireOnce.push(observer);
};

/**
 * Adds an observer that will be fired every time a global change
 * is triggered.
 */
StateManager.prototype.subscribeToGlobalChange = function subscribeToGlobalChange (observer) {
    this._globalObserversThatFireEvery.push(observer);
};

/**
 * Calls set state with current state's value on each state.
 * @param {RegEx} whiteList RegEx defining which keys state should be triggered on.
 *                          If both a whiteList & blackList are passed in, the whiteList's result
 *                          takes precedence over the blackList.
 * @param {RegEx} blackList RegEx defining which keys state should NOT be triggered on.
 */
StateManager.prototype.triggerGlobalChange = function triggerGlobalChange (whiteList, blackList) {
    for (var key in this._state) {
        if (whiteList) {
            if (whiteList.test(key)) this.setState(key, this.getState(key));
        }
        else if (blackList) {
            if (!blackList.test(key)) this.setState(key, this.getState(key));
        }
        else {
            this.setState(key, this.getState(key));
        }
    }

    var i;
    for (i = 0; i < this._globalObservers.length; i++) {
        this._globalObservers[i]();
    }
    for (i = 0; i < this._globalObserversThatFireEvery.length; i++) {
        this._globalObserversThatFireEvery[i]();
    }

    var observer;
    while (this._globalObserversThatFireOnce.length) {
        observer = this._globalObserversThatFireOnce.pop();
        observer();
    }
};

StateManager.prototype._fireObservers = function _fireObservers (key, value) {
    // if stateName is an array (nested state), use first string
    var parsedKey = parse(key);
    if (isArray(parsedKey)) key = parsedKey[0];

    if (this._observers[key]) {
        for (var i = 0; i < this._observers[key].length; i++) {
            this._observers[key][i](key, value);
        }
    }

    for (var j = 0; j < this._globalObservers.length; j++) {
        this._globalObservers[j](key, value);
    }
};

StateManager.prototype.onUpdate = function onUpdate () {
    // keep internal state object in sync with transitionable values
    for (var key in this._transitionables) {
        if (this._transitionables.hasOwnProperty(key)) {
            var t = this._transitionables[key];
            if (t && t.isActive()) {
                setOnObject(key, t.get(), this._state);
                this._fireObservers(key, t.get());
            }
        }
    }

    // Update on each tick
    this._famousNode.requestUpdateOnNextTick(this._famousNodeId);
};

/**
 * Sets properties on an object.
 * Handles traversing nested object setting.
 */
function setOnObject (key, val, object) {
    key = parse(key);

    if (isString(key)) {
        object[key] = val;
    }
    else if (isArray(key)) {
        var targetKey = key[key.length - 1];
        var hostObject = traverse(object, key);
        hostObject[targetKey] = val;
    }
}

/**
 * Returns the host object of nested state.
 */
function traverse (object, path) {
    if (object.hasOwnProperty(path[0])) {
        if (path.length === 1) return object;
        else return traverse(object[path.slice(0, 1)], path.slice(1, path.length));
    }
    else {
        console.error('Incorrect path: ' + path[0]);
    }
}

/**
 * Parses nested state keys.
 * Returns key if not nested.
 */
function parse (key) {
    if (isString(key) && key.indexOf('[') === 0) {
        return JSON.parse(key);
    }
    else {
        return key;
    }
}

/**
 * Returns a Boolean indication whether the state is an array or not.
 */
function isArray (state) {
    return getType(state) === 'Array';
}

/**
 * Returns a Boolean indication whether the state is a string or not.
 */
function isString (state) {
    return getType(state) === 'String';
}

/**
 * Returns a String corresponding to the state's type.
 */
function getType (state) {
    return Object.prototype.toString.call(state).slice(8, -1);
}

module.exports = StateManager;
<|endoftext|>


<commit_before>'use strict'
var Event = require('vigour-js/lib/event')
var hash = require('vigour-js/lib/util/hash')
var isEmpty = require('vigour-js/lib/util/is/empty')
var Observable = require('vigour-js/lib/observable')
var _subscribe = Observable.prototype.subscribe

exports.properties = {
  subsrefs: true,
  subscollectors: true
}

exports.define = {
  subscribe (pattern, fn, key, unique, event, nocnt) {
    // var add
    // NEEDS GAURD
    // if (!this._on[subs.key]) {
    //   add = true
    // }
    var subs = _subscribe.apply(this, arguments)
    // if (!dontadd) {
    this.sendSubscriptionUpstream(subs.pattern, void 0, subs.key, nocnt)
    // }
    return subs
  },
  unsubscribe (key, event) {
    console.log('dirt mc gurd 2', key)
    if (typeof key === 'string') {
      // this has to be handled better
      if (this.removeSubscription(key)) {
        console.log('LULZZZ')
        // let emitter = this._on && this._on[key]
        if (!event) {
          event = new Event(this, 'removeitsubs')
          event.stamp += 'subscription'
        }
        this.sendUpstream(void 0, event, { unsubscribe: { val: [ key ], path: this.syncPath } })
        // if (emitter) {
          // have to count listeners and shit
          // emitter.remove()
        // }
        // for non server hub this is wrong!
        // if emitter is empty
        // emitter.remove()
      }
    }
  },
  onSubRemove (emitter, event) {
    console.log('onSubRemove'.red)
    if (!this.subsrefs) {
      return
    }
    let path = emitter._path.join('.')
    let hashed = this.subsrefs[path]
    if (hashed) {
      delete this.subsrefs[path]
      if (isEmpty(this.subsrefs)) {
        delete this.subsrefs
      }
      this.unsubscribe(hashed, event)
    }
  },
  addSubscription (key, nocnt) {
    var newsub
    if (!this.hasOwnProperty('subscollectors')) {
      this.subscollectors = {}
    }
    if (!this.subscollectors[key]) {
      this.subscollectors[key] = 0
      newsub = true
    }
    if (!nocnt || newsub) {
      this.subscollectors[key]++
    }
    return newsub
  },
  removeSubscription (key) {
    if (this.subscollectors && this.subscollectors[key]) {
      this.subscollectors[key]--
      if (!this.subscollectors[key]) {
        delete this.subscollectors[key]
        if (isEmpty(this.subscollectors)) {
          delete this.subscollectors
        }
        return true
      }
    }
  },
  onSub (event, emitter, pattern) {
    var genpath = emitter.path.join('.')
    if (this.subsrefs && this.subsrefs[genpath]) {
      return
    }
    if (!this.hasOwnProperty('substore')) {
      this.subsrefs = {}
    }
    let key = this.sendSubscriptionUpstream(pattern, event)
    this.subsrefs[genpath] = key
  },
  sendSubscriptionUpstream (pattern, event, key, nocnt) {
    if (!event) {
      event = new Event(this, 'makeitsubs')
      event.stamp += 'subscription'
    }
    var path = this.syncPath
    var serialized = pattern.serialize()
    walker(serialized)
    if (!key) {
      key = hash(JSON.stringify(serialized))
    }
    if (this.addSubscription(key, nocnt)) {
      this.sendUpstream(void 0, event, { [key]: { val: serialized, path: path } })
    }
    return key
  }
}

function walker (val) {
  for (var i in val) {
    if (typeof val[i] === 'object') {
      walker(val[i])
    } else {
      val[i] = true
    }
  }
}
<commit_msg>syncable<commit_after>'use strict'
var Event = require('vigour-js/lib/event')
var hash = require('vigour-js/lib/util/hash')
var isEmpty = require('vigour-js/lib/util/is/empty')
var Observable = require('vigour-js/lib/observable')
var _subscribe = Observable.prototype.subscribe

exports.properties = {
  subsrefs: true,
  subscollectors: true
}

exports.define = {
  subscribe (pattern, fn, key, unique, event, nocnt) {
    // var add
    // NEEDS GAURD
    // if (!this._on[subs.key]) {
    //   add = true
    // }
    var subs = _subscribe.apply(this, arguments)
    // if (!dontadd) {
    this.sendSubscriptionUpstream(subs.pattern, void 0, subs.key, nocnt)
    // }
    return subs
  },
  unsubscribe (key, event) {
    console.log('dirt mc gurd 2', key)
    if (typeof key === 'string') {
      // this has to be handled better
      if (this.removeSubscription(key)) {
        console.log('LULZZZ')
        // let emitter = this._on && this._on[key]
        if (!event) {
          event = new Event(this, 'removeitsubs')
          event.stamp += 'subscription'
        }
        this.sendUpstream(void 0, event, { unsubscribe: { val: [ key ], path: this.syncPath } })
        // if (emitter) {
          // have to count listeners and shit
          // emitter.remove()
        // }
        // for non server hub this is wrong!
        // if emitter is empty
        // emitter.remove()
      }
    }
  },
  onSubRemove (emitter, event) {
    console.log('onSubRemove'.red)
    if (!this.subsrefs) {
      return
    }
    let path = emitter._path.join('.')
    let hashed = this.subsrefs[path]
    if (hashed) {
      delete this.subsrefs[path]
      if (isEmpty(this.subsrefs)) {
        delete this.subsrefs
      }
      this.unsubscribe(hashed, event)
    }
  },
  addSubscription (key, nocnt) {
    var newsub
    if (!this.hasOwnProperty('subscollectors')) {
      this.subscollectors = {}
    }
    if (!this.subscollectors[key]) {
      this.subscollectors[key] = 0
      newsub = true
    }
    if (!nocnt || newsub) {
      this.subscollectors[key]++
    }
    return newsub
  },
  removeSubscription (key) {
    if (this.subscollectors && this.subscollectors[key]) {
      this.subscollectors[key]--
      if (!this.subscollectors[key]) {
        delete this.subscollectors[key]
        if (isEmpty(this.subscollectors)) {
          delete this.subscollectors
        }
        return true
      }
    }
  },
  onSub (event, emitter, pattern) {
    console.log('onSub'.green)
    var genpath = emitter.path.join('.')
    if (this.subsrefs && this.subsrefs[genpath]) {
      return
    }
    if (!this.hasOwnProperty('substore')) {
      this.subsrefs = {}
    }
    let key = this.sendSubscriptionUpstream(pattern, event)
    this.subsrefs[genpath] = key
  },
  sendSubscriptionUpstream (pattern, event, key, nocnt) {
    if (!event) {
      event = new Event(this, 'makeitsubs')
      event.stamp += 'subscription'
    }
    var path = this.syncPath
    var serialized = pattern.serialize()
    walker(serialized)
    if (!key) {
      key = hash(JSON.stringify(serialized))
    }
    if (this.addSubscription(key, nocnt)) {
      this.sendUpstream(void 0, event, { [key]: { val: serialized, path: path } })
    }
    return key
  }
}

function walker (val) {
  for (var i in val) {
    if (typeof val[i] === 'object') {
      walker(val[i])
    } else {
      val[i] = true
    }
  }
}
<|endoftext|>


<commit_before>'use strict';

/*!
 * Snakeskin
 * https://github.com/SnakeskinTpl/Snakeskin
 *
 * Released under the MIT license
 * https://github.com/SnakeskinTpl/Snakeskin/blob/master/LICENSE
 */

import Snakeskin from '../core';
import { ws } from '../helpers/string';

Snakeskin.addDirective(
	'yield',

	{
		ancestorsBlacklist: Snakeskin.group('function'),
		generator: true,
		group: 'yield',
		placement: 'template'
	},

	function (command) {
		if (command) {
			this.append(`yield ${this.out(command, {unsafe: true})};`);

		} else {
			this.append(ws`
				yield ${this.getReturnResultDecl()};
				__RESULT__ = ${this.getResultDecl()};
			`);
		}
	}

);
<commit_msg>https://github.com/SnakeskinTpl/Snakeskin/issues/58<commit_after>'use strict';

/*!
 * Snakeskin
 * https://github.com/SnakeskinTpl/Snakeskin
 *
 * Released under the MIT license
 * https://github.com/SnakeskinTpl/Snakeskin/blob/master/LICENSE
 */

import Snakeskin from '../core';
import { ws } from '../helpers/string';

Snakeskin.addDirective(
	'yield',

	{
		ancestorsBlacklist: Snakeskin.group('function'),
		block: true,
		deferInit: true,
		generator: true,
		group: 'yield',
		placement: 'template'
	},

	function (command) {
		if (command.slice(-1) === '/') {
			this.startInlineDir(null, {command: command.slice(0, -1), short: true});
			return;
		}

		this.startDir(null, {command});

		if (!command) {
			this.append(ws`
				${this.declVars('__CALL_CACHE__ = __RESULT__', {sys: true})}
				__RESULT__ = ${this.getResultDecl()};
			`);
		}
	},

	function () {
		const
			p = this.structure.params;

		if (p.command) {
			this.append(`yield ${this.out(p.command, {unsafe: true})};`);

		} else if (p.short) {
			this.append(ws`
				yield ${this.getReturnResultDecl()};
				__RESULT__ = ${this.getResultDecl()};
			`);

		} else {
			const
				tmp = this.getVar('__CALL_CACHE__');

			this.append(ws`
				if (__LENGTH__(__RESULT__)) {
					yield ${this.getReturnResultDecl()};
					__RESULT__ = ${tmp};

				} else {
					__RESULT__ = ${tmp};
					yield ${this.getReturnResultDecl()};
					__RESULT__ = ${this.getResultDecl()};
				}
			`);
		}
	}

);
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import PropTypes from 'prop-types';

import { isEqual, transitionEventName } from './utils';

export default class AnimakitExpander extends Component {
  constructor(props) {
    super(props);

    this.state = {
      animation: false,
      prepare: false,
      expanded: !!props.expanded,

      size: 0,

      duration: 0,
    };
  }

  componentWillMount() {
    this.contentNode = null;
    this.canAnimate = !this.props.expanded;

    this.transitionEventName = transitionEventName();

    this.listeners = this.getListeners();

    this.animationReset = false;
    this.animationResetTO = null;
    this.resizeCheckerRAF = null;
  }

  componentDidMount() {
    this.repaint(this.props);

    this.toggleAnimationListener(true);
  }

  componentWillReceiveProps(nextProps) {
    this.repaint(nextProps);
  }

  shouldComponentUpdate(nextProps, nextState) {
    const stateChanged = !isEqual(nextState, this.state);

    const childrenChanged = !isEqual(nextProps.children, this.props.children);

    return stateChanged || childrenChanged;
  }

  componentWillUpdate() {
    this.toggleResizeChecker(false);
  }

  componentDidUpdate() {
    this.toggleResizeChecker(true);
  }

  componentWillUnmount() {
    this.toggleResizeChecker(false);
    this.toggleAnimationReset(false);
    this.toggleAnimationListener(false);
  }

  getListeners() {
    return {
      onCheckResize: this.onCheckResize.bind(this),
      onTransitionEnd: this.onTransitionEnd.bind(this),
    };
  }

  toggleResizeChecker(start) {
    if (typeof requestAnimationFrame === 'undefined') return;

    if (start) {
      this.resizeCheckerRAF = requestAnimationFrame(this.listeners.onCheckResize);
    } else if (this.resizeCheckerRAF) {
      cancelAnimationFrame(this.resizeCheckerRAF);
    }
  }

  toggleAnimationReset(add) {
    if (this.animationResetTO) clearTimeout(this.animationResetTO);

    if (add) {
      this.animationResetTO = setTimeout(() => {
        this.animationReset = true;
      }, this.state.duration);
    } else {
      this.animationReset = false;
    }
  }

  toggleAnimationListener(add) {
    const method = add ? 'addEventListener' : 'removeEventListener';
    this.rootNode[method](this.transitionEventName, this.listeners.onTransitionEnd, false);
  }

  onTransitionEnd() {
    if (!this.animationReset) return;

    this.setState({
      animation: false,
    });
  }

  onCheckResize() {
    this.toggleResizeChecker(false);

    this.repaint(this.props);

    this.toggleResizeChecker(true);
  }

  getRootStyles() {
    const { animation, expanded, prepare } = this.state;

    if (!animation && !prepare && !this.props.children) return {};

    const position = 'relative';
    const overflow = 'hidden';
    const horizontal = this.props.horizontal;

    const size = expanded || prepare ? `${this.state.size}px` : 0;
    const styles = horizontal ? { width: size } : { height: size };

    if (!animation && !prepare) {
      if (expanded && !prepare) return {};

      return { ...styles };
    }

    const easing = this.props.easing;
    const duration = this.state.duration;

    const dimension = horizontal ? 'width' : 'height';
    const transition = `${dimension} ${duration}ms ${easing}`;

    return { position, overflow, transition, ...styles };
  }

  getContentStyles() {
    const { animation, prepare } = this.state;

    if (!animation && !prepare && !this.props.children) return {};

    const { horizontal, align } = this.props;

    if (horizontal) {
      const float = align === 'right' ? 'right' : 'left';

      return { float };
    }

    if (align === 'bottom' && animation) {
      return {
        position: 'absolute',
        bottom: 0,
        left: 0,
        width: '100%',
      };
    }

    return {};
  }

  getClearance() {
    return (

    );
  }

  calcDuration(size) {
    if (!this.canAnimate) {
      this.canAnimate = true;
      return 0;
    }

    const { durationPerPx, duration } = this.props;

    if (!durationPerPx) return duration;

    const sizeDiff = Math.abs(this.state.size - size);
    return durationPerPx * sizeDiff;
  }

  calcSize() {
    const node = this.contentNode;
    const newSize = this.props.horizontal ? node.offsetWidth : node.offsetHeight;
    const size = this.state.size;

    return (Math.abs(newSize - size) <= 1) ? size : newSize;
  }

  repaint(nextProps) {
    const expanded = nextProps.expanded;

    const { expanded: curExpanded, prepare: curPrepare } = this.state;

    let size = 0;
    const lastSize = this.state.size;

    if (curExpanded) {
      size = this.calcSize();
      if (!size && !expanded) size = lastSize;
    }

    if (curExpanded === expanded && size === lastSize && !curPrepare) return;

    const prepare = !curPrepare && (curExpanded !== expanded);
    const animation = !prepare;
    const duration = animation ? this.calcDuration(expanded ? size : 0) : 0;

    const state = { expanded, size, prepare, animation, duration };

    setTimeout(() => {
      this.applyState(state);
    }, +curPrepare);
  }

  applyState(state) {
    if (!Object.keys(state).length) return;

    if (state.animation) {
      this.toggleAnimationReset(false);
    }

    this.setState(state);

    if (state.animation) {
      this.toggleAnimationReset(true);
    }
  }

  render() {
    const { animation, expanded, prepare } = this.state;
    const showChildren = expanded || prepare || animation;
    const hasChildren = !!this.props.children;

    return (

 { this.rootNode = c; }}
        style={ showChildren ? this.getRootStyles() : {} }
      >

 { this.contentNode = c; }}
          style={ showChildren ? this.getContentStyles() : {} }
        >
          { showChildren && hasChildren && this.getClearance() }
          { showChildren && this.props.children }
          { showChildren && hasChildren && this.getClearance() }

    );
  }
}

AnimakitExpander.propTypes = {
  children: PropTypes.any,
  expanded: PropTypes.bool,
  horizontal: PropTypes.bool,
  align: PropTypes.string,
  duration: PropTypes.number,
  durationPerPx: PropTypes.number,
  easing: PropTypes.string,
  smoothResize: PropTypes.bool,
};

AnimakitExpander.defaultProps = {
  expanded: true,
  horizontal: false,
  align: 'left',
  duration: 500,
  durationPerPx: 0,
  easing: 'ease-out',
};
<commit_msg>Don't use inlined arrow functions in component props/attributes. It will create new function every time causing performance degradation in React applications.<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';

import { isEqual, transitionEventName } from './utils';

export default class AnimakitExpander extends Component {
  constructor(props) {
    super(props);

    this.state = {
      animation: false,
      prepare: false,
      expanded: !!props.expanded,

      size: 0,

      duration: 0,
    };

    this.setRootNode = this.setRootNode.bind(this);
    this.setContentNode = this.setContentNode.bind(this);
  }

  componentWillMount() {
    this.contentNode = null;
    this.canAnimate = !this.props.expanded;

    this.transitionEventName = transitionEventName();

    this.listeners = this.getListeners();

    this.animationReset = false;
    this.animationResetTO = null;
    this.resizeCheckerRAF = null;
  }

  componentDidMount() {
    this.repaint(this.props);

    this.toggleAnimationListener(true);
  }

  componentWillReceiveProps(nextProps) {
    this.repaint(nextProps);
  }

  shouldComponentUpdate(nextProps, nextState) {
    const stateChanged = !isEqual(nextState, this.state);

    const childrenChanged = !isEqual(nextProps.children, this.props.children);

    return stateChanged || childrenChanged;
  }

  componentWillUpdate() {
    this.toggleResizeChecker(false);
  }

  componentDidUpdate() {
    this.toggleResizeChecker(true);
  }

  componentWillUnmount() {
    this.toggleResizeChecker(false);
    this.toggleAnimationReset(false);
    this.toggleAnimationListener(false);
  }

  getListeners() {
    return {
      onCheckResize: this.onCheckResize.bind(this),
      onTransitionEnd: this.onTransitionEnd.bind(this),
    };
  }

  toggleResizeChecker(start) {
    if (typeof requestAnimationFrame === 'undefined') return;

    if (start) {
      this.resizeCheckerRAF = requestAnimationFrame(this.listeners.onCheckResize);
    } else if (this.resizeCheckerRAF) {
      cancelAnimationFrame(this.resizeCheckerRAF);
    }
  }

  toggleAnimationReset(add) {
    if (this.animationResetTO) clearTimeout(this.animationResetTO);

    if (add) {
      this.animationResetTO = setTimeout(() => {
        this.animationReset = true;
      }, this.state.duration);
    } else {
      this.animationReset = false;
    }
  }

  toggleAnimationListener(add) {
    const method = add ? 'addEventListener' : 'removeEventListener';
    this.rootNode[method](this.transitionEventName, this.listeners.onTransitionEnd, false);
  }

  onTransitionEnd() {
    if (!this.animationReset) return;

    this.setState({
      animation: false,
    });
  }

  onCheckResize() {
    this.toggleResizeChecker(false);

    this.repaint(this.props);

    this.toggleResizeChecker(true);
  }

  getRootStyles() {
    const { animation, expanded, prepare } = this.state;

    if (!animation && !prepare && !this.props.children) return {};

    const position = 'relative';
    const overflow = 'hidden';
    const horizontal = this.props.horizontal;

    const size = expanded || prepare ? `${this.state.size}px` : 0;
    const styles = horizontal ? { width: size } : { height: size };

    if (!animation && !prepare) {
      if (expanded && !prepare) return {};

      return { ...styles };
    }

    const easing = this.props.easing;
    const duration = this.state.duration;

    const dimension = horizontal ? 'width' : 'height';
    const transition = `${dimension} ${duration}ms ${easing}`;

    return { position, overflow, transition, ...styles };
  }

  getContentStyles() {
    const { animation, prepare } = this.state;

    if (!animation && !prepare && !this.props.children) return {};

    const { horizontal, align } = this.props;

    if (horizontal) {
      const float = align === 'right' ? 'right' : 'left';

      return { float };
    }

    if (align === 'bottom' && animation) {
      return {
        position: 'absolute',
        bottom: 0,
        left: 0,
        width: '100%',
      };
    }

    return {};
  }

  getClearance() {
    return (

    );
  }

  calcDuration(size) {
    if (!this.canAnimate) {
      this.canAnimate = true;
      return 0;
    }

    const { durationPerPx, duration } = this.props;

    if (!durationPerPx) return duration;

    const sizeDiff = Math.abs(this.state.size - size);
    return durationPerPx * sizeDiff;
  }

  calcSize() {
    const node = this.contentNode;
    const newSize = this.props.horizontal ? node.offsetWidth : node.offsetHeight;
    const size = this.state.size;

    return (Math.abs(newSize - size) <= 1) ? size : newSize;
  }

  repaint(nextProps) {
    const expanded = nextProps.expanded;

    const { expanded: curExpanded, prepare: curPrepare } = this.state;

    let size = 0;
    const lastSize = this.state.size;

    if (curExpanded) {
      size = this.calcSize();
      if (!size && !expanded) size = lastSize;
    }

    if (curExpanded === expanded && size === lastSize && !curPrepare) return;

    const prepare = !curPrepare && (curExpanded !== expanded);
    const animation = !prepare;
    const duration = animation ? this.calcDuration(expanded ? size : 0) : 0;

    const state = { expanded, size, prepare, animation, duration };

    setTimeout(() => {
      this.applyState(state);
    }, +curPrepare);
  }

  applyState(state) {
    if (!Object.keys(state).length) return;

    if (state.animation) {
      this.toggleAnimationReset(false);
    }

    this.setState(state);

    if (state.animation) {
      this.toggleAnimationReset(true);
    }
  }

  setRootNode(c) {
    this.rootNode = c;
  }

  setContentNode(c) {
    this.contentNode = c;
  }

  render() {
    const { animation, expanded, prepare } = this.state;
    const showChildren = expanded || prepare || animation;
    const hasChildren = !!this.props.children;

    return (

          { showChildren && hasChildren && this.getClearance() }
          { showChildren && this.props.children }
          { showChildren && hasChildren && this.getClearance() }

    );
  }
}

AnimakitExpander.propTypes = {
  children: PropTypes.any,
  expanded: PropTypes.bool,
  horizontal: PropTypes.bool,
  align: PropTypes.string,
  duration: PropTypes.number,
  durationPerPx: PropTypes.number,
  easing: PropTypes.string,
  smoothResize: PropTypes.bool,
};

AnimakitExpander.defaultProps = {
  expanded: true,
  horizontal: false,
  align: 'left',
  duration: 500,
  durationPerPx: 0,
  easing: 'ease-out',
};
<|endoftext|>


<commit_before>import exportSchema from 'thinky-export-schema'
import mapValues from 'lodash.mapvalues'
import map from 'lodash.map'

export default (prefix = '', resources) =>
  mapValues(resources, (endpoints) => ({
    model: exportSchema(endpoints[0].model),
    endpoints: map(endpoints, (endpoint) => ({
      name: endpoint.name,
      method: endpoint.method.toUpperCase(),
      path: prefix ? `${prefix}${endpoint.path}` : endpoint.path,
      instance: endpoint.instance
    }))
  })
)
<commit_msg>fix empty folder bug<commit_after>import exportSchema from 'thinky-export-schema'
import mapValues from 'lodash.mapvalues'
import map from 'lodash.map'

export default (prefix = '', resources) =>
  mapValues(resources, (endpoints) => ({
    model: endpoints[0] && endpoints[0].model
      ? exportSchema(endpoints[0].model)
      : undefined,
    endpoints: map(endpoints, (endpoint) => ({
      name: endpoint.name,
      method: endpoint.method.toUpperCase(),
      path: prefix ? `${prefix}${endpoint.path}` : endpoint.path,
      instance: endpoint.instance
    }))
  })
)
<|endoftext|>


<commit_before><<<<<<< Updated upstream
var chalk    = require('chalk')
 ,  print    = console.log
 ,  fs       = require('fs-extra')
 ,  path     = require('path')
 ,  zip      = require('adm-zip')
 ,  clui     = require('clui')
 ,  Spinner  = clui.Spinner
 ,  clayConfigGenerator = require('./clay-config-generator.js')
 ,  rp       = require('request-promise-native');

module.exports = function(serviceName, options) {
  return new Promise((resolve, reject) => {
    // format serviceName
    options = options || {suppressMsg: false};

    serviceName = serviceName.split('/').slice(-2).join('-');

    const dir = path.resolve(process.cwd(), `${serviceName}`)

    if(fs.existsSync(dir)) {
      print(chalk.white(`Directory `)+chalk.red(`${dir}`)+chalk.white(` already exists please delete it to download this Clay service`));
      process.exit();
    }

    if(!options.suppressMsg) {
        var status = new Spinner('Getting service info..');
        status.start();
    }

    var getFunctionOptions = {
      uri: this.apis.downloadApi,
      method: 'POST',
      body: {
        apiToken: this.credentials.token,
        serviceName: serviceName
      },
      timeout: 0,
      json: true
    }

    rp(getFunctionOptions)
      .then((response) => {
        if(status && response.statusCode != 200) { status.stop(); }

        if(response.statusCode == 401) {
          print(chalk.white(`Make sure you entered the name of the service or the url to the service. E.g. `)+chalk.red(`clay download nicoslepicos/whois`)+chalk.white(` or `)+chalk.red(`clay download http://clay.run/services/nicoslepicos/whois`))
          print(chalk.white(`Also, make sure that this service exists and is owned by you or it is a public service`))
          process.exit();
        }
        else if(response.statusCode == 500) {
          print(chalk.white(`Error has occurred please contact support@clay.run`));
          process.exit();
        }

        if(status) {
            status.message('Downloading service..')
        }
        var downloadOptions = {
          uri: response.serviceUrl,
          method: 'GET',
          timeout: 0,
          encoding: null,
          json: true
        }
        return Promise.all([rp(downloadOptions), Promise.resolve(response.service)])
      })
      .then(([downloadedCode, service]) => {
        fs.writeFileSync(`${dir}.zip`, downloadedCode)
        var zipFolder = new zip(`${dir}.zip`)
        zipFolder.extractAllTo(`${dir}`)
        var clayConfig = clayConfigGenerator.defaultTemplate(service.name, service.type, service.function_input, service.name.split('-').shift())
        fs.writeFileSync(`${dir}/clay-config.json`, JSON.stringify(clayConfig, null, 2))
        if(!options.suppressMsg) print((`✅ Successfully downloaded the Clay service to this directory `)+chalk.green(`${dir}`));
        fs.removeSync(`${dir}.zip`)
        resolve(dir);
      })
      .catch((err) => {
        if(status) { status.stop() }
        if(process.env.CLAY_DEV) console.log(err);
        process.exit();
      })
  })

}

<commit_msg>remove spinner after download<commit_after>var chalk    = require('chalk')
 ,  print    = console.log
 ,  fs       = require('fs-extra')
 ,  path     = require('path')
 ,  zip      = require('adm-zip')
 ,  clui     = require('clui')
 ,  Spinner  = clui.Spinner
 ,  clayConfigGenerator = require('./clay-config-generator.js')
 ,  rp       = require('request-promise-native');

module.exports = function(serviceName, options) {
  return new Promise((resolve, reject) => {
    // format serviceName
    options = options || {suppressMsg: false};

    serviceName = serviceName.split('/').slice(-2).join('-');

    const dir = path.resolve(process.cwd(), `${serviceName}`)

    if(fs.existsSync(dir)) {
      print(chalk.white(`Directory `)+chalk.red(`${dir}`)+chalk.white(` already exists please delete it to download this Clay service`));
      process.exit();
    }

    if(!options.suppressMsg) {
        var status = new Spinner('Getting service info..');
        status.start();
    }

    var getFunctionOptions = {
      uri: this.apis.downloadApi,
      method: 'POST',
      body: {
        apiToken: this.credentials.token,
        serviceName: serviceName
      },
      timeout: 0,
      json: true
    }

    rp(getFunctionOptions)
      .then((response) => {
        if(status && response.statusCode != 200) { status.stop(); }

        if(response.statusCode == 401) {
          print(chalk.white(`Make sure you entered the name of the service or the url to the service. E.g. `)+chalk.red(`clay download nicoslepicos/whois`)+chalk.white(` or `)+chalk.red(`clay download http://clay.run/services/nicoslepicos/whois`))
          print(chalk.white(`Also, make sure that this service exists and is owned by you or it is a public service`))
          process.exit();
        }
        else if(response.statusCode == 500) {
          print(chalk.white(`Error has occurred please contact support@clay.run`));
          process.exit();
        }

        if(status) {
            status.message('Downloading service..')
        }
        var downloadOptions = {
          uri: response.serviceUrl,
          method: 'GET',
          timeout: 0,
          encoding: null,
          json: true
        }
        return Promise.all([rp(downloadOptions), Promise.resolve(response.service)])
      })
      .then(([downloadedCode, service]) => {
        fs.writeFileSync(`${dir}.zip`, downloadedCode)
        var zipFolder = new zip(`${dir}.zip`)
        zipFolder.extractAllTo(`${dir}`)
        var clayConfig = clayConfigGenerator.defaultTemplate(service.name, service.type, service.function_input, service.name.split('-').shift())
        fs.writeFileSync(`${dir}/clay-config.json`, JSON.stringify(clayConfig, null, 2))
        if(!options.suppressMsg) print((`✅ Successfully downloaded the Clay service to this directory `)+chalk.green(`${dir}`));
        if(status) { status.stop() }
        fs.removeSync(`${dir}.zip`)
        resolve(dir);
      })
      .catch((err) => {
        if(status) { status.stop() }
        if(process.env.CLAY_DEV) console.log(err);
        process.exit();
      })
  })

}

<|endoftext|>


<commit_before>const { AuthenticationBaseStrategy } = require('@feathersjs/authentication');
const { NotAuthenticated } = require('@feathersjs/errors');
const { Configuration } = require('@schul-cloud/commons');

class ApiKeyStrategy extends AuthenticationBaseStrategy {
	parse(req, res) {
		if (this.validateApiKey(req.headers['x-api-key'])) {
			return {
				strategy: this.name,
				apiKey: req.headers['x-api-key'],
				// eslint-disable-next-line no-underscore-dangle
				route: req._parsedUrl.path.split('/')[1], // todo: remove this when there are permissions for api-keys
			};
		}
		return null;
	}

	async authenticate(authentication, params) {
		// todo: compare keys to a database collection instead.
		if (this.credentialCheck(authentication.apiKey, authentication.route)) {
			return {
				authentication: { strategy: this.name },
			};
		}
		throw new NotAuthenticated('invalid token');
	}

	/**
	 * validate that key exists, is typeof string, and not empty
	 * @param key content of a x-api-key header
	 */
	validateApiKey(key) {
		return (key && typeof key === 'string' && key !== '');
	}

	credentialCheck(key, route) {
		// todo: authenticate against database collection, return permissions.
		// todo: remove route logic, give api-keys permissions instead.
		if (route === 'sync') return (key === Configuration.get('SYNC_API_KEY'));
		if (route === 'resolve') return (key === Configuration.get('CALENDAR_API_KEY'));
		return false;
	}
}

module.exports = ApiKeyStrategy;
<commit_msg>fix api-key-strategy with queryparams<commit_after>const { AuthenticationBaseStrategy } = require('@feathersjs/authentication');
const { NotAuthenticated } = require('@feathersjs/errors');
const { Configuration } = require('@schul-cloud/commons');

class ApiKeyStrategy extends AuthenticationBaseStrategy {
	parse(req, res) {
		if (this.validateApiKey(req.headers['x-api-key'])) {
			return {
				strategy: this.name,
				apiKey: req.headers['x-api-key'],
				// todo: remove route when there are permissions for api-keys
				// eslint-disable-next-line no-underscore-dangle
				route: req._parsedUrl.path.split('/')[1].split('?')[0],
			};
		}
		return null;
	}

	async authenticate(authentication, params) {
		// todo: compare keys to a database collection instead.
		if (this.credentialCheck(authentication.apiKey, authentication.route)) {
			return {
				authentication: { strategy: this.name },
			};
		}
		throw new NotAuthenticated('invalid token');
	}

	/**
	 * validate that key exists, is typeof string, and not empty
	 * @param key content of a x-api-key header
	 */
	validateApiKey(key) {
		return (key && typeof key === 'string' && key !== '');
	}

	credentialCheck(key, route) {
		// todo: authenticate against database collection, return permissions.
		// todo: remove route logic, give api-keys permissions instead.
		if (route === 'sync') return (key === Configuration.get('SYNC_API_KEY'));
		if (route === 'resolve') return (key === Configuration.get('CALENDAR_API_KEY'));
		return false;
	}
}

module.exports = ApiKeyStrategy;
<|endoftext|>


<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

var localStrings = new LocalStrings();
var hasPDFPlugin = true;
var expectedPageCount = 0;
var pageRangesInfo = [];
var printJobTitle = '';

/**
 * Window onload handler, sets up the page.
 */
function load() {
  initializeAnimation();

  updateSummary();

  $('printer-list').disabled = true;
  $('print-button').disabled = true;
  $('print-button').addEventListener('click', printFile);
  $('cancel-button').addEventListener('click', function(e) {
    window.close();
  });

  $('copies').addEventListener('input', validateNumberOfCopies);
  $('copies').addEventListener('blur', handleCopiesFieldBlur);
  $('individual-pages').addEventListener('blur', handlePageRangesFieldBlur);
  $('landscape').onclick = getPreview;
  $('portrait').onclick = getPreview;
  $('color').onclick = getPreview;
  $('bw').onclick = getPreview;

  chrome.send('getPrinters');
};

/**
 * Parses the copies field text for validation and updates the state of print
 * button and collate checkbox. If the specified value is invalid, displays an
 * invalid warning icon on the text box and sets the error message as the title
 * message of text box.
 */
function validateNumberOfCopies() {
  var copiesField = $('copies');
  var message = '';
  if (!isNumberOfCopiesValid())
    message = localStrings.getString('invalidNumberOfCopiesTitleToolTip');
  copiesField.setCustomValidity(message);
  copiesField.title = message;
  updatePrintButtonState();
}

/**
 * Handles copies field blur event.
 */
function handleCopiesFieldBlur() {
  checkAndSetCopiesField();
  printSettingChanged();
}

/**
 * Handles page ranges field blur event.
 */
function handlePageRangesFieldBlur() {
  checkAndSetPageRangesField();
}

/**
 * Depending on 'copies' value, shows/hides the collate checkbox.
 */
function updateCollateCheckboxState() {
  $('collate-option').hidden = getCopies() <= 1;
}

/**
 * Validates the copies text field value.
 * NOTE: An empty copies field text is considered valid because the blur event
 * listener of this field will set it back to a default value.
 * @return {boolean} true if the number of copies is valid else returns false.
 */
function isNumberOfCopiesValid() {
  var copiesFieldText = $('copies').value.replace(/\s/g, '');
  if (copiesFieldText == '')
    return true;

  var numericExp = /^[0-9]+$/;
  return (numericExp.test(copiesFieldText) && Number(copiesFieldText) > 0);
}

/**
 * Checks the value of the copies field. If it is a valid number it does
 * nothing. If it can only parse the first part of the string it replaces the
 * string with the first part. Example: '123abcd' becomes '123'.
 * If the string can't be parsed at all it replaces with 1.
 */
function checkAndSetCopiesField() {
  var copiesField = $('copies');
  var copies = parseInt(copiesField.value, 10);
  if (isNaN(copies))
    copies = 1;
  copiesField.value = copies;
  updateSummary();
}

/**
 * Checks the value of the page ranges text field. It parses the page ranges and
 * normalizes them. For example: '1,2,3,5,9-10' becomes '1-3, 5, 9-10'.
 * If it can't parse the whole string it will replace with the part it parsed.
 * For example: '1-6,9-10,sd343jf' becomes '1-6, 9-10'. If the specified page
 * range includes all pages it replaces it with the empty string (so that the
 * example text is automatically shown.
 *
 */
function checkAndSetPageRangesField() {
  var pageRanges = getPageRanges();
  var parsedPageRanges = '';
  var individualPagesField = $('individual-pages');

  if (pageRanges.length == 1 && pageRanges[0].from == 1 &&
          pageRanges[0].to == expectedPageCount) {
    individualPagesField.value = parsedPageRanges;
    return;
  }

  for (var i = 0; i < pageRanges.length; i++) {
    if (pageRanges[i].from == pageRanges[i].to)
      parsedPageRanges += pageRanges[i].from;
    else
      parsedPageRanges += pageRanges[i].from + '-' + pageRanges[i].to;
    if (i < pageRanges.length - 1)
      parsedPageRanges += ', ';
  }
  individualPagesField.value = parsedPageRanges;
  updateSummary();
}

/**
 * Checks whether the preview layout setting is set to 'landscape' or not.
 *
 * @return {boolean} true if layout is 'landscape'.
 */
function isLandscape() {
  return $('landscape').checked;
}

/**
 * Checks whether the preview color setting is set to 'color' or not.
 *
 * @return {boolean} true if color is 'color'.
 */
function isColor() {
  return $('color').checked;
}

/**
 * Checks whether the preview collate setting value is set or not.
 *
 * @return {boolean} true if collate setting is enabled and checked.
 */
function isCollated() {
  var collateField = $('collate');
  return !collateField.disabled && collateField.checked;
}

/**
 * Returns the number of copies currently indicated in the copies textfield. If
 * the contents of the textfield can not be converted to a number or if <1 it
 * returns 1.
 *
 * @return {number} number of copies.
 */
function getCopies() {
  var copies = parseInt($('copies').value, 10);
  if (!copies || copies <= 1)
    copies = 1;
  return copies;
}

/**
 * Checks whether the preview two-sided checkbox is checked.
 *
 * @return {boolean} true if two-sided is checked.
 */
function isTwoSided() {
  return $('two-sided').checked;
}

/**
 * Creates a JSON string based on the values in the printer settings.
 *
 * @return {string} JSON string with print job settings.
 */
function getSettingsJSON() {
  var selectedPrinter = $('printer-list').selectedIndex;
  var printerName = '';
  if (selectedPrinter >= 0)
    printerName = $('printer-list').options[selectedPrinter].textContent;
  var printAll = $('all-pages').checked;
  var printToPDF = (printerName == localStrings.getString('printToPDF'));

  return JSON.stringify({'printerName': printerName,
                         'pageRange': getPageRanges(),
                         'printAll': printAll,
                         'twoSided': isTwoSided(),
                         'copies': getCopies(),
                         'collate': isCollated(),
                         'landscape': isLandscape(),
                         'color': isColor(),
                         'printToPDF': printToPDF});
}

/**
 * Asks the browser to print the preview PDF based on current print settings.
 */
function printFile() {
  chrome.send('print', [getSettingsJSON()]);
}

/**
 * Asks the browser to generate a preview PDF based on current print settings.
 */
function getPreview() {
  chrome.send('getPreview', [getSettingsJSON()]);
}

/**
 * Fill the printer list drop down.
 * @param {Array} printers Array of printer names.
 * @param {number} defaultPrinterIndex The index of the default printer.
 */
function setPrinters(printers, defaultPrinterIndex) {
  if (printers.length > 0) {
    for (var i = 0; i < printers.length; ++i) {
      var option = document.createElement('option');
      option.textContent = printers[i];
      $('printer-list').add(option);
      if (i == defaultPrinterIndex)
        option.selected = true;
    }
  } else {
    var option = document.createElement('option');
    option.textContent = localStrings.getString('noPrinter');
    $('printer-list').add(option);
  }

  // Adding option for saving PDF to disk.
  var option = document.createElement('option');
  option.textContent = localStrings.getString('printToPDF');
  $('printer-list').add(option);
  $('printer-list').disabled = false;

  // Once the printer list is populated, generate the initial preview.
  getPreview();
}

/**
 * Sets the color mode for the PDF plugin.
 * @param {boolean} color is true if the PDF plugin should display in color.
 */
function setColor(color) {
  if (!hasPDFPlugin) {
    return;
  }
  $('pdf-viewer').grayscale(!color);
}

function onPDFLoad() {
  if (isLandscape())
    $('pdf-viewer').fitToWidth();
  else
    $('pdf-viewer').fitToHeight();
}

/**
 * Update the print preview when new preview data is available.
 * Create the PDF plugin as needed.
 * @param {number} pageCount The expected total pages count.
 * @param {string} jobTitle The print job title.
 *
 */
function updatePrintPreview(pageCount, jobTitle) {
  // Set the expected page count.
  expectedPageCount = pageCount;

  // Set the print job title.
  printJobTitle = jobTitle;

  // Update the current tab title.
  document.title = localStrings.getStringF('printPreviewTitleFormat', jobTitle);

  createPDFPlugin();

  updateSummary();
}

/**
 * Create the PDF plugin or reload the existing one.
 */
function createPDFPlugin() {
  if (!hasPDFPlugin) {
    return;
  }

  // Enable the print button.
  if (!$('printer-list').disabled) {
    $('print-button').disabled = false;
  }

  if ($('pdf-viewer')) {
    $('pdf-viewer').reload();
    $('pdf-viewer').grayscale(!isColor());
    return;
  }

  var loadingElement = $('loading');
  loadingElement.classList.add('hidden');
  var mainView = loadingElement.parentNode;

  var pdfPlugin = document.createElement('embed');
  pdfPlugin.setAttribute('id', 'pdf-viewer');
  pdfPlugin.setAttribute('type', 'application/pdf');
  pdfPlugin.setAttribute('src', 'chrome://print/print.pdf');
  mainView.appendChild(pdfPlugin);
  if (!pdfPlugin.onload) {
    hasPDFPlugin = false;
    mainView.removeChild(pdfPlugin);
    $('no-plugin').classList.remove('hidden');
    return;
  }
  pdfPlugin.grayscale(true);
  pdfPlugin.onload('onPDFLoad()');
}

/**
 * Updates the state of print button depending on the user selection.
 *
 * If the user has selected 'All' pages option, enables the print button.
 * If the user has selected a page range, depending on the validity of page
 * range text enables/disables the print button.
 * Depending on the validity of 'copies' value, enables/disables the print
 * button.
 */
function updatePrintButtonState() {
  $('print-button').disabled = (!($('all-pages').checked ||
                                  $('individual-pages').checkValidity()) ||
                                !$('copies').checkValidity());
}

window.addEventListener('DOMContentLoaded', load);

/**
 * Listener function that executes whenever any of the available settings
 * is changed.
 */
function printSettingChanged() {
  updateCollateCheckboxState();
  updateSummary();
}

/**
 * Updates the print summary based on the currently selected user options.
 *
 */
function updateSummary() {
  var html = '';
  var pageList = getPageList();
  var copies = getCopies();
  var printButton = $('print-button');
  var printSummary = $('print-summary');

  if (isNaN($('copies').value)) {
    html = localStrings.getString('invalidNumberOfCopiesTitleToolTip');
    printSummary.innerHTML = html;
    return;
  }

  if (pageList.length <= 0) {
    html = localStrings.getString('pageRangeInvalidTitleToolTip');
    printSummary.innerHTML = html;
    printButton.disabled = true;
    return;
  }

  var pagesLabel = localStrings.getString('printPreviewPageLabelSingular');
  var twoSidedLabel = '';
  var timesSign = '';
  var numOfCopies = '';
  var copiesLabel = '';
  var equalSign = '';
  var numOfSheets = '';
  var sheetsLabel = '';

  printButton.disabled = false;

  if (pageList.length > 1)
    pagesLabel = localStrings.getString('printPreviewPageLabelPlural');

  if (isTwoSided())
    twoSidedLabel = '('+localStrings.getString('optionTwoSided')+')';

  if (copies > 1) {
    timesSign = '×';
    numOfCopies = copies;
    copiesLabel = localStrings.getString('copiesLabel').toLowerCase();
  }

  if ((copies > 1) || (isTwoSided())) {
    numOfSheets = pageList.length;

    if (isTwoSided())
      numOfSheets = Math.ceil(numOfSheets / 2);

    equalSign = '=';
    numOfSheets *= copies;
    sheetsLabel = localStrings.getString('printPreviewSheetsLabel');
  }

  html = localStrings.getStringF('printPreviewSummaryFormat', pageList.length,
                                  pagesLabel, twoSidedLabel, timesSign,
                                  numOfCopies, copiesLabel, equalSign,
                                  ''+numOfSheets+'',
                                  ''+sheetsLabel+'');

  // Removing extra spaces from within the string.
  html.replace(/\s{2,}/g, ' ');
  printSummary.innerHTML = html;
}

/**
 * Handles a click event on the two-sided option.
 */
function handleTwoSidedClick(event) {
  var el = $('binding');
  handleZippyClickEl(el);
  printSettingChanged(event);
}

/**
 * Returns a list of all pages in the specified ranges. If the page ranges can't
 * be parsed an empty list is returned.
 *
 * @return {Array}
 */
function getPageList() {
  var pageText = $('individual-pages').value;

  if ($('all-pages').checked || pageText == '')
    pageText = '1-' + expectedPageCount;

  var pageList = [];
  var parts = pageText.split(/,/);

  for (var i = 0; i < parts.length; i++) {
    var part = parts[i];
    var match = part.match(/([0-9]+)-([0-9]+)/);

    if (match && match[1] && match[2]) {
      var from = parseInt(match[1], 10);
      var to = parseInt(match[2], 10);

      if (from && to) {
        for (var j = from; j <= to; j++)
          if (j <= expectedPageCount)
            pageList.push(j);
      }
    } else if (parseInt(part, 10)) {
      if (parseInt(part, 10) <= expectedPageCount)
        pageList.push(parseInt(part, 10));
    }
  }
  return pageList;
}

/**
 * Parses the selected page ranges, processes them and returns the results.
 * It squashes whenever possible. Example '1-2,3,5-7' becomes 1-3,5-7
 *
 * @return {Array} an array of page range objects. A page range object has
 *     fields 'from' and 'to'.
 */
function getPageRanges() {
  var pageList = getPageList();
  var pageRanges = [];
  for (var i = 0; i < pageList.length; i++) {
    tempFrom = pageList[i];
    while (i + 1 < pageList.length && pageList[i + 1] == pageList[i] + 1)
      i++;
    tempTo = pageList[i];
    pageRanges.push({'from': tempFrom, 'to': tempTo});
  }
  return pageRanges;
}
<commit_msg>Reseting page range text field when user changes printing orientation.<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

var localStrings = new LocalStrings();
var hasPDFPlugin = true;
var expectedPageCount = 0;
var pageRangesInfo = [];
var printJobTitle = '';

/**
 * Window onload handler, sets up the page.
 */
function load() {
  initializeAnimation();

  updateSummary();

  $('printer-list').disabled = true;
  $('print-button').disabled = true;
  $('print-button').addEventListener('click', printFile);
  $('cancel-button').addEventListener('click', function(e) {
    window.close();
  });

  $('copies').addEventListener('input', validateNumberOfCopies);
  $('copies').addEventListener('blur', handleCopiesFieldBlur);
  $('individual-pages').addEventListener('blur', handlePageRangesFieldBlur);
  $('landscape').onclick = onLayoutModeToggle;
  $('portrait').onclick = onLayoutModeToggle;
  $('color').onclick = getPreview;
  $('bw').onclick = getPreview;

  chrome.send('getPrinters');
};

/**
 * Parses the copies field text for validation and updates the state of print
 * button and collate checkbox. If the specified value is invalid, displays an
 * invalid warning icon on the text box and sets the error message as the title
 * message of text box.
 */
function validateNumberOfCopies() {
  var copiesField = $('copies');
  var message = '';
  if (!isNumberOfCopiesValid())
    message = localStrings.getString('invalidNumberOfCopiesTitleToolTip');
  copiesField.setCustomValidity(message);
  copiesField.title = message;
  updatePrintButtonState();
}

/**
 * Handles copies field blur event.
 */
function handleCopiesFieldBlur() {
  checkAndSetCopiesField();
  printSettingChanged();
}

/**
 * Handles page ranges field blur event.
 */
function handlePageRangesFieldBlur() {
  checkAndSetPageRangesField();
}

/**
 * Depending on 'copies' value, shows/hides the collate checkbox.
 */
function updateCollateCheckboxState() {
  $('collate-option').hidden = getCopies() <= 1;
}

/**
 * Validates the copies text field value.
 * NOTE: An empty copies field text is considered valid because the blur event
 * listener of this field will set it back to a default value.
 * @return {boolean} true if the number of copies is valid else returns false.
 */
function isNumberOfCopiesValid() {
  var copiesFieldText = $('copies').value.replace(/\s/g, '');
  if (copiesFieldText == '')
    return true;

  var numericExp = /^[0-9]+$/;
  return (numericExp.test(copiesFieldText) && Number(copiesFieldText) > 0);
}

/**
 * Checks the value of the copies field. If it is a valid number it does
 * nothing. If it can only parse the first part of the string it replaces the
 * string with the first part. Example: '123abcd' becomes '123'.
 * If the string can't be parsed at all it replaces with 1.
 */
function checkAndSetCopiesField() {
  var copiesField = $('copies');
  var copies = parseInt(copiesField.value, 10);
  if (isNaN(copies))
    copies = 1;
  copiesField.value = copies;
  updateSummary();
}

/**
 * Checks the value of the page ranges text field. It parses the page ranges and
 * normalizes them. For example: '1,2,3,5,9-10' becomes '1-3, 5, 9-10'.
 * If it can't parse the whole string it will replace with the part it parsed.
 * For example: '1-6,9-10,sd343jf' becomes '1-6, 9-10'. If the specified page
 * range includes all pages it replaces it with the empty string (so that the
 * example text is automatically shown.
 *
 */
function checkAndSetPageRangesField() {
  var pageRanges = getPageRanges();
  var parsedPageRanges = '';
  var individualPagesField = $('individual-pages');

  if (pageRanges.length == 1 && pageRanges[0].from == 1 &&
          pageRanges[0].to == expectedPageCount) {
    individualPagesField.value = parsedPageRanges;
    return;
  }

  for (var i = 0; i < pageRanges.length; i++) {
    if (pageRanges[i].from == pageRanges[i].to)
      parsedPageRanges += pageRanges[i].from;
    else
      parsedPageRanges += pageRanges[i].from + '-' + pageRanges[i].to;
    if (i < pageRanges.length - 1)
      parsedPageRanges += ', ';
  }
  individualPagesField.value = parsedPageRanges;
  updateSummary();
}

/**
 * Checks whether the preview layout setting is set to 'landscape' or not.
 *
 * @return {boolean} true if layout is 'landscape'.
 */
function isLandscape() {
  return $('landscape').checked;
}

/**
 * Checks whether the preview color setting is set to 'color' or not.
 *
 * @return {boolean} true if color is 'color'.
 */
function isColor() {
  return $('color').checked;
}

/**
 * Checks whether the preview collate setting value is set or not.
 *
 * @return {boolean} true if collate setting is enabled and checked.
 */
function isCollated() {
  var collateField = $('collate');
  return !collateField.disabled && collateField.checked;
}

/**
 * Returns the number of copies currently indicated in the copies textfield. If
 * the contents of the textfield can not be converted to a number or if <1 it
 * returns 1.
 *
 * @return {number} number of copies.
 */
function getCopies() {
  var copies = parseInt($('copies').value, 10);
  if (!copies || copies <= 1)
    copies = 1;
  return copies;
}

/**
 * Checks whether the preview two-sided checkbox is checked.
 *
 * @return {boolean} true if two-sided is checked.
 */
function isTwoSided() {
  return $('two-sided').checked;
}

/**
 * Creates a JSON string based on the values in the printer settings.
 *
 * @return {string} JSON string with print job settings.
 */
function getSettingsJSON() {
  var selectedPrinter = $('printer-list').selectedIndex;
  var printerName = '';
  if (selectedPrinter >= 0)
    printerName = $('printer-list').options[selectedPrinter].textContent;
  var printAll = $('all-pages').checked;
  var printToPDF = (printerName == localStrings.getString('printToPDF'));

  return JSON.stringify({'printerName': printerName,
                         'pageRange': getPageRanges(),
                         'printAll': printAll,
                         'twoSided': isTwoSided(),
                         'copies': getCopies(),
                         'collate': isCollated(),
                         'landscape': isLandscape(),
                         'color': isColor(),
                         'printToPDF': printToPDF});
}

/**
 * Asks the browser to print the preview PDF based on current print settings.
 */
function printFile() {
  chrome.send('print', [getSettingsJSON()]);
}

/**
 * Asks the browser to generate a preview PDF based on current print settings.
 */
function getPreview() {
  chrome.send('getPreview', [getSettingsJSON()]);
}

/**
 * Fill the printer list drop down.
 * @param {Array} printers Array of printer names.
 * @param {number} defaultPrinterIndex The index of the default printer.
 */
function setPrinters(printers, defaultPrinterIndex) {
  if (printers.length > 0) {
    for (var i = 0; i < printers.length; ++i) {
      var option = document.createElement('option');
      option.textContent = printers[i];
      $('printer-list').add(option);
      if (i == defaultPrinterIndex)
        option.selected = true;
    }
  } else {
    var option = document.createElement('option');
    option.textContent = localStrings.getString('noPrinter');
    $('printer-list').add(option);
  }

  // Adding option for saving PDF to disk.
  var option = document.createElement('option');
  option.textContent = localStrings.getString('printToPDF');
  $('printer-list').add(option);
  $('printer-list').disabled = false;

  // Once the printer list is populated, generate the initial preview.
  getPreview();
}

/**
 * Sets the color mode for the PDF plugin.
 * @param {boolean} color is true if the PDF plugin should display in color.
 */
function setColor(color) {
  if (!hasPDFPlugin) {
    return;
  }
  $('pdf-viewer').grayscale(!color);
}

function onPDFLoad() {
  if (isLandscape())
    $('pdf-viewer').fitToWidth();
  else
    $('pdf-viewer').fitToHeight();
}

/**
 * Update the print preview when new preview data is available.
 * Create the PDF plugin as needed.
 * @param {number} pageCount The expected total pages count.
 * @param {string} jobTitle The print job title.
 *
 */
function updatePrintPreview(pageCount, jobTitle) {
  // Set the expected page count.
  expectedPageCount = pageCount;

  // Set the print job title.
  printJobTitle = jobTitle;

  // Update the current tab title.
  document.title = localStrings.getStringF('printPreviewTitleFormat', jobTitle);

  createPDFPlugin();

  updateSummary();
}

/**
 * Create the PDF plugin or reload the existing one.
 */
function createPDFPlugin() {
  if (!hasPDFPlugin) {
    return;
  }

  // Enable the print button.
  if (!$('printer-list').disabled) {
    $('print-button').disabled = false;
  }

  if ($('pdf-viewer')) {
    $('pdf-viewer').reload();
    $('pdf-viewer').grayscale(!isColor());
    return;
  }

  var loadingElement = $('loading');
  loadingElement.classList.add('hidden');
  var mainView = loadingElement.parentNode;

  var pdfPlugin = document.createElement('embed');
  pdfPlugin.setAttribute('id', 'pdf-viewer');
  pdfPlugin.setAttribute('type', 'application/pdf');
  pdfPlugin.setAttribute('src', 'chrome://print/print.pdf');
  mainView.appendChild(pdfPlugin);
  if (!pdfPlugin.onload) {
    hasPDFPlugin = false;
    mainView.removeChild(pdfPlugin);
    $('no-plugin').classList.remove('hidden');
    return;
  }
  pdfPlugin.grayscale(true);
  pdfPlugin.onload('onPDFLoad()');
}

/**
 * Updates the state of print button depending on the user selection.
 *
 * If the user has selected 'All' pages option, enables the print button.
 * If the user has selected a page range, depending on the validity of page
 * range text enables/disables the print button.
 * Depending on the validity of 'copies' value, enables/disables the print
 * button.
 */
function updatePrintButtonState() {
  $('print-button').disabled = (!($('all-pages').checked ||
                                  $('individual-pages').checkValidity()) ||
                                !$('copies').checkValidity());
}

window.addEventListener('DOMContentLoaded', load);

/**
 * Listener function that executes whenever any of the available settings
 * is changed.
 */
function printSettingChanged() {
  updateCollateCheckboxState();
  updateSummary();
}

/**
 * Updates the print summary based on the currently selected user options.
 *
 */
function updateSummary() {
  var html = '';
  var pageList = getPageList();
  var copies = getCopies();
  var printButton = $('print-button');
  var printSummary = $('print-summary');

  if (isNaN($('copies').value)) {
    html = localStrings.getString('invalidNumberOfCopiesTitleToolTip');
    printSummary.innerHTML = html;
    return;
  }

  if (pageList.length <= 0) {
    html = localStrings.getString('pageRangeInvalidTitleToolTip');
    printSummary.innerHTML = html;
    printButton.disabled = true;
    return;
  }

  var pagesLabel = localStrings.getString('printPreviewPageLabelSingular');
  var twoSidedLabel = '';
  var timesSign = '';
  var numOfCopies = '';
  var copiesLabel = '';
  var equalSign = '';
  var numOfSheets = '';
  var sheetsLabel = '';

  printButton.disabled = false;

  if (pageList.length > 1)
    pagesLabel = localStrings.getString('printPreviewPageLabelPlural');

  if (isTwoSided())
    twoSidedLabel = '('+localStrings.getString('optionTwoSided')+')';

  if (copies > 1) {
    timesSign = '×';
    numOfCopies = copies;
    copiesLabel = localStrings.getString('copiesLabel').toLowerCase();
  }

  if ((copies > 1) || (isTwoSided())) {
    numOfSheets = pageList.length;

    if (isTwoSided())
      numOfSheets = Math.ceil(numOfSheets / 2);

    equalSign = '=';
    numOfSheets *= copies;
    sheetsLabel = localStrings.getString('printPreviewSheetsLabel');
  }

  html = localStrings.getStringF('printPreviewSummaryFormat', pageList.length,
                                  pagesLabel, twoSidedLabel, timesSign,
                                  numOfCopies, copiesLabel, equalSign,
                                  ''+numOfSheets+'',
                                  ''+sheetsLabel+'');

  // Removing extra spaces from within the string.
  html.replace(/\s{2,}/g, ' ');
  printSummary.innerHTML = html;
}

/**
 * Handles a click event on the two-sided option.
 */
function handleTwoSidedClick(event) {
  var el = $('binding');
  handleZippyClickEl(el);
  printSettingChanged(event);
}

/**
 * When the user switches printing orientation mode the page field selection is
 * reset to "all pages selected". After the change the number of pages will be
 * different and currently selected page numbers might no longer be valid.
 * Even if they are still valid the content of these pages will be different.
 */
function onLayoutModeToggle() {
  $('individual-pages').value = '';
  $('all-pages').checked = true;
  getPreview();
}

/**
 * Returns a list of all pages in the specified ranges. If the page ranges can't
 * be parsed an empty list is returned.
 *
 * @return {Array}
 */
function getPageList() {
  var pageText = $('individual-pages').value;

  if ($('all-pages').checked || pageText == '')
    pageText = '1-' + expectedPageCount;

  var pageList = [];
  var parts = pageText.split(/,/);

  for (var i = 0; i < parts.length; i++) {
    var part = parts[i];
    var match = part.match(/([0-9]+)-([0-9]+)/);

    if (match && match[1] && match[2]) {
      var from = parseInt(match[1], 10);
      var to = parseInt(match[2], 10);

      if (from && to) {
        for (var j = from; j <= to; j++)
          if (j <= expectedPageCount)
            pageList.push(j);
      }
    } else if (parseInt(part, 10)) {
      if (parseInt(part, 10) <= expectedPageCount)
        pageList.push(parseInt(part, 10));
    }
  }
  return pageList;
}

/**
 * Parses the selected page ranges, processes them and returns the results.
 * It squashes whenever possible. Example '1-2,3,5-7' becomes 1-3,5-7
 *
 * @return {Array} an array of page range objects. A page range object has
 *     fields 'from' and 'to'.
 */
function getPageRanges() {
  var pageList = getPageList();
  var pageRanges = [];
  for (var i = 0; i < pageList.length; i++) {
    tempFrom = pageList[i];
    while (i + 1 < pageList.length && pageList[i + 1] == pageList[i] + 1)
      i++;
    tempTo = pageList[i];
    pageRanges.push({'from': tempFrom, 'to': tempTo});
  }
  return pageRanges;
}
<|endoftext|>


<commit_before>import * as DS    from './delaney';
import * as props from './properties';
import * as comb  from '../common/combinatorics';

export const dual = ds => DS.buildDSymbol({
  dim: ds.dim,
  size: ds.size,
  getS: (i, D) => ds.s(ds.dim - i, D),
  getV: (i, D) => ds.v(ds.dim - i - 1, ds.dim - i, D)
});

export const cover = (ds, nrSheets, fn) => {
  const d = DS.dim(ds);
  const n = DS.size(ds);

  const pairingsFn = (_, i) => {
    const out = [];
    for (const D of ds.elements()) {
      for (let k = 0; k < nrSheets; ++k)
        out.push([k * n + D, fn(k, i, D) * n + ds.s(i, D)]);
    }
    return out;
  };

  const branchingsFn = (tmp, i) => {
    const out = [];
    for (const D of DS.orbitReps2(tmp, i, i+1)) {
      const m = DS.m(ds, i, i+1, (D - 1) % n + 1) || 0;
      out.push([D, m / DS.r(tmp, i, i+1, D)]);
    }
    return out;
  };

  return DS.build(d, n * nrSheets, pairingsFn, branchingsFn);
};

export const orientedCover = ds => {
  if (props.isOriented(ds))
    return ds;
  else {
    const ori = props.partialOrientation(ds);
    return cover(ds, 2, (k, i, D) => ori[D] == ori[ds.s(i, D)] ? 1 - k : k);
  }
};

export const minimal = ds => {
  if (props.isMinimal(ds))
    return ds;
  else {
    const p = props.typePartition(ds);

    const reps = [];
    const seen = {};
    for (const D of ds.elements()) {
      const r = p.find(D);
      if (seen[r] == null) {
        seen[r] = true;
        reps.push(r);
      }
    }

    return DS.build(
      DS.dim(ds), reps.length,
      (_, i) => reps.map(
        (D, k) => [k+1, reps.indexOf(p.find(ds.s(i, D))) + 1]),
      (tmp, i) => reps.map(
        (D, k) => [k+1, (DS.m(ds, i, i+1, D) || 0) / DS.r(tmp, i, i+1, k+1)]));
  }
};

const _apply = (p, i) => (i < 0 || i >= p.length) ? i : p[i] - 1;

export const barycentricSubdivision = (ds, splitDim) => {
  if (splitDim == null)
    splitDim = DS.dim(ds);

  if (splitDim == 0)
    return ds;
  else {
    const n = DS.size(ds);

    const perms = [];
    const elements = [];
    let j = 0;
    for (const p of comb.permutations(splitDim + 1)) {
      perms.push(p);
      for (const D of ds.elements())
        elements.push([p, j, D]);
      ++j;
    }

    const sigma = (p, i) => {
      const q = p.slice();
      [q[i], q[i+1]] = [p[i+1], p[i]];
      return perms.findIndex(p => p <= q && p >= q);
    };

    const pairingsFn = (_, i) => elements.map(([p, j, D]) => [
      n * j + D,
      (i < splitDim) ?
        n * sigma(p, i) + D :
        n * j + ds.s(_apply(p, i), D)
    ]);

    const branchingsFn = (tmp, i) => elements.map(([p, j, D]) => [
      n * j + D,
      (i < splitDim - 1) ?
        1 :
        ds.v(_apply(p, i), _apply(p, i+1), D)
    ]);

    return DS.build(DS.dim(ds), n * perms.length, pairingsFn, branchingsFn);
  }
};

export const tAnalog = ds => dual(barycentricSubdivision(ds));

export const canonical = ds => {
  const inv = props.invariant(ds);
  const dim = DS.dim(ds);
  const size = DS.size(ds);

  const ops = new Array(dim + 1).fill(0).map(_ => []);
  const brs = new Array(dim).fill(0).map(_ => []);

  let n = 0;
  let k = -1;

  while (k+1 < inv.length) {
    const i = inv[++k];
    const D = inv[++k];
    const E = (i >= 0) ? inv[++k] : D;
    if (E > n) {
      for (let j = 0; j < dim; ++j)
        brs[j].push([E, inv[++k]]);
      n = E;
    }
    if (i >= 0)
      ops[i].push([D, E]);
  }

  return DS.build(dim, size, (_, i) => ops[i], (_, i) => brs[i]);
};

export const subsymbol = (ds, indices, seed) => {
  const elements = props.orbit(ds, indices, seed);
  const dim = indices.length - 1;
  const size = elements.length;

  const mappedElement = {};
  for (let D = 1; D <= size; ++D)
    mappedElement[elements[D - 1]] = D;

  const ops = new Array(dim + 1).fill(0).map(_ => []);
  const brs = new Array(dim).fill(0).map(_ => []);

  for (const D of elements) {
    for (let i = 0; i <= dim; ++i)
      ops[i].push([mappedElement[D], mappedElement[ds.s(indices[i], D)]]);

    for (let i = 0; i < dim; ++i)
      brs[i].push([mappedElement[D], ds.v(indices[i], indices[i + 1], D)])
  }

  return DS.build(dim, size, (_, i) => ops[i], (_, i) => brs[i]);
};

if (require.main == module) {
  const test = ds => {
    console.log(`ds = ${ds}`);
    console.log();

    console.log(`    dual          : ${dual(ds)}`);
    console.log(`    minimal image : ${minimal(ds)}`);
    console.log(`    oriented cover: ${orientedCover(ds)}`);
    console.log(`    canonical     : ${canonical(ds)}`);
    console.log();

    for (const i of ds.indices()) {
      const idcs = ds.indices().filter(k => k != i);
      const sub = subsymbol(ds, idcs, 1);
      console.log(`    ${JSON.stringify(idcs)}-subsymbol at 1: ${sub}`);
    }
    console.log();

    for (const i of ds.indices())
      console.log(`    ${i}-subdivision : ${barycentricSubdivision(ds, i)}`);
    console.log();
    console.log();
  };

  test(DS.parse(
    '<1.1:24:' +
      '2 4 6 8 10 12 14 16 18 20 22 24,' +
      '16 3 5 7 9 11 13 15 24 19 21 23,' +
      '10 9 20 19 14 13 22 21 24 23 18 17:' +
      '8 4,3 3 3 3>'));

  test(DS.parse('<1.1:3:1 2 3,1 3,2 3:4 8,3>'));
  test(DS.parse('<1.1:2 3:2,1 2,1 2,2:6,3 2,6>'));
}
<commit_msg>derived: convert cover() and minimal() to new DSymbol builder<commit_after>import * as DS    from './delaney';
import * as props from './properties';
import * as comb  from '../common/combinatorics';

export const dual = ds => DS.buildDSymbol({
  dim: ds.dim,
  size: ds.size,
  getS: (i, D) => ds.s(ds.dim - i, D),
  getV: (i, D) => ds.v(ds.dim - i - 1, ds.dim - i, D)
});

export const cover = (ds, nrSheets, fn) => {
  const src = D => (D - 1) % ds.size + 1;
  const sheet = D => (D - src(D)) / ds.size;

  return DS.buildDSymbol({
    dim: ds.dim,
    size: ds.size * nrSheets,
    getS: (i, D) => ds.size * fn(sheet(D), i, src(D)) + ds.s(i, src(D)),
    getM: (i, D) => ds.m(i, i+1, src(D)) || 0
  });
};

export const orientedCover = ds => {
  if (props.isOriented(ds))
    return ds;
  else {
    const ori = props.partialOrientation(ds);
    return cover(ds, 2, (k, i, D) => ori[D] == ori[ds.s(i, D)] ? 1 - k : k);
  }
};

export const minimal = ds => {
  if (props.isMinimal(ds))
    return ds;
  else {
    const p = props.typePartition(ds);

    const reps = [0];
    const seen = {};
    for (const D of ds.elements()) {
      const r = p.find(D);
      if (seen[r] == null) {
        seen[r] = true;
        reps.push(r);
      }
    }

    return DS.buildDSymbol({
      dim: ds.dim,
      size: reps.length - 1,
      getS: (i, D) => reps.indexOf(p.find(ds.s(i, reps[D]))),
      getM: (i, D) => ds.m(i, i+1, reps[D]) || 0
    });
  }
};

const _apply = (p, i) => (i < 0 || i >= p.length) ? i : p[i] - 1;

export const barycentricSubdivision = (ds, splitDim) => {
  if (splitDim == null)
    splitDim = DS.dim(ds);

  if (splitDim == 0)
    return ds;
  else {
    const n = DS.size(ds);

    const perms = [];
    const elements = [];
    let j = 0;
    for (const p of comb.permutations(splitDim + 1)) {
      perms.push(p);
      for (const D of ds.elements())
        elements.push([p, j, D]);
      ++j;
    }

    const sigma = (p, i) => {
      const q = p.slice();
      [q[i], q[i+1]] = [p[i+1], p[i]];
      return perms.findIndex(p => p <= q && p >= q);
    };

    const pairingsFn = (_, i) => elements.map(([p, j, D]) => [
      n * j + D,
      (i < splitDim) ?
        n * sigma(p, i) + D :
        n * j + ds.s(_apply(p, i), D)
    ]);

    const branchingsFn = (tmp, i) => elements.map(([p, j, D]) => [
      n * j + D,
      (i < splitDim - 1) ?
        1 :
        ds.v(_apply(p, i), _apply(p, i+1), D)
    ]);

    return DS.build(DS.dim(ds), n * perms.length, pairingsFn, branchingsFn);
  }
};

export const tAnalog = ds => dual(barycentricSubdivision(ds));

export const canonical = ds => {
  const inv = props.invariant(ds);
  const dim = DS.dim(ds);
  const size = DS.size(ds);

  const ops = new Array(dim + 1).fill(0).map(_ => []);
  const brs = new Array(dim).fill(0).map(_ => []);

  let n = 0;
  let k = -1;

  while (k+1 < inv.length) {
    const i = inv[++k];
    const D = inv[++k];
    const E = (i >= 0) ? inv[++k] : D;
    if (E > n) {
      for (let j = 0; j < dim; ++j)
        brs[j].push([E, inv[++k]]);
      n = E;
    }
    if (i >= 0)
      ops[i].push([D, E]);
  }

  return DS.build(dim, size, (_, i) => ops[i], (_, i) => brs[i]);
};

export const subsymbol = (ds, indices, seed) => {
  const elements = props.orbit(ds, indices, seed);
  const dim = indices.length - 1;
  const size = elements.length;

  const mappedElement = {};
  for (let D = 1; D <= size; ++D)
    mappedElement[elements[D - 1]] = D;

  const ops = new Array(dim + 1).fill(0).map(_ => []);
  const brs = new Array(dim).fill(0).map(_ => []);

  for (const D of elements) {
    for (let i = 0; i <= dim; ++i)
      ops[i].push([mappedElement[D], mappedElement[ds.s(indices[i], D)]]);

    for (let i = 0; i < dim; ++i)
      brs[i].push([mappedElement[D], ds.v(indices[i], indices[i + 1], D)])
  }

  return DS.build(dim, size, (_, i) => ops[i], (_, i) => brs[i]);
};

if (require.main == module) {
  const test = ds => {
    console.log(`ds = ${ds}`);
    console.log();

    console.log(`    dual          : ${dual(ds)}`);
    console.log(`    minimal image : ${minimal(ds)}`);
    console.log(`    oriented cover: ${orientedCover(ds)}`);
    console.log(`    canonical     : ${canonical(ds)}`);
    console.log();

    for (const i of ds.indices()) {
      const idcs = ds.indices().filter(k => k != i);
      const sub = subsymbol(ds, idcs, 1);
      console.log(`    ${JSON.stringify(idcs)}-subsymbol at 1: ${sub}`);
    }
    console.log();

    for (const i of ds.indices())
      console.log(`    ${i}-subdivision : ${barycentricSubdivision(ds, i)}`);
    console.log();
    console.log();
  };

  test(DS.parse(
    '<1.1:24:' +
      '2 4 6 8 10 12 14 16 18 20 22 24,' +
      '16 3 5 7 9 11 13 15 24 19 21 23,' +
      '10 9 20 19 14 13 22 21 24 23 18 17:' +
      '8 4,3 3 3 3>'));

  test(DS.parse('<1.1:3:1 2 3,1 3,2 3:4 8,3>'));
  test(DS.parse('<1.1:2 3:2,1 2,1 2,2:6,3 2,6>'));
}
<|endoftext|>