<commit_before>
var moment = require('moment');
var query = require('../utils/query'),
    sql = require('../utils/sql'),
    template = require('../utils/template');

exports.query = function (args) {
    var view = args.config,
        table = view.table,
        columns = view.columns;

    var names = [],
        joins = [];
    for (var i=0; i < columns.length; i++) {
        if (!columns[i].listview.show) continue;
        if (columns[i].oneToMany) {
            var ref = columns[i].oneToMany,
                cols = sql.fullNames(ref.table, ref.columns);
            names.push(sql.group(sql.cast(cols), columns[i].name));
            joins.push(sql.joins(table.name, columns[i].name, ref.table, ref.pk));
        }
        else if (columns[i].manyToMany) {
            var ref = columns[i].manyToMany.ref,
                cols = sql.fullNames(ref.table, ref.columns);

            names.push(sql.group(sql.cast(cols), columns[i].name));

            var link = columns[i].manyToMany.link;
            joins.push(sql.joins(table.name, table.pk, link.table, link.parentPk));
            joins.push(sql.joins(link.table, link.childPk, ref.table, ref.pk));
        }
        else {
            names.push(sql.fullName(table.name, columns[i].name));
        }
    }
    names.unshift(sql.pk(table.name, table.pk));

    var where = statement(table, columns, args.filter);

    // always group by pk inside the listview!
    var group = sql.groupby(sql.fullName(table.name, table.pk));

    var order = '';
    if (args.filter.order && args.filter.order != 'NULL') {
        for (var i=0; i < columns.length; i++) {
            if (columns[i].name == args.filter.order) {
                var direction = args.filter.direction || 'asc';
                var column = (columns[i].oneToMany || columns[i].manyToMany)
                    ? args.filter.order
                    : sql.fullName(table.name, args.filter.order);
                order = [column, direction].join(' ');
                break;
            }
        }
    }
    else if (!Object.keys(view.listview.order).length) {
        order = sql.fullName(table.name, table.pk) + ' asc';
    }
    else {
        order = sql.order(table.name, view.listview.order).join();
    }

    var from = args.page ? (args.page-1)*view.listview.page : 0;

    args.statements = {
        columns: names.join(), table: table.name, join: joins.join(' '),
        where: where, group: group, order: order,
        from: from, to: view.listview.page
    }
    return query.replace('list-view',
        /*columns*/names.join(), table.name,
        joins.join(' '), where, group, order, from, view.listview.page);
}

exports.data = function (args, cb) {
    var columns = args.config.columns;

    var visible = [],
        names = [];
    for (var i=0; i < columns.length; i++) {
        if (!columns[i].listview.show) continue;
        visible.push(columns[i]);
        names.push(columns[i].verbose);
    }
    columns = visible;

    args.db.client.query(args.query, function (err, rows) {
        if (err) return cb(err);
        var records = [];
        for (var i=0; i < rows.length; i++) {
            var pk = {
                id: rows[i]['__pk'],
                text: rows[i][columns[0].name]
            };
            var values = [];
            for (var j=1; j < columns.length; j++) {
                var value = rows[i][columns[j].name];
                value = format(columns[j], value);
                if (columns[j].manyToMany) value = {mtm: value.split(',')}
                values.push(value);
            }
            records.push({pk: pk, values: values});
        }
        cb(null, {columns: names, records: records});
    });
}

function statement (table, columns, filter) {
    var statements = [];
    for (var i=0; i < columns.length; i++) {
        var column = columns[i],
            value = filter.columns[column.name];
        if (!value) continue;
        var clause = sql.where(column.control, value);
        if (!clause) continue;
        var name = '';
        if (column.manyToMany) {
            var ref = column.manyToMany.ref;
            name = sql.fullName(ref.table, ref.pk);
        } else {
            name = sql.fullName(table.name, column.name);
        }
        statements.push([name, clause].join(' '));
    }
    return statements.length ? ' WHERE ' + statements.join(' AND ') : '';
}

function format (column, value) {
    if (/^(date|datetime|timestamp).*/i.test(column.type)) {
        return value ? moment(value).format('ddd MMM DD YYYY') : '';

    } else if (column.control.radio) {
        // mysql
        if (typeof value === 'number') {
            return (value == 1) // flip
                ? column.control.options[0]
                : column.control.options[1];
        }
        // pg
        if (typeof value === 'boolean') {
            return (value == true) // flip
                ? column.control.options[0]
                : column.control.options[1];
        }
    }
    return value
}
<commit_msg>Show full time on datatime columns inside the Listview<commit_after>
var moment = require('moment');
var query = require('../utils/query'),
    sql = require('../utils/sql'),
    template = require('../utils/template');

exports.query = function (args) {
    var view = args.config,
        table = view.table,
        columns = view.columns;

    var names = [],
        joins = [];
    for (var i=0; i < columns.length; i++) {
        if (!columns[i].listview.show) continue;
        if (columns[i].oneToMany) {
            var ref = columns[i].oneToMany,
                cols = sql.fullNames(ref.table, ref.columns);
            names.push(sql.group(sql.cast(cols), columns[i].name));
            joins.push(sql.joins(table.name, columns[i].name, ref.table, ref.pk));
        }
        else if (columns[i].manyToMany) {
            var ref = columns[i].manyToMany.ref,
                cols = sql.fullNames(ref.table, ref.columns);

            names.push(sql.group(sql.cast(cols), columns[i].name));

            var link = columns[i].manyToMany.link;
            joins.push(sql.joins(table.name, table.pk, link.table, link.parentPk));
            joins.push(sql.joins(link.table, link.childPk, ref.table, ref.pk));
        }
        else {
            names.push(sql.fullName(table.name, columns[i].name));
        }
    }
    names.unshift(sql.pk(table.name, table.pk));

    var where = statement(table, columns, args.filter);

    // always group by pk inside the listview!
    var group = sql.groupby(sql.fullName(table.name, table.pk));

    var order = '';
    if (args.filter.order && args.filter.order != 'NULL') {
        for (var i=0; i < columns.length; i++) {
            if (columns[i].name == args.filter.order) {
                var direction = args.filter.direction || 'asc';
                var column = (columns[i].oneToMany || columns[i].manyToMany)
                    ? args.filter.order
                    : sql.fullName(table.name, args.filter.order);
                order = [column, direction].join(' ');
                break;
            }
        }
    }
    else if (!Object.keys(view.listview.order).length) {
        order = sql.fullName(table.name, table.pk) + ' asc';
    }
    else {
        order = sql.order(table.name, view.listview.order).join();
    }

    var from = args.page ? (args.page-1)*view.listview.page : 0;

    args.statements = {
        columns: names.join(), table: table.name, join: joins.join(' '),
        where: where, group: group, order: order,
        from: from, to: view.listview.page
    }
    return query.replace('list-view',
        /*columns*/names.join(), table.name,
        joins.join(' '), where, group, order, from, view.listview.page);
}

exports.data = function (args, cb) {
    var columns = args.config.columns;

    var visible = [],
        names = [];
    for (var i=0; i < columns.length; i++) {
        if (!columns[i].listview.show) continue;
        visible.push(columns[i]);
        names.push(columns[i].verbose);
    }
    columns = visible;

    args.db.client.query(args.query, function (err, rows) {
        if (err) return cb(err);
        var records = [];
        for (var i=0; i < rows.length; i++) {
            var pk = {
                id: rows[i]['__pk'],
                text: rows[i][columns[0].name]
            };
            var values = [];
            for (var j=1; j < columns.length; j++) {
                var value = rows[i][columns[j].name];
                value = format(columns[j], value);
                if (columns[j].manyToMany) value = {mtm: value.split(',')}
                values.push(value);
            }
            records.push({pk: pk, values: values});
        }
        cb(null, {columns: names, records: records});
    });
}

function statement (table, columns, filter) {
    var statements = [];
    for (var i=0; i < columns.length; i++) {
        var column = columns[i],
            value = filter.columns[column.name];
        if (!value) continue;
        var clause = sql.where(column.control, value);
        if (!clause) continue;
        var name = '';
        if (column.manyToMany) {
            var ref = column.manyToMany.ref;
            name = sql.fullName(ref.table, ref.pk);
        } else {
            name = sql.fullName(table.name, column.name);
        }
        statements.push([name, clause].join(' '));
    }
    return statements.length ? ' WHERE ' + statements.join(' AND ') : '';
}

function format (column, value) {
    if (/^(datetime|timestamp).*/i.test(column.type)) {
        return value ? moment(value).format('ddd MMM DD YYYY hh:mm:ss') : '';
    }
    else if (/^date.*/i.test(column.type)) {
        return value ? moment(value).format('ddd MMM DD YYYY') : '';
    }
    else if (column.control.radio) {
        // mysql
        if (typeof value === 'number') {
            return (value == 1) // flip
                ? column.control.options[0]
                : column.control.options[1];
        }
        // pg
        if (typeof value === 'boolean') {
            return (value == true) // flip
                ? column.control.options[0]
                : column.control.options[1];
        }
    }
    return value
}
<|endoftext|>


<commit_before>/**
 * Created by nuintun on 2015/10/26.
 */

'use strict';

module.exports = {
  params: require('./params'),
  selector: require('./selector'),
  declaration: require('./declaration')
};
<commit_msg>update files<commit_after>/**
 * Created by nuintun on 2015/10/26.
 */

'use strict';

/**
 * exports module.
 */
module.exports = {
  params: require('./params'),
  selector: require('./selector'),
  declaration: require('./declaration')
};
<|endoftext|>


<commit_before>function DatabaseBackend(name, options) {
  this._name = name;
  this._options = options;
}

/**
 * Return an array of peer dictionaries for the provided info_hash (torrent)
 */
DatabaseBackend.prototype.getPeersByInfoHash = function(infoHash, noPeerId, compact,
                                                        callback) {
  throw new Error('Not implemented');
};

DatabaseBackend.prototype.addAnnonceEvent = function() {
  throw new Error('Not implemented');
};

DatabaseBackend.prototype.addStartedEvent = function() {
  throw new Error('Not implemented');
};

DatabaseBackend.prototype.addStoppedEvent = function() {
  throw new Error('Not implemented');
};

DatabaseBackend.prototype.addCompletedEvent = function() {
  throw new Error('Not implemented');
};

exports.DatabaseBackend = DatabaseBackend;
<commit_msg>Update method signatures.<commit_after>function DatabaseBackend(name, options) {
  this._name = name;
  this._options = options;
}

/**
 * Dump the whole DB.
 */
DatabaseBackend.prototype.dumpData = function(callback) {
  throw new Error('Not implemented');
};

/**
 * Return an array of peer dictionaries for the provided info_hash (torrent)
 */
DatabaseBackend.prototype.getPeersByInfoHash = function(infoHash, noPeerId, compact,
                                                        callback) {
  throw new Error('Not implemented');
};

DatabaseBackend.prototype.addAnnonceEvent = function(infoHash, peerInfo, uploaded,
                                                     downloaded, left, callback) {
  throw new Error('Not implemented');
};

DatabaseBackend.prototype.addStartedEvent = function(infoHash, peerInfo, callback) {
  throw new Error('Not implemented');
};

DatabaseBackend.prototype.addStoppedEvent = function(infoHash, peerInfo, callback) {
  throw new Error('Not implemented');
};

DatabaseBackend.prototype.addCompletedEvent = function(infoHash, peerInfo, callback) {
  throw new Error('Not implemented');
};

exports.DatabaseBackend = DatabaseBackend;
<|endoftext|>


<commit_before>'use babel';

import { CompositeDisposable } from 'atom';
import request from 'request'
import SolarCalc from 'solar-calc'
import _ from 'underscore-plus';
import MyConfig from './config-schema.json';

// Use raw API rather than navigator due to unavailability in Atom
const GEOLOCATION_API = 'https://maps.googleapis.com/maps/api/browserlocation/json?browser=chromium&sensor=true'

// Global variables to be accessed and checked from functions
var day, sunset, sunrise, daytime_themes, nighttime_themes, lat, lng

// Attempting to resolve "_this" issue
var _this = this

// From https://github.com/qwtel/theme-flux-solar
// Get a human readable title for the given theme name.
function getThemeTitle(themeName = '') {
  const title = themeName.replace(/-(ui|syntax)/g, '').replace(/-theme$/g, '');
  return _.undasherize(_.uncamelcase(title));
}

function themeToConfigStringEnum({ metadata: { name } }) {
  return {
    value: name,
    description: getThemeTitle(name),
  };
}

// From https://github.com/as-cii/theme-flux/blob/master/lib/theme-flux.js
const loadedThemes = atom.themes.getLoadedThemes();

const uiThemesEnum = loadedThemes
  .filter(theme => theme.metadata.theme === 'ui')
  .map(themeToConfigStringEnum);

const syntaxThemesEnum = loadedThemes
  .filter(theme => theme.metadata.theme === 'syntax')
  .map(themeToConfigStringEnum);

export default {

  config: MyConfig,

  subscriptions: null,

  activate(state) {

    _this.setup()

    // TODO: Allow user configurable refresh interval
    // TODO: Done, I think? Maybe check.
    interval = atom.config.get('day-and-night.Activation.interval')
    tock = setInterval(_this.tick, interval * 60 * 1000)

    // Enumerate the theme options and set in config page
    _this.config.Appearance.properties.daytime_syntax_theme.enum =
      _this.config.Appearance.properties.nighttime_syntax_theme.enum =
        syntaxThemesEnum
    _this.config.Appearance.properties.daytime_ui_theme.enum =
      _this.config.Appearance.properties.nighttime_ui_theme.enum =
        uiThemesEnum

    // Calculate the solar times
    _this.sunTimes();

    // Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
    _this.subscriptions = new CompositeDisposable();

    // Register command that toggles
    _this.subscriptions.add(atom.commands.add('atom-workspace', {
      'day-and-night:toggle': () => _this.toggle()
    }));

    _this.subscriptions.add(_this.subscribeToConfigChanges());

    _this.tick()

  },

  // From: https://github.com/Haacked/encourage-atom/blob/master/lib/encourage.js
  subscribeToConfigChanges() {
    const subscriptions = new CompositeDisposable();

    const appearanceObserver = atom.config.observe(
      'day-and-night.Appearance',
      (value) => {
        _this.setup()
        _this.tick()
      });
    subscriptions.add(appearanceObserver);

    const intervalObserver = atom.config.observe(
      'day-and-night.Activation.interval',
      (value) => {
        interval = atom.config.get('day-and-night.Activation.interval')
        clearInterval(tock)
        tock = setInterval(_this.tick, interval * 60 * 1000)
      });
    subscriptions.add(intervalObserver);

    return subscriptions;
  },

  deactivate() {
    _this.subscriptions.dispose();
  },

  download(url) {
    return new Promise((resolve, reject) => {
      request(url, (error, response, body) => {
        if (!error && response.statusCode == 200) {
          resolve(body)
        } else {
          reject({
            reason: 'Unable to download page'
          })
        }
      })
    })
  },

  //  Run general setup functions
  setup(){
    // Save the theme settings so aren't reloaded each time
    daytime_themes = [
      atom.config.get('day-and-night.Appearance.daytime_ui_theme'),
      atom.config.get('day-and-night.Appearance.daytime_syntax_theme')
    ]
    nighttime_themes = [
      atom.config.get('day-and-night.Appearance.nighttime_ui_theme'),
      atom.config.get('day-and-night.Appearance.nighttime_syntax_theme')
    ]
  },

  serialize(){
    return
  },

  // Get the sunrise and sunset times for today and user defined location
  sunTimes() {

    // Get the current date
    now = new Date()
    // Day is also saved and used to check if new solar times are needed
    day = now.getDate()

    // Get the GPS config coordinates from config as backup
    lat = atom.config.get('day-and-night.Location.latitude')
    lng = atom.config.get('day-and-night.Location.longitude')

    // Use Google geolocation API to determine sunrise and sunset
    _this.download(GEOLOCATION_API).then((html) => {
        json = JSON.parse(html);
        console.log(json)
        lat = json.location.lat
        lng = json.location.lng
        // If update is set in config then update the location config
        if (atom.config.get('day-and-night.Location.update')){
          console.log("Updating location config")
          atom.config.set('day-and-night.Location.latitude', lat)
          atom.config.set('day-and-night.Location.longitude', lng)
        }

        // Use the NPM solar-calc package to determine sunrise and sunset
        var solar = new SolarCalc(now,lat,lng);
        sunrise = solar.sunrise
        sunset = solar.sunset
      }).catch((error) => {
        // If there's an error then throw warning
        atom.notifications.addWarning(error.reason)
      })
  },

  // Legacy, for debugging
  toggle() {
    _this.setup()
    _this.tick()
  },

  tick() {
    console.log("tick")
    // If the plugin has not been configured then exit, relies on user config
    if (!atom.config.get('day-and-night.Activation.configured')){
      return
    }
    // Get the current date and time
    now = new Date()
    // Check if the day has changed since sun times last retrieved
    if (now.getDate() != day){
      console.log("Day changed!")
      _this.sunTimes()
    }

    // Do theme changing stuff
    current_themes = atom.config.get('core.themes')
    // Apparently copying the array
    themes = current_themes.slice()
    if (now >= sunset) {
      themes = nighttime_themes
    }
    else if (now >= sunrise) {
      themes = daytime_themes
    }

    // If there is a change to the themes, then update the config
    if (themes != current_themes){
      atom.config.set('core.themes', themes)
    }

  }

};
<commit_msg>Reverted change to fix "this", did not fix<commit_after>'use babel';

import { CompositeDisposable } from 'atom';
import request from 'request'
import SolarCalc from 'solar-calc'
import _ from 'underscore-plus';
import MyConfig from './config-schema.json';

// Use raw API rather than navigator due to unavailability in Atom
const GEOLOCATION_API = 'https://maps.googleapis.com/maps/api/browserlocation/json?browser=chromium&sensor=true'

// Global variables to be accessed and checked from functions
var day, sunset, sunrise, daytime_themes, nighttime_themes, lat, lng

// Attempting to resolve "this" issue
// var this = this

// From https://github.com/qwtel/theme-flux-solar
// Get a human readable title for the given theme name.
function getThemeTitle(themeName = '') {
  const title = themeName.replace(/-(ui|syntax)/g, '').replace(/-theme$/g, '');
  return _.undasherize(_.uncamelcase(title));
}

function themeToConfigStringEnum({ metadata: { name } }) {
  return {
    value: name,
    description: getThemeTitle(name),
  };
}

// From https://github.com/as-cii/theme-flux/blob/master/lib/theme-flux.js
const loadedThemes = atom.themes.getLoadedThemes();

const uiThemesEnum = loadedThemes
  .filter(theme => theme.metadata.theme === 'ui')
  .map(themeToConfigStringEnum);

const syntaxThemesEnum = loadedThemes
  .filter(theme => theme.metadata.theme === 'syntax')
  .map(themeToConfigStringEnum);

export default {

  config: MyConfig,

  subscriptions: null,

  activate(state) {

    this.setup()

    // TODO: Allow user configurable refresh interval
    // TODO: Done, I think? Maybe check.
    interval = atom.config.get('day-and-night.Activation.interval')
    tock = setInterval(this.tick, interval * 60 * 1000)

    // Enumerate the theme options and set in config page
    this.config.Appearance.properties.daytime_syntax_theme.enum =
      this.config.Appearance.properties.nighttime_syntax_theme.enum =
        syntaxThemesEnum
    this.config.Appearance.properties.daytime_ui_theme.enum =
      this.config.Appearance.properties.nighttime_ui_theme.enum =
        uiThemesEnum

    // Calculate the solar times
    this.sunTimes();

    // Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
    this.subscriptions = new CompositeDisposable();

    // Register command that toggles
    this.subscriptions.add(atom.commands.add('atom-workspace', {
      'day-and-night:toggle': () => this.toggle()
    }));

    this.subscriptions.add(this.subscribeToConfigChanges());

    this.tick()

  },

  // From: https://github.com/Haacked/encourage-atom/blob/master/lib/encourage.js
  subscribeToConfigChanges() {
    const subscriptions = new CompositeDisposable();

    const appearanceObserver = atom.config.observe(
      'day-and-night.Appearance',
      (value) => {
        this.setup()
        this.tick()
      });
    subscriptions.add(appearanceObserver);

    const intervalObserver = atom.config.observe(
      'day-and-night.Activation.interval',
      (value) => {
        interval = atom.config.get('day-and-night.Activation.interval')
        clearInterval(tock)
        tock = setInterval(this.tick, interval * 60 * 1000)
      });
    subscriptions.add(intervalObserver);

    return subscriptions;
  },

  deactivate() {
    this.subscriptions.dispose();
  },

  download(url) {
    return new Promise((resolve, reject) => {
      request(url, (error, response, body) => {
        if (!error && response.statusCode == 200) {
          resolve(body)
        } else {
          reject({
            reason: 'Unable to download page'
          })
        }
      })
    })
  },

  //  Run general setup functions
  setup(){
    // Save the theme settings so aren't reloaded each time
    daytime_themes = [
      atom.config.get('day-and-night.Appearance.daytime_ui_theme'),
      atom.config.get('day-and-night.Appearance.daytime_syntax_theme')
    ]
    nighttime_themes = [
      atom.config.get('day-and-night.Appearance.nighttime_ui_theme'),
      atom.config.get('day-and-night.Appearance.nighttime_syntax_theme')
    ]
  },

  serialize(){
    return
  },

  // Get the sunrise and sunset times for today and user defined location
  sunTimes() {

    // Get the current date
    now = new Date()
    // Day is also saved and used to check if new solar times are needed
    day = now.getDate()

    // Get the GPS config coordinates from config as backup
    lat = atom.config.get('day-and-night.Location.latitude')
    lng = atom.config.get('day-and-night.Location.longitude')

    // Use Google geolocation API to determine sunrise and sunset
    this.download(GEOLOCATION_API).then((html) => {
        json = JSON.parse(html);
        console.log(json)
        lat = json.location.lat
        lng = json.location.lng
        // If update is set in config then update the location config
        if (atom.config.get('day-and-night.Location.update')){
          console.log("Updating location config")
          atom.config.set('day-and-night.Location.latitude', lat)
          atom.config.set('day-and-night.Location.longitude', lng)
        }

        // Use the NPM solar-calc package to determine sunrise and sunset
        var solar = new SolarCalc(now,lat,lng);
        sunrise = solar.sunrise
        sunset = solar.sunset
      }).catch((error) => {
        // If there's an error then throw warning
        atom.notifications.addWarning(error.reason)
      })
  },

  // Legacy, for debugging
  toggle() {
    this.setup()
    this.tick()
  },

  tick() {
    console.log("tick")
    // If the plugin has not been configured then exit, relies on user config
    if (!atom.config.get('day-and-night.Activation.configured')){
      return
    }
    // Get the current date and time
    now = new Date()
    // Check if the day has changed since sun times last retrieved
    if (now.getDate() != day){
      console.log("Day changed!")
      this.sunTimes()
    }

    // Do theme changing stuff
    current_themes = atom.config.get('core.themes')
    // Apparently copying the array
    themes = current_themes.slice()
    if (now >= sunset) {
      themes = nighttime_themes
    }
    else if (now >= sunrise) {
      themes = daytime_themes
    }

    // If there is a change to the themes, then update the config
    if (themes != current_themes){
      atom.config.set('core.themes', themes)
    }

  }

};
<|endoftext|>


<commit_before>/*
  Copyright 2015 Google Inc. All Rights Reserved.
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at
      http://www.apache.org/licenses/LICENSE-2.0
  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/
var Promise = require('bluebird');
var fs = Promise.promisifyAll(require('fs'));
var spawn = require('child_process').spawn;
var exec = Promise.promisify(require('child_process').exec);
var http = require('http');
var path = require('path');
var expandHomeDir = require('expand-home-dir');

var StringDecoder = require('string_decoder').StringDecoder;

var types = require('../core/types');
var options = require('../core/options');

var phase = require('../core/register').phase;

function expandPath(p) {
  return path.resolve(expandHomeDir(path.normalize(p)));
}

// update PYTHONPATH for all telemetry invocations
if (options.chromium !== undefined) {
  chromium_path = expandPath(options.chromium);
  console.log("chromium path=%s", chromium_path);
  process.env.PYTHONPATH += path.delimiter + chromium_path + '/tools/telemetry';
  process.env.PYTHONPATH += path.delimiter + chromium_path + '/tools';
}

function telemetryTask(pyScript, pyArgs) {
  return getBrowser().then(function(resolveLock) {
    return _telemetryTask(pyScript, pyArgs).then(function(result) {
      resolveLock();
      return result;
    });
  });
}

function _telemetryTask(pyScript, pyArgs) {
  return new Promise(function(resolve, reject) {
    var result = "";
    pyScript = 'telemetry/' + pyScript;
    if (!fs.existsSync(pyScript))
      pyScript = 'node_modules/erlenmeyer/' + pyScript;
    if (!fs.existsSync(pyScript))
      reject("couldn't find erlenmeyer install either locally or in node_modules");
    var task = spawn('python', [pyScript].concat(pyArgs));
    task.stdout.on('data', function(data) { result += data; });
    task.stderr.on('data', function(data) { console.log('stderr: ' + data); });
    task.on('close', function(code) {
      resolve(JSON.parse(result))
    });
  });
}

function BrowserWrapper(url, options, resolve) {
  console.log(options);
  var cmd = ['telemetry/newBrowser.py', '--browser=' + options.browser, '--chromium', chromium_path];
  if (options.perf) {
    cmd.push('--perf');
  }
  cmd.push('--', '--disable-web-security' );
  this.task = spawn('python', cmd);
  this.url = url;
  this.data = undefined;
  this.processResult = this.defaultProcessResult;
  this.task.stdout.on('data', function(data) { this.processResult(data); }.bind(this) );
  this.task.stderr.on('data', function(data) { console.log('stderr: ' + data); });
  this.task.on('close', function(code) { resolve(); });
}

BrowserWrapper.prototype = {
  waitForOK: function() {
    var promise = new Promise(function(resolve, reject) {
      this.processResult = function(data) {
        data = new StringDecoder('utf8').write(data);
        if (data == 'OK') {
          resolve(this);
        } else {
          reject();
        }
        this.processResult = this.defaultProcessResult;
      }
    }.bind(this));
    return promise;
  },
  waitForData: function() {
    var promise = new Promise(function(resolve, reject) {
      this.processResult = function(data) {
        filename = new StringDecoder('utf8').write(data).trim();
        return fs.readFileAsync(filename).then(function (data) {
          this.task.stdin.write('done\n');
          this.data = data;
          resolve(this);
        }.bind(this));
      }
    }.bind(this));
    return promise;
  },
  defaultProcessResult: function() { },
  load: function() {
    this.task.stdin.write("load:" + this.url + '\n');
    return this.waitForOK();
  },
  startTracing: function() {
    this.task.stdin.write('startTracing\n');
    return this.waitForOK();
  },
  endTracing: function() {
    this.task.stdin.write('endTracing\n');
    return this.waitForData();
  },
  close: function() {
    this.task.stdin.write('exit\n');
    return this.data == undefined ? this.url : this.data;
  },
  execJS: function(js) {
    js = '(' + js + ')()';
    this.task.stdin.write('exec:' + js.length + '\n');
    this.task.stdin.write(js);
    return this.waitForData();
  }
}

var browserLock = Promise.resolve();

/*
 * resolveStart fires when the lock is acquired, while resolveEnd fires when
 * it is released. So this method's returned promise gates on lock acquisition,
 * but the new browserLock that this method installs gates on lock release.
 */
function getBrowser() {
  return new Promise(function(resolveStart) {
    var newBrowserLock = new Promise(function(resolveEnd) {
      browserLock.then(function() {
        resolveStart(resolveEnd);
      });
    });
    browserLock = newBrowserLock;
  });
}

function adbPath() {
  var adb = options.adb || 'adb';
  return expandPath(adb);
}

function startADBForwarding(browser) {
  if (browser == 'system')
    return Promise.resolve();
  return exec(adbPath() + ' reverse tcp:8000 tcp:8000');
}

function stopADBForwarding(browser) {
  if (browser == 'system')
    return Promise.resolve();
  return exec(adbPath() + ' reverse --remove tcp:8000');
}

function startServing(data) {
  return http.createServer(function(req, res) {
    res.writeHead(200, {'Content-Type': 'text/html'});
    res.end(data);
  }).listen(8000, '127.0.0.1');
}

function stopServing(server) {
  server.close();
}

function hostedTelemetryTask(data, pyScript, pyArgs, options) {
  return getBrowser().then(function(resolveLock) {
    return startADBForwarding(options.browser).then(function() {
      var server = startServing(data);
      return _telemetryTask(pyScript, pyArgs).then(function(outData) {
        stopServing(server);
        return stopADBForwarding(options.browser).then(function() {
          resolveLock();
          return Promise.resolve(outData);
        });
      });
    });
  });
}

module.exports.fetch = phase({input: types.string, output: types.JSON, arity: '1:1', async: true, parallel: 1},
  function(url) {
    return telemetryTask('save.py', ['--browser=' + this.options.browser, '--', url]);
  },
  {browser: 'system'});

module.exports.fetchWithInlineStyle = phase({input: types.string, output: types.JSON, arity: '1:1', async: true, parallel: 1},
  function(url) {
    return telemetryTask('save-no-style.py', ['--browser=' + this.options.browser, '--', url]);
  },
  {browser: 'system'});

module.exports.traceURL = phase({input: types.string, output: types.JSON, arity: "1:1", async: true, parallel: 1},
  function(url) {
    return telemetryTask('perf.py', ['--browser=' + this.options.browser, '--', url]);
  },
  {browser: 'system'});

module.exports.trace = phase({input: types.string, output: types.JSON, arity: "1:1", async: true, parallel: 1},
    function(html) {
      return hostedTelemetryTask(html, 'perf.py', ['--browser=' + this.options.browser, '--', 'http://localhost:8000'], this.options);
    },
    {browser: 'system'});

module.exports.traceLayout = phase({input: types.string, output: types.JSON, arity: "1:1", async: true, parallel: 1},
    function(html) {
      return hostedTelemetryTask(html, 'perf.py', ['--browser=' + this.options.browser, '--', 'http://localhost:8000', this.options.iterations]);
    },
    {browser: 'system', iterations: 1});

module.exports.browser = phase({input: types.string, output: types.Browser(types.string), arity: "1:1", async: true},
    function(url) {
      var options = this.options;
      return getBrowser().then(function(resolveEnd) {
        return new BrowserWrapper(url, options, resolveEnd);
      });
    },
    {browser: 'android-chromium', perf: false});

function typeVar(s) { return function(v) {
  if (!v[s]) {
    v[s] = types.newTypeVar();
  }
  return v[s];
}; }

function browserTypeVar(s) {
  return function(v) {
    return types.Browser(typeVar(s)(v));
  }; }

module.exports.closeBrowser = phase({input: browserTypeVar('a'), output: typeVar('a'), arity: "1:1"},
    function(browser) {
      return browser.close();
    });

var browserCommands = [{name: 'load', input: types.string, output: types.string},
                       {name: 'startTracing', input: types.string, output: types.string},
                       {name: 'endTracing', input: types.string, output: types.buffer}];
for (var i = 0; i < browserCommands.length; i++) {
  var cmd = browserCommands[i];
  (function(cmd) {
    module.exports[cmd.name] = phase({input: types.Browser(cmd.input), output: types.Browser(cmd.output), arity: "1:1", async: true, parallel: 1},
      function(browser) {
        return browser[cmd.name]();
    });
  })(cmd);
}

<commit_msg>New approach, approved by Doug(TM)<commit_after>/*
  Copyright 2015 Google Inc. All Rights Reserved.
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at
      http://www.apache.org/licenses/LICENSE-2.0
  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/
var Promise = require('bluebird');
var fs = Promise.promisifyAll(require('fs'));
var spawn = require('child_process').spawn;
var exec = Promise.promisify(require('child_process').exec);
var http = require('http');
var path = require('path');
var expandHomeDir = require('expand-home-dir');

var StringDecoder = require('string_decoder').StringDecoder;

var types = require('../core/types');
var options = require('../core/options');

var phase = require('../core/register').phase;

function expandPath(p) {
  return path.resolve(expandHomeDir(path.normalize(p)));
}

// update PYTHONPATH for all telemetry invocations
if (options.chromium !== undefined) {
  chromium_path = expandPath(options.chromium);
  console.log("chromium path=%s", chromium_path);
  process.env.PYTHONPATH += path.delimiter + chromium_path + '/tools/telemetry';
  process.env.PYTHONPATH += path.delimiter + chromium_path + '/tools';
}

function telemetryTask(pyScript, pyArgs) {
  return getBrowser().then(function(resolveLock) {
    return _telemetryTask(pyScript, pyArgs).then(function(result) {
      resolveLock();
      return result;
    });
  });
}

function _telemetryTask(pyScript, pyArgs) {
  return new Promise(function(resolve, reject) {
    var result = "";
    pyScript = path.join(__dirname, '../telemetry/' + pyScript);
    var task = spawn('python', [pyScript].concat(pyArgs));
    task.stdout.on('data', function(data) { result += data; });
    task.stderr.on('data', function(data) { console.log('stderr: ' + data); });
    task.on('close', function(code) {
      resolve(JSON.parse(result))
    });
  });
}

function BrowserWrapper(url, options, resolve) {
  console.log(options);
  var cmd = ['telemetry/newBrowser.py', '--browser=' + options.browser, '--chromium', chromium_path];
  if (options.perf) {
    cmd.push('--perf');
  }
  cmd.push('--', '--disable-web-security' );
  this.task = spawn('python', cmd);
  this.url = url;
  this.data = undefined;
  this.processResult = this.defaultProcessResult;
  this.task.stdout.on('data', function(data) { this.processResult(data); }.bind(this) );
  this.task.stderr.on('data', function(data) { console.log('stderr: ' + data); });
  this.task.on('close', function(code) { resolve(); });
}

BrowserWrapper.prototype = {
  waitForOK: function() {
    var promise = new Promise(function(resolve, reject) {
      this.processResult = function(data) {
        data = new StringDecoder('utf8').write(data);
        if (data == 'OK') {
          resolve(this);
        } else {
          reject();
        }
        this.processResult = this.defaultProcessResult;
      }
    }.bind(this));
    return promise;
  },
  waitForData: function() {
    var promise = new Promise(function(resolve, reject) {
      this.processResult = function(data) {
        filename = new StringDecoder('utf8').write(data).trim();
        return fs.readFileAsync(filename).then(function (data) {
          this.task.stdin.write('done\n');
          this.data = data;
          resolve(this);
        }.bind(this));
      }
    }.bind(this));
    return promise;
  },
  defaultProcessResult: function() { },
  load: function() {
    this.task.stdin.write("load:" + this.url + '\n');
    return this.waitForOK();
  },
  startTracing: function() {
    this.task.stdin.write('startTracing\n');
    return this.waitForOK();
  },
  endTracing: function() {
    this.task.stdin.write('endTracing\n');
    return this.waitForData();
  },
  close: function() {
    this.task.stdin.write('exit\n');
    return this.data == undefined ? this.url : this.data;
  },
  execJS: function(js) {
    js = '(' + js + ')()';
    this.task.stdin.write('exec:' + js.length + '\n');
    this.task.stdin.write(js);
    return this.waitForData();
  }
}

var browserLock = Promise.resolve();

/*
 * resolveStart fires when the lock is acquired, while resolveEnd fires when
 * it is released. So this method's returned promise gates on lock acquisition,
 * but the new browserLock that this method installs gates on lock release.
 */
function getBrowser() {
  return new Promise(function(resolveStart) {
    var newBrowserLock = new Promise(function(resolveEnd) {
      browserLock.then(function() {
        resolveStart(resolveEnd);
      });
    });
    browserLock = newBrowserLock;
  });
}

function adbPath() {
  var adb = options.adb || 'adb';
  return expandPath(adb);
}

function startADBForwarding(browser) {
  if (browser == 'system')
    return Promise.resolve();
  return exec(adbPath() + ' reverse tcp:8000 tcp:8000');
}

function stopADBForwarding(browser) {
  if (browser == 'system')
    return Promise.resolve();
  return exec(adbPath() + ' reverse --remove tcp:8000');
}

function startServing(data) {
  return http.createServer(function(req, res) {
    res.writeHead(200, {'Content-Type': 'text/html'});
    res.end(data);
  }).listen(8000, '127.0.0.1');
}

function stopServing(server) {
  server.close();
}

function hostedTelemetryTask(data, pyScript, pyArgs, options) {
  return getBrowser().then(function(resolveLock) {
    return startADBForwarding(options.browser).then(function() {
      var server = startServing(data);
      return _telemetryTask(pyScript, pyArgs).then(function(outData) {
        stopServing(server);
        return stopADBForwarding(options.browser).then(function() {
          resolveLock();
          return Promise.resolve(outData);
        });
      });
    });
  });
}

module.exports.fetch = phase({input: types.string, output: types.JSON, arity: '1:1', async: true, parallel: 1},
  function(url) {
    return telemetryTask('save.py', ['--browser=' + this.options.browser, '--', url]);
  },
  {browser: 'system'});

module.exports.fetchWithInlineStyle = phase({input: types.string, output: types.JSON, arity: '1:1', async: true, parallel: 1},
  function(url) {
    return telemetryTask('save-no-style.py', ['--browser=' + this.options.browser, '--', url]);
  },
  {browser: 'system'});

module.exports.traceURL = phase({input: types.string, output: types.JSON, arity: "1:1", async: true, parallel: 1},
  function(url) {
    return telemetryTask('perf.py', ['--browser=' + this.options.browser, '--', url]);
  },
  {browser: 'system'});

module.exports.trace = phase({input: types.string, output: types.JSON, arity: "1:1", async: true, parallel: 1},
    function(html) {
      return hostedTelemetryTask(html, 'perf.py', ['--browser=' + this.options.browser, '--', 'http://localhost:8000'], this.options);
    },
    {browser: 'system'});

module.exports.traceLayout = phase({input: types.string, output: types.JSON, arity: "1:1", async: true, parallel: 1},
    function(html) {
      return hostedTelemetryTask(html, 'perf.py', ['--browser=' + this.options.browser, '--', 'http://localhost:8000', this.options.iterations]);
    },
    {browser: 'system', iterations: 1});

module.exports.browser = phase({input: types.string, output: types.Browser(types.string), arity: "1:1", async: true},
    function(url) {
      var options = this.options;
      return getBrowser().then(function(resolveEnd) {
        return new BrowserWrapper(url, options, resolveEnd);
      });
    },
    {browser: 'android-chromium', perf: false});

function typeVar(s) { return function(v) {
  if (!v[s]) {
    v[s] = types.newTypeVar();
  }
  return v[s];
}; }

function browserTypeVar(s) {
  return function(v) {
    return types.Browser(typeVar(s)(v));
  }; }

module.exports.closeBrowser = phase({input: browserTypeVar('a'), output: typeVar('a'), arity: "1:1"},
    function(browser) {
      return browser.close();
    });

var browserCommands = [{name: 'load', input: types.string, output: types.string},
                       {name: 'startTracing', input: types.string, output: types.string},
                       {name: 'endTracing', input: types.string, output: types.buffer}];
for (var i = 0; i < browserCommands.length; i++) {
  var cmd = browserCommands[i];
  (function(cmd) {
    module.exports[cmd.name] = phase({input: types.Browser(cmd.input), output: types.Browser(cmd.output), arity: "1:1", async: true, parallel: 1},
      function(browser) {
        return browser[cmd.name]();
    });
  })(cmd);
}

<|endoftext|>


<commit_before>'use strict';

// #region Imports
var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var express = require('express');
var favicon = require('serve-favicon');
var glob = require('glob');
var lessMw = require('less-mw');
var lodash = require('lodash');
var logger = require('morgan');
var path = require('path');
var sassMw = require('node-sass-middleware');
var StaticBuild = require('../../index.js');
// #endregion

var app = module.exports = express();
/** The target instance of StaticBuild. */
var build;
/** View Engine Modules */
var veng = {
  jade: undefined,
  nunjucks: undefined
};
/** View Renderer Functions */
var vrend = {};

/**
 * Initializes the Express app without running it.
 * @param {StaticBuild} [targetBuild] The target build.
 */
function init(targetBuild) {
  // CONSIDER: The `|| StaticBuild.current` isn't really necessary.
  build = targetBuild || StaticBuild.current;
  initViewEngines();
  // Request Pipeline
  initFavicon();
  initLogging();
  initParsing();
  initCssLESS();
  initSASS();
  initViewRouting();
  initErrorHandling();
}
app.init = init;

function verbose(data) {
  /*jshint unused:false*/
  if (build.verbose)
    console.log.apply(console, arguments);
}

// #region Features

function initFavicon() {
  if (!build.favicon)
    return;
  // TODO: Replace serve-favicon with a module that doesn't have an error when
  // the favicon file doesn't exist.
  // See https://github.com/expressjs/serve-favicon/issues/27
  app.use(favicon(build.resolveSrcPath(build.favicon)));
}

function initLogging() {
  app.use(logger('dev'));
}

function initParsing() {
  app.use(bodyParser.json());
  app.use(bodyParser.urlencoded({ extended: false }));
  app.use(cookieParser());
}

// #endregion

// #region CSS/LESS

function initCssLESS() {
  // Options for less-middleware.
  var opt = {
    debug: false,
    render: {
      compress: false
    }
  };
  var sm;
  if (build.engine.less.map.enabled === true) {
    // Less-middleware passes opt.render data to the less.render function.
    opt.render.sourceMap = sm = {
      sourceMapBasepath: build.sourceDir,
      sourceMapRootpath: '/'
    };
    if (build.engine.less.map.inline === true) {
      // BUG: External map files are still created, even when inline is enabled.
      sm.sourceMapFileInline = true;
      sm.sourceMapLessInline = true;
      sm.sourceMapMapInline = true;
      sm.outputSourceFiles = true;
    } else {
      // HACK: Specifying sourceMapFilename here causes less to put the same
      // sourceMappingURL in every css file, so instead we use a special token
      // and fix the situation during postprocess.css.
      sm.sourceMapFilename = initCssLESS.SOURCEMAP_FILENAME_TOKEN;
      opt.postprocess = {
        css: postprocessCss
      };
    }
  }
  app.use(lessMw(build.sourceDir, opt));
}
initCssLESS.SOURCEMAP_REQUESTPATH_TOKEN = '___req.path___';
initCssLESS.SOURCEMAP_FILENAME_TOKEN =
  initCssLESS.SOURCEMAP_REQUESTPATH_TOKEN + '.map';

function postprocessCss(css, req) {
  if (css && css.replace)
    css = css.replace(initCssLESS.SOURCEMAP_REQUESTPATH_TOKEN, req.path);
  return css;
}

// #endregion

// #region CSS/SASS

function initSASS() {
  var sassMap = build.engine.sass.map;
  var opt = {
    src: build.sourceDir,
    // Recompile on every request.
    force: true,
    // Write the .css file, do not stream it.
    response: false,
    // Configure source maps.
    sourceMap: sassMap.enabled,
    sourceMapEmbed: sassMap.inline
  };
  // Handle the .sass file extension.
  var sassOpt = lodash.assign({ indentedSyntax: true }, opt);
  app.use(sassMw(sassOpt));
  // Handle the .scss file extension.
  var scssOpt = lodash.assign({ indentedSyntax: false }, opt);
  app.use(sassMw(scssOpt));
}

// #endregion

// #region View Engines

function initViewEngines() {
  var relativePathToViews = path.relative(build.baseDir, build.sourceDir);
  verbose('relativePathToViews: ' + relativePathToViews);

  app.set('views', relativePathToViews);
  app.locals = build.context;

  var eng = build.engine.jade;
  if (eng && eng.extension)
    vrend['.' + eng.extension] = renderJade;
  eng = build.engine.nunjucks;
  if (eng && eng.extension)
    vrend['.' + eng.extension] = renderNunjucks;
}

function initJade() {
  veng.jade = require('jade');
  // CONSIDER: I'd like to configure jade `pretty` somewhere else;
  // polluting locals data is a bad idea IMO. See the following issue for a
  // Express 3 workaround - https://github.com/jadejs/jade/issues/2026
  // (We're using Express 4 here so the solution in the comments there
  // probably won't work. Haven't tried it yet.)
  if (build.engine.jade.options.pretty) {
    app.locals.pretty = true;
  }
}

function initNunjucks() {
  veng.nunjucks = require('nunjucks');
  var options = lodash.cloneDeep(build.engine.nunjucks.options);
  var relativePathToViews = app.get('views');
  options.express = app;
  var nunjucks_env = veng.nunjucks.configure(relativePathToViews, options);
  initNunjucksExtensions(nunjucks_env);
  initNunjucksFilters(nunjucks_env);
}

function initNunjucksExtensions(nunjucks_env) {
  var collection = build.engine.nunjucks.extensions;
  if (collection === undefined || collection === null)
    return;
  /*jshint -W089 */
  for (var name in collection)
    nunjucks_env.addExtension(name, collection[name]);
}

function initNunjucksFilters(nunjucks_env) {
  var collection = build.engine.nunjucks.filters;
  if (collection === undefined || collection === null)
    return;
  /*jshint -W089 */
  for (var name in collection)
    nunjucks_env.addFilter(name, collection[name]);
}

function renderJade(req, res, next) {
  if (!veng.jade)
    initJade();
  var context = app.locals;
  veng.jade.renderFile(req.viewpath, context, function (err, html) {
    if (!err)
      return res.send(html);
    return next(err);
  });
}

function renderNunjucks(req, res, next) {
  if (!veng.nunjucks)
    initNunjucks();
  var context = lodash.assign({}, app.locals, build.engine.nunjucks.functions);
  veng.nunjucks.render(req.viewpath, context, function (err, html) {
    if (!err)
      return res.send(html);
    return next(err);
  });
}

// #endregion

// #endregion

// #region View Routing

// TODO: Make findView async.
// TODO: Generate findView pattern file extensions from configured engines.
function findView(req) {
  var viewpath = normalizeRequestPath(req.path, 'index');
  // This pattern finds all possible view files.
  var pattern = viewpath + '{/index.,.}+(jade|htm)';
  // #region How it works
  // See https://github.com/isaacs/node-glob#glob-primer
  // (For the following examples to work, the files must exist.)
  //
  // Given a sourceDir of '/x/y/z' and a viewpath of 'hello':
  // > glob.sync('hello{/index.,.}+(jade|htm)', {cwd: sourceDir, nodir: true});
  // ['hello.jade', 'hello/index.jade']
  //
  // If the viewpath equals the word 'index':
  // > glob.sync('index{/index.,.}+(jade|htm)', {cwd: sourceDir, nodir: true});
  // ['index.htm', 'index.jade']
  //
  // If the viewpath is a directory like 'hello/goodbye':
  // > glob.sync('hello/goodbye{/index.,.}+(jade|htm)',
  //     { cwd: sourceDir, nodir: true});
  // ['hello/goodbye.jade', 'hello/goodbye/index.jade']
  // #endregion
  verbose('Finding views in: ', build.sourceDir, ' with pattern: ', pattern);
  var files = glob.sync(pattern, {
    cwd: build.sourceDir,
    nodir: true
  });
  if (files.length === 0)
    return false;
  req.viewpath = path.join(build.sourceDir, files[0]);
  console.log('Found view: ' + req.viewpath);
  return true;
}

function initViewRouting() {
  app.use(express.static(build.sourceDir));
  if (build.verbose)
    console.log('Mapping paths');
  lodash.forEach(build.pathMap, function (mapping, name, pathMap) {
    if (build.verbose)
      console.log('  - ' + name +
        ': from "' + mapping.fs +
        '" to "' + mapping.url + '"');
    app.use(mapping.url, express.static(mapping.fs));
  });
  app.get('*', renderView);
}

function normalizeRequestPath(reqpath, defaultIfBlank) {
  reqpath = '' + reqpath;

  // Remove the leading (or only) slash.
  if (reqpath.charAt(0) === '/')
    reqpath = reqpath.substr(1, reqpath.length - 1);

  // Remove any trailing slash.
  if (reqpath.charAt(reqpath.length - 1) === '/')
    reqpath = reqpath.substr(0, reqpath.length - 1);

  if (reqpath.length === 0)
    return defaultIfBlank;

  return reqpath;
}

function renderView(req, res, next) {
  if (!findView(req))
    return next();
  var ext = path.extname(req.viewpath).toLowerCase();
  if (ext in vrend) {
    // TODO: If there is a .view.js file that matches req.viewpath, load it into
    // req.locals and then use that as the context in each render function.
    vrend[ext](req, res, next);
  }
}

// #endregion

// #region Error Handling

var errfile = path.resolve(path.join(__dirname, '../../content/error.jade'));

function initErrorHandling() {
  app.use(error404Handler);
  app.use(errorHandler);
}

function error404Handler(req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
}

function errorHandler(err, req, res, next) {
  /*jshint unused:false*/
  var msg = err.toString();
  console.error(msg);
  err.status = err.status || 500;
  res.status(err.status);
  var html = veng.jade.renderFile(errfile, {
    message: msg,
    error: err
  });
  res.send(html);
}

// #endregion
<commit_msg>Now rendering views when the path matches the requested URL sans its file extension.<commit_after>'use strict';

// #region Imports
var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var express = require('express');
var favicon = require('serve-favicon');
var glob = require('glob');
var lessMw = require('less-mw');
var lodash = require('lodash');
var logger = require('morgan');
var path = require('path');
var sassMw = require('node-sass-middleware');
var StaticBuild = require('../../index.js');
var URL = require('url');
// #endregion

var app = module.exports = express();
/** The target instance of StaticBuild. */
var build;
/** View Engine Modules */
var veng = {
  jade: undefined,
  nunjucks: undefined
};
/** View Renderer Functions */
var vrend = {};

/**
 * Initializes the Express app without running it.
 * @param {StaticBuild} [targetBuild] The target build.
 */
function init(targetBuild) {
  // CONSIDER: The `|| StaticBuild.current` isn't really necessary.
  build = targetBuild || StaticBuild.current;
  initViewEngines();
  // Request Pipeline
  initFavicon();
  initLogging();
  initParsing();
  initCssLESS();
  initSASS();
  initViewRouting();
  initErrorHandling();
}
app.init = init;

function verbose(data) {
  /*jshint unused:false*/
  if (build.verbose)
    console.log.apply(console, arguments);
}

// #region Features

function initFavicon() {
  if (!build.favicon)
    return;
  // TODO: Replace serve-favicon with a module that doesn't have an error when
  // the favicon file doesn't exist.
  // See https://github.com/expressjs/serve-favicon/issues/27
  app.use(favicon(build.resolveSrcPath(build.favicon)));
}

function initLogging() {
  app.use(logger('dev'));
}

function initParsing() {
  app.use(bodyParser.json());
  app.use(bodyParser.urlencoded({ extended: false }));
  app.use(cookieParser());
}

// #endregion

// #region CSS/LESS

function initCssLESS() {
  // Options for less-middleware.
  var opt = {
    debug: false,
    render: {
      compress: false
    }
  };
  var sm;
  if (build.engine.less.map.enabled === true) {
    // Less-middleware passes opt.render data to the less.render function.
    opt.render.sourceMap = sm = {
      sourceMapBasepath: build.sourceDir,
      sourceMapRootpath: '/'
    };
    if (build.engine.less.map.inline === true) {
      // BUG: External map files are still created, even when inline is enabled.
      sm.sourceMapFileInline = true;
      sm.sourceMapLessInline = true;
      sm.sourceMapMapInline = true;
      sm.outputSourceFiles = true;
    } else {
      // HACK: Specifying sourceMapFilename here causes less to put the same
      // sourceMappingURL in every css file, so instead we use a special token
      // and fix the situation during postprocess.css.
      sm.sourceMapFilename = initCssLESS.SOURCEMAP_FILENAME_TOKEN;
      opt.postprocess = {
        css: postprocessCss
      };
    }
  }
  app.use(lessMw(build.sourceDir, opt));
}
initCssLESS.SOURCEMAP_REQUESTPATH_TOKEN = '___req.path___';
initCssLESS.SOURCEMAP_FILENAME_TOKEN =
  initCssLESS.SOURCEMAP_REQUESTPATH_TOKEN + '.map';

function postprocessCss(css, req) {
  if (css && css.replace)
    css = css.replace(initCssLESS.SOURCEMAP_REQUESTPATH_TOKEN, req.path);
  return css;
}

// #endregion

// #region CSS/SASS

function initSASS() {
  var sassMap = build.engine.sass.map;
  var opt = {
    src: build.sourceDir,
    // Recompile on every request.
    force: true,
    // Write the .css file, do not stream it.
    response: false,
    // Configure source maps.
    sourceMap: sassMap.enabled,
    sourceMapEmbed: sassMap.inline
  };
  // Handle the .sass file extension.
  var sassOpt = lodash.assign({ indentedSyntax: true }, opt);
  app.use(sassMw(sassOpt));
  // Handle the .scss file extension.
  var scssOpt = lodash.assign({ indentedSyntax: false }, opt);
  app.use(sassMw(scssOpt));
}

// #endregion

// #region View Engines

function initViewEngines() {
  var relativePathToViews = path.relative(build.baseDir, build.sourceDir);
  verbose('relativePathToViews: ' + relativePathToViews);

  app.set('views', relativePathToViews);
  app.locals = build.context;

  var eng = build.engine.jade;
  if (eng && eng.extension)
    vrend['.' + eng.extension] = renderJade;
  eng = build.engine.nunjucks;
  if (eng && eng.extension)
    vrend['.' + eng.extension] = renderNunjucks;
}

function initJade() {
  veng.jade = require('jade');
  // CONSIDER: I'd like to configure jade `pretty` somewhere else;
  // polluting locals data is a bad idea IMO. See the following issue for a
  // Express 3 workaround - https://github.com/jadejs/jade/issues/2026
  // (We're using Express 4 here so the solution in the comments there
  // probably won't work. Haven't tried it yet.)
  if (build.engine.jade.options.pretty) {
    app.locals.pretty = true;
  }
}

function initNunjucks() {
  veng.nunjucks = require('nunjucks');
  var options = lodash.cloneDeep(build.engine.nunjucks.options);
  var relativePathToViews = app.get('views');
  options.express = app;
  var nunjucks_env = veng.nunjucks.configure(relativePathToViews, options);
  initNunjucksExtensions(nunjucks_env);
  initNunjucksFilters(nunjucks_env);
}

function initNunjucksExtensions(nunjucks_env) {
  var collection = build.engine.nunjucks.extensions;
  if (collection === undefined || collection === null)
    return;
  /*jshint -W089 */
  for (var name in collection)
    nunjucks_env.addExtension(name, collection[name]);
}

function initNunjucksFilters(nunjucks_env) {
  var collection = build.engine.nunjucks.filters;
  if (collection === undefined || collection === null)
    return;
  /*jshint -W089 */
  for (var name in collection)
    nunjucks_env.addFilter(name, collection[name]);
}

function renderJade(req, res, next) {
  if (!veng.jade)
    initJade();
  var context = app.locals;
  veng.jade.renderFile(req.viewpath, context, function (err, html) {
    if (!err)
      return res.send(html);
    return next(err);
  });
}

function renderNunjucks(req, res, next) {
  if (!veng.nunjucks)
    initNunjucks();
  var context = lodash.assign({}, app.locals, build.engine.nunjucks.functions);
  veng.nunjucks.render(req.viewpath, context, function (err, html) {
    if (!err)
      return res.send(html);
    return next(err);
  });
}

// #endregion

// #endregion

// #region View Routing

// TODO: Make findView async.
// TODO: Generate findView pattern file extensions from configured engines.
function findView(req) {
  var viewpath = normalizeRequestPath(req.path, 'index');
  // This pattern finds all possible view files.
  var pattern = viewpath + '{/index.,.}+(jade|htm)';
  // #region How it works
  // See https://github.com/isaacs/node-glob#glob-primer
  // (For the following examples to work, the files must exist.)
  //
  // Given a sourceDir of '/x/y/z' and a viewpath of 'hello':
  // > glob.sync('hello{/index.,.}+(jade|htm)', {cwd: sourceDir, nodir: true});
  // ['hello.jade', 'hello/index.jade']
  //
  // If the viewpath equals the word 'index':
  // > glob.sync('index{/index.,.}+(jade|htm)', {cwd: sourceDir, nodir: true});
  // ['index.htm', 'index.jade']
  //
  // If the viewpath is a directory like 'hello/goodbye':
  // > glob.sync('hello/goodbye{/index.,.}+(jade|htm)',
  //     { cwd: sourceDir, nodir: true});
  // ['hello/goodbye.jade', 'hello/goodbye/index.jade']
  // #endregion
  verbose('Finding views in: ', build.sourceDir, ' with pattern: ', pattern);
  var files = glob.sync(pattern, {
    cwd: build.sourceDir,
    nodir: true
  });
  if (files.length === 0)
    return false;
  req.viewpath = path.join(build.sourceDir, files[0]);
  console.log('GET ' + req.path + ' -> ' + req.viewpath);
  return true;
}

function initViewRouting() {
  app.use(express.static(build.sourceDir));
  if (build.verbose)
    console.log('Mapping paths');
  lodash.forEach(build.pathMap, function (mapping, name, pathMap) {
    if (build.verbose)
      console.log('  - ' + name +
        ': from "' + mapping.fs +
        '" to "' + mapping.url + '"');
    app.use(mapping.url, express.static(mapping.fs));
  });
  app.get('*', renderView);
}

function normalizeRequestPath(reqpath, defaultIfBlank) {
  reqpath = '' + reqpath;

  // Remove the leading (or only) slash.
  if (reqpath.charAt(0) === '/')
    reqpath = reqpath.substr(1, reqpath.length - 1);

  // Remove any trailing slash.
  if (reqpath.charAt(reqpath.length - 1) === '/')
    reqpath = reqpath.substr(0, reqpath.length - 1);

  if (reqpath.length === 0)
    return defaultIfBlank;

  return reqpath;
}

function renderView(req, res, next) {
  var req2, reqFileExt;
  if (!findView(req)) {
    // For now, if there is a file extension in the requested url then we try
    // finding a view again, this time without the file extension.
    // TODO: Create a map from url file extensions to view engines.
    req2 = {
      path: URL.parse(req.originalUrl).pathname,
      viewpath: ''
    };
    reqFileExt = path.extname(req2.path);
    if (!reqFileExt)
      return next();
    // Chop off the extension and try finding a view with that path.
    req2.path = req2.path.substr(0, req2.path.length - reqFileExt.length);
    if (!findView(req2))
      return next();
    req.viewpath = req2.viewpath;
  }
  var foundFileExt = path.extname(req.viewpath).toLowerCase();
  if (foundFileExt in vrend) {
    // TODO: If there is a .view.js file that matches req.viewpath, load it into
    // req.locals and then use that as the context in each render function.
    vrend[foundFileExt](req, res, next);
  }
}

// #endregion

// #region Error Handling

var errfile = path.resolve(path.join(__dirname, '../../content/error.jade'));

function initErrorHandling() {
  app.use(error404Handler);
  app.use(errorHandler);
}

function error404Handler(req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
}

function errorHandler(err, req, res, next) {
  /*jshint unused:false*/
  var msg = err.toString();
  console.error(msg);
  err.status = err.status || 500;
  res.status(err.status);
  var html = veng.jade.renderFile(errfile, {
    message: msg,
    error: err
  });
  res.send(html);
}

// #endregion
<|endoftext|>


<commit_before>/**
 * Copyright reelyActive 2014
 * We believe in an open Internet of Things
 */

var util = require('util');
var events = require('events');
var nedb = require('nedb');

var TEMPORAL_EVENTS_INTERVAL_MILLISECONDS = 1000;
var DISAPPEARANCE_MILLISECONDS = 10000;

/**
 * EventsManager Class
 * Collects and manages events in a database
 * @param {Object} options The options as a JSON object.
 * @constructor
 */
function EventsManager(options) {
  var self = this;
  this.disappearanceMilliseconds = options.disappearanceMilliseconds ||
                                   DISAPPEARANCE_MILLISECONDS;
  this.db = new nedb();
  this.db.ensureIndex({ fieldName: "tiraid.timestamp" });
  this.db.ensureIndex({ fieldName: "tiraid.radioDecodings.0.identifier.value" });

  function temporalEventDetection() {
    detectDisappearances(self);
    updateStatisticsRecords(self);
  }

  setInterval(temporalEventDetection, TEMPORAL_EVENTS_INTERVAL_MILLISECONDS);

  events.EventEmitter.call(this);
};
util.inherits(EventsManager, events.EventEmitter);

/**
 * Handle a tiraid, update the database and detect Displacement and
 * Appearance events.
 * @param {Object} tiraid The tiraid object.
 */
EventsManager.prototype.handleTiraid = function(tiraid) {
  var self = this;
  var id = tiraid.identifier.value;
  var strongestDecoder = tiraid.radioDecodings[0].identifier.value;
  self.currentStatistics.tiraids++;

  // Try to update a record with the same ID and strongestDecoder,
  // if successful, no event occurred.
  this.db.update({ _id: id, "tiraid.radioDecodings.0.identifier.value": strongestDecoder },
                 { $set: { tiraid: tiraid } }, { upsert: true },
                 function(err, numReplaced, newDoc) {

    // ID exists but strongestDecoder is different, this is a Displacement
    if(err && (err.errorType == "uniqueViolated")) {
      self.db.update({ _id: id }, { $set: { tiraid: tiraid } }, { upsert: true },
                     function(err, numReplaced, newDoc) {
        // TODO: tiraid mashup if multiple origins
        self.emit('displacement', tiraid);
        self.currentStatistics.displacements++;
      });
    }

    // A new record was created, this is an Appearance
    else if(newDoc) {
      self.emit('appearance', tiraid);
      self.currentStatistics.appearances++;
    }
  });
};

/**
 * Get the current state of events.
 * @param {Object} options The options as a JSON object.
 * @param {callback} callback Function to call on completion.
 */
EventsManager.prototype.getState = function(options, callback) {
  var self = this;

  if(options.ids) {
    this.db.find({ $or: [{ _id: { $in: options.ids } },
                         { "tiraid.radioDecodings.identifier.value":
                           { $in: options.ids } }] },
                 { _id: 0 }, function(err, matching) {
      convertToState(err, matching, options, callback);
    });
  }
  else {
    callback({});
  }
}

/**
 * Get the latest statistics record.
 */
EventsManager.prototype.getStatistics = function() {
  var self = this;

  return self.compiledStatistics;
}

/**
 * Convert database query results into state
 * @param {error} err The error message from the database query.
 * @param {Array} matching The array of matching query results.
 * @param {Object} options The options as a JSON object.
 * @param {callback} callback Function to call on completion.
 */
function convertToState(err, matching, options, callback) {
  var state = {};
  for(var cId = 0; cId < matching.length; cId++) {
    var tiraid = matching[cId].tiraid;
    var identifierValue = tiraid.identifier.value;
    var isMatchingTransmitter = (options.ids.indexOf(identifierValue) != -1);

    // Include the full tiraid if the id matches that of a transmitter
    if(isMatchingTransmitter) {
      state[identifierValue] = tiraid;
    }

    // Include only the tiraid identifier if the id matches that of a receiver
    else {
      state[identifierValue] = { "identifier": tiraid.identifier };
    }
  }
  callback(state);
}

/**
 * Detect disappearances from the database
 * @param {EventsManager} instance The given instance.
 */
function detectDisappearances(instance) {
  var currentTime = new Date();
  var cutoffTime = currentTime.setMilliseconds(currentTime.getMilliseconds() -
                                          instance.disappearanceMilliseconds);
  cutoffTime = new Date(cutoffTime).toISOString();
  instance.db.find({ "tiraid.timestamp": { $lt: cutoffTime } }, function(err, docs) {
    docs.forEach(function(value, index, array) {
      var id = value._id;
      instance.db.remove({ _id: value._id }, {}, function(err, numRemoved) {
        instance.emit('disappearance', value.tiraid);
        instance.currentStatistics.disappearances++;
      });
    });
  });
}

/**
 * Update statistics records on the captured events
 * @param {EventsManager} instance The given instance.
 */
function updateStatisticsRecords(instance) {
  if(instance.compiledStatistics === undefined) {
    instance.compiledStatistics = { devices: 0, tiraids: 0, appearances: 0,
                                    displacements: 0, disappearances: 0 };
    instance.currentStatistics = { tiraids: 0, appearances: 0,
                                   displacements: 0, disappearances: 0 };
  }
  else {
    var seconds = TEMPORAL_EVENTS_INTERVAL_MILLISECONDS / 1000;
    for(key in instance.currentStatistics) {
      instance.compiledStatistics[key] = instance.currentStatistics[key] / seconds;
      instance.currentStatistics[key] = 0;
    }
    instance.db.count({}, function (err, count) {
      instance.compiledStatistics.devices = count;
    });
  }
}

module.exports = EventsManager;
<commit_msg>Added handleEvent() function<commit_after>/**
 * Copyright reelyActive 2014
 * We believe in an open Internet of Things
 */

var util = require('util');
var events = require('events');
var nedb = require('nedb');

var TEMPORAL_EVENTS_INTERVAL_MILLISECONDS = 1000;
var DISAPPEARANCE_MILLISECONDS = 10000;

/**
 * EventsManager Class
 * Collects and manages events in a database
 * @param {Object} options The options as a JSON object.
 * @constructor
 */
function EventsManager(options) {
  var self = this;
  this.disappearanceMilliseconds = options.disappearanceMilliseconds ||
                                   DISAPPEARANCE_MILLISECONDS;
  this.db = new nedb();
  this.db.ensureIndex({ fieldName: "tiraid.timestamp" });
  this.db.ensureIndex({ fieldName: "tiraid.radioDecodings.0.identifier.value" });

  function temporalEventDetection() {
    detectDisappearances(self);
    updateStatisticsRecords(self);
  }

  setInterval(temporalEventDetection, TEMPORAL_EVENTS_INTERVAL_MILLISECONDS);

  events.EventEmitter.call(this);
};
util.inherits(EventsManager, events.EventEmitter);

/**
 * Handle a tiraid, update the database and detect Displacement and
 * Appearance events.
 * @param {Object} tiraid The tiraid object.
 */
EventsManager.prototype.handleTiraid = function(tiraid) {
  var self = this;
  var id = tiraid.identifier.value;
  var strongestDecoder = tiraid.radioDecodings[0].identifier.value;
  self.currentStatistics.tiraids++;

  // Try to update a record with the same ID and strongestDecoder,
  // if successful, no event occurred.
  this.db.update({ _id: id, "tiraid.radioDecodings.0.identifier.value": strongestDecoder },
                 { $set: { tiraid: tiraid } }, { upsert: true },
                 function(err, numReplaced, newDoc) {

    // ID exists but strongestDecoder is different, this is a Displacement
    if(err && (err.errorType == "uniqueViolated")) {
      self.db.update({ _id: id }, { $set: { tiraid: tiraid } }, { upsert: true },
                     function(err, numReplaced, newDoc) {
        // TODO: tiraid mashup if multiple origins
        self.emit('displacement', tiraid);
        self.currentStatistics.displacements++;
      });
    }

    // A new record was created, this is an Appearance
    else if(newDoc) {
      self.emit('appearance', tiraid);
      self.currentStatistics.appearances++;
    }
  });
};

/**
 * Handle a tiraid, update the database and detect Displacement and
 * Appearance events.
 * @param {String} event The name of the event type.
 * @param {Object} tiraid The tiraid object.
 * @param {callback} callback Function to call on completion.
 */
EventsManager.prototype.handleEvent = function(event, tiraid, callback) {
  var self = this;
  if(event === "disappearance") {
    callback({}, 200);  // TODO: anything to do for disappearances?
  }
  else {
    self.handleTiraid(tiraid);  // TODO: handleTiraid callback?
    callback({}, 200);
  }
}

/**
 * Get the current state of events.
 * @param {Object} options The options as a JSON object.
 * @param {callback} callback Function to call on completion.
 */
EventsManager.prototype.getState = function(options, callback) {
  var self = this;

  if(options.ids) {
    this.db.find({ $or: [{ _id: { $in: options.ids } },
                         { "tiraid.radioDecodings.identifier.value":
                           { $in: options.ids } }] },
                 { _id: 0 }, function(err, matching) {
      convertToState(err, matching, options, callback);
    });
  }
  else {
    callback({});
  }
}

/**
 * Get the latest statistics record.
 */
EventsManager.prototype.getStatistics = function() {
  var self = this;

  return self.compiledStatistics;
}

/**
 * Convert database query results into state
 * @param {error} err The error message from the database query.
 * @param {Array} matching The array of matching query results.
 * @param {Object} options The options as a JSON object.
 * @param {callback} callback Function to call on completion.
 */
function convertToState(err, matching, options, callback) {
  var state = {};
  for(var cId = 0; cId < matching.length; cId++) {
    var tiraid = matching[cId].tiraid;
    var identifierValue = tiraid.identifier.value;
    var isMatchingTransmitter = (options.ids.indexOf(identifierValue) != -1);

    // Include the full tiraid if the id matches that of a transmitter
    if(isMatchingTransmitter) {
      state[identifierValue] = tiraid;
    }

    // Include only the tiraid identifier if the id matches that of a receiver
    else {
      state[identifierValue] = { "identifier": tiraid.identifier };
    }
  }
  callback(state);
}

/**
 * Detect disappearances from the database
 * @param {EventsManager} instance The given instance.
 */
function detectDisappearances(instance) {
  var currentTime = new Date();
  var cutoffTime = currentTime.setMilliseconds(currentTime.getMilliseconds() -
                                          instance.disappearanceMilliseconds);
  cutoffTime = new Date(cutoffTime).toISOString();
  instance.db.find({ "tiraid.timestamp": { $lt: cutoffTime } }, function(err, docs) {
    docs.forEach(function(value, index, array) {
      var id = value._id;
      instance.db.remove({ _id: value._id }, {}, function(err, numRemoved) {
        instance.emit('disappearance', value.tiraid);
        instance.currentStatistics.disappearances++;
      });
    });
  });
}

/**
 * Update statistics records on the captured events
 * @param {EventsManager} instance The given instance.
 */
function updateStatisticsRecords(instance) {
  if(instance.compiledStatistics === undefined) {
    instance.compiledStatistics = { devices: 0, tiraids: 0, appearances: 0,
                                    displacements: 0, disappearances: 0 };
    instance.currentStatistics = { tiraids: 0, appearances: 0,
                                   displacements: 0, disappearances: 0 };
  }
  else {
    var seconds = TEMPORAL_EVENTS_INTERVAL_MILLISECONDS / 1000;
    for(key in instance.currentStatistics) {
      instance.compiledStatistics[key] = instance.currentStatistics[key] / seconds;
      instance.currentStatistics[key] = 0;
    }
    instance.db.count({}, function (err, count) {
      instance.compiledStatistics.devices = count;
    });
  }
}

module.exports = EventsManager;
<|endoftext|>


<commit_before>/*
 * nodeON-express-error
 * The nodeON Express Error Handler
 * https://github.com/thanpolas/nodeON-express-error
 *
 * Copyright ©2015 Thanasis Polychronakis
 * Licensed under the MIT license.
 */

/**
 * @fileOverview nodeON-express-error Base.
 */
module.exports = {};

var appError = require('nodeon-error');
var helpers = require('nodeon-helpers');

var log = require('logg').getLogger('nodeonExpressError.errorHandler');

/**
 * The master Error Handler.
 *
 * @param {Error} err The error thrown.
 * @param {Object} req The request Object.
 * @param {Object} res The response Object.
 * @param {Function(Error=)} next passing control to the next middleware.
 */
/*jshint unused:false */
module.exports = function (err, req, res, next) {
  var status = 500;
  var error;

  if (err instanceof Error) {
    status = err.status || status;

    // check if it's a nodeon-error
    if (typeof err.toApi === 'function') {
      error = err.toApi();
    } else {
      var nodeonError = new appError.Error(err);
      error = nodeonError.toApi();
    }
  } else {
    var tmpErr = new appError(err);
    error = tmpErr.toApi();
  }

  log.warn('handle() :: Handling error, status:', status, 'Request Url:',
    req.originalUrl, 'Message:', error.message);
  log.finest('handle() :: Raw error object:', err);

  res.status(status);

  // figure out what the client accepts
  if (helpers.isRequestJson(req)) {
    res.json(error);
  } else {
    res.render('error/500', {error: error});
  }
};
<commit_msg>update expectation for http status code<commit_after>/*
 * nodeON-express-error
 * The nodeON Express Error Handler
 * https://github.com/thanpolas/nodeON-express-error
 *
 * Copyright ©2015 Thanasis Polychronakis
 * Licensed under the MIT license.
 */

/**
 * @fileOverview nodeON-express-error Base.
 */
module.exports = {};

var appError = require('nodeon-error');
var helpers = require('nodeon-helpers');

var log = require('logg').getLogger('nodeonExpressError.errorHandler');

/**
 * The master Error Handler.
 *
 * @param {Error} err The error thrown.
 * @param {Object} req The request Object.
 * @param {Object} res The response Object.
 * @param {Function(Error=)} next passing control to the next middleware.
 */
/*jshint unused:false */
module.exports = function (err, req, res, next) {
  var httpCode = 500;
  var error;

  if (err instanceof Error) {
    httpCode = err.httpCode || httpCode;

    // check if it's a nodeon-error
    if (err.isNodeon) {
      error = err.toApi();
    } else {
      var nodeonError = new appError.Error(err);
      error = nodeonError.toApi();
    }
  } else {
    var tmpErr = new appError(err);
    error = tmpErr.toApi();
  }

  log.warn('handle() :: Handling error, httpCode:', httpCode, 'Request Url:',
    req.originalUrl, 'Message:', error.message);
  log.finest('handle() :: Raw error object:', err);

  res.status(httpCode);

  // figure out what the client accepts
  if (helpers.isRequestJson(req)) {
    res.json(error);
  } else {
    res.render('error/500', {error: error});
  }
};
<|endoftext|>


<commit_before>'use strict';

var every          = Array.prototype.every
  , call           = Function.prototype.call
  , assertNotNull  = require('es5-ext/lib/assert-not-null')
  , isError        = require('es5-ext/lib/Error/is-error')
  , assertCallable = require('es5-ext/lib/Object/assert-callable')
  , create         = require('es5-ext/lib/Object/prototype/plain-create')
  , deferred       = require('../../deferred')
  , isPromise      = require('../../is-promise');

var proto = {
	iterate: function (value, index) {
		if (!this.cb && isError(value)) {
			this.d.resolve(value);
			return false;
		}
		++this.waiting;
		if (isPromise(value)) {
			value(this.processValue.bind(this, index), this.d.resolve);
		} else {
			return this.processValue(index, value);
		}
		return true;
	},
	processValue: function (index, value) {
		if (this.d.promise._base.resolved) {
			return false;
		}
		if (this.cb) {
			try {
				value = call.call(this.cb, this.context, value, index, this.list);
			} catch (e) {
				this.d.resolve(e);
				return false;
			}
			if (isPromise(value)) {
				value(this.processResult.bind(this, index), this.d.resolve);
				return true;
			} else if (isError(value)) {
				this.d.resolve(value);
				return false;
			}
		}
		this.processResult(index, value);
		return true;
	},
	processResult: function (index, value) {
		if (this.d.promise._base.resolved) {
			return;
		}
		this.result[index] = value;
		if (!--this.waiting && this.initialized) {
			this.d.resolve(this.result);
		}
	}
};

module.exports = function (cb, thisArg) {
	var result, iterator, d;
	assertNotNull(this);
	if (cb != null) {
		assertCallable(cb);
	}
	d = deferred();
	every.call(this, (iterator = create.call(proto, {
		d: d,
		cb: cb,
		list: this,
		context: thisArg,
		waiting: 0,
		result: Array(this.length >>> 0)
	})).iterate, iterator);
	iterator.initialized = true;
	if (!iterator.waiting) {
		d.resolve(iterator.result);
	}
	return d.promise;
};
<commit_msg>Better error detection in array's map extension. It will not continue work if resolved failed promises were found on the way.<commit_after>'use strict';

var every          = Array.prototype.every
  , call           = Function.prototype.call
  , assertNotNull  = require('es5-ext/lib/assert-not-null')
  , isError        = require('es5-ext/lib/Error/is-error')
  , assertCallable = require('es5-ext/lib/Object/assert-callable')
  , create         = require('es5-ext/lib/Object/prototype/plain-create')
  , deferred       = require('../../deferred')
  , isPromise      = require('../../is-promise');

var proto = {
	iterate: function (value, index) {
		if (!this.cb && isError(value)) {
			this.d.resolve(value);
			return false;
		}
		++this.waiting;
		if (isPromise(value)) {
			if (isPromise(value = value.valueOf())) {
				value(this.processValue.bind(this, index), this.d.resolve);
			} else if (isError(value)) {
				this.d.resolve(value);
				return false;
			} else {
				return this.processValue(index, value);
			}
		} else {
			return this.processValue(index, value);
		}
		return true;
	},
	processValue: function (index, value) {
		if (this.d.promise._base.resolved) {
			return false;
		}
		if (this.cb) {
			try {
				value = call.call(this.cb, this.context, value, index, this.list);
			} catch (e) {
				this.d.resolve(e);
				return false;
			}
			if (isPromise(value)) {
				value(this.processResult.bind(this, index), this.d.resolve);
				return true;
			} else if (isError(value)) {
				this.d.resolve(value);
				return false;
			}
		}
		this.processResult(index, value);
		return true;
	},
	processResult: function (index, value) {
		if (this.d.promise._base.resolved) {
			return;
		}
		this.result[index] = value;
		if (!--this.waiting && this.initialized) {
			this.d.resolve(this.result);
		}
	}
};

module.exports = function (cb, thisArg) {
	var result, iterator, d;
	assertNotNull(this);
	if (cb != null) {
		assertCallable(cb);
	}
	d = deferred();
	every.call(this, (iterator = create.call(proto, {
		d: d,
		cb: cb,
		list: this,
		context: thisArg,
		waiting: 0,
		result: Array(this.length >>> 0)
	})).iterate, iterator);
	iterator.initialized = true;
	if (!iterator.waiting) {
		d.resolve(iterator.result);
	}
	return d.promise;
};
<|endoftext|>


<commit_before>// keeps track of underline instances for class naming
var Underline_id = 0;

function Underline(identifier, options) {
    'use strict';
    return this.init(identifier, options);
}

if (window.module !== undefined) {
    module.exports = Underline;
}

(function (window, document) {
    'use strict';

    // stolen from daviferreira's medium clone :)
    function extend(b, a) {
        var prop;
        if (b === undefined) {
            return a;
        }
        for (prop in a) {
            if (a.hasOwnProperty(prop) && b.hasOwnProperty(prop) === false) {
                b[prop] = a[prop];
            }
        }
        return b;
    }

    // checks if element a is in array b
    function aInb(a, b) {
        for (var e in b) {
            if (b[e] === a) {
                return true;
            }
        }
        return false;
    }

    function initChild(tag, classes) {
        var child = document.createElement(tag);
        if (typeof classes === 'string') {
            child.classList.add(classes);
        }
        else {
            classes.forEach(function (c) {
                child.classList.add(c);
            });
        }
        return child;
    }

    function appendTextNode(child, text) {
        child.appendChild( document.createTextNode(text) );
        return child;
    }

    Underline.prototype = {
        defaults: {
            margin: 0,
            width: 1,
            style: 'solid',
            color: 'black',
            noUnderline: ['g', 'j', 'y', 'p', 'q']
        },

        init: function (identifier, options) {
            console.log(this.defaults.hasOwnProperty);
            this.elements = typeof identifier === 'string' ? document.querySelectorAll(identifier) : identifier;
            this.options = extend(options, this.defaults);
            this.id = Underline_id++;
            this.format()
                .underline();
        },

        format: function () {
            var i,
                s,
                result,
                string,
                child,
                e;
            for (i = 0; i < this.elements.length; i++) {
                e = this.elements[i];
                string = e.textContent;
                e.innerHTML = '';

                result = '';
                child = initChild('span', 'underline-text' + this.id);
                for (s in string) {
                    if (aInb(string[s], this.options.noUnderline)) {
                        e.appendChild( appendTextNode(child, result) );

                        child = initChild('span', 'nounderline-text' + this.id);
                        e.appendChild( appendTextNode(child, string[s]) );

                        result = '';
                        child = initChild('span', 'underline-text' + this.id);
                    }
                    else {
                        result += string[s];
                    }
                }
                e.appendChild( appendTextNode(child, result) );
            }
            return this;
        },

        underline: function() {
            var head = document.getElementsByTagName('head')[0],
                s = document.createElement('style');
            // holy shit this is bad
            var css = ".underline-text" + this.id + ":after {";
                css += "content: '';";
                css += "position: absolute;";
                css += "border-bottom: " + this.options.width + 'px ' + this.options.style + ' ' + this.options.color + ';';
                css += "height: 1.15em; /* insane default.. */";
                css += "left: 0;";
                css += "right: 0;";
                css += "margin-top: " + this.options.margin + 'px;';
                css += '}';
                css += ".underline-text" + this.id + " { position:relative; } ";
            s.setAttribute('type', 'text/css');
            if (s.styleSheet) {   // IE
                s.styleSheet.cssText = css;
            } else {                // the world
                s.appendChild(document.createTextNode(css));
            }
            head.appendChild(s);
        }
    }
})(window, document);
<commit_msg>remove debugging console.log statement...<commit_after>// keeps track of underline instances for class naming
var Underline_id = 0;

function Underline(identifier, options) {
    'use strict';
    return this.init(identifier, options);
}

if (window.module !== undefined) {
    module.exports = Underline;
}

(function (window, document) {
    'use strict';

    // stolen from daviferreira's medium clone :)
    function extend(b, a) {
        var prop;
        if (b === undefined) {
            return a;
        }
        for (prop in a) {
            if (a.hasOwnProperty(prop) && b.hasOwnProperty(prop) === false) {
                b[prop] = a[prop];
            }
        }
        return b;
    }

    // checks if element a is in array b
    function aInb(a, b) {
        for (var e in b) {
            if (b[e] === a) {
                return true;
            }
        }
        return false;
    }

    function initChild(tag, classes) {
        var child = document.createElement(tag);
        if (typeof classes === 'string') {
            child.classList.add(classes);
        }
        else {
            classes.forEach(function (c) {
                child.classList.add(c);
            });
        }
        return child;
    }

    function appendTextNode(child, text) {
        child.appendChild( document.createTextNode(text) );
        return child;
    }

    Underline.prototype = {
        defaults: {
            margin: 0,
            width: 1,
            style: 'solid',
            color: 'black',
            noUnderline: ['g', 'j', 'y', 'p', 'q']
        },

        init: function (identifier, options) {
            this.elements = typeof identifier === 'string' ? document.querySelectorAll(identifier) : identifier;
            this.options = extend(options, this.defaults);
            this.id = Underline_id++;
            this.format()
                .underline();
        },

        format: function () {
            var i,
                s,
                result,
                string,
                child,
                e;
            for (i = 0; i < this.elements.length; i++) {
                e = this.elements[i];
                string = e.textContent;
                e.innerHTML = '';

                result = '';
                child = initChild('span', 'underline-text' + this.id);
                for (s in string) {
                    if (aInb(string[s], this.options.noUnderline)) {
                        e.appendChild( appendTextNode(child, result) );

                        child = initChild('span', 'nounderline-text' + this.id);
                        e.appendChild( appendTextNode(child, string[s]) );

                        result = '';
                        child = initChild('span', 'underline-text' + this.id);
                    }
                    else {
                        result += string[s];
                    }
                }
                e.appendChild( appendTextNode(child, result) );
            }
            return this;
        },

        underline: function() {
            var head = document.getElementsByTagName('head')[0],
                s = document.createElement('style');
            // holy shit this is bad
            var css = ".underline-text" + this.id + ":after {";
                css += "content: '';";
                css += "position: absolute;";
                css += "border-bottom: " + this.options.width + 'px ' + this.options.style + ' ' + this.options.color + ';';
                css += "height: 1.15em; /* insane default.. */";
                css += "left: 0;";
                css += "right: 0;";
                css += "margin-top: " + this.options.margin + 'px;';
                css += '}';
                css += ".underline-text" + this.id + " { position:relative; } ";
            s.setAttribute('type', 'text/css');
            if (s.styleSheet) {   // IE
                s.styleSheet.cssText = css;
            } else {                // the world
                s.appendChild(document.createTextNode(css));
            }
            head.appendChild(s);
        }
    }
})(window, document);
<|endoftext|>


<commit_before>var _ = require('lodash'),
  humans = require('npm-humans');

var groups = {
  friends: [
    'aredridel',
    'groundwater',
    'jdalton',
    'ljharb',
    'maxogden',
    'mikeal',
    'smikes'
  ],
  'npm-humans': Object.keys(humans)
};

var featureCache = {};

function calculate(feature) {
  var setting = process.env['FEATURE_' + feature.replace(/^feature_/i, "").toUpperCase()]
  if (!setting || !setting.length || setting.toLowerCase() === 'false') {
    featureCache[feature] = false;
  } else if (setting.toLowerCase() === 'true') {
    featureCache[feature] = true;
  } else {
    var allowedUsers = [];
    setting.split(/\,\s?/).forEach(function(name) {
      if (name.match(/^group:/)) {
        name = name.replace(/^group:/, '');
        var group = groups[name];
        if (!group) {
          throw ('invalid feature flag group: ' + name);
        }
        allowedUsers = allowedUsers.concat(group);
      } else {
        allowedUsers.push(name);
      }
    });
    featureCache[feature] = allowedUsers;
  }
}

var isFeatureEnabled = module.exports = function isFeatureEnabled(feature, request) {
  if (!featureCache.hasOwnProperty(feature)) {
    calculate(feature);
  }
  var value = featureCache[feature];
  if (_.isBoolean(value)) {
    return value;
  }

  if (Array.isArray(value) && request && request.auth && request.auth.credentials) {
    return value.indexOf(request.auth.credentials.name) > -1;
  }

  return false;
};

isFeatureEnabled.calculate = calculate;
isFeatureEnabled.cache = featureCache;
<commit_msg>Referencing the username instead of the key<commit_after>var _ = require('lodash'),
  humans = require('npm-humans');

var groups = {
  friends: [
    'aredridel',
    'groundwater',
    'jdalton',
    'ljharb',
    'maxogden',
    'mikeal',
    'smikes'
  ],
  'npm-humans': Object.keys(humans).map(function(human) {
    return humans[human].username;
  })
};

var featureCache = {};

function calculate(feature) {
  var setting = process.env['FEATURE_' + feature.replace(/^feature_/i, "").toUpperCase()]
  if (!setting || !setting.length || setting.toLowerCase() === 'false') {
    featureCache[feature] = false;
  } else if (setting.toLowerCase() === 'true') {
    featureCache[feature] = true;
  } else {
    var allowedUsers = [];
    setting.split(/\,\s?/).forEach(function(name) {
      if (name.match(/^group:/)) {
        name = name.replace(/^group:/, '');
        var group = groups[name];
        if (!group) {
          throw ('invalid feature flag group: ' + name);
        }
        allowedUsers = allowedUsers.concat(group);
      } else {
        allowedUsers.push(name);
      }
    });
    featureCache[feature] = allowedUsers;
  }
}

var isFeatureEnabled = module.exports = function isFeatureEnabled(feature, request) {
  if (!featureCache.hasOwnProperty(feature)) {
    calculate(feature);
  }
  var value = featureCache[feature];
  if (_.isBoolean(value)) {
    return value;
  }

  if (Array.isArray(value) && request && request.auth && request.auth.credentials) {
    return value.indexOf(request.auth.credentials.name) > -1;
  }

  return false;
};

isFeatureEnabled.calculate = calculate;
isFeatureEnabled.cache = featureCache;
<|endoftext|>