<commit_before>import { CliTrackerController } from 'azk/cli/cli_tracker_controller';
import { Helpers } from 'azk/cli/helpers';
import { _, config, lazy_require } from 'azk';
import { defer, asyncUnsubscribe } from 'azk/utils/promises';
import { subscribe } from 'azk/utils/postal';

var lazy = lazy_require({
  Manifest: ['azk/manifest'],
  docker  : ['azk/docker', 'default'],
});

export default class Shell extends CliTrackerController {
  index() {
    var { options } = this.normalized_params;
    var args = this.normalized_params.arguments;

    var _subscription = subscribe('docker.pull.status', (data) => {
      this.view('image_pull').render(data);
    });

    return asyncUnsubscribe(this, _subscription, function* () {
      var dir = this.cwd;
      var manifest, system;

      yield Helpers.requireAgent(this.ui);

      if (options.image) {
        // Arbitrary image
        manifest = lazy.Manifest.makeFake(dir, options.image);
        system   = manifest.systemDefault;
      } else {
        manifest = new lazy.Manifest(dir, true);
        Helpers.manifestValidate(this.ui, manifest);

        system   = manifest.systemDefault;
        if (args.system) {
          system = manifest.system(args.system, true);
        }
      }

      // Use or not tty?
      var shell_args = args['shell-args'];
      var tty_default = options.tty || (_.isEmpty(options.command) && _.isEmpty(shell_args));
      var tty = (options['no-tty']) ? (options.tty || false) : tty_default;

      var stdin = this.ui.stdin();
      stdin.custom_pipe = () => { };

      var run_options = {
        interactive: tty,
        pull   : this.ui.stdout(),
        stdout : this.ui.stdout(),
        stderr : this.ui.stderr(),
        stdin  : stdin,
        workdir: options.cwd || null,
      };

      // Support extra envs, ports and mount volumes
      run_options.envs       = this._parse_option(options.env  , /.+=.+/, '=', 'invalid_env');
      run_options.shell_term = process.env.TERM;
      run_options.mounts     = this._parse_option(options.mount, /.+:.+:?.*/, ':', 'invalid_mount', 1, (opts) => {
        return { type: (opts[2] ? opts[1] : 'path'), value: (opts[2] ? opts[2] : opts[0]) };
      });

      // Remove container before run
      var is_remove = !options['no-remove'] ? config("docker:remove_container") : !options['no-remove'];
      run_options = _.merge(run_options, {
        shell          : options.shell,
        shell_args     : shell_args,
        command        : options.command,
        build_force    : options.rebuild || false,
        provision_force: (options.rebuild ? true : options.reprovision) || false,
        remove         : is_remove,
      });

      var result = defer((resolver, reject) => {
        var escape = (key, container, next) => {
          if (key === ".") {
            process.nextTick(() => {
              lazy.docker.getContainer(container).stop({ t: 5000 }).catch(reject);
            });
            return true;
          } else if (key === "?") {
            this.ok("coming soon...");
            process.nextTick(() => next());
            return true;
          }
          return false;
        };

        var _subscription_run = subscribe('docker.run.status', (data) => {
          this._escapeAndPullProgress(escape, system,
            !options.silent, options.verbose, options.stdout)(data);
        });

        system
          .runShell(run_options)
          .then(function (result) {
            _subscription_run.unsubscribe();
            return result;
          })
          .then(resolver, reject)
          .catch(function (err) {
            _subscription_run.unsubscribe();
            throw err;
          });
      });

      result = yield result.catch((error) => {
        return this.parseError(error);
      });

      return result.code;
    });
  }

  parseError(error) {
    if (error.statusCode) {
      if (error.statusCode === 404 && error.reason === "no such container") {
        this.ui.fail("commands.shell.ended.removed");
        return { code: 127 };
      }
    } else if (error.code === 'ECONNRESET') {
      this.ui.fail("commands.shell.ended.docker_end");
      return { code: 127 };
    } else if (error.code === 'ECONNREFUSED') {
      this.ui.fail("commands.shell.ended.docker_not_found");
      return { code: 127 };
    }
    throw error;
  }

  _escapeAndPullProgress(escape, system, show_logs, verbose) {
    return (event) => {
      var escapeCapture   = Helpers.escapeCapture(escape);

      // show verbose output
      if (verbose && event.stream) {
        this.ui.stdout().write('  ' + event.stream);
      }

      if (event.type === "stdin_pipe") {
        escapeCapture(event);
      } else if (show_logs) {
        if (show_logs && event.type === "pull_msg") {
          this.view('image_pull').render(event);
        } else if (event.type === "action") {
          var keys = ["commands", "scale"];
          var actions = ["pull_image", "build_image"];

          if (actions.indexOf(event.action) > -1) {
            var data = { image: system.image.name };
            this.ui.ok([...keys].concat(event.action), data);
          }
        }
      }
    };
  }

  _parse_option(option, regex, split, fail, key_index = 0, format = null) {
    var result = {};
    for (var j = 0; j < option.length; j++) {
      var opt = option[j];
      if (opt.match(regex)) {
        opt = opt.split(split);
        result[opt[key_index]] = format ? format(opt) : opt[1];
      } else {
        this.ui.fail('commands.shell.' + fail, { value: opt });
        return 1;
      }
    }
    return result;
  }
}
<commit_msg>Allow unsetting of variables in `azk shell`<commit_after>import { CliTrackerController } from 'azk/cli/cli_tracker_controller';
import { Helpers } from 'azk/cli/helpers';
import { _, config, lazy_require } from 'azk';
import { defer, asyncUnsubscribe } from 'azk/utils/promises';
import { subscribe } from 'azk/utils/postal';

var lazy = lazy_require({
  Manifest: ['azk/manifest'],
  docker  : ['azk/docker', 'default'],
});

export default class Shell extends CliTrackerController {
  index() {
    var { options } = this.normalized_params;
    var args = this.normalized_params.arguments;

    var _subscription = subscribe('docker.pull.status', (data) => {
      this.view('image_pull').render(data);
    });

    return asyncUnsubscribe(this, _subscription, function* () {
      var dir = this.cwd;
      var manifest, system;

      yield Helpers.requireAgent(this.ui);

      if (options.image) {
        // Arbitrary image
        manifest = lazy.Manifest.makeFake(dir, options.image);
        system   = manifest.systemDefault;
      } else {
        manifest = new lazy.Manifest(dir, true);
        Helpers.manifestValidate(this.ui, manifest);

        system   = manifest.systemDefault;
        if (args.system) {
          system = manifest.system(args.system, true);
        }
      }

      // Use or not tty?
      var shell_args = args['shell-args'];
      var tty_default = options.tty || (_.isEmpty(options.command) && _.isEmpty(shell_args));
      var tty = (options['no-tty']) ? (options.tty || false) : tty_default;

      var stdin = this.ui.stdin();
      stdin.custom_pipe = () => { };

      var run_options = {
        interactive: tty,
        pull   : this.ui.stdout(),
        stdout : this.ui.stdout(),
        stderr : this.ui.stderr(),
        stdin  : stdin,
        workdir: options.cwd || null,
      };

      // Support extra envs, ports and mount volumes
      run_options.envs       = this._parse_option(options.env  , /.+=/, '=', 'invalid_env');
      run_options.shell_term = process.env.TERM;
      run_options.mounts     = this._parse_option(options.mount, /.+:.+:?.*/, ':', 'invalid_mount', 1, (opts) => {
        return { type: (opts[2] ? opts[1] : 'path'), value: (opts[2] ? opts[2] : opts[0]) };
      });

      // Remove container before run
      var is_remove = !options['no-remove'] ? config("docker:remove_container") : !options['no-remove'];
      run_options = _.merge(run_options, {
        shell          : options.shell,
        shell_args     : shell_args,
        command        : options.command,
        build_force    : options.rebuild || false,
        provision_force: (options.rebuild ? true : options.reprovision) || false,
        remove         : is_remove,
      });

      var result = defer((resolver, reject) => {
        var escape = (key, container, next) => {
          if (key === ".") {
            process.nextTick(() => {
              lazy.docker.getContainer(container).stop({ t: 5000 }).catch(reject);
            });
            return true;
          } else if (key === "?") {
            this.ok("coming soon...");
            process.nextTick(() => next());
            return true;
          }
          return false;
        };

        var _subscription_run = subscribe('docker.run.status', (data) => {
          this._escapeAndPullProgress(escape, system,
            !options.silent, options.verbose, options.stdout)(data);
        });

        system
          .runShell(run_options)
          .then(function (result) {
            _subscription_run.unsubscribe();
            return result;
          })
          .then(resolver, reject)
          .catch(function (err) {
            _subscription_run.unsubscribe();
            throw err;
          });
      });

      result = yield result.catch((error) => {
        return this.parseError(error);
      });

      return result.code;
    });
  }

  parseError(error) {
    if (error.statusCode) {
      if (error.statusCode === 404 && error.reason === "no such container") {
        this.ui.fail("commands.shell.ended.removed");
        return { code: 127 };
      }
    } else if (error.code === 'ECONNRESET') {
      this.ui.fail("commands.shell.ended.docker_end");
      return { code: 127 };
    } else if (error.code === 'ECONNREFUSED') {
      this.ui.fail("commands.shell.ended.docker_not_found");
      return { code: 127 };
    }
    throw error;
  }

