<commit_before>// Initializes (start) HTTP and RPC server listeners.
// Used as last step in `cli/server.js`
//

'use strict';

// stdlib
var fs    = require('fs');
var path  = require('path');
var http  = require('http');
var https = require('https');
var url   = require('url');
var querystring = require('querystring');

// 3rd-party
var _        = require('lodash');
var async    = require('async');
var parseURL = require('pointer').parseURL;

// internal
var env = require('../env');

////////////////////////////////////////////////////////////////////////////////

// Parse binding config and returns a parsed map:
//
//  {
//    '127.0.0.1:3000': {
//      address: '127.0.0.1',
//      port:    3000,
//      ssl:     null,
//      hosts:  [ 'localhost', ... ]
//    },
//    // ...
//  }
//
function extractBindings(config, mainApp, logger) {
  var result = {};

  // Collect non-SSL bindings.
  _.forEach(config, function (options, apiPath) {
    if ('_' === apiPath) {
      return; // skip special case keyword
    }

    if (!result[options.listen]) {
      result[options.listen] = {
        address:   options.listen.address
      , port:      options.listen.port
      , forwarded: options.forwarded
      , hosts:     []
      , ssl:       null
      };
    }
  });

  // Collect SSL bindings.
  _(config).select('ssl').forEach(function (options, apiPath) {
    if ('_' === apiPath) {
      return; // skip special case keyword
    }

    if (!result[options.ssl.listen]) {
      result[options.ssl.listen] = {
        address:   options.ssl.listen.address
      , port:      options.ssl.listen.port
      , forwarded: options.ssl.forwarded
      , hosts:     []
      , ssl:       _.omit(options.ssl, 'listen')
      };

      // Read files.
      _(options.ssl).pick('key', 'cert').map(function (filename, key) {
        filename = path.resolve(mainApp.root, filename);

        try {
          result[options.ssl.listen].ssl[key] = fs.readFileSync(filename, 'utf8');
        } catch (err) {
          logger.error('Can\'t read %s-file from SSL config (%s): %s', key, filename, err);
        }
      });
    }
  });

  // Fill-in host lists that should be served by <address:port>
  _.forEach(config, function (options, apiPath) {
    if ('_' === apiPath) {
      return; // skip special case keyword
    }

    var host = parseURL(options.mount).host || '*';

    // For non-SSL.
    if (!_.contains(result[options.listen].hosts, host)) {
      result[options.listen].hosts.push(host);
    }

    // For SSL.
    if (options.ssl && !_.contains(result[options.ssl.listen].hosts, host)) {
      result[options.ssl.listen].hosts.push(host);
    }
  });

  return result;
}

// Returns protocol name of `req` request object.
// If `isForwarded` is true, it will respect X-Forwarded-* headers used when
// Nodeca is behind a proxy.
//
function getRequestProtocol(req, isForwarded) {
  if (isForwarded) {
    if ('on' === req.headers['x-forwarded-ssl']) {
      return 'https';
    }

    if (req.headers['x-forwarded-scheme']) {
      return req.headers['x-forwarded-scheme'];
    }

    if (req.headers['x-forwarded-proto']) {
      return req.headers['x-forwarded-proto'].split(/\s*,\s*/)[0];
    }
  }

  return req.connection.encrypted ? 'https' : 'http';
}

// Returns remote IP address of `req` request object.
// If `isForwarded` is true, it will respect X-Forwarded-For header used when
// Nodeca is behind a proxy.
//
function getRequestRemoteAddress(req, isForwarded) {
  if (isForwarded && req.headers['x-forwarded-for']) {
    // X-Forwarded-For can contain chain of proxies.
    return req.headers['x-forwarded-for'].split(/\s*,\s*/)[0];
  }

  return req.connection.remoteAddress;
}

// Takes a params hash (can be nested) and tries to parse each string-value as
// number or boolean. Returns a new hash with parsed values.
//
/*function castParamTypes(inputValue) {
  var parsedValue;

  if (_.isArray(inputValue)) {
    return _.map(inputValue, castParamTypes);

  } else if (_.isObject(inputValue)) {
    parsedValue = {};

    _.forEach(inputValue, function (value, key) {
      parsedValue[key] = castParamTypes(value);
    });

    return parsedValue;

  } else if ('true' === inputValue) {
    return true;

  } else if ('false' === inputValue) {
    return false;

  } else if (/^[0-9\.\-]+$/.test(inputValue)) {
    parsedValue = Number(inputValue);
    return String(parsedValue) === inputValue ? parsedValue : inputValue;

  } else {
    return inputValue;
  }
}*/

// Parses URL query string and resolves numbers and booleans.
//
function parseQueryString(inputQuery) {
  if (!inputQuery) {
    return {};
  }

  //return castParamTypes(querystring.parse(String(inputQuery)));
  return querystring.parse(String(inputQuery));
}

// MODULE EXPORTS //////////////////////////////////////////////////////////////

// starts http(s) server(s) and attach application listener to it
//
module.exports = function (N) {

  N.wire.on('init:server', function models_init(N, cb) {
    var bindings = extractBindings(N.config.bind, N.runtime.mainApp, N.logger),
        handle_invalid_host = N.config.bind._;

    //
    // set 'host not found' handler if it was not set
    //

    if (!_.isFunction(handle_invalid_host)) {
      handle_invalid_host = function (req, res) {
        res.writeHead(N.io.NOT_FOUND, { 'Content-Type': 'text/plain' });
        res.end('Invalid host ' + req.headers.host);
      };
    }

    //
    // bind listeners
    //

    async.eachSeries(_.keys(bindings), function (point, next) {
      var options = bindings[point],
          address = options.address,
          port    = options.port,
          server,
          host_is_valid;

      try {
        server = options.ssl ? https.createServer(options.ssl) : http.createServer();
      } catch (err) {
        next(err); // Possibly bad SSL options.
        return;
      }

      //
      // Create host validation function
      //

      if (_.contains(options.hosts, '*')) {
        // if binding address:port has *any host* mount point,
        // skip host validation
        host_is_valid = function () {
          return true;
        };
      } else {
        host_is_valid = function (host) {
          return _.contains(options.hosts, host);
        };
      }

      //
      // attach event handlers
      //

      server.on('error', function handle_startup_error(err) {
        var err_prefix = 'Can\'t bind to <' + address + '> with port <' + port + '>: ';

        if ('EADDRINUSE' === err.code) {
          next(err_prefix + 'Address in use...');
          return;
        }

        if ('EADDRNOTAVAIL' === err.code) {
          // system has no such ip address
          next(err_prefix + 'Address is not available...');
          return;
        }

        if ('ENOENT' === err.code) {
          // failed resolve addressname to ip address
          next(err_prefix + 'Failed to resolve IP address...');
          return;
        }

        // unexpected / unknown error
        next(err_prefix + err);
      });

      server.on('connection', function handle_connection(socket) {
        socket.setTimeout(15 * 1000);
        socket.setNoDelay();
      });

      server.on('request', function handle_request(req, res) {
        var protocol, isEncrypted, httpMethod, parsedUrl, fullUrl, type, routeMatch;

        // hard limit max request size to 20 mb
        // user should not be able to upload temporary files of unlimited size
        var received = 0;
        var maxRequestSize = 20 * 1024 * 1024;
        req.on('data', function (chunk) {
          received += chunk.length;
          if (received > maxRequestSize) {
            req.destroy();
          }
        });

        if (_.isEmpty(req.headers.host)) {
          // Host header is not set or empty.
          res.writeHead(N.io.BAD_REQUEST, { 'Content-Type': 'text/plain' });
          res.end('[' + N.io.BAD_REQUEST + '] Bad Request: missed \'Host\' header');
          return;
        }

        if (!host_is_valid(req.headers.host)) {
          // Host is unknown.
          handle_invalid_host(req, res);
          return;
        }

        // Frontend server may handle HTTPS request from the client
        // but connect with us via plain HTTP. So, detect protocol.
        protocol     = getRequestProtocol(req, options.forwarded);
        isEncrypted  = 'https' === protocol;

        httpMethod   = req.method.toLowerCase();
        parsedUrl    = url.parse(req.url);
        fullUrl      = protocol + '://' + req.headers.host + parsedUrl.pathname;
        req.query    = parseQueryString(parsedUrl.query);

        //
        // Choose a responder to use.
        //

        if ('/io/rpc' === parsedUrl.pathname) {
          type = 'rpc';
        } else {
          type = 'http';

          routeMatch = _.find(N.runtime.router.matchAll(fullUrl), function (match) {
            return _.has(match.meta.methods, httpMethod);
          });

          if (routeMatch) {
            type = routeMatch.meta.responder;
            //routeMatch.params = castParamTypes(routeMatch.params);
            routeMatch.params = routeMatch.params || {};
          }
        }

        //
        // Try to process request: create enviroment & run responder chain
        // Responders are totally pluggable, via hooks
        //

        var _env = env(N, {
          req:           req
        , res:           res
        , type:          type
        , isEncrypted:   isEncrypted
        , matched:       routeMatch
        , remoteAddress: getRequestRemoteAddress(req, options.forwarded)
        });

        N.wire.emit('responder:' + type, _env);
      });

      server.on('listening', function handle_listening() {
        // remove error and listening handlers,
        // once we successfully bounded on address:port
        server.removeAllListeners('error');
        server.removeAllListeners('listening');

        //
        // Notify that we started listening
        //

        N.logger.info('Listening on %s:%s %s',
                      options.address,
                      options.port,
                      options.ssl ? 'SSL' : 'NON-SSL');

        //
        // Successfully binded, process to the next mount point
        //

        next();
      });

      //
      // try to bind to the port
      //

      server.listen(port, address);
    },

    function (err) {
      if (err) {
        cb(err);
        return;
      }

      N.logger.info('Services init done');
      cb();
    });
  });
};
<commit_msg>Revert "Added global request size limit"<commit_after>// Initializes (start) HTTP and RPC server listeners.
// Used as last step in `cli/server.js`
//

'use strict';

// stdlib
var fs    = require('fs');
var path  = require('path');
var http  = require('http');
var https = require('https');
var url   = require('url');
var querystring = require('querystring');

// 3rd-party
var _        = require('lodash');
var async    = require('async');
var parseURL = require('pointer').parseURL;

// internal
var env = require('../env');

////////////////////////////////////////////////////////////////////////////////

// Parse binding config and returns a parsed map:
//
//  {
//    '127.0.0.1:3000': {
//      address: '127.0.0.1',
//      port:    3000,
//      ssl:     null,
//      hosts:  [ 'localhost', ... ]
//    },
//    // ...
//  }
//
function extractBindings(config, mainApp, logger) {
  var result = {};

  // Collect non-SSL bindings.
  _.forEach(config, function (options, apiPath) {
    if ('_' === apiPath) {
      return; // skip special case keyword
    }

    if (!result[options.listen]) {
      result[options.listen] = {
        address:   options.listen.address
      , port:      options.listen.port
      , forwarded: options.forwarded
      , hosts:     []
      , ssl:       null
      };
    }
  });

  // Collect SSL bindings.
  _(config).select('ssl').forEach(function (options, apiPath) {
    if ('_' === apiPath) {
      return; // skip special case keyword
    }

    if (!result[options.ssl.listen]) {
      result[options.ssl.listen] = {
        address:   options.ssl.listen.address
      , port:      options.ssl.listen.port
      , forwarded: options.ssl.forwarded
      , hosts:     []
      , ssl:       _.omit(options.ssl, 'listen')
      };

      // Read files.
      _(options.ssl).pick('key', 'cert').map(function (filename, key) {
        filename = path.resolve(mainApp.root, filename);

        try {
          result[options.ssl.listen].ssl[key] = fs.readFileSync(filename, 'utf8');
        } catch (err) {
          logger.error('Can\'t read %s-file from SSL config (%s): %s', key, filename, err);
        }
      });
    }
  });

  // Fill-in host lists that should be served by <address:port>
  _.forEach(config, function (options, apiPath) {
    if ('_' === apiPath) {
      return; // skip special case keyword
    }

    var host = parseURL(options.mount).host || '*';

    // For non-SSL.
    if (!_.contains(result[options.listen].hosts, host)) {
      result[options.listen].hosts.push(host);
    }

    // For SSL.
    if (options.ssl && !_.contains(result[options.ssl.listen].hosts, host)) {
      result[options.ssl.listen].hosts.push(host);
    }
  });

  return result;
}

