<commit_before>'use strict';

module.exports = tileReduce;

var EventEmitter = require('events').EventEmitter;
var cpus = require('os').cpus().length;
var fork = require('child_process').fork;
var path = require('path');
var fs = require('fs');
var binarysplit = require('binary-split');
var cover = require('./cover');
var streamArray = require('stream-array');
var MBTiles = require('mbtiles');

// Suppress max listener warnings. We need at least 1 listener per worker.
process.stdout.setMaxListeners(0);
process.stderr.setMaxListeners(0);

function tileReduce(options) {
  var workers = [];
  var workersReady = 0;
  var tileStream = null;
  var tilesDone = 0;
  var tilesSent = 0;
  var pauseLimit = 5000;
  var start = Date.now();

  log('Starting up ' + cpus + ' workers... ');

  for (var i = 0; i < cpus; i++) {
    var worker = fork(path.join(__dirname, 'worker.js'), [options.map, JSON.stringify(options.sources)], {silent: true});
    worker.stdout.pipe(binarysplit('\x1e')).pipe(process.stdout);
    worker.stderr.pipe(process.stderr);
    worker.on('message', handleMessage);
    workers.push(worker);
  }

  function handleMessage(message) {
    if (message.reduce) reduce(message.value);
    else if (message.ready && ++workersReady === workers.length) run();
  }

  var ee = new EventEmitter();
  var timer;

  function run() {
    log('Job started.\n');

    ee.emit('start');
    timer = setInterval(updateStatus, 64);

    var tiles = cover(options);

    if (tiles) {
      // JS tile array, GeoJSON or bbox
      log('Processing ' + tiles.length + ' tiles.\n');
      tileStream = streamArray(tiles).on('data', handleTile);

    } else if (typeof options.tiles === 'string') {
      // text file tile stream ("x y z\n")
      log('Processing tile coords from ' + path.basename(options.tiles) + '.\n');
      tileStream = fs.createReadStream(options.tiles);
      tileStream.pipe(binarysplit()).on('data', handleTileLine);

    } else {
      // try to automatically get tiles from mbtiles
      var source;
      for (var i = 0; i < options.sources.length; i++) {
        if (options.sources[i].mbtiles) {
          source = options.sources[i];
          break;
        }
      }
      if (source) {
        log('Processing tile coords from "' + source.name + '" source.\n');
        var db = new MBTiles(source.mbtiles, function(err) {
          if (err) throw err;
          tileStream = db.createZXYStream().pipe(binarysplit()).on('data', handleZXYLine);
        });

      } else {
        throw new Error('No area or tiles specified for the job.');
      }
    }
  }

  var paused = false;

  function handleTile(tile) {
    workers[tilesSent++ % workers.length].send(tile);
    if (!paused && tilesSent - tilesDone > pauseLimit) {
      paused = true;
      tileStream.pause();
    }
  }

  function handleTileLine(line) {
    handleTile(line.toString().split(' ').map(Number));
  }

  function handleZXYLine(line) {
    var tile = line.toString().split('/');
    handleTile([+tile[1], +tile[2], +tile[0]]);
  }

  function reduce(value) {
    if (value !== null && value !== undefined) ee.emit('reduce', value);
    if (paused && tilesSent - tilesDone < (pauseLimit / 2)) {
      paused = false;
      tileStream.resume();
    }
    if (++tilesDone === tilesSent) shutdown();
  }

  function shutdown() {
    while (workers.length) workers.pop().kill();

    clearTimeout(timer);
    updateStatus();
    log('.\n');

    ee.emit('end');
  }

  function updateStatus() {
    if (options.log === false || !process.stderr.cursorTo) return;

    var s = Math.floor((Date.now() - start) / 1000);
    var h = Math.floor(s / 3600);
    var m = Math.floor((s - h * 3600) / 60);
    var time = (h ? h + 'h ' : '') + (h || m ? m + 'm ' : '') + (s % 60) + 's';

    process.stderr.cursorTo(0);
    process.stderr.write(tilesDone + ' tiles processed in ' + time);
    process.stderr.clearLine(1);
  }

  function log(str) {
    if (options.log !== false) process.stderr.write(str);
  }

  return ee;
}
<commit_msg>add sourceCover option<commit_after>'use strict';

module.exports = tileReduce;

var EventEmitter = require('events').EventEmitter;
var cpus = require('os').cpus().length;
var fork = require('child_process').fork;
var path = require('path');
var fs = require('fs');
var binarysplit = require('binary-split');
var cover = require('./cover');
var streamArray = require('stream-array');
var MBTiles = require('mbtiles');

// Suppress max listener warnings. We need at least 1 listener per worker.
process.stdout.setMaxListeners(0);
process.stderr.setMaxListeners(0);

function tileReduce(options) {
  var workers = [];
  var workersReady = 0;
  var tileStream = null;
  var tilesDone = 0;
  var tilesSent = 0;
  var pauseLimit = 5000;
  var start = Date.now();

  log('Starting up ' + cpus + ' workers... ');

  for (var i = 0; i < cpus; i++) {
    var worker = fork(path.join(__dirname, 'worker.js'), [options.map, JSON.stringify(options.sources)], {silent: true});
    worker.stdout.pipe(binarysplit('\x1e')).pipe(process.stdout);
    worker.stderr.pipe(process.stderr);
    worker.on('message', handleMessage);
    workers.push(worker);
  }

  function handleMessage(message) {
    if (message.reduce) reduce(message.value);
    else if (message.ready && ++workersReady === workers.length) run();
  }

  var ee = new EventEmitter();
  var timer;

  function run() {
    log('Job started.\n');

    ee.emit('start');
    timer = setInterval(updateStatus, 64);

    var tiles = cover(options);

    if (tiles) {
      // JS tile array, GeoJSON or bbox
      log('Processing ' + tiles.length + ' tiles.\n');
      tileStream = streamArray(tiles).on('data', handleTile);

    } else if (typeof options.tiles === 'string') {
      // text file tile stream ("x y z\n")
      log('Processing tile coords from ' + path.basename(options.tiles) + '.\n');
      tileStream = fs.createReadStream(options.tiles);
      tileStream.pipe(binarysplit()).on('data', handleTileLine);

    } else {
      // try to get tiles from mbtiles (either specified by sourceCover or first encountered)
      var source;
      for (var i = 0; i < options.sources.length; i++) {
        source = options.sources[i];
        if (options.sources[i].mbtiles && (!options.sourceCover || options.sourceCover === source.name)) break;
        source = null;
      }
      if (source) {
        log('Processing tile coords from "' + source.name + '" source.\n');
        var db = new MBTiles(source.mbtiles, function(err) {
          if (err) throw err;
          tileStream = db.createZXYStream().pipe(binarysplit()).on('data', handleZXYLine);
        });

      } else {
        throw new Error(options.sourceCover ?
          'Specified source for cover not found.' :
          'No area or tiles specified for the job.');
      }
    }
  }

  var paused = false;

  function handleTile(tile) {
    workers[tilesSent++ % workers.length].send(tile);
    if (!paused && tilesSent - tilesDone > pauseLimit) {
      paused = true;
      tileStream.pause();
    }
  }

  function handleTileLine(line) {
    handleTile(line.toString().split(' ').map(Number));
  }

  function handleZXYLine(line) {
    var tile = line.toString().split('/');
    handleTile([+tile[1], +tile[2], +tile[0]]);
  }

  function reduce(value) {
    if (value !== null && value !== undefined) ee.emit('reduce', value);
    if (paused && tilesSent - tilesDone < (pauseLimit / 2)) {
      paused = false;
      tileStream.resume();
    }
    if (++tilesDone === tilesSent) shutdown();
  }

  function shutdown() {
    while (workers.length) workers.pop().kill();

    clearTimeout(timer);
    updateStatus();
    log('.\n');

    ee.emit('end');
  }

  function updateStatus() {
    if (options.log === false || !process.stderr.cursorTo) return;

    var s = Math.floor((Date.now() - start) / 1000);
    var h = Math.floor(s / 3600);
    var m = Math.floor((s - h * 3600) / 60);
    var time = (h ? h + 'h ' : '') + (h || m ? m + 'm ' : '') + (s % 60) + 's';

    process.stderr.cursorTo(0);
    process.stderr.write(tilesDone + ' tiles processed in ' + time);
    process.stderr.clearLine(1);
  }

  function log(str) {
    if (options.log !== false) process.stderr.write(str);
  }

  return ee;
}
<|endoftext|>


<commit_before>/*
 * grunt-pelican
 * https://github.com/chuwy/grunt-pelican
 *
 * Copyright (c) 2014 Anton Parkhomenko
 * Licensed under the MIT license.
 */

'use strict';

var exec = require('child_process').exec;