  _escapeAndPullProgress(escape, system, show_logs, verbose) {
    return (event) => {
      var escapeCapture   = Helpers.escapeCapture(escape);

      // show verbose output
      if (verbose && event.stream) {
        this.ui.stdout().write('  ' + event.stream);
      }

      if (event.type === "stdin_pipe") {
        escapeCapture(event);
      } else if (show_logs) {
        if (show_logs && event.type === "pull_msg") {
          this.view('image_pull').render(event);
        } else if (event.type === "action") {
          var keys = ["commands", "scale"];
          var actions = ["pull_image", "build_image"];

          if (actions.indexOf(event.action) > -1) {
            var data = { image: system.image.name };
            this.ui.ok([...keys].concat(event.action), data);
          }
        }
      }
    };
  }

  _parse_option(option, regex, split, fail, key_index = 0, format = null) {
    var result = {};
    for (var j = 0; j < option.length; j++) {
      var opt = option[j];
      if (opt.match(regex)) {
        opt = opt.split(split);
        result[opt[key_index]] = format ? format(opt) : opt[1];
      } else {
        this.ui.fail('commands.shell.' + fail, { value: opt });
        return 1;
      }
    }
    return result;
  }
}
<|endoftext|>


<commit_before>
cash.fn.extend({

  each: function(callback){
    cash.each(this, callback);
  },

  eq: function(index){
    return $(this[index]);
  },

  filter: function(){
    if(typeof arguments[0] === "string") {
      var selector = arguments[0];
      return Array.prototype.filter.call(this, function(e){
        return cash.matches(e, selector);
      });
    } else {
      return Array.prototype.filter.call(this, arguments[0]);
    }
  },

  first: function(){
    return $(this[0]);
  },

  get: function( num ) {
    return this[num];
  },

  index: function(elem){
    if(!elem) {
      return Array.prototype.slice.call(cash(this[0]).parent().children()).indexOf(this[0]);
    } else {
      return Array.prototype.slice.call(cash(elem).children()).indexOf(this[0]);
    }
  },

  last: function(){
    return $(this[this.length -1]);
  },

  add: function(){
    var ret = $.merge( cash() , this ), filter = false;
    for( var arg in arguments ) {
      if( arguments[arg] instanceof cash ) {
        ret = cash.merge( ret , arguments[arg] );
        filter = true;
      }
    }
    if( filter ) {
      ret = ret.filter( function( el, index, obj ) {
        return Array.prototype.indexOf.call( obj , el ) === index;
      });
    }
    return cash.merge( cash() , ret );
  }

});<commit_msg>allowing selector string input<commit_after>
cash.fn.extend({

  each: function(callback){
    cash.each(this, callback);
  },

  eq: function(index){
    return $(this[index]);
  },

  filter: function(){
    if(typeof arguments[0] === "string") {
      var selector = arguments[0];
      return Array.prototype.filter.call(this, function(e){
        return cash.matches(e, selector);
      });
    } else {
      return Array.prototype.filter.call(this, arguments[0]);
    }
  },

  first: function(){
    return $(this[0]);
  },

  get: function( num ) {
    return this[num];
  },

  index: function(elem){
    if(!elem) {
      return Array.prototype.slice.call(cash(this[0]).parent().children()).indexOf(this[0]);
    } else {
      return Array.prototype.slice.call(cash(elem).children()).indexOf(this[0]);
    }
  },

  last: function(){
    return $(this[this.length -1]);
  },

  add: function(){
    var ret = $.merge( cash() , this ), filter = false;
    for( var arg in arguments ) {
      if( arguments[arg] instanceof cash ) {
        ret = cash.merge( ret , arguments[arg] );
        filter = true;
      } else if( typeof arguments[arg] === "string" ) {
        ret = cash.merge( ret , $(arguments[arg]) );
        filter = true;
      }
    }
    if( filter ) {
      ret = ret.filter( function( el, index, obj ) {
        return Array.prototype.indexOf.call( obj , el ) === index;
      });
    }
    return $.merge( cash() , ret );
  }

});<|endoftext|>


<commit_before>import BasicAdapter from "./basic";
const Ember = window.Ember;
const { $, computed, run, RSVP: { Promise } } = Ember;

export default BasicAdapter.extend({

  sendMessage(options) {
    options = options || {};
    this.get('socket').emit('emberInspectorMessage', options);
  },

  socket: computed(function() {
    return window.EMBER_INSPECTOR_CONFIG.remoteDebugSocket;
  }),

  _listen() {
    this.get('socket').on('emberInspectorMessage', function(message) {
      run(() => {
        this._messageReceived(message);
      });
    });
  },

  _disconnect() {
    this.get('socket').removeAllListeners("emberInspectorMessage");
  },

  connect() {
    return new Promise((resolve, reject) => {
      $(() => {
        if (this.isDestroyed) { reject(); }
        const EMBER_INSPECTOR_CONFIG = window.EMBER_INSPECTOR_CONFIG;
        if (typeof EMBER_INSPECTOR_CONFIG === 'object' && EMBER_INSPECTOR_CONFIG.remoteDebugSocket) {
          resolve();
        }
      });
    }).then(() => {
      this._listen();
    });
  },

  willDestroy() {
    this._disconnect();
  }
});

<commit_msg>Fix websocket adapter ES6 conversion<commit_after>import BasicAdapter from "./basic";
const Ember = window.Ember;
const { $, computed, run, RSVP: { Promise } } = Ember;

export default BasicAdapter.extend({

  sendMessage(options) {
    options = options || {};
    this.get('socket').emit('emberInspectorMessage', options);
  },

  socket: computed(function() {
    return window.EMBER_INSPECTOR_CONFIG.remoteDebugSocket;
  }),

  _listen() {
    this.get('socket').on('emberInspectorMessage', message => {
      run(() => {
        this._messageReceived(message);
      });
    });
  },

  _disconnect() {
    this.get('socket').removeAllListeners("emberInspectorMessage");
  },

  connect() {
    return new Promise((resolve, reject) => {
      $(() => {
        if (this.isDestroyed) { reject(); }
        const EMBER_INSPECTOR_CONFIG = window.EMBER_INSPECTOR_CONFIG;
        if (typeof EMBER_INSPECTOR_CONFIG === 'object' && EMBER_INSPECTOR_CONFIG.remoteDebugSocket) {
          resolve();
        }
      });
    }).then(() => {
      this._listen();
    });
  },

  willDestroy() {
    this._disconnect();
  }
});

<|endoftext|>


<commit_before>import Color from './color';
import {CMYK} from './space';

const KEY = Object.freeze({
  'C': Symbol.for('c'),
  'M': Symbol.for('m'),
  'Y': Symbol.for('y'),
  'K': Symbol.for('k')
});

export default class Cmyk extends Color {
  constructor(c, m, y, k) {
    super(CMYK, [[KEY.C, c], [KEY.M, m], [KEY.Y, y], [KEY.K, k]]);
  }

  isValid() {
    for (let value of this.values()) {
      if (!Number.isFinite(value)) {
        return false;
      }
      if (value < 0 || value > 1) {
        return false;
      }
    }
    return true;
  }

  c(value = null) {
    return this.access(KEY.C, value);
  }

  m(value = null) {
    return this.access(KEY.M, value);
  }

  y(value = null) {
    return this.access(KEY.Y, value);
  }

  k(value = null) {
    return this.access(KEY.K, value);
  }
}
<commit_msg>Added a conversion method that converts from CMYK to byte array<commit_after>import Color from './color';
import {CMYK} from './space';

const KEY = Object.freeze({
  'C': Symbol.for('c'),
  'M': Symbol.for('m'),
  'Y': Symbol.for('y'),
  'K': Symbol.for('k')
});

export default class Cmyk extends Color {
  constructor(c, m, y, k) {
    super(CMYK, [[KEY.C, c], [KEY.M, m], [KEY.Y, y], [KEY.K, k]]);
  }

  toBytes() {
    let k = this.k();
    if (k === 1) {
      return [0, 0, 0, 0];
    }
    return [
      Math.floor(0xFF * (1 - this.c()) * (1 - k)),
      Math.floor(0xFF * (1 - this.m()) * (1 - k)),
      Math.floor(0xFF * (1 - this.y()) * (1 - k)),
      0
    ];
  }

  isValid() {
    for (let value of this.values()) {
      if (!Number.isFinite(value)) {
        return false;
      }
      if (value < 0 || value > 1) {
        return false;
      }
    }
    return true;
  }

  c(value = null) {
    return this.access(KEY.C, value);
  }

  m(value = null) {
    return this.access(KEY.M, value);
  }

  y(value = null) {
    return this.access(KEY.Y, value);
  }

  k(value = null) {
    return this.access(KEY.K, value);
  }
}
<|endoftext|>


<commit_before>'use strict';

var CallbackStore = require('famous-utilities').CallbackStore;
var Vec2 = require('famous-math').Vec2;

var VEC_REGISTER = new Vec2();

var gestures = {drag: true, tap: true, rotate: true, pinch: true};
var progressbacks = [_processPointerStart, _processPointerMove, _processPointerEnd];