// Returns protocol name of `req` request object.
// If `isForwarded` is true, it will respect X-Forwarded-* headers used when
// Nodeca is behind a proxy.
//
function getRequestProtocol(req, isForwarded) {
  if (isForwarded) {
    if ('on' === req.headers['x-forwarded-ssl']) {
      return 'https';
    }

    if (req.headers['x-forwarded-scheme']) {
      return req.headers['x-forwarded-scheme'];
    }

    if (req.headers['x-forwarded-proto']) {
      return req.headers['x-forwarded-proto'].split(/\s*,\s*/)[0];
    }
  }

  return req.connection.encrypted ? 'https' : 'http';
}

// Returns remote IP address of `req` request object.
// If `isForwarded` is true, it will respect X-Forwarded-For header used when
// Nodeca is behind a proxy.
//
function getRequestRemoteAddress(req, isForwarded) {
  if (isForwarded && req.headers['x-forwarded-for']) {
    // X-Forwarded-For can contain chain of proxies.
    return req.headers['x-forwarded-for'].split(/\s*,\s*/)[0];
  }

  return req.connection.remoteAddress;
}

// Takes a params hash (can be nested) and tries to parse each string-value as
// number or boolean. Returns a new hash with parsed values.
//
/*function castParamTypes(inputValue) {
  var parsedValue;

  if (_.isArray(inputValue)) {
    return _.map(inputValue, castParamTypes);

  } else if (_.isObject(inputValue)) {
    parsedValue = {};

    _.forEach(inputValue, function (value, key) {
      parsedValue[key] = castParamTypes(value);
    });

    return parsedValue;

  } else if ('true' === inputValue) {
    return true;

  } else if ('false' === inputValue) {
    return false;

  } else if (/^[0-9\.\-]+$/.test(inputValue)) {
    parsedValue = Number(inputValue);
    return String(parsedValue) === inputValue ? parsedValue : inputValue;

  } else {
    return inputValue;
  }
}*/

// Parses URL query string and resolves numbers and booleans.
//
function parseQueryString(inputQuery) {
  if (!inputQuery) {
    return {};
  }

  //return castParamTypes(querystring.parse(String(inputQuery)));
  return querystring.parse(String(inputQuery));
}

// MODULE EXPORTS //////////////////////////////////////////////////////////////

// starts http(s) server(s) and attach application listener to it
//
module.exports = function (N) {

  N.wire.on('init:server', function models_init(N, cb) {
    var bindings = extractBindings(N.config.bind, N.runtime.mainApp, N.logger),
        handle_invalid_host = N.config.bind._;

    //
    // set 'host not found' handler if it was not set
    //

    if (!_.isFunction(handle_invalid_host)) {
      handle_invalid_host = function (req, res) {
        res.writeHead(N.io.NOT_FOUND, { 'Content-Type': 'text/plain' });
        res.end('Invalid host ' + req.headers.host);
      };
    }

    //
    // bind listeners
    //

    async.eachSeries(_.keys(bindings), function (point, next) {
      var options = bindings[point],
          address = options.address,
          port    = options.port,
          server,
          host_is_valid;

      try {
        server = options.ssl ? https.createServer(options.ssl) : http.createServer();
      } catch (err) {
        next(err); // Possibly bad SSL options.
        return;
      }

      //
      // Create host validation function
      //

      if (_.contains(options.hosts, '*')) {
        // if binding address:port has *any host* mount point,
        // skip host validation
        host_is_valid = function () {
          return true;
        };
      } else {
        host_is_valid = function (host) {
          return _.contains(options.hosts, host);
        };
      }

      //
      // attach event handlers
      //

      server.on('error', function handle_startup_error(err) {
        var err_prefix = 'Can\'t bind to <' + address + '> with port <' + port + '>: ';

        if ('EADDRINUSE' === err.code) {
          next(err_prefix + 'Address in use...');
          return;
        }

        if ('EADDRNOTAVAIL' === err.code) {
          // system has no such ip address
          next(err_prefix + 'Address is not available...');
          return;
        }

        if ('ENOENT' === err.code) {
          // failed resolve addressname to ip address
          next(err_prefix + 'Failed to resolve IP address...');
          return;
        }

        // unexpected / unknown error
        next(err_prefix + err);
      });

      server.on('connection', function handle_connection(socket) {
        socket.setTimeout(15 * 1000);
        socket.setNoDelay();
      });

      server.on('request', function handle_request(req, res) {
        var protocol, isEncrypted, httpMethod, parsedUrl, fullUrl, type, routeMatch;

        if (_.isEmpty(req.headers.host)) {
          // Host header is not set or empty.
          res.writeHead(N.io.BAD_REQUEST, { 'Content-Type': 'text/plain' });
          res.end('[' + N.io.BAD_REQUEST + '] Bad Request: missed \'Host\' header');
          return;
        }

        if (!host_is_valid(req.headers.host)) {
          // Host is unknown.
          handle_invalid_host(req, res);
          return;
        }

        // Frontend server may handle HTTPS request from the client
        // but connect with us via plain HTTP. So, detect protocol.
        protocol     = getRequestProtocol(req, options.forwarded);
        isEncrypted  = 'https' === protocol;

        httpMethod   = req.method.toLowerCase();
        parsedUrl    = url.parse(req.url);
        fullUrl      = protocol + '://' + req.headers.host + parsedUrl.pathname;
        req.query    = parseQueryString(parsedUrl.query);

        //
        // Choose a responder to use.
        //

        if ('/io/rpc' === parsedUrl.pathname) {
          type = 'rpc';
        } else {
          type = 'http';

          routeMatch = _.find(N.runtime.router.matchAll(fullUrl), function (match) {
            return _.has(match.meta.methods, httpMethod);
          });

          if (routeMatch) {
            type = routeMatch.meta.responder;
            //routeMatch.params = castParamTypes(routeMatch.params);
            routeMatch.params = routeMatch.params || {};
          }
        }

        //
        // Try to process request: create enviroment & run responder chain
        // Responders are totally pluggable, via hooks
        //

        var _env = env(N, {
          req:           req
        , res:           res
        , type:          type
        , isEncrypted:   isEncrypted
        , matched:       routeMatch
        , remoteAddress: getRequestRemoteAddress(req, options.forwarded)
        });

        N.wire.emit('responder:' + type, _env);
      });

      server.on('listening', function handle_listening() {
        // remove error and listening handlers,
        // once we successfully bounded on address:port
        server.removeAllListeners('error');
        server.removeAllListeners('listening');

        //
        // Notify that we started listening
        //

        N.logger.info('Listening on %s:%s %s',
                      options.address,
                      options.port,
                      options.ssl ? 'SSL' : 'NON-SSL');

        //
        // Successfully binded, process to the next mount point
        //

        next();
      });

      //
      // try to bind to the port
      //

      server.listen(port, address);
    },

    function (err) {
      if (err) {
        cb(err);
        return;
      }

      N.logger.info('Services init done');
      cb();
    });
  });
};
<|endoftext|>


<commit_before>/*global module:false*/

/*** GRUNT 3rd PARTY PLUGINS: DEPENDENCIES: ***/
/*
    - grunt-compass (https://npmjs.org/package/grunt-compass)
    - grunt-contrib-watch (https://github.com/gruntjs/grunt-contrib-watch)
    - grunt-contrib-copy (https://npmjs.org/package/grunt-contrib-copy)
    - grunt-reload (https://npmjs.org/package/grunt-reload)
*/

module.exports = function(grunt) {

// Project configuration.
grunt.initConfig({
    meta: {
        version: '0.1.0',
        banner: '/*! PROJECT_NAME - v<%= meta.version %> - ' +
            '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
            '* http://PROJECT_WEBSITE/\n' +
            '* Copyright (c) <%= grunt.template.today("yyyy") %> ' +
            '  YOUR_NAME; Licensed MIT */'
    },
    qunit: {
        files: ['test/**/*.html']
    },
    watch: {
        compass: {
            files: ['dev/_ui/css/**/*.scss'],
            tasks: ['compass:dev']
        },
        reload: {
            files: [
              'dev/**/*.html',
              'dev/_ui/css/**/*.css',
              'dev/_ui/js/**/*.js',
              'dev/_ui/img/**/*'
            ],
            tasks: 'copy:dev'
        }
    },
    // reload server currently does not work, watch https://github.com/gruntjs/grunt-contrib-livereload for possible future plugin
    reload: {
        port: 6001,
        proxy: {
            host: 'localhost',
            port: 8000 // should match server.port config
        }
    },
    server: {
        port: 8000,
        base: 'temp/'
    },
    lint: {
        files: grunt.file.expandFiles([
            'grunt.js',
            'dev/_ui/js/**/*.js',
            'test/**/*.js'
        ]).filter(function(f){ // filter out 3rd party js in the js/lib folder, since lint quality can't be controlled
            return !/dev\/_ui\/js\/lib\//.test(f);
        })
    },
    jshint: {
        options: {
            curly: true,
            eqeqeq: true,
            immed: true,
            latedef: true,
            newcap: true,
            noarg: true,
            sub: true,
            undef: true,
            boss: true,
            eqnull: true,
            browser: true
        },
        globals: {}
    },
    compass: {
        dev: {
            src: 'dev/_ui/css',
            dest: 'temp/_ui/css',
            linecomments: true,
            debugsass: true,
            relativeassets: true
        },
        dist: {
            src: 'dev/_ui/css',
            dest: 'dist/_ui/css',
            linecomments: false,
            outputstyle: 'compressed',
            debugsass: false,
            relativeassets: true
        }
    },

    // build config
    copy: {
        dev: {
            files: {
                'temp/': grunt.file.expandFiles([
                    'dev/**'
                ]).filter(function(f){// copy over everything except scss, which will be processed/copied with compass
                    return !/.+(\.scss)$/gm.test(f);
                })
            }
        },
        dist: {
            files: {
                // TODO: switch to grunt.file.expand in grunt v0.4.0a, which will support exclusions in minimatch
                'dist/': grunt.file.expandFiles([
                    'dev/**'
                ]).filter(function(f){// copy over everything except css and js, which will be processed/copied with compass and concat/min functions
                    return !/dev\/_ui\/css|dev\/_ui\/js/.test(f);
                })
            }
        }
    },
    concat: {
        dist: {
            src: ['dev/_ui/js/**/*.js'],
            dest: 'dist/_ui/js/scripts.js'
        }
    },
    min: {
        dist: {
            src: ['dist/_ui/js/scripts.js'],
            dest: 'dist/_ui/js/scripts.min.js'
        }
    },
    // replace js and css link build blocks within specified files with their concatenated versions
    replacelinks: {
        files: [
            'dist/*.html'
        ]
    }
});

// grunt 3rd party plugins
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-compass');

// custom/ported tasks
grunt.loadTasks('tasks/');

// Default task.
grunt.registerTask('run', 'lint qunit copy:dev compass:dev server watch');
grunt.registerTask('build', 'lint qunit copy:dist compass:dist concat min replacelinks');

};
<commit_msg>add img folder, remove live reload node dependency from comments in grunt.js<commit_after>/*global module:false*/