module.exports = function(grunt) {
    grunt.registerMultiTask('pelican', 'Grunt plugin for build Pelican static blog', function() {
        // Merge task-specific and/or target-specific options with these defaults.
        var done = this.async()

        var options = this.options({
            punctuation: '.',
            separator: ', '
        });

        var cmd = 'pelican ';
        var contentDir = '';
        var configFile = '';
        var outputDir = '';

        if ('contentDir' in options && typeof options.contentDir === 'string') {
            contentDir = options.contentDir;
        }
        if ('contneDir' in this.data && typeof this.data.contentDir === 'string') {
            contnetDir = this.data.contentDir;
        }
        cmd += contentDir;

        if ('configFile' in options && typeof options.configFile === 'string') {
            configFile = options.configFile;
        }
        if ('configFile' in this.data && typeof this.data.configFile === 'string') {
            configFile = this.data.configFile;
        }
        cmd += ' --settings ' + configFile;

        if ('outputDir' in options && typeof options.outputDir === 'string') {
            outputDir = options.outputDir;
        }
        if ('outputDir' in this.data && typeof this.data.outputDir === 'string') {
            outputDir = this.data.outputDir;
        }
        cmd += ' --output ' + outputDir;

        var cp = exec(cmd, function (err, stdout, stderr) {

            grunt.log.writeln(cmd);

            if (typeof options.callback === 'function') {
                options.callback.call(this, err, stdout, stderr, done)
            } else {
                if (err && options.failOnError) {
                    grunt.warn(err);
                }
                done();
            }
            grunt.log.writeln(stdout);
        }.bind(this));

    });

};
<commit_msg>Error output<commit_after>/*
 * grunt-pelican
 * https://github.com/chuwy/grunt-pelican
 *
 * Copyright (c) 2014 Anton Parkhomenko
 * Licensed under the MIT license.
 */

'use strict';

var exec = require('child_process').exec;

module.exports = function(grunt) {
    grunt.registerMultiTask('pelican', 'Grunt plugin for build Pelican static blog', function() {
        // Merge task-specific and/or target-specific options with these defaults.
        var done = this.async()

        var options = this.options({
            punctuation: '.',
            separator: ', '
        });

        var cmd = 'pelican ';
        var contentDir = '';
        var configFile = '';
        var outputDir = '';

        if ('contentDir' in options && typeof options.contentDir === 'string') {
            contentDir = options.contentDir;
        }
        if ('contneDir' in this.data && typeof this.data.contentDir === 'string') {
            contnetDir = this.data.contentDir;
        }
        cmd += contentDir;

        if ('configFile' in options && typeof options.configFile === 'string') {
            configFile = options.configFile;
        }
        if ('configFile' in this.data && typeof this.data.configFile === 'string') {
            configFile = this.data.configFile;
        }
        cmd += ' --settings ' + configFile;

        if ('outputDir' in options && typeof options.outputDir === 'string') {
            outputDir = options.outputDir;
        }
        if ('outputDir' in this.data && typeof this.data.outputDir === 'string') {
            outputDir = this.data.outputDir;
        }
        cmd += ' --output ' + outputDir;

        var cp = exec(cmd, function (err, stdout, stderr) {

            grunt.log.writeln('INFO: ' + cmd);

            if (typeof options.callback === 'function') {
                options.callback.call(this, err, stdout, stderr, done)
            } else {
                if (err && options.failOnError) {
                    grunt.warn(err);
                }
            }
            grunt.log.errorlns(stderr);
            grunt.log.oklns(stdout);
        }.bind(this));

    });

};
<|endoftext|>


<commit_before>'use strict';

class Hook {
  constructor(options) {
    options = options || {};
    this.name = options.name ? options.name : 'Hook';
    this.flags = {
      execute: true,
      hook: true,
      postProcess: true
    };
    this.defaults = {};
    this.hook = undefined;
    this.settings = options;
  }

  execute(thing) {}

  process(thing) {
    this.setFlags(true);
    if (this.preProcess(thing)) {
      if (this.flags.execute) {
        this.execute(thing);
      }
    }
    if (this.flags.hook && this.isHook(this.hook)) {
      this.hook.process(thing);
    }
    if (this.flags.postProcess) {
      this.postProcess(thing);
    }
  }

  preProcess(thing) {
    return true;
  }

  postProcess(thing) {}

  setHook(hook) {
    if (this.isHook(this.hook)) {
      this.hook.setHook(hook);
    } else {
      this.hook = hook;
    }

    return hook;
  }

  setFlags(flag = true) {
    for (const key in this.flags) {
      this.flags[key] = flag;
    }
  }

  isHook(hook) {
    const type = typeof hook;
    return hook !== null && (type === 'object' || type === 'function') && ('hook' in hook);
  }

  classInstanceFromString(packageLocation, ...a) {
    const Temp = require(packageLocation);
    return new Temp(...a);
  }
}

class Anchor extends Hook {
  constructor(options) {
    options = options || {};
    super(options);
    this.name = options.name ? options.name : 'Anchor';
    this.hooks = [];
  }

  process(thing) {
    this.setFlags(true);
    if (this.preProcess(thing)) {
      if (this.flags.execute) {
        this.execute(thing);
      }
    }
    if (this.flags.hook && this.isHook(this.hook)) {
      this.hook.process(thing);
      this.hooks.forEach( (hook) => hook.process(thing) );
    }
    if (this.flags.postProcess) {
      this.postProcess(thing);
    }
  }
}

module.exports = {
  Hook,
  Anchor
};
<commit_msg>Restrict hook assignment to hook instances.<commit_after>'use strict';

class Hook {
  constructor(options) {
    options = options || {};
    this.name = options.name ? options.name : 'Hook';
    this.flags = {
      execute: true,
      hook: true,
      postProcess: true
    };
    this.defaults = {};
    this.hook = undefined;
    this.settings = options;
  }

  execute(thing) {}

  process(thing) {
    this.setFlags(true);
    if (this.preProcess(thing)) {
      if (this.flags.execute) {
        this.execute(thing);
      }
    }
    if (this.flags.hook && this.isHook(this.hook)) {
      this.hook.process(thing);
    }
    if (this.flags.postProcess) {
      this.postProcess(thing);
    }
  }

  preProcess(thing) {
    return true;
  }

  postProcess(thing) {}

  setHook(hook) {
    if (this.isHook(this.hook)) {
      this.hook.setHook(hook);
    } else {
      // only allow hooks as hooks
      if (this.isHook(hook)) {
        this.hook = hook;
      }
    }

    return hook;
  }

  setFlags(flag = true) {
    for (const key in this.flags) {
      this.flags[key] = flag;
    }
  }

  isHook(hook) {
    const type = typeof hook;
    return hook !== null && (type === 'object' || type === 'function') && ('hook' in hook);
  }

  classInstanceFromString(packageLocation, ...a) {
    const Temp = require(packageLocation);
    return new Temp(...a);
  }
}

class Anchor extends Hook {
  constructor(options) {
    options = options || {};
    super(options);
    this.name = options.name ? options.name : 'Anchor';
    this.hooks = [];
  }

  process(thing) {
    this.setFlags(true);
    if (this.preProcess(thing)) {
      if (this.flags.execute) {
        this.execute(thing);
      }
    }
    if (this.flags.hook && this.isHook(this.hook)) {
      this.hook.process(thing);
      this.hooks.forEach( (hook) => hook.process(thing) );
    }
    if (this.flags.postProcess) {
      this.postProcess(thing);
    }
  }
}

module.exports = {
  Hook,
  Anchor
};
<|endoftext|>