var touchEvents = ['touchstart', 'touchmove', 'touchend'];
var mouseEvents = ['mousedown', 'mousemove', 'mouseup'];
var methods = ['preventDefault'];
var touchProperties = [{targetTouches: {0: ['pageX', 'pageY', 'identifier'], 1: ['pageX', 'pageY', 'identifier']}}];
var mouseProperties = ['pageX', 'pageY'];

/**
 * Component to manage gesture events. Will track 'pinch', 'rotate', 'tap', and 'drag' events, on an
 * as-requested basis.
 *
 * @class GestureHandler
 * @param {LocalDispatch} dispatch The dispatch with which to register the handler.
 * @param {Object[]} events An array of event objects specifying .event and .callback properties.
 */

function GestureHandler (dispatch, events) {
    this.dispatch = dispatch;

    this.last1 = new Vec2();
    this.last2 = new Vec2();

    this.delta1 = new Vec2();
    this.delta2 = new Vec2();

    this.velocity1 = new Vec2();
    this.velocity2 = new Vec2();

    this.dist = 0;
    this.diff12 = new Vec2();

    this.center = new Vec2();
    this.centerDelta = new Vec2();
    this.centerVelocity = new Vec2();

    this.pointer1 = {
            position: this.last1,
            delta: this.delta1,
            velocity: this.velocity1,
    };

    this.pointer2 = {
            position: this.last2,
            delta: this.delta2,
            velocity: this.velocity2,
    };

    this.event = {
        status: null,
        time: 0,
        pointers: [],
        center: this.center,
        centerDelta: this.centerDelta,
        centerVelocity: this.centerVelocity,
        points: 0,
        current: 0
    };

    this.trackedPointerIDs = [-1, -1];
    this.timeOfPointer = 0;
    this.multiTap = 0;

    this.mice = [];

    this.gestures = [];
    this.options = {};
    this.trackedGestures = {};

    this._events = new CallbackStore();
    for (var i = 0, len = events.length; i < len; i++) {
        var gesture = events[i].event;
        var callback = events[i].callback;
        if (gestures[gesture]) {
            this.trackedGestures[gesture] = true;
            this.gestures.push(gesture);
            if (events[i].event) this.options[gesture] = events[i];
            this._events.on(gesture, callback);
        }
    }

    var renderables = dispatch.getRenderables();
    for (var i = 0, len = renderables.length; i < len; i++) {
        for (var j = 0; j < 3; j++) {
            var touchEvent = touchEvents[j];
            var mouseEvent = mouseEvents[j];
            if (renderables[i].on) renderables[i].on(touchEvent, methods, touchProperties);
            dispatch.registerTargetedEvent(touchEvent, progressbacks[j].bind(this));
            if (renderables[i].on) renderables[i].on(mouseEvent, methods, mouseProperties);
            dispatch.registerTargetedEvent(mouseEvent, progressbacks[j].bind(this));
        }
        if (renderables[i].on) renderables[i].on('mouseleave', methods, mouseProperties);
        dispatch.registerTargetedEvent('mouseleave', _processMouseLeave.bind(this));
    }
}

/**
 * Returns the name of GestureHandler as a string.
 *
 * @method toString
 * @static
 * @return {String} 'GestureHandler'
 */
GestureHandler.toString = function toString() {
    return 'GestureHandler';
};

/**
 * Trigger gestures in the order they were requested, if they occured.
 *
 * @method triggerGestures
 */
GestureHandler.prototype.triggerGestures = function() {
    var payload = this.event;
    for (var i = 0, len = this.gestures.length; i < len; i++) {
        var gesture = this.gestures[i];
        switch (gesture) {
            case 'rotate':
            case 'pinch':
                if (payload.points === 2) this.trigger(gesture, payload);
                break;
            case 'tap':
                if (payload.status === 'start') {
                    if (this.options['tap']) {
                        var pts = this.options['tap'].points || 1;
                        if(this.multiTap >= pts && payload.points >= pts) this.trigger(gesture, payload);
                    }
                    else this.trigger(gesture, payload);
                }
                break;
            default:
                this.trigger(gesture, payload);
                break;
        }
    }
};

/**
 * Trigger the callback associated with an event, passing in a payload.
 *
 * @method trigger
 * @param {String} ev The event name.
 * @param {Object} payload The event payload.
 */
GestureHandler.prototype.trigger = function trigger (ev, payload) {
    this._events.trigger(ev, payload);
};

/**
 * Process up to the first two touch/mouse move events. Exit out if the first two points are already being tracked.
 *
 * @method _processPointerStart
 * @private
 * @param {Object} e The event object.
 */
function _processPointerStart(e) {
    var t;
    if (!e.targetTouches) {
        this.mice[0] = e;
        t = this.mice;
        e.identifier = 1;
    }
    else t = e.targetTouches;

    if (t[0] && t[1] && this.trackedPointerIDs[0] === t[0].identifier && this.trackedPointerIDs[1] === t[1].identifier) {
        return;
    }

    this.event.time = Date.now();

    if (this.trackedPointerIDs[0] !== t[0].identifier) {
        if (this.trackedGestures['tap']) {
            var threshold = (this.options['tap'] && this.options['tap'].threshold) || 250;
            if (this.event.time - this.timeOfPointer < threshold) this.event.taps++;
            else this.event.taps = 1;
            this.timeOfPointer = this.event.time;
            this.multiTap = 1;
        }
        this.event.current = 1;
        this.event.points = 1;
        var id = t[0].identifier;
        this.trackedPointerIDs[0] = id;

        this.last1.set(t[0].pageX, t[0].pageY);
        this.velocity1.clear();
        this.delta1.clear();
        this.event.pointers.push(this.pointer1);
    }
    if (t[1] && this.trackedPointerIDs[1] !== t[1].identifier) {
        if (this.trackedGestures['tap']) {
            var threshold = (this.options['tap'] && this.options['tap'].threshold) || 250;
            if (this.event.time - this.timeOfPointer < threshold) this.multiTap = 2;
        }
        this.event.current = 2;
        this.event.points = 2;
        var id = t[1].identifier;
        this.trackedPointerIDs[1] = id;

        this.last2.set(t[1].pageX, t[1].pageY);
        this.velocity2.clear();
        this.delta2.clear();

        Vec2.add(this.last1, this.last2, this.center).scale(0.5);
        this.centerDelta.clear();
        this.centerVelocity.clear();

        Vec2.subtract(this.last2, this.last1, this.diff12);
        this.dist = this.diff12.length();

        if (this.trackedGestures['pinch']) {
            this.event.scale = this.event.scale || 1;
            this.event.scaleDelta = 0;
            this.event.scaleVelocity = 0;
        }
        if (this.trackedGestures['rotate']) {
            this.event.rotation = this.event.rotation || 0;
            this.event.rotationDelta = 0;
            this.event.rotationVelocity = 0;
        }
        this.event.pointers.push(this.pointer2);
    }

    this.event.status = 'start';
    if (this.event.points === 1) {
        this.center.copy(this.last1);
        this.centerDelta.clear();
        this.centerVelocity.clear();
        if (this.trackedGestures['pinch']) {
            this.event.scale = 1;
            this.event.scaleDelta = 0;
            this.event.scaleVelocity = 0;
        }
        if (this.trackedGestures['rotate']) {
            this.event.rotation = 0;
            this.event.rotationDelta = 0;
            this.event.rotationVelocity = 0;
        }
    }
    this.triggerGestures();
}

/**
 * Process up to the first two touch/mouse move events.
 *
 * @method _processPointerMove
 * @private
 * @param {Object} e The event object.
 */
function _processPointerMove(e) {
    var t;
    if (!e.targetTouches) {
        if (!this.event.current) return;
        this.mice[0] = e;
        t = this.mice;
        e.identifier = 1;
    }
    else t = e.targetTouches;

    var time = Date.now();
    var dt = time - this.event.time;
    if (dt === 0) return;
    var invDt = 1000 / dt;
    this.event.time = time;

    this.event.current = 1;
    this.event.points = 1;
    if (this.trackedPointerIDs[0] === t[0].identifier) {
        VEC_REGISTER.set(t[0].pageX, t[0].pageY);
        Vec2.subtract(VEC_REGISTER, this.last1, this.delta1);
        Vec2.scale(this.delta1, invDt, this.velocity1);
        this.last1.copy(VEC_REGISTER);

    }
    if (t[1]) {
        this.event.current = 2;
        this.event.points = 2;
        VEC_REGISTER.set(t[1].pageX, t[1].pageY);
        Vec2.subtract(VEC_REGISTER, this.last2, this.delta2);
        Vec2.scale(this.delta2, invDt, this.velocity2);
        this.last2.copy(VEC_REGISTER);

        Vec2.add(this.last1, this.last2, VEC_REGISTER).scale(0.5);
        Vec2.subtract(VEC_REGISTER, this.center, this.centerDelta);
        Vec2.add(this.velocity1, this.velocity2, this.centerVelocity).scale(0.5);
        this.center.copy(VEC_REGISTER);

        Vec2.subtract(this.last2, this.last1, VEC_REGISTER);

        if (this.trackedGestures['rotate']) {
            var dot = VEC_REGISTER.dot(this.diff12);
            var cross = VEC_REGISTER.cross(this.diff12);
            var theta = -Math.atan2(cross, dot);
            this.event.rotation += theta;
            this.event.rotationDelta = theta;
            this.event.rotationVelocity = theta * invDt;
        }

        var dist = VEC_REGISTER.length();
        var scale = dist / this.dist;
        this.diff12.copy(VEC_REGISTER);
        this.dist = dist;

        if (this.trackedGestures['pinch']) {
            this.event.scale *= scale;
            scale -= 1.0;
            this.event.scaleDelta = scale;
            this.event.scaleVelocity = scale * invDt;
        }
    }

    this.event.status = 'move';
    if (this.event.points === 1) {
        this.center.copy(this.last1);
        this.centerDelta.copy(this.delta1);
        this.centerVelocity.copy(this.velocity1);
        if (this.trackedGestures['pinch']) {
            this.event.scale = 1;
            this.event.scaleDelta = 0;
            this.event.scaleVelocity = 0;
        }
        if (this.trackedGestures['rotate']) {
            this.event.rotation = 0;
            this.event.rotationDelta = 0;
            this.event.rotationVelocity = 0;
        }
    }
    this.triggerGestures();
}

