<commit_before>var fs = require('fs');
var gonzales = require('gonzales');
var util = require('util');
var _ = require('underscore');
var CleanCss = require('clean-css');

var htmlEls = ['a','abbr','acronym','address','applet','area','article','aside','audio','b','base','basefont','bdi','bdo','bgsound','big','blink','blockquote','body','br','button','canvas','caption','center','cite','code','col','colgroup','command','content','data','datalist','dd','del','details','dfn','dialog','dir','div','dl','dt','element','em','embed','fieldset','figcaption','figure','font','footer','form','frame','frameset','head','header','hgroup','hr','html','i','iframe','image','img','input','ins','isindex','kbd','keygen','label','legend','li','link','listing','main','map','mark','marquee','menu','menuitem','meta','meter','multicol','nav','nobr','noembed','noframes','noscript','object','ol','optgroup','option','output','p','param','picture','plaintext','pre','progress','q','rp','rt','rtc','ruby','s','samp','script','section','select','shadow','small','source','spacer','span','strike','strong','style','sub','summary','sup','table','tbody','td','template','textarea','tfoot','th','thead','time','title','tr','track','tt','u','ul','var','video','wbr','xmp'];

//////////////////////////////////////////////
// The main function is the "purify" function.
// Everything else is a helper
//////////////////////////////////////////////

var concatFiles = function(files){
  return files.reduce(function(total, file){
    return total + fs.readFileSync(file, 'utf8');
  }, '');
};

var getAllUsedCharactersInClasses = function(classes){
  var usedChars = {};

  classes.forEach(function(selector){
    for(var i = 0; i < selector.length; i++){
      var chr = selector[i];
      usedChars[chr] = true;
    }
  });

  return usedChars;
};

var getAllUsedWords = function(content, usedChars){
  var used = {};
  var word = "";

  for(var i = 0; i < content.length; i++){
    var chr = content[i];

    if(chr in usedChars){
      word += chr;
    } else {
      used[word] = true;
      word = "";
    }
  }

  return used;
};

var formatCSS = function(styles){
  styles = styles.split('\\n\\n').join('');
  styles = styles.split(' \\n').join('');
  styles = styles.split('   ').join('');
  styles = styles.split('}').join('}\\n');
  styles = styles.split('}\\n\\n').join('}\\n');
  styles = styles.split(' }').join('}');
  styles = styles.split(' }').join('}');

  return styles;
};

var extractIDsAndClassesFromCSS = function(css){
  var ids = [];
  var classes = [];

  for(var i = 0; i < css.length; i++){
    if(css[i] === 'clazz'){
      classes.push(css[i + 2]);
    }

    if(css[i] === 'shash'){
      ids.push(css[i + 1]);
    }
  }

  ids = _.uniq(ids);
  classes = _.uniq(classes);

  return {
    ids: ids,
    classes: classes
  };
};

var findWordsInWordHash = function(words, wordsHash){
  return _.filter(words, function(word){
    return wordsHash[word] ||
           _.every(word.split('-'), function(part){
             return wordsHash[part] || wordsHash[part + '-'] ||
                    wordsHash['-' + part + '-'] || wordsHash['-' + part];
           });
  });
};

var filterByUsedClassesAndHtmlEls = function(ast, classes, htmlEls){
  ast.forEach(function(branch){
    if(branch[0] !== 'ruleset'){
      return;
    }

    for(var i = 1; i < branch.length; i++){
      if(branch[i][0] !== 'selector'){
        continue;
      }

      var throwDelim = false;
      branch[i] = _.filter(branch[i], function(twig){
        var flatTwig = _.flatten(twig);

        if(flatTwig[0] === 'delim' && throwDelim){
          throwDelim = false;
          return false;
        }

        if(flatTwig[0] !== 'simpleselector'){
          return true;
        }

        var hasClass = flatTwig.indexOf('clazz') > -1;

        if(hasClass){
          for(var j = 1; j < flatTwig.length; j++){
            if(flatTwig[j] === 'clazz'){
              if(classes.indexOf(flatTwig[j + 2]) === -1){
                throwDelim = true;
                return false;
              }
            }
          }
        } else {
          for(var j = 1; j < flatTwig.length; j++){
            if(flatTwig[i] === 'ident' && htmlEls.indexOf(flatTwig[i + 1]) > -1){
              return true;
            }
          }

          throwDelim = true;
          return false;
        }

        return true;
      });

      if(branch[i][branch[i].length - 1][0] === 'delim'){
        branch[i] = branch[i].slice(0, branch[i].length - 1);
      }
    }
  });

  return _.filter(ast, function(branch){
    if(branch[0] !== 'ruleset'){
      return true;
    }

    var flatBranch = _.flatten(branch);
    return flatBranch.indexOf('simpleselector') > -1;
  });
};

var filterByUsedIds = function(ast, ids){
  return _.filter(ast, function(branch){
    var flatBranch = _.flatten(branch.slice());

    if(flatBranch[0] === 's'){
      return true;
    }

    for(var i = 0; i < flatBranch.length; i++){
      if(flatBranch[i] === 'shash'){
        return ids.indexOf(flatBranch[i + 1].toLowerCase()) > -1;
      }
    }

    return false;
  });
};

var removeUnusedMedias = function(atSign, classes){
  atSign.forEach(function(branch){
    if(branch[0] !== 'atruler'){
      return;
    }

    if(_.flatten(branch).indexOf('media') === -1){
      return;
    }
    for(var i = 1; i < branch.length; i++){
      if(branch[i][0] !== 'atrulers'){
        continue;
      }

      branch[i] = _.filter(branch[i], function(twig){
        if(twig[0] !== 'ruleset'){
          return true;
        }
        var flattened = _.flatten(twig);
        var flag = false;

        for(var j = 0; j < flattened.length; j++){
          if(flattened[j] === 'clazz'){
            if(classes.indexOf(flattened[j + 2]) > -1){
              flag = true;
            } else {
              return false;
            }
          }
        }

        return flag;
      });
    }
  });
};

var filterMediasByZeroClasses = function(atSign){
  return _.filter(atSign, function(branch){
    if(branch[0] !== 'atruler'){
      return true;
    }

    var flatBranch = _.flatten(branch);
    var count = 0;

    for(var i = 0; i < flatBranch.length; i++){
      if(flatBranch[i] === 'property'){
        count++;
      }
    }

    return count > 0;
  });
};

var reduceContent = function(content){
  return content
    .split('\n').join('')
    .replace(/\s\s+/g, ' ');
};

var DEFAULT_OPTIONS = {
  write: false,
  minify: false,
  info: false
};
////////////////////
// ARGUMENTS
// files    = an array of filepaths to html/js files OR a raw string of content to search through
// css      = an array of filepaths to css files OR a raw string of css to filter
// options  = {
//   write  : string (filepath to write purified css to. if false, function returns raw string)
//   minify : boolean (if true, will minify the purified css)
//   info   : boolean (if true, will log out stats of how much css was reduced)
// }
////////////////////
var purify = function(files, css, options, callback){
  if(typeof options === 'function'){
    callback = options;
    options = {};
  }
  options = options || {};
  options = _.extend({}, DEFAULT_OPTIONS, options);

  var startTime = new Date();

  var cssString = Array.isArray(css) ? concatFiles(css) : css;
  var content = Array.isArray(files) ? concatFiles(files) : files;
  content = reduceContent(content.toLowerCase());

  // Store starting length. Will be helpful later to show how much was reduced
  var beginningLength = cssString.length;

  // Turn css into abstract syntax tree
  var original = gonzales.srcToCSSP(cssString);

  // Tree with everything that begins with @ in css (@media, @keyframe, etc.)
  var atSign = _.filter(original, function(branch){
    return branch[0] === 'atruler' || branch[0] === 's';
  });

  // Tree with everything that doesn't start with an @ (classes, elements, ids, etc.)
  original = _.filter(original, function(branch){
    return branch[0] !== 'atruler';
  });

  // console.log(util.inspect(atSign, false, null));

  var flattenedCSS = _.flatten(original.slice());

  // Get list of things that are used
  var extraction = extractIDsAndClassesFromCSS(flattenedCSS);
  var classes = extraction.classes;
  var ids = extraction.ids;

  // Get hash of word-parts appearing in content
  var chrHash = getAllUsedCharactersInClasses(classes);
  var wordsInContent = getAllUsedWords(content, chrHash);

  // Narrow list down to things that are found in content
  classes = findWordsInWordHash(classes, wordsInContent);
  var filteredHtmlEls = findWordsInWordHash(htmlEls, wordsInContent);
  ids = findWordsInWordHash(ids, wordsInContent);

  // Narrow CSS tree down to things that remain on the list
  var stylesheet = filterByUsedClassesAndHtmlEls(original, classes, filteredHtmlEls);
  ids = filterByUsedIds(original, ids);
  removeUnusedMedias(atSign, classes);
  atSign = filterMediasByZeroClasses(atSign);

  // Turn tree back into css
  var idStyles = gonzales.csspToSrc(ids);
  var classStyles = gonzales.csspToSrc(stylesheet);
  var atStyles = gonzales.csspToSrc(atSign);

  // Combine and format
  var styles = classStyles + '\n' + atStyles + '\n' + idStyles;
  styles = JSON.parse(formatCSS(JSON.stringify(styles)));

  if(options.minify){
    styles = new CleanCss().minify(styles).styles;
  }

  if(options.info){
    console.log('##################################');
    console.log('Before purify, CSS was ' + beginningLength + ' chars long.');
    console.log('After purify, CSS is ' + styles.length + ' chars long. (' +
      Math.floor((beginningLength / styles.length * 10)) / 10  + ' times smaller)');
    console.log('##################################');
    console.log('This function took: ', new Date() - startTime, 'ms');
  }

  if(!options.output){
    if(callback){
      callback(styles);
    } else {
      return styles
    }
  } else {
    fs.writeFile(options.output, styles, function(err){
      if(err) return err;
    });
  }
};

module.exports = purify;
<commit_msg>Only add hyphens in check if word has many parts<commit_after>var fs = require('fs');
var gonzales = require('gonzales');
var util = require('util');
var _ = require('underscore');
var CleanCss = require('clean-css');

var htmlEls = ['a','abbr','acronym','address','applet','area','article','aside','audio','b','base','basefont','bdi','bdo','bgsound','big','blink','blockquote','body','br','button','canvas','caption','center','cite','code','col','colgroup','command','content','data','datalist','dd','del','details','dfn','dialog','dir','div','dl','dt','element','em','embed','fieldset','figcaption','figure','font','footer','form','frame','frameset','head','header','hgroup','hr','html','i','iframe','image','img','input','ins','isindex','kbd','keygen','label','legend','li','link','listing','main','map','mark','marquee','menu','menuitem','meta','meter','multicol','nav','nobr','noembed','noframes','noscript','object','ol','optgroup','option','output','p','param','picture','plaintext','pre','progress','q','rp','rt','rtc','ruby','s','samp','script','section','select','shadow','small','source','spacer','span','strike','strong','style','sub','summary','sup','table','tbody','td','template','textarea','tfoot','th','thead','time','title','tr','track','tt','u','ul','var','video','wbr','xmp'];