<commit_before>
/*
 *  Copyright 2012 outaTiME.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

module.exports = function (grunt) {

  grunt.registerMultiTask('replace', 'Replace inline patterns with defined variables.', function () {

    var
      path = require('path'),
      files = grunt.file.expandFiles(this.file.src),
      target = this.target,
      config = grunt.config(['replace', this.target]),
      dest = config.dest || '.',
      variables = config.variables,
      prefix = config.prefix,
      locals = {},
      processed = 0;

    if (typeof variables === 'object') {
      grunt.verbose.writeln('Using "' + target + '" replace variables options.');
    } else {
      grunt.verbose.writeln('Using master replacer variables options.');
      variables = grunt.config('replacer.variables') || {};
    }

    grunt.verbose.writeflags(variables, 'variables');

    if (typeof prefix === 'string') {
      grunt.verbose.writeln('Using "' + target + '" replace prefix options.');
    } else {
      grunt.verbose.writeln('Using master replacer prefix options.');
      prefix = grunt.config('replacer.prefix') || '@@';
    }

    grunt.verbose.writeflags(prefix, 'prefix');

    Object.keys(variables).forEach(function (variable) {
      var value = variables[variable];
      if (typeof value === 'string') {
        locals[variable] = grunt.template.process(value);
      }
    });

    files.forEach(function (filepath, index) {
      var filename = path.basename(filepath), dest_filepath = path.join(dest, filename);
      grunt.file.copy(filepath, dest_filepath, {
        process: function (contents) {
          var updated = false;
          Object.keys(locals).forEach(function (local) {
            var re = new RegExp(prefix + local, "g"), value = locals[local];
            updated = updated || contents.match(re);
            contents = contents.replace(re, value);
          });
          if (updated) {
            grunt.log.writeln('Replace "' + filepath + '" > "' + dest_filepath + '"');
            processed++;
          } else {
            return false;
          }
          return contents;
        }
      });

    });

    if (processed === 0) {
      grunt.log.writeln('No documents updated.');
    }

  });

};
<commit_msg>Processing variables from config in destination options<commit_after>
/*
 *  Copyright 2012 outaTiME.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

module.exports = function (grunt) {

  grunt.registerMultiTask('replace', 'Replace inline patterns with defined variables.', function () {

    var
      path = require('path'),
      files = grunt.file.expandFiles(this.file.src),
      target = this.target,
      config = grunt.config(['replace', this.target]),
      dest = this.file.dest || '.',
      variables = config.variables,
      prefix = config.prefix,
      locals = {},
      processed = 0;

    if (typeof variables === 'object') {
      grunt.verbose.writeln('Using "' + target + '" replace variables options.');
    } else {
      grunt.verbose.writeln('Using master replacer variables options.');
      variables = grunt.config('replacer.variables') || {};
    }

    grunt.verbose.writeflags(variables, 'variables');

    if (typeof prefix === 'string') {
      grunt.verbose.writeln('Using "' + target + '" replace prefix options.');
    } else {
      grunt.verbose.writeln('Using master replacer prefix options.');
      prefix = grunt.config('replacer.prefix') || '@@';
    }

    grunt.verbose.writeflags(prefix, 'prefix');

    Object.keys(variables).forEach(function (variable) {
      var value = variables[variable];
      if (typeof value === 'string') {
        locals[variable] = grunt.template.process(value);
      }
    });

    files.forEach(function (filepath, index) {
      var filename = path.basename(filepath), dest_filepath = path.join(dest, filename);
      grunt.file.copy(filepath, dest_filepath, {
        process: function (contents) {
          var updated = false;
          Object.keys(locals).forEach(function (local) {
            var re = new RegExp(prefix + local, "g"), value = locals[local];
            updated = updated || contents.match(re);
            contents = contents.replace(re, value);
          });
          if (updated) {
            grunt.log.writeln('Replace "' + filepath + '" > "' + dest_filepath + '"');
            processed++;
          } else {
            return false;
          }
          return contents;
        }
      });

    });

    if (processed === 0) {
      grunt.log.writeln('No documents updated.');
    }

  });

};
<|endoftext|>


<commit_before>import {default as RandomForestClassifier} from './RandomForestClassifier';
import {default as RandomForestRegression} from './RandomForestRegression';

export default {
    RandomForestClassifier,
    RandomForestRegression
};
<commit_msg>remove default from index.js<commit_after>import {default as RandomForestClassifier} from './RandomForestClassifier';
import {default as RandomForestRegression} from './RandomForestRegression';

export {
    RandomForestClassifier,
    RandomForestRegression
};
<|endoftext|>


<commit_before>import React from 'react';
import ReactDOM from 'react-dom';
import store from './app/Store';
import Canvas from './components/Canvas/Canvas';
import './index.css';
import { Provider } from 'react-redux';
import Menu from './components/Menu/Menu';
import Mixer from './components/Mixer/Mixer';
import ControlPanel from './components/ControlPanel/ControlPanel';
import FXPanel from './components/FXPanel/FXPanel';
import Transport from './components/Transport/Transport';
import midiContext from './app/context/MIDIContext';
import eventHandler from './app/EventHandler';
import hydrator from './app/Hydrator';

const renderDom = () => {
  ReactDOM.render(
    <Provider store={store}>

        <Mixer/>

          <Menu/>

            <Transport/>
            <Canvas/>
            <FXPanel/>

          <ControlPanel/>

    </Provider>,
    document.getElementById('root')
  );
};

const initialize = () => {
  eventHandler.initialize();
  midiContext.initialize()
    .then(() => {

      renderDom();

      if (localStorage.QState) {
        let payload = JSON.parse(localStorage.QState);
        hydrator.hydrate(store, payload);
      }

      for (let entry of midiContext.outputs.entries()) {
        for (let destination of entry) {
          if (destination.state && destination.state === 'connected' && destination.name) {
            store.dispatch({
              type: 'ADD_DESTINATION',
              destination
            });
          }
        }
      }
    }
  );
};

initialize();
<commit_msg>Remove Mixer component.<commit_after>import React from 'react';
import ReactDOM from 'react-dom';
import store from './app/Store';
import Canvas from './components/Canvas/Canvas';
import './index.css';
import { Provider } from 'react-redux';
import Menu from './components/Menu/Menu';
// import Mixer from './components/Mixer/Mixer';
import ControlPanel from './components/ControlPanel/ControlPanel';
import FXPanel from './components/FXPanel/FXPanel';
import Transport from './components/Transport/Transport';
import midiContext from './app/context/MIDIContext';
import eventHandler from './app/EventHandler';
import hydrator from './app/Hydrator';

const renderDom = () => {
  ReactDOM.render(
    <Provider store={store}>

        {/*<Mixer/>*/}

          <Menu/>

            <Transport/>
            <Canvas/>
            <FXPanel/>

          <ControlPanel/>

    </Provider>,
    document.getElementById('root')
  );
};

const initialize = () => {
  eventHandler.initialize();
  midiContext.initialize()
    .then(() => {

      renderDom();

      if (localStorage.QState) {
        let payload = JSON.parse(localStorage.QState);
        hydrator.hydrate(store, payload);
      }

      for (let entry of midiContext.outputs.entries()) {
        for (let destination of entry) {
          if (destination.state && destination.state === 'connected' && destination.name) {
            store.dispatch({
              type: 'ADD_DESTINATION',
              destination
            });
          }
        }
      }
    }
  );
};

initialize();
<|endoftext|>


<commit_before>var cleanText = require('./util/cleanText');
var initUI = require('./UI');

module.exports = {
  create: create
};

function create(container, pillCorpus, options) {
  var props = initProps(pillCorpus, options);
  var pillerInstance = {
    UI: initUI(container),
    destroy: destroy.bind(null, props)
  };

  defineModelValueOnInstance(pillerInstance, props);
  registerTextareaEvents(pillerInstance.UI, props);

  return pillerInstance;
}

function initProps(pillCorpus, options) {
  var props = {
    options: options,
    getPillCorpus: function() {
      return typeof pillCorpus === 'function' && pillCorpus() || pillCorpus || [];
    }
  };

  return props;
}

function destroy(props) {
  clearTypingTimers(props);
  clearTimeout(props.focusTimeout);
  clearTimeout(props.postInputCleanupTimeout);
}

function defineModelValueOnInstance(pillerInstance, props) {
  Object.defineProperty(pillerInstance, 'modelValue', {
    enumerable: true,
    get: function() {
      return props.modelValue;
    },
    set: setModelValue.bind(null, pillerInstance.UI, props)
  });
}

function registerTextareaEvents(ui, props) {
  var onPreInputEventSetupListener = onPreInputEventSetup.bind(null, ui, props);

  ['keydown', 'keypress', 'paste', 'cut'].forEach(function(eventName) {
    ui.textarea.addEventListener(eventName, onPreInputEventSetupListener);
  });

  ['drop', 'dragstart'].forEach(function(eventName) {
    ui.textarea.addEventListener(eventName, function(e) {
      e.preventDefault();
    });
  });

  ui.textarea.addEventListener('keydown', onPreInputKeydown.bind(null, ui, props));
  ui.textarea.addEventListener('keypress', onPreInputKeypress.bind(null, ui, props));
  ui.textarea.addEventListener('keyup', onPreInputKeyup.bind(null, ui, props));
  ui.textarea.addEventListener('focus', onTextareaFocus.bind(null, ui, props));
  ui.textarea.addEventListener('blur', onTextareaBlur.bind(null, ui, props));
  ui.textarea.addEventListener('input', onTextareaInput.bind(null, ui, props));
}

function onPreInputEventSetup(ui, props, e) {
  props.preInputEvent = e;
  props.preInputVal = ui.textarea.value;
  storePreInputSelection(ui, props);

  //timeout needed to reset preInput declarations above after all key and input events fire
  clearTimeout(props.postInputCleanupTimeout);
  props.postInputCleanupTimeout = setTimeout(postInputCleanup.bind(null, props));
}

function onPreInputKeydown(ui, props, e) {
  var isValidEvent = false;
  var preventIfAtStart = false;
  var preventIfAtEnd = false;

  props.originalKeyEvent = e;

  switch (e.which) {
    case 37: // LEFT_ARROW
    case 8: // BACKSPACE
      preventIfAtStart = isValidEvent = true;
      break;
    case 39: // RIGHT_ARROW
    case 46: // DELETE
      preventIfAtEnd = isValidEvent = true;
      break;
  }

  if (isValidEvent && props.preInputSelStart === props.preInputSelEnd) {
    maybeFocusPill(ui, props, e, preventIfAtStart, preventIfAtEnd);
  }
}

function onPreInputKeypress(ui, props, e) {
  props.originalKeyEvent = e;
}

function onPreInputKeyup(ui, props, e) {
  switch (e.which) {
    case 37: // LEFT_ARROW
    case 38: // UP_ARROW
    case 39: // RIGHT_ARROW
    case 40: // DOWN_ARROW
      storePreInputSelection(ui, props);
      maybeFocusPill(ui, props, e, true, true);
      break;
  }
}

function onTextareaFocus(ui, props, e) {
  //timeout needed to reflect correct selection start & end in textarea
  clearTimeout(props.focusTimeout);
  props.focusTimeout = setTimeout(function() {
    storePreInputSelection(ui, props);
    maybeFocusPill(ui, props, e, true, true);
  });
}

function onTextareaBlur(ui, props) {
  clearTimeout(props.focusTimeout);
}

function onTextareaInput(ui, props) {
  props.modelValue.text = ui.textarea.value;

  if (!props.preInputVal || !props.modelValue.text) {
    props.modelValue.clearPillRanges();
  } else if (props.preInputEvent && props.preInputSelStart === props.preInputSelEnd) { //don't continue for selection due to Mac difference with backspace for single word selection
    var prevType = props.preInputEvent.type.toLowerCase();
    var hasShortcutKey = props.preInputEvent.ctrlKey || props.preInputEvent.metaKey || props.preInputEvent.altKey;

    if (props.preInputEvent.which === 8 && !hasShortcutKey) { // BACKSPACE
      updateRanges(props, props.preInputSelStart - 1, props.preInputSelEnd);
    } else if (props.preInputEvent === 46 && !hasShortcutKey) { // DELETE
      updateRanges(props, props.preInputSelStart, props.preInputSelEnd + 1);
    } else if (prevType === 'keypress' && !hasShortcutKey) {
      updateRanges(props, props.preInputSelStart, props.preInputSelEnd, 1);
    } else {
      updateRangesForStringDiff(props);
    }
  } else {
    updateRangesForStringDiff(props);
  }

  synchronize(ui, props);
  postInputCleanup(props);
}

function updateRanges(props, changeStart, changeEnd, insertionCount) {
  var indexDelta = changeStart - changeEnd + (insertionCount || 0);
  var retainedPillRanges = [];

  props.modelValue.getPillRanges().forEach(function(pillRange) {
    //update position for and retain pill when the change ends before it
    if (changeEnd <= pillRange.positionStart) {
      pillRange.positionStart = pillRange.positionStart + indexDelta;
      retainedPillRanges.push(pillRange);
    }
    //retain pill when the change starts after it
    else if (changeStart >= pillRange.positionEnd) {
      retainedPillRanges.push(pillRange);
    }
  });

  props.modelValue.setPillRanges(retainedPillRanges);
}

function updateRangesForStringDiff(props) {
  var startDiff = getDifferenceStartIdx(props);
  var endDiff;

  if (startDiff < props.modelValue.text.length || startDiff < props.preInputVal.length) {
    endDiff = getDifferenceEndIdx(props, startDiff);
  } else {
    endDiff = Math.max(props.modelValue.text.length, props.preInputVal.length);
  }

  var insertionCount = endDiff + props.modelValue.text.length - props.preInputVal.length - startDiff;

  updateRanges(props, startDiff, endDiff, insertionCount > 0 ? insertionCount : 0);
}

function getDifferenceStartIdx(props) {
  var startDiff;

  for (startDiff = 0; startDiff < props.modelValue.text.length && startDiff < props.preInputVal.length; ++startDiff) {
    if (props.modelValue.text[startDiff] !== props.preInputVal[startDiff]) {
      break;
    }
  }

  return startDiff;
}

function getDifferenceEndIdx(props, startDiff) {
  var endDiff, newValIdx;
  if (startDiff === props.preInputVal.length) {
    endDiff = startDiff;
  } else if (startDiff === props.modelValue.text.length) {
    endDiff = props.preInputVal.length;
  } else {
    var newValLengthDiff = props.modelValue.text.length - props.preInputVal.length;

    for (endDiff = props.preInputVal.length, newValIdx = endDiff + newValLengthDiff; endDiff > startDiff && newValIdx > startDiff; endDiff--, newValIdx = endDiff + newValLengthDiff) {
      if (props.preInputVal[endDiff - 1] !== props.modelValue.text[endDiff + newValLengthDiff - 1]) {
        break;
      }
    }
  }
  return endDiff;
}

function storePreInputSelection(ui, props) {
  props.preInputSelStart = ui.textarea.selectionStart;
  props.preInputSelEnd = ui.textarea.selectionEnd;
}

function postInputCleanup(props) {
  //reset vars to account for input event that might not have a preInputEvent (e.g. spell correction only fires input event)
  props.preInputEvent = null;
  props.preInputVal = props.modelValue.text;
}

function synchronize(ui, props, newModelValue) {
  props.modelValue = newModelValue || props.modelValue || RiqTextModelValue();
  ui.textarea.value = props.modelValue.text;
  updateDecorator(ui, props);
  updateStorageTimer();
}

function setModelValue(ui, props, newModelValue) {
  clearTypingTimers();

  if (newModelValue) {
    synchronize(ui, props, newModelValue);
    maybeStoreModelValue(props);
  } else if (props.modelValue) {
    clearStorageState(props);
  } else {
    newModelValue = getStoredModelValue(props);
  }

  synchronize(newModelValue || RiqTextModelValue());
}

function updateDecorator(ui, props) {
  var cleanHtml = getCleanHtmlWithRanges(ui, props);

  //  is an invisible character that gives a true height to the div when
 is ignored for size
  if (cleanHtml.endsWith('
')) {
    cleanHtml += ' ';
  }

  ui.decorator.innerHTML = cleanHtml;

  var decoratorHeight = ui.decorator.getBoundingClientRect().height;

  if (decoratorHeight !== props.elementHeight) {
    props.elementHeight = decoratorHeight;
    ui.textarea.style.height = props.elementHeight + 'px';
  }
}

function getCleanHtmlWithRanges(ui, props) {
  var lastPositionEnd = 0;
  var cleanResult = '';

  props.modelValue.getPillRanges().forEach(function(range) {
    cleanResult += cleanText(props.modelValue.text.substring(lastPositionEnd, range.positionStart), true);
    cleanResult += range.html;
    lastPositionEnd = range.positionEnd;
  });

  cleanResult += cleanText(props.modelValue.text.substring(lastPositionEnd), true);

  return cleanResult;
}

function maybeFocusPill(ui, props, e, preventIfAtStart, preventIfAtEnd) {
  if (props.originalKeyEvent && !props.originalKeyEvent.ctrlKey && !props.originalKeyEvent.metaKey && props.preInputSelStart === props.preInputSelEnd) {
    var pillRangeWithCaret = props.modelValue.getPillRanges().some(function(pillRange) {
      if (isIndexWithinRange(props.preInputSelStart, pillRange) &&
        !(preventIfAtStart && props.preInputSelStart === pillRange.positionStart) &&
        !(preventIfAtEnd && props.preInputSelStart === pillRange.positionEnd)) {
        pillRangeWithCaret = pillRange;
        return true;
      }
    });

    if (pillRangeWithCaret) {
      var indexOfRangeWithCaret = 0;
      e.preventDefault();

      props.modelValue.getPillRanges().forEach(function(pillRange) {
        if (pillRange === pillRangeWithCaret) {
          return false;
        }
        indexOfRangeWithCaret++;
      });

      var pillEl = ui.decorator.querySelectorAll[indexOfRangeWithCaret];

      if (pillEl) {
        pillEl.focus();
      }
    }
  }
}

function isIndexWithinRange(index, range) {
  return index >= range.positionStart && index <= range.positionEnd;
}

function updateStorageTimer(props) {
  if (!props.options.storageKey) {
    return;
  }

  if (!props.typingInterval) {
    props.typingInterval = setInterval(function() {
      maybeStoreModelValue(props);
    }, 500);
  }

  clearTimeout(props.typingTimeout);
  props.typingTimeout = setTimeout(clearTypingWatch, 500);
}

function clearTypingTimers(props) {
  clearTypingWatch(props);
  clearTimeout(props.typingTimeout);
}

function clearStorageState(props) {
  maybeStoreModelValue(props, true);
}

function clearTypingWatch(props) {
  clearInterval(props.typingInterval);
  props.typingInterval = null;
}

function maybeStoreModelValue(props, isClearLS) {
  if (props.options.storageKey) {
    if (isClearLS || !props.modelValue.text) {
      localStorage.removeItem(props.options.storageKey);
    } else {
      localStorage.setItem(props.options.storageKey, props.modelValue.getStorageValue());
    }
  }
}

function getStoredModelValue(props) {
  var storedValue = props.options.storageKey && localStorage.getItem(props.options.storageKey) || null;

  if (storedValue) {
    var instantiatedPills = [];
    if (storedValue.pillRanges) {
      storedValue.pillRanges.forEach(function(ref) {
        props.getPillCorpus(props).forEach(function(corpusRef) {
          if (corpusRef.id === ref.id) {
            var refInstance = RiqTextAtReference(ref.id, ref.value, ref.text.from(1), ref.searchText, ref.className, ref.positionStart);
            instantiatedPills.push(refInstance);
            return false;
          }
        });
      });
    }
    return RiqTextModelValue(storedValue.text, instantiatedPills);
  }

  return null;
}<commit_msg>decorator events and pill studd<commit_after>var cleanText = require('./util/cleanText');
var initUI = require('./UI');

module.exports = {
  create: create
};

function create(container, pillCorpus, options) {
  var props = initProps(pillCorpus, options);
  var pillerInstance = {
    UI: initUI(container),
    destroy: destroy.bind(null, props)
  };

  defineModelValueOnInstance(pillerInstance, props);
  registerTextareaEvents(pillerInstance.UI, props);

  return pillerInstance;
}

function initProps(pillCorpus, options) {
  var props = {
    options: options,
    getPillCorpus: function() {
      return typeof pillCorpus === 'function' && pillCorpus() || pillCorpus || [];
    }
  };

  return props;
}

function destroy(props) {
  clearTypingTimers(props);
  clearTimeout(props.focusTimeout);
  clearTimeout(props.postInputCleanupTimeout);
}

function defineModelValueOnInstance(pillerInstance, props) {
  Object.defineProperty(pillerInstance, 'modelValue', {
    enumerable: true,
    get: function() {
      return props.modelValue;
    },
    set: setModelValue.bind(null, pillerInstance.UI, props)
  });
}

function registerTextareaEvents(ui, props) {
  var onPreInputEventSetupListener = onPreInputEventSetup.bind(null, ui, props);

  ['keydown', 'keypress', 'paste', 'cut'].forEach(function(eventName) {
    ui.textarea.addEventListener(eventName, onPreInputEventSetupListener);
  });

  ['drop', 'dragstart'].forEach(function(eventName) {
    ui.textarea.addEventListener(eventName, function(e) {
      e.preventDefault();
    });
  });

  ui.textarea.addEventListener('keydown', onPreInputKeydown.bind(null, ui, props));
  ui.textarea.addEventListener('keypress', onPreInputKeypress.bind(null, ui, props));
  ui.textarea.addEventListener('keyup', onPreInputKeyup.bind(null, ui, props));
  ui.textarea.addEventListener('focus', onTextareaFocus.bind(null, ui, props));
  ui.textarea.addEventListener('blur', onTextareaBlur.bind(null, ui, props));
  ui.textarea.addEventListener('input', onTextareaInput.bind(null, ui, props));
}

function registerDecoratorEvents(ui, props) {
  addDelegatedPillListener(ui, 'keydown', onPillKeydown.bind(null, ui, props));
  addDelegatedPillListener(ui, 'focusin', onPillFocus.bind(null, ui, props));
  addDelegatedPillListener(ui, 'focusout', onPillBlur.bind(null, ui, props));
}

function addDelegatedPillListener(ui, eventName, listener) {
  ui.decorator.addEventListener(eventName, function(e) {
    if (e.target.classList.contains('js-piller-pill')) {
      listener(e);
    }
  });
}

function onPreInputEventSetup(ui, props, e) {
  props.preInputEvent = e;
  props.preInputVal = ui.textarea.value;
  storePreInputSelection(ui, props);

  //timeout needed to reset preInput declarations above after all key and input events fire
  clearTimeout(props.postInputCleanupTimeout);
  props.postInputCleanupTimeout = setTimeout(postInputCleanup.bind(null, props));
}

function onPreInputKeydown(ui, props, e) {
  var isValidEvent = false;
  var preventIfAtStart = false;
  var preventIfAtEnd = false;

  props.originalKeyEvent = e;

  switch (e.which) {
    case 37: // LEFT_ARROW
    case 8: // BACKSPACE
      preventIfAtStart = isValidEvent = true;
      break;
    case 39: // RIGHT_ARROW
    case 46: // DELETE
      preventIfAtEnd = isValidEvent = true;
      break;
  }

  if (isValidEvent && props.preInputSelStart === props.preInputSelEnd) {
    maybeFocusPill(ui, props, e, preventIfAtStart, preventIfAtEnd);
  }
}

function onPillFocus(ui) {
  ui.textarea.classList.add('focus');
}

function onPillBlur(ui) {
  ui.textarea.classList.remove('focus');
}

function onPreInputKeypress(ui, props, e) {
  props.originalKeyEvent = e;
}

function onPreInputKeyup(ui, props, e) {
  switch (e.which) {
    case 37: // LEFT_ARROW
    case 38: // UP_ARROW
    case 39: // RIGHT_ARROW
    case 40: // DOWN_ARROW
      storePreInputSelection(ui, props);
      maybeFocusPill(ui, props, e, true, true);
      break;
  }
}

function onTextareaFocus(ui, props, e) {
  //timeout needed to reflect correct selection start & end in textarea
  clearTimeout(props.focusTimeout);
  props.focusTimeout = setTimeout(function() {
    storePreInputSelection(ui, props);
    maybeFocusPill(ui, props, e, true, true);
  });
}

function onTextareaBlur(ui, props) {
  clearTimeout(props.focusTimeout);
}

function onTextareaInput(ui, props) {
  props.modelValue.text = ui.textarea.value;

  if (!props.preInputVal || !props.modelValue.text) {
    props.modelValue.clearPillRanges();
  } else if (props.preInputEvent && props.preInputSelStart === props.preInputSelEnd) { //don't continue for selection due to Mac difference with backspace for single word selection
    var prevType = props.preInputEvent.type.toLowerCase();
    var hasShortcutKey = props.preInputEvent.ctrlKey || props.preInputEvent.metaKey || props.preInputEvent.altKey;

    if (props.preInputEvent.which === 8 && !hasShortcutKey) { // BACKSPACE
      updateRanges(props, props.preInputSelStart - 1, props.preInputSelEnd);
    } else if (props.preInputEvent === 46 && !hasShortcutKey) { // DELETE
      updateRanges(props, props.preInputSelStart, props.preInputSelEnd + 1);
    } else if (prevType === 'keypress' && !hasShortcutKey) {
      updateRanges(props, props.preInputSelStart, props.preInputSelEnd, 1);
    } else {
      updateRangesForStringDiff(props);
    }
  } else {
    updateRangesForStringDiff(props);
  }

  synchronize(ui, props);
  postInputCleanup(props);
}

function onPillKeydown(ui, props, e) {
  e.preventDefault();

  if (e.which !== 27) {
    e.stopPropagation();
  }

  switch (e.which) {
    case 8: // BACKSPACE
    case 46: // DELETE
      onRemovePill(ui, props, e);
      break;
    case 88: // X
      if (e.metaKey && !e.shiftKey) {
        onRemovePill(ui, props, e);
      }
      break;
    case 68: // D
      if (e.ctrlKey && !e.shiftKey) {
        onRemovePill(ui, props, e);
      }
      break;
    case 9: // TAB
    case 37: // LEFT_ARROW
    case 38: // UP_ARROW
    case 39: // RIGHT_ARROW
    case 40: // DOWN_ARROW
      e.preventDefault();
      var pillObj = getPillRangeFromEvent(e);
      var newCaretPos = pillObj.positionStart;

      if ((e.which === 9 && !e.shiftKey) || e.which === 39 || e.which === 40) {
        newCaretPos = pillObj.positionEnd;
      }

      setCaretPosition(ui.textarea, newCaretPos);
      break;
  }
}

function onRemovePill(ui, props, e) {
  var pillRange = getPillRangeFromEvent(ui, props, e);

  e.preventDefault();
  props.modelValue.removePillRange(pillRange);
  updateRanges(props, pillRange.positionStart, pillRange.positionEnd, 0);
  synchronize(ui, props);
  setCaretPosition(ui.textarea, pillRange.positionStart);
}

function getPillRangeFromEvent(ui, props, e) {
  var rangeIndex = ui.decorator.querySelectorAll('.js-piller-pill').indexOf(e.target);
  return props.modelValue.getPillRanges()[rangeIndex];
}

function updateRanges(props, changeStart, changeEnd, insertionCount) {
  var indexDelta = changeStart - changeEnd + (insertionCount || 0);
  var retainedPillRanges = [];

  props.modelValue.getPillRanges().forEach(function(pillRange) {
    //update position for and retain pill when the change ends before it
    if (changeEnd <= pillRange.positionStart) {
      pillRange.positionStart = pillRange.positionStart + indexDelta;
      retainedPillRanges.push(pillRange);
    }
    //retain pill when the change starts after it
    else if (changeStart >= pillRange.positionEnd) {
      retainedPillRanges.push(pillRange);
    }
  });

  props.modelValue.setPillRanges(retainedPillRanges);
}

function updateRangesForStringDiff(props) {
  var startDiff = getDifferenceStartIdx(props);
  var endDiff;

  if (startDiff < props.modelValue.text.length || startDiff < props.preInputVal.length) {
    endDiff = getDifferenceEndIdx(props, startDiff);
  } else {
    endDiff = Math.max(props.modelValue.text.length, props.preInputVal.length);
  }

  var insertionCount = endDiff + props.modelValue.text.length - props.preInputVal.length - startDiff;

  updateRanges(props, startDiff, endDiff, insertionCount > 0 ? insertionCount : 0);
}

function getDifferenceStartIdx(props) {
  var startDiff;

  for (startDiff = 0; startDiff < props.modelValue.text.length && startDiff < props.preInputVal.length; ++startDiff) {
    if (props.modelValue.text[startDiff] !== props.preInputVal[startDiff]) {
      break;
    }
  }

  return startDiff;
}

function getDifferenceEndIdx(props, startDiff) {
  var endDiff, newValIdx;
  if (startDiff === props.preInputVal.length) {
    endDiff = startDiff;
  } else if (startDiff === props.modelValue.text.length) {
    endDiff = props.preInputVal.length;
  } else {
    var newValLengthDiff = props.modelValue.text.length - props.preInputVal.length;

    for (endDiff = props.preInputVal.length, newValIdx = endDiff + newValLengthDiff; endDiff > startDiff && newValIdx > startDiff; endDiff--, newValIdx = endDiff + newValLengthDiff) {
      if (props.preInputVal[endDiff - 1] !== props.modelValue.text[endDiff + newValLengthDiff - 1]) {
        break;
      }
    }
  }
  return endDiff;
}

function storePreInputSelection(ui, props) {
  props.preInputSelStart = ui.textarea.selectionStart;
  props.preInputSelEnd = ui.textarea.selectionEnd;
}

function postInputCleanup(props) {
  //reset vars to account for input event that might not have a preInputEvent (e.g. spell correction only fires input event)
  props.preInputEvent = null;
  props.preInputVal = props.modelValue.text;
}

function synchronize(ui, props, newModelValue) {
  props.modelValue = newModelValue || props.modelValue || RiqTextModelValue();
  ui.textarea.value = props.modelValue.text;
  updateDecorator(ui, props);
  updateStorageTimer();
}

function setModelValue(ui, props, newModelValue) {
  clearTypingTimers();

  if (newModelValue) {
    synchronize(ui, props, newModelValue);
    maybeStoreModelValue(props);
  } else if (props.modelValue) {
    clearStorageState(props);
  } else {
    newModelValue = getStoredModelValue(props);
  }

  synchronize(newModelValue || RiqTextModelValue());
}

function updateDecorator(ui, props) {
  var cleanHtml = getCleanHtmlWithRanges(ui, props);

  //  is an invisible character that gives a true height to the div when
 is ignored for size
  if (cleanHtml.endsWith('
')) {
    cleanHtml += ' ';
  }

  ui.decorator.innerHTML = cleanHtml;

  var decoratorHeight = ui.decorator.getBoundingClientRect().height;

  if (decoratorHeight !== props.elementHeight) {
    props.elementHeight = decoratorHeight;
    ui.textarea.style.height = props.elementHeight + 'px';
  }
}

function getCleanHtmlWithRanges(ui, props) {
  var lastPositionEnd = 0;
  var cleanResult = '';

  props.modelValue.getPillRanges().forEach(function(range) {
    cleanResult += cleanText(props.modelValue.text.substring(lastPositionEnd, range.positionStart), true);
    cleanResult += range.html;
    lastPositionEnd = range.positionEnd;
  });

  cleanResult += cleanText(props.modelValue.text.substring(lastPositionEnd), true);

  return cleanResult;
}

function maybeFocusPill(ui, props, e, preventIfAtStart, preventIfAtEnd) {
  if (props.originalKeyEvent && !props.originalKeyEvent.ctrlKey && !props.originalKeyEvent.metaKey && props.preInputSelStart === props.preInputSelEnd) {
    var pillRangeWithCaret = props.modelValue.getPillRanges().some(function(pillRange) {
      if (isIndexWithinRange(props.preInputSelStart, pillRange) &&
        !(preventIfAtStart && props.preInputSelStart === pillRange.positionStart) &&
        !(preventIfAtEnd && props.preInputSelStart === pillRange.positionEnd)) {
        pillRangeWithCaret = pillRange;
        return true;
      }
    });

    if (pillRangeWithCaret) {
      var indexOfRangeWithCaret = 0;
      e.preventDefault();

      props.modelValue.getPillRanges().forEach(function(pillRange) {
        if (pillRange === pillRangeWithCaret) {
          return false;
        }
        indexOfRangeWithCaret++;
      });

      var pillEl = ui.decorator.querySelectorAll[indexOfRangeWithCaret];

      if (pillEl) {
        pillEl.focus();
      }
    }
  }
}

function isIndexWithinRange(index, range) {
  return index >= range.positionStart && index <= range.positionEnd;
}

function updateStorageTimer(props) {
  if (!props.options.storageKey) {
    return;
  }

  if (!props.typingInterval) {
    props.typingInterval = setInterval(function() {
      maybeStoreModelValue(props);
    }, 500);
  }

  clearTimeout(props.typingTimeout);
  props.typingTimeout = setTimeout(clearTypingWatch, 500);
}

function clearTypingTimers(props) {
  clearTypingWatch(props);
  clearTimeout(props.typingTimeout);
}

function clearStorageState(props) {
  maybeStoreModelValue(props, true);
}

function clearTypingWatch(props) {
  clearInterval(props.typingInterval);
  props.typingInterval = null;
}

function maybeStoreModelValue(props, isClearLS) {
  if (props.options.storageKey) {
    if (isClearLS || !props.modelValue.text) {
      localStorage.removeItem(props.options.storageKey);
    } else {
      localStorage.setItem(props.options.storageKey, props.modelValue.getStorageValue());
    }
  }
}

function getStoredModelValue(props) {
  var storedValue = props.options.storageKey && localStorage.getItem(props.options.storageKey) || null;

  if (storedValue) {
    var instantiatedPills = [];

    if (storedValue.pillRanges) {
      storedValue.pillRanges.forEach(function(pill) {
        props.getPillCorpus(props).forEach(function(corpusPill) {
          if (corpusPill.id === pill.id) {
            var pillInstance = RiqTextAtReference(pill.id, pill.value, pill.text.substring(1), pill.searchText, pill.className, pill.positionStart);
            instantiatedPills.push(pillInstance);
            return false;
          }
        });
      });
    }

    return RiqTextModelValue(storedValue.text, instantiatedPills);
  }

  return null;
}

function setCaretPosition(el, caretPosition) {
  el.focus();
  el.selectionStart = caretPosition;
  el.selectionEnd = caretPosition;
}<|endoftext|>


<commit_before>/*
 * grunt-topcoat
 * https://github.com/krjoseph/grunt-topcoat
 *
 */

'use strict';

module.exports = function(grunt) {

    // Please see the grunt documentation for more information regarding task
    // creation: https://github.com/gruntjs/grunt/blob/devel/docs/toc.md
    grunt.registerMultiTask('topcoat', 'Downloads specified repos for use in the TopDoat build process.', function() {
        var _ = grunt.util._,
            async = grunt.util.async,
            done = this.async(),
            spawn = grunt.util.spawn,
            file = grunt.file,
            options = this.options(),
            prefix = "https://github.com/",
            suffix = "/archive/",
            ext = ".zip",
            srcPath = options.srcPath || "src/",
            deps = options.repos,
            controls = deps.controls || {},
            skins = deps.skins || {},
            theme = deps.theme || {},
            controlsUrls, skinsUrls;

        var getRepoName = function(key) {
                if (key) return key.split('/')[1];
            };

        var downloadResources = function(obj, path, callback) {
                var urls = [];
                _.forIn(obj, function(value, key) {
                    var name = getRepoName(key);
                    urls.push({
                        name: name,
                        url: prefix + key + suffix + value + ext
                    });
                });
                console.log("URLS:", urls);

                async.forEachSeries(urls, function(obj, next) {
                    var zipPath = path + obj.name + ext;
                    grunt.log.writeln("Downloading: " + zipPath);
                    var downloadProcess = spawn({
                        cmd: 'curl',
                        args: ['-L', '-o', zipPath, obj.url]
                    }, next);

                    downloadProcess.stdout.pipe(process.stdout);
                    downloadProcess.stderr.pipe(process.stderr);
                }, callback);
            }

            // If a version number is given we assume it is a tag and use this url
            // convention:
            // https://github.com/topcoat/topcoat/archive/0.1.0.zip
            //
            // TODO:
            // If no version is given we use the download api with this url convention:
            // https://api.github.com/repos/user/repo/zipball/dev
            //
            // Loop over controls object
            // Example:
            // "controls": {
            //    "topcoat/button": "0.1.0"
            //  }
            //
            //  controls becomes the name of the directory under src. src/controls
            //  download url is constructed with:
            //    prefix + key + suffix + value + ext
            //
            // Example:
            // https://github.com/topcoat/button/archive/0.1.0.zip
            //
            async.series([

            function(callback) {
                if (!_.isEmpty(controls)) {
                    var controlsPath = srcPath + "controls/";
                    file.mkdir(controlsPath);
                    downloadResources(controls, controlsPath, callback);
                } else {
                    callback();
                    grunt.log.writeln("No controls specified");
                }
            },

            function(callback) {
                // Download theme into srcPath/theme
                if (!_.isEmpty(theme)) {
                    downloadResources(theme, srcPath, callback);
                } else {
                    callback();
                    grunt.log.writeln("No theme specified");
                }
            },

            function(callback) {
                // Download theme into srcPath/theme
                if (!_.isEmpty(skins)) {
                    var skinsPath = srcPath + "skins/";
                    file.mkdir(skinsPath);
                    downloadResources(skins, skinsPath, callback);
                } else {
                    callback();
                    grunt.log.writeln("No skins specified");
                }
            }, function() {
                done();
            }]);

    });

};
<commit_msg>Update topcoat.js<commit_after>/*
 * grunt-topcoat
 *
 */

'use strict';

module.exports = function(grunt) {

    // Please see the grunt documentation for more information regarding task
    // creation: https://github.com/gruntjs/grunt/blob/devel/docs/toc.md
    grunt.registerMultiTask('topcoat', 'Downloads specified repos for use in the TopDoat build process.', function() {
        var _ = grunt.util._,
            async = grunt.util.async,
            done = this.async(),
            spawn = grunt.util.spawn,
            file = grunt.file,
            options = this.options(),
            prefix = "https://github.com/",
            suffix = "/archive/",
            ext = ".zip",
            srcPath = options.srcPath || "src/",
            deps = options.repos,
            controls = deps.controls || {},
            skins = deps.skins || {},
            theme = deps.theme || {},
            controlsUrls, skinsUrls;

        var getRepoName = function(key) {
                if (key) return key.split('/')[1];
            };

        var downloadResources = function(obj, path, callback) {
                var urls = [];
                _.forIn(obj, function(value, key) {
                    var name = getRepoName(key);
                    urls.push({
                        name: name,
                        url: prefix + key + suffix + value + ext
                    });
                });
                console.log("URLS:", urls);

                async.forEachSeries(urls, function(obj, next) {
                    var zipPath = path + obj.name + ext;
                    grunt.log.writeln("Downloading: " + zipPath);
                    var downloadProcess = spawn({
                        cmd: 'curl',
                        args: ['-L', '-o', zipPath, obj.url]
                    }, next);

                    downloadProcess.stdout.pipe(process.stdout);
                    downloadProcess.stderr.pipe(process.stderr);
                }, callback);
            }

            // If a version number is given we assume it is a tag and use this url
            // convention:
            // https://github.com/topcoat/topcoat/archive/0.1.0.zip
            //
            // TODO:
            // If no version is given we use the download api with this url convention:
            // https://api.github.com/repos/user/repo/zipball/dev
            //
            // Loop over controls object
            // Example:
            // "controls": {
            //    "topcoat/button": "0.1.0"
            //  }
            //
            //  controls becomes the name of the directory under src. src/controls
            //  download url is constructed with:
            //    prefix + key + suffix + value + ext
            //
            // Example:
            // https://github.com/topcoat/button/archive/0.1.0.zip
            //
            async.series([

            function(callback) {
                if (!_.isEmpty(controls)) {
                    var controlsPath = srcPath + "controls/";
                    file.mkdir(controlsPath);
                    downloadResources(controls, controlsPath, callback);
                } else {
                    callback();
                    grunt.log.writeln("No controls specified");
                }
            },

            function(callback) {
                // Download theme into srcPath/theme
                if (!_.isEmpty(theme)) {
                    downloadResources(theme, srcPath, callback);
                } else {
                    callback();
                    grunt.log.writeln("No theme specified");
                }
            },

            function(callback) {
                // Download theme into srcPath/theme
                if (!_.isEmpty(skins)) {
                    var skinsPath = srcPath + "skins/";
                    file.mkdir(skinsPath);
                    downloadResources(skins, skinsPath, callback);
                } else {
                    callback();
                    grunt.log.writeln("No skins specified");
                }
            }, function() {
                done();
            }]);

    });

};
<|endoftext|>


<commit_before>import defaultsDeep from 'lodash.defaultsdeep';

import axis from './axis';
import { getBreakpointLabel } from './breakpoint';
import bounds from './bounds';
import defaultConfiguration from './config';
import dropLine from './dropLine';
import zoom from './zoom';
import { addMetaballsDefs } from './metaballs';

import './style.css';
import { withinRange } from './withinRange';

// do not export anything else here to keep window.eventDrops as a function
export default ({ d3 = window.d3, ...customConfiguration }) => {
    const initChart = selection => {
        selection.selectAll('svg').remove();

        const root = selection.selectAll('svg').data(selection.data());
        root.exit().remove();

        const config = defaultsDeep(
            customConfiguration || {},
            defaultConfiguration(d3)
        );

        const {
            drops,
            zoom: zoomConfig,
            drop: { onClick, onMouseOut, onMouseOver },
            metaballs,
            label: { width: labelWidth },
            line: { height: lineHeight },
            range: { start: rangeStart, end: rangeEnd },
            margin,
            breakpoints,
        } = config;

        const getEvent = () => d3.event; // keep d3.event mutable see https://github.com/d3/d3/issues/2733

        // Follow margins conventions (https://bl.ocks.org/mbostock/3019563)
        const width = selection.node().clientWidth - margin.left - margin.right;

        const xScale = d3
            .scaleTime()
            .domain([rangeStart, rangeEnd])
            .range([0, width - labelWidth]);

        chart._scale = xScale;
        chart.currentBreakpointLabel = getBreakpointLabel(
            breakpoints,
            global.innerWidth
        );

        const svg = root
            .enter()
            .append('svg')
            .attr('width', width)
            .classed('event-drop-chart', true);

        if (zoomConfig) {
            svg.call(zoom(d3, svg, config, xScale, draw, getEvent));
        }

        if (metaballs) {
            svg.call(addMetaballsDefs(config));
        }

        svg
            .merge(root)
            .attr(
                'height',
                d => (d.length + 1) * lineHeight + margin.top + margin.bottom
            );

        svg
            .append('g')
            .classed('viewport', true)
            .attr('transform', `translate(${margin.left},${margin.top})`)
            .call(draw(config, xScale));
    };

    const chart = selection => {
        chart._initialize = () => initChart(selection);
        chart._initialize();

        global.addEventListener('resize', chart._initialize, true);
    };

    chart.scale = () => chart._scale;
    chart.filteredData = () => chart._filteredData;
    chart.destroy = (callback = () => {}) => {
        global.removeEventListener('resize', chart._initialize, true);
        callback();
    };

    const draw = (config, scale) => selection => {
        const { drop: { date: dropDate } } = config;

        const dateBounds = scale.domain().map(d => new Date(d));
        const filteredData = selection.data().map(dataSet => {
            if (!Array.isArray(dataSet)) {
                throw new Error(
                    'Selection data is not an array. Are you sure you provided an array of arrays to `data` function?'
                );
            }

            return dataSet.map(row => {
                if (!row.fullData) {
                    row.fullData = config.drops(row);
                    if (!row.fullData) {
                        throw new Error(
                            'No drops data has been found. It looks by default in the `data` property. You can use the `drops` configuration parameter to tune it.'
                        );
                    }
                }

                row.data = row.fullData.filter(d =>
                    withinRange(dropDate(d), dateBounds)
                );

                return row;
            });
        });

        chart._scale = scale;
        chart._filteredData = filteredData[0];

        selection
            .data(filteredData)
            .call(dropLine(config, scale))
            .call(bounds(config, scale))
            .call(axis(d3, config, scale, chart.currentBreakpointLabel));
    };

    chart.draw = draw;

    return chart;
};
<commit_msg>Fixed bug here global was undefined With 1.3.0 Events Drop and D3 5.7.0, the global in index.js was undefined. Therefore adding it to the parameters. If there is a better way of doing this please let me know and I will modify it.<commit_after>import defaultsDeep from 'lodash.defaultsdeep';

import axis from './axis';
import { getBreakpointLabel } from './breakpoint';
import bounds from './bounds';
import defaultConfiguration from './config';
import dropLine from './dropLine';
import zoom from './zoom';
import { addMetaballsDefs } from './metaballs';

import './style.css';
import { withinRange } from './withinRange';

// do not export anything else here to keep window.eventDrops as a function
export default ({
    d3 = window.d3,
    global = window,
    ...customConfiguration
}) => {
    const initChart = selection => {
        selection.selectAll('svg').remove();

        const root = selection.selectAll('svg').data(selection.data());
        root.exit().remove();

        const config = defaultsDeep(
            customConfiguration || {},
            defaultConfiguration(d3)
        );

        const {
            drops,
            zoom: zoomConfig,
            drop: { onClick, onMouseOut, onMouseOver },
            metaballs,
            label: { width: labelWidth },
            line: { height: lineHeight },
            range: { start: rangeStart, end: rangeEnd },
            margin,
            breakpoints,
        } = config;

        const getEvent = () => d3.event; // keep d3.event mutable see https://github.com/d3/d3/issues/2733

        // Follow margins conventions (https://bl.ocks.org/mbostock/3019563)
        const width = selection.node().clientWidth - margin.left - margin.right;

        const xScale = d3
            .scaleTime()
            .domain([rangeStart, rangeEnd])
            .range([0, width - labelWidth]);

        chart._scale = xScale;
        chart.currentBreakpointLabel = getBreakpointLabel(
            breakpoints,
            global.innerWidth
        );

        const svg = root
            .enter()
            .append('svg')
            .attr('width', width)
            .classed('event-drop-chart', true);

        if (zoomConfig) {
            svg.call(zoom(d3, svg, config, xScale, draw, getEvent));
        }

        if (metaballs) {
            svg.call(addMetaballsDefs(config));
        }

        svg.merge(root).attr(
            'height',
            d => (d.length + 1) * lineHeight + margin.top + margin.bottom
        );

        svg.append('g')
            .classed('viewport', true)
            .attr('transform', `translate(${margin.left},${margin.top})`)
            .call(draw(config, xScale));
    };

    const chart = selection => {
        chart._initialize = () => initChart(selection);
        chart._initialize();

        global.addEventListener('resize', chart._initialize, true);
    };

    chart.scale = () => chart._scale;
    chart.filteredData = () => chart._filteredData;
    chart.destroy = (callback = () => {}) => {
        global.removeEventListener('resize', chart._initialize, true);
        callback();
    };

    const draw = (config, scale) => selection => {
        const {
            drop: { date: dropDate },
        } = config;

        const dateBounds = scale.domain().map(d => new Date(d));
        const filteredData = selection.data().map(dataSet => {
            if (!Array.isArray(dataSet)) {
                throw new Error(
                    'Selection data is not an array. Are you sure you provided an array of arrays to `data` function?'
                );
            }

            return dataSet.map(row => {
                if (!row.fullData) {
                    row.fullData = config.drops(row);
                    if (!row.fullData) {
                        throw new Error(
                            'No drops data has been found. It looks by default in the `data` property. You can use the `drops` configuration parameter to tune it.'
                        );
                    }
                }

                row.data = row.fullData.filter(d =>
                    withinRange(dropDate(d), dateBounds)
                );

                return row;
            });
        });

        chart._scale = scale;
        chart._filteredData = filteredData[0];

        selection
            .data(filteredData)
            .call(dropLine(config, scale))
            .call(bounds(config, scale))
            .call(axis(d3, config, scale, chart.currentBreakpointLabel));
    };

    chart.draw = draw;

    return chart;
};
<|endoftext|>


<commit_before>'use strict';

module.exports = function (grunt) {

    var vanilli = require('vanilli');

    grunt.registerTask('vanilli', 'Start/Stop Vanilli', function (target) {
        var config = this.options(),
            targets = {
                start: function () {
                    vanilli.start(config);
                },
                stop: function () {
                    vanilli.stop();
                }
            };

        if (targets[target]) {
            targets[target]();
        } else {
            throw new Error("Unrecognised target '" + this.target + "'.");
        }
    });
};
<commit_msg>Update to latest bootstrap technique.<commit_after>'use strict';
var vanilli = require('vanilli');

module.exports = function (grunt) {
    var vanilliServer;

    grunt.registerTask('vanilli', 'Start/Stop Vanilli', function (target) {

        var config  = this.options(),
            targets = {
                start: function () {
                    vanilliServer = vanilli.init(config);
                    vanilliServer.listen(config.port);
                },
                stop: function () {
                    vanilliServer.stop();
                }
            };

        if (targets[ target ]) {
            targets[ target ]();
        } else {
            throw new Error("Unrecognised target '" + this.target + "'.");
        }
    });
};
<|endoftext|>


<commit_before>const {create, assign} = Object
  , {stringify} = JSON
  , client = require('./client')
  , validators = require('./validators')
  ;

const recentBillTypes = new Set([
  'introduced',
  'updated',
  'passed',
  'major'
]);

function isValidChamber(chamber) {
  return new Promise((resolve, reject) => validators.isValidChamber(chamber)
    ? resolve(true)
    : reject(new Error(`Received invalid chamber: ${stringify(chamber)}`))
  );
}

function isValidCongress(session, earliestSession) {
  return new Promise((resolve, reject) => validators.isValidCongress(session, earliestSession)
    ? resolve(true)
    : reject(new Error(`Received invalid congress: ${stringify(session)}`))
  );
}

function isValidType(type, typeSet, descriptor) {
  return new Promise((resolve, reject) => validators.isValidType(type, typeSet)
    ? resolve(true)
    : reject(new Error(`Received invalid ${descriptor}: ${stringify(type)}`))
  );
}

const proto = {
  /**
   * Resolves to summaries of the 20 most recent bills by type. For the current Congress,
   * “recent bills” can be one of four types. For previous Congresses, “recent bills” means the last
   * 20 bills of that Congress.
   *
   * @param {String} chamber 'senate' or 'house'
   * @param {String} recentBillType
   * @param {Object} [{congress = this.congress, offset = 0}={}]
   * @returns {Promise}
   */
  getRecentBills(chamber, recentBillType, {congress = this.congress, offset = 0} = {}) {
    return Promise.all([
      isValidChamber(chamber),
      isValidCongress(congress, 105),
      isValidType(recentBillType, recentBillTypes, 'recent bill type')
    ]).then(() => this.client.get(`${congress}/${chamber}/bills/${recentBillType}`, offset));
  }
};

module.exports = {
  create(key, congress) {
    if (congress && !validators.isValidCongress(congress)) {
      throw new Error(`Received invalid congress: ${stringify(congress)}`);
    }
    return assign(create(proto), {
      congress,
      client: client.create(key)
    });
  }
};
<commit_msg>Renaming these functions and no longer resolving to anything. It should be enough that it resolves.<commit_after>const {create, assign} = Object
  , {stringify} = JSON
  , client = require('./client')
  , validators = require('./validators')
  ;

const recentBillTypes = new Set([
  'introduced',
  'updated',
  'passed',
  'major'
]);

function validateChamber(chamber) {
  return new Promise((resolve, reject) => validators.isValidChamber(chamber)
    ? resolve()
    : reject(new Error(`Received invalid chamber: ${stringify(chamber)}`))
  );
}

function validateCongress(session, earliestSession) {
  return new Promise((resolve, reject) => validators.isValidCongress(session, earliestSession)
    ? resolve()
    : reject(new Error(`Received invalid congress: ${stringify(session)}`))
  );
}

function validateType(type, typeSet, descriptor) {
  return new Promise((resolve, reject) => validators.isValidType(type, typeSet)
    ? resolve()
    : reject(new Error(`Received invalid ${descriptor}: ${stringify(type)}`))
  );
}

const proto = {
  /**
   * Resolves to summaries of the 20 most recent bills by type. For the current Congress,
   * “recent bills” can be one of four types. For previous Congresses, “recent bills” means the last
   * 20 bills of that Congress.
   *
   * @param {String} chamber 'senate' or 'house'
   * @param {String} recentBillType
   * @param {Object} [{congress = this.congress, offset = 0}={}]
   * @returns {Promise}
   */
  getRecentBills(chamber, recentBillType, {congress = this.congress, offset = 0} = {}) {
    return Promise.all([
      validateChamber(chamber),
      validateCongress(congress, 105),
      validateType(recentBillType, recentBillTypes, 'recent bill type')
    ]).then(() => this.client.get(`${congress}/${chamber}/bills/${recentBillType}`, offset));
  }
};

module.exports = {
  create(key, congress) {
    if (congress && !validators.isValidCongress(congress)) {
      throw new Error(`Received invalid congress: ${stringify(congress)}`);
    }
    return assign(create(proto), {
      congress,
      client: client.create(key)
    });
  }
};
<|endoftext|>


<commit_before>var isPrime = function isPrime(num) {
  for (var i = 2; i <= num; i++) {
    if (num % 2 === 0) {
      return false;
    }
  }
  return true;
};

var sumPrimes = function sumPrimes(num, callback) {
  if (isNaN(num)) {
    return callback(Error('Param must be a number'));
  }

  if (num < 0) {
    var err = new Error('Param must be > 0');
    return callback(err);
  }

  var sum = 0;
  for (var i = 0; i < num; i++) {
    if (isPrime(i) === true) {
      sum += i;
    }
  }

  var s = sum;
  return callback(null, s);
};

module.exports = {
  sumPrimes
};
<commit_msg>update style code space<commit_after>var isPrime = function isPrime(num) {
  for (var i = 2; i <= num; i++) {
    if (num % 2 === 0) {
      return false;
    }
  }
  return true;
};

var sumPrimes = function sumPrimes(num, callback) {
  if (isNaN(num)) {
    return callback(Error('Param must be a number'));
  }

  if (num < 0) {
    var err = new Error('Param must be > 0');
    return callback(err);
  }

  var sum = 0;
  for (var i = 0; i < num; i++) {
    if (isPrime(i) === true) {
      sum += i;
    }
  }

  var s = sum;
  return callback(null, s);
};

module.exports = {
  sumPrimes
};<|endoftext|>


<commit_before>var generateValues = function () {
    var MIN_VALUE = 1,
        MAX_VALUE = 100,
        results = [],
        isDivisibleBy2,
        isDivisibleBy3,
        isDivisibleBy5,
        number;

    for (number = MIN_VALUE; number <= MAX_VALUE; number++) {
        isDivisibleBy5 = number % 5 == 0;
        isDivisibleBy2 = number % 2 == 0;
        isDivisibleBy3 = number % 3 == 0;

        if (isDivisibleBy2 && isDivisibleBy5 && isDivisibleBy3) results.push("FizzBuzzPop");
        else if (isDivisibleBy2 && isDivisibleBy5) results.push("BuzzPop");
        else if (isDivisibleBy2 && isDivisibleBy3) results.push("FizzPop");
        else if (isDivisibleBy3 && isDivisibleBy5) results.push("FizzBuzz");
        else if (isDivisibleBy3) results.push("Fizz");
        else if (isDivisibleBy5) results.push("Buzz");
        else if (isDivisibleBy2) results.push("Pop");
        else results.push(number.toString());
    }

    return results;
};

module.exports = {
    generateValues: generateValues
};
<commit_msg>refactor(FizzBuzzPopCalculator): Renamed the results variable to values.<commit_after>var generateValues = function () {
    var MIN_VALUE = 1,
        MAX_VALUE = 100,
        values = [],
        isDivisibleBy2,
        isDivisibleBy3,
        isDivisibleBy5,
        number;

    for (number = MIN_VALUE; number <= MAX_VALUE; number++) {
        isDivisibleBy5 = number % 5 == 0;
        isDivisibleBy2 = number % 2 == 0;
        isDivisibleBy3 = number % 3 == 0;

        if (isDivisibleBy2 && isDivisibleBy5 && isDivisibleBy3) values.push("FizzBuzzPop");
        else if (isDivisibleBy2 && isDivisibleBy5) values.push("BuzzPop");
        else if (isDivisibleBy2 && isDivisibleBy3) values.push("FizzPop");
        else if (isDivisibleBy3 && isDivisibleBy5) values.push("FizzBuzz");
        else if (isDivisibleBy3) values.push("Fizz");
        else if (isDivisibleBy5) values.push("Buzz");
        else if (isDivisibleBy2) values.push("Pop");
        else values.push(number.toString());
    }

    return values;
};

module.exports = {
    generateValues: generateValues
};
<|endoftext|>