/**
 * Process up to the first two touch/mouse end events. Exit out if the two points being tracked are still active.
 *
 * @method _processPointerEnd
 * @private
 * @param {Object} e The event object.
 */
function _processPointerEnd(e) {
    var t;
    if (!e.targetTouches) {
        if (!this.event.current) return;
        this.mice.pop();
        t = this.mice;
    }
    else t = e.targetTouches;

    if (t[0] && t[1] && this.trackedPointerIDs[0] === t[0].identifier && this.trackedPointerIDs[1] === t[1].identifier) {
            return;
    }

    this.event.status = 'end';
    if (!t[0]) {
        this.event.current = 0;
        this.trackedPointerIDs[0] = -1;
        this.trackedPointerIDs[1] = -1;
        this.triggerGestures();
        this.event.pointers.pop();
        this.event.pointers.pop();
        return;
    }
    else if(this.trackedPointerIDs[0] !== t[0].identifier) {
        this.trackedPointerIDs[0] = -1;
        var id = t[0].identifier;
        this.trackedPointerIDs[0] = id;

        this.last1.set(t[0].pageX, t[0].pageY);
        this.velocity1.clear();
        this.delta1.clear();
    }
    if (!t[1]) {
        this.event.current = 1;
        this.trackedPointerIDs[1] = -1;
        this.triggerGestures();
        this.event.points = 1;
        this.event.pointers.pop();
    }
    else if (this.trackedPointerIDs[1] !== t[1].identifier) {
        this.trackedPointerIDs[1] = -1;
        this.event.points = 2;
        var id = t[1].identifier;
        this.trackedPointerIDs[1] = id;

        this.last2.set(t[1].pageX, t[1].pageY);
        this.velocity2.clear();
        this.delta2.clear();

        Vec2.add(this.last1, this.last2, this.center).scale(0.5);
        this.centerDelta.clear();
        this.centerVelocity.clear();

        Vec2.subtract(this.last2, this.last1, this.diff12);
        this.dist = this.diff12.length();
    }
}

/**
 * Treats a mouseleave event as a gesture end.
 *
 * @method _processMouseLeave
 * @private
 * @param {Object} e The event object.
 */
function _processMouseLeave(e) {
    if (this.event.current) {
        this.event.status = 'end';
        this.event.current = 0;
        this.trackedPointerIDs[0] = -1;
        this.triggerGestures();
        this.event.pointers.pop();
    }
}

module.exports = GestureHandler;
<commit_msg>feature: updated GestureHandler to new Core<commit_after>'use strict';

var CallbackStore = require('famous-utilities').CallbackStore;
var Vec2 = require('famous-math').Vec2;

var VEC_REGISTER = new Vec2();

var gestures = {drag: true, tap: true, rotate: true, pinch: true};
var progressbacks = [_processPointerStart, _processPointerMove, _processPointerEnd];

var touchEvents = ['touchstart', 'touchmove', 'touchend'];
var mouseEvents = ['mousedown', 'mousemove', 'mouseup'];
var methods = ['preventDefault'];
var touchProperties = [{targetTouches: {0: ['pageX', 'pageY', 'identifier'], 1: ['pageX', 'pageY', 'identifier']}}];
var mouseProperties = ['pageX', 'pageY'];

/**
 * Component to manage gesture events. Will track 'pinch', 'rotate', 'tap', and 'drag' events, on an
 * as-requested basis.
 *
 * @class GestureHandler
 * @param {LocalDispatch} node The node with which to register the handler.
 * @param {Object[]} events An array of event objects specifying .event and .callback properties.
 */

function GestureHandler (node, events) {
    this.node = node;

    this.last1 = new Vec2();
    this.last2 = new Vec2();

    this.delta1 = new Vec2();
    this.delta2 = new Vec2();

    this.velocity1 = new Vec2();
    this.velocity2 = new Vec2();

    this.dist = 0;
    this.diff12 = new Vec2();

    this.center = new Vec2();
    this.centerDelta = new Vec2();
    this.centerVelocity = new Vec2();

    this.pointer1 = {
            position: this.last1,
            delta: this.delta1,
            velocity: this.velocity1,
    };

    this.pointer2 = {
            position: this.last2,
            delta: this.delta2,
            velocity: this.velocity2,
    };

    this.event = {
        status: null,
        time: 0,
        pointers: [],
        center: this.center,
        centerDelta: this.centerDelta,
        centerVelocity: this.centerVelocity,
        points: 0,
        current: 0
    };

    this.trackedPointerIDs = [-1, -1];
    this.timeOfPointer = 0;
    this.multiTap = 0;

    this.mice = [];

    this.gestures = [];
    this.options = {};
    this.trackedGestures = {};

    this._events = new CallbackStore();
    for (var i = 0, len = events.length; i < len; i++) {
        var gesture = events[i].event;
        var callback = events[i].callback;
        if (gestures[gesture]) {
            this.trackedGestures[gesture] = true;
            this.gestures.push(gesture);
            if (events[i].event) this.options[gesture] = events[i];
            this._events.on(gesture, callback);
        }
    }

    for (var i = 0 ; i < 3 ; i++) {
        var touchEvent = touchEvents[j];
        var mouseEvents = mouseEvents[j];
        node.addUIEvent(touchEvent);
        node.addUIEvent(mouseEvent);
    }

    node.addUIEvent('mouseleave');

    /*
    var renderables = node.getRenderables();
    for (var i = 0, len = renderables.length; i < len; i++) {
        for (var j = 0; j < 3; j++) {
            var touchEvent = touchEvents[j];
            var mouseEvent = mouseEvents[j];
            if (renderables[i].on) renderables[i].on(touchEvent, methods, touchProperties);
            node.registerTargetedEvent(touchEvent, progressbacks[j].bind(this));
            if (renderables[i].on) renderables[i].on(mouseEvent, methods, mouseProperties);
            node.registerTargetedEvent(mouseEvent, progressbacks[j].bind(this));
        }
        if (renderables[i].on) renderables[i].on('mouseleave', methods, mouseProperties);
        node.registerTargetedEvent('mouseleave', _processMouseLeave.bind(this));
    }
    */
}

GestureHandler.prototype.onReceive = function onReceive (event, payload) {
    var index = mouseEvents.indexOf(event);
    index = index !== -1 ? index : touchEvents.indexOf(event);

    if (index !== -1) progressbacks[index].call(this, payload);
    else if (event === 'mouseleave') _processMouseLeave.call(this, payload);
};

/**
 * Returns the name of GestureHandler as a string.
 *
 * @method toString
 * @static
 * @return {String} 'GestureHandler'
 */
GestureHandler.toString = function toString() {
    return 'GestureHandler';
};

/**
 * Trigger gestures in the order they were requested, if they occured.
 *
 * @method triggerGestures
 */
GestureHandler.prototype.triggerGestures = function() {
    var payload = this.event;
    for (var i = 0, len = this.gestures.length; i < len; i++) {
        var gesture = this.gestures[i];
        switch (gesture) {
            case 'rotate':
            case 'pinch':
                if (payload.points === 2) this.trigger(gesture, payload);
                break;
            case 'tap':
                if (payload.status === 'start') {
                    if (this.options['tap']) {
                        var pts = this.options['tap'].points || 1;
                        if(this.multiTap >= pts && payload.points >= pts) this.trigger(gesture, payload);
                    }
                    else this.trigger(gesture, payload);
                }
                break;
            default:
                this.trigger(gesture, payload);
                break;
        }
    }
};

/**
 * Trigger the callback associated with an event, passing in a payload.
 *
 * @method trigger
 * @param {String} ev The event name.
 * @param {Object} payload The event payload.
 */
GestureHandler.prototype.trigger = function trigger (ev, payload) {
    this._events.trigger(ev, payload);
};

/**
 * Process up to the first two touch/mouse move events. Exit out if the first two points are already being tracked.
 *
 * @method _processPointerStart
 * @private
 * @param {Object} e The event object.
 */
function _processPointerStart(e) {
    var t;
    if (!e.targetTouches) {
        this.mice[0] = e;
        t = this.mice;
        e.identifier = 1;
    }
    else t = e.targetTouches;

    if (t[0] && t[1] && this.trackedPointerIDs[0] === t[0].identifier && this.trackedPointerIDs[1] === t[1].identifier) {
        return;
    }

    this.event.time = Date.now();

    if (this.trackedPointerIDs[0] !== t[0].identifier) {
        if (this.trackedGestures['tap']) {
            var threshold = (this.options['tap'] && this.options['tap'].threshold) || 250;
            if (this.event.time - this.timeOfPointer < threshold) this.event.taps++;
            else this.event.taps = 1;
            this.timeOfPointer = this.event.time;
            this.multiTap = 1;
        }
        this.event.current = 1;
        this.event.points = 1;
        var id = t[0].identifier;
        this.trackedPointerIDs[0] = id;

        this.last1.set(t[0].pageX, t[0].pageY);
        this.velocity1.clear();
        this.delta1.clear();
        this.event.pointers.push(this.pointer1);
    }
    if (t[1] && this.trackedPointerIDs[1] !== t[1].identifier) {
        if (this.trackedGestures['tap']) {
            var threshold = (this.options['tap'] && this.options['tap'].threshold) || 250;
            if (this.event.time - this.timeOfPointer < threshold) this.multiTap = 2;
        }
        this.event.current = 2;
        this.event.points = 2;
        var id = t[1].identifier;
        this.trackedPointerIDs[1] = id;

        this.last2.set(t[1].pageX, t[1].pageY);
        this.velocity2.clear();
        this.delta2.clear();

        Vec2.add(this.last1, this.last2, this.center).scale(0.5);
        this.centerDelta.clear();
        this.centerVelocity.clear();

        Vec2.subtract(this.last2, this.last1, this.diff12);
        this.dist = this.diff12.length();

        if (this.trackedGestures['pinch']) {
            this.event.scale = this.event.scale || 1;
            this.event.scaleDelta = 0;
            this.event.scaleVelocity = 0;
        }
        if (this.trackedGestures['rotate']) {
            this.event.rotation = this.event.rotation || 0;
            this.event.rotationDelta = 0;
            this.event.rotationVelocity = 0;
        }
        this.event.pointers.push(this.pointer2);
    }

    this.event.status = 'start';
    if (this.event.points === 1) {
        this.center.copy(this.last1);
        this.centerDelta.clear();
        this.centerVelocity.clear();
        if (this.trackedGestures['pinch']) {
            this.event.scale = 1;
            this.event.scaleDelta = 0;
            this.event.scaleVelocity = 0;
        }
        if (this.trackedGestures['rotate']) {
            this.event.rotation = 0;
            this.event.rotationDelta = 0;
            this.event.rotationVelocity = 0;
        }
    }
    this.triggerGestures();
}

/**
 * Process up to the first two touch/mouse move events.
 *
 * @method _processPointerMove
 * @private
 * @param {Object} e The event object.
 */
function _processPointerMove(e) {
    var t;
    if (!e.targetTouches) {
        if (!this.event.current) return;
        this.mice[0] = e;
        t = this.mice;
        e.identifier = 1;
    }
    else t = e.targetTouches;

    var time = Date.now();
    var dt = time - this.event.time;
    if (dt === 0) return;
    var invDt = 1000 / dt;
    this.event.time = time;

    this.event.current = 1;
    this.event.points = 1;
    if (this.trackedPointerIDs[0] === t[0].identifier) {
        VEC_REGISTER.set(t[0].pageX, t[0].pageY);
        Vec2.subtract(VEC_REGISTER, this.last1, this.delta1);
        Vec2.scale(this.delta1, invDt, this.velocity1);
        this.last1.copy(VEC_REGISTER);

    }
    if (t[1]) {
        this.event.current = 2;
        this.event.points = 2;
        VEC_REGISTER.set(t[1].pageX, t[1].pageY);
        Vec2.subtract(VEC_REGISTER, this.last2, this.delta2);
        Vec2.scale(this.delta2, invDt, this.velocity2);
        this.last2.copy(VEC_REGISTER);

        Vec2.add(this.last1, this.last2, VEC_REGISTER).scale(0.5);
        Vec2.subtract(VEC_REGISTER, this.center, this.centerDelta);
        Vec2.add(this.velocity1, this.velocity2, this.centerVelocity).scale(0.5);
        this.center.copy(VEC_REGISTER);

        Vec2.subtract(this.last2, this.last1, VEC_REGISTER);

        if (this.trackedGestures['rotate']) {
            var dot = VEC_REGISTER.dot(this.diff12);
            var cross = VEC_REGISTER.cross(this.diff12);
            var theta = -Math.atan2(cross, dot);
            this.event.rotation += theta;
            this.event.rotationDelta = theta;
            this.event.rotationVelocity = theta * invDt;
        }

        var dist = VEC_REGISTER.length();
        var scale = dist / this.dist;
        this.diff12.copy(VEC_REGISTER);
        this.dist = dist;

        if (this.trackedGestures['pinch']) {
            this.event.scale *= scale;
            scale -= 1.0;
            this.event.scaleDelta = scale;
            this.event.scaleVelocity = scale * invDt;
        }
    }

    this.event.status = 'move';
    if (this.event.points === 1) {
        this.center.copy(this.last1);
        this.centerDelta.copy(this.delta1);
        this.centerVelocity.copy(this.velocity1);
        if (this.trackedGestures['pinch']) {
            this.event.scale = 1;
            this.event.scaleDelta = 0;
            this.event.scaleVelocity = 0;
        }
        if (this.trackedGestures['rotate']) {
            this.event.rotation = 0;
            this.event.rotationDelta = 0;
            this.event.rotationVelocity = 0;
        }
    }
    this.triggerGestures();
}

/**
 * Process up to the first two touch/mouse end events. Exit out if the two points being tracked are still active.
 *
 * @method _processPointerEnd
 * @private
 * @param {Object} e The event object.
 */
function _processPointerEnd(e) {
    var t;
    if (!e.targetTouches) {
        if (!this.event.current) return;
        this.mice.pop();
        t = this.mice;
    }
    else t = e.targetTouches;

    if (t[0] && t[1] && this.trackedPointerIDs[0] === t[0].identifier && this.trackedPointerIDs[1] === t[1].identifier) {
            return;
    }

    this.event.status = 'end';
    if (!t[0]) {
        this.event.current = 0;
        this.trackedPointerIDs[0] = -1;
        this.trackedPointerIDs[1] = -1;
        this.triggerGestures();
        this.event.pointers.pop();
        this.event.pointers.pop();
        return;
    }
    else if(this.trackedPointerIDs[0] !== t[0].identifier) {
        this.trackedPointerIDs[0] = -1;
        var id = t[0].identifier;
        this.trackedPointerIDs[0] = id;

        this.last1.set(t[0].pageX, t[0].pageY);
        this.velocity1.clear();
        this.delta1.clear();
    }
    if (!t[1]) {
        this.event.current = 1;
        this.trackedPointerIDs[1] = -1;
        this.triggerGestures();
        this.event.points = 1;
        this.event.pointers.pop();
    }
    else if (this.trackedPointerIDs[1] !== t[1].identifier) {
        this.trackedPointerIDs[1] = -1;
        this.event.points = 2;
        var id = t[1].identifier;
        this.trackedPointerIDs[1] = id;

        this.last2.set(t[1].pageX, t[1].pageY);
        this.velocity2.clear();
        this.delta2.clear();

        Vec2.add(this.last1, this.last2, this.center).scale(0.5);
        this.centerDelta.clear();
        this.centerVelocity.clear();

        Vec2.subtract(this.last2, this.last1, this.diff12);
        this.dist = this.diff12.length();
    }
}

/**
 * Treats a mouseleave event as a gesture end.
 *
 * @method _processMouseLeave
 * @private
 * @param {Object} e The event object.
 */
function _processMouseLeave(e) {
    if (this.event.current) {
        this.event.status = 'end';
        this.event.current = 0;
        this.trackedPointerIDs[0] = -1;
        this.triggerGestures();
        this.event.pointers.pop();
    }
}

module.exports = GestureHandler;
<|endoftext|>


<commit_before>/*!
 * Copyright (c) 2015-2017 Cisco Systems, Inc. See LICENSE file.
 */

/* eslint-env browser */

import localforage from 'localforage';

import {NotFoundError} from '@ciscospark/spark-core';

const namespaces = new WeakMap();
const loggers = new WeakMap();

/**
* IndexedDB adapter for spark-core storage layer
*/
export default class StorageAdapterLocalForage {
  /**
   * @constructs {StorageAdapterLocalForage}
   * @param {string} basekey localforage key under which
   * all namespaces will be stored
   */
  constructor() {
    /**
     * localforage binding
     */
    this.Bound = class {
      /**
       * @constructs {Bound}
       * @param {string} namespace
       * @param {Object} options
       */
      constructor(namespace, options) {
        namespaces.set(this, namespace);
        loggers.set(this, options.logger);
      }

      /**
       * Clears the localforage
       * @param {string} key
       * @returns {Promise}
       */
      clear() {
        loggers.get(this).info(`local-forage-store-adapter: clearing localforage`);
        return Promise.resolve(localforage.clear());
      }

      /**
       * Removes the specified key
       * @param {string} key
       * @returns {Promise}
       */
      del(key) {
        const key_ = `${namespaces.get(this)}/${key}`;
        loggers.get(this).info(`local-forage-store-adapter: deleting \`${key_}\``);
        return localforage.removeItem(key_);
      }

      /**
       * Retrieves the data at the specified key
       * @param {string} key
       * @see https://localforage.github.io/localForage/#data-api-getitem
       * @returns {Promise<mixed>}
       */
      get(key) {
        const key_ = `${namespaces.get(this)}/${key}`;
        loggers.get(this).info(`local-forage-store-adapter: reading \`${key_}\``);
        return localforage.getItem(key_)
          .then((value) => {
            // if the key does not exist, getItem() will return null
            if (value === null) {
              return localforage.keys()
                .then((keys) => {
                  if (keys.indexOf(key_) > -1) {
                    return Promise.resolve(value);
                  }
                  return Promise.reject(new NotFoundError(`No value found for ${key_}`));
                });
            }
            //  even if undefined is saved, null will be returned by getItem()
            return Promise.resolve(value);
          });
      }

      /**
       * Stores the specified value at the specified key.
       * If key is undefined, removes the specified key.
       * @param {string} key
       * @param {mixed} value
       * @returns {Promise}
       */
      put(key, value) {
        if (typeof value === `undefined`) {
          return this.del(key);
        }
        const key_ = `${namespaces.get(this)}/${key}`;
        loggers.get(this).info(`local-forage-store-adapter: writing \`${key_}\``);
        return localforage.setItem(key_, value);
      }
    };
  }

  /**
  * Returns an adapter bound to the specified namespace
  * @param {string} namespace
  * @param {Object} options
  * @returns {Promise<Bound>}
  */
  bind(namespace, options) {
    options = options || {};
    if (!namespace) {
      return Promise.reject(new Error(`\`namespace\` is required`));
    }

    if (!options.logger) {
      return Promise.reject(new Error(`\`options.logger\` is required`));
    }

    options.logger.info(`local-forage-store-adapter: returning binding`);

    return Promise.resolve(new this.Bound(namespace, options));
  }
}
<commit_msg>refactor(@ciscospark/s-a-local-forage): reduce log level<commit_after>/*!
 * Copyright (c) 2015-2017 Cisco Systems, Inc. See LICENSE file.
 */

/* eslint-env browser */

import localforage from 'localforage';

import {NotFoundError} from '@ciscospark/spark-core';

const namespaces = new WeakMap();
const loggers = new WeakMap();

/**
* IndexedDB adapter for spark-core storage layer
*/
export default class StorageAdapterLocalForage {
  /**
   * @constructs {StorageAdapterLocalForage}
   * @param {string} basekey localforage key under which
   * all namespaces will be stored
   */
  constructor() {
    /**
     * localforage binding
     */
    this.Bound = class {
      /**
       * @constructs {Bound}
       * @param {string} namespace
       * @param {Object} options
       */
      constructor(namespace, options) {
        namespaces.set(this, namespace);
        loggers.set(this, options.logger);
      }

      /**
       * Clears the localforage
       * @param {string} key
       * @returns {Promise}
       */
      clear() {
        loggers.get(this).debug(`storage-adapter-local-forage: clearing localforage`);
        return Promise.resolve(localforage.clear());
      }

      /**
       * Removes the specified key
       * @param {string} key
       * @returns {Promise}
       */
      del(key) {
        const key_ = `${namespaces.get(this)}/${key}`;
        loggers.get(this).debug(`storage-adapter-local-forage: deleting \`${key_}\``);
        return localforage.removeItem(key_);
      }

      /**
       * Retrieves the data at the specified key
       * @param {string} key
       * @see https://localforage.github.io/localForage/#data-api-getitem
       * @returns {Promise<mixed>}
       */
      get(key) {
        const key_ = `${namespaces.get(this)}/${key}`;
        loggers.get(this).debug(`storage-adapter-local-forage: reading \`${key_}\``);
        return localforage.getItem(key_)
          .then((value) => {
            // if the key does not exist, getItem() will return null
            if (value === null) {
              return localforage.keys()
                .then((keys) => {
                  if (keys.indexOf(key_) > -1) {
                    return Promise.resolve(value);
                  }
                  return Promise.reject(new NotFoundError(`No value found for ${key_}`));
                });
            }
            //  even if undefined is saved, null will be returned by getItem()
            return Promise.resolve(value);
          });
      }

      /**
       * Stores the specified value at the specified key.
       * If key is undefined, removes the specified key.
       * @param {string} key
       * @param {mixed} value
       * @returns {Promise}
       */
      put(key, value) {
        if (typeof value === `undefined`) {
          return this.del(key);
        }
        const key_ = `${namespaces.get(this)}/${key}`;
        loggers.get(this).debug(`storage-adapter-local-forage: writing \`${key_}\``);
        return localforage.setItem(key_, value);
      }
    };
  }

  /**
  * Returns an adapter bound to the specified namespace
  * @param {string} namespace
  * @param {Object} options
  * @returns {Promise<Bound>}
  */
  bind(namespace, options) {
    options = options || {};
    if (!namespace) {
      return Promise.reject(new Error(`\`namespace\` is required`));
    }

    if (!options.logger) {
      return Promise.reject(new Error(`\`options.logger\` is required`));
    }

    options.logger.info(`storage-adapter-local-forage: returning binding`);

    return Promise.resolve(new this.Bound(namespace, options));
  }
}
<|endoftext|>


<commit_before>if (typeof define !== 'function') {
    var define = require('amdefine')(module);
}
define(
// Dependencies
[ 'require', 'underscore', 'mosaic-commons', 'react' ],
// Module
function(require) {

    var Mosaic = require('mosaic-commons');
    var _ = require('underscore');
    var React = require('react');

    /**
     * This is an "infinite scroll" widget allowing to load elements by their
     * position.
     */
    return React.createClass({

        /** The main rendering method. */
        render : function() {
            var items = this.state.items || [];
            var size = this._getSize();
            var h = this._getItemHeight();
            var blockPos = (this.state.index || 0) * h;
            var fullHeight = size * h;

            return React.DOM.div({
                id : this.props.id,
                className : this.props.className,
                onScroll : this._onScroll
            }, React.DOM.div({
                ref : 'wrapper',
                style : {
                    height : fullHeight + 'px',
                    position : 'relative',
                // overflow : 'hidden'
                }
            }, React.DOM.div({
                ref : 'block',
                style : {
                    top : blockPos + 'px',
                    position : 'absolute',
                    left : '0px',
                    right : '0px',
                    bottom : 'auto'
                }
            }, items)));
        },

        // -------------------------------------------------------------------
        // React lifecycle methods
        /**
         * This method creates "debounced" versions of some methods to avoid to
         * frequent calls
         */
        componentWillMount : function(nextProps) {
            this._adjustPosition = _.debounce(this._adjustPosition, 5);
            this._setScrollPos = _.debounce(_.bind(this._setScrollPos, this),
                    30);
        },
        /** Initializes the scroll and set it in an initial position. */
        componentDidMount : function() {
            this._resetScrollPos(this.props);
        },
        /** Updates the position of the scroller. */
        componentWillReceiveProps : function(nextProps) {
            this._resetScrollPos(nextProps);
        },
        /**
         * This method is called after the component is rendered and it adjusts
         * position of the item block.
         */
        componentDidUpdate : function() {
            this._adjustPosition();
        },
        /** Creates and returns the inital state object for this component. */
        getInitialState : function() {
            return this._newState({
                index : 0,
                length : 0,
                items : [],
                scrollPos : 0
            });
        },

        // -------------------------------------------------------------------
        // Internal (private) methods

        /**
         * This is an internal method creating and returning a new state.
         */
        _newState : function(options) {
            return _.extend({}, this.state, options);
        },
        /**
         * This method returns the size of a "page" - size of block of items
         * loaded at once. By default this method returns the
         * "this.props.pageSize" value.
         */
        _getPageSize : function() {
            return this.props.pageSize || 4;
        },
        /**
         * Returns the total number of items to visualize in this scroll.
         */
        _getSize : function() {
            return this.props.length || 0;
        },
        /**
         * Returns an average size of each individual item in the list. This
         * value is used to calculate the total size of the scroll. By default
         * this method returns the "this.props.itemHeight" value.
         */
        _getItemHeight : function() {
            return this.props.itemHeight || 10;
        },
        /**
         * @param params.index
         *            start index of the item to load
         * @param params.length
         *            number of items to load
         * @param params.callback
         *            a callback method accepting the resulting items
         */
        _loadItems : function(params) {
            this.props.loadItems(params);
        },
        /**
         * Sets the scroll in initial position or in an already existing one if
         * the initial position is not defined.
         */
        _resetScrollPos : function(props) {
            props = props || this.props;
            var scrollPos = this.state.scrollPos;
            var focusedPos = props.focusedIndex;
            focusedPos = !isNaN(focusedPos) ? focusedPos : -1;
            var size = this._getSize();
            if (!isNaN(focusedPos) && focusedPos >= 0 && focusedPos < size) {
                var h = this._getItemHeight();
                scrollPos = focusedPos * h;
            }
            this._focusedItemIdx = focusedPos;
            this._setScrollPos(scrollPos, true);
        },
        /**
         * Adjust the absolute position of the items block to reflect exactly
         * the position of the scroller. This method is required because real
         * items sizes could be different from the average size returned by the
         * "_getItemHeight" method.
         */
        _adjustPosition : function() {
            var that = this;
            var container = that.getDOMNode();
            var scrollPos = container.scrollTop;

            var h = that._getItemHeight();
            var pos = scrollPos - that.state.index * h;
            var height = that.state.items.length * h;
            var blockNode = that.refs.block.getDOMNode();
            var blockHeight = blockNode.offsetHeight;

            var blockPos = scrollPos - blockHeight * (pos / height);
            blockPos = Math.round(blockPos);
            blockNode.style.top = blockPos + 'px';

            if (!isNaN(that._focusedItemIdx)) {
                delete that._focusedItemIdx;
                setTimeout(function() {
                    container.scrollTop = that.state.scrollPos;
                }, 30);
            }
        },
        /**
         * Sets the scroller in the specified position and updates the internal
         * state if the content should be re-loaded.
         */
        _setScrollPos : function(scrollPos, force) {
            var that = this;
            var pageSize = that._getPageSize();
            var h = that._getItemHeight();
            var container = that.getDOMNode();

            var windowHeight = container.offsetHeight;
            var delta = windowHeight / 4;

            var startPos = Math.max(scrollPos - delta, 0);
            var endPos = scrollPos + windowHeight + delta;

            var startItem = Math.floor(startPos / h);
            var endItem = Math.ceil((endPos + h - 1) / h);

            var startPage = Math.floor(startItem / pageSize);
            var endPage = Math.ceil((endItem + pageSize - 1) / pageSize);

            var index = startPage * pageSize;
            var length = (endPage - startPage) * pageSize;
            var params = {
                scrollPos : scrollPos,
                index : index,
                length : length
            };
            if (force || params.index != that.state.index
                    || params.length != that.state.length) {
                params.callback = function(items) {
                    params.items = items;
                    that.setState(that._newState(params));
                }
                that._loadItems(params);
            } else {
                that._adjustPosition();
            }
        },
        /** This handler is called when the scroller changes its position. */
        _onScroll : function(event) {
            this._setScrollPos(this.getDOMNode().scrollTop);
        },

    });

});<commit_msg>Add styles parameter to the inifite scroll<commit_after>if (typeof define !== 'function') {
    var define = require('amdefine')(module);
}
define(
// Dependencies
[ 'require', 'underscore', 'mosaic-commons', 'react' ],
// Module
function(require) {

    var Mosaic = require('mosaic-commons');
    var _ = require('underscore');
    var React = require('react');

    /**
     * This is an "infinite scroll" widget allowing to load elements by their
     * position.
     */
    return React.createClass({

        /** The main rendering method. */
        render : function() {
            var items = this.state.items || [];
            var size = this._getSize();
            var h = this._getItemHeight();
            var blockPos = (this.state.index || 0) * h;
            var fullHeight = size * h;

            return React.DOM.div({
                id : this.props.id,
                className : this.props.className,
                style : this.props.style,
                onScroll : this._onScroll
            }, React.DOM.div({
                ref : 'wrapper',
                style : {
                    height : fullHeight + 'px',
                    position : 'relative',
                // overflow : 'hidden'
                }
            }, React.DOM.div({
                ref : 'block',
                style : {
                    top : blockPos + 'px',
                    position : 'absolute',
                    left : '0px',
                    right : '0px',
                    bottom : 'auto'
                }
            }, items)));
        },

        // -------------------------------------------------------------------
        // React lifecycle methods
        /**
         * This method creates "debounced" versions of some methods to avoid to
         * frequent calls
         */
        componentWillMount : function(nextProps) {
            this._adjustPosition = _.debounce(this._adjustPosition, 5);
            this._setScrollPos = _.debounce(_.bind(this._setScrollPos, this),
                    30);
        },
        /** Initializes the scroll and set it in an initial position. */
        componentDidMount : function() {
            this._resetScrollPos(this.props);
        },
        /** Updates the position of the scroller. */
        componentWillReceiveProps : function(nextProps) {
            this._resetScrollPos(nextProps);
        },
        /**
         * This method is called after the component is rendered and it adjusts
         * position of the item block.
         */
        componentDidUpdate : function() {
            this._adjustPosition();
        },
        /** Creates and returns the inital state object for this component. */
        getInitialState : function() {
            return this._newState({
                index : 0,
                length : 0,
                items : [],
                scrollPos : 0
            });
        },

        // -------------------------------------------------------------------
        // Internal (private) methods

        /**
         * This is an internal method creating and returning a new state.
         */
        _newState : function(options) {
            return _.extend({}, this.state, options);
        },
        /**
         * This method returns the size of a "page" - size of block of items
         * loaded at once. By default this method returns the
         * "this.props.pageSize" value.
         */
        _getPageSize : function() {
            return this.props.pageSize || 4;
        },
        /**
         * Returns the total number of items to visualize in this scroll.
         */
        _getSize : function() {
            return this.props.length || 0;
        },
        /**
         * Returns an average size of each individual item in the list. This
         * value is used to calculate the total size of the scroll. By default
         * this method returns the "this.props.itemHeight" value.
         */
        _getItemHeight : function() {
            return this.props.itemHeight || 10;
        },
        /**
         * @param params.index
         *            start index of the item to load
         * @param params.length
         *            number of items to load
         * @param params.callback
         *            a callback method accepting the resulting items
         */
        _loadItems : function(params) {
            this.props.loadItems(params);
        },
        /**
         * Sets the scroll in initial position or in an already existing one if
         * the initial position is not defined.
         */
        _resetScrollPos : function(props) {
            props = props || this.props;
            var scrollPos = this.state.scrollPos;
            var focusedPos = props.focusedIndex;
            focusedPos = !isNaN(focusedPos) ? focusedPos : -1;
            var size = this._getSize();
            if (!isNaN(focusedPos) && focusedPos >= 0 && focusedPos < size) {
                var h = this._getItemHeight();
                scrollPos = focusedPos * h;
            }
            this._focusedItemIdx = focusedPos;
            this._setScrollPos(scrollPos, true);
        },
        /**
         * Adjust the absolute position of the items block to reflect exactly
         * the position of the scroller. This method is required because real
         * items sizes could be different from the average size returned by the
         * "_getItemHeight" method.
         */
        _adjustPosition : function() {
            var that = this;
            var container = that.getDOMNode();
            var scrollPos = container.scrollTop;

            var h = that._getItemHeight();
            var pos = scrollPos - that.state.index * h;
            var height = that.state.items.length * h;
            var blockNode = that.refs.block.getDOMNode();
            var blockHeight = blockNode.offsetHeight;

            var blockPos = scrollPos - blockHeight * (pos / height);
            blockPos = Math.round(blockPos);
            blockNode.style.top = blockPos + 'px';

            if (!isNaN(that._focusedItemIdx)) {
                delete that._focusedItemIdx;
                setTimeout(function() {
                    container.scrollTop = that.state.scrollPos;
                }, 30);
            }
        },
        /**
         * Sets the scroller in the specified position and updates the internal
         * state if the content should be re-loaded.
         */
        _setScrollPos : function(scrollPos, force) {
            var that = this;
            var pageSize = that._getPageSize();
            var h = that._getItemHeight();
            var container = that.getDOMNode();

            var windowHeight = container.offsetHeight;
            var delta = windowHeight / 4;

            var startPos = Math.max(scrollPos - delta, 0);
            var endPos = scrollPos + windowHeight + delta;

            var startItem = Math.floor(startPos / h);
            var endItem = Math.ceil((endPos + h - 1) / h);

            var startPage = Math.floor(startItem / pageSize);
            var endPage = Math.ceil((endItem + pageSize - 1) / pageSize);

            var index = startPage * pageSize;
            var length = (endPage - startPage) * pageSize;
            var params = {
                scrollPos : scrollPos,
                index : index,
                length : length
            };
            if (force || params.index != that.state.index
                    || params.length != that.state.length) {
                params.callback = function(items) {
                    params.items = items;
                    that.setState(that._newState(params));
                }
                that._loadItems(params);
            } else {
                that._adjustPosition();
            }
        },
        /** This handler is called when the scroller changes its position. */
        _onScroll : function(event) {
            this._setScrollPos(this.getDOMNode().scrollTop);
        },

    });

});<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import debounce from 'lodash/debounce';
import EventListener from 'react-event-listener';
import withStyles from '../styles/withStyles';

const ROWS_HEIGHT = 19;

export const styles = {
  root: {
    position: 'relative', // because the shadow has position: 'absolute',
    width: '100%',
  },
  textarea: {
    width: '100%',
    height: '100%',
    resize: 'none',
    font: 'inherit',
    padding: 0,
    cursor: 'inherit',
    boxSizing: 'border-box',
    lineHeight: 'inherit',
    border: 'none',
    outline: 'none',
    background: 'transparent',
  },
  shadow: {
    resize: 'none',
    // Overflow also needed to here to remove the extra row
    // added to textareas in Firefox.
    overflow: 'hidden',
    // Visibility needed to hide the extra text area on ipads
    visibility: 'hidden',
    position: 'absolute',
    height: 'auto',
    whiteSpace: 'pre-wrap',
  },
};

/**
 * @ignore - internal component.
 */
class Textarea extends React.Component {
  state = {
    height: null,
  };

  componentWillMount() {
    // <Input> expects the components it renders to respond to 'value'
    // so that it can check whether they are dirty
    this.value = this.props.value || this.props.defaultValue || '';
    this.setState({
      height: Number(this.props.rows) * ROWS_HEIGHT,
    });
  }

  componentDidMount() {
    this.syncHeightWithShadow(null);
  }

  componentWillReceiveProps(nextProps) {
    if (
      nextProps.value !== this.props.value ||
      Number(nextProps.rowsMax) !== Number(this.props.rowsMax)
    ) {
      this.syncHeightWithShadow(null, nextProps);
    }
  }

  componentWillUnmount() {
    this.handleResize.cancel();
  }

  shadow = null;
  singlelineShadow = null;
  input = null;
  value = null;

  handleResize = debounce(event => {
    this.syncHeightWithShadow(event);
  }, 166);

  syncHeightWithShadow(event, props = this.props) {
    if (this.shadow && this.singlelineShadow) {
      // The component is controlled, we need to update the shallow value.
      if (typeof this.props.value !== 'undefined') {
        this.shadow.value = props.value == null ? '' : String(props.value);
      }

      const lineHeight = this.singlelineShadow.scrollHeight;
      let newHeight = this.shadow.scrollHeight;

      // Guarding for jsdom, where scrollHeight isn't present.
      // See https://github.com/tmpvar/jsdom/issues/1013
      if (newHeight === undefined) {
        return;
      }

      if (Number(props.rowsMax) >= Number(props.rows)) {
        newHeight = Math.min(Number(props.rowsMax) * lineHeight, newHeight);
      }

      newHeight = Math.max(newHeight, lineHeight);

      if (this.state.height !== newHeight) {
        this.setState({
          height: newHeight,
        });
      }
    }
  }

  handleRefInput = node => {
    this.input = node;
    if (this.props.textareaRef) {
      this.props.textareaRef(node);
    }
  };

  handleRefSinglelineShadow = node => {
    this.singlelineShadow = node;
  };

  handleRefShadow = node => {
    this.shadow = node;
  };

  handleChange = (event: SyntheticInputEvent<HTMLInputElement>) => {
    this.value = event.target.value;

    if (typeof this.props.value === 'undefined' && this.shadow) {
      // The component is not controlled, we need to update the shallow value.
      this.shadow.value = this.value;
      this.syncHeightWithShadow(event);
    }

    if (this.props.onChange) {
      this.props.onChange(event);
    }
  };

  render() {
    const {
      classes,
      className,
      defaultValue,
      onChange,
      rows,
      rowsMax,
      textareaRef,
      value,
      ...other
    } = this.props;

    return (

        <EventListener target="window" onResize={this.handleResize} />
        <textarea
          ref={this.handleRefSinglelineShadow}
          className={classnames(classes.shadow, classes.textarea)}
          tabIndex={-1}
          rows="1"
          readOnly
          aria-hidden="true"
          value=""
        />
        <textarea
          ref={this.handleRefShadow}
          className={classnames(classes.shadow, classes.textarea)}
          tabIndex={-1}
          rows={rows}
          aria-hidden="true"
          readOnly
          defaultValue={defaultValue}
          value={value}
        />
        <textarea
          rows={rows}
          className={classnames(classes.textarea, className)}
          defaultValue={defaultValue}
          value={value}
          onChange={this.handleChange}
          ref={this.handleRefInput}
          {...other}
        />

    );
  }
}

Textarea.propTypes = {
  /**
   * Useful to extend the style applied to components.
   */
  classes: PropTypes.object.isRequired,
  /**
   * @ignore
   */
  className: PropTypes.string,
  /**
   * @ignore
   */
  defaultValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
  /**
   * @ignore
   */
  disabled: PropTypes.bool,
  /**
   * @ignore
   */
  onChange: PropTypes.func,
  /**
   * Number of rows to display when multiline option is set to true.
   */
  rows: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
  /**
   * Maximum number of rows to display when multiline option is set to true.
   */
  rowsMax: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
  /**
   * Use that property to pass a ref callback to the native textarea element.
   */
  textareaRef: PropTypes.func,
  /**
   * @ignore
   */
  value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
};

Textarea.defaultProps = {
  rows: 1,
};

export default withStyles(styles, { name: 'MuiTextarea' })(Textarea);
<commit_msg>[Textarea] Prepare React 16.3.0 (#10489)<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import debounce from 'lodash/debounce';
import EventListener from 'react-event-listener';
import withStyles from '../styles/withStyles';

const ROWS_HEIGHT = 19;

export const styles = {
  root: {
    position: 'relative', // because the shadow has position: 'absolute',
    width: '100%',
  },
  textarea: {
    width: '100%',
    height: '100%',
    resize: 'none',
    font: 'inherit',
    padding: 0,
    cursor: 'inherit',
    boxSizing: 'border-box',
    lineHeight: 'inherit',
    border: 'none',
    outline: 'none',
    background: 'transparent',
  },
  shadow: {
    resize: 'none',
    // Overflow also needed to here to remove the extra row
    // added to textareas in Firefox.
    overflow: 'hidden',
    // Visibility needed to hide the extra text area on ipads
    visibility: 'hidden',
    position: 'absolute',
    height: 'auto',
    whiteSpace: 'pre-wrap',
  },
};

/**
 * @ignore - internal component.
 */
class Textarea extends React.Component {
  constructor(props, context) {
    super(props, context);

    // <Input> expects the components it renders to respond to 'value'
    // so that it can check whether they are dirty
    this.value = props.value || props.defaultValue || '';
    this.state = {
      height: Number(props.rows) * ROWS_HEIGHT,
    };
  }

  state = {
    height: null,
  };

  componentDidMount() {
    this.syncHeightWithShadow();
  }

  componentDidUpdate() {
    this.syncHeightWithShadow();
  }

  componentWillUnmount() {
    this.handleResize.cancel();
  }

  shadow = null;
  singlelineShadow = null;
  input = null;
  value = null;

  handleResize = debounce(() => {
    this.syncHeightWithShadow();
  }, 166);

  syncHeightWithShadow(props = this.props) {
    if (!this.shadow || !this.singlelineShadow) {
      return;
    }

    // The component is controlled, we need to update the shallow value.
    if (typeof this.props.value !== 'undefined') {
      this.shadow.value = props.value == null ? '' : String(props.value);
    }

    const lineHeight = this.singlelineShadow.scrollHeight;
    let newHeight = this.shadow.scrollHeight;

    // Guarding for jsdom, where scrollHeight isn't present.
    // See https://github.com/tmpvar/jsdom/issues/1013
    if (newHeight === undefined) {
      return;
    }

    if (Number(props.rowsMax) >= Number(props.rows)) {
      newHeight = Math.min(Number(props.rowsMax) * lineHeight, newHeight);
    }

    newHeight = Math.max(newHeight, lineHeight);

    if (this.state.height !== newHeight) {
      this.setState({
        height: newHeight,
      });
    }
  }

  handleRefInput = node => {
    this.input = node;
    if (this.props.textareaRef) {
      this.props.textareaRef(node);
    }
  };

  handleRefSinglelineShadow = node => {
    this.singlelineShadow = node;
  };

  handleRefShadow = node => {
    this.shadow = node;
  };

  handleChange = event => {
    this.value = event.target.value;

    if (typeof this.props.value === 'undefined' && this.shadow) {
      // The component is not controlled, we need to update the shallow value.
      this.shadow.value = this.value;
      this.syncHeightWithShadow();
    }

    if (this.props.onChange) {
      this.props.onChange(event);
    }
  };

  render() {
    const {
      classes,
      className,
      defaultValue,
      onChange,
      rows,
      rowsMax,
      textareaRef,
      value,
      ...other
    } = this.props;

    return (

        <EventListener target="window" onResize={this.handleResize} />
        <textarea
          ref={this.handleRefSinglelineShadow}
          className={classnames(classes.shadow, classes.textarea)}
          tabIndex={-1}
          rows="1"
          readOnly
          aria-hidden="true"
          value=""
        />
        <textarea
          ref={this.handleRefShadow}
          className={classnames(classes.shadow, classes.textarea)}
          tabIndex={-1}
          rows={rows}
          aria-hidden="true"
          readOnly
          defaultValue={defaultValue}
          value={value}
        />
        <textarea
          rows={rows}
          className={classnames(classes.textarea, className)}
          defaultValue={defaultValue}
          value={value}
          onChange={this.handleChange}
          ref={this.handleRefInput}
          {...other}
        />

    );
  }
}

Textarea.propTypes = {
  /**
   * Useful to extend the style applied to components.
   */
  classes: PropTypes.object.isRequired,
  /**
   * @ignore
   */
  className: PropTypes.string,
  /**
   * @ignore
   */
  defaultValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
  /**
   * @ignore
   */
  disabled: PropTypes.bool,
  /**
   * @ignore
   */
  onChange: PropTypes.func,
  /**
   * Number of rows to display when multiline option is set to true.
   */
  rows: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
  /**
   * Maximum number of rows to display when multiline option is set to true.
   */
  rowsMax: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
  /**
   * Use that property to pass a ref callback to the native textarea element.
   */
  textareaRef: PropTypes.func,
  /**
   * @ignore
   */
  value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
};

Textarea.defaultProps = {
  rows: 1,
};

export default withStyles(styles, { name: 'MuiTextarea' })(Textarea);
<|endoftext|>