<commit_before>var pty = require('pty.js');
var EventEmitter = require('events').EventEmitter;

function BunyipRunner(options) {
    this.options = options;
    this.failed = -1;
    this.agents = -1;
}

function clone(object) {
    function OneShotConstructor(){}
    OneShotConstructor.prototype = object;
    return new OneShotConstructor();
}

/**
 * Inherit from `EventEmitter.prototype`.
 */

BunyipRunner.prototype = clone(EventEmitter.prototype);
BunyipRunner.prototype.constructor = BunyipRunner;

BunyipRunner.prototype.run = function(options) {
    options = this.options || options;
    this.options = options;
    var self = this;
    if(!options) {
        throw "No options given. Check your parameters!";
    }
    var args = options.args;
    var term = pty.spawn('bash', [], {
        name: 'xterm-color',
        cols: 80,
        rows: 30,
        cwd: process.cwd(),
        env: process.env
    });
    self.on("yeti", function() {
        setTimeout(function() {
            term.write('\r');
            self.emit("onBrowserEnter");
        }, options.waitBrowsersFor || 5000);
    });

    var exit = function() {
        if(typeof self.exit === 'function') {
            self.exit();
        }
    };

    process.on('exit', exit);
    term.on('exit', exit);

    var commands = 0;

    term.on('data', function(data) {
        var strData = ""+data;
        if(strData.indexOf('bash') !== -1) {
            if(commands > 0) {
                self.destroyTerminal(term);
            }
            commands++;
        }
        if(strData.indexOf('press Enter to begin testing') !== -1) {
            self.emit("yeti");
        }
        if(strData.indexOf('100% complete') !== -1) {
            var agentRegexp = /100% complete \((\d)/;
            var agents = agentRegexp.exec(strData);
            self.agents = parseInt(agents[1], 10);
        }
        if(strData.indexOf('tests failed') !== -1) {
            var failureRegexp = /(\d+) of (\d+) tests failed\. \((\d+)ms\)/;
            var fails = failureRegexp.exec(strData);
            self.failed = parseInt(fails[1], 10);
            self.total = parseInt(fails[2], 10);
            self.time = parseInt(fails[3], 10);
        }
        if(strData.indexOf('tests passed') !== -1) {
            var passedRegexp = /(\d+) tests passed!\u001b\[0m \((\d+)ms\)/;
            var passed = passedRegexp.exec(strData);
            self.total = parseInt(passed[1], 10);
            self.time = parseInt(passed[2], 10);
            self.failed = 0;
        }
        self.emit("data", data);
        console.log(strData);
    });
    setTimeout(function() {
        console.log("Running timeouted...");
        term.destroy();
    }, options.timeout || 30000);

    term.write(exports.createCommandFromArgs(args)+'\r');
};

BunyipRunner.prototype.destroyTerminal = function(term) {
    var self = this;
    setTimeout(function() {
        term.destroy();
    }, 1000);
};

BunyipRunner.prototype.exit = function() {
    this.exit = true;
    var values = {
        agents: this.agents,
        failed: this.failed,
        total: this.total,
        time: this.time
    };
    this.emit('exit', values);
};

exports.BunyipRunner = BunyipRunner;

function sanitizeArguments(args) {
    var sanitized = [];
    for(var i=0; i<args.length; i++) {
        var argh = args[i];
        if(!exports.isSane(argh)) {
            console.warn('Skipping parameter', argh);
        }
        else {
            sanitized.push(argh);
        }
    }
    return sanitized;
}

exports.createCommandFromArgs = function(args) {
    var command = "bunyip ";
    var sanitized = sanitizeArguments(args);
    for(var i=0; i<sanitized.length; i++) {
        command += sanitized[i];
        command += (i+1 === sanitized.length ? "" : " ");
    }
    return command;
};

var apostrophes = ["'", '"', '`'];

function isApostropheCharacter(chr) {
    var r = false;
    for(var i=0; i<apostrophes.length; i++) {
        if(apostrophes[i] === chr) {
            r = true;
        }
    }
    return r;
}

function onlyInEndAndBegin(str) {
    if(str.length < 2) {
        return false;
    }
    var begin = str[0],
        end = str[str.length-1];
    var r = false;
    for(var i=1; i<str.length; i++) {
        if(str[i] === begin && i === str.length-1) {
            r = true;
        }
    }
    return r;
}

var sanitizers = [
    function(param) {
        return param.indexOf('config') === -1;
    },
    function(param) {
        return param.indexOf(';') === -1;
    },
    function(param) {
        var r = false;
        if(!isApostropheCharacter(param[0])) {
            r = true;
        }
        else {
            r = onlyInEndAndBegin(param);
        }
        return r;
    },
    function(param) {
        var r = false;
        if(param.indexOf('|') !== -1) {
            var begin = param[0],
                end = param[param.length-1];
            if(isApostropheCharacter(begin) &&
               onlyInEndAndBegin(param)) {
                r = true;
            }
        }
        else {
            r = true;
        }
        return r;
    }
];

exports.isSane = function(arg) {
    for(var i=0; i<sanitizers.length; i++) {
        if(!sanitizers[i](arg)) {
            return false;
        }
    }
    return true;
};

module.exports = exports;
<commit_msg>Separated output parser and fixed command calculation.<commit_after>var pty = require('pty.js');
var EventEmitter = require('events').EventEmitter;

function BunyipOutputParser() {
}

BunyipOutputParser.prototype.matchYetiEnter = function(strData) {
    return strData.indexOf('press Enter to begin testing') !== -1;
};

BunyipOutputParser.prototype.matchTestsPassed = function(strData) {
    return strData.indexOf('tests passed') !== -1;
};

BunyipOutputParser.prototype.parsePassedTestsData = function(strData) {
    var values;
    if(this.matchTestsPassed(strData)) {
        values = {};
        var passedRegexp = /(\d+) tests passed!\u001b\[0m \((\d+)ms\)/;
        var passed = passedRegexp.exec(strData);
        values.total = parseInt(passed[1], 10);
        values.time = parseInt(passed[2], 10);
        values.failed = 0;
    }
    return values;
};

BunyipOutputParser.prototype.matchTestsFailed = function(strData) {
    return strData.indexOf('tests failed') !== -1;
};

BunyipOutputParser.prototype.parseFailedTestsData = function(strData) {
    var values;
    if(this.matchTestsFailed(strData)) {
        values = {};
        var failureRegexp = /(\d+) of (\d+) tests failed\. \((\d+)ms\)/;
        var fails = failureRegexp.exec(strData);
        values.failed = parseInt(fails[1], 10);
        values.total = parseInt(fails[2], 10);
        values.time = parseInt(fails[3], 10);
    }
    return values;
};

BunyipOutputParser.prototype.matchAgentsString = function(strData) {
    return strData.indexOf('100% complete') !== -1;
};

BunyipOutputParser.prototype.parseAgents = function(strData) {
    var agents;
    if(this.matchAgentsString(strData)) {
        var agentRegexp = /100% complete \((\d+)/;
        var agent = agentRegexp.exec(strData);
        agents = parseInt(agent[1], 10);
    }
    return agents;
};

exports.BunyipOutputParser = BunyipOutputParser;

function BunyipRunner(options) {
    this.options = options;
    this.testData = {};
}

function clone(object) {
    function OneShotConstructor(){}
    OneShotConstructor.prototype = object;
    return new OneShotConstructor();
}

/**
 * Inherit from `EventEmitter.prototype`.
 */

BunyipRunner.prototype = clone(EventEmitter.prototype);
BunyipRunner.prototype.constructor = BunyipRunner;

BunyipRunner.prototype.run = function(options) {
    options = this.options || options;
    this.options = options;
    var self = this;
    if(!options) {
        throw "No options given. Check your parameters!";
    }
    var args = options.args;
    var term = pty.spawn('bash', [], {
        name: 'xterm-color',
        cols: 80,
        rows: 30,
        cwd: process.cwd(),
        env: process.env
    });
    self.on("yeti", function() {
        setTimeout(function() {
            term.write('\r');
            self.emit("onBrowserEnter");
        }, options.waitBrowsersFor || 5000);
    });

    self.on('testData', function(data) {
        self.setTestData(data);
    });

    self.on('agents', function(agents) {
        self.setAgents(agents);
    });

    var exit = function() {
        if(typeof self.exit === 'function') {
            self.exit();
        }
    };

    process.on('exit', function() {
        if(term.writable || term.readable) {
            term.destroy();
        }
    });
    term.on('exit', exit);

    var parser = new BunyipOutputParser();
    var commands = 0;
    var dataCount = 0;
    var bashStr;
    var command = exports.createCommandFromArgs(args);

    var toBePrinted = [];
    var partial = "";

    term.on('data', function(data) {
        var printString = partial+data;
        var strData = ""+data;
        var split = printString.split('\r\n');
        if(split.length > 0) {
            partial = split.pop();
            toBePrinted = split;
        }
        if(dataCount === 1) {
            bashStr = strData;
            if(bashStr.indexOf('\r')) {
                bashStr = bashStr.split('\r')[0];
            }
        }
        if(bashStr &&
           (strData).indexOf(bashStr) !== -1) {
            if(commands === 2) {
                self.destroyTerminal(term);
            }
            commands++;
        }
        if(parser.matchYetiEnter(strData)) {
            self.emit("yeti");
        }
        var agents = parser.parseAgents(strData);
        if(agents) {
            self.emit('agents', agents);
        }
        var failedData = parser.parseFailedTestsData(strData);
        if(failedData) {
            self.emit('testData', failedData);
        }
        var passedData = parser.parsePassedTestsData(strData);
        if(passedData) {
            self.emit('testData', passedData);
        }
        for(var i=0; i<toBePrinted.length; i++) {
            console.log(toBePrinted[i]);
        }
        dataCount++;
        self.emit("data", data);
    });
    setTimeout(function() {
        console.log("Running timeouted...");
        term.destroy();
    }, options.timeout || 30000);
    term.write('\r');
    term.write(command+'\r');
};

BunyipRunner.prototype.setAgents = function(agents) {
    this.testData.agents = agents;
};

BunyipRunner.prototype.setTestData = function(values) {
    this.testData.total = values.total;
    this.testData.failed = values.failed;
    this.testData.time = values.time;
};

BunyipRunner.prototype.destroyTerminal = function(term) {
    var self = this;
    setTimeout(function() {
        term.destroy();
    }, 1000);
};

BunyipRunner.prototype.exit = function() {
    this.emit('exit', this.testData);
};

exports.BunyipRunner = BunyipRunner;

function sanitizeArguments(args) {
    var sanitized = [];
    for(var i=0; i<args.length; i++) {
        var argh = args[i];
        if(!exports.isSane(argh)) {
            console.warn('Skipping parameter', argh);
        }
        else {
            sanitized.push(argh);
        }
    }
    return sanitized;
}

exports.createCommandFromArgs = function(args) {
    var command = "bunyip ";
    var sanitized = sanitizeArguments(args);
    for(var i=0; i<sanitized.length; i++) {
        command += sanitized[i];
        command += (i+1 === sanitized.length ? "" : " ");
    }
    return command;
};

var apostrophes = ["'", '"', '`'];

function isApostropheCharacter(chr) {
    var r = false;
    for(var i=0; i<apostrophes.length; i++) {
        if(apostrophes[i] === chr) {
            r = true;
        }
    }
    return r;
}

function onlyInEndAndBegin(str) {
    if(str.length < 2) {
        return false;
    }
    var begin = str[0],
        end = str[str.length-1];
    var r = false;
    for(var i=1; i<str.length; i++) {
        if(str[i] === begin && i === str.length-1) {
            r = true;
        }
    }
    return r;
}

var sanitizers = [
    function(param) {
        return param.indexOf('config') === -1;
    },
    function(param) {
        return param.indexOf(';') === -1;
    },
    function(param) {
        var r = false;
        if(!isApostropheCharacter(param[0])) {
            r = true;
        }
        else {
            r = onlyInEndAndBegin(param);
        }
        return r;
    },
    function(param) {
        var r = false;
        if(param.indexOf('|') !== -1) {
            var begin = param[0],
                end = param[param.length-1];
            if(isApostropheCharacter(begin) &&
               onlyInEndAndBegin(param)) {
                r = true;
            }
        }
        else {
            r = true;
        }
        return r;
    }
];

exports.isSane = function(arg) {
    for(var i=0; i<sanitizers.length; i++) {
        if(!sanitizers[i](arg)) {
            return false;
        }
    }
    return true;
};

module.exports = exports;
<|endoftext|>


<commit_before>/*global exports, require, process*/

//node
var path = require('path'),
  fs = require('fs');

//npm
var _ = require('lodash');
//npm install wrapper
var phantomjs = require('phantomjs');

/**
 * Initializer For Grunt
 * @param grunt
 */
exports.init = function (grunt) {
  'use strict';

  return {

    testOnlyOptions : {
      pre         : true,
      post        : true,
      includes    : true,
      verbose     : true,
      'log-level' : true,
      'fail-fast' : true,
      'concise'   : true,
      'xunit'     : true
    },

    supportedEngines : [
      'phantomjs',
      'slimerjs'
    ],

    _helpers : {

      /**
       * Spawn Casperjs Child Process
       * @param cwd
       * @param args
       * @param next
       */
      spawn : function (cwd, args, next) {
        grunt.verbose.write('Spawning casperjs with args: ' + args + '\n');

        //Set PhantomJS Path
        process.env["PHANTOMJS_EXECUTABLE"] = phantomjs.path;

        //Local casperjs dependency path
        var isWindows = /^win/.test(process.platform),
         //Task Node Modules Path
         localModulesPath = path.resolve(__dirname, '../../node_modules'),
         //Casper Binary Location Via Npm Install
         casperBinaryPath = "/casperjs/bin/casperjs" + (isWindows ? ".exe" : ""),
         //Cross Platform Binary Support
         casperBin = localModulesPath + casperBinaryPath;

        if (!fs.existsSync(casperBin)) {
          grunt.log.error("CasperJS Binary Not Found, try `npm install`");
          return next(true);
        }

        grunt.util.spawn({
          cmd  : casperBin,
          args : args,
          opts : {
            cwd   : cwd,
            //see CasperJs output live
            stdio : 'inherit'
          }
        }, function (errorObj, result, code) {

          if (code > 0) {
            grunt.log.error(result.stdout);
            return next(true);
          }

          if (result.stdout) grunt.log.write(result.stdout + '\n\n');
          if (result.stderr) grunt.log.write(result.stderr + '\n\n');
          next();
        });
      }

    },

    execute : function (src, dest, options, args, next) {
      var self = this;
      grunt.verbose.write('Preparing casperjs spawn\n');
      var spawnOpts = [];
      var cwd = options.cwd || process.cwd();

      //add verbose flag for printing logs to screen
      if (options['log-level'] && !options.verbose) spawnOpts.push('--verbose');

      _.forEach(options, function (value, option) {
        if (options.test.length && self._helpers.testOnlyOptions[option]) {
          grunt.log.warn('Option ' + option + ' only available in test mode');
          return;
        }

        if (option) {
          switch (option) {
            case 'test':
              //Test requires specific order logic
              break;
            case 'xunit_out':
              if (typeof options.xunit_out === 'function') {
                //src passed as array reference
                options.xunit = options.xunit_out(src);
              } else {
                options.xunit = options.xunit_out;
              }
              break;
            case 'args' :
              //grunt arguments
              if (options.test.length) {
                grunt.log.warn('Arguments not supported ins test mode');
              } else {
                if (args.length) spawnOpts.push(args);
                value.forEach(function (arg) {
                  spawnOpts.push(arg);
                });
              }
              break;
            //add engine support outside of phantomJS
            case 'engine' :
              if (self.supportedEngines.indexOf(options['engine'])) {
                spawnOpts.push('--engine=' + options['engine']);
              } else {
                grunt.log.warn('Engine ' + options['engine'] + ' not available. [' + self.supportedEngines.join(',') + ']');
              }
              break;
            default:
              var currentOption = '--' + option + '=' + value;
              grunt.verbose.write('Adding Option ' + currentOption + '\n');
              spawnOpts.push(currentOption);
          }
        }
      });

      if (dest) {
        if (typeof dest === 'function') {
          dest = dest(src);
        }
        spawnOpts.push('--xunit=' + dest);
      }

      if (typeof src === 'object') {
        src.filter(function (file) {
          if (!grunt.file.exists(file)) {
            grunt.log.warn('Source file "' + file + '" not found.');
            return false;
          }
          return true;
        }).map(function (file) {
            //Make path absolute for SlimerJS
            if (!grunt.file.isPathAbsolute(file)) {
              file = path.join(cwd, file);
            }
            spawnOpts.unshift(file);
          });
      } else {
        spawnOpts.unshift(src);
      }

      if (options.test) {
        spawnOpts.unshift('test');
      }

      //Spawn Child Process
      this._helpers.spawn(cwd, spawnOpts, next);
    }
  };
};
<commit_msg>Check that phantomjs.path is actually a file<commit_after>/*global exports, require, process*/

//node
var path = require('path'),
  fs = require('fs');

//npm
var _ = require('lodash');
//npm install wrapper
var phantomjs = require('phantomjs');

/**
 * Initializer For Grunt
 * @param grunt
 */
exports.init = function (grunt) {
  'use strict';

  return {

    testOnlyOptions : {
      pre         : true,
      post        : true,
      includes    : true,
      verbose     : true,
      'log-level' : true,
      'fail-fast' : true,
      'concise'   : true,
      'xunit'     : true
    },

    supportedEngines : [
      'phantomjs',
      'slimerjs'
    ],

    _helpers : {

      /**
       * Spawn Casperjs Child Process
       * @param cwd
       * @param args
       * @param next
       */
      spawn : function (cwd, args, next) {
        grunt.verbose.write('Spawning casperjs with args: ' + args + '\n');

        //Set PhantomJS Path only if the file exists, otherwise fall back to ENV
        if (fs.existsSync(phantomjs.path)) {
          process.env["PHANTOMJS_EXECUTABLE"] = phantomjs.path;
        }

        //Local casperjs dependency path
        var isWindows = /^win/.test(process.platform),
         //Task Node Modules Path
         localModulesPath = path.resolve(__dirname, '../../node_modules'),
         //Casper Binary Location Via Npm Install
         casperBinaryPath = "/casperjs/bin/casperjs" + (isWindows ? ".exe" : ""),
         //Cross Platform Binary Support
         casperBin = localModulesPath + casperBinaryPath;

        if (!fs.existsSync(casperBin)) {
          grunt.log.error("CasperJS Binary Not Found, try `npm install`");
          return next(true);
        }

        grunt.util.spawn({
          cmd  : casperBin,
          args : args,
          opts : {
            cwd   : cwd,
            //see CasperJs output live
            stdio : 'inherit'
          }
        }, function (errorObj, result, code) {

          if (code > 0) {
            grunt.log.error(result.stdout);
            return next(true);
          }

          if (result.stdout) grunt.log.write(result.stdout + '\n\n');
          if (result.stderr) grunt.log.write(result.stderr + '\n\n');
          next();
        });
      }

    },

    execute : function (src, dest, options, args, next) {
      var self = this;
      grunt.verbose.write('Preparing casperjs spawn\n');
      var spawnOpts = [];
      var cwd = options.cwd || process.cwd();

      //add verbose flag for printing logs to screen
      if (options['log-level'] && !options.verbose) spawnOpts.push('--verbose');

      _.forEach(options, function (value, option) {
        if (options.test.length && self._helpers.testOnlyOptions[option]) {
          grunt.log.warn('Option ' + option + ' only available in test mode');
          return;
        }

        if (option) {
          switch (option) {
            case 'test':
              //Test requires specific order logic
              break;
            case 'xunit_out':
              if (typeof options.xunit_out === 'function') {
                //src passed as array reference
                options.xunit = options.xunit_out(src);
              } else {
                options.xunit = options.xunit_out;
              }
              break;
            case 'args' :
              //grunt arguments
              if (options.test.length) {
                grunt.log.warn('Arguments not supported ins test mode');
              } else {
                if (args.length) spawnOpts.push(args);
                value.forEach(function (arg) {
                  spawnOpts.push(arg);
                });
              }
              break;
            //add engine support outside of phantomJS
            case 'engine' :
              if (self.supportedEngines.indexOf(options['engine'])) {
                spawnOpts.push('--engine=' + options['engine']);
              } else {
                grunt.log.warn('Engine ' + options['engine'] + ' not available. [' + self.supportedEngines.join(',') + ']');
              }
              break;
            default:
              var currentOption = '--' + option + '=' + value;
              grunt.verbose.write('Adding Option ' + currentOption + '\n');
              spawnOpts.push(currentOption);
          }
        }
      });

      if (dest) {
        if (typeof dest === 'function') {
          dest = dest(src);
        }
        spawnOpts.push('--xunit=' + dest);
      }

      if (typeof src === 'object') {
        src.filter(function (file) {
          if (!grunt.file.exists(file)) {
            grunt.log.warn('Source file "' + file + '" not found.');
            return false;
          }
          return true;
        }).map(function (file) {
            //Make path absolute for SlimerJS
            if (!grunt.file.isPathAbsolute(file)) {
              file = path.join(cwd, file);
            }
            spawnOpts.unshift(file);
          });
      } else {
        spawnOpts.unshift(src);
      }

      if (options.test) {
        spawnOpts.unshift('test');
      }

      //Spawn Child Process
      this._helpers.spawn(cwd, spawnOpts, next);
    }
  };
};
<|endoftext|>


<commit_before>/*
 * grunt-list-files
 * https://github.com/phollome/grunt-list-files
 *
 * Copyright (c) 2016 Peter Holló
 * Licensed under the MIT license.
 */

'use strict';

var colors = require('colors');
var fs = require('fs');

var deepMerge = require('./../lib/deep-merge');
var deepClean = require('./../lib/deep-clean');

module.exports = function(grunt) {

  grunt.registerMultiTask('listfiles', 'List recursively all files from given folder.', function() {

    var options = this.options({
      ignore: '',
      clean: true,
      structured: true,
      combine: false
    });

    if(options.combine && !options.structured) {
      options.structured = true;
    }

    this.files.forEach(function(file) {
      var struct = {};
      if(options.structured) {
        struct['structure'] = {};
      }
      if(options.combine || !options.structured) {
        struct['files'] = [];
      }

      var cwd = file.cwd || '';
      file.src.filter(function(path) {
        var realPath = cwd + path;
        if (!grunt.file.exists(realPath)) {
          grunt.log.warn('Source "' + realPath + '" not found.');
          return false;
        } else if (realPath.length < options.ignore.length) {
          grunt.log.writeln(colors.italic('Ignoring "' + realPath + '".'));
          return false;
        }
        return true;
      }).map(function(path) {
        if (!path) {
          return;
        }
        if (options.structured) {
          var pathArr = path.replace(options.ignore, '').split('/');
          if (grunt.file.isDir(cwd + path)) {
            var dest = struct.structure;
            for (var i = 0, len = pathArr.length; i < len; i++) {
              if (grunt.file.isDir(cwd + path) && !dest[pathArr[i]]) {
                dest[pathArr[i]] = {
                  files: []
                };
              }
              dest = dest[pathArr[i]];
            }
            struct.structure = deepMerge(struct.structure, dest);
          }
        }
        if (grunt.file.isFile(cwd + path)) {
          return path;
        }
      }).forEach(function(path) {
        if (!path) {
          return;
        }
        if (options.structured) {
          var pathArr = path.replace(options.ignore, '').split('/');
          pathArr.pop();
          var dest = struct.structure;
          for (var i = 0, len = pathArr.length; i < len; i++) {
            dest = dest[pathArr[i]];
          }
          if (dest.files) {
            dest.files.push(path.replace(options.ignore, ''));
          }
        }
        if (!options.structured || options.combine){
          struct.files.push(path.replace(options.ignore, ''));
        }
      });
      if (options.ignore !== '') {
        struct['ignored'] = options.ignore;
      }
      if (options.clean && options.structured) {
        grunt.log.writeln(colors.italic('Removing empty elements by default.'));
        deepClean(struct);
      }
      grunt.file.write(file.dest, JSON.stringify(struct));
      grunt.log.write('File structure saved to "' + file.dest + '"...');
      grunt.log.writeln(colors.green('OK'));
    });
  });
};
<commit_msg>add route member if exists<commit_after>/*
 * grunt-list-files
 * https://github.com/phollome/grunt-list-files
 *
 * Copyright (c) 2016 Peter Holló
 * Licensed under the MIT license.
 */

'use strict';

var colors = require('colors');
var fs = require('fs');

var deepMerge = require('./../lib/deep-merge');
var deepClean = require('./../lib/deep-clean');

module.exports = function(grunt) {

  grunt.registerMultiTask('listfiles', 'List recursively all files from given folder.', function() {

    var options = this.options({
      ignore: '',
      clean: true,
      structured: true,
      combine: false
    });

    if(options.combine && !options.structured) {
      options.structured = true;
    }

    this.files.forEach(function(file) {
      var struct = {};
      if(options.structured) {
        struct['structure'] = {};
      }
      if(options.combine || !options.structured) {
        struct['files'] = [];
      }

      var cwd = file.cwd || '';
      file.src.filter(function(path) {
        var realPath = cwd + path;
        if (!grunt.file.exists(realPath)) {
          grunt.log.warn('Source "' + realPath + '" not found.');
          return false;
        } else if (realPath.length < options.ignore.length) {
          grunt.log.writeln(colors.italic('Ignoring "' + realPath + '".'));
          return false;
        }
        return true;
      }).map(function(path) {
        if (!path) {
          return;
        }
        if (options.structured) {
          var pathArr = path.replace(options.ignore, '').split('/');
          if (grunt.file.isDir(cwd + path)) {
            var dest = struct.structure;
            for (var i = 0, len = pathArr.length; i < len; i++) {
              if (grunt.file.isDir(cwd + path) && !dest[pathArr[i]]) {
                dest[pathArr[i]] = {
                  files: []
                };
              }
              dest = dest[pathArr[i]];
            }
            struct.structure = deepMerge(struct.structure, dest);
          }
        }
        if (grunt.file.isFile(cwd + path)) {
          return path;
        }
      }).forEach(function(path) {
        if (!path) {
          return;
        }
        if (options.structured) {
          var pathArr = path.replace(options.ignore, '').split('/');
          pathArr.pop();
          var dest = struct.structure;
          for (var i = 0, len = pathArr.length; i < len; i++) {
            dest = dest[pathArr[i]];
          }
          if (dest.files) {
            dest.files.push(path.replace(options.ignore, ''));
          }
        }
        if (!options.structured || options.combine){
          struct.files.push(path.replace(options.ignore, ''));
        }
      });
      if (options.ignore !== '') {
        struct['ignored'] = options.ignore;
      }
      if(options.route) {
        struct['route'] = options.route;
      }
      if (options.clean && options.structured) {
        grunt.log.writeln(colors.italic('Removing empty elements by default.'));
        deepClean(struct);
      }
      grunt.file.write(file.dest, JSON.stringify(struct));
      grunt.log.write('File structure saved to "' + file.dest + '"...');
      grunt.log.writeln(colors.green('OK'));
    });
  });
};
<|endoftext|>


<commit_before>/*
* grunt-tfs-unlock
* https://github.com/danactive/grunt-tfs-unlock
*
* Copyright (c) 2013 Dan BROOKS
* Licensed under the MIT license.
*/

'use strict';

module.exports = function(grunt) {

	// Please see the Grunt documentation for more information regarding task
	// creation: http://gruntjs.com/creating-tasks

	grunt.registerMultiTask('tfs-unlock', 'Checkout TFS files', function() {
		var tfs = require('./../node_modules/tfs-unlock/tfs-unlock.js'),
			options = this.options();

		if (options.tfsPath && options.tfsPath.length >= 2) {
			tfs.init({
				"callback": grunt.task.current.async(),
				"visualStudioPath": tfs[options.tfsPath[0]][options.tfsPath[1]]
			});
		} else {
			tfs.init({
				"callback": grunt.task.current.async()
			});
		}

		// Iterate over all specified file groups.
		this.files.forEach(function (file) {
			file.src.filter(function (filepath) {
				// Warn on and remove invalid source files (if nonull was set).
				if (!grunt.file.exists(filepath)) {
					grunt.log.warn('Source file "' + filepath + '" not found.');
					return false;
				}
				console.log(tfs[options.action]([filepath]));
				return true;
			});
		});
	});
};
<commit_msg>Fix async flow<commit_after>/*
* grunt-tfs-unlock
* https://github.com/danactive/grunt-tfs-unlock
*
* Copyright (c) 2013 Dan BROOKS
* Licensed under the MIT license.
*/

'use strict';

module.exports = function(grunt) {
	grunt.registerMultiTask('tfs-unlock', 'Checkout TFS files', function () {
		var tfs = require('tfs-unlock'),
			options = this.options(),
			done = this.async();

		if (options.tfsPath && options.tfsPath.length >= 2) {
			tfs.init({
				"callback": done,
				"visualStudioPath": tfs[options.tfsPath[0]][options.tfsPath[1]]
			});
		} else {
			tfs.init({
				"callback": done
			});
		}

		// Iterate over all specified file groups.
		var files = this.filesSrc.filter(function(file) {
			if (!grunt.file.exists(file)) {
				grunt.log.warn('Source file "' + file + '" not found.');
				return false;
			}
			return true;
		});

		tfs[options.action](files).catch(function(e) {
			grunt.log.error(e.message);
			done(false);
		});
	});
};
<|endoftext|>


<commit_before>// ==UserScript==
// @name         NBR_sidebar
// @version       1.0
// @description  show hide side menu on http://forum.notebookreview.com
// @author        katalin_2003
// @include        http://forum.notebookreview.com/*
// @match         http://forum.notebookreview.com/
// @grant          none
// ==/UserScript==

// Let's make it easier to add global styles
function addStyle(css) {
    var head, style;
    head = document.getElementsByTagName('head')[0];
    if (!head) { return; }
    style = document.createElement('style');
    style.type = 'text/css';
    style.innerHTML = css;
    head.appendChild(style);
}

// change font style in messages
addStyle('.messageText { font-family: sans-serif !important; }');

// reduce the min-height of messages to gain some vertical space in the case of small messages.
// or you can get the size of .messageUserInfo and use that value as the min-height for messages, though the former might not always have a fixed height for all users.
addStyle('.messageContent { min-height: 2em !important; }');

// add the missing right border..OCD much :)
addStyle('ol#forums { border-right: 1px solid #7B7B7B !important; }');
addStyle('div.sectionMain { border-right: 1px solid #7B7B7B !important; }');

function insertAfter(referenceNode, newNode) {
    referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}

// Toggle visibility function
function toggle(obj) {
    //  var el = document.getElementById(obj);
    var el = document.getElementsByClassName(obj)[0],
          mainContent = document.getElementsByClassName('mainContent')[0];
    if ( el.style.display != 'none' ) {
        el.style.display = 'none';
        sideTgl.innerHTML = '<<';
        mainContent.style.width = '100%';

    var	elWidth = el.offsetWidth,
        mainContentWidth = mainContent.offsetWidth;
        console.log('katalin_2003: sidebar hidden');
        console.log('katalin_2003: mainContent width is: ' + mainContentWidth + 'px');
    }
    else {
        el.style.display = '';
        sideTgl.innerHTML = '>>';

    var elWidth = el.offsetWidth,
          mainContentWidth = mainContent.offsetWidth;
          mainContent.style.width = (mainContentWidth - elWidth-10) +'px';

        console.log('katalin_2003: sidebar visible');
        console.log('katalin_2003: mainContent width is: ' + (mainContentWidth-elWidth-10) + 'px');
    }
}

var sideTgl = document.createElement('div');
       sideTgl.setAttribute('id', "sidebarToggle");
       sideTgl.style.cssFloat = 'right';
       sideTgl.style.color = '#000000';

sideTgl.innerHTML = ">>";
var div = document.getElementsByClassName("blockLinksList")[0];
insertAfter(div, sideTgl);

addStyle('div#sidebarToggle { border: 1px solid #7B7B7B; }');
addStyle('div#sidebarToggle { border-radius: 2px; }');
addStyle('div#sidebarToggle { padding: 1px 10px; }');
addStyle('div#sidebarToggle { margin-top: 3px; }');
addStyle('div#sidebarToggle { margin-right: 20px; }');
addStyle('div#sidebarToggle:hover { cursor: pointer; }');
addStyle('div#sidebarToggle:hover { background-color: #F0F0F0; }');

var sidebarToggle = document.getElementById('sidebarToggle');
sidebarToggle.setAttribute('title' , 'Toggle sidebar visibility');

// Hide the sidebar by default
(function() {
    toggle('sidebar');
    console.log('hidden');
    return false;
})();

// Toggle sidebar's visibility on click
sidebarToggle.onclick = function () {
    toggle('sidebar');
    return false;
}<commit_msg>Update nbr_sidebar.js<commit_after>// ==UserScript==
// @name         NBR_sidebar
// @version      1.0
// @description  show hide side menu on http://forum.notebookreview.com
// @author       katalin_2003
// @include      http://forum.notebookreview.com/*
// @match        http://forum.notebookreview.com/
// @grant        none
// ==/UserScript==

// Let's make it easier to add global styles
function addStyle(css) {
    var head, style;
    head = document.getElementsByTagName('head')[0];
    if (!head) { return; }
    style = document.createElement('style');
    style.type = 'text/css';
    style.innerHTML = css;
    head.appendChild(style);
}

// change font style in messages
addStyle('.messageText { font-family: sans-serif !important; }');

// reduce the min-height of messages to gain some vertical space in the case of small messages.
// or you can get the size of .messageUserInfo and use that value as the min-height for messages, though the former might not always have a fixed height for all users.
addStyle('.messageContent { min-height: 2em !important; }');

// add the missing right border..OCD much :)
addStyle('ol#forums { border-right: 1px solid #7B7B7B !important; }');
addStyle('div.sectionMain { border-right: 1px solid #7B7B7B !important; }');

function insertAfter(referenceNode, newNode) {
    referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}

// Toggle visibility function
function toggle(obj) {
    //  var el = document.getElementById(obj);
    var el = document.getElementsByClassName(obj)[0],
          mainContent = document.getElementsByClassName('mainContent')[0];
    if ( el.style.display != 'none' ) {
        el.style.display = 'none';
        sideTgl.innerHTML = '<<';
        mainContent.style.width = '100%';

    var	elWidth = el.offsetWidth,
        mainContentWidth = mainContent.offsetWidth;
        console.log('katalin_2003: sidebar hidden');
        console.log('katalin_2003: mainContent width is: ' + mainContentWidth + 'px');
    }
    else {
        el.style.display = '';
        sideTgl.innerHTML = '>>';

    var elWidth = el.offsetWidth,
          mainContentWidth = mainContent.offsetWidth;
          mainContent.style.width = (mainContentWidth - elWidth-10) +'px';

        console.log('katalin_2003: sidebar visible');
        console.log('katalin_2003: mainContent width is: ' + (mainContentWidth-elWidth-10) + 'px');
    }
}

var sideTgl = document.createElement('div');
       sideTgl.setAttribute('id', "sidebarToggle");
       sideTgl.style.cssFloat = 'right';
       sideTgl.style.color = '#000000';

sideTgl.innerHTML = ">>";
var div = document.getElementsByClassName("blockLinksList")[0];
insertAfter(div, sideTgl);

addStyle('div#sidebarToggle { border: 1px solid #7B7B7B; }');
addStyle('div#sidebarToggle { border-radius: 2px; }');
addStyle('div#sidebarToggle { padding: 1px 10px; }');
addStyle('div#sidebarToggle { margin-top: 3px; }');
addStyle('div#sidebarToggle { margin-right: 20px; }');
addStyle('div#sidebarToggle:hover { cursor: pointer; }');
addStyle('div#sidebarToggle:hover { background-color: #F0F0F0; }');

var sidebarToggle = document.getElementById('sidebarToggle');
sidebarToggle.setAttribute('title' , 'Toggle sidebar visibility');

// Hide the sidebar by default
(function() {
    toggle('sidebar');
    console.log('hidden');
    return false;
})();

// Toggle sidebar's visibility on click
sidebarToggle.onclick = function () {
    toggle('sidebar');
    return false;
}
<|endoftext|>


<commit_before>/// <reference types="node" />
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments)).next());
    });
};
const http_1 = require("http");
const fs_1 = require("fs");
const path = require("path");
const newPackageName = process.argv[2];
if (!newPackageName) {
    throw new Error("Usage: node scripts/new-package.js new-package-name");
}
run().catch(e => {
    console.error(e);
    process.exit(1);
});
function run() {
    return __awaiter(this, void 0, void 0, function* () {
        const files = [
            ["index.d.ts", yield getIndex()],
            [`${newPackageName}-tests.ts`, ""],
            ["tsconfig.json", `${JSON.stringify(getTSConfig(), undefined, 4)}\n`],
            ["tslint.json", '{ "extends": "../tslint.json" }\n'],
        ];
        try {
            yield pify1(fs_1.mkdir, newPackageName);
            yield Promise.all(files.map(([name, text]) => write(name, text)));
        }
        catch (e) {
            if (e.code === "EEXIST") {
                console.warn(`Module “${newPackageName}” already exists!`);
            }
            else {
                console.error(`Error creating module files: ${e}\nCleaning Up.`);
                yield Promise.all(files.map(([name]) => rm(name)));
                yield pify1(fs_1.rmdir, newPackageName);
            }
            process.exit(1);
        }
    });
}
function getIndex() {
    return __awaiter(this, void 0, void 0, function* () {
        let version = "x.x";
        let project = "https://github.com/baz/foo (Does not have to be to GitHub, but prefer linking to a source code repository rather than to a project website.)";
        try {
            const reg = yield loadString(`http://registry.npmjs.org/${newPackageName}`).then(JSON.parse);
            const { latest } = reg["dist-tags"];
            const { homepage } = reg.versions[latest];
            version = latest.split(".").slice(0, 2).join(".");
            if (homepage !== undefined)
                project = homepage;
        }
        catch (e) {
            console.warn(`Could not retrieve version/homepage information: ${e.stack}`);
        }
        return `// Type definitions for ${newPackageName} ${version}
// Project: ${project}
// Definitions by: My Self <https://github.com/me>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped

Fill the header in!
`;
    });
}
function getTSConfig() {
    return {
        "compilerOptions": {
            "module": "commonjs",
            "target": "es6",
            "noImplicitAny": true,
            "strictNullChecks": true,
            "baseUrl": "../",
            "typeRoots": [
                "../"
            ],
            "types": [],
            "noEmit": true,
            "forceConsistentCasingInFileNames": true
        },
        "files": [
            "index.d.ts",
            `${newPackageName}-tests.ts`
        ]
    };
}
function write(name, content) {
    return new Promise((resolve, reject) => {
        fs_1.writeFile(path.join(newPackageName, name), content, err => err ? reject(err) : resolve());
    });
}
function rm(name) {
    return pify1(fs_1.unlink, path.join(newPackageName, name)).catch((e) => {
        if (e.code === "ENOENT")
            return;
        throw e;
    });
}
function pify1(fn, arg) {
    return new Promise((resolve, reject) => {
        fn(arg, err => err ? reject(err) : resolve());
    });
}
function loadString(url) {
    return new Promise((resolve, reject) => {
        http_1.get(url, (res) => {
            if (res.statusCode !== 200) {
                return reject(new Error(`HTTP Error ${res.statusCode}: ${http_1.STATUS_CODES[res.statusCode || 500]} for ${url}`));
            }
            let rawData = "";
            res.on("data", chunk => rawData += chunk);
            res.on("end", () => resolve(rawData));
        }).on("error", e => reject(e));
    });
}
<commit_msg>Compile scripts (#13548)<commit_after>/// <reference types="node" />
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
const http_1 = require("http");
const fs_1 = require("fs");
const path = require("path");
const newPackageName = process.argv[2];
if (!newPackageName) {
    throw new Error("Usage: node scripts/new-package.js new-package-name");
}
run().catch(e => {
    console.error(e);
    process.exit(1);
});
function run() {
    return __awaiter(this, void 0, void 0, function* () {
        const files = [
            ["index.d.ts", yield getIndex()],
            [`${newPackageName}-tests.ts`, ""],
            ["tsconfig.json", `${JSON.stringify(getTSConfig(), undefined, 4)}\n`],
            ["tslint.json", '{ "extends": "../tslint.json" }\n'],
        ];
        try {
            yield pify1(fs_1.mkdir, newPackageName);
            yield Promise.all(files.map(([name, text]) => write(name, text)));
        }
        catch (e) {
            if (e.code === "EEXIST") {
                console.warn(`Module “${newPackageName}” already exists!`);
            }
            else {
                console.error(`Error creating module files: ${e}\nCleaning Up.`);
                yield Promise.all(files.map(([name]) => rm(name)));
                yield pify1(fs_1.rmdir, newPackageName);
            }
            process.exit(1);
        }
    });
}
function getIndex() {
    return __awaiter(this, void 0, void 0, function* () {
        let version = "x.x";
        let project = "https://github.com/baz/foo (Does not have to be to GitHub, but prefer linking to a source code repository rather than to a project website.)";
        try {
            const reg = yield loadString(`http://registry.npmjs.org/${newPackageName}`).then(JSON.parse);
            const { latest } = reg["dist-tags"];
            const { homepage } = reg.versions[latest];
            version = latest.split(".").slice(0, 2).join(".");
            if (homepage !== undefined)
                project = homepage;
        }
        catch (e) {
            console.warn(`Could not retrieve version/homepage information: ${e.stack}`);
        }
        return `// Type definitions for ${newPackageName} ${version}
// Project: ${project}
// Definitions by: My Self <https://github.com/me>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped

Fill the header in!
`;
    });
}
function getTSConfig() {
    return {
        "compilerOptions": {
            "module": "commonjs",
            "target": "es6",
            "noImplicitAny": true,
            "noImplicitThis": true,
            "strictNullChecks": true,
            "baseUrl": "../",
            "typeRoots": [
                "../"
            ],
            "types": [],
            "noEmit": true,
            "forceConsistentCasingInFileNames": true
        },
        "files": [
            "index.d.ts",
            `${newPackageName}-tests.ts`
        ]
    };
}
function write(name, content) {
    return new Promise((resolve, reject) => {
        fs_1.writeFile(path.join(newPackageName, name), content, err => err ? reject(err) : resolve());
    });
}
function rm(name) {
    return pify1(fs_1.unlink, path.join(newPackageName, name)).catch((e) => {
        if (e.code === "ENOENT")
            return;
        throw e;
    });
}
function pify1(fn, arg) {
    return new Promise((resolve, reject) => {
        fn(arg, err => err ? reject(err) : resolve());
    });
}
function loadString(url) {
    return new Promise((resolve, reject) => {
        http_1.get(url, (res) => {
            if (res.statusCode !== 200) {
                return reject(new Error(`HTTP Error ${res.statusCode}: ${http_1.STATUS_CODES[res.statusCode || 500]} for ${url}`));
            }
            let rawData = "";
            res.on("data", chunk => rawData += chunk);
            res.on("end", () => resolve(rawData));
        }).on("error", e => reject(e));
    });
}
<|endoftext|>


<commit_before>// Split a string from a style attribute into a hash of styles
// e.g. style="fill:#269276;opacity:1" => {fill: '#269276', opacity: '1'}

var parseStyle = function(styleString) {
    var styles = {};
    var styleArray = styleString.split(/\s*;\s*/);

    for (var i = 0; i < styleArray.length; i++) {
        var value = styleArray[i].split(/\s*:\s*/);

        if (value.length === 2) {
            styles[value[0]] = value[1];
        }
    }

    return styles;
}

// Node in an SVG document
// Contains all the options for optimising how the SVG is written

var SVG_Element = function(element, parents) {
    this.tag = element.nodeName;
    this.attributes = {};
    this.styles = {};
    this.parents = parents;
    this.text = "";

    // Add attributes to hash
    // Style attributes have a separate hash
    if (element.attributes) {
        for (var i = 0; i < element.attributes.length; i++){
            var attr = element.attributes.item(i);

            if (attr.nodeName === 'style') {
                this.styles = parseStyle(attr.nodeValue);
            } else {
                // Should add attributes like 'fill' to style hash
                this.attributes[attr.nodeName] = attr.nodeValue;
            }
        }
        this.id = this.attributes.id;
    }

    // Add children
    this.children = [];

    for (var i = 0; i < element.childNodes.length; i++) {
        var child = element.childNodes[i];
        if (child instanceof Text) {
            // Tag contains text
            if (child.data.replace(/^\s*/, "") !== "") {
                this.text = child.data;
            }
        } else {
            this.children.push(new SVG_Element(child, this));
        }
    }
};

// Return an array of attributes that have not been removed
SVG_Element.prototype.getUsedAttributes = function(options) {
    var usedAttributes = [];

    for (var attr in this.attributes) {
        // Remove attributes whose namespace has been removed and links to namespace URIs
        if (attr.indexOf(':') !== -1) {
            var ns = attr.split(':');
            if (!options.namespaces[ns[0]] || (ns[0] === 'xmlns' && !options.namespaces[ns[1]])) {
                continue;
            }
        }

        // TODO: only remove ids that are not referenced elsewhere
        if (options.removeIDs && attr === 'id') {
            continue;
        }

        usedAttributes.push(attr);
    }

    return usedAttributes;
};

// Return a list of strings in the form "style:value" for the styles that are to be used
SVG_Element.prototype.getUsedStyles = function(removeDefaultStyles) {
    if (!this.styles) { return []; }

    var usedStyles = [];
    var ignoreFill = (this.styles['fill'] === 'none' || this.styles['fill-opacity'] === '0');
    var ignoreStroke = (this.styles['stroke'] === 'none' || this.styles['stroke-opacity'] === '0' || this.styles['stroke-width'] === '0');

    for (var style in this.styles) {
        if (ignoreFill && style.substr(0, 4) === 'fill') { continue; }
        if (ignoreStroke && style.substr(0, 6) === 'stroke') { continue; }
        if (removeDefaultStyles && this.styles[style] === defaultStyles[style]) { continue; }

        usedStyles.push(style + ":" + this.styles[style]);
    }

    if (ignoreFill) { usedStyles.push('fill:none'); }

    return usedStyles.sort();
};

// Return a string representing the SVG element
SVG_Element.prototype.toString = function(options, depth) {
    // Remove namespace information
    if (this.tag.indexOf(':') !== -1) {
        var ns = this.tag.split(':')[0];
        if (!options.namespaces[ns]) {
            return "";
        }
    }

    var depth = depth || 0;
    var indent = (options.whitespace === 'remove') ? '' : new Array(depth + 1).join('  ');

    var str = indent + '<' + this.tag;

    // Write attributes
    var usedAttributes = this.getUsedAttributes(options);
    for (var i = 0; i < usedAttributes.length; i++) {
        var attr = usedAttributes[i];
        str += ' ' + attr + '="';
        str += this.attributes[attr];
        str += '"';
    }

    // Write styles
    var styleString = this.getUsedStyles(options.removeDefaultStyles).join(';');
    if (styleString) {
        str += ' style="' + styleString + '"';
    }

    // Write child information
    var childString = "";
    for (var i = 0; i < this.children.length; i++) {
        childString += this.children[i].toString(options, depth + 1);
    }

    if (this.text.length + childString.length > 0) {
        str += ">" + options.newLine;
        if (this.text) {
            str += indent + "  " + this.text + options.newLine;
        }
        if (childString) {
            str += childString;
        }
        str += indent + "</" + this.tag + ">" + options.newLine;
    } else {
        if (options.removeEmptyElements && usedAttributes.length === 0) {
            return "";
        }
        str += "/>" + options.newLine;
    }

    return str;
};

// A wrapper for SVG_Elements which store the options for optimisation
// Build from a jQuery object representing the SVG
var SVG_Object = function(jQuerySVG) {
    this.elements = new SVG_Element(jQuerySVG, null);

    // Set default options
    this.options = {
        whitespace: 'remove',
        removeIDs: false,
        removeDefaultStyles: true,
        removeEmptyElements: true
    };

    // Namespaces are attributes of the SVG element, prefaced with 'xmlns:'
    // Create a hash mapping namespaces to false, except for the SVG namespace
    this.findNamespaces = function() {
        var namespaces = {};

        for (attr in this.elements.attributes) {
            if (attr.slice(0,6) === 'xmlns:') {
                var ns = attr.split(':')[1];
                namespaces[ns] = (ns === 'svg');
            }
        }

        return namespaces;
    };
    this.options.namespaces = this.findNamespaces();
};

SVG_Object.prototype.toString = function() {
    this.options.newLine = (this.options.whitespace === 'remove') ? "": "\n";

    return this.elements.toString(this.options);
};<commit_msg>Remove groups that don't do anything (but show the children)<commit_after>// Split a string from a style attribute into a hash of styles
// e.g. style="fill:#269276;opacity:1" => {fill: '#269276', opacity: '1'}

var parseStyle = function(styleString) {
    var styles = {};
    var styleArray = styleString.split(/\s*;\s*/);

    for (var i = 0; i < styleArray.length; i++) {
        var value = styleArray[i].split(/\s*:\s*/);

        if (value.length === 2) {
            styles[value[0]] = value[1];
        }
    }

    return styles;
}

// Node in an SVG document
// Contains all the options for optimising how the SVG is written

var SVG_Element = function(element, parents) {
    this.tag = element.nodeName;
    this.attributes = {};
    this.styles = {};
    this.parents = parents;
    this.text = "";

    // Add attributes to hash
    // Style attributes have a separate hash
    if (element.attributes) {
        for (var i = 0; i < element.attributes.length; i++){
            var attr = element.attributes.item(i);

            if (attr.nodeName === 'style') {
                this.styles = parseStyle(attr.nodeValue);
            } else {
                // Should add attributes like 'fill' to style hash
                this.attributes[attr.nodeName] = attr.nodeValue;
            }
        }
        this.id = this.attributes.id;
    }

    // Add children
    this.children = [];

    for (var i = 0; i < element.childNodes.length; i++) {
        var child = element.childNodes[i];
        if (child instanceof Text) {
            // Tag contains text
            if (child.data.replace(/^\s*/, "") !== "") {
                this.text = child.data;
            }
        } else {
            this.children.push(new SVG_Element(child, this));
        }
    }
};

// Return an array of attributes that have not been removed
SVG_Element.prototype.getUsedAttributes = function(options) {
    var usedAttributes = [];

    for (var attr in this.attributes) {
        // Remove attributes whose namespace has been removed and links to namespace URIs
        if (attr.indexOf(':') !== -1) {
            var ns = attr.split(':');
            if (!options.namespaces[ns[0]] || (ns[0] === 'xmlns' && !options.namespaces[ns[1]])) {
                continue;
            }
        }

        // TODO: only remove ids that are not referenced elsewhere
        if (options.removeIDs && attr === 'id') {
            continue;
        }

        usedAttributes.push(attr);
    }

    return usedAttributes;
};

// Return a list of strings in the form "style:value" for the styles that are to be used
SVG_Element.prototype.getUsedStyles = function(removeDefaultStyles) {
    if (!this.styles) { return []; }

    var usedStyles = [];
    var ignoreFill = (this.styles['fill'] === 'none' || this.styles['fill-opacity'] === '0');
    var ignoreStroke = (this.styles['stroke'] === 'none' || this.styles['stroke-opacity'] === '0' || this.styles['stroke-width'] === '0');

    for (var style in this.styles) {
        if (ignoreFill && style.substr(0, 4) === 'fill') { continue; }
        if (ignoreStroke && style.substr(0, 6) === 'stroke') { continue; }
        if (removeDefaultStyles && this.styles[style] === defaultStyles[style]) { continue; }

        usedStyles.push(style + ":" + this.styles[style]);
    }

    if (ignoreFill) { usedStyles.push('fill:none'); }

    return usedStyles.sort();
};

// Return a string representing the SVG element
SVG_Element.prototype.toString = function(options, depth) {
    // Remove namespace information
    if (this.tag.indexOf(':') !== -1) {
        var ns = this.tag.split(':')[0];
        if (!options.namespaces[ns]) {
            return "";
        }
    }

    var depth = depth || 0;
    var indent = (options.whitespace === 'remove') ? '' : new Array(depth + 1).join('  ');

    var str = indent + '<' + this.tag;

    // Write attributes
    var usedAttributes = this.getUsedAttributes(options);
    for (var i = 0; i < usedAttributes.length; i++) {
        var attr = usedAttributes[i];
        str += ' ' + attr + '="';
        str += this.attributes[attr];
        str += '"';
    }

    // Write styles
    var styleString = this.getUsedStyles(options.removeDefaultStyles).join(';');
    if (styleString) {
        str += ' style="' + styleString + '"';
    }

    // Don't write group if it has no attributes, but do write its children
    // Assume g element has no text (which it shouldn't)
    if (this.tag === 'g' && options.removeCleanGroups && usedAttributes.length === 0 && styleString === "") {
        var childString = "";
        for (var i = 0; i < this.children.length; i++) {
            childString += this.children[i].toString(options, depth + 1);
        }
        return childString;
    }

    // Write child information
    var childString = "";
    for (var i = 0; i < this.children.length; i++) {
        childString += this.children[i].toString(options, depth + 1);
    }

    if (this.text.length + childString.length > 0) {
        str += ">" + options.newLine;
        if (this.text) {
            str += indent + "  " + this.text + options.newLine;
        }
        if (childString) {
            str += childString;
        }
        str += indent + "</" + this.tag + ">" + options.newLine;
    } else {
        if (options.removeEmptyElements && usedAttributes.length === 0) {
            return "";
        }
        str += "/>" + options.newLine;
    }

    return str;
};

// A wrapper for SVG_Elements which store the options for optimisation
// Build from a jQuery object representing the SVG
var SVG_Object = function(jQuerySVG) {
    this.elements = new SVG_Element(jQuerySVG, null);

    // Set default options
    this.options = {
        whitespace: 'remove',
        removeIDs: false,
        removeDefaultStyles: true,
        removeEmptyElements: true,
        removeCleanGroups: true
    };

    // Namespaces are attributes of the SVG element, prefaced with 'xmlns:'
    // Create a hash mapping namespaces to false, except for the SVG namespace
    this.findNamespaces = function() {
        var namespaces = {};

        for (attr in this.elements.attributes) {
            if (attr.slice(0,6) === 'xmlns:') {
                var ns = attr.split(':')[1];
                namespaces[ns] = (ns === 'svg');
            }
        }

        return namespaces;
    };
    this.options.namespaces = this.findNamespaces();
};

SVG_Object.prototype.toString = function() {
    this.options.newLine = (this.options.whitespace === 'remove') ? "": "\n";

    return this.elements.toString(this.options);
};<|endoftext|>


<commit_before>module.exports = 'ec2-user@blot.im';<commit_msg>updated remote script<commit_after>module.exports = 'blot';

// this works as a host because I have aliased
// blot to ec2-user@blot.im ~/.ssh/config with
// appropriate path to public key<|endoftext|>


<commit_before>#!/usr/bin/nodejs

var request = require('request');
var util = require('util');
var argv = require('minimist')(process.argv.slice(2));

if (argv._.length < 2) {
    console.error('Usage: nodejs renderLayer.js [--mode=<generate|force>] [--threads=num] url zoom [end_zoom]\n');
    process.exit(1);
}

var mode = argv.mode || 'force';
if (mode != 'generate' && mode != 'force') {
    console.error('Unknown mode\n');
    process.exit(1);
}
var threads = argv.threads || 1;
var baseUrl = argv._[0];
var startZoom = parseInt(argv._[1]);
var endZoom = parseInt(argv._[2]) || startZoom;

var zoom = startZoom - 1, x, y;
var dim;

function nextZoomLevel() {
    zoom++;

    if (zoom > endZoom) {
        return false;
    }
    x = y = 0;
    dim = Math.pow(2, zoom);

    return true;
}

function nextTile() {
    if (x >= dim) {
        y++;
        x = 0;
    }
    if (y >= dim) {
        if (!nextZoomLevel()) {
            return false;
        }
    }
    var result = [zoom, x, y];
    x++;

    return result;
}

function renderTile(threadNo) {
    var zxy = nextTile();

    if (!zxy) {
        console.log('Thread ' + threadNo + ' finished!');
        return;
    }

    var url = util.format('%s/%d/%d/%d.vector.pbf.%s', baseUrl, zxy[0], zxy[1], zxy[2], mode);
    console.log('Thread ' + threadNo + ' is requesting ' + url);
    request.get(url)
        .on('response', function(response) {
            if (response.statusCode != 200) {
                console.error(url + ': HTTP ' + response.statusCode);
            }
            renderTile(threadNo);
        })
        .on('error', function(err) {
            console.error(typeof err, err);
            process.exit(1);
        });
}

nextZoomLevel();
for (var i = 0; i < threads; i++) {
    renderTile(i);
}
<commit_msg>Use preq instead of request<commit_after>#!/usr/bin/nodejs

var preq = require('preq');
var util = require('util');
var argv = require('minimist')(process.argv.slice(2));

if (argv._.length < 2) {
    console.error('Usage: nodejs renderLayer.js [--mode=<generate|force>] [--threads=num] url zoom [end_zoom]\n');
    process.exit(1);
}

var mode = argv.mode || 'force';
if (mode != 'generate' && mode != 'force') {
    console.error('Unknown mode\n');
    process.exit(1);
}
var threads = argv.threads || 1;
var baseUrl = argv._[0];
var startZoom = parseInt(argv._[1]);
var endZoom = parseInt(argv._[2]) || startZoom;

var zoom = startZoom - 1, x, y;
var dim;

function nextZoomLevel() {
    zoom++;

    if (zoom > endZoom) {
        return false;
    }
    x = y = 0;
    dim = Math.pow(2, zoom);

    return true;
}

function nextTile() {
    if (x >= dim) {
        y++;
        x = 0;
    }
    if (y >= dim) {
        if (!nextZoomLevel()) {
            return false;
        }
    }
    var result = [zoom, x, y];
    x++;

    return result;
}

function renderTile(threadNo) {
    var zxy = nextTile();

    if (!zxy) {
        console.log('Thread ' + threadNo + ' finished!');
        return;
    }

    var url = util.format('%s/%d/%d/%d.vector.pbf.%s', baseUrl, zxy[0], zxy[1], zxy[2], mode);
    console.log('Thread ' + threadNo + ' is requesting ' + url);
    preq.get(url)
        .then(function(response) {
            renderTile(threadNo);
        })
        .catch(function(err) {
            console.error(err.body.stack);
            process.exit(1);
        });
}

nextZoomLevel();
for (var i = 0; i < threads; i++) {
    renderTile(i);
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2012 Adobe Systems Incorporated and other contributors.
 * All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, browser: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */
/*global define, brackets, $ */

/**
 * provides events:
 *
 *  - editorChanged (editor, oldEditor, path)
 */

define(function (require, exports, module) {
    "use strict";

    var DocumentManager = brackets.getModule("document/DocumentManager");
    var EditorManager   = brackets.getModule("editor/EditorManager");
    var ProjectManager  = brackets.getModule("project/ProjectManager");
    var Agent           = require("Agent");
    var Main            = require("main");

    var $exports = $(exports);
    var _editor;
    var _enabled = false;

    function _removeURLScheme(url) {
        return url.replace(/^[^:]+:\/\//, "");
    }

    function _editorChanged(event, focusedEditor, oldEditor) {
        // ignore focusedEditor, which could be inline. gimme the full editor.
        if (EditorManager.getCurrentFullEditor() !== _editor) {
            _editor = EditorManager.getCurrentFullEditor();
            if (_enabled) {
                $exports.triggerHandler("editorChanged", [_editor, oldEditor, currentPath()]);
            }
        }
    }

    function _scrollIntoView(editor, start, end) {
        var codeMirror = editor._codeMirror;

        var startPos = codeMirror.charCoords(start, "local");
        var endPos = codeMirror.charCoords(end, "local");
        var scrollInfo = codeMirror.getScrollInfo();

        var rangeTop = startPos.top;
        var rangeBottom = endPos.bottom;
        var viewportTop = scrollInfo.top;
        var viewportBottom = scrollInfo.top + scrollInfo.height;

        codeMirror.scrollIntoView({
            left: 0,
            right: 0,
            top: rangeTop - 15,
            bottom: rangeBottom + 15
        });
    }

    function _mark(editor, start, end, klass) {
        return editor._codeMirror.markText({ line: start.line - 1, ch: start.column },
                                           { line: end.line - 1,   ch: end.column },
                                           klass);
    }

    function currentEditor() {
        return _editor;
    }

    /** path of the currently active editor (or undefined) */
    function currentPath() {
        if (_editor) {
            return _removeURLScheme(_editor.document.file.fullPath);
        }
    }

    /**
     * highlights the function if it's in the current file
     * @return a CodeMirror mark with a clear() you can call to erase the mark
     */
    function markFunction(f) {
        if (f.path === currentPath()) {
            return _mark(_editor, f.start, f.end, "current-function");
        }
    }

    /**
     * highlights the call site if it's in the current file
     * @return a CodeMirror mark with a clear() you can call to erase the mark
     */
    function markCallSite(callSite) {
        if (callSite.path === currentPath()) {
            return _mark(_editor, callSite.start, callSite.end, "current-call-site");
        }
    }

    function mark(editor, start, end, klass) {
        return _mark(editor, start, end, klass);
    }

    /**
     * switches to an editor for the given path, then calls callback with
     * the editor (or no arguments if the path couldn't be opened)
     */
    function switchToEditorFor(path, callback) {
        if (currentPath() === path) {
            callback(currentEditor());
        } else if (ProjectManager.isWithinProject(path)) {
            DocumentManager.getDocumentForPath(path).done(function (doc) {
                DocumentManager.setCurrentDocument(doc);
                callback(EditorManager.getCurrentFullEditor());
            });
        } else {
            callback();
        }
    }

    function revealFunction(f, callback) {
        switchToEditorFor(f.path, function (editor) {
            var start = { line: f.start.line - 1, ch: f.start.column };
            var end = { line: f.end.line - 1,   ch: f.end.column };
            _scrollIntoView(editor, start, end);
            callback && callback(editor);
        });
    }

    function _enable() {
        _enabled = true;
    }

    function _disable() {
        _enabled = false;
    }

    function init() {
        $(EditorManager).on("activeEditorChange", _editorChanged);
        _editorChanged(null, EditorManager.getCurrentFullEditor());

        $(Main).on("enable", _enable);
        $(Main).on("disable", _disable);
    }

    function unload() {
        $(Main).off("enable", _enable);
        $(Main).off("disable", _disable);

        $exports.off();
    }

    exports.init = init;
    exports.unload = unload;
    exports.currentEditor = currentEditor;
    exports.currentPath = currentPath;
    exports.markFunction = markFunction;
    exports.markCallSite = markCallSite;
    exports.mark = mark;
    exports.switchToEditorFor = switchToEditorFor;
    exports.revealFunction = revealFunction;
});
<commit_msg>removed a bunch of dead code from EditorInterface.js<commit_after>/*
 * Copyright (c) 2012 Adobe Systems Incorporated and other contributors.
 * All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, browser: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */
/*global define, brackets, $ */

/**
 * provides events:
 *
 *  - editorChanged (editor, oldEditor, path)
 */

define(function (require, exports, module) {
    "use strict";

    var DocumentManager = brackets.getModule("document/DocumentManager");
    var EditorManager   = brackets.getModule("editor/EditorManager");
    var ProjectManager  = brackets.getModule("project/ProjectManager");
    var Agent           = require("Agent");
    var Main            = require("main");

    var $exports = $(exports);
    var _editor;
    var _enabled = false;

    function _removeURLScheme(url) {
        return url.replace(/^[^:]+:\/\//, "");
    }

    function _editorChanged(event, focusedEditor, oldEditor) {
        // ignore focusedEditor, which could be inline. gimme the full editor.
        if (EditorManager.getCurrentFullEditor() !== _editor) {
            _editor = EditorManager.getCurrentFullEditor();
            if (_enabled) {
                $exports.triggerHandler("editorChanged", [_editor, oldEditor, currentPath()]);
            }
        }
    }

    function _scrollIntoView(editor, start, end) {
        var codeMirror = editor._codeMirror;

        var startPos = codeMirror.charCoords(start, "local");
        var endPos = codeMirror.charCoords(end, "local");
        var scrollInfo = codeMirror.getScrollInfo();

        var rangeTop = startPos.top;
        var rangeBottom = endPos.bottom;
        var viewportTop = scrollInfo.top;
        var viewportBottom = scrollInfo.top + scrollInfo.height;

        codeMirror.scrollIntoView({
            left: 0,
            right: 0,
            top: rangeTop - 15,
            bottom: rangeBottom + 15
        });
    }

    function currentEditor() {
        return _editor;
    }

    /** path of the currently active editor (or undefined) */
    function currentPath() {
        if (_editor) {
            return _removeURLScheme(_editor.document.file.fullPath);
        }
    }

    /**
     * switches to an editor for the given path, then calls callback with
     * the editor (or no arguments if the path couldn't be opened)
     */
    function switchToEditorFor(path, callback) {
        if (currentPath() === path) {
            callback(currentEditor());
        } else if (ProjectManager.isWithinProject(path)) {
            DocumentManager.getDocumentForPath(path).done(function (doc) {
                DocumentManager.setCurrentDocument(doc);
                callback(EditorManager.getCurrentFullEditor());
            });
        } else {
            callback();
        }
    }

    function revealFunction(f, callback) {
        switchToEditorFor(f.path, function (editor) {
            var start = { line: f.start.line - 1, ch: f.start.column };
            var end = { line: f.end.line - 1,   ch: f.end.column };
            _scrollIntoView(editor, start, end);
            callback && callback(editor);
        });
    }

    function _enable() {
        _enabled = true;
    }

    function _disable() {
        _enabled = false;
    }

    function init() {
        $(EditorManager).on("activeEditorChange", _editorChanged);
        _editorChanged(null, EditorManager.getCurrentFullEditor());

        $(Main).on("enable", _enable);
        $(Main).on("disable", _disable);
    }

    function unload() {
        $(Main).off("enable", _enable);
        $(Main).off("disable", _disable);

        $exports.off();
    }

    exports.init = init;
    exports.unload = unload;
    exports.currentEditor = currentEditor;
    exports.currentPath = currentPath;
    exports.switchToEditorFor = switchToEditorFor;
    exports.revealFunction = revealFunction;
});
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import { View, WebView } from 'react-native';

/**
 * @author 田尘殇Sean(sean.snow@live.com)
 * @date 2017/6/16
 */
class Icon extends Component {

  static propTypes = {
    ...WebView.propTypes,
    fill: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
    height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
    viewBox: PropTypes.string,
    width: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
  };

  static defaultProps = {
    height: 24,
    width: 24
  };

  render() {
    const {width, height, viewBox, fill, style, ...props} = this.props;

    let js = `
    var svg = document.getElementsByTagName('svg')[0];
    var paths = document.getElementsByTagName('path');
    `;
    if (width) {
      js = `${js} svg.setAttribute('width','${width}px');`;
    }
    if (height) {
      js = `${js} svg.setAttribute('height','${height}px');`;
    }
    if (viewBox) {
      js = `${js} svg.setAttribute('height','${viewBox}');`;
    }
    if (fill) {
      const onlyOneColor = !Array.isArray(fill);
      js = `${js}
      var colors = ${JSON.stringify(fill)};
      for(var i = 0; i < paths.length; i++){
        var path = paths[i];
        var color = ${onlyOneColor} ? '${fill}' : colors[i] || '#000';
        path && path.setAttribute('fill',  color);
      }
      `;

    }

    const size = {
      width,
      height
    };
    return (
      <View style={[size, style]}>
        <WebView {...props}
                 domStorageEnabled
                 injectedJavaScript={js}
                 javaScriptEnabled
                 scrollEnabled={false}
                 style={{backgroundColor: 'transparent'}}
        />
      </View>
    );
  }

}

export default Icon;
<commit_msg>webview size 设置<commit_after>import React, { Component, PropTypes } from 'react';
import { View, WebView } from 'react-native';

/**
 * @author 田尘殇Sean(sean.snow@live.com)
 * @date 2017/6/16
 */
class Icon extends Component {

  static propTypes = {
    ...WebView.propTypes,
    fill: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
    height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
    viewBox: PropTypes.string,
    width: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
  };

  static defaultProps = {
    height: 24,
    width: 24
  };

  render() {
    const {width, height, viewBox, fill, style, ...props} = this.props;

    let js = `
    var svg = document.getElementsByTagName('svg')[0];
    var paths = document.getElementsByTagName('path');
    `;
    if (width) {
      js = `${js} svg.setAttribute('width','${width}px');`;
    }
    if (height) {
      js = `${js} svg.setAttribute('height','${height}px');`;
    }
    if (viewBox) {
      js = `${js} svg.setAttribute('height','${viewBox}');`;
    }
    if (fill) {
      const onlyOneColor = !Array.isArray(fill);
      js = `${js}
      var colors = ${JSON.stringify(fill)};
      for(var i = 0; i < paths.length; i++){
        var path = paths[i];
        var color = ${onlyOneColor} ? '${fill}' : colors[i] || '#000';
        path && path.setAttribute('fill',  color);
      }
      `;

    }

    const size = {
      width,
      height
    };
    return (
      <View style={[size, style]}>
        <WebView {...props}
                 domStorageEnabled
                 injectedJavaScript={js}
                 javaScriptEnabled
                 scrollEnabled={false}
                 style={[size, {backgroundColor: 'transparent'}]}
        />
      </View>
    );
  }

}

export default Icon;
<|endoftext|>


<commit_before>import MeshBVHNode from './MeshBVHNode.js';
import { CENTER } from './Constants.js';
import { buildTree } from './buildFunctions.js';
import {
	raycast,
	raycastFirst,
	shapecast,
	intersectsGeometry,
	intersectsBox,
	intersectsSphere,
	closestPointToPoint,
	closestPointToGeometry,
} from './castFunctions.js';

// boundingData  		: 6 float32
// left / offset 		: 1 uint32
// right / count 		: 1 uint32
// splitAxis / isLeaf 	: 1 uint32
const BYTES_PER_NODE = 6 * 4 + 4 + 4 + 4;
const IS_LEAFNODE_FLAG = 0xFFFFFFFF;
const SKIP_GENERATION = Symbol( 'skip tree generation' );

export default class MeshBVH {

	static serialize( bvh, geometry, copyIndexBuffer = true ) {

		function finishTree( node ) {

			if ( node.continueGeneration ) {

				node.continueGeneration();

			}

			if ( ! node.count ) {

				finishTree( node.left );
				finishTree( node.right );

			}

		}

		function countNodes( node ) {

			if ( node.count ) {

				return 1;

			} else {

				return 1 + countNodes( node.left ) + countNodes( node.right );

			}

		}

		function populateBuffer( arrayOffset, float32Array, uint32Array, node ) {

			const isLeaf = ! ! node.count;
			const boundingData = node.boundingData;
			for ( let i = 0; i < 6; i ++ ) {

				float32Array[ arrayOffset + i ] = boundingData[ i ];

			}

			if ( isLeaf ) {

				const offset = node.offset;
				const count = node.count;
				uint32Array[ arrayOffset + 6 ] = offset;
				uint32Array[ arrayOffset + 7 ] = count;
				uint32Array[ arrayOffset + 8 ] = IS_LEAFNODE_FLAG;
				return arrayOffset + BYTES_PER_NODE / 4;

			} else {

				const left = node.left;
				const right = node.right;
				const splitAxis = node.splitAxis;

				let nextUnusedPointer;

				uint32Array[ arrayOffset + 6 ] = arrayOffset + BYTES_PER_NODE / 4;
				nextUnusedPointer = populateBuffer( arrayOffset + BYTES_PER_NODE / 4, float32Array, uint32Array, left );

				uint32Array[ arrayOffset + 7 ] = nextUnusedPointer;
				nextUnusedPointer = populateBuffer( nextUnusedPointer, float32Array, uint32Array, right );

				uint32Array[ arrayOffset + 8 ] = splitAxis;
				return nextUnusedPointer;

			}

		}

		const roots = bvh._roots;
		const rootData = [];
		for ( let i = 0; i < roots.length; i ++ ) {

			const root = roots[ i ];
			finishTree( root );
			let nodeCount = countNodes( root );

			const buffer = new ArrayBuffer( BYTES_PER_NODE * nodeCount );
			const float32Array = new Float32Array( buffer );
			const uint32Array = new Uint32Array( buffer );
			rootData.push( buffer );
			populateBuffer( 0, float32Array, uint32Array, root );

		}

		const indexAttribute = geometry.getIndex();
		const result = {
			roots: rootData,
			index: copyIndexBuffer ? indexAttribute.array.slice() : indexAttribute.array,
		};

		return result;

	}

	static deserialize( data, geometry, setIndex = true ) {

		function setData( arrayOffset, float32Array, uint32Array, node ) {

			const boundingData = new Float32Array( 6 );
			for ( let i = 0; i < 6; i ++ ) {

				boundingData[ i ] = float32Array[ arrayOffset + i ];

			}
			node.boundingData = boundingData;

			const isLeaf = uint32Array[ arrayOffset + 8 ] === IS_LEAFNODE_FLAG;
			if ( isLeaf ) {

				node.offset = uint32Array[ arrayOffset + 6 ];
				node.count = uint32Array[ arrayOffset + 7 ];

			} else {

				const left = new MeshBVHNode();
				const right = new MeshBVHNode();
				const leftOffset = uint32Array[ arrayOffset + 6 ];
				const rightOffset = uint32Array[ arrayOffset + 7 ];

				setData( leftOffset, float32Array, uint32Array, left );
				setData( rightOffset, float32Array, uint32Array, right );

				node.left = left;
				node.right = right;
				node.splitAxis = uint32Array[ arrayOffset + 8 ];

			}

		}

		const { index, roots } = data;
		const bvh = new MeshBVH( geometry, { [ SKIP_GENERATION ]: true } );
		bvh._roots = roots.map( buffer => {

			const float32Array = new Float32Array( buffer );
			const uint32Array = new Uint32Array( buffer );

			const root = new MeshBVHNode();
			setData( 0, float32Array, uint32Array, root );
			return root;

		} );

		if ( setIndex ) {

			const indexAttribute = geometry.getIndex();
			if ( indexAttribute.array !== index ) {

				indexAttribute.array.set( index );
				indexAttribute.needsUpdate = true;

			}

		}

		return bvh;

	}

	constructor( geo, options = {} ) {

		if ( ! geo.isBufferGeometry ) {

			throw new Error( 'MeshBVH: Only BufferGeometries are supported.' );

		} else if ( geo.attributes.position.isInterleavedBufferAttribute ) {

			throw new Error( 'MeshBVH: InterleavedBufferAttribute is not supported for the position attribute.' );

		} else if ( geo.index && geo.index.isInterleavedBufferAttribute ) {

			throw new Error( 'MeshBVH: InterleavedBufferAttribute is not supported for the index attribute.' );

		}

		// default options
		options = Object.assign( {

			strategy: CENTER,
			maxDepth: 40,
			maxLeafTris: 10,
			verbose: true,
			lazyGeneration: false,
			[ SKIP_GENERATION ]: false

		}, options );
		options.strategy = Math.max( 0, Math.min( 2, options.strategy ) );

		if ( options[ SKIP_GENERATION ] ) {

			this._roots = null;

		} else {

			this._roots = buildTree( geo, options );

		}

	}

	/* Public Functions */
	raycast( mesh, raycaster, ray, intersects ) {

		for ( const root of this._roots ) {

			raycast( root, mesh, raycaster, ray, intersects );

		}

	}

	raycastFirst( mesh, raycaster, ray ) {

		let closestResult = null;
		for ( const root of this._roots ) {

			const result = raycastFirst( root, mesh, raycaster, ray );
			if ( result != null && ( closestResult == null || result.distance < closestResult.distance ) ) {

				closestResult = result;

			}

		}

		return closestResult;

	}

	intersectsGeometry( mesh, geometry, geomToMesh ) {

		for ( const root of this._roots ) {

			if ( intersectsGeometry( root, mesh, geometry, geomToMesh ) ) return true;

		}

		return false;

	}

	shapecast( mesh, intersectsBoundsFunc, intersectsTriangleFunc = null, orderNodesFunc = null ) {

		for ( const root of this._roots ) {

			if ( shapecast( root, mesh, intersectsBoundsFunc, intersectsTriangleFunc, orderNodesFunc ) ) return true;

		}

		return false;

	}

	intersectsBox( mesh, box, boxToMesh ) {

		for ( const root of this._roots ) {

			if ( intersectsBox( root, mesh, box, boxToMesh ) ) return true;

		}

		return false;

	}

	intersectsSphere( mesh, sphere ) {

		for ( const root of this._roots ) {

			if ( intersectsSphere( root, mesh, sphere ) ) return true;

		}

		return false;

	}

	closestPointToGeometry( mesh, geom, matrix, target1, target2, minThreshold, maxThreshold ) {

		let closestDistance = Infinity;
		for ( const root of this._roots ) {

			const dist = closestPointToGeometry( root, mesh, geom, matrix, target1, target2, minThreshold, maxThreshold );
			if ( dist < closestDistance ) closestDistance = dist;
			if ( dist < minThreshold ) return dist;

		}

		return closestDistance;

	}

	distanceToGeometry( mesh, geom, matrix, minThreshold, maxThreshold ) {

		return this.closestPointToGeometry( mesh, geom, matrix, null, null, minThreshold, maxThreshold );

	}

	closestPointToPoint( mesh, point, target, minThreshold, maxThreshold ) {

		let closestDistance = Infinity;
		for ( const root of this._roots ) {

			const dist = closestPointToPoint( root, mesh, point, target, minThreshold, maxThreshold );
			if ( dist < closestDistance ) closestDistance = dist;
			if ( dist < minThreshold ) return dist;

		}

		return closestDistance;

	}

	distanceToPoint( mesh, point, minThreshold, maxThreshold ) {

		return this.closestPointToPoint( mesh, point, null, minThreshold, maxThreshold );

	}

}
<commit_msg>pack the serialize function more<commit_after>import MeshBVHNode from './MeshBVHNode.js';
import { CENTER } from './Constants.js';
import { buildTree } from './buildFunctions.js';
import {
	raycast,
	raycastFirst,
	shapecast,
	intersectsGeometry,
	intersectsBox,
	intersectsSphere,
	closestPointToPoint,
	closestPointToGeometry,
} from './castFunctions.js';

// boundingData  				: 6 float32
// right / offset 				: 1 uint32
// splitAxis / isLeaf + count 	: 1 uint32 / 2 uint16
const BYTES_PER_NODE = 6 * 4 + 4 + 4;
const IS_LEAFNODE_FLAG = 0xFFFF;
const SKIP_GENERATION = Symbol( 'skip tree generation' );

export default class MeshBVH {

	static serialize( bvh, geometry, copyIndexBuffer = true ) {

		function finishTree( node ) {

			if ( node.continueGeneration ) {

				node.continueGeneration();

			}

			if ( ! node.count ) {

				finishTree( node.left );
				finishTree( node.right );

			}

		}

		function countNodes( node ) {

			if ( node.count ) {

				return 1;

			} else {

				return 1 + countNodes( node.left ) + countNodes( node.right );

			}

		}

		function populateBuffer( byteOffset, node ) {

			const stride4Offset = byteOffset / 4;
			const stride2Offset = byteOffset / 2;
			const isLeaf = ! ! node.count;
			const boundingData = node.boundingData;
			for ( let i = 0; i < 6; i ++ ) {

				float32Array[ stride4Offset + i ] = boundingData[ i ];

			}

			if ( isLeaf ) {

				const offset = node.offset;
				const count = node.count;
				uint32Array[ stride4Offset + 6 ] = offset;
				uint16Array[ stride2Offset + 14 ] = count;
				uint16Array[ stride2Offset + 15 ] = IS_LEAFNODE_FLAG;
				return byteOffset + BYTES_PER_NODE;

			} else {

				const left = node.left;
				const right = node.right;
				const splitAxis = node.splitAxis;

				let nextUnusedPointer;

				// uint32Array[ stride4Offset + 6 ] = stride4Offset + BYTES_PER_NODE / 4;
				nextUnusedPointer = populateBuffer( byteOffset + BYTES_PER_NODE, left );

				uint32Array[ stride4Offset + 6 ] = nextUnusedPointer / 4;
				nextUnusedPointer = populateBuffer( nextUnusedPointer, right );

				uint32Array[ stride4Offset + 7 ] = splitAxis;
				return nextUnusedPointer;

			}

		}

		let float32Array;
		let uint32Array;
		let uint16Array;

		const roots = bvh._roots;
		const rootData = [];
		for ( let i = 0; i < roots.length; i ++ ) {

			const root = roots[ i ];
			finishTree( root );
			let nodeCount = countNodes( root );

			const buffer = new ArrayBuffer( BYTES_PER_NODE * nodeCount );
			float32Array = new Float32Array( buffer );
			uint32Array = new Uint32Array( buffer );
			uint16Array = new Uint16Array( buffer );
			populateBuffer( 0, root );
			rootData.push( buffer );

		}

		const indexAttribute = geometry.getIndex();
		const result = {
			roots: rootData,
			index: copyIndexBuffer ? indexAttribute.array.slice() : indexAttribute.array,
		};

		return result;

	}

	static deserialize( data, geometry, setIndex = true ) {

		function setData( byteOffset, node ) {

			const stride4Offset = byteOffset / 4;
			const stride2Offset = byteOffset / 2;
			const boundingData = new Float32Array( 6 );
			for ( let i = 0; i < 6; i ++ ) {

				boundingData[ i ] = float32Array[ stride4Offset + i ];

			}
			node.boundingData = boundingData;

			const isLeaf = uint16Array[ stride2Offset + 15 ] === IS_LEAFNODE_FLAG;
			if ( isLeaf ) {

				node.offset = uint32Array[ stride4Offset + 6 ];
				node.count = uint16Array[ stride2Offset + 14 ];

			} else {

				const left = new MeshBVHNode();
				const right = new MeshBVHNode();
				const leftOffset = stride4Offset + BYTES_PER_NODE / 4;
				const rightOffset = uint32Array[ stride4Offset + 6 ];

				setData( leftOffset * 4, left );
				setData( rightOffset * 4, right );

				node.left = left;
				node.right = right;
				node.splitAxis = uint32Array[ stride4Offset + 7 ];

			}

		}

		let float32Array;
		let uint32Array;
		let uint16Array;

		const { index, roots } = data;
		const bvh = new MeshBVH( geometry, { [ SKIP_GENERATION ]: true } );
		bvh._roots = [];
		for ( let i = 0; i < roots.length; i ++ ) {

			const buffer = roots[ i ];
			float32Array = new Float32Array( buffer );
			uint32Array = new Uint32Array( buffer );
			uint16Array = new Uint16Array( buffer );

			const root = new MeshBVHNode();
			setData( 0, root );
			bvh._roots.push( root );

		}

		if ( setIndex ) {

			const indexAttribute = geometry.getIndex();
			if ( indexAttribute.array !== index ) {

				indexAttribute.array.set( index );
				indexAttribute.needsUpdate = true;

			}

		}

		return bvh;

	}

	constructor( geo, options = {} ) {

		if ( ! geo.isBufferGeometry ) {

			throw new Error( 'MeshBVH: Only BufferGeometries are supported.' );

		} else if ( geo.attributes.position.isInterleavedBufferAttribute ) {

			throw new Error( 'MeshBVH: InterleavedBufferAttribute is not supported for the position attribute.' );

		} else if ( geo.index && geo.index.isInterleavedBufferAttribute ) {

			throw new Error( 'MeshBVH: InterleavedBufferAttribute is not supported for the index attribute.' );

		}

		// default options
		options = Object.assign( {

			strategy: CENTER,
			maxDepth: 40,
			maxLeafTris: 10,
			verbose: true,
			lazyGeneration: false,
			[ SKIP_GENERATION ]: false

		}, options );
		options.strategy = Math.max( 0, Math.min( 2, options.strategy ) );

		if ( options[ SKIP_GENERATION ] ) {

			this._roots = null;

		} else {

			this._roots = buildTree( geo, options );

		}

	}

	/* Public Functions */
	raycast( mesh, raycaster, ray, intersects ) {

		for ( const root of this._roots ) {

			raycast( root, mesh, raycaster, ray, intersects );

		}

	}

	raycastFirst( mesh, raycaster, ray ) {

		let closestResult = null;
		for ( const root of this._roots ) {

			const result = raycastFirst( root, mesh, raycaster, ray );
			if ( result != null && ( closestResult == null || result.distance < closestResult.distance ) ) {

				closestResult = result;

			}

		}

		return closestResult;

	}

	intersectsGeometry( mesh, geometry, geomToMesh ) {

		for ( const root of this._roots ) {

			if ( intersectsGeometry( root, mesh, geometry, geomToMesh ) ) return true;

		}

		return false;

	}

	shapecast( mesh, intersectsBoundsFunc, intersectsTriangleFunc = null, orderNodesFunc = null ) {

		for ( const root of this._roots ) {

			if ( shapecast( root, mesh, intersectsBoundsFunc, intersectsTriangleFunc, orderNodesFunc ) ) return true;

		}

		return false;

	}

	intersectsBox( mesh, box, boxToMesh ) {

		for ( const root of this._roots ) {

			if ( intersectsBox( root, mesh, box, boxToMesh ) ) return true;

		}

		return false;

	}

	intersectsSphere( mesh, sphere ) {

		for ( const root of this._roots ) {

			if ( intersectsSphere( root, mesh, sphere ) ) return true;

		}

		return false;

	}

	closestPointToGeometry( mesh, geom, matrix, target1, target2, minThreshold, maxThreshold ) {

		let closestDistance = Infinity;
		for ( const root of this._roots ) {

			const dist = closestPointToGeometry( root, mesh, geom, matrix, target1, target2, minThreshold, maxThreshold );
			if ( dist < closestDistance ) closestDistance = dist;
			if ( dist < minThreshold ) return dist;

		}

		return closestDistance;

	}

	distanceToGeometry( mesh, geom, matrix, minThreshold, maxThreshold ) {

		return this.closestPointToGeometry( mesh, geom, matrix, null, null, minThreshold, maxThreshold );

	}

	closestPointToPoint( mesh, point, target, minThreshold, maxThreshold ) {

		let closestDistance = Infinity;
		for ( const root of this._roots ) {

			const dist = closestPointToPoint( root, mesh, point, target, minThreshold, maxThreshold );
			if ( dist < closestDistance ) closestDistance = dist;
			if ( dist < minThreshold ) return dist;

		}

		return closestDistance;

	}

	distanceToPoint( mesh, point, minThreshold, maxThreshold ) {

		return this.closestPointToPoint( mesh, point, null, minThreshold, maxThreshold );

	}

}
<|endoftext|>