//////////////////////////////////////////////
// The main function is the "purify" function.
// Everything else is a helper
//////////////////////////////////////////////

var concatFiles = function(files){
  return files.reduce(function(total, file){
    return total + fs.readFileSync(file, 'utf8');
  }, '');
};

var getAllUsedCharactersInClasses = function(classes){
  var usedChars = {};

  classes.forEach(function(selector){
    for(var i = 0; i < selector.length; i++){
      var chr = selector[i];
      usedChars[chr] = true;
    }
  });

  return usedChars;
};

var getAllUsedWords = function(content, usedChars){
  var used = {};
  var word = "";

  for(var i = 0; i < content.length; i++){
    var chr = content[i];

    if(chr in usedChars){
      word += chr;
    } else {
      used[word] = true;
      word = "";
    }
  }

  return used;
};

var formatCSS = function(styles){
  styles = styles.split('\\n\\n').join('');
  styles = styles.split(' \\n').join('');
  styles = styles.split('   ').join('');
  styles = styles.split('}').join('}\\n');
  styles = styles.split('}\\n\\n').join('}\\n');
  styles = styles.split(' }').join('}');
  styles = styles.split(' }').join('}');

  return styles;
};

var extractIDsAndClassesFromCSS = function(css){
  var ids = [];
  var classes = [];

  for(var i = 0; i < css.length; i++){
    if(css[i] === 'clazz'){
      classes.push(css[i + 2]);
    }

    if(css[i] === 'shash'){
      ids.push(css[i + 1]);
    }
  }

  ids = _.uniq(ids);
  classes = _.uniq(classes);

  return {
    ids: ids,
    classes: classes
  };
};

var findWordsInWordHash = function(words, wordsHash){
  return _.filter(words, function(word){
    var splitWord = word.split('-');

    if(splitWord.length === 1){
      return wordsHash[word];
    } else {
      return wordsHash[word] ||
             _.every(splitWord, function(part){
               return wordsHash[part] || wordsHash[part + '-'] ||
                      wordsHash['-' + part + '-'] || wordsHash['-' + part];
             });
    }
  });
};

var filterByUsedClassesAndHtmlEls = function(ast, classes, htmlEls){
  ast.forEach(function(branch){
    if(branch[0] !== 'ruleset'){
      return;
    }

    for(var i = 1; i < branch.length; i++){
      if(branch[i][0] !== 'selector'){
        continue;
      }

      var throwDelim = false;
      branch[i] = _.filter(branch[i], function(twig){
        var flatTwig = _.flatten(twig);

        if(flatTwig[0] === 'delim' && throwDelim){
          throwDelim = false;
          return false;
        }

        if(flatTwig[0] !== 'simpleselector'){
          return true;
        }

        var hasClass = flatTwig.indexOf('clazz') > -1;

        if(hasClass){
          for(var j = 1; j < flatTwig.length; j++){
            if(flatTwig[j] === 'clazz'){
              if(classes.indexOf(flatTwig[j + 2]) === -1){
                throwDelim = true;
                return false;
              }
            }
          }
        } else {
          for(var j = 1; j < flatTwig.length; j++){
            if(flatTwig[i] === 'ident' && htmlEls.indexOf(flatTwig[i + 1]) > -1){
              return true;
            }
          }

          throwDelim = true;
          return false;
        }

        return true;
      });

      if(branch[i][branch[i].length - 1][0] === 'delim'){
        branch[i] = branch[i].slice(0, branch[i].length - 1);
      }
    }
  });

  return _.filter(ast, function(branch){
    if(branch[0] !== 'ruleset'){
      return true;
    }

    var flatBranch = _.flatten(branch);
    return flatBranch.indexOf('simpleselector') > -1;
  });
};

var filterByUsedIds = function(ast, ids){
  return _.filter(ast, function(branch){
    var flatBranch = _.flatten(branch.slice());

    if(flatBranch[0] === 's'){
      return true;
    }

    for(var i = 0; i < flatBranch.length; i++){
      if(flatBranch[i] === 'shash'){
        return ids.indexOf(flatBranch[i + 1].toLowerCase()) > -1;
      }
    }

    return false;
  });
};

var removeUnusedMedias = function(atSign, classes){
  atSign.forEach(function(branch){
    if(branch[0] !== 'atruler'){
      return;
    }

    if(_.flatten(branch).indexOf('media') === -1){
      return;
    }
    for(var i = 1; i < branch.length; i++){
      if(branch[i][0] !== 'atrulers'){
        continue;
      }

      branch[i] = _.filter(branch[i], function(twig){
        if(twig[0] !== 'ruleset'){
          return true;
        }
        var flattened = _.flatten(twig);
        var flag = false;

        for(var j = 0; j < flattened.length; j++){
          if(flattened[j] === 'clazz'){
            if(classes.indexOf(flattened[j + 2]) > -1){
              flag = true;
            } else {
              return false;
            }
          }
        }

        return flag;
      });
    }
  });
};

var filterMediasByZeroClasses = function(atSign){
  return _.filter(atSign, function(branch){
    if(branch[0] !== 'atruler'){
      return true;
    }

    var flatBranch = _.flatten(branch);
    var count = 0;

    for(var i = 0; i < flatBranch.length; i++){
      if(flatBranch[i] === 'property'){
        count++;
      }
    }

    return count > 0;
  });
};

var reduceContent = function(content){
  return content
    .split('\n').join('')
    .replace(/\s\s+/g, ' ');
};

var DEFAULT_OPTIONS = {
  write: false,
  minify: false,
  info: false
};
////////////////////
// ARGUMENTS
// files    = an array of filepaths to html/js files OR a raw string of content to search through
// css      = an array of filepaths to css files OR a raw string of css to filter
// options  = {
//   write  : string (filepath to write purified css to. if false, function returns raw string)
//   minify : boolean (if true, will minify the purified css)
//   info   : boolean (if true, will log out stats of how much css was reduced)
// }
////////////////////
var purify = function(files, css, options, callback){
  if(typeof options === 'function'){
    callback = options;
    options = {};
  }
  options = options || {};
  options = _.extend({}, DEFAULT_OPTIONS, options);

  var startTime = new Date();

  var cssString = Array.isArray(css) ? concatFiles(css) : css;
  var content = Array.isArray(files) ? concatFiles(files) : files;
  content = reduceContent(content.toLowerCase());

  // Store starting length. Will be helpful later to show how much was reduced
  var beginningLength = cssString.length;

  // Turn css into abstract syntax tree
  var original = gonzales.srcToCSSP(cssString);

  // Tree with everything that begins with @ in css (@media, @keyframe, etc.)
  var atSign = _.filter(original, function(branch){
    return branch[0] === 'atruler' || branch[0] === 's';
  });

  // Tree with everything that doesn't start with an @ (classes, elements, ids, etc.)
  original = _.filter(original, function(branch){
    return branch[0] !== 'atruler';
  });

  // console.log(util.inspect(atSign, false, null));

  var flattenedCSS = _.flatten(original.slice());

  // Get list of things that are used
  var extraction = extractIDsAndClassesFromCSS(flattenedCSS);
  var classes = extraction.classes;
  var ids = extraction.ids;

  // Get hash of word-parts appearing in content
  var chrHash = getAllUsedCharactersInClasses(classes);
  var wordsInContent = getAllUsedWords(content, chrHash);

  // Narrow list down to things that are found in content
  classes = findWordsInWordHash(classes, wordsInContent);
  var filteredHtmlEls = findWordsInWordHash(htmlEls, wordsInContent);
  ids = findWordsInWordHash(ids, wordsInContent);

  // Narrow CSS tree down to things that remain on the list
  var stylesheet = filterByUsedClassesAndHtmlEls(original, classes, filteredHtmlEls);
  ids = filterByUsedIds(original, ids);
  removeUnusedMedias(atSign, classes);
  atSign = filterMediasByZeroClasses(atSign);

  // Turn tree back into css
  var idStyles = gonzales.csspToSrc(ids);
  var classStyles = gonzales.csspToSrc(stylesheet);
  var atStyles = gonzales.csspToSrc(atSign);

  // Combine and format
  var styles = classStyles + '\n' + atStyles + '\n' + idStyles;
  styles = JSON.parse(formatCSS(JSON.stringify(styles)));

  if(options.minify){
    styles = new CleanCss().minify(styles).styles;
  }

  if(options.info){
    console.log('##################################');
    console.log('Before purify, CSS was ' + beginningLength + ' chars long.');
    console.log('After purify, CSS is ' + styles.length + ' chars long. (' +
      Math.floor((beginningLength / styles.length * 10)) / 10  + ' times smaller)');
    console.log('##################################');
    console.log('This function took: ', new Date() - startTime, 'ms');
  }

  if(!options.output){
    if(callback){
      callback(styles);
    } else {
      return styles
    }
  } else {
    fs.writeFile(options.output, styles, function(err){
      if(err) return err;
    });
  }
};

module.exports = purify;
<|endoftext|>


<commit_before><commit_msg>[Echo Canvas] making ajax request asynchronously in case of canvas file is cached (IE issue)<commit_after><|endoftext|>


<commit_before>/*global THREE, requestAnimationFrame, createShipGeometry, createGunGeometry, createTurretGeometry*/
(function( window, document, undefined ) {
  'use strict';

  var container;

  var scene, camera, controls, renderer;

  var shipGeometry, shipMaterial, shipMesh;
  var gunGeometry, gunMesh;
  var turretGeometry, turretMesh;

  var ambient;
  var light;

  function init() {
    container = document.createElement( 'div' );
    document.body.appendChild( container );

    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize( window.innerWidth, window.innerHeight );
    renderer.shadowMapEnabled = true;
    container.appendChild( renderer.domElement );

    scene = new THREE.Scene();

    camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight );
    camera.position.set( 0, 3, 16 );
    scene.add( camera );

    controls = new THREE.OrbitControls( camera, renderer.domElement );

    shipGeometry = createShipGeometry();
    shipMaterial = new THREE.MeshPhongMaterial({
      color: 0xdddddd,
      specular: 0xffffff
    });
    shipMesh = new THREE.Mesh( shipGeometry, shipMaterial );
    shipMesh.rotation.x = -Math.PI / 2;
    scene.add( shipMesh );

    // (x, y) coordinates.
    var turretPositions = [
      // Aft turrets.
      [ 0, 4 ],
      [ 0, 1 ],
      // Wing turrets.
      [ 0.5, -1.5 ],
      [ -0.5, -1.5 ],
      // Fore turret.
      [ 0, -4 ]
    ];

    turretGeometry = createTurretGeometry();
    gunGeometry = createGunGeometry();
    for ( var i = 0; i < 5; i++ ) {
      turretMesh = new THREE.Mesh( turretGeometry, shipMaterial );
      turretMesh.castShadow = true;
      turretMesh.position.set(
        turretPositions[i][0],
        turretPositions[i][1],
        0.5
      );

      gunMesh = new THREE.Mesh( gunGeometry, shipMaterial );
      gunMesh.castShadow = true;
      gunMesh.receiveShadow = true;
      gunMesh.position.y = 0.4;
      turretMesh.add( gunMesh );

      // Aft turrets.
      if ( i > 1 ) {
        turretMesh.rotation.x = Math.PI;
      }

      shipMesh.add( turretMesh );
    }

    light = new THREE.SpotLight( 0xffffff, 2 );
    light.position.set( 4, 6, 16 );
    light.castShadow = true;
    light.shadowCameraNear = 6;
    light.shadowCameraFar = 32;
    light.shadowCameraVisible = true;
    scene.add( light );

    ambient = new THREE.AmbientLight( 0x333333 );
    scene.add( ambient );

    var lightHelper = new THREE.SpotLightHelper( light, 1 );
    scene.add( lightHelper );
  }

  function animate() {
    renderer.render( scene, camera );
    requestAnimationFrame( animate );
  }

  init();
  animate();

}) ( window, document );
<commit_msg>tactics: Add ship light position controls.<commit_after>/*global THREE, requestAnimationFrame, createShipGeometry, createGunGeometry, createTurretGeometry*/
(function( window, document, undefined ) {
  'use strict';

  var keys = [];

  var container;

  var scene, camera, controls, renderer;

  var clock = new THREE.Clock();

  var shipGeometry, shipMaterial, shipMesh;
  var gunGeometry, gunMesh;
  var turretGeometry, turretMesh;

  var ambient;
  var light;
  var lightSpeed = 8;

  function init() {
    container = document.createElement( 'div' );
    document.body.appendChild( container );

    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize( window.innerWidth, window.innerHeight );
    renderer.shadowMapEnabled = true;
    container.appendChild( renderer.domElement );

    scene = new THREE.Scene();

    camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight );
    camera.position.set( 0, 3, 16 );
    scene.add( camera );

    controls = new THREE.OrbitControls( camera, renderer.domElement );

    shipGeometry = createShipGeometry();
    shipMaterial = new THREE.MeshPhongMaterial({
      color: 0xdddddd,
      specular: 0xffffff
    });
    shipMesh = new THREE.Mesh( shipGeometry, shipMaterial );
    shipMesh.rotation.x = -Math.PI / 2;
    scene.add( shipMesh );

    // (x, y) coordinates.
    var turretPositions = [
      // Aft turrets.
      [ 0, 4 ],
      [ 0, 1 ],
      // Wing turrets.
      [ 0.5, -1.5 ],
      [ -0.5, -1.5 ],
      // Fore turret.
      [ 0, -4 ]
    ];

    turretGeometry = createTurretGeometry();
    gunGeometry = createGunGeometry();
    for ( var i = 0; i < 5; i++ ) {
      turretMesh = new THREE.Mesh( turretGeometry, shipMaterial );
      turretMesh.castShadow = true;
      turretMesh.position.set(
        turretPositions[i][0],
        turretPositions[i][1],
        0.5
      );

      gunMesh = new THREE.Mesh( gunGeometry, shipMaterial );
      gunMesh.castShadow = true;
      gunMesh.receiveShadow = true;
      gunMesh.position.y = 0.4;
      turretMesh.add( gunMesh );

      // Aft turrets.
      if ( i > 1 ) {
        turretMesh.rotation.x = Math.PI;
      }

      shipMesh.add( turretMesh );
    }

    light = new THREE.SpotLight( 0xffffff, 2 );
    light.position.set( 4, 6, 16 );
    light.castShadow = true;
    light.shadowCameraNear = 6;
    light.shadowCameraFar = 128;
    light.shadowCameraVisible = true;
    scene.add( light );

    ambient = new THREE.AmbientLight( 0x333333 );
    scene.add( ambient );

    var lightHelper = new THREE.SpotLightHelper( light, 1 );
    scene.add( lightHelper );
  }

  function animate() {
    // Update light position.
    var dt = clock.getDelta();
    // I. Forward.
    if ( keys[ 73 ] ) { light.position.z -= lightSpeed * dt; }
    // K. Backward.
    if ( keys[ 75 ] ) { light.position.z += lightSpeed * dt; }
    // J. Left.
    if ( keys[ 74 ] ) { light.position.x -= lightSpeed * dt; }
    // L. Right.
    if ( keys[ 76 ] ) { light.position.x += lightSpeed * dt; }

    renderer.render( scene, camera );
    requestAnimationFrame( animate );
  }

  init();
  animate();

  document.addEventListener( 'keydown', function( event ) {
    keys[ event.which ] = true;
  });

  document.addEventListener( 'keyup', function( event ) {
    keys[ event.which ] = false;
  });

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


<commit_before>(function (Object) {
  'use strict';

  // { name: 'core', dependencies: ['none'] }
  function a (o) { return Object.prototype.toString.call(o) === '[object Array]'; }
  function atoa (a) { return Array.prototype.slice.call(a); }
  function cb (fn, args, ctx) { if (!fn) { return; } tick(function run () { fn.apply(ctx || null, args || []); }); }
  function once (fn) {
    var disposed;
    function disposable () {
      if (disposed) { return; }
      disposed = true;
      fn.apply(null, arguments);
    }
    disposable.discard = function () { disposed = true; };
    return disposable;
  }
  function handle (args, done, disposable) {
    var err = args.shift();
    if (err) { if (disposable) { disposable.discard(); } cb(done, [err]); return true; }
  }

  // cross-platform ticker
  var tick, si = typeof setImmediate === 'function';
  if (typeof process === 'undefined' || !process.nextTick) {
    if (si) {
      tick = function tick (fn) { setImmediate(fn); };
    } else {
      tick = function tick (fn) { setTimeout(fn, 0); };
    }
  } else {
    tick = si ? setImmediate : process.nextTick;
  }

  // { name: 'apply', dependencies: ['core'] }
  function _apply () {
    var args = atoa(arguments);
    var method = args.shift();
    return function curried () {
      var more = atoa(arguments);
      method.apply(method, args.concat(more));
    };
  }

  // { name: 'waterfall', dependencies: ['core'] }
  function _waterfall (steps, done) {
    function next () {
      var d = once(done);
      return once(function callback () {
        var args = atoa(arguments);
        var step = steps.shift();
        if (step) {
          if (handle(args, d)) { return; }
          args.push(next());
          cb(step, args);
        } else {
          cb(d, arguments);
        }
      });
    }
    next()();
  }

  // { name: 'series', dependencies: ['core'] }
  function _series (tasks, done) {
    var d = once(done);
    var keys = Object.keys(tasks);
    var results = a(tasks) ? [] : {};
    var pk;
    function next () {
      return once(function callback () {
        var k = keys.shift();
        var args = atoa(arguments);
        var step = tasks[k];
        if (pk) {
          if (handle(args, d)) { return; }
          results[pk] = args.shift();
        }
        pk = k;
        if (step) {
          cb(step, [next()]);
        } else {
          cb(d, [null, results]);
        }
      });
    }
    next()();
  }

  // { name: 'concurrent', dependencies: ['core'] }
  function _concurrent (tasks, done) {
    var d = once(done);
    var keys = Object.keys(tasks);
    var results = a(tasks) ? [] : {};
    var completed = 0, all = keys.length;
    keys.forEach(function iterator (key) { cb(tasks[key], [next(key)]); });
    function next (k) {
      var fn = once(function callback () {
        var args = atoa(arguments);
        if (handle(args, d, fn)) { return; }
        results[k] = args.shift();
        if (++completed === all) {
          cb(d, [null, results]);
        }
      });
      return fn;
    }
  }

  // { name: 'map', dependencies: ['series' or 'concurrent'] }
  function _map (flow, finish) {
    return function map (collection, iterator, done) {
      var keys = Object.keys(collection);
      var tasks = a(collection) ? [] : {};
      keys.forEach(function insert (key) {
        tasks[key] = function iterate (cb) {
          iterator(collection[key], cb);
        };
      });
      flow(tasks, finish ? finish(collection, done) : done);
    };
  }

  // dependencies: ['map']
  function _each (flow) {
    return _map(flow, finish);
    function finish (collection, done) {
      return function mask (err) {
        done(err); // only return the error, no more arguments
      };
    }
  }

  // dependencies: ['map']
  function _filter (flow) {
    return _map(flow, finish);
    function finish (collection, done) {
      return function filter (err, results) {
        function exists (item, key) {
          return !!results[key];
        }
        function ofilter () {
          var filtered = {};
          Object.keys(collection).forEach(function omapper (key) {
            if (exists(null, key)) { filtered[key] = collection[key]; }
          });
          return filtered;
        }
        if (err) { done(err); return; }
        done(null, a(results) ? collection.filter(exists) : ofilter());
      };
    }
  }

  // { name: 'emitter', dependencies: ['core'] }
  function _emitter (thing) {
    /* jshint validthis:true */
    var me = this;
    var evt = {};
    thing.on = function (type, fn) {
      if (!evt[type]) {
        evt[type] = [fn];
      } else {
        evt[type].push(fn);
      }
    };
    thing.emit = function () {
      var args = atoa(arguments);
      var type = args.shift();
      var et = evt[type];
      if (type === 'error' && !et) { throw args.length === 1 ? args[0] : args; }
      if (!et) { return; }
      et.forEach(function emitter (s) { cb(s, args, me); });
    };
    return thing;
  }

  // { name: 'queue', dependencies: ['core'] }
  var _queue = function (worker, concurrency) {
    var q = [], load = 0, max = concurrency || 1, paused;
    function _add (task, top, done) {
      var m = top ? 'unshift' : 'push';
      var tasks = a(task) ? task : [task];
      tasks.forEach(function insert (t) { q[m]({ t: t, done: done }); });
      cb(labor);
    }
    function labor () {
      if (paused || load >= max || !q.length) { return; }
      load++;
      var job = q.pop();
      worker(job.t, once(complete.bind(null, job)));
    }
    function complete (job, err) {
      load--;
      cb(job.done, [err]);
      cb(labor);
    }
    var qq = {
      push: function (task, done) { _add(task, false, done); },
      unshift: function (task, done) { _add(task, true, done); },
      pause: function () { paused = true; },
      resume: function () { paused = false; labor(); }
    };
    Object.defineProperty(qq, 'length', { get: function () { return q.length; } });
    return qq;
  };

  // { name: 'outro', dependencies: ['core'] }
  var $ = {
    apply: _apply,
    concurrent: _concurrent,
    series: _series,
    waterfall: _waterfall,
    each: _each(_concurrent),
    map: _map(_concurrent),
    filter: _filter(_concurrent),
    queue: _queue,
    emitter: _emitter
  };

  $.each.series = _each(_series);
  $.map.series = _map(_series);
  $.filter.series = _filter(_series);

  // cross-platform export
  if (typeof module !== 'undefined' && module.exports) {
    module.exports = $;
  } else {
    window.contra = $;
  }
})(Object);
<commit_msg>rm strict statement<commit_after>(function (Object) {
  /* jshint strict:false */
  // { name: 'core', dependencies: ['none'] }
  function a (o) { return Object.prototype.toString.call(o) === '[object Array]'; }
  function atoa (a) { return Array.prototype.slice.call(a); }
  function cb (fn, args, ctx) { if (!fn) { return; } tick(function run () { fn.apply(ctx || null, args || []); }); }
  function once (fn) {
    var disposed;
    function disposable () {
      if (disposed) { return; }
      disposed = true;
      fn.apply(null, arguments);
    }
    disposable.discard = function () { disposed = true; };
    return disposable;
  }
  function handle (args, done, disposable) {
    var err = args.shift();
    if (err) { if (disposable) { disposable.discard(); } cb(done, [err]); return true; }
  }

  // cross-platform ticker
  var tick, si = typeof setImmediate === 'function';
  if (typeof process === 'undefined' || !process.nextTick) {
    if (si) {
      tick = function tick (fn) { setImmediate(fn); };
    } else {
      tick = function tick (fn) { setTimeout(fn, 0); };
    }
  } else {
    tick = si ? setImmediate : process.nextTick;
  }

  // { name: 'apply', dependencies: ['core'] }
  function _apply () {
    var args = atoa(arguments);
    var method = args.shift();
    return function curried () {
      var more = atoa(arguments);
      method.apply(method, args.concat(more));
    };
  }

  // { name: 'waterfall', dependencies: ['core'] }
  function _waterfall (steps, done) {
    function next () {
      var d = once(done);
      return once(function callback () {
        var args = atoa(arguments);
        var step = steps.shift();
        if (step) {
          if (handle(args, d)) { return; }
          args.push(next());
          cb(step, args);
        } else {
          cb(d, arguments);
        }
      });
    }
    next()();
  }

  // { name: 'series', dependencies: ['core'] }
  function _series (tasks, done) {
    var d = once(done);
    var keys = Object.keys(tasks);
    var results = a(tasks) ? [] : {};
    var pk;
    function next () {
      return once(function callback () {
        var k = keys.shift();
        var args = atoa(arguments);
        var step = tasks[k];
        if (pk) {
          if (handle(args, d)) { return; }
          results[pk] = args.shift();
        }
        pk = k;
        if (step) {
          cb(step, [next()]);
        } else {
          cb(d, [null, results]);
        }
      });
    }
    next()();
  }

  // { name: 'concurrent', dependencies: ['core'] }
  function _concurrent (tasks, done) {
    var d = once(done);
    var keys = Object.keys(tasks);
    var results = a(tasks) ? [] : {};
    var completed = 0, all = keys.length;
    keys.forEach(function iterator (key) { cb(tasks[key], [next(key)]); });
    function next (k) {
      var fn = once(function callback () {
        var args = atoa(arguments);
        if (handle(args, d, fn)) { return; }
        results[k] = args.shift();
        if (++completed === all) {
          cb(d, [null, results]);
        }
      });
      return fn;
    }
  }

  // { name: 'map', dependencies: ['series' or 'concurrent'] }
  function _map (flow, finish) {
    return function map (collection, iterator, done) {
      var keys = Object.keys(collection);
      var tasks = a(collection) ? [] : {};
      keys.forEach(function insert (key) {
        tasks[key] = function iterate (cb) {
          iterator(collection[key], cb);
        };
      });
      flow(tasks, finish ? finish(collection, done) : done);
    };
  }

  // dependencies: ['map']
  function _each (flow) {
    return _map(flow, finish);
    function finish (collection, done) {
      return function mask (err) {
        done(err); // only return the error, no more arguments
      };
    }
  }

  // dependencies: ['map']
  function _filter (flow) {
    return _map(flow, finish);
    function finish (collection, done) {
      return function filter (err, results) {
        function exists (item, key) {
          return !!results[key];
        }
        function ofilter () {
          var filtered = {};
          Object.keys(collection).forEach(function omapper (key) {
            if (exists(null, key)) { filtered[key] = collection[key]; }
          });
          return filtered;
        }
        if (err) { done(err); return; }
        done(null, a(results) ? collection.filter(exists) : ofilter());
      };
    }
  }

  // { name: 'emitter', dependencies: ['core'] }
  function _emitter (thing) {
    /* jshint validthis:true */
    var me = this;
    var evt = {};
    thing.on = function (type, fn) {
      if (!evt[type]) {
        evt[type] = [fn];
      } else {
        evt[type].push(fn);
      }
    };
    thing.emit = function () {
      var args = atoa(arguments);
      var type = args.shift();
      var et = evt[type];
      if (type === 'error' && !et) { throw args.length === 1 ? args[0] : args; }
      if (!et) { return; }
      et.forEach(function emitter (s) { cb(s, args, me); });
    };
    return thing;
  }

  // { name: 'queue', dependencies: ['core'] }
  var _queue = function (worker, concurrency) {
    var q = [], load = 0, max = concurrency || 1, paused;
    function _add (task, top, done) {
      var m = top ? 'unshift' : 'push';
      var tasks = a(task) ? task : [task];
      tasks.forEach(function insert (t) { q[m]({ t: t, done: done }); });
      cb(labor);
    }
    function labor () {
      if (paused || load >= max || !q.length) { return; }
      load++;
      var job = q.pop();
      worker(job.t, once(complete.bind(null, job)));
    }
    function complete (job, err) {
      load--;
      cb(job.done, [err]);
      cb(labor);
    }
    var qq = {
      push: function (task, done) { _add(task, false, done); },
      unshift: function (task, done) { _add(task, true, done); },
      pause: function () { paused = true; },
      resume: function () { paused = false; labor(); }
    };
    Object.defineProperty(qq, 'length', { get: function () { return q.length; } });
    return qq;
  };

  // { name: 'outro', dependencies: ['core'] }
  var $ = {
    apply: _apply,
    concurrent: _concurrent,
    series: _series,
    waterfall: _waterfall,
    each: _each(_concurrent),
    map: _map(_concurrent),
    filter: _filter(_concurrent),
    queue: _queue,
    emitter: _emitter
  };

  $.each.series = _each(_series);
  $.map.series = _map(_series);
  $.filter.series = _filter(_series);

  // cross-platform export
  if (typeof module !== 'undefined' && module.exports) {
    module.exports = $;
  } else {
    window.contra = $;
  }
})(Object);
<|endoftext|>


<commit_before>/**
 * @author Ryan Johnson <http://syntacticx.com/>
 * @copyright 2008 PersonalGrid Corporation <http://personalgrid.com/>
 * @package LivePipe UI
 * @license MIT
 * @url http://livepipe.net/controls/hotkey/
 * @attribution http://www.quirksmode.org/js/cookies.html
 */

if(typeof(Prototype) == "undefined")
  throw "Cookie requires Prototype to be loaded."
if(typeof(Object.Event) == "undefined")
  throw "Cookie requires Object.Event to be loaded.";

var Cookie = {
  build: function() {
    return $A(arguments).compact().join("; ");
  },
  secondsFromNow: function(seconds) {
    var d = new Date();
    d.setTime(d.getTime() + (seconds * 1000));
    return d.toGMTString();
  },
  set: function(name,value,seconds){
    Cookie.notify('set',name,value);
    var expiry = seconds ? 'expires=' + Cookie.secondsFromNow(seconds) : null;
    document.cookie = Cookie.build(name + "=" + value, expiry, "path=/");
  },
  get: function(name){
    Cookie.notify('get',name);
    var valueMatch = new RegExp(name + "=([^;]+)").exec(document.cookie);
    return valueMatch ? valueMatch[1] : null;
  },
  unset: function(name){
    Cookie.notify('unset',name);
    Cookie.set(name,'',-1);
  }
};
Object.Event.extend(Cookie);<commit_msg>cookie.js lint<commit_after>/**
 * @author Ryan Johnson <http://syntacticx.com/>
 * @copyright 2008 PersonalGrid Corporation <http://personalgrid.com/>
 * @package LivePipe UI
 * @license MIT
 * @url http://livepipe.net/controls/hotkey/
 * @attribution http://www.quirksmode.org/js/cookies.html
 */

/*global document, Prototype, $A */

if(typeof(Prototype) == "undefined") {
  throw "Cookie requires Prototype to be loaded."; }
if(typeof(Object.Event) == "undefined") {
  throw "Cookie requires Object.Event to be loaded."; }

var Cookie = {
  build: function() {
    return $A(arguments).compact().join("; ");
  },
  secondsFromNow: function(seconds) {
    var d = new Date();
    d.setTime(d.getTime() + (seconds * 1000));
    return d.toGMTString();
  },
  set: function(name,value,seconds){
    Cookie.notify('set',name,value);
    var expiry = seconds ? 'expires=' + Cookie.secondsFromNow(seconds) : null;
    document.cookie = Cookie.build(name + "=" + value, expiry, "path=/");
  },
  get: function(name){
    Cookie.notify('get',name);
    var valueMatch = new RegExp(name + "=([^;]+)").exec(document.cookie);
    return valueMatch ? valueMatch[1] : null;
  },
  unset: function(name){
    Cookie.notify('unset',name);
    Cookie.set(name,'',-1);
  }
};
Object.Event.extend(Cookie);
<|endoftext|>


<commit_before>/**
 * Baobab Cursor Abstraction
 * ==========================
 *
 * Nested selection into a baobab tree.
 */
var EventEmitter = require('emmett'),
    helpers = require('./helpers.js'),
    defaults = require('../defaults.js'),
    type = require('./type.js');

/**
 * Main Class
 */
function Cursor(tree, path, solvedPath, hash) {
  var self = this;

  // Extending event emitter
  EventEmitter.call(this);

  // Enforcing array
  path = path || [];

  // Properties
  this.tree = tree;
  this.path = path;
  this.hash = hash;
  this.archive = null;
  this.undoing = false;

  // Privates
  this._identity = '[object Cursor]';

  // Complex path?
  this.complexPath = !!solvedPath;
  this.solvedPath = this.complexPath ? solvedPath : this.path;

  // Relevant?
  this.relevant = this.get() !== undefined;

  // Root listeners
  function update(previousState) {
    if (self.isRecording() && !self.undoing) {

      // Handle archive
      var data = helpers.getIn(previousState, self.solvedPath, self.tree),
          record = helpers.deepClone(data);

      self.archive.add(record);
    }

    self.undoing = false;
    return self.emit('update');
  }

  this.updateHandler = function(e) {
    var log = e.data.log,
        previousState = e.data.previousState,
        shouldFire = false,
        c, p, l, m, i, j;

    // Solving path if needed
    if (self.complexPath)
      self.solvedPath = helpers.solvePath(self.tree.data, self.path, self.tree);

    // If selector listens at tree, we fire
    if (!self.path.length)
      return update(previousState);

    // Checking update log to see whether the cursor should update.
    if (self.solvedPath)
      shouldFire = helpers.solveUpdate(log, [self.solvedPath]);

    // Handling relevancy
    var data = self.get() !== undefined;

    if (self.relevant) {
      if (data && shouldFire) {
        update(previousState);
      }
      else if (!data) {
        self.emit('irrelevant');
        self.relevant = false;
      }
    }
    else {
      if (data && shouldFire) {
        self.emit('relevant');
        update(previousState);
        self.relevant = true;
      }
    }
  };

  // Lazy binding
  var bound = false;

  this._lazyBind = function() {
    if (bound)
      return;
    bound = true;
    self.tree.on('update', self.updateHandler);
  };

  this.on = helpers.before(this._lazyBind, this.on.bind(this));
  this.once = helpers.before(this._lazyBind, this.once.bind(this));

  if (this.complexPath)
    this._lazyBind();
}

helpers.inherits(Cursor, EventEmitter);

/**
 * Predicates
 */
Cursor.prototype.isRoot = function() {
  return !this.path.length;
};

Cursor.prototype.isLeaf = function() {
  return type.Primitive(this.get());
};

Cursor.prototype.isBranch = function() {
  return !this.isLeaf() && !this.isRoot();
};

/**
 * Traversal
 */
Cursor.prototype.root = function() {
  return this.tree.root();
};

Cursor.prototype.select = function(path) {
  if (arguments.length > 1)
    path = helpers.arrayOf(arguments);

  if (!type.Path(path))
    throw Error('baobab.Cursor.select: invalid path.');
  return this.tree.select(this.path.concat(path));
};

Cursor.prototype.up = function() {
  if (this.solvedPath && this.solvedPath.length)
    return this.tree.select(this.path.slice(0, -1));
  else
    return null;
};

Cursor.prototype.left = function() {
  var last = +this.solvedPath[this.solvedPath.length - 1];

  if (isNaN(last))
    throw Error('baobab.Cursor.left: cannot go left on a non-list type.');

  return last ?
    this.tree.select(this.solvedPath.slice(0, -1).concat(last - 1)) :
    null;
};

Cursor.prototype.leftmost = function() {
  var last = +this.solvedPath[this.solvedPath.length - 1];

  if (isNaN(last))
    throw Error('baobab.Cursor.leftmost: cannot go left on a non-list type.');

  return this.tree.select(this.solvedPath.slice(0, -1).concat(0));
};

Cursor.prototype.right = function() {
  var last = +this.solvedPath[this.solvedPath.length - 1];

  if (isNaN(last))
    throw Error('baobab.Cursor.right: cannot go right on a non-list type.');

  if (last + 1 === this.up().get().length)
    return null;

  return this.tree.select(this.solvedPath.slice(0, -1).concat(last + 1));
};

Cursor.prototype.rightmost = function() {
  var last = +this.solvedPath[this.solvedPath.length - 1];

  if (isNaN(last))
    throw Error('baobab.Cursor.right: cannot go right on a non-list type.');

  var list = this.up().get();

  return this.tree.select(this.solvedPath.slice(0, -1).concat(list.length - 1));
};

Cursor.prototype.down = function() {
  var last = +this.solvedPath[this.solvedPath.length - 1];

  if (!(this.get() instanceof Array))
    return null;

  return this.tree.select(this.solvedPath.concat(0));
};

/**
 * Access
 */
Cursor.prototype.get = function(path) {
  if (arguments.length > 1)
    path = helpers.arrayOf(arguments);

  var fullPath = this.solvedPath.concat(
    [].concat(path || path === 0 ? path : [])
  );

  return helpers.getIn(this.tree.data, fullPath, this.tree);
};

/**
 * Update
 */
function pathPolymorphism(method, allowedType, key, val) {
  if (arguments.length > 5)
    throw Error('baobab.Cursor.' + method + ': too many arguments.');

  if (arguments.length < 4) {
    val = key;
    key = [];
  }

  key = key || [];

  var path = [].concat(key),
      solvedPath = helpers.solvePath(this.get(), path, this.tree);

  if (!solvedPath)
    throw Error('baobab.Cursor.' + method + ': could not solve dynamic path.');

  if (allowedType) {
    var data = this.get(solvedPath);

    if (!type[allowedType](data))
      throw Error('baobab.Cursor.' + method + ': invalid target.');
  }

  var leaf = {};
  leaf['$' + method] = val;

  var spec = helpers.pathObject(solvedPath, leaf);

  return spec;
}

function makeUpdateMethod(command, type) {
  Cursor.prototype[command] = function() {
    var spec = pathPolymorphism.bind(this, command, type).apply(this, arguments);

    return this.update(spec);
  };
}

makeUpdateMethod('set');
makeUpdateMethod('apply');
makeUpdateMethod('chain');
makeUpdateMethod('push', 'Array');
makeUpdateMethod('unshift', 'Array');

Cursor.prototype.merge = function(o) {
  if (!type.Object(o))
    throw Error('baobab.Cursor.merge: trying to merge a non-object.');

  var spec = pathPolymorphism.bind(this, 'merge', 'Object').apply(this, arguments);

  return this.update(spec);
};

Cursor.prototype.unset = function(key) {
  if (key === undefined && this.isRoot())
    throw Error('baobab.Cursor.unset: cannot remove root node.');

  var spec = pathPolymorphism.bind(this, 'unset', null).apply(this, [key, true]);

  return this.update(spec);
};

Cursor.prototype.update = function(key, spec) {
  if (arguments.length < 2) {
    this.tree.stack(helpers.pathObject(this.solvedPath, key));
    return this;
  }

  // Solving path
  var path = [].concat(key),
      solvedPath = helpers.solvePath(this.get(), path, this.tree);

  if (!solvedPath)
    throw Error('baobab.Cursor.update: could not solve dynamic path.');

  this.tree.stack(helpers.pathObject(this.solvedPath.concat(solvedPath), spec));
  return this;
};

/**
 * History
 */
Cursor.prototype.startRecording = function(maxRecords) {
  maxRecords = maxRecords || 5;

  if (maxRecords < 1)
    throw Error('baobab.Cursor.startRecording: invalid maximum number of records.');

  if (this.archive)
    return this;

  // Lazy bind
  this._lazyBind();

  this.archive = helpers.archive(maxRecords);
  return this;
};

Cursor.prototype.stopRecording = function() {
  this.archive = null;
  return this;
};

Cursor.prototype.undo = function(steps) {
  steps = steps || 1;

  if (!this.isRecording())
    throw Error('baobab.Cursor.undo: cursor is not recording.');

  if (!type.PositiveInteger(steps))
    throw Error('baobab.Cursor.undo: expecting a positive integer.');

  var record = this.archive.back(steps);

  if (!record)
    throw Error('boabab.Cursor.undo: cannot find a relevant record (' + steps + ' back).');

  this.undoing = true;
  return this.set(record);
};

Cursor.prototype.isRecording = function() {
  return !!this.archive;
};

Cursor.prototype.hasHistory = function() {
  return !!(this.archive && this.archive.get().length);
};

Cursor.prototype.getHistory = function() {
  return this.archive ? this.archive.get() : [];
};

/**
 * Releasing
 */
Cursor.prototype.release = function() {

  // Removing listener on parent
  this.tree.off('update', this.updateHandler);

  // If the cursor is hashed, we unsubscribe from the parent
  if (this.hash)
    delete this.tree._cursors[this.hash];

  // Dereferencing
  delete this.tree;
  delete this.path;
  delete this.solvedPath;
  delete this.archive;

  // Killing emitter
  this.kill();
};

/**
 * Output
 */
Cursor.prototype.toJSON = function() {
  return this.get();
};

Cursor.prototype.toString = function() {
  return this._identity;
};

type.Cursor = function (value) {
  return value instanceof Cursor;
};

/**
 * Export
 */
module.exports = Cursor;
<commit_msg>Removing now useless type.Cursor<commit_after>/**
 * Baobab Cursor Abstraction
 * ==========================
 *
 * Nested selection into a baobab tree.
 */
var EventEmitter = require('emmett'),
    helpers = require('./helpers.js'),
    defaults = require('../defaults.js'),
    type = require('./type.js');

/**
 * Main Class
 */
function Cursor(tree, path, solvedPath, hash) {
  var self = this;

  // Extending event emitter
  EventEmitter.call(this);

  // Enforcing array
  path = path || [];

  // Properties
  this.tree = tree;
  this.path = path;
  this.hash = hash;
  this.archive = null;
  this.undoing = false;

  // Privates
  this._identity = '[object Cursor]';

  // Complex path?
  this.complexPath = !!solvedPath;
  this.solvedPath = this.complexPath ? solvedPath : this.path;

  // Relevant?
  this.relevant = this.get() !== undefined;

  // Root listeners
  function update(previousState) {
    if (self.isRecording() && !self.undoing) {

      // Handle archive
      var data = helpers.getIn(previousState, self.solvedPath, self.tree),
          record = helpers.deepClone(data);

      self.archive.add(record);
    }

    self.undoing = false;
    return self.emit('update');
  }

  this.updateHandler = function(e) {
    var log = e.data.log,
        previousState = e.data.previousState,
        shouldFire = false,
        c, p, l, m, i, j;

    // Solving path if needed
    if (self.complexPath)
      self.solvedPath = helpers.solvePath(self.tree.data, self.path, self.tree);

    // If selector listens at tree, we fire
    if (!self.path.length)
      return update(previousState);

    // Checking update log to see whether the cursor should update.
    if (self.solvedPath)
      shouldFire = helpers.solveUpdate(log, [self.solvedPath]);

    // Handling relevancy
    var data = self.get() !== undefined;

    if (self.relevant) {
      if (data && shouldFire) {
        update(previousState);
      }
      else if (!data) {
        self.emit('irrelevant');
        self.relevant = false;
      }
    }
    else {
      if (data && shouldFire) {
        self.emit('relevant');
        update(previousState);
        self.relevant = true;
      }
    }
  };

  // Lazy binding
  var bound = false;

  this._lazyBind = function() {
    if (bound)
      return;
    bound = true;
    self.tree.on('update', self.updateHandler);
  };

  this.on = helpers.before(this._lazyBind, this.on.bind(this));
  this.once = helpers.before(this._lazyBind, this.once.bind(this));

  if (this.complexPath)
    this._lazyBind();
}

helpers.inherits(Cursor, EventEmitter);

/**
 * Predicates
 */
Cursor.prototype.isRoot = function() {
  return !this.path.length;
};

Cursor.prototype.isLeaf = function() {
  return type.Primitive(this.get());
};

Cursor.prototype.isBranch = function() {
  return !this.isLeaf() && !this.isRoot();
};

/**
 * Traversal
 */
Cursor.prototype.root = function() {
  return this.tree.root();
};

Cursor.prototype.select = function(path) {
  if (arguments.length > 1)
    path = helpers.arrayOf(arguments);

  if (!type.Path(path))
    throw Error('baobab.Cursor.select: invalid path.');
  return this.tree.select(this.path.concat(path));
};

Cursor.prototype.up = function() {
  if (this.solvedPath && this.solvedPath.length)
    return this.tree.select(this.path.slice(0, -1));
  else
    return null;
};

Cursor.prototype.left = function() {
  var last = +this.solvedPath[this.solvedPath.length - 1];

  if (isNaN(last))
    throw Error('baobab.Cursor.left: cannot go left on a non-list type.');

  return last ?
    this.tree.select(this.solvedPath.slice(0, -1).concat(last - 1)) :
    null;
};

Cursor.prototype.leftmost = function() {
  var last = +this.solvedPath[this.solvedPath.length - 1];

  if (isNaN(last))
    throw Error('baobab.Cursor.leftmost: cannot go left on a non-list type.');

  return this.tree.select(this.solvedPath.slice(0, -1).concat(0));
};

Cursor.prototype.right = function() {
  var last = +this.solvedPath[this.solvedPath.length - 1];

  if (isNaN(last))
    throw Error('baobab.Cursor.right: cannot go right on a non-list type.');

  if (last + 1 === this.up().get().length)
    return null;

  return this.tree.select(this.solvedPath.slice(0, -1).concat(last + 1));
};

Cursor.prototype.rightmost = function() {
  var last = +this.solvedPath[this.solvedPath.length - 1];

  if (isNaN(last))
    throw Error('baobab.Cursor.right: cannot go right on a non-list type.');

  var list = this.up().get();

  return this.tree.select(this.solvedPath.slice(0, -1).concat(list.length - 1));
};

Cursor.prototype.down = function() {
  var last = +this.solvedPath[this.solvedPath.length - 1];

  if (!(this.get() instanceof Array))
    return null;

  return this.tree.select(this.solvedPath.concat(0));
};

/**
 * Access
 */
Cursor.prototype.get = function(path) {
  if (arguments.length > 1)
    path = helpers.arrayOf(arguments);

  var fullPath = this.solvedPath.concat(
    [].concat(path || path === 0 ? path : [])
  );

  return helpers.getIn(this.tree.data, fullPath, this.tree);
};

/**
 * Update
 */
function pathPolymorphism(method, allowedType, key, val) {
  if (arguments.length > 5)
    throw Error('baobab.Cursor.' + method + ': too many arguments.');

  if (arguments.length < 4) {
    val = key;
    key = [];
  }

  key = key || [];

  var path = [].concat(key),
      solvedPath = helpers.solvePath(this.get(), path, this.tree);

  if (!solvedPath)
    throw Error('baobab.Cursor.' + method + ': could not solve dynamic path.');

  if (allowedType) {
    var data = this.get(solvedPath);

    if (!type[allowedType](data))
      throw Error('baobab.Cursor.' + method + ': invalid target.');
  }

  var leaf = {};
  leaf['$' + method] = val;

  var spec = helpers.pathObject(solvedPath, leaf);

  return spec;
}

function makeUpdateMethod(command, type) {
  Cursor.prototype[command] = function() {
    var spec = pathPolymorphism.bind(this, command, type).apply(this, arguments);

    return this.update(spec);
  };
}

makeUpdateMethod('set');
makeUpdateMethod('apply');
makeUpdateMethod('chain');
makeUpdateMethod('push', 'Array');
makeUpdateMethod('unshift', 'Array');

Cursor.prototype.merge = function(o) {
  if (!type.Object(o))
    throw Error('baobab.Cursor.merge: trying to merge a non-object.');

  var spec = pathPolymorphism.bind(this, 'merge', 'Object').apply(this, arguments);

  return this.update(spec);
};

Cursor.prototype.unset = function(key) {
  if (key === undefined && this.isRoot())
    throw Error('baobab.Cursor.unset: cannot remove root node.');

  var spec = pathPolymorphism.bind(this, 'unset', null).apply(this, [key, true]);

  return this.update(spec);
};

Cursor.prototype.update = function(key, spec) {
  if (arguments.length < 2) {
    this.tree.stack(helpers.pathObject(this.solvedPath, key));
    return this;
  }

  // Solving path
  var path = [].concat(key),
      solvedPath = helpers.solvePath(this.get(), path, this.tree);

  if (!solvedPath)
    throw Error('baobab.Cursor.update: could not solve dynamic path.');

  this.tree.stack(helpers.pathObject(this.solvedPath.concat(solvedPath), spec));
  return this;
};

/**
 * History
 */
Cursor.prototype.startRecording = function(maxRecords) {
  maxRecords = maxRecords || 5;

  if (maxRecords < 1)
    throw Error('baobab.Cursor.startRecording: invalid maximum number of records.');

  if (this.archive)
    return this;

  // Lazy bind
  this._lazyBind();

  this.archive = helpers.archive(maxRecords);
  return this;
};

Cursor.prototype.stopRecording = function() {
  this.archive = null;
  return this;
};

Cursor.prototype.undo = function(steps) {
  steps = steps || 1;

  if (!this.isRecording())
    throw Error('baobab.Cursor.undo: cursor is not recording.');

  if (!type.PositiveInteger(steps))
    throw Error('baobab.Cursor.undo: expecting a positive integer.');

  var record = this.archive.back(steps);

  if (!record)
    throw Error('boabab.Cursor.undo: cannot find a relevant record (' + steps + ' back).');

  this.undoing = true;
  return this.set(record);
};

Cursor.prototype.isRecording = function() {
  return !!this.archive;
};

Cursor.prototype.hasHistory = function() {
  return !!(this.archive && this.archive.get().length);
};

Cursor.prototype.getHistory = function() {
  return this.archive ? this.archive.get() : [];
};

/**
 * Releasing
 */
Cursor.prototype.release = function() {

  // Removing listener on parent
  this.tree.off('update', this.updateHandler);

  // If the cursor is hashed, we unsubscribe from the parent
  if (this.hash)
    delete this.tree._cursors[this.hash];

  // Dereferencing
  delete this.tree;
  delete this.path;
  delete this.solvedPath;
  delete this.archive;

  // Killing emitter
  this.kill();
};

/**
 * Output
 */
Cursor.prototype.toJSON = function() {
  return this.get();
};

Cursor.prototype.toString = function() {
  return this._identity;
};

/**
 * Export
 */
module.exports = Cursor;
<|endoftext|>


<commit_before>var _ = require('lodash'),
    rp = require('request-promise'),
    argv = require('minimist')(process.argv.slice(2)),
    jsontr = require('./json-transform.js'),
    LdapClient = require('promised-ldap');

var items = require('./people.json');
var transform = {
  uuid: {
    props: {value:String}
  },
  changed: {
    props: {value:Number}
  },
  body: {
    props: {value: String}
  },
  field_computing_id: {
    newName: "computingId",
    props: {value: String}
  },
  field_image: {
    props: {
      alt:String, width:Number, height:Number, url:String,
      target_uuid: {type: String, newName: "uuid"}
    }
  },
//  field_job_title: {
//    newName: "jobTitle",
//    props: {value:String}
//  },
  field_linkedin: {
    newName: "linkedin",
    props: {value:String}
  },
  field_orcid_id: {
    newName: "field_orcid_id",
    props: {value:String}
  },
  field_preferred_pronouns: {
    newName: "pronouns",
    props: {value:String}
  },
  field_primary_office_location: {
    newName: "officeLocation",
    props: {value:String}
  },
  field_primary_phone: {
    newName: "phone",
    props: {value:String}
  },
  field_professional_profile: {
    newName:"profile",
    props: {value:String}
  },
  field_twitter: {
    newName:"twitter",
    props: {value:String}
  },
  field_website: {
    newName:"site",
    props: {value:String}
  },
  field_library: {
    newName:"library",
    props: {
      target_uuid: {type: String, newName: "uuid"}
    }
  },
  field_address: {
    newName:"address",
    props: {value:String}
  },
  field_ask_me_about: {
    newName:"askMeAbout",
    props: {value:String}
  },
  field_cv: {
    newName:"cv",
    props: {uri:String}
  },
  field_email_alias: {
    newName:"emailAlias",
    props: {value:String}
  },
  field_employee_preferred_name: {
    newName:"preferredName",
    props: {value:String}
  },
  field_languages_spoken: {
    newName:"languages",
    props:{value:String}
  },
  field_research_guides: {
    newName:"guides",
    props:{uri:String}
  },
  field_schools: {
    newName:"schools",
    props:{value:String}
  },
  field_subject_specialties: {
    newName:"specialties",
    props:{value:String}
  },
  field_schedule: {
    newName:"schedule",
    props: {uri:String}
  },
  field_private: {
    newName:"private",
    prop: {value:Boolean}
  }
};

function stripEmpty(o) {
  for(k in o) {
    var v = o[k]
    if (v==="" || v===null || v===undefined || (v && v.length==0))
      delete o[k]
  }
  return o
}

var clean = function(item) {
  if (item) {
    if (Array.isArray(item)) {
      item = item.filter(i=>{return i.indexOf("E1:")==-1});
      item = item.map(i=>{return i.replace(/^E0:/,'').replace(/^\+1 /g,'')});
    } else {
      item = item.replace(/^E0:/,'').replace(/^\+1 /g,'');
    }
    return item;
  } else {
    return "";
  }
}

function capFL(string) {
    if (string)
    return string.charAt(0).toUpperCase() + string.slice(1);
    else return string;
}

var tweekPerson = function(person){
  var p = {
    fullName: person.displayName,
    address: clean(person.physicalDeliveryOfficeName),
    computingId: person.uid,
    email: person.mailForwardingAddress,
    nickName: person.eduPersonNickname,
    jobTitle: clean(person.title),
    displayName: person.displayName,
//    displayName: capFL(person.givenName) + " " + capFL(person.sn),
    phone: clean(person.telephoneNumber),
    fax: clean(person.facsimileTelephoneNumber),
    firstName: capFL(person.givenName),
    lastName: capFL(person.sn),
    middleName: person.initials
  };
  return stripEmpty(p);
};

async function getPeopleFromLdap(){
  var client = new LdapClient({url: 'ldap://ldap.virginia.edu'});
  var base = "ou=People,o=University of Virginia,c=US";
  var staff = await client.search(base, {filter:"(&(ou=E0:LB-*)(!(uvaPersonFoundationName=Judge Advocate General School))(!(uvaPersonIAMAffiliation=sponsored)))", scope:'sub'});
//  var staff = await client.search(base, {filter:"ou=E0:LB-Central Svcs", scope:'sub'});
  return staff.entries.map(s=>{return tweekPerson(s.object)});
}

async function doIt(){
  var peopleFromDrupal = jsontr.transform(items,transform).reduce(function(o,val){ o[val.computingId.trim()]=stripEmpty(val); return o; },{});
  var peopleFromLdap = await getPeopleFromLdap();
  // Merge the peopleFromLdap with peopleFromDrupal with drupal having priority priority for same keys
  //var result = peopleFromLdap.map((uid)=>Object.assign(peopleFromLdap[uid], peopleFromDrupal[uid]))
  peopleFromLdap.forEach(p=>{
    if (peopleFromDrupal[p.computingId]) {
      p = Object.assign(p, peopleFromDrupal[p.computingId]);
    }
  });
  var people = peopleFromLdap.filter(p=>(!p.private || (Object.keys(p.private).length === 0 && p.private.constructor === Object)));

  var t = await rp({uri:'https://uvalib-api.firebaseio.com/teams.json',json:true});
  people.forEach(p=>{
    p.computingId = p.computingId.trim();
    p.teams = t.filter(t=>(t.members)?t.members.indexOf(p.computingId)>-1:false).map(t=>t.uuid);
    p.fullTeams = t.filter(t=>(t.members)?t.members.indexOf(p.computingId)>-1:false).map(t=>{ return {uuid:t.uuid,title:t.title}; });
    if (p.field_image && p.field_image.url) p.field_image.url = p.field_image.url.replace("drupal.lib.virginia.edu/sites/default","www.library.virginia.edu");
    if (p.specialties && !Array.isArray(p.specialties)) p.specialties = [{value:p.specialties}];
  });

  return people;
}

doIt().then(function(result){
  console.log(JSON.stringify(result))
  process.exit()})
.catch(function(e){console.log(e); process.exit(1)});
<commit_msg>removing phone from drupal slurp as the field has been hidden but still has (outdated in some cases) data<commit_after>var _ = require('lodash'),
    rp = require('request-promise'),
    argv = require('minimist')(process.argv.slice(2)),
    jsontr = require('./json-transform.js'),
    LdapClient = require('promised-ldap');

var items = require('./people.json');
var transform = {
  uuid: {
    props: {value:String}
  },
  changed: {
    props: {value:Number}
  },
  body: {
    props: {value: String}
  },
  field_computing_id: {
    newName: "computingId",
    props: {value: String}
  },
  field_image: {
    props: {
      alt:String, width:Number, height:Number, url:String,
      target_uuid: {type: String, newName: "uuid"}
    }
  },
//  field_job_title: {
//    newName: "jobTitle",
//    props: {value:String}
//  },
  field_linkedin: {
    newName: "linkedin",
    props: {value:String}
  },
  field_orcid_id: {
    newName: "field_orcid_id",
    props: {value:String}
  },
  field_preferred_pronouns: {
    newName: "pronouns",
    props: {value:String}
  },
  field_primary_office_location: {
    newName: "officeLocation",
    props: {value:String}
  },
//  field_primary_phone: {
//    newName: "phone",
//    props: {value:String}
//  },
  field_professional_profile: {
    newName:"profile",
    props: {value:String}
  },
  field_twitter: {
    newName:"twitter",
    props: {value:String}
  },
  field_website: {
    newName:"site",
    props: {value:String}
  },
  field_library: {
    newName:"library",
    props: {
      target_uuid: {type: String, newName: "uuid"}
    }
  },
  field_address: {
    newName:"address",
    props: {value:String}
  },
  field_ask_me_about: {
    newName:"askMeAbout",
    props: {value:String}
  },
  field_cv: {
    newName:"cv",
    props: {uri:String}
  },
  field_email_alias: {
    newName:"emailAlias",
    props: {value:String}
  },
  field_employee_preferred_name: {
    newName:"preferredName",
    props: {value:String}
  },
  field_languages_spoken: {
    newName:"languages",
    props:{value:String}
  },
  field_research_guides: {
    newName:"guides",
    props:{uri:String}
  },
  field_schools: {
    newName:"schools",
    props:{value:String}
  },
  field_subject_specialties: {
    newName:"specialties",
    props:{value:String}
  },
  field_schedule: {
    newName:"schedule",
    props: {uri:String}
  },
  field_private: {
    newName:"private",
    prop: {value:Boolean}
  }
};

function stripEmpty(o) {
  for(k in o) {
    var v = o[k]
    if (v==="" || v===null || v===undefined || (v && v.length==0))
      delete o[k]
  }
  return o
}

var clean = function(item) {
  if (item) {
    if (Array.isArray(item)) {
      item = item.filter(i=>{return i.indexOf("E1:")==-1});
      item = item.map(i=>{return i.replace(/^E0:/,'').replace(/^\+1 /g,'')});
    } else {
      item = item.replace(/^E0:/,'').replace(/^\+1 /g,'');
    }
    return item;
  } else {
    return "";
  }
}

function capFL(string) {
    if (string)
    return string.charAt(0).toUpperCase() + string.slice(1);
    else return string;
}

var tweekPerson = function(person){
  var p = {
    fullName: person.displayName,
    address: clean(person.physicalDeliveryOfficeName),
    computingId: person.uid,
    email: person.mailForwardingAddress,
    nickName: person.eduPersonNickname,
    jobTitle: clean(person.title),
    displayName: person.displayName,
//    displayName: capFL(person.givenName) + " " + capFL(person.sn),
    phone: clean(person.telephoneNumber),
    fax: clean(person.facsimileTelephoneNumber),
    firstName: capFL(person.givenName),
    lastName: capFL(person.sn),
    middleName: person.initials
  };
  return stripEmpty(p);
};

async function getPeopleFromLdap(){
  var client = new LdapClient({url: 'ldap://ldap.virginia.edu'});
  var base = "ou=People,o=University of Virginia,c=US";
  var staff = await client.search(base, {filter:"(&(ou=E0:LB-*)(!(uvaPersonFoundationName=Judge Advocate General School))(!(uvaPersonIAMAffiliation=sponsored)))", scope:'sub'});
//  var staff = await client.search(base, {filter:"ou=E0:LB-Central Svcs", scope:'sub'});
  return staff.entries.map(s=>{return tweekPerson(s.object)});
}

async function doIt(){
  var peopleFromDrupal = jsontr.transform(items,transform).reduce(function(o,val){ o[val.computingId.trim()]=stripEmpty(val); return o; },{});
  var peopleFromLdap = await getPeopleFromLdap();
  // Merge the peopleFromLdap with peopleFromDrupal with drupal having priority priority for same keys
  //var result = peopleFromLdap.map((uid)=>Object.assign(peopleFromLdap[uid], peopleFromDrupal[uid]))
  peopleFromLdap.forEach(p=>{
    if (peopleFromDrupal[p.computingId]) {
      p = Object.assign(p, peopleFromDrupal[p.computingId]);
    }
  });
  var people = peopleFromLdap.filter(p=>(!p.private || (Object.keys(p.private).length === 0 && p.private.constructor === Object)));

  var t = await rp({uri:'https://uvalib-api.firebaseio.com/teams.json',json:true});
  people.forEach(p=>{
    p.computingId = p.computingId.trim();
    p.teams = t.filter(t=>(t.members)?t.members.indexOf(p.computingId)>-1:false).map(t=>t.uuid);
    p.fullTeams = t.filter(t=>(t.members)?t.members.indexOf(p.computingId)>-1:false).map(t=>{ return {uuid:t.uuid,title:t.title}; });
    if (p.field_image && p.field_image.url) p.field_image.url = p.field_image.url.replace("drupal.lib.virginia.edu/sites/default","www.library.virginia.edu");
    if (p.specialties && !Array.isArray(p.specialties)) p.specialties = [{value:p.specialties}];
  });

  return people;
}

doIt().then(function(result){
  console.log(JSON.stringify(result))
  process.exit()})
.catch(function(e){console.log(e); process.exit(1)});
<|endoftext|>


<commit_before><commit_msg>escape magic chars in patterns<commit_after><|endoftext|>


<commit_before><commit_msg>added default port for pilight. added request error handling<commit_after><|endoftext|>


<commit_before>var h = require('html')
var Panel = require('panel')

function Editor() {
  this.layersPanel = new Panel(this, 215, h('.layers-panel'))
  this.inspectorPanel = new Panel(this, 215, h('.inspector-panel'))

  this.el = h('.editor', [
    this.layersPanel.el,
    h('.canvas'),
    this.inspectorPanel.el
  ])
}

Editor.prototype.start = function() {
  document.body.appendChild(this.el)
  return this
}

exports = Editor
<commit_msg>Add stub getData() and loadData() methods to Editor<commit_after>var h = require('html')
var Panel = require('panel')

function Editor() {
  this.layersPanel = new Panel(this, 215, h('.layers-panel'))
  this.inspectorPanel = new Panel(this, 215, h('.inspector-panel'))

  this.el = h('.editor', [
    this.layersPanel.el,
    h('.canvas'),
    this.inspectorPanel.el
  ])
}

Editor.prototype.start = function() {
  document.body.appendChild(this.el)
  return this
}

Editor.prototype.getData = function() {
  throw new Error("Unimplemented")
}

Error.prototype.loadData = function() {
  throw new Error("Unimplemented")
}

exports = Editor
<|endoftext|>


<commit_before>;(function($$, window){ 'use strict';

  $$.Fabric = function( N ){
    if( !(this instanceof $$.Fabric) ){
      return new $$.Fabric( N );
    }

    var _p = this._private = {
      pass: []
    };

    var defN = 4;

    if( $$.is.number(N) ){
      // then use the specified number of threads
    } if( typeof navigator !== 'undefined' && navigator.hardwareConcurrency != null ){
      N = navigator.hardwareConcurrency;
    } else if( typeof module !== 'undefined' ){
      N = require('os').cpus().length;
    } else { // TODO could use an estimation here but would the additional expense be worth it?
      N = defN;
    }

    for( var i = 0; i < N; i++ ){
      this[i] = $$.Thread();
    }

    this.length = N;
  };

  $$.fabric = $$.Fabric;
  $$.fabfn = $$.Fabric.prototype; // short alias

  $$.fn.fabric = function( fnMap, options ){
    for( var name in fnMap ){
      var fn = fnMap[name];
      $$.Fabric.prototype[ name ] = fn;
    }
  };

  $$.fn.fabric({

    // require fn in all threads
    require: function( fn, as ){
      for( var i = 0; i < this.length; i++ ){
        var thread = this[i];

        thread.require( fn, as );
      }

      return this;
    },

    // get a random thread
    random: function(){
      var i = Math.round( (this.length - 1) * Math.random() );
      var thread = this[i];

      return thread;
    },

    // run on random thread
    run: function( fn ){
      return this.random().run( fn );
    },

    // sends a random thread a message
    message: function( m ){
      return this.random().message( m );
    },

    // send all threads a message
    broadcast: function( m ){
      for( var i = 0; i < this.length; i++ ){
        var thread = this[i];

        thread.message( m );
      }

      return this; // chaining
    },

    // stop all threads
    stop: function(){
      for( var i = 0; i < this.length; i++ ){
        var thread = this[i];

        thread.stop();
      }

      return this.trigger('stop'); // chaining
    },

    // pass data to be used with .spread() etc.
    pass: function( data ){
      var pass = this._private.pass;

      if( $$.is.array(data) ){
        pass.push( data );
      } else if( $$.is.elementOrCollection(data) ){
        var eles = data;
        pass.push( eles.jsons() );
      } else {
        $$.util.error('Only arrays or collections may be used with fabric.pass()');
      }

      return this; // chaining
    },

    spreadSize: function(){
      var subsize =  Math.ceil( this._private.pass[0].length / this.length );

      subsize = Math.max( 1, subsize ); // don't pass less than one ele to each thread

      return subsize;
    },

    // split the data into slices to spread the data equally among threads
    spread: function( fn ){
      var self = this;
      var _p = self._private;
      var subsize = self.spreadSize(); // number of pass eles to handle in each thread
      var pass = _p.pass.shift().concat([]); // keep a copy
      var runPs = [];

      for( var i = 0; i < this.length; i++ ){
        var thread = this[i];
        var slice = pass.splice( 0, subsize );

        var runP = thread.pass( slice ).run( fn );

        runPs.push( runP );

        var doneEarly = pass.length === 0;
        if( doneEarly ){ break; }
      }

      return $$.Promise.all( runPs ).then(function( thens ){
        var postpass = new Array();
        var p = 0;

        // fill postpass with the total result joined from all threads
        for( var i = 0; i < thens.length; i++ ){
          var then = thens[i]; // array result from thread i

          for( var j = 0; j < then.length; j++ ){
            var t = then[j]; // array element

            postpass[ p++ ] = t;
          }
        }

        return postpass;
      });
    },

    // parallel version of array.map()
    map: function( fn ){
      var self = this;

      self.require( fn, '_$_$_fabmap' );

      return self.spread(function( split ){
        var mapped = [];
        var origResolve = resolve;

        resolve = function( val ){
          mapped.push( val );
        };

        for( var i = 0; i < split.length; i++ ){
          var oldLen = mapped.length;
          var ret = _$_$_fabmap( split[i] );
          var nothingInsdByResolve = oldLen === mapped.length;

          if( nothingInsdByResolve ){
            mapped.push( ret );
          }
        }

        resolve = origResolve;

        return mapped;
      }).then(function( ret ){
        console.log( ret )

        return ret;
      });

    },

    // parallel version of array.filter()
    filter: function( fn ){
      var _p = this._private;
      var pass = _p.pass[0];

      return this.map( fn ).then(function( include ){
        var ret = [];

        for( var i = 0; i < pass.length; i++ ){
          var datum = pass[i];
          var incDatum = include[i];

          if( incDatum ){
            ret.push( datum );
          }
        }

        return ret;
      });
    },

    // sorts the passed array using a divide and conquer strategy
    sort: function( cmp ){
      var self = this;
      var P = this._private.pass[0].length;
      var subsize = this.spreadSize();
      var N = this.length;

      cmp = cmp || function( a, b ){ // default comparison function
        if( a < b ){
          return -1;
        } else if( a > b ){
          return 1;
        }

        return 0;
      };

      self.require( cmp, '_$_$_cmp' );

      return self.spread(function( split ){ // sort each split normally
        var sortedSplit = split.sort( _$_$_cmp );
        resolve( sortedSplit );

      }).then(function( joined ){
        // do all the merging in the main thread to minimise data transfer

        // TODO could do merging in separate threads but would incur add'l cost of data transfer
        // for each level of the merge

        var merge = function( i, j, max ){
          // don't overflow array
          j = Math.min( j, P );
          max = Math.min( max, P );

          // left and right sides of merge
          var l = i;
          var r = j;

          var sorted = [];

          for( var k = l; k < max; k++ ){

            var eleI = joined[i];
            var eleJ = joined[j];

            if( i < r && ( j >= max || cmp(eleI, eleJ) <= 0 ) ){
              sorted.push( eleI );
              i++;
            } else {
              sorted.push( eleJ );
              j++;
            }

          }

          // in the array proper, put the sorted values
          for( var k = 0; k < sorted.length; k++ ){ // kth sorted item
            var index = l + k;

            joined[ index ] = sorted[k];
          }
        };

        for( var splitL = subsize; splitL < P; splitL *= 2 ){ // merge until array is "split" as 1

          for( var i = 0; i < P; i += 2*splitL ){
            merge( i, i + splitL, i + 2*splitL );
          }

        }

        return joined;
      });
    }

  });

  var defineRandomPasser = function( opts ){
    opts = opts || {};

    return function( fn ){
      var pass = this._private.pass.shift();

      return this.random().pass( pass )[ opts.threadFn ]( fn );
    };
  };

  $$.fn.fabric({
    randomMap: defineRandomPasser({ threadFn: 'map' }),

    reduce: defineRandomPasser({ threadFn: 'reduce' }),

    reduceRight: defineRandomPasser({ threadFn: 'reduceRight' })
  });

  // aliases
  var fn = $$.fabfn;
  fn.promise = fn.run;
  fn.terminate = fn.halt = fn.stop;

  // pull in event apis
  $$.fn.fabric({
    on: $$.define.on(),
    one: $$.define.on({ unbindSelfOnTrigger: true }),
    once: $$.define.on({ unbindAllBindersOnTrigger: true }),
    off: $$.define.off(),
    trigger: $$.define.trigger()
  });

  $$.define.eventAliasesOn( $$.fabfn );

})( cytoscape, typeof window === 'undefined' ? null : window );
<commit_msg>remove log<commit_after>;(function($$, window){ 'use strict';

  $$.Fabric = function( N ){
    if( !(this instanceof $$.Fabric) ){
      return new $$.Fabric( N );
    }

    var _p = this._private = {
      pass: []
    };

    var defN = 4;

    if( $$.is.number(N) ){
      // then use the specified number of threads
    } if( typeof navigator !== 'undefined' && navigator.hardwareConcurrency != null ){
      N = navigator.hardwareConcurrency;
    } else if( typeof module !== 'undefined' ){
      N = require('os').cpus().length;
    } else { // TODO could use an estimation here but would the additional expense be worth it?
      N = defN;
    }

    for( var i = 0; i < N; i++ ){
      this[i] = $$.Thread();
    }

    this.length = N;
  };

  $$.fabric = $$.Fabric;
  $$.fabfn = $$.Fabric.prototype; // short alias

  $$.fn.fabric = function( fnMap, options ){
    for( var name in fnMap ){
      var fn = fnMap[name];
      $$.Fabric.prototype[ name ] = fn;
    }
  };

  $$.fn.fabric({

    // require fn in all threads
    require: function( fn, as ){
      for( var i = 0; i < this.length; i++ ){
        var thread = this[i];

        thread.require( fn, as );
      }

      return this;
    },

    // get a random thread
    random: function(){
      var i = Math.round( (this.length - 1) * Math.random() );
      var thread = this[i];

      return thread;
    },

    // run on random thread
    run: function( fn ){
      return this.random().run( fn );
    },

    // sends a random thread a message
    message: function( m ){
      return this.random().message( m );
    },

    // send all threads a message
    broadcast: function( m ){
      for( var i = 0; i < this.length; i++ ){
        var thread = this[i];

        thread.message( m );
      }

      return this; // chaining
    },

    // stop all threads
    stop: function(){
      for( var i = 0; i < this.length; i++ ){
        var thread = this[i];

        thread.stop();
      }

      return this.trigger('stop'); // chaining
    },

    // pass data to be used with .spread() etc.
    pass: function( data ){
      var pass = this._private.pass;

      if( $$.is.array(data) ){
        pass.push( data );
      } else if( $$.is.elementOrCollection(data) ){
        var eles = data;
        pass.push( eles.jsons() );
      } else {
        $$.util.error('Only arrays or collections may be used with fabric.pass()');
      }

      return this; // chaining
    },

    spreadSize: function(){
      var subsize =  Math.ceil( this._private.pass[0].length / this.length );

      subsize = Math.max( 1, subsize ); // don't pass less than one ele to each thread

      return subsize;
    },

    // split the data into slices to spread the data equally among threads
    spread: function( fn ){
      var self = this;
      var _p = self._private;
      var subsize = self.spreadSize(); // number of pass eles to handle in each thread
      var pass = _p.pass.shift().concat([]); // keep a copy
      var runPs = [];

      for( var i = 0; i < this.length; i++ ){
        var thread = this[i];
        var slice = pass.splice( 0, subsize );

        var runP = thread.pass( slice ).run( fn );

        runPs.push( runP );

        var doneEarly = pass.length === 0;
        if( doneEarly ){ break; }
      }

      return $$.Promise.all( runPs ).then(function( thens ){
        var postpass = new Array();
        var p = 0;

        // fill postpass with the total result joined from all threads
        for( var i = 0; i < thens.length; i++ ){
          var then = thens[i]; // array result from thread i

          for( var j = 0; j < then.length; j++ ){
            var t = then[j]; // array element

            postpass[ p++ ] = t;
          }
        }

        return postpass;
      });
    },

    // parallel version of array.map()
    map: function( fn ){
      var self = this;

      self.require( fn, '_$_$_fabmap' );

      return self.spread(function( split ){
        var mapped = [];
        var origResolve = resolve;

        resolve = function( val ){
          mapped.push( val );
        };

        for( var i = 0; i < split.length; i++ ){
          var oldLen = mapped.length;
          var ret = _$_$_fabmap( split[i] );
          var nothingInsdByResolve = oldLen === mapped.length;

          if( nothingInsdByResolve ){
            mapped.push( ret );
          }
        }

        resolve = origResolve;

        return mapped;
      });

    },

    // parallel version of array.filter()
    filter: function( fn ){
      var _p = this._private;
      var pass = _p.pass[0];

      return this.map( fn ).then(function( include ){
        var ret = [];

        for( var i = 0; i < pass.length; i++ ){
          var datum = pass[i];
          var incDatum = include[i];

          if( incDatum ){
            ret.push( datum );
          }
        }

        return ret;
      });
    },

    // sorts the passed array using a divide and conquer strategy
    sort: function( cmp ){
      var self = this;
      var P = this._private.pass[0].length;
      var subsize = this.spreadSize();
      var N = this.length;

      cmp = cmp || function( a, b ){ // default comparison function
        if( a < b ){
          return -1;
        } else if( a > b ){
          return 1;
        }

        return 0;
      };

      self.require( cmp, '_$_$_cmp' );

      return self.spread(function( split ){ // sort each split normally
        var sortedSplit = split.sort( _$_$_cmp );
        resolve( sortedSplit );

      }).then(function( joined ){
        // do all the merging in the main thread to minimise data transfer

        // TODO could do merging in separate threads but would incur add'l cost of data transfer
        // for each level of the merge

        var merge = function( i, j, max ){
          // don't overflow array
          j = Math.min( j, P );
          max = Math.min( max, P );

          // left and right sides of merge
          var l = i;
          var r = j;

          var sorted = [];

          for( var k = l; k < max; k++ ){

            var eleI = joined[i];
            var eleJ = joined[j];

            if( i < r && ( j >= max || cmp(eleI, eleJ) <= 0 ) ){
              sorted.push( eleI );
              i++;
            } else {
              sorted.push( eleJ );
              j++;
            }

          }

          // in the array proper, put the sorted values
          for( var k = 0; k < sorted.length; k++ ){ // kth sorted item
            var index = l + k;

            joined[ index ] = sorted[k];
          }
        };

        for( var splitL = subsize; splitL < P; splitL *= 2 ){ // merge until array is "split" as 1

          for( var i = 0; i < P; i += 2*splitL ){
            merge( i, i + splitL, i + 2*splitL );
          }

        }

        return joined;
      });
    }

  });

  var defineRandomPasser = function( opts ){
    opts = opts || {};

    return function( fn ){
      var pass = this._private.pass.shift();

      return this.random().pass( pass )[ opts.threadFn ]( fn );
    };
  };

  $$.fn.fabric({
    randomMap: defineRandomPasser({ threadFn: 'map' }),

    reduce: defineRandomPasser({ threadFn: 'reduce' }),

    reduceRight: defineRandomPasser({ threadFn: 'reduceRight' })
  });

  // aliases
  var fn = $$.fabfn;
  fn.promise = fn.run;
  fn.terminate = fn.halt = fn.stop;

  // pull in event apis
  $$.fn.fabric({
    on: $$.define.on(),
    one: $$.define.on({ unbindSelfOnTrigger: true }),
    once: $$.define.on({ unbindAllBindersOnTrigger: true }),
    off: $$.define.off(),
    trigger: $$.define.trigger()
  });

  $$.define.eventAliasesOn( $$.fabfn );

})( cytoscape, typeof window === 'undefined' ? null : window );
<|endoftext|>