/*** GRUNT 3rd PARTY PLUGINS: DEPENDENCIES: ***/
/*
    - grunt-compass (https://npmjs.org/package/grunt-compass)
    - grunt-contrib-watch (https://github.com/gruntjs/grunt-contrib-watch)
    - grunt-contrib-copy (https://npmjs.org/package/grunt-contrib-copy)
*/

module.exports = function(grunt) {

// Project configuration.
grunt.initConfig({
    meta: {
        version: '0.1.0',
        banner: '/*! PROJECT_NAME - v<%= meta.version %> - ' +
            '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
            '* http://PROJECT_WEBSITE/\n' +
            '* Copyright (c) <%= grunt.template.today("yyyy") %> ' +
            '  YOUR_NAME; Licensed MIT */'
    },
    qunit: {
        files: ['test/**/*.html']
    },
    watch: {
        compass: {
            files: ['dev/_ui/css/**/*.scss'],
            tasks: ['compass:dev']
        },
        reload: {
            files: [
              'dev/**/*.html',
              'dev/_ui/css/**/*.css',
              'dev/_ui/js/**/*.js',
              'dev/_ui/img/**/*'
            ],
            tasks: 'copy:dev'
        }
    },
    // reload server currently does not work, watch https://github.com/gruntjs/grunt-contrib-livereload for possible future plugin
    reload: {
        port: 6001,
        proxy: {
            host: 'localhost',
            port: 8000 // should match server.port config
        }
    },
    server: {
        port: 8000,
        base: 'temp/'
    },
    lint: {
        files: grunt.file.expandFiles([
            'grunt.js',
            'dev/_ui/js/**/*.js',
            'test/**/*.js'
        ]).filter(function(f){ // filter out 3rd party js in the js/lib folder, since lint quality can't be controlled
            return !/dev\/_ui\/js\/lib\//.test(f);
        })
    },
    jshint: {
        options: {
            curly: true,
            eqeqeq: true,
            immed: true,
            latedef: true,
            newcap: true,
            noarg: true,
            sub: true,
            undef: true,
            boss: true,
            eqnull: true,
            browser: true
        },
        globals: {}
    },
    compass: {
        dev: {
            src: 'dev/_ui/css',
            dest: 'temp/_ui/css',
            linecomments: true,
            debugsass: true,
            relativeassets: true
        },
        dist: {
            src: 'dev/_ui/css',
            dest: 'dist/_ui/css',
            linecomments: false,
            outputstyle: 'compressed',
            debugsass: false,
            relativeassets: true
        }
    },

    // build config
    copy: {
        dev: {
            files: {
                'temp/': grunt.file.expandFiles([
                    'dev/**'
                ]).filter(function(f){// copy over everything except scss, which will be processed/copied with compass
                    return !/.+(\.scss)$/gm.test(f);
                })
            }
        },
        dist: {
            files: {
                // TODO: switch to grunt.file.expand in grunt v0.4.0a, which will support exclusions in minimatch
                'dist/': grunt.file.expandFiles([
                    'dev/**'
                ]).filter(function(f){// copy over everything except css and js, which will be processed/copied with compass and concat/min functions
                    return !/dev\/_ui\/css|dev\/_ui\/js/.test(f);
                })
            }
        }
    },
    concat: {
        dist: {
            src: ['dev/_ui/js/**/*.js'],
            dest: 'dist/_ui/js/scripts.js'
        }
    },
    min: {
        dist: {
            src: ['dist/_ui/js/scripts.js'],
            dest: 'dist/_ui/js/scripts.min.js'
        }
    },
    // replace js and css link build blocks within specified files with their concatenated versions
    replacelinks: {
        files: [
            'dist/*.html'
        ]
    }
});

// grunt 3rd party plugins
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-compass');

// custom/ported tasks
grunt.loadTasks('tasks/');

// Default task.
grunt.registerTask('run', 'lint qunit copy:dev compass:dev server watch');
grunt.registerTask('build', 'lint qunit copy:dist compass:dist concat min replacelinks');

};
<|endoftext|>


<commit_before>var testacular = require('testacular');

/*global module:false*/
module.exports = function (grunt) {

  grunt.loadNpmTasks('grunt-recess');
  grunt.loadNpmTasks('grunt-coffee');

  // Project configuration.
  grunt.initConfig({
    builddir: 'build',
    pkg: '<json:package.json>',
    meta: {
      banner: '/**\n' + ' * <%= pkg.description %>\n' +
      ' * @version v<%= pkg.version %> - ' +
      '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
      ' * @link <%= pkg.homepage %>\n' +
      ' * @license MIT License, http://www.opensource.org/licenses/MIT\n' + ' */'
    },
    coffee: {
      build: {
        src: ['common/*.coffee', 'modules/**/*.coffee'],
        extension: ".coffee.js"
      }
    },
    concat: {
      build: {
        src: ['<banner:meta.banner>', 'common/*.js'],
        dest: '<%= builddir %>/<%= pkg.name %>.js'
      },
      ieshiv: {
        src: ['<banner:meta.banner>', 'common/ieshiv/*.js'],
        dest: '<%= builddir %>/<%= pkg.name %>-ieshiv.js'
      }
    },
    min: {
      build: {
        src: ['<banner:meta.banner>', '<config:concat.build.dest>'],
        dest: '<%= builddir %>/<%= pkg.name %>.min.js'
      },
      ieshiv: {
        src: ['<banner:meta.banner>', '<config:concat.ieshiv.dest>'],
        dest: '<%= builddir %>/<%= pkg.name %>-ieshiv.min.js'
      }
    },
    recess: {
      build: {
        src: ['common/**/*.less'],
        dest: '<%= builddir %>/<%= pkg.name %>.css',
        options: {
          compile: true
        }
      },
      min: {
        src: '<config:recess.build.dest>',
        dest: '<%= builddir %>/<%= pkg.name %>.min.css',
        options: {
          compress: true
        }
      }
    },
    test: {
      files: ['modules/**/test/*.js', 'templates/**/test/*.js']
    },
    lint: {
      files: ['grunt.js', 'common/**/*.js', 'modules/**/*.js']
    },
    watch: {
      files: ['modules/**/*.coffee', 'modules/**/*.js', 'common/**/*.js', 'templates/**/*.js'],
      tasks: 'coffee concat:build test'
    }
  });

  //Get all the available modules into an array
  var modules = {};
  grunt.file.recurse('modules', function(abspath, rootdir, subdir, filename) {
    var moduleDir;
    //If number of / is 1, we know it's first in depth. eg filters/inflector, not filters/inflector/test
    if (subdir.split('/').length === 2) {
      moduleDir = rootdir + '/' + subdir;
      modules[subdir] = {
        js: grunt.file.expand(moduleDir + '/*.js'),
        less: grunt.file.expand(moduleDir + '/*.less')
      };
    }
  });

  // Default task.
  grunt.registerTask('default', 'coffee build test');

  grunt.registerTask('build', 'build all or some of the angular-ui modules', function() {

    var jsBuildFiles = grunt.config('concat.build.src');
    var lessBuildFiles = grunt.config('recess.build.src');

    function addModuleFiles(module) {
      module.js.forEach(function(file) {
        jsBuildFiles.push(file);
      });
      module.less.forEach(function(file) {
        lessBuildFiles.push(file);
      });
    }

    if (this.args.length === 0) {
      //if no modules given as args, go to default: build all
      for (var moduleName in modules) {
        if (modules.hasOwnProperty(moduleName)) {
          addModuleFiles(modules[moduleName]);
        }
      }
    } else {
      //if args are found, build given modules & build to custom folder
      grunt.config('builddir', 'build/custom');
      this.args.forEach(function(moduleName) {
        if (modules[moduleName]) {
          addModuleFiles(modules[moduleName]);
        }
      });
    }

    //Set config with our new file lists
    grunt.config('concat.build.src', jsBuildFiles);
    grunt.config('recess.build.src', lessBuildFiles);

    grunt.task.run('concat min recess:build recess:min');
  });

  grunt.registerTask('server', 'start testacular server', function () {
    //Mark the task as async but never call done, so the server stays up
    var done = this.async();
    testacular.server.start('test/test-config.js');
  });

  grunt.registerTask('test', 'run tests (make sure server task is run first)', function () {
    var done = this.async();
    grunt.utils.spawn({
      cmd: process.platform === 'win32' ? 'testacular-run.cmd' : 'testacular-run',
      args: ['test/test-config.js']
    }, function (error, result, code) {
      if (error) {
        grunt.warn("Make sure the testacular server is online: run `grunt server`.\n" +
          "Also make sure you have a browser open to http://localhost:8080/.\n" +
          error.stdout + error.stderr);
        //the testacular runner somehow modifies the files if it errors(??).
        //this causes grunt's watch task to re-fire itself constantly,
        //unless we wait for a sec
        setTimeout(done, 1000);
      } else {
        grunt.log.write(result.stdout);
        done();
      }
    });
  });
};
<commit_msg>Fix grunt watch task to work with new build task<commit_after>var testacular = require('testacular');

/*global module:false*/
module.exports = function (grunt) {

  grunt.loadNpmTasks('grunt-recess');
  grunt.loadNpmTasks('grunt-coffee');

  // Project configuration.
  grunt.initConfig({
    builddir: 'build',
    pkg: '<json:package.json>',
    meta: {
      banner: '/**\n' + ' * <%= pkg.description %>\n' +
      ' * @version v<%= pkg.version %> - ' +
      '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
      ' * @link <%= pkg.homepage %>\n' +
      ' * @license MIT License, http://www.opensource.org/licenses/MIT\n' + ' */'
    },
    coffee: {
      build: {
        src: ['common/*.coffee', 'modules/**/*.coffee'],
        extension: ".coffee.js"
      }
    },
    concat: {
      build: {
        src: ['<banner:meta.banner>', 'common/*.js'],
        dest: '<%= builddir %>/<%= pkg.name %>.js'
      },
      ieshiv: {
        src: ['<banner:meta.banner>', 'common/ieshiv/*.js'],
        dest: '<%= builddir %>/<%= pkg.name %>-ieshiv.js'
      }
    },
    min: {
      build: {
        src: ['<banner:meta.banner>', '<config:concat.build.dest>'],
        dest: '<%= builddir %>/<%= pkg.name %>.min.js'
      },
      ieshiv: {
        src: ['<banner:meta.banner>', '<config:concat.ieshiv.dest>'],
        dest: '<%= builddir %>/<%= pkg.name %>-ieshiv.min.js'
      }
    },
    recess: {
      build: {
        src: ['common/**/*.less'],
        dest: '<%= builddir %>/<%= pkg.name %>.css',
        options: {
          compile: true
        }
      },
      min: {
        src: '<config:recess.build.dest>',
        dest: '<%= builddir %>/<%= pkg.name %>.min.css',
        options: {
          compress: true
        }
      }
    },
    lint: {
      files: ['grunt.js', 'common/**/*.js', 'modules/**/*.js']
    },
    watch: {
      files: ['modules/**/*.coffee', 'modules/**/*.js', 'common/**/*.js', 'templates/**/*.js'],
      tasks: 'coffee build test'
    }
  });

  //Get all the available modules into an array
  var modules = {};
  grunt.file.recurse('modules', function(abspath, rootdir, subdir, filename) {
    var moduleDir;
    //If number of / is 1, we know it's first in depth. eg filters/inflector, not filters/inflector/test
    if (subdir.split('/').length === 2) {
      moduleDir = rootdir + '/' + subdir;
      modules[subdir] = {
        js: grunt.file.expand(moduleDir + '/*.js'),
        less: grunt.file.expand(moduleDir + '/*.less')
      };
    }
  });

  // Default task.
  grunt.registerTask('default', 'coffee build test');

  grunt.registerTask('build', 'build all or some of the angular-ui modules', function() {

    var jsBuildFiles = grunt.config('concat.build.src');
    var lessBuildFiles = grunt.config('recess.build.src');

    function addModuleFiles(module) {
      module.js.forEach(function(file) {
        jsBuildFiles.push(file);
      });
      module.less.forEach(function(file) {
        lessBuildFiles.push(file);
      });
    }

    if (this.args.length === 0) {
      //if no modules given as args, go to default: build all
      for (var moduleName in modules) {
        if (modules.hasOwnProperty(moduleName)) {
          addModuleFiles(modules[moduleName]);
        }
      }
    } else {
      //if args are found, build given modules & build to custom folder
      grunt.config('builddir', 'build/custom');
      this.args.forEach(function(moduleName) {
        if (modules[moduleName]) {
          addModuleFiles(modules[moduleName]);
        }
      });
    }

    //Set config with our new file lists
    grunt.config('concat.build.src', jsBuildFiles);
    grunt.config('recess.build.src', lessBuildFiles);

    grunt.task.run('concat min recess:build recess:min');
  });

  grunt.registerTask('server', 'start testacular server', function () {
    //Mark the task as async but never call done, so the server stays up
    var done = this.async();
    testacular.server.start('test/test-config.js');
  });

  grunt.registerTask('test', 'run tests (make sure server task is run first)', function () {
    var done = this.async();
    grunt.utils.spawn({
      cmd: process.platform === 'win32' ? 'testacular-run.cmd' : 'testacular-run',
      args: ['test/test-config.js']
    }, function (error, result, code) {
      if (error) {
        grunt.warn("Make sure the testacular server is online: run `grunt server`.\n" +
          "Also make sure you have a browser open to http://localhost:8080/.\n" +
          error.stdout + error.stderr);
        //the testacular runner somehow modifies the files if it errors(??).
        //this causes grunt's watch task to re-fire itself constantly,
        //unless we wait for a sec
        setTimeout(done, 1000);
      } else {
        grunt.log.write(result.stdout);
        done();
      }
    });
  });
};
<|endoftext|>


<commit_before>/*global module:false*/
module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    pkg: '<json:package.json>',
    meta: {
      banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' +
        '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
        '<%= pkg.homepage ? "* " + pkg.homepage + "\n" : "" %>' +
        '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' +
        ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */'
    },
    concat: {
      dist_js: {
        src: ['<banner:meta.banner>', '<file_strip_banner:src/jquery.coords.js>', '<file_strip_banner:src/jquery.collision.js>', 'src/utils.js', '<file_strip_banner:src/jquery.draggable.js>', '<file_strip_banner:src/<%= pkg.name %>.js>'],
        dest: 'dist/<%= pkg.name %>.js'
      },
      dist_css: {
        src: ['<banner:meta.banner>', 'src/<%= pkg.name %>.css'],
        dest: 'dist/<%= pkg.name %>.css'
      },

      dist_demo_js: {
        src: ['<banner:meta.banner>', '<file_strip_banner:src/jquery.coords.js>', '<file_strip_banner:src/jquery.collision.js>', 'src/utils.js', '<file_strip_banner:src/jquery.draggable.js>', '<file_strip_banner:src/<%= pkg.name %>.js>'],
        dest: 'gh-pages/dist/<%= pkg.name %>.js'
      },
      dist_demo_css: {
        src: ['<banner:meta.banner>', 'src/<%= pkg.name %>.css'],
        dest: 'gh-pages/dist/<%= pkg.name %>.css'
      }
    },
    min: {
      dist: {
        src: ['<banner:meta.banner>', '<config:concat.dist_js.dest>'],
        dest: 'dist/<%= pkg.name %>.min.js'
      },

      dist_demo: {
        src: ['<banner:meta.banner>', '<config:concat.dist_js.dest>'],
        dest: 'gh-pages/dist/<%= pkg.name %>.min.js'
      }
    },
    mincss: {
      compress: {
        files: {
          "dist/<%= pkg.name %>.min.css": ["dist/<%= pkg.name %>.css"],
          "gh-pages/dist/<%= pkg.name %>.min.css": ["dist/<%= pkg.name %>.css"]
        }
      }
    },
    qunit: {
      files: ['test/**/*.html']
    },
    lint: {
      files: ['grunt.js', 'src/**/*.js', 'test/**/*.js']
    },
    watch: {
      files: ['<config:lint.files>', 'src/<%= pkg.name %>.css'],
      tasks: 'lint concat'
    },
    jshint: {
      options: {
        curly: true,
        eqeqeq: true,
        immed: true,
        latedef: true,
        newcap: true,
        noarg: true,
        sub: true,
        undef: true,
        boss: true,
        eqnull: true,
        browser: true
      },
      globals: {
        jQuery: true
      }
    },
    uglify: {},
    yuidoc: {
      compile: {
        "name": 'gridster.js',
        "description": 'gridster.js, a drag-and-drop multi-column jQuery grid plugin',
        "version": '0.1.0',
        "url": 'http://gridster.net/',
        "logo": 'http://ducksboard.com/wp-content/themes/blog-theme-ducksboard/images/ducksboard.png',
        options: {
          paths: "src/",
          outdir: "gh-pages/docs/"
        }
      }
    }
  });

  grunt.loadNpmTasks('grunt-contrib');

  // Default task.
  grunt.registerTask('default', 'lint qunit concat min mincss yuidoc');

};
<commit_msg>grunt: modified watch command config<commit_after>/*global module:false*/
module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    pkg: '<json:package.json>',
    meta: {
      banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' +
        '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
        '<%= pkg.homepage ? "* " + pkg.homepage + "\n" : "" %>' +
        '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' +
        ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */'
    },
    concat: {
      dist_js: {
        src: ['<banner:meta.banner>', '<file_strip_banner:src/jquery.coords.js>', '<file_strip_banner:src/jquery.collision.js>', 'src/utils.js', '<file_strip_banner:src/jquery.draggable.js>', '<file_strip_banner:src/<%= pkg.name %>.js>'],
        dest: 'dist/<%= pkg.name %>.js'
      },
      dist_css: {
        src: ['<banner:meta.banner>', 'src/<%= pkg.name %>.css'],
        dest: 'dist/<%= pkg.name %>.css'
      },

      dist_demo_js: {
        src: ['<banner:meta.banner>', '<file_strip_banner:src/jquery.coords.js>', '<file_strip_banner:src/jquery.collision.js>', 'src/utils.js', '<file_strip_banner:src/jquery.draggable.js>', '<file_strip_banner:src/<%= pkg.name %>.js>'],
        dest: 'gh-pages/dist/<%= pkg.name %>.js'
      },
      dist_demo_css: {
        src: ['<banner:meta.banner>', 'src/<%= pkg.name %>.css'],
        dest: 'gh-pages/dist/<%= pkg.name %>.css'
      }
    },
    min: {
      dist: {
        src: ['<banner:meta.banner>', '<config:concat.dist_js.dest>'],
        dest: 'dist/<%= pkg.name %>.min.js'
      },

      dist_demo: {
        src: ['<banner:meta.banner>', '<config:concat.dist_js.dest>'],
        dest: 'gh-pages/dist/<%= pkg.name %>.min.js'
      }
    },
    mincss: {
      compress: {
        files: {
          "dist/<%= pkg.name %>.min.css": ["dist/<%= pkg.name %>.css"],
          "gh-pages/dist/<%= pkg.name %>.min.css": ["dist/<%= pkg.name %>.css"]
        }
      }
    },
    qunit: {
      files: ['test/**/*.html']
    },
    lint: {
      files: ['grunt.js', 'src/**/*.js', 'test/**/*.js']
    },
    watch: {
      files: ['<config:lint.files>', 'src/<%= pkg.name %>.css'],
      tasks: 'min concat'
    },
    jshint: {
      options: {
        curly: true,
        eqeqeq: true,
        immed: true,
        latedef: true,
        newcap: true,
        noarg: true,
        sub: true,
        undef: true,
        boss: true,
        eqnull: true,
        browser: true
      },
      globals: {
        jQuery: true
      }
    },
    uglify: {},
    yuidoc: {
      compile: {
        "name": 'gridster.js',
        "description": 'gridster.js, a drag-and-drop multi-column jQuery grid plugin',
        "version": '0.1.0',
        "url": 'http://gridster.net/',
        "logo": 'http://ducksboard.com/wp-content/themes/blog-theme-ducksboard/images/ducksboard.png',
        options: {
          paths: "src/",
          outdir: "gh-pages/docs/"
        }
      }
    }
  });

  grunt.loadNpmTasks('grunt-contrib');

  // Default task.
  grunt.registerTask('default', 'lint qunit concat min mincss yuidoc');

};
<|endoftext|>


<commit_before>import external from './../../externalModules.js';
import BaseBrushTool from './../base/BaseBrushTool.js';
import {
  getToolState,
  addToolState,
} from './../../stateManagement/toolState.js';
import store from './../../store/index.js';
import brushUtils from './../../util/brush/index.js';
import EVENTS from '../../events.js';

const { drawBrushPixels, getCircle } = brushUtils;

const brushModule = store.modules.brush;

/**
 * @public
 * @class BrushTool
 * @memberof Tools.Brush
 * @classdesc Tool for drawing segmentations on an image.
 * @extends Tools.Base.BaseBrushTool
 */
export default class BrushTool extends BaseBrushTool {
  constructor(configuration = {}) {
    const defaultConfig = {
      name: 'Brush',
      supportedInteractionTypes: ['Mouse'],
      strategies: {
        overlapping: _overlappingStrategy,
        nonOverlapping: _nonOverlappingStrategy,
      },
      defaultStrategy: 'overlapping',
      configuration: {},
    };
    const initialConfiguration = Object.assign(defaultConfig, configuration);

    super(initialConfiguration);

    this.initialConfiguration = initialConfiguration;
  }

  /**
   * Called by the event dispatcher to render the image.
   *
   * @param {Object} evt - The event.
   * @returns {void}
   */
  renderBrush(evt) {
    const eventData = evt.detail;
    const viewport = eventData.viewport;

    let mousePosition;

    if (this._drawing) {
      mousePosition = this._lastImageCoords;
    } else if (this._mouseUpRender) {
      mousePosition = this._lastImageCoords;
      this._mouseUpRender = false;
    } else {
      mousePosition = store.state.mousePositionImage;
    }

    if (!mousePosition) {
      return;
    }

    const { rows, columns } = eventData.image;
    const { x, y } = mousePosition;

    if (x < 0 || x > columns || y < 0 || y > rows) {
      return;
    }

    // Draw the hover overlay on top of the pixel data
    const radius = brushModule.state.radius;
    const context = eventData.canvasContext;
    const element = eventData.element;
    const drawId = brushModule.state.drawColorId;
    const color = this._getBrushColor(drawId);

    context.setTransform(1, 0, 0, 1, 0, 0);

    const { cornerstone } = external;

    const circleRadius = radius * viewport.scale;
    const mouseCoordsCanvas = cornerstone.pixelToCanvas(element, mousePosition);

    context.beginPath();
    context.strokeStyle = color;
    context.ellipse(
      mouseCoordsCanvas.x,
      mouseCoordsCanvas.y,
      circleRadius,
      circleRadius,
      0,
      0,
      2 * Math.PI
    );
    context.stroke();
  }

  /**
   * Paints the data to the canvas.
   *
   * @private
   * @param  {Object} evt The data object associated with the event.
   * @returns {void}
   */
  _paint(evt) {
    this.applyActiveStrategy(evt, this.configuration);

    external.cornerstone.triggerEvent(
      evt.detail.element,
      EVENTS.MEASUREMENT_MODIFIED,
      evt.detail
    );

    external.cornerstone.updateImage(evt.detail.element);
  }
}

function _overlappingStrategy(evt) {
  const eventData = evt.detail;
  const element = eventData.element;
  const { rows, columns } = eventData.image;
  const { x, y } = eventData.currentPoints.image;
  let toolState = getToolState(
    element,
    BaseBrushTool.getReferencedToolDataName()
  );

  if (!toolState) {
    addToolState(element, BaseBrushTool.getReferencedToolDataName(), {});
    toolState = getToolState(
      element,
      BaseBrushTool.getReferencedToolDataName()
    );
  }

  const toolData = toolState.data;

  if (x < 0 || x > columns || y < 0 || y > rows) {
    return;
  }

  const radius = brushModule.state.radius;
  const pointerArray = getCircle(radius, rows, columns, x, y);

  _drawMainColor(eventData, toolData, pointerArray);
}

function _nonOverlappingStrategy(evt) {
  const eventData = evt.detail;
  const element = eventData.element;
  const { rows, columns } = eventData.image;
  const { x, y } = eventData.currentPoints.image;

  let toolState = getToolState(
    element,
    BaseBrushTool.getReferencedToolDataName()
  );

  if (!toolState) {
    addToolState(element, BaseBrushTool.getReferencedToolDataName(), {});
    toolState = getToolState(
      element,
      BaseBrushTool.getReferencedToolDataName()
    );
  }

  const toolData = toolState.data;
  const segmentationIndex = brushModule.state.drawColorId;

  if (x < 0 || x > columns || y < 0 || y > rows) {
    return;
  }

  const radius = brushModule.state.radius;
  const pointerArray = getCircle(radius, rows, columns, x, y);

  const numberOfColors = BaseBrushTool.getNumberOfColors();

  // If there is brush data in this region for other colors, delete it.
  for (let i = 0; i < numberOfColors; i++) {
    if (i === segmentationIndex) {
      continue;
    }

    if (toolData[i] && toolData[i].pixelData) {
      drawBrushPixels(pointerArray, toolData[i], columns, true);
      toolData[i].invalidated = true;
    }
  }

  _drawMainColor(eventData, toolData, pointerArray);
}

function _drawMainColor(eventData, toolData, pointerArray) {
  const shouldErase = _isCtrlDown(eventData);
  const columns = eventData.image.columns;
  const segmentationIndex = brushModule.state.drawColorId;

  if (shouldErase && !toolData[segmentationIndex]) {
    // Erase command, yet no data yet, just return.
    return;
  }

  if (!toolData[segmentationIndex]) {
    toolData[segmentationIndex] = {};
  }

  if (!toolData[segmentationIndex].pixelData) {
    const enabledElement = external.cornerstone.getEnabledElement(
      eventData.element
    );
    const enabledElementUID = enabledElement.uuid;

    // Clear cache for this color to avoid flickering.
    const imageBitmapCacheForElement = brushModule.getters.imageBitmapCacheForElement(
      enabledElementUID
    );

    if (imageBitmapCacheForElement) {
      imageBitmapCacheForElement[segmentationIndex] = null;
    }

    // Add a new pixelData array.
    toolData[segmentationIndex].pixelData = new Uint8ClampedArray(
      eventData.image.width * eventData.image.height
    );
  }

  const toolDataI = toolData[segmentationIndex];

  // Draw / Erase the active color.
  drawBrushPixels(pointerArray, toolDataI, columns, shouldErase);

  toolDataI.invalidated = true;
}

function _isCtrlDown(eventData) {
  return (eventData.event && eventData.event.ctrlKey) || eventData.ctrlKey;
}
<commit_msg>Touch supported for brush.<commit_after>import external from './../../externalModules.js';
import BaseBrushTool from './../base/BaseBrushTool.js';
import {
  getToolState,
  addToolState,
} from './../../stateManagement/toolState.js';
import store from './../../store/index.js';
import brushUtils from './../../util/brush/index.js';
import EVENTS from '../../events.js';

const { drawBrushPixels, getCircle } = brushUtils;

const brushModule = store.modules.brush;

/**
 * @public
 * @class BrushTool
 * @memberof Tools.Brush
 * @classdesc Tool for drawing segmentations on an image.
 * @extends Tools.Base.BaseBrushTool
 */
export default class BrushTool extends BaseBrushTool {
  constructor(configuration = {}) {
    const defaultConfig = {
      name: 'Brush',
      supportedInteractionTypes: ['Mouse', 'Touch'],
      strategies: {
        overlapping: _overlappingStrategy,
        nonOverlapping: _nonOverlappingStrategy,
      },
      defaultStrategy: 'overlapping',
      configuration: {},
    };
    const initialConfiguration = Object.assign(defaultConfig, configuration);

    super(initialConfiguration);

    this.touchDragCallback = this._paint.bind(this);

    this.initialConfiguration = initialConfiguration;
  }

  /**
   * Called by the event dispatcher to render the image.
   *
   * @param {Object} evt - The event.
   * @returns {void}
   */
  renderBrush(evt) {
    const eventData = evt.detail;
    const viewport = eventData.viewport;

    let mousePosition;

    if (this._drawing) {
      mousePosition = this._lastImageCoords;
    } else if (this._mouseUpRender) {
      mousePosition = this._lastImageCoords;
      this._mouseUpRender = false;
    } else {
      mousePosition = store.state.mousePositionImage;
    }

    if (!mousePosition) {
      return;
    }

    const { rows, columns } = eventData.image;
    const { x, y } = mousePosition;

    if (x < 0 || x > columns || y < 0 || y > rows) {
      return;
    }

    // Draw the hover overlay on top of the pixel data
    const radius = brushModule.state.radius;
    const context = eventData.canvasContext;
    const element = eventData.element;
    const drawId = brushModule.state.drawColorId;
    const color = this._getBrushColor(drawId);

    context.setTransform(1, 0, 0, 1, 0, 0);

    const { cornerstone } = external;

    const circleRadius = radius * viewport.scale;
    const mouseCoordsCanvas = cornerstone.pixelToCanvas(element, mousePosition);

    context.beginPath();
    context.strokeStyle = color;
    context.ellipse(
      mouseCoordsCanvas.x,
      mouseCoordsCanvas.y,
      circleRadius,
      circleRadius,
      0,
      0,
      2 * Math.PI
    );
    context.stroke();
  }

  /**
   * Paints the data to the canvas.
   *
   * @private
   * @param  {Object} evt The data object associated with the event.
   * @returns {void}
   */
  _paint(evt) {
    this.applyActiveStrategy(evt, this.configuration);

    external.cornerstone.triggerEvent(
      evt.detail.element,
      EVENTS.MEASUREMENT_MODIFIED,
      evt.detail
    );

    external.cornerstone.updateImage(evt.detail.element);
  }
}

function _overlappingStrategy(evt) {
  const eventData = evt.detail;
  const element = eventData.element;
  const { rows, columns } = eventData.image;
  const { x, y } = eventData.currentPoints.image;
  let toolState = getToolState(
    element,
    BaseBrushTool.getReferencedToolDataName()
  );

  if (!toolState) {
    addToolState(element, BaseBrushTool.getReferencedToolDataName(), {});
    toolState = getToolState(
      element,
      BaseBrushTool.getReferencedToolDataName()
    );
  }

  const toolData = toolState.data;

  if (x < 0 || x > columns || y < 0 || y > rows) {
    return;
  }

  const radius = brushModule.state.radius;
  const pointerArray = getCircle(radius, rows, columns, x, y);

  _drawMainColor(eventData, toolData, pointerArray);
}

function _nonOverlappingStrategy(evt) {
  const eventData = evt.detail;
  const element = eventData.element;
  const { rows, columns } = eventData.image;
  const { x, y } = eventData.currentPoints.image;

  let toolState = getToolState(
    element,
    BaseBrushTool.getReferencedToolDataName()
  );

  if (!toolState) {
    addToolState(element, BaseBrushTool.getReferencedToolDataName(), {});
    toolState = getToolState(
      element,
      BaseBrushTool.getReferencedToolDataName()
    );
  }

  const toolData = toolState.data;
  const segmentationIndex = brushModule.state.drawColorId;

  if (x < 0 || x > columns || y < 0 || y > rows) {
    return;
  }

  const radius = brushModule.state.radius;
  const pointerArray = getCircle(radius, rows, columns, x, y);

  const numberOfColors = BaseBrushTool.getNumberOfColors();

  // If there is brush data in this region for other colors, delete it.
  for (let i = 0; i < numberOfColors; i++) {
    if (i === segmentationIndex) {
      continue;
    }

    if (toolData[i] && toolData[i].pixelData) {
      drawBrushPixels(pointerArray, toolData[i], columns, true);
      toolData[i].invalidated = true;
    }
  }

  _drawMainColor(eventData, toolData, pointerArray);
}

function _drawMainColor(eventData, toolData, pointerArray) {
  const shouldErase = _isCtrlDown(eventData);
  const columns = eventData.image.columns;
  const segmentationIndex = brushModule.state.drawColorId;

  if (shouldErase && !toolData[segmentationIndex]) {
    // Erase command, yet no data yet, just return.
    return;
  }

  if (!toolData[segmentationIndex]) {
    toolData[segmentationIndex] = {};
  }

  if (!toolData[segmentationIndex].pixelData) {
    const enabledElement = external.cornerstone.getEnabledElement(
      eventData.element
    );
    const enabledElementUID = enabledElement.uuid;

    // Clear cache for this color to avoid flickering.
    const imageBitmapCacheForElement = brushModule.getters.imageBitmapCacheForElement(
      enabledElementUID
    );

    if (imageBitmapCacheForElement) {
      imageBitmapCacheForElement[segmentationIndex] = null;
    }

    // Add a new pixelData array.
    toolData[segmentationIndex].pixelData = new Uint8ClampedArray(
      eventData.image.width * eventData.image.height
    );
  }

  const toolDataI = toolData[segmentationIndex];

  // Draw / Erase the active color.
  drawBrushPixels(pointerArray, toolDataI, columns, shouldErase);

  toolDataI.invalidated = true;
}

function _isCtrlDown(eventData) {
  return (eventData.event && eventData.event.ctrlKey) || eventData.ctrlKey;
}
<|endoftext|>


<commit_before>

var assert = require('assert'),
    Badger = require('../lib/honeybadger');

suite('node.js honeybadger.io notifier', function () {
  var hb1, hb2, payloads = [], payloadCount = 0;

  setup(function () {
    // Don't send actual requests to honeybadger.io from the test suite
    Badger.prototype._post = function (data) { payloads.push(data); };
  });

  suite('Creating a Badger without an API key', function () {
    var hb = new Badger({
      apiKey: null,
      server: { testmeta: 'data' }
    });

    setup(function (done) {
      payloadCount = payloads.length;
      hb.send(new Error('test error 1'));
      setImmediate(done);
    });

    test('makes it a no-op when used', function () {
      assert(payloads.length === payloadCount, 'Payload was sent without API key');
    });
  });

  suite('Creating a Badger with server metadata', function () {
    var hb = new Badger({
      // Because we've mocked the POST, the API key here only needs to be
      // non-falsy.
      apiKey: 'fake api key',
      server: {
        name: 'honeybadge',
        role: 'testing'
      },
    });

    setup(function (done) {
      payloadCount = payloads.length;
      hb.send(new Error('test error 2'));
      setImmediate(done);
    });

    test('successfully sends the payload', function () {
      var p;
      assert(payloads.length === (payloadCount + 1), 'payload not sent');
      p = payloads[payloads.length - 1];
      assert(p.error.message = 'test error 2', 'payload incorrect');
    });

    test('the server metadata is added to the payload', function () {
      var s = payloads[payloads.length - 1];
      assert(s.server.name === 'honeybadge', 'Server name not set.');
      assert(s.server.role === 'testing', 'Server role not set.');
    });
  });

  suite('Creating a Badger with a custom notifier', function () {
    var hb = new Badger({
      apiKey: 'fake api key',
      server: { name: 'honeybadger' },
      notifier: {
        name: 'honeybadger test suite',
        url: 'https://notarealsite.net/page14.php',
        version: '0.4.18'
      }
    });

    setup(function (done) {
      payloadCount = payloads.length;
      hb.send(new Error('test error 3'));
      setImmediate(done);
    });

    test('successfully sends the payload', function () {
      var p;
      assert(payloads.length === (payloadCount + 1), 'payload not sent');
      p = payloads[payloads.length - 1];
      assert(p.error.message === 'test error 3', 'payload incorrect');
    });

    test('correctly sets the notifier field in the payload', function () {
      var n;
      n = payloads[payloads.length - 1].notifier;
      assert(n.name === 'honeybadger test suite', 'name not set');
      assert(n.url === 'https://notarealsite.net/page14.php', 'url not set');
      assert(n.version === '0.4.18', 'version not set');
    });
  });

  suite('Passing contextual metadata with an error', function () {
    var hb = new Badger({ apiKey: 'fake api key' });

    var meta = {
      context: {
        uid: '0xdeadbeef',
        email: 'test@testing.biz'
      },
      session: {
        id: '1234567890',
        data: {a: 1, b: 2, c: 3}
      },
      params: {
        arg1: 'hello'
      },
      cookies: {
        omnom: 'nom'
      }
    };

    setup(function (done) {
      payloadCount = payloads.length;
      hb.send(new Error('test error 4'), meta);
      setImmediate(done);
    });

    test('successfully sends the payload', function () {
      var p;
      assert(payloads.length === (payloadCount + 1), 'payload not sent');
      p = payloads[payloads.length - 1];
      assert(p.error.message === 'test error 4', 'payload incorrect');
    });

    test('The contextual metadata is passed correctly', function () {
      var p = payloads[payloads.length - 1];
      assert.deepEqual(p.request, meta, 'Metadata incorrect');
    });
  });
});
<commit_msg>Refactor tests, finally win all the races<commit_after>

var assert = require('assert'),
    Badger = require('../lib/honeybadger');

suite('node.js honeybadger.io notifier', function () {
  var payloads = [], payloadCount;

  setup(function () {
    // Don't send actual requests to honeybadger.io from the test suite
    Badger.prototype._post = function (data) {
      payloads.push(data);
      setImmediate(this.emit.bind(this, 'sent', {}));
    };
  });

  suite('Creating a Badger without an API key', function () {
    var hb = new Badger({
          apiKey: null,
          server: { testmeta: 'data' }
        }),
        payloadCount = payloads.length;

    test('makes it a no-op when used', function (done) {
      hb.once('sent', function () {
        throw new Error('This event should not fire!');
      });
      hb.send(new Error('test error 1'));
      setTimeout(function () {
        assert(payloads.length === payloadCount, 'Payload was sent without API key');
        done();
      }, 10);
    });
  });

  suite('Creating a Badger with server metadata', function () {
    var hb = new Badger({
          // Because we've mocked the POST, the API key here only needs to be
          // non-falsy.
          apiKey: 'fake api key',
          server: {
            name: 'honeybadge',
            role: 'testing'
          },
        });

    test('successfully sends the payload', function (done) {
      payloadCount = payloads.length;
      hb.once('sent', function () {
        var p;
        assert(payloads.length === (payloadCount + 1), 'payload not sent');
        p = payloads[payloads.length - 1];
        assert(p.error.message = 'test error 2', 'payload incorrect');
        done();
      });
      hb.send(new Error('test error 2'));
    });

    test('the server metadata is added to the payload', function () {
      var s = payloads[payloads.length - 1];
      assert(s.server.name === 'honeybadge', 'Server name not set.');
      assert(s.server.role === 'testing', 'Server role not set.');
    });
  });

  suite('Creating a Badger with a custom notifier', function () {
    var hb = new Badger({
      apiKey: 'fake api key',
      server: { name: 'honeybadger' },
      notifier: {
        name: 'honeybadger test suite',
        url: 'https://notarealsite.net/page14.php',
        version: '0.4.18'
      }
    });

    test('successfully sends the payload', function (done) {
      payloadCount = payloads.length;
      hb.once('sent', function () {
        var p;
        assert(payloads.length === (payloadCount + 1), 'payload not sent');
        p = payloads[payloads.length - 1];
        assert(p.error.message === 'test error 3', 'payload incorrect');
        done();
      });
      hb.send(new Error('test error 3'));
    });

    test('correctly sets the notifier field in the payload', function () {
      var n;
      n = payloads[payloads.length - 1].notifier;
      assert(n.name === 'honeybadger test suite', 'name not set');
      assert(n.url === 'https://notarealsite.net/page14.php', 'url not set');
      assert(n.version === '0.4.18', 'version not set');
    });
  });

  suite('Passing contextual metadata with an error', function () {
    var hb = new Badger({ apiKey: 'fake api key' });

    var meta = {
      context: {
        uid: '0xdeadbeef',
        email: 'test@testing.biz'
      },
      session: {
        id: '1234567890',
        data: {a: 1, b: 2, c: 3}
      },
      params: {
        arg1: 'hello'
      },
      cookies: {
        omnom: 'nom'
      }
    };

    test('successfully sends the payload', function (done) {
      payloadCount = payloads.length;
      hb.once('sent', function () {
        var p;
        assert(payloads.length === (payloadCount + 1), 'payload not sent');
        p = payloads[payloads.length - 1];
        assert(p.error.message === 'test error 4', 'payload incorrect');
        done();
      });
      hb.send(new Error('test error 4'), meta);
    });

    test('The contextual metadata is passed correctly', function () {
      var p = payloads[payloads.length - 1];
      assert.deepEqual(p.request, meta, 'Metadata incorrect');
    });
  });
});
<|endoftext|>


<commit_before>/*!
 * integration test for all components
 */

var Assert = require('assert');
var Vows = require('vows');

var Parser = require('../lib/parser');
var Environment = require('../lib/environment');
var interpreter = require('../lib/interpreter').createInterpreter(
  Environment.createEnvironment()
);

function exec(code) {
  var ast = Parser.parse(code);
  return interpreter.eval(ast);
}

function execAssert(code, result) {
  var res = exec(code);
  Assert.strictEqual(res, result);
}

Vows.describe('test all components of interpreter').addBatch({
    'some simple calclulations' : {

      '3 + 2 = 5' : function () {
        execAssert('(+ 3 2)', 5);
      },

      '10 + 2 - 5 = 7' : function () {
        var test = '(- (+ 10 2) 5)';
        execAssert(test, 7)
      },

      '1 + 2 + 3 + 4 + 5 = 15' : function () {
        var test = '(+ 1 2 3 4 5)';
        execAssert(test, 15);
      },

      '2 * 2 + 4 * 4 = 20' : function () {
        var test = '(+ (* 2 2) (* 4 4))';
        execAssert(test, 20);
      }
    },

    'test boolean expressions' : {
      '1 == 1' : function () {
        var test = '(eq? 1 1)';
        execAssert(test, true);
      },

      '1 === 2' : function () {
        var test = '(eq? 1 2)';
        execAssert(test, false);
      }
    },

    'test conditional expressions' : {

      'if (1 === 1)' : function () {
        var test = '(if (eq? 1 1) #t #f)';
        execAssert(test, true);
      },

      'if (1 === 2)' : function () {
        var test = '(if (eq? 1 2) #t #f)';
        execAssert(test, false);
      },

      'cond without else 1' : function () {
        var test = '(cond ((eq? 1 2) "a") ((eq? 2 2) "b")';
        execAssert(test, 'b');
      },

      'cond without else 2' : function () {
        var test = '(cond ((eq? 1 1) "a") ((eq? 2 2) "b")';
        execAssert(test, 'a');
      },

      'cond with else 1' : function () {
        var test = '(cond (#f "should not happen") (else "test"))';
        execAssert(test, 'test');
      },

      'cond with else 2' : function () {
        var test = '(cond (#f "a") (#f "b") (#t "c") (else "test"))';
        execAssert(test, 'c');
      }

    },

    'test lambda expressions' : {

      'simple lambda' : function () {
        var test = '((lambda (n) (* n 10)) 3)';
        execAssert(test, 30);
      },

      'define lambda and call it' : function () {
        var test = '(define timesten (lambda (n) (* n 10)))';
        exec(test);
        execAssert('(timesten 5)', 50);
      }

    }

  }).export(module);
<commit_msg>Add closure integration tests<commit_after>/*!
 * integration test for all components
 */

var Assert = require('assert');
var Vows = require('vows');

var Parser = require('../lib/parser');
var Environment = require('../lib/environment');
var interpreter = require('../lib/interpreter').createInterpreter(
  Environment.createEnvironment()
);

function exec(code) {
  var ast = Parser.parse(code);
  return interpreter.eval(ast);
}

function execAssert(code, result) {
  var res = exec(code);
  Assert.strictEqual(res, result);
}

Vows.describe('test all components of interpreter').addBatch({
    'some simple calclulations' : {

      '3 + 2 = 5' : function () {
        execAssert('(+ 3 2)', 5);
      },

      '10 + 2 - 5 = 7' : function () {
        var test = '(- (+ 10 2) 5)';
        execAssert(test, 7)
      },

      '1 + 2 + 3 + 4 + 5 = 15' : function () {
        var test = '(+ 1 2 3 4 5)';
        execAssert(test, 15);
      },

      '2 * 2 + 4 * 4 = 20' : function () {
        var test = '(+ (* 2 2) (* 4 4))';
        execAssert(test, 20);
      }
    },

    'test boolean expressions' : {
      '1 == 1' : function () {
        var test = '(eq? 1 1)';
        execAssert(test, true);
      },

      '1 === 2' : function () {
        var test = '(eq? 1 2)';
        execAssert(test, false);
      }
    },

    'test conditional expressions' : {

      'if (1 === 1)' : function () {
        var test = '(if (eq? 1 1) #t #f)';
        execAssert(test, true);
      },

      'if (1 === 2)' : function () {
        var test = '(if (eq? 1 2) #t #f)';
        execAssert(test, false);
      },

      'cond without else 1' : function () {
        var test = '(cond ((eq? 1 2) "a") ((eq? 2 2) "b")';
        execAssert(test, 'b');
      },

      'cond without else 2' : function () {
        var test = '(cond ((eq? 1 1) "a") ((eq? 2 2) "b")';
        execAssert(test, 'a');
      },

      'cond with else 1' : function () {
        var test = '(cond (#f "should not happen") (else "test"))';
        execAssert(test, 'test');
      },

      'cond with else 2' : function () {
        var test = '(cond (#f "a") (#f "b") (#t "c") (else "test"))';
        execAssert(test, 'c');
      }

    },

    'test lambda expressions' : {

      'simple lambda' : function () {
        var test = '((lambda (n) (* n 10)) 3)';
        execAssert(test, 30);
      },

      'multiple argument lambda' : function () {
        var test = '((lambda (a b c) (+ a b c)) 1 2 3)';
        execAssert(test, 6);
      },

      'define lambda and call it' : function () {
        var test = '(define timesten (lambda (n) (* n 10)))';
        exec(test);
        execAssert('(timesten 5)', 50);
      }

    },

    'test closures' : {

      'simple closure' : function () {
        exec('(define multiplier 10)');
        execAssert('((lambda (x) (* x multiplier)) 10)', 100);
      },

      'simple closure' : function () {
        exec('(define multipair (cons 2 5))');
        execAssert('((lambda (x) (* (car multipair) (cdr multipair) x)) 10)', 100);
      }

    }

  }).export(module);
<|endoftext|>


<commit_before>'use strict'

module.exports = toHashOfStrings;

function toHashOfStrings (obj) {
  var out = {};
  for (var key in obj) {
    if (obj.hasOwnProperty(key)) {
      out[key] = obj[key].toString();
    }
  }
  return out;
}
<commit_msg>Fix null/undefined error in hash of strings<commit_after>'use strict'

module.exports = toHashOfStrings;

function toHashOfStrings (obj) {
  var out = {};
  for (var key in obj) {
    if (obj.hasOwnProperty(key)) {
      out[key] = new String(obj[key]).toString();
    }
  }
  return out;
}
<|endoftext|>


<commit_before>width = 740;
height = 200;
margin = 10;
labels = 40;

lookback = 60*60*24*31;

function add_fields(fields) {
    return (d) => {
        var sum = 0;
        for (var i = 0; i < fields.length; i++) {
            sum += (d[fields[i]] || 0);
        }
        return sum;
    }
}

function render_key(key, value) {
    switch (key) {
        case ":time": return (new Date(value * 1000)).toString();
        case ":runtime":
        if (value < 2 * 60 * 1000) {
            return Math.floor(value / 1000) + "s";
        } else {
            var h = Math.floor(value / 1000 / 60 / 60);
            var m = Math.floor(value / 1000 / 60);
            return h + ":" + (m - 60 * h);
        }
        default: return value;
    }
}

function html_path(json_path) {
    return json_path.replace("/json/", "/").replace(".json", ".html");
}

MONTHS = "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" ");

function pretty_date(date) {
    return date.getDate() + " " + MONTHS[date.getMonth()] + " " + date.getFullYear();
}

function pretty_time(date) {
    var minutes = date.getMinutes();
    return date.getHours() + ":" + (minutes < 10 ? "0" : "") + date.getMinutes();
}

function draw_class(node, name, data) {
    var header = node.append("header");

    var g = node.append("svg")
        .attr("width", width + 2 * margin + labels)
        .attr("height", height + 2 * margin + labels)
        .append("g").attr("transform", "translate(" + (margin + labels) + "," + margin + ")");

    var table = node.append("table");

    var x = d3.scaleTime().range([0, width]);
    var y = d3.scaleLinear().range([height, 0]);
    x.domain(d3.extent(data, x => new Date(x[":time"] * 1000)));
    y.domain(d3.extent(data, x => add_fields(["success", "expected"])(x)));
    g.append("g").call(d3.axisLeft().scale(y))
    g.append("g").call(d3.axisBottom().scale(x))
        .attr("transform", "translate(0," + height + ")")

    branches = {}
    for (var i = 0; i < data.length; i++) {
        var array = (branches[data[i][":branch"]] || []);
        array.push(data[i]);
        branches[data[i][":branch"]] = array;
    }
    var key = d3.scaleOrdinal(d3.schemeCategory10).domain(Object.keys(branches));

    header.append("ul").selectAll("li")
        .data(Object.keys(branches)).enter().append("li")
        .style("border-left-width", "1em")
        .style("border-left-color", key)
        .text((x) => x);
    header.append("h1").text(name);

    for (var branch in branches) {
        console.log(name, branch, key(branch))
        if (!branches.hasOwnProperty(branch)) continue;
        g.append("g").selectAll("circle")
            .data(branches[branch]).enter().append("circle")
            .attr("cx", (d) => x(new Date(d[":time"] * 1000)))
            .attr("cy", (d) => y(add_fields(["success", "expected"])(d)))
            .attr("r", 5)
            .attr("fill", key(branch))
            .append("title")
            .text((d) => {
                var s = "";
                var keys = Object.keys(d);
                keys.sort();
                for (var i = 0; i < keys.length; i++) {
                    s += keys[i] + ": " + render_key(keys[i], d[keys[i]]) + "\n";
                }
                return s;
            });
    }

    table.append("thead").selectAll("th")
        .data(["Date", "Time", "Branch", "Passing", "Failures", "Link"]).enter().append("th")
        .text((d) => d);

    table.append("tbody").selectAll("tr")
        .data(data).enter().append("tr")
        .on("click", function() { this.children[this.children.length - 1].children[0].click() })
        .selectAll("td").data(d => [
            pretty_date(new Date(d[":time"] * 1000)),
            pretty_time(new Date(d[":time"] * 1000)),
            "" + d[":branch"] + "",
            add_fields(["success", "expected"])(d) + "/" +
                add_fields(["success", "expected", "timeout", "fail", "unsupported"])(d),
            add_fields(["fail", "error"])(d),
            "»",
        ]).enter().append("td")
        .html(d => d);
}

function relevant_data(data) {
    return data[":time"] > new Date() / 1000 - lookback;
}

function draw(node, data) {
    for (var i in data) {
        if (!data.hasOwnProperty(i)) continue;
        var class_data = data[i].sort((a, b) => b[":time"] - a[":time"]).slice(0, 30);
        if (class_data.length > 0) {
            draw_class(node.append("section").attr("data-suite", i), i, class_data);
        }
    }
}
<commit_msg>Drop irrelevant charts<commit_after>width = 740;
height = 200;
margin = 10;
labels = 40;

lookback = 60*60*24*31;

function add_fields(fields) {
    return (d) => {
        var sum = 0;
        for (var i = 0; i < fields.length; i++) {
            sum += (d[fields[i]] || 0);
        }
        return sum;
    }
}

function render_key(key, value) {
    switch (key) {
        case ":time": return (new Date(value * 1000)).toString();
        case ":runtime":
        if (value < 2 * 60 * 1000) {
            return Math.floor(value / 1000) + "s";
        } else {
            var h = Math.floor(value / 1000 / 60 / 60);
            var m = Math.floor(value / 1000 / 60);
            return h + ":" + (m - 60 * h);
        }
        default: return value;
    }
}

function html_path(json_path) {
    return json_path.replace("/json/", "/").replace(".json", ".html");
}

MONTHS = "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" ");

function pretty_date(date) {
    return date.getDate() + " " + MONTHS[date.getMonth()] + " " + date.getFullYear();
}

function pretty_time(date) {
    var minutes = date.getMinutes();
    return date.getHours() + ":" + (minutes < 10 ? "0" : "") + date.getMinutes();
}

function draw_class(node, name, data) {
    var header = node.append("header");

    var g = node.append("svg")
        .attr("width", width + 2 * margin + labels)
        .attr("height", height + 2 * margin + labels)
        .append("g").attr("transform", "translate(" + (margin + labels) + "," + margin + ")");

    var table = node.append("table");

    var x = d3.scaleTime().range([0, width]);
    var y = d3.scaleLinear().range([height, 0]);
    x.domain(d3.extent(data, x => new Date(x[":time"] * 1000)));
    y.domain(d3.extent(data, x => add_fields(["success", "expected"])(x)));
    g.append("g").call(d3.axisLeft().scale(y))
    g.append("g").call(d3.axisBottom().scale(x))
        .attr("transform", "translate(0," + height + ")")

    branches = {}
    for (var i = 0; i < data.length; i++) {
        var array = (branches[data[i][":branch"]] || []);
        array.push(data[i]);
        branches[data[i][":branch"]] = array;
    }
    var key = d3.scaleOrdinal(d3.schemeCategory10).domain(Object.keys(branches));

    header.append("ul").selectAll("li")
        .data(Object.keys(branches)).enter().append("li")
        .style("border-left-width", "1em")
        .style("border-left-color", key)
        .text((x) => x);
    header.append("h1").text(name);

    for (var branch in branches) {
        console.log(name, branch, key(branch))
        if (!branches.hasOwnProperty(branch)) continue;
        g.append("g").selectAll("circle")
            .data(branches[branch]).enter().append("circle")
            .attr("cx", (d) => x(new Date(d[":time"] * 1000)))
            .attr("cy", (d) => y(add_fields(["success", "expected"])(d)))
            .attr("r", 5)
            .attr("fill", key(branch))
            .append("title")
            .text((d) => {
                var s = "";
                var keys = Object.keys(d);
                keys.sort();
                for (var i = 0; i < keys.length; i++) {
                    s += keys[i] + ": " + render_key(keys[i], d[keys[i]]) + "\n";
                }
                return s;
            });
    }

    table.append("thead").selectAll("th")
        .data(["Date", "Time", "Branch", "Passing", "Failures", "Link"]).enter().append("th")
        .text((d) => d);

    table.append("tbody").selectAll("tr")
        .data(data).enter().append("tr")
        .on("click", function() { this.children[this.children.length - 1].children[0].click() })
        .selectAll("td").data(d => [
            pretty_date(new Date(d[":time"] * 1000)),
            pretty_time(new Date(d[":time"] * 1000)),
            "" + d[":branch"] + "",
            add_fields(["success", "expected"])(d) + "/" +
                add_fields(["success", "expected", "timeout", "fail", "unsupported"])(d),
            add_fields(["fail", "error"])(d),
            "»",
        ]).enter().append("td")
        .html(d => d);
}

function relevant_data(data) {
    return data[":time"] > new Date() / 1000 - lookback;
}

function draw(node, data) {
    for (var i in data) {
        if (!data.hasOwnProperty(i)) continue;
        var class_data = data[i].sort((a, b) => b[":time"] - a[":time"]).slice(0, 30);
        if (class_data.length > 0 && relevant_data(class_data[0])) {
            draw_class(node.append("section").attr("data-suite", i), i, class_data);
        }
    }
}
<|endoftext|>


<commit_before>// Avoid `console` errors in browsers that lack a console.
(function() {
    var method;
    var noop = function () {};
    var methods = [
        'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error',
        'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log',
        'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd',
        'timeline', 'timelineEnd', 'timeStamp', 'trace', 'warn'
    ];
    var length = methods.length;
    var console = (window.console = window.console || {});

    while (length--) {
        method = methods[length];

        // Only stub undefined methods.
        if (!console[method]) {
            console[method] = noop;
        }
    }
}());

// ------------------------------------------------------------Viewport Size
$(document).ready(function() {showViewportSize();});
$(window).resize(function() {showViewportSize();});
function showViewportSize() {
    var the_width = $(window).width();
    var the_height = $(window).height();
    $('#width').text(the_width);
    $('#height').text(the_height);
}

// ------------------------------------------------------------Back To Top
// Each (jQuery) could be replaced with a ($)
jQuery(document).ready(function() {
    var offset = 500;
    var duration = 200;
    jQuery(window).scroll(function() {
        if (jQuery(this).scrollTop() > offset) {
            jQuery('#backToTop').fadeIn(duration);
        } else {
            jQuery('#backToTop').fadeOut(duration);
        }
    });

    jQuery('#backToTop').click(function(event) {
        event.preventDefault();
        jQuery('html, body').animate({
            scrollTop: 0
        }, duration);
        return false;
    })
});
<commit_msg>js/plugins.js : rm<commit_after><|endoftext|>


<commit_before>var THREE = require("./three64.js");    // Modified version to use 64-bit double precision floats for matrix math
var ThreeboxConstants = require("./constants.js");
var CameraSync = require("./Camera/CameraSync.js");
var utils = require("./Utils/Utils.js");
var AnimationManager = require("./Animation/AnimationManager.js");

function Threebox(map){
    this.map = map;

    // Set up a THREE.js scene
    this.renderer = new THREE.WebGLRenderer( { alpha: true, antialias:true} );
    this.renderer.setSize( this.map.transform.width, this.map.transform.height );
    this.renderer.shadowMap.enabled = true;

    this.map._container.appendChild( this.renderer.domElement );
    this.renderer.domElement.style["position"] = "relative";
    this.renderer.domElement.style["pointer-events"] = "none";
    this.renderer.domElement.style["z-index"] = 1000;

    var _this = this;
    this.map.on("resize", function() { _this.renderer.setSize(_this.map.transform.width, _this.map.transform.height); } );

    this.scene = new THREE.Scene();
    this.camera = new THREE.PerspectiveCamera( 28, window.innerWidth / window.innerHeight, 0.000001, 5000000000);

    // The CameraSync object will keep the Mapbox and THREE.js camera movements in sync.
    // It requires a world group to scale as we zoom in. Rotation is handled in the camera's
    // projection matrix itself (as is field of view and near/far clipping)
    // It automatically registers to listen for move events on the map so we don't need to do that here
    this.world = new THREE.Group();
    this.scene.add(this.world);
    this.cameraSynchronizer = new CameraSync(this.map, this.camera, this.world);

    this.animationManager = new AnimationManager();
    this.update();
}

Threebox.prototype = {
    update: function(timestamp) {
        // Update any animations
        this.animationManager.update(timestamp);

        // Render the scene
        this.renderer.render( this.scene, this.camera );

        // Run this again next frame
        var thisthis = this;
        requestAnimationFrame(function(timestamp) { thisthis.update(timestamp); } );
    },

    projectToWorld: function (coords){
        // Spherical mercator forward projection, re-scaling to WORLD_SIZE
        var projected = [
             ThreeboxConstants.MERCATOR_A * coords[0] * ThreeboxConstants.DEG2RAD * ThreeboxConstants.PROJECTION_WORLD_SIZE,
            -ThreeboxConstants.MERCATOR_A * Math.log(Math.tan((Math.PI*0.25) + (0.5 * coords[1] * ThreeboxConstants.DEG2RAD))) * ThreeboxConstants.PROJECTION_WORLD_SIZE
        ];

        var pixelsPerMeter = this.projectedUnitsPerMeter(coords[1]);

        //z dimension
        var height = coords[2] || 0;
        projected.push( height * pixelsPerMeter );

        var result = new THREE.Vector3(projected[0], projected[1], projected[2]);

        return result;
    },
    projectedUnitsPerMeter: function(latitude) {
        return Math.abs(ThreeboxConstants.WORLD_SIZE * Math.cos(latitude*Math.PI/180)/ThreeboxConstants.EARTH_CIRCUMFERENCE);
    },
    _scaleVerticesToMeters: function(centerLatLng, vertices) {
        var pixelsPerMeter = this.projectedUnitsPerMeter(centerLatLng[1]);
        var centerProjected = this.projectToWorld(centerLatLng);

        for (var i = 0; i < vertices.length; i++) {
            vertices[i].multiplyScalar(pixelsPerMeter);
        }

        return vertices;
    },
    projectToScreen: function(coords) {
        console.log("WARNING: Projecting to screen coordinates is not yet implemented");
    },
    unprojectFromScreen: function (pixel) {
        console.log("WARNING: unproject is not yet implemented");
    },
    unprojectFromWorld: function (pixel) {

        var unprojected = [
            pixel.x / (ThreeboxConstants.MERCATOR_A * ThreeboxConstants.DEG2RAD * ThreeboxConstants.PROJECTION_WORLD_SIZE),
            pixel.y * 2 / (ThreeboxConstants.DEG2RAD * ThreeboxConstants.PROJECTION_WORLD_SIZE) + ThreeboxConstants.MERCATOR_A * Math.log(Math.tan((Math.PI*0.25)
        ];

        var pixelsPerMeter = this.projectedUnitsPerMeter(coords[1]);

        //z dimension
        var height = pixel.z || 0;
        unprojected.push( height / pixelsPerMeter );

        return unprojected;
    },

    addAtCoordinate: function(obj, lnglat, options) {
        /* Place the given object on the map, centered around the provided longitude and latitude
            The object's internal coordinates are assumed to be in meter-offset format, meaning
            1 unit represents 1 meter distance away from the provided coordinate.

            TODO: detect object type and actually do the meter-offset calculations for meshes
        */

        obj.position.copy(this.projectToWorld(lnglat));

        // Re-project mesh coordinates to mercator meters
        var v;
        console.time("Project coords");
        this._scaleVerticesToMeters(lnglat, obj.geometry.vertices);
        console.timeEnd("Project coords");

        console.log(obj.position);

        obj.coordinates = lnglat;

        this.world.add(obj);

        // Bestow this mesh with animation superpowers and keeps track of its mvoements in the global animation queue
        this.animationManager.enroll(obj);

        return obj;
    },

    addGeoreferencedMesh: function(mesh, options) {
        /* Place the mesh on the map, assuming its internal (x,y) coordinates are already in (longitude, latitude) format
            TODO:
        */

    },

    remove: function(obj) {
        this.world.remove(obj);
    },

    setupDefaultLights: function() {
        this.scene.add( new THREE.AmbientLight( 0x999999 ) );

        var lights = [];
        lights[ 0 ] = new THREE.PointLight( 0x999999, 1, 0 );
        lights[ 1 ] = new THREE.PointLight( 0x999999, 1, 0 );
        lights[ 2 ] = new THREE.PointLight( 0x999999, 1, 0 );

        lights[ 0 ].position.set( 0, 200, 0 );
        lights[ 1 ].position.set( 100, 200, 100 );
        lights[ 2 ].position.set( -100, -200, -100 );

        //scene.add( lights[ 0 ] );
        this.scene.add( lights[ 1 ] );
        this.scene.add( lights[ 2 ] );
    }
}

module.exports = exports = Threebox;

<commit_msg>yep, definitely goofed<commit_after>var THREE = require("./three64.js");    // Modified version to use 64-bit double precision floats for matrix math
var ThreeboxConstants = require("./constants.js");
var CameraSync = require("./Camera/CameraSync.js");
var utils = require("./Utils/Utils.js");
var AnimationManager = require("./Animation/AnimationManager.js");

function Threebox(map){
    this.map = map;

    // Set up a THREE.js scene
    this.renderer = new THREE.WebGLRenderer( { alpha: true, antialias:true} );
    this.renderer.setSize( this.map.transform.width, this.map.transform.height );
    this.renderer.shadowMap.enabled = true;

    this.map._container.appendChild( this.renderer.domElement );
    this.renderer.domElement.style["position"] = "relative";
    this.renderer.domElement.style["pointer-events"] = "none";
    this.renderer.domElement.style["z-index"] = 1000;

    var _this = this;
    this.map.on("resize", function() { _this.renderer.setSize(_this.map.transform.width, _this.map.transform.height); } );

    this.scene = new THREE.Scene();
    this.camera = new THREE.PerspectiveCamera( 28, window.innerWidth / window.innerHeight, 0.000001, 5000000000);

    // The CameraSync object will keep the Mapbox and THREE.js camera movements in sync.
    // It requires a world group to scale as we zoom in. Rotation is handled in the camera's
    // projection matrix itself (as is field of view and near/far clipping)
    // It automatically registers to listen for move events on the map so we don't need to do that here
    this.world = new THREE.Group();
    this.scene.add(this.world);
    this.cameraSynchronizer = new CameraSync(this.map, this.camera, this.world);

    this.animationManager = new AnimationManager();
    this.update();
}

Threebox.prototype = {
    update: function(timestamp) {
        // Update any animations
        this.animationManager.update(timestamp);

        // Render the scene
        this.renderer.render( this.scene, this.camera );

        // Run this again next frame
        var thisthis = this;
        requestAnimationFrame(function(timestamp) { thisthis.update(timestamp); } );
    },

    projectToWorld: function (coords){
        // Spherical mercator forward projection, re-scaling to WORLD_SIZE
        var projected = [
             ThreeboxConstants.MERCATOR_A * coords[0] * ThreeboxConstants.DEG2RAD * ThreeboxConstants.PROJECTION_WORLD_SIZE,
            -ThreeboxConstants.MERCATOR_A * Math.log(Math.tan((Math.PI*0.25) + (0.5 * coords[1] * ThreeboxConstants.DEG2RAD))) * ThreeboxConstants.PROJECTION_WORLD_SIZE
        ];

        var pixelsPerMeter = this.projectedUnitsPerMeter(coords[1]);

        //z dimension
        var height = coords[2] || 0;
        projected.push( height * pixelsPerMeter );

        var result = new THREE.Vector3(projected[0], projected[1], projected[2]);

        return result;
    },
    projectedUnitsPerMeter: function(latitude) {
        return Math.abs(ThreeboxConstants.WORLD_SIZE * Math.cos(latitude*Math.PI/180)/ThreeboxConstants.EARTH_CIRCUMFERENCE);
    },
    _scaleVerticesToMeters: function(centerLatLng, vertices) {
        var pixelsPerMeter = this.projectedUnitsPerMeter(centerLatLng[1]);
        var centerProjected = this.projectToWorld(centerLatLng);

        for (var i = 0; i < vertices.length; i++) {
            vertices[i].multiplyScalar(pixelsPerMeter);
        }

        return vertices;
    },
    projectToScreen: function(coords) {
        console.log("WARNING: Projecting to screen coordinates is not yet implemented");
    },
    unprojectFromScreen: function (pixel) {
        console.log("WARNING: unproject is not yet implemented");
    },
    unprojectFromWorld: function (pixel) {

        var unprojected = [
            pixel.x / (ThreeboxConstants.MERCATOR_A * ThreeboxConstants.DEG2RAD * ThreeboxConstants.PROJECTION_WORLD_SIZE),
            2*(Math.atan(Math.exp(pixel.y/(ThreeboxConstants.PROJECTION_WORLD_SIZE*(-ThreeboxConstants.MERCATOR_A))))-Math.PI/4)/ThreeboxConstants.DEG2RAD
        ];

        var pixelsPerMeter = this.projectedUnitsPerMeter(unprojected[1]);

        //z dimension
        var height = pixel.z || 0;
        unprojected.push( height / pixelsPerMeter );

        return unprojected;
    },

    addAtCoordinate: function(obj, lnglat, options) {
        /* Place the given object on the map, centered around the provided longitude and latitude
            The object's internal coordinates are assumed to be in meter-offset format, meaning
            1 unit represents 1 meter distance away from the provided coordinate.

            TODO: detect object type and actually do the meter-offset calculations for meshes
        */

        obj.position.copy(this.projectToWorld(lnglat));

        // Re-project mesh coordinates to mercator meters
        var v;
        console.time("Project coords");
        this._scaleVerticesToMeters(lnglat, obj.geometry.vertices);
        console.timeEnd("Project coords");

        console.log(obj.position);

        obj.coordinates = lnglat;

        this.world.add(obj);

        // Bestow this mesh with animation superpowers and keeps track of its mvoements in the global animation queue
        this.animationManager.enroll(obj);

        return obj;
    },

    addGeoreferencedMesh: function(mesh, options) {
        /* Place the mesh on the map, assuming its internal (x,y) coordinates are already in (longitude, latitude) format
            TODO:
        */

    },

    remove: function(obj) {
        this.world.remove(obj);
    },

    setupDefaultLights: function() {
        this.scene.add( new THREE.AmbientLight( 0x999999 ) );

        var lights = [];
        lights[ 0 ] = new THREE.PointLight( 0x999999, 1, 0 );
        lights[ 1 ] = new THREE.PointLight( 0x999999, 1, 0 );
        lights[ 2 ] = new THREE.PointLight( 0x999999, 1, 0 );

        lights[ 0 ].position.set( 0, 200, 0 );
        lights[ 1 ].position.set( 100, 200, 100 );
        lights[ 2 ].position.set( -100, -200, -100 );

        //scene.add( lights[ 0 ] );
        this.scene.add( lights[ 1 ] );
        this.scene.add( lights[ 2 ] );
    }
}

module.exports = exports = Threebox;

<|endoftext|>