<commit_before>
var curVersion = 'v6';

var request = require('request');

module.exports = function(grunt) {

  var firebaseUrl = grunt.config.get('webhook').firebase || '';
  var firebaseUri = null;
  if(firebaseUrl) {
    firebaseUri = 'https://' + firebaseUrl +  '.firebaseio.com/generator_version.json';
  }

  var checkVersion = function(callback) {
    if(firebaseUri === null) {
      callback();
    } else {
      request({ url: firebaseUri, json: true }, function(e, r, body) {
        if(body) {
          if(body !== curVersion) {
            console.log('Your site is using an old version of Webhook. Please run wh update in your site directory.'.red)
          }

          callback();
        } else {
          callback();
        }
      });
    }
  };

  var generator = require('../libs/generator').generator(grunt.config, grunt.log, grunt.file, root);

  grunt.registerTask('buildTemplates', 'Generate static files from templates directory', function() {
    var done = this.async();
    generator.renderTemplates(done, generator.reloadFiles);
  });

  grunt.registerTask('buildPages', 'Generate static files from pages directory', function() {
    var done = this.async();
    generator.renderPages(done, generator.reloadFiles);
  });

  grunt.registerTask('scaffolding', 'Generate scaffolding for a new object', function(name) {
    var done = this.async();

    var force = grunt.option('force');

    var result = generator.makeScaffolding(name, done, force);

    if(!result) {
      grunt.log.error('Scaffolding for ' + name + ' already exists, use --force to overwrite');
    }
  });

  grunt.registerTask('watch', 'Watch for changes in templates and regenerate site', function() {
    generator.startLiveReload();
    grunt.task.run('simple-watch');
  });

  grunt.registerTask('webListener', 'Listens for commands from CMS through websocket', function() {
    var done = this.async();
    generator.webListener(done);
  });

  grunt.registerTask('webListener-open', 'Listens for commands from CMS through websocket', function() {
    var done = this.async();
    generator.webListener(done);

    grunt.util.spawn({
      grunt: true,
      args: ['open:wh-open'].concat(grunt.option.flags()),
      opts: { stdio: 'inherit' }
    }, function (err, result, code) {
      if (err || code > 0) {
        grunt.warn(result.stderr || result.stdout);
      }
      grunt.log.writeln('\n' + result.stdout);
    });
  });

  grunt.registerTask('clean', 'Clean build files', function() {
    var done = this.async();
    generator.cleanFiles(done);
  });

  // Build Task.
  grunt.registerTask('build', 'Clean files and then generate static site into build', function() {
    var done = this.async();

    var versionString = grunt.option('build-version');

    if(versionString)
    {
      generator.setBuildVersion(versionString);
    }

    checkVersion(function() {
      generator.buildBoth(done, generator.reloadFiles);
    })
  });

  // Change this to optionally prompt instead of requiring a sitename
  grunt.registerTask('assets', 'Initialize the firebase configuration file (installer should do this as well)', function() {
    generator.assets(grunt);
  });

  grunt.registerTask('assetsAfter', 'Initialize the firebase configuration file (installer should do this as well)', function() {
    generator.assetsAfter(grunt);
  });

  // Change this to optionally prompt instead of requiring a sitename
  grunt.registerTask('init', 'Initialize the firebase configuration file (installer should do this as well)', function() {
    var done = this.async();

    var sitename = grunt.option('sitename');
    var secretkey = grunt.option('secretkey');
    var copyCms = grunt.option('copycms');

    generator.init(sitename, secretkey, copyCms, done);
  });

  // Check if initialized properly before running all these tasks
  grunt.registerTask('default',  'Clean, Build, Start Local Server, and Watch', function() {
    grunt.task.run('connect:wh-server');
    grunt.task.run('build');
    grunt.task.run('concurrent:wh-concurrent');
  });

};
<commit_msg>up version<commit_after>
var curVersion = 'v7';

var request = require('request');

module.exports = function(grunt) {

  var firebaseUrl = grunt.config.get('webhook').firebase || '';
  var firebaseUri = null;
  if(firebaseUrl) {
    firebaseUri = 'https://' + firebaseUrl +  '.firebaseio.com/generator_version.json';
  }

  var checkVersion = function(callback) {
    if(firebaseUri === null) {
      callback();
    } else {
      request({ url: firebaseUri, json: true }, function(e, r, body) {
        if(body) {
          if(body !== curVersion) {
            console.log('Your site is using an old version of Webhook. Please run wh update in your site directory.'.red)
          }

          callback();
        } else {
          callback();
        }
      });
    }
  };

  var generator = require('../libs/generator').generator(grunt.config, grunt.log, grunt.file, root);

  grunt.registerTask('buildTemplates', 'Generate static files from templates directory', function() {
    var done = this.async();
    generator.renderTemplates(done, generator.reloadFiles);
  });

  grunt.registerTask('buildPages', 'Generate static files from pages directory', function() {
    var done = this.async();
    generator.renderPages(done, generator.reloadFiles);
  });

  grunt.registerTask('scaffolding', 'Generate scaffolding for a new object', function(name) {
    var done = this.async();

    var force = grunt.option('force');

    var result = generator.makeScaffolding(name, done, force);

    if(!result) {
      grunt.log.error('Scaffolding for ' + name + ' already exists, use --force to overwrite');
    }
  });

  grunt.registerTask('watch', 'Watch for changes in templates and regenerate site', function() {
    generator.startLiveReload();
    grunt.task.run('simple-watch');
  });

  grunt.registerTask('webListener', 'Listens for commands from CMS through websocket', function() {
    var done = this.async();
    generator.webListener(done);
  });

  grunt.registerTask('webListener-open', 'Listens for commands from CMS through websocket', function() {
    var done = this.async();
    generator.webListener(done);

    grunt.util.spawn({
      grunt: true,
      args: ['open:wh-open'].concat(grunt.option.flags()),
      opts: { stdio: 'inherit' }
    }, function (err, result, code) {
      if (err || code > 0) {
        grunt.warn(result.stderr || result.stdout);
      }
      grunt.log.writeln('\n' + result.stdout);
    });
  });

  grunt.registerTask('clean', 'Clean build files', function() {
    var done = this.async();
    generator.cleanFiles(done);
  });

  // Build Task.
  grunt.registerTask('build', 'Clean files and then generate static site into build', function() {
    var done = this.async();

    var versionString = grunt.option('build-version');

    if(versionString)
    {
      generator.setBuildVersion(versionString);
    }

    checkVersion(function() {
      generator.buildBoth(done, generator.reloadFiles);
    })
  });

  // Change this to optionally prompt instead of requiring a sitename
  grunt.registerTask('assets', 'Initialize the firebase configuration file (installer should do this as well)', function() {
    generator.assets(grunt);
  });

  grunt.registerTask('assetsAfter', 'Initialize the firebase configuration file (installer should do this as well)', function() {
    generator.assetsAfter(grunt);
  });

  // Change this to optionally prompt instead of requiring a sitename
  grunt.registerTask('init', 'Initialize the firebase configuration file (installer should do this as well)', function() {
    var done = this.async();

    var sitename = grunt.option('sitename');
    var secretkey = grunt.option('secretkey');
    var copyCms = grunt.option('copycms');

    generator.init(sitename, secretkey, copyCms, done);
  });

  // Check if initialized properly before running all these tasks
  grunt.registerTask('default',  'Clean, Build, Start Local Server, and Watch', function() {
    grunt.task.run('connect:wh-server');
    grunt.task.run('build');
    grunt.task.run('concurrent:wh-concurrent');
  });

};
<|endoftext|>


<commit_before>var git = require( '../' ),
  success = require('./utilities').success,
  events = require('events');

/**
 * Convenience commit constructor.
 *
 * @param  {RawCommit|Null} rawCommit
 * @return {Commit}
 */
var Commit = function(rawRepo, rawCommit) {

  if (!(rawRepo instanceof git.raw.Repo)) {
    throw git.error('First parameter for Commit must be a raw repo');
  }

  this.rawRepo = rawRepo;

  if (rawCommit instanceof git.raw.Commit) {
    this.rawCommit = rawCommit;
  } else {
    this.rawCommit = new git.raw.Commit();
  }
};

/**
 * Look up the commit referenced by oid, replace this.commit with the result.
 *
 * @param  {git.oid|git.raw.Oid|String} oid
 * @param  {Function} callback
 */
Commit.prototype.lookup = function(oid, callback) {
  if (typeof oid !== 'string' && !(oid instanceof git.raw.Oid)) {
    oid = oid.getRawOid();
  }
  var self = this;
  self.rawCommit.lookup(self.rawRepo, oid, function commitLookup(error, rawCommit) {
    if (success(error, callback)) {
      self.rawCommit = rawCommit;
      callback(null, self);
    }
  });
};

Commit.prototype.oid = function(callback) {
  callback(null, new git.oid(this.rawCommit.oid()));
};

Commit.prototype.sha = function(callback) {
  this.rawCommit.sha(function(error, sha) {
    if (success(error, callback)) {
      callback(null, sha);
    }
  });
};

/**
 * Retrieve the tree for this commit.
 *
 * @param  {Function} callback
 */
Commit.prototype.tree = function(callback) {
  var self = this;
  self.rawCommit.tree(function commitTree(error, rawTree) {
    if (success(error, callback)) {
      callback(null, new git.tree(self.rawRepo, rawTree));
    }
  });
};

/**
 * Retrieve the file represented by path for this commit.
 *
 * @param  {String} path
 * @param  {Function} callback
 */
Commit.prototype.file = function(path, callback) {
  this.tree(function commitFile(error, tree) {
    if (!success(error, callback)) {
      return;
    }
    tree.entry(path, function(error, entry) {
      if (success(error, callback)) {
        callback(null, entry);
      }
    });
  });
};

/**
 * Walk the history of this commit.
 *
 * @return {Event} Event emits 'commit', with error, commit and 'end', with
 *                       error, commits[]
 */
Commit.prototype.history = function() {
  var revwalk = git.revwalk(self.repo),
      event = new events.EventEmitter(),
      commits = [];

  revwalk.walk(self.id, function(error, index, commit, noMoreCommits) {
    if(error) {
      event.emit('end', error, commits);
      return false;
    }

    if (noMoreCommits) {
      event.emit('end', null, commits);
      return;
    }
    event.emit('commit', null, commit);
    commits.push(commit);
  });

  return event;
};

/**
 * Retrieve the commit's parent at the given position asynchronously.
 *
 * @param  {Integer} position
 */
Commit.prototype.parent = function(position, callback) {
  self.rawCommit.parent(position, function processParent(error, rawParent) {
    if (success(error, callback)) {
      callback(null, new Commit(this.rawRepo, rawParent));
    }
  });
};

/**
 * Get a diff tree showing changes between this commit and its parent(s).
 *
 * @param  {Function} callback
 */
Commit.prototype.parentsDiffTrees = function(callback) {
  var self = this;
  self.sha(function(error, commitSha) {
    if (!success(error, callback)) {
      return;
    }
    self.parents(function commitParents(error, parents) {
      if (!success(error, callback)) {
        return;
      }
      var parentDiffLists = [];
      parents.forEach(function commitEachParent(parent) {
        parent.sha(function commitParentSha(error, parentSha) {
          (new git.diffList(self.repo)).treeToTree(parentSha, commitSha, function walkDiffList(error, diffList) {
            if (!success(error, callback)) {
              return;
            }
            parentDiffLists.push(diffList);
            if (parentDiffLists.length === parents.length) {
              callback(null, parentDiffLists);
            }
          });
        });
      });
    });
  });
};

exports.commit = Commit;
<commit_msg>Fixed commit lookup oid checking<commit_after>var git = require( '../' ),
  success = require('./utilities').success,
  events = require('events');

/**
 * Convenience commit constructor.
 *
 * @param  {RawCommit|Null} rawCommit
 * @return {Commit}
 */
var Commit = function(rawRepo, rawCommit) {

  if (!(rawRepo instanceof git.raw.Repo)) {
    throw git.error('First parameter for Commit must be a raw repo');
  }

  this.rawRepo = rawRepo;

  if (rawCommit instanceof git.raw.Commit) {
    this.rawCommit = rawCommit;
  } else {
    this.rawCommit = new git.raw.Commit();
  }
};

/**
 * Look up the commit referenced by oid, replace this.commit with the result.
 *
 * @param  {git.oid|git.raw.Oid|String} oid
 * @param  {Function} callback
 */
Commit.prototype.lookup = function(oid, callback) {
  if (typeof oid !== 'undefined' &&
      typeof oid !== 'string' &&
      !(oid instanceof git.raw.Oid)) {
    oid = oid.getRawOid();
  }
  var self = this;
  self.rawCommit.lookup(self.rawRepo, oid, function commitLookup(error, rawCommit) {
    if (success(error, callback)) {
      self.rawCommit = rawCommit;
      callback(null, self);
    }
  });
};

Commit.prototype.oid = function(callback) {
  callback(null, new git.oid(this.rawCommit.oid()));
};

Commit.prototype.sha = function(callback) {
  this.rawCommit.sha(function(error, sha) {
    if (success(error, callback)) {
      callback(null, sha);
    }
  });
};

/**
 * Retrieve the tree for this commit.
 *
 * @param  {Function} callback
 */
Commit.prototype.tree = function(callback) {
  var self = this;
  self.rawCommit.tree(function commitTree(error, rawTree) {
    if (success(error, callback)) {
      callback(null, new git.tree(self.rawRepo, rawTree));
    }
  });
};

/**
 * Retrieve the file represented by path for this commit.
 *
 * @param  {String} path
 * @param  {Function} callback
 */
Commit.prototype.file = function(path, callback) {
  this.tree(function commitFile(error, tree) {
    if (!success(error, callback)) {
      return;
    }
    tree.entry(path, function(error, entry) {
      if (success(error, callback)) {
        callback(null, entry);
      }
    });
  });
};

/**
 * Walk the history of this commit.
 *
 * @return {Event} Event emits 'commit', with error, commit and 'end', with
 *                       error, commits[]
 */
Commit.prototype.history = function() {
  var revwalk = git.revwalk(self.repo),
      event = new events.EventEmitter(),
      commits = [];

  revwalk.walk(self.id, function(error, index, commit, noMoreCommits) {
    if(error) {
      event.emit('end', error, commits);
      return false;
    }

    if (noMoreCommits) {
      event.emit('end', null, commits);
      return;
    }
    event.emit('commit', null, commit);
    commits.push(commit);
  });

  return event;
};

/**
 * Retrieve the commit's parent at the given position asynchronously.
 *
 * @param  {Integer} position
 */
Commit.prototype.parent = function(position, callback) {
  self.rawCommit.parent(position, function processParent(error, rawParent) {
    if (success(error, callback)) {
      callback(null, new Commit(this.rawRepo, rawParent));
    }
  });
};

/**
 * Get a diff tree showing changes between this commit and its parent(s).
 *
 * @param  {Function} callback
 */
Commit.prototype.parentsDiffTrees = function(callback) {
  var self = this;
  self.sha(function(error, commitSha) {
    if (!success(error, callback)) {
      return;
    }
    self.parents(function commitParents(error, parents) {
      if (!success(error, callback)) {
        return;
      }
      var parentDiffLists = [];
      parents.forEach(function commitEachParent(parent) {
        parent.sha(function commitParentSha(error, parentSha) {
          (new git.diffList(self.repo)).treeToTree(parentSha, commitSha, function walkDiffList(error, diffList) {
            if (!success(error, callback)) {
              return;
            }
            parentDiffLists.push(diffList);
            if (parentDiffLists.length === parents.length) {
              callback(null, parentDiffLists);
            }
          });
        });
      });
    });
  });
};

exports.commit = Commit;
<|endoftext|>


<commit_before>// Load in our dependencies
var ipc = require('ipc');
var _ = require('underscore');
var GoogleMusic = require('google-music');
var MprisService = require('mpris-service');

// Define a function to set up mpris
exports.init = function (gme) {
  // https://github.com/emersion/mpris-service/tree/a245730635b55c8eb06c605f4ece61e251f04e20
  // https://github.com/emersion/mpris-service/blob/a245730635b55c8eb06c605f4ece61e251f04e20/index.js
  // http://www.freedesktop.org/wiki/Specifications/mpris-spec/metadata/
  // http://specifications.freedesktop.org/mpris-spec/latest/Player_Interface.html
  var mpris = new MprisService({
    name: 'google-music-electron'
  });
  mpris.on('next', gme.controlNext);
  mpris.on('playpause', gme.controlPlayPause);
  mpris.on('previous', gme.controlPrevious);
  mpris.on('quit', gme.quitApplication);
  mpris.on('raise', gme.onRaise);
  // Currently, position, seek, and stop aren't supported due inability to test in Cinnamon (seems broken)
  // DEV: We choose to let the OS volume be controlled by MPRIS

  var songInfo = {};
  ipc.on('change:song', function handleSongChange (evt, _songInfo) {
    mpris.metadata = songInfo = {
      'mpris:artUrl': _songInfo.art,
      // Convert milliseconds to microseconds (1s = 1e3ms = 1e6µs)
      'mpris:length': _songInfo.duration * 1e3,
      'xesam:album': _songInfo.album,
      'xesam:artist': _songInfo.artist,
      'xesam:title': _songInfo.title
    };
  });

  ipc.on('change:playback-time', function handlePlaybackUpdate (evt, playbackInfo) {
    // Convert milliseconds to microseconds (1s = 1e3ms = 1e6µs)
    var newPosition = playbackInfo.current * 1e3;
    var newTotal = playbackInfo.total * 1e3;

    // If the total has been updated, update our songInfo cache
    // DEV: This is due to `google-music.js` not always having an up to date length upon song change
    if (songInfo['mpris:length'] !== newTotal) {
      mpris.metadata = _.extend(songInfo, {
        'mpris:length': newTotal
      });
    }

    // If our position varies by 2 seconds, consider it a seek
    // DEV: Seeked takes the delta (positive/negative depending on position
    var delta = newPosition - mpris.position;
    if (Math.abs(delta) > 2e6) {
      mpris.seeked(delta);
    }
  });

  var playbackStrings = {};
  playbackStrings[GoogleMusic.Playback.PLAYING] = 'Playing';
  playbackStrings[GoogleMusic.Playback.PAUSED] = 'Paused';
  playbackStrings[GoogleMusic.Playback.STOPPED] = 'Stopped';
  ipc.on('change:playback', function handlePlaybackChange (evt, playbackState) {
    mpris.playbackStatus = playbackStrings[playbackState];
  });
};
<commit_msg>Trying out position/seek<commit_after>// Load in our dependencies
var ipc = require('ipc');
var _ = require('underscore');
var GoogleMusic = require('google-music');
var MprisService = require('mpris-service');

// Define a function to set up mpris
exports.init = function (gme) {
  // https://github.com/emersion/mpris-service/tree/a245730635b55c8eb06c605f4ece61e251f04e20
  // https://github.com/emersion/mpris-service/blob/a245730635b55c8eb06c605f4ece61e251f04e20/index.js
  // http://www.freedesktop.org/wiki/Specifications/mpris-spec/metadata/
  // http://specifications.freedesktop.org/mpris-spec/latest/Player_Interface.html
  var mpris = new MprisService({
    name: 'google-music-electron'
  });
  mpris.on('next', gme.controlNext);
  mpris.on('playpause', gme.controlPlayPause);
  mpris.on('previous', gme.controlPrevious);
  mpris.on('quit', gme.quitApplication);
  mpris.on('raise', gme.onRaise);
  // Currently, position, seek
  // DEV: Stop isn't supported in Google Music (unless it's pause + set position 0)
  // DEV: We choose to let the OS volume be controlled by MPRIS
  mpris.on('position', function () {
    console.log('position', arguments);
  });
  mpris.on('seek', function () {
    console.log('seek', arguments);
  });

  var songInfo = {};
  ipc.on('change:song', function handleSongChange (evt, _songInfo) {
    mpris.metadata = songInfo = {
      'mpris:artUrl': _songInfo.art,
      // Convert milliseconds to microseconds (1s = 1e3ms = 1e6µs)
      'mpris:length': _songInfo.duration * 1e3,
      'xesam:album': _songInfo.album,
      'xesam:artist': _songInfo.artist,
      'xesam:title': _songInfo.title
    };
  });

  ipc.on('change:playback-time', function handlePlaybackUpdate (evt, playbackInfo) {
    // Convert milliseconds to microseconds (1s = 1e3ms = 1e6µs)
    var newPosition = playbackInfo.current * 1e3;
    var newTotal = playbackInfo.total * 1e3;

    // If the total has been updated, update our songInfo cache
    // DEV: This is due to `google-music.js` not always having an up to date length upon song change
    if (songInfo['mpris:length'] !== newTotal) {
      mpris.metadata = _.extend(songInfo, {
        'mpris:length': newTotal
      });
    }

    // If our position varies by 2 seconds, consider it a seek
    // DEV: Seeked takes the delta (positive/negative depending on position
    var delta = newPosition - mpris.position;
    if (Math.abs(delta) > 2e6) {
      mpris.seeked(delta);
    }
  });

  var playbackStrings = {};
  playbackStrings[GoogleMusic.Playback.PLAYING] = 'Playing';
  playbackStrings[GoogleMusic.Playback.PAUSED] = 'Paused';
  playbackStrings[GoogleMusic.Playback.STOPPED] = 'Stopped';
  ipc.on('change:playback', function handlePlaybackChange (evt, playbackState) {
    mpris.playbackStatus = playbackStrings[playbackState];
  });
};
<|endoftext|>


<commit_before>function ddg_spice_github(api_result) {
    var query = DDG.get_query()
                .replace(/^\s*github\s+/, "");

    var results = api_result.data.repositories;
    if (results.length == 0) return;

    Spice.render({
        data             : api_result,
        header1          : query + " (GitHub)",
        source_url       : 'http://www.github.com/search?q='
                            + encodeURIComponent(query),
        source_name      : 'GitHub',
        spice_name       : 'github',

        template_frame   : 'list',
        template_options: {
            items: results,
            template_item: "github_item",
            single_template: "github",
            show: 3,
            type: 'ul',
        },

        force_big_header : true,
        force_no_fold    : true
    });
}

// Make sure we display only three items.
Handlebars.registerHelper("last_pushed", function(){

    var last_pushed = Math.floor((new Date() - new Date(this.pushed)) / (1000*60*60*24));

    var years_ago = Math.floor(last_pushed / 365);
    if (years_ago >= 1) {
        last_pushed = years_ago + " year" + (years_ago == 1 ? "" : "s") + " ago";
    } else if (last_pushed == 0) {
        last_pushed = "today";
    } else if (last_pushed == 1) {
        last_pushed = "yesterday";
    } else {
        last_pushed = last_pushed + " day" + (last_pushed == 1 ? "" : "s") + " ago";
    }

    return last_pushed;
});
<commit_msg>Github: Removed trailing comma.<commit_after>function ddg_spice_github(api_result) {
    var query = DDG.get_query()
                .replace(/^\s*github\s+/, "");

    var results = api_result.data.repositories;
    if (results.length == 0) return;

    Spice.render({
        data             : api_result,
        header1          : query + " (GitHub)",
        source_url       : 'http://www.github.com/search?q='
                            + encodeURIComponent(query),
        source_name      : 'GitHub',
        spice_name       : 'github',

        template_frame   : 'list',
        template_options: {
            items: results,
            template_item: "github_item",
            single_template: "github",
            show: 3,
            type: 'ul'
        },

        force_big_header : true,
        force_no_fold    : true
    });
}

// Make sure we display only three items.
Handlebars.registerHelper("last_pushed", function(){

    var last_pushed = Math.floor((new Date() - new Date(this.pushed)) / (1000*60*60*24));

    var years_ago = Math.floor(last_pushed / 365);
    if (years_ago >= 1) {
        last_pushed = years_ago + " year" + (years_ago == 1 ? "" : "s") + " ago";
    } else if (last_pushed == 0) {
        last_pushed = "today";
    } else if (last_pushed == 1) {
        last_pushed = "yesterday";
    } else {
        last_pushed = last_pushed + " day" + (last_pushed == 1 ? "" : "s") + " ago";
    }

    return last_pushed;
});
<|endoftext|>


<commit_before>/*
 * Licensed to Cloudkick, Inc ('Cloudkick') under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * Cloudkick licenses this file to You 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 path = require('path');
var net = require('net');
var util = require('util');

var sprintf = require('sprintf').sprintf;
var async = require('async');
var gex = require('gex');

var assert = require('./assert');
var constants = require('./constants');
var testUtil = require('./util');
var coverage = require('./coverage');
var scopeleaks = require('./scopeleaks');

var isValidTestFunctionName = function(name) {
  if (name.indexOf('test') === 0) {
    return true;
  }

  return false;
};

// foo.bar.test.* -> [ foo/bar/test.js, '*' ]
// test. -> [ 'test.js', '*']
// some.path.foo.test_bar* -> [ 'some/path/foo.js', 'test_bar*' ]
function getTestFilePathAndPattern(patternString) {
  if (/\.js$/.test(patternString)) {
    return [ patternString, '*' ];
  }

  var split = patternString.split('.');
  var len = split.length;
  var testFile, basePath, testPath, pattern;

  testFile = sprintf('%s.js', split[len - 2]);
  pattern = split[len - 1] || '*';
  basePath = split.splice(0, len - 2).join('/');
  testPath = path.join(basePath, testFile);

  return [ testPath, pattern ];
}

function SkipError(test, msg) {
  this.test = test;
  this.msg = msg || '';
  Error.call(this, 'skipped');
}

util.inherits(SkipError, Error);

var runInitFunction = function(filePath, callback) {
  var testObj;
  var initModule = null;

  try {
    initModule = require(filePath);
  }
  catch (err) {
    // Invalid init file path provided
    callback();
    return;
  }

  if (initModule) {
    if (initModule.hasOwnProperty(constants.INIT_FUNCTION_NAME)) {
      try {
        initModule[constants.INIT_FUNCTION_NAME](callback);
        return;
      }
      catch (err2) {
        callback();
        return;
      }
    }
  }

  callback();
};

function Test(testName, testFunction, scopeLeaks) {
  this._testName = testName;
  this._testFunction = testFunction;

  this._finished = false;
  this._testObj = null;
  this._assertObj = null;

  this._timeStart = null;
  this._timeEnd = null;
  this._status = null; // 'success' or 'failure'
  this._err = null; // only populated if _status = 'failure'
  this._skipMsg = null;
  this._scopeLeaks = scopeLeaks || false;
  this._leakedVariables = null; // a list of variables which leaked into
                                // global scope
}

Test.prototype._getScopeSnapshot = function(scope) {
  if (!this._scopeLeaks) {
    return null;
  }

  return scopeleaks.getSnapshot(scope);
};

Test.prototype._getLeakedVariables = function(scopeBefore, scopeAfter) {
  if (!this._scopeLeaks) {
    return null;
  }

  return scopeleaks.getDifferences(scopeBefore, scopeAfter);
};

Test.prototype.run = function(callback) {
  var self = this;
  var scopeBefore, scopeAfter;
  var finishCallbackCalled = false;

  function finishCallback() {
    callback(self.getResultObject());
  }

  function finishFunc() {
    if (finishCallbackCalled) {
      // someone called .finish() twice.
      return;
    }

    if (!self._status) {
      scopeAfter = self._getScopeSnapshot(global);
      self._leakedVariables = self._getLeakedVariables(scopeBefore, scopeAfter);

      self._markAsSucceeded();
    }

    self._timeEnd = new Date().getTime();
    finishCallbackCalled = true;
    finishCallback();
  }

  this._testObj = this._getTestObject(finishFunc);
  this._assertObj = this._getAssertObject();

  self._timeStart = new Date().getTime();

  try {
    scopeBefore = this._getScopeSnapshot(global);
    this._testFunction(this._testObj, this._assertObj);

  }
  catch (err) {
    if (!err.hasOwnProperty('message') && err.toString && typeof err.toString === 'function') {
      err.message = err.toString();
    }

    scopeAfter = this._getScopeSnapshot(global);
    this._leakedVariables = this._getLeakedVariables(scopeBefore, scopeAfter);

    if (err instanceof SkipError) {
      this._markAsSkipped(err.msg);
    }
    else {
      this._markAsFailed(err);
    }

    finishFunc();
    return;
  }
};

Test.prototype._getTestObject = function(finishFunc) {
  var self = this;
  var testObj = function test() {
    return finishFunc.apply(undefined, arguments);
  };

  function skipFunc(msg) {
    throw new SkipError(self, msg);
  }

  testObj.finish = finishFunc;
  testObj.skip = skipFunc;

  return testObj;
};

Test.prototype._getAssertObject = function() {
  return assert.getAssertModule(this);
};

Test.prototype._markAsSucceeded = function() {
  this._finished = true;
  this._status = 'success';
};

Test.prototype._markAsFailed = function(err) {
  var stack;

  if (err.hasOwnProperty('test')) {
    delete err.test;
  }

  if (err.stack) {
    // zomg, hacky, but in a later versions of V8 it looks like stack is some
    // kind of special attribute which can't be serialized.
    stack = err.stack.toString();
    delete err.stack;
    err.stack = stack;
  }

  this._finished = true;
  this._err = err;
  this._status = 'failure';
};

Test.prototype._markAsSkipped = function(msg) {
  this._finished = true;
  this._status = 'skipped';
  this._skipMsg = msg;
};

Test.prototype.isRunning = function() {
  return !this._finished;
};

Test.prototype.beforeExit = function(handler) {
  this._beforeExitHandler = handler;
};

Test.prototype.getResultObject = function() {
  var resultObj = {
    'name': this._testName,
    'status': this._status,
    'error': this._err,
    'skip_msg': this._skipMsg,
    'leaked_variables': this._leakedVariables,
    'time_start': this._timeStart,
    'time_end': this._timeEnd
  };

  return resultObj;
};

function TestFile(filePath, options) {
  this._filePath = filePath;
  this._pattern = options['pattern'];

  this._socketPath = options['socket_path'];
  this._fileName = path.basename(this._filePath);
  this._testInitFile = options['init_file'];
  this._timeout = options['timeout'];
  this._concurrency = options['concurrency'];
  this._scopeLeaks = options['scope_leaks'];

  this._tests = [];
  this._uncaughtExceptions = [];

  this._runningTest = null;
}

TestFile.prototype.addTest = function(test) {
  this._tests.push(test);
};

TestFile.prototype.runTests = function(callback) {
  var self = this;
  var i, test, exportedFunctions, exportedFunctionsNames, errName;
  var setUpFunc, tearDownFunc, setUpFuncIndex, tearDownFuncIndex;
  var testName, testFunc, testsLen;
  var callbackCalled = false;
  var testModule = this._filePath.replace(/\.js$/, '');

  function handleEnd() {
    var resultObj;
    if (callbackCalled) {
      return;
    }

    callbackCalled = true;
    callback();
  }

  function onTestDone(test, callback) {
    var resultObj = test.getResultObject();

    self.addTest(test);
    self._reportTestResult(resultObj);
    callback();
  }

  async.series([
    // Obtain the connection
    function(callback) {
      self._getConnection(function(err, connection) {
        if (err) {
          callback(new Error('Unable to establish connection with the master ' +
                             'process'));
          return;
        }

        self._connection = connection;
        callback();
      });
    },

    // if test init file is present, run init function in it
    function(callback) {
      if (!self._testInitFile) {
        callback();
        return;
      }

      runInitFunction(self._testInitFile, callback);
    },

    // Require the test file
    function(callback) {
      var errName;
      try {
        exportedFunctions = require(testModule);
      }
      catch (err) {
        if (err.message.indexOf(testModule) !== -1 &&
            err.message.match(/cannot find module/i)) {
            errName = 'file_does_not_exist';
          }
          else {
            errName = 'uncaught_exception';
          }

          test = new Test(errName, null);
          test._markAsFailed(err);
          self._reportTestResult(test.getResultObject());
          callback(err);
          return;
      }

      exportedFunctionsNames = Object.keys(exportedFunctions);
      exportedFunctionsNames = exportedFunctionsNames.filter(isValidTestFunctionName);
      testsLen = exportedFunctionsNames.length;
      setUpFunc = exportedFunctions[constants.SETUP_FUNCTION_NAME];
      tearDownFunc = exportedFunctions[constants.TEARDOWN_FUNCTION_NAME];

      callback();
    },

    // if setUp function is present, run it
    function(callback){
      if (!setUpFunc) {
        callback();
        return;
      }

      var test = new Test(constants.SETUP_FUNCTION_NAME, setUpFunc,
                          self._scopeLeaks);
      test.run(async.apply(onTestDone, test, callback));
    },

    // Run the tests
    function(callback) {
      var queue;

      if (exportedFunctionsNames.length === 0) {
        callback();
        return;
      }

      function taskFunc(task, _callback) {
        var test = task.test;
        self._runningTest = test;
        test.run(async.apply(onTestDone, task.test, _callback));
      }

      function onDrain() {
        callback();
      }

      queue = async.queue(taskFunc, self._concurrency);
      queue.drain = onDrain;

      for (i = 0; i < testsLen; i++) {
        testName = exportedFunctionsNames[i];
        testFunc = exportedFunctions[testName];

        if (!gex(self._pattern).on(testName)) {
          continue;
        }

        test = new Test(testName, testFunc, self._scopeLeaks);
        queue.push({'test': test});
      }

      if (queue.length() === 0) {
        // No test matched the provided pattern
        callback();
      }

    },

    // if tearDown function is present, run it
    function(callback) {
      if (!tearDownFunc) {
        callback();
        return;
      }

      var test = new Test(constants.TEARDOWN_FUNCTION_NAME, tearDownFunc,
                          self._scopeLeaks);
      test.run(async.apply(onTestDone, test, callback));
    }
  ],

  function(err) {
    handleEnd();
  });
};

TestFile.prototype._getConnection = function(callback) {
  var connection = net.createConnection(this._socketPath);

  connection.on('connect', function onConnect() {
    callback(null, connection);
  });

  connection.on('error', function onError(err) {
    callback(err, null);
  });
};

TestFile.prototype._reportTestResult = function(resultObj) {
  var payload;
  payload = sprintf('%s%s%s%s%s\n', constants.TEST_START_MARKER, this._filePath,
                    constants.DELIMITER, JSON.stringify(resultObj),
                    constants.TEST_END_MARKER);
  this._connection.write(payload);
};

TestFile.prototype._reportTestCoverage = function(coverageObj) {
  this._connection.write(sprintf('%s%s%s%s\n',
                                 this._filePath,
                                 constants.DELIMITER,
                                 coverage.stringifyCoverage(coverageObj),
                                 constants.COVERAGE_END_MARKER));
};

TestFile.prototype._reportTestFileEnd = function() {
  this._connection.end(sprintf('%s%s\n', this._filePath,
                               constants.TEST_FILE_END_MARKER));
};

TestFile.prototype.addUncaughtException = function(err) {
  var test = err.test;

  if (test) {
    if (err instanceof SkipError) {
      test._markAsSkipped(err.msg);
      test._testObj.finish();
    }
    else {
      test._markAsFailed(err);
      test._testObj.finish();
    }

    this.addTest(test);
  }
  else if (this._runningTest) {
    // User did not use our assert module or uncaughtException was thrown
    // somewhere in the async code.
    // Check which test is still running, mark it as failed and finish it.
    test = this._runningTest;
    test._markAsFailed(err);
    test._testObj.finish();
    this.addTest(test);
  }
  else {
    // Can't figure out the origin, just add it to the _uncaughtExceptions
    // array.
    this._uncaughtExceptions.push(err);
  }
};

TestFile.prototype.getResultObject = function(errObj) {
  var i, test, result, name, uncaughtException;
  var testsLen = this._tests.length;
  var uncaughtExceptionsLen = this._uncaughtExceptions.length;

  var resultObj = {
    'file_path': this._filePath,
    'file_name': this._fileName,
    'error': null,
    'timeout': false,
    'stdout': '',
    'stderr': '',
    'tests': {}
  };

  if (errObj) {
    resultObj.error = errObj;
    return resultObj;
  }

  for (i = 0; i < testsLen; i++) {
    test = this._tests[i];
    result = test.getResultObject();
    resultObj.tests[result.name] = result;
  }

  for (i = 0; i < uncaughtExceptionsLen; i++) {
    name = sprintf('uncaught_exception_%d', i + 1);
    uncaughtException = this._uncaughtExceptions[i];
    test = new Test(name, null);
    test._markAsFailed(uncaughtException);
    resultObj.tests[name] = test.getResultObject();
  }

  return resultObj;
};

function registerCustomAssertionFunctions(functions) {
  assert.merge(null, functions);
}

exports.Test = Test;
exports.TestFile = TestFile;
exports.getTestFilePathAndPattern = getTestFilePathAndPattern;

exports.registerCustomAssertionFunctions = registerCustomAssertionFunctions;
<commit_msg>Simplify the condition.<commit_after>/*
 * Licensed to Cloudkick, Inc ('Cloudkick') under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * Cloudkick licenses this file to You 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 path = require('path');
var net = require('net');
var util = require('util');

var sprintf = require('sprintf').sprintf;
var async = require('async');
var gex = require('gex');

var assert = require('./assert');
var constants = require('./constants');
var testUtil = require('./util');
var coverage = require('./coverage');
var scopeleaks = require('./scopeleaks');

var isValidTestFunctionName = function(name) {
  return name.indexOf('test') === 0;
};

// foo.bar.test.* -> [ foo/bar/test.js, '*' ]
// test. -> [ 'test.js', '*']
// some.path.foo.test_bar* -> [ 'some/path/foo.js', 'test_bar*' ]
function getTestFilePathAndPattern(patternString) {
  if (/\.js$/.test(patternString)) {
    return [ patternString, '*' ];
  }

  var split = patternString.split('.');
  var len = split.length;
  var testFile, basePath, testPath, pattern;

  testFile = sprintf('%s.js', split[len - 2]);
  pattern = split[len - 1] || '*';
  basePath = split.splice(0, len - 2).join('/');
  testPath = path.join(basePath, testFile);

  return [ testPath, pattern ];
}

function SkipError(test, msg) {
  this.test = test;
  this.msg = msg || '';
  Error.call(this, 'skipped');
}

util.inherits(SkipError, Error);

var runInitFunction = function(filePath, callback) {
  var testObj;
  var initModule = null;

  try {
    initModule = require(filePath);
  }
  catch (err) {
    // Invalid init file path provided
    callback();
    return;
  }

  if (initModule) {
    if (initModule.hasOwnProperty(constants.INIT_FUNCTION_NAME)) {
      try {
        initModule[constants.INIT_FUNCTION_NAME](callback);
        return;
      }
      catch (err2) {
        callback();
        return;
      }
    }
  }

  callback();
};

function Test(testName, testFunction, scopeLeaks) {
  this._testName = testName;
  this._testFunction = testFunction;

  this._finished = false;
  this._testObj = null;
  this._assertObj = null;

  this._timeStart = null;
  this._timeEnd = null;
  this._status = null; // 'success' or 'failure'
  this._err = null; // only populated if _status = 'failure'
  this._skipMsg = null;
  this._scopeLeaks = scopeLeaks || false;
  this._leakedVariables = null; // a list of variables which leaked into
                                // global scope
}

Test.prototype._getScopeSnapshot = function(scope) {
  if (!this._scopeLeaks) {
    return null;
  }

  return scopeleaks.getSnapshot(scope);
};

Test.prototype._getLeakedVariables = function(scopeBefore, scopeAfter) {
  if (!this._scopeLeaks) {
    return null;
  }

  return scopeleaks.getDifferences(scopeBefore, scopeAfter);
};

Test.prototype.run = function(callback) {
  var self = this;
  var scopeBefore, scopeAfter;
  var finishCallbackCalled = false;

  function finishCallback() {
    callback(self.getResultObject());
  }

  function finishFunc() {
    if (finishCallbackCalled) {
      // someone called .finish() twice.
      return;
    }

    if (!self._status) {
      scopeAfter = self._getScopeSnapshot(global);
      self._leakedVariables = self._getLeakedVariables(scopeBefore, scopeAfter);

      self._markAsSucceeded();
    }

    self._timeEnd = new Date().getTime();
    finishCallbackCalled = true;
    finishCallback();
  }

  this._testObj = this._getTestObject(finishFunc);
  this._assertObj = this._getAssertObject();

  self._timeStart = new Date().getTime();

  try {
    scopeBefore = this._getScopeSnapshot(global);
    this._testFunction(this._testObj, this._assertObj);

  }
  catch (err) {
    if (!err.hasOwnProperty('message') && err.toString && typeof err.toString === 'function') {
      err.message = err.toString();
    }

    scopeAfter = this._getScopeSnapshot(global);
    this._leakedVariables = this._getLeakedVariables(scopeBefore, scopeAfter);

    if (err instanceof SkipError) {
      this._markAsSkipped(err.msg);
    }
    else {
      this._markAsFailed(err);
    }

    finishFunc();
    return;
  }
};

Test.prototype._getTestObject = function(finishFunc) {
  var self = this;
  var testObj = function test() {
    return finishFunc.apply(undefined, arguments);
  };

  function skipFunc(msg) {
    throw new SkipError(self, msg);
  }

  testObj.finish = finishFunc;
  testObj.skip = skipFunc;

  return testObj;
};

Test.prototype._getAssertObject = function() {
  return assert.getAssertModule(this);
};

Test.prototype._markAsSucceeded = function() {
  this._finished = true;
  this._status = 'success';
};

Test.prototype._markAsFailed = function(err) {
  var stack;

  if (err.hasOwnProperty('test')) {
    delete err.test;
  }

  if (err.stack) {
    // zomg, hacky, but in a later versions of V8 it looks like stack is some
    // kind of special attribute which can't be serialized.
    stack = err.stack.toString();
    delete err.stack;
    err.stack = stack;
  }

  this._finished = true;
  this._err = err;
  this._status = 'failure';
};

Test.prototype._markAsSkipped = function(msg) {
  this._finished = true;
  this._status = 'skipped';
  this._skipMsg = msg;
};

Test.prototype.isRunning = function() {
  return !this._finished;
};

Test.prototype.beforeExit = function(handler) {
  this._beforeExitHandler = handler;
};

Test.prototype.getResultObject = function() {
  var resultObj = {
    'name': this._testName,
    'status': this._status,
    'error': this._err,
    'skip_msg': this._skipMsg,
    'leaked_variables': this._leakedVariables,
    'time_start': this._timeStart,
    'time_end': this._timeEnd
  };

  return resultObj;
};

function TestFile(filePath, options) {
  this._filePath = filePath;
  this._pattern = options['pattern'];

  this._socketPath = options['socket_path'];
  this._fileName = path.basename(this._filePath);
  this._testInitFile = options['init_file'];
  this._timeout = options['timeout'];
  this._concurrency = options['concurrency'];
  this._scopeLeaks = options['scope_leaks'];

  this._tests = [];
  this._uncaughtExceptions = [];

  this._runningTest = null;
}

TestFile.prototype.addTest = function(test) {
  this._tests.push(test);
};

TestFile.prototype.runTests = function(callback) {
  var self = this;
  var i, test, exportedFunctions, exportedFunctionsNames, errName;
  var setUpFunc, tearDownFunc, setUpFuncIndex, tearDownFuncIndex;
  var testName, testFunc, testsLen;
  var callbackCalled = false;
  var testModule = this._filePath.replace(/\.js$/, '');

  function handleEnd() {
    var resultObj;
    if (callbackCalled) {
      return;
    }

    callbackCalled = true;
    callback();
  }

  function onTestDone(test, callback) {
    var resultObj = test.getResultObject();

    self.addTest(test);
    self._reportTestResult(resultObj);
    callback();
  }

  async.series([
    // Obtain the connection
    function(callback) {
      self._getConnection(function(err, connection) {
        if (err) {
          callback(new Error('Unable to establish connection with the master ' +
                             'process'));
          return;
        }

        self._connection = connection;
        callback();
      });
    },

    // if test init file is present, run init function in it
    function(callback) {
      if (!self._testInitFile) {
        callback();
        return;
      }

      runInitFunction(self._testInitFile, callback);
    },

    // Require the test file
    function(callback) {
      var errName;
      try {
        exportedFunctions = require(testModule);
      }
      catch (err) {
        if (err.message.indexOf(testModule) !== -1 &&
            err.message.match(/cannot find module/i)) {
            errName = 'file_does_not_exist';
          }
          else {
            errName = 'uncaught_exception';
          }

          test = new Test(errName, null);
          test._markAsFailed(err);
          self._reportTestResult(test.getResultObject());
          callback(err);
          return;
      }

      exportedFunctionsNames = Object.keys(exportedFunctions);
      exportedFunctionsNames = exportedFunctionsNames.filter(isValidTestFunctionName);
      testsLen = exportedFunctionsNames.length;
      setUpFunc = exportedFunctions[constants.SETUP_FUNCTION_NAME];
      tearDownFunc = exportedFunctions[constants.TEARDOWN_FUNCTION_NAME];

      callback();
    },

    // if setUp function is present, run it
    function(callback){
      if (!setUpFunc) {
        callback();
        return;
      }

      var test = new Test(constants.SETUP_FUNCTION_NAME, setUpFunc,
                          self._scopeLeaks);
      test.run(async.apply(onTestDone, test, callback));
    },

    // Run the tests
    function(callback) {
      var queue;

      if (exportedFunctionsNames.length === 0) {
        callback();
        return;
      }

      function taskFunc(task, _callback) {
        var test = task.test;
        self._runningTest = test;
        test.run(async.apply(onTestDone, task.test, _callback));
      }

      function onDrain() {
        callback();
      }

      queue = async.queue(taskFunc, self._concurrency);
      queue.drain = onDrain;

      for (i = 0; i < testsLen; i++) {
        testName = exportedFunctionsNames[i];
        testFunc = exportedFunctions[testName];

        if (!gex(self._pattern).on(testName)) {
          continue;
        }

        test = new Test(testName, testFunc, self._scopeLeaks);
        queue.push({'test': test});
      }

      if (queue.length() === 0) {
        // No test matched the provided pattern
        callback();
      }

    },

    // if tearDown function is present, run it
    function(callback) {
      if (!tearDownFunc) {
        callback();
        return;
      }

      var test = new Test(constants.TEARDOWN_FUNCTION_NAME, tearDownFunc,
                          self._scopeLeaks);
      test.run(async.apply(onTestDone, test, callback));
    }
  ],

  function(err) {
    handleEnd();
  });
};

TestFile.prototype._getConnection = function(callback) {
  var connection = net.createConnection(this._socketPath);

  connection.on('connect', function onConnect() {
    callback(null, connection);
  });

  connection.on('error', function onError(err) {
    callback(err, null);
  });
};

TestFile.prototype._reportTestResult = function(resultObj) {
  var payload;
  payload = sprintf('%s%s%s%s%s\n', constants.TEST_START_MARKER, this._filePath,
                    constants.DELIMITER, JSON.stringify(resultObj),
                    constants.TEST_END_MARKER);
  this._connection.write(payload);
};

TestFile.prototype._reportTestCoverage = function(coverageObj) {
  this._connection.write(sprintf('%s%s%s%s\n',
                                 this._filePath,
                                 constants.DELIMITER,
                                 coverage.stringifyCoverage(coverageObj),
                                 constants.COVERAGE_END_MARKER));
};

TestFile.prototype._reportTestFileEnd = function() {
  this._connection.end(sprintf('%s%s\n', this._filePath,
                               constants.TEST_FILE_END_MARKER));
};

TestFile.prototype.addUncaughtException = function(err) {
  var test = err.test;

  if (test) {
    if (err instanceof SkipError) {
      test._markAsSkipped(err.msg);
      test._testObj.finish();
    }
    else {
      test._markAsFailed(err);
      test._testObj.finish();
    }

    this.addTest(test);
  }
  else if (this._runningTest) {
    // User did not use our assert module or uncaughtException was thrown
    // somewhere in the async code.
    // Check which test is still running, mark it as failed and finish it.
    test = this._runningTest;
    test._markAsFailed(err);
    test._testObj.finish();
    this.addTest(test);
  }
  else {
    // Can't figure out the origin, just add it to the _uncaughtExceptions
    // array.
    this._uncaughtExceptions.push(err);
  }
};

TestFile.prototype.getResultObject = function(errObj) {
  var i, test, result, name, uncaughtException;
  var testsLen = this._tests.length;
  var uncaughtExceptionsLen = this._uncaughtExceptions.length;

  var resultObj = {
    'file_path': this._filePath,
    'file_name': this._fileName,
    'error': null,
    'timeout': false,
    'stdout': '',
    'stderr': '',
    'tests': {}
  };

  if (errObj) {
    resultObj.error = errObj;
    return resultObj;
  }

  for (i = 0; i < testsLen; i++) {
    test = this._tests[i];
    result = test.getResultObject();
    resultObj.tests[result.name] = result;
  }

  for (i = 0; i < uncaughtExceptionsLen; i++) {
    name = sprintf('uncaught_exception_%d', i + 1);
    uncaughtException = this._uncaughtExceptions[i];
    test = new Test(name, null);
    test._markAsFailed(uncaughtException);
    resultObj.tests[name] = test.getResultObject();
  }

  return resultObj;
};

function registerCustomAssertionFunctions(functions) {
  assert.merge(null, functions);
}

exports.Test = Test;
exports.TestFile = TestFile;
exports.getTestFilePathAndPattern = getTestFilePathAndPattern;

exports.registerCustomAssertionFunctions = registerCustomAssertionFunctions;
<|endoftext|>


<commit_before>exports.init = function (dbname) {
    return [
        `CREATE DATABASE ${dbname};`,
        `CREATE TABLE ${dbname}.dbo.sources     (id INTEGER NOT NULL PRIMARY KEY IDENTITY(1,1), name constchar(255));`,
        `CREATE TABLE ${dbname}.dbo.datapoints  (id INTEGER NOT NULL PRIMARY KEY IDENTITY(1,1), name constchar(255), type INTEGER);`,
        `CREATE TABLE ${dbname}.dbo.ts_number   (id INTEGER, ts BIGINT, val REAL, ack BIT, _from INTEGER, q INTEGER);`,
        `CREATE TABLE ${dbname}.dbo.ts_string   (id INTEGER, ts BIGINT, val TEXT, ack BIT, _from INTEGER, q INTEGER);`,
        `CREATE TABLE ${dbname}.dbo.ts_bool     (id INTEGER, ts BIGINT, val BIT,  ack BIT, _from INTEGER, q INTEGER);`,
        `CREATE TABLE ${dbname}.dbo.ts_counter  (id INTEGER, ts BIGINT, val REAL);`
    ];
};

exports.destroy = function (dbname) {
    return [
        `DROP TABLE ${dbname}.dbo.ts_counter;`,
        `DROP TABLE ${dbname}.dbo.ts_number;`,
        `DROP TABLE ${dbname}.dbo.ts_string;`,
        `DROP TABLE ${dbname}.dbo.ts_bool;`,
        `DROP TABLE ${dbname}.dbo.sources;`,
        `DROP TABLE ${dbname}.dbo.datapoints;`,
        `DROP DATABASE ${dbname};`,
        `DBCC FREEPROCCACHE;`
    ];
};

exports.getFirstTs = function (dbname, db) {
    return `SELECT id, MIN(ts) AS ts FROM ${dbname}.dbo.${db} GROUP BY id;`;
};

exports.insert = function (dbname, index, state, from, db) {
    if (!state || state.val === null || state.val === undefined) {
        state.val = 'NULL';
    } else if (db === 'ts_bool') {
        state.val = state.val ? 1 : 0;
    }else if (db === 'ts_string') {
        state.val = `'${state.val.toString().replace(/'/g, '')}'`;
    }

    if (db === 'ts_counter') {
        return `INSERT INTO ${dbname}.dbo.${db} (id, ts, val, ack, _from, q) VALUES(` + index + `, ` + state.ts + `, ` + state.val + `);`;
    } else {
        return `INSERT INTO ${dbname}.dbo.${db} (id, ts, val, ack, _from, q) VALUES(${index}, ${state.ts}, ${state.val}, ${state.ack ? 1 : 0}, ${from || 0}, ${state.q || 0});`;
    }
};

exports.retention = function (dbname, index, db, retention) {
    const d = new Date();
    d.setSeconds(-retention);
    let query = `DELETE FROM ${dbname}.dbo.${db} WHERE`;
    query += ` id=` + index;
    query += ` AND ts < ` + d.getTime();
    query += `;`;
    return query;
};

exports.getIdSelect = function (dbname, name) {
    if (!name) {
        return `SELECT id, type, name FROM ${dbname}.dbo.datapoints;`;
    } else {
        return `SELECT id, type, name FROM ${dbname}.dbo.datapoints WHERE name='${name}';`;
    }
};

exports.getIdInsert = function (dbname, name, type) {
    return  `INSERT INTO ${dbname}.dbo.datapoints (name, type) VALUES('${name}', ${type});`;
};

exports.getIdUpdate = function (dbname, id, type) {
    return  `UPDATE ${dbname}.dbo.datapoints SET type=${type} WHERE id=${id};`;
};

exports.getFromSelect = function (dbname, from) {
    if (from) {
        return `SELECT id FROM ${dbname}.dbo.sources WHERE name='${from}';`;
    } else {
        return `SELECT id, name FROM ${dbname}.dbo.sources;`;

    }
};

exports.getFromInsert = function (dbname, from) {
    return `INSERT INTO ${dbname}.dbo.sources (name) VALUES('${from}');`;
};

exports.getCounterDiff = function (dbname, options) {
    // Take first real value after start
    const subQueryStart =          `SELECT TOP 1 val, ts FROM ${dbname}.dbo.ts_number  WHERE ${dbname}.dbo.ts_number.id=${options.id} AND ${dbname}.dbo.ts_number.ts>=${options.start} AND ${dbname}.dbo.ts_number.ts<${options.end} AND ${dbname}.dbo.ts_number.val IS NOT NULL ORDER BY ${dbname}.dbo.ts_number.ts ASC`;
    // Take last real value before end
    const subQueryEnd   =          `SELECT TOP 1 val, ts FROM ${dbname}.dbo.ts_number  WHERE ${dbname}.dbo.ts_number.id=${options.id} AND ${dbname}.dbo.ts_number.ts>=${options.start} AND ${dbname}.dbo.ts_number.ts<${options.end} AND ${dbname}.dbo.ts_number.val IS NOT NULL ORDER BY ${dbname}.dbo.ts_number.ts DESC`;
    // Take last value before start
    const subQueryFirst =          `SELECT TOP 1 val, ts FROM ${dbname}.dbo.ts_number  WHERE ${dbname}.dbo.ts_number.id=${options.id} AND ${dbname}.dbo.ts_number.ts< ${options.start} ORDER BY ${dbname}.dbo.ts_number.ts DESC`;
    // Take next value after end
    const subQueryLast =           `SELECT TOP 1 val, ts FROM ${dbname}.dbo.ts_number  WHERE ${dbname}.dbo.ts_number.id=${options.id} AND ${dbname}.dbo.ts_number.ts>=${options.end}   ORDER BY ${dbname}.dbo.ts_number.ts ASC`;
    // get values from counters where counter values changed
    const subQueryCounterChanges = `SELECT       val, ts FROM ${dbname}.dbo.ts_counter WHERE ${dbname}.dbo.ts_number.id=${options.id} AND ${dbname}.dbo.ts_number.ts>${options.start} AND ${dbname}.dbo.ts_number.ts<${options.end} AND ${dbname}.dbo.ts_number.val IS NOT NULL ORDER BY ${dbname}.dbo.ts_number.ts ASC`;

    return `${subQueryFirst} ` +
        `UNION ALL (${subQueryStart}) a ` +
        `UNION ALL (${subQueryEnd}) b ` +
        `UNION ALL (${subQueryLast}) c` +
        `UNION ALL (${subQueryCounterChanges}) d`;
};

exports.getHistory = function (dbname, db, options) {
    let query = `SELECT `;
    if (!options.start && options.count) {
        query += ` TOP ` + options.count;
    }
    query += ` ts, val` +
        (!options.id  ? (`, ${db}.id as id`) : '') +
        (options.ack  ? `, ack` : '') +
        (options.from ? (`, ${dbname}.dbo.sources.name as 'from'`) : '') +
        (options.q    ? `, q` : '') + ` FROM ${dbname}.dbo.` + db;

    if (options.from) {
        query += ` INNER JOIN ${dbname}.dbo.sources ON ${dbname}.dbo.sources.id=${dbname}.dbo.${db}._from`;
    }

    let where = '';

    if (options.id) {
        where += ` ${dbname}.dbo.${db}.id=` + options.id;
    }
    if (options.end) {
        where += (where ? ` AND` : '') + ` ${dbname}.dbo.${db}.ts < ` + options.end;
    }
    if (options.start) {
        where += (where ? ` AND` : '') + ` ${dbname}.dbo.${db}.ts >= ` + options.start;

        // add last value before start
        let subQuery;
        let subWhere;
        subQuery = ` SELECT TOP 1 ts, val` +
            (!options.id  ? (`, ${db}.id as id`) : '') +
            (options.ack  ? `, ack` : '') +
            (options.from ? (`, ${dbname}.dbo.sources.name as 'from'`) : '') +
            (options.q    ? `, q` : '') + ` FROM ${dbname}.dbo.` + db;
        if (options.from) {
            subQuery += ` INNER JOIN ${dbname}.dbo.sources ON ${dbname}.dbo.sources.id=${dbname}.dbo.${db}._from`;
        }
        subWhere = '';
        if (options.id) {
            subWhere += ` ${dbname}.dbo.${db}.id=` + options.id;
        }
        if (options.ignoreNull) {
            //subWhere += (subWhere ? ` AND` : '') + ` val <> NULL`;
        }
        subWhere += `${subWhere ? ` AND` : ''} ${dbname}.dbo.${db}.ts < ` + options.start;
        if (subWhere) subQuery += ` WHERE ` + subWhere;
        subQuery += ` ORDER BY ${dbname}.dbo.${db}.ts DESC`;
        where += ` UNION SELECT * FROM (${subQuery}) a`;

        // add next value after end
        subQuery = ` SELECT TOP 1 ts, val` +
            (!options.id  ? (`, ${db}.id as id`) : '') +
            (options.ack  ? `, ack` : '') +
            (options.from ? (`, ${dbname}.dbo.sources.name as 'from'`) : '') +
            (options.q    ? `, q` : '') + ` FROM ${dbname}.dbo.${db}`;
        if (options.from) {
            subQuery += ` INNER JOIN ${dbname}.dbo.sources ON ${dbname}.dbo.sources.id=${dbname}.dbo.${db}._from`;
        }
        subWhere = '';
        if (options.id) {
            subWhere += ` ${dbname}.dbo.${db}.id=${options.id}`;
        }
        if (options.ignoreNull) {
            //subWhere += (subWhere ? ` AND` : '') + ` val <> NULL`;
        }
        subWhere += (subWhere ? ` AND` : '') + ` ${dbname}.dbo.${db}.ts >= ${options.end}`;
        if (subWhere) subQuery += ` WHERE ${subWhere}`;
        subQuery += ` ORDER BY ${dbname}.dbo.${db}.ts ASC`;
        where += ` UNION SELECT * FROM (${subQuery}) b`;
    }

    if (where) query += ` WHERE ` + where;

    query += ` ORDER BY ts`;
    if (!options.start && options.count) {
        query += ` DESC`;
    } else {
        query += ` ASC`;
    }
    query += `;`;

    return query;
};
<commit_msg>fix mssql query broken by search and replace<commit_after>exports.init = function (dbname) {
    return [
        `CREATE DATABASE ${dbname};`,
        `CREATE TABLE ${dbname}.dbo.sources     (id INTEGER NOT NULL PRIMARY KEY IDENTITY(1,1), name varchar(255));`,
        `CREATE TABLE ${dbname}.dbo.datapoints  (id INTEGER NOT NULL PRIMARY KEY IDENTITY(1,1), name varchar(255), type INTEGER);`,
        `CREATE TABLE ${dbname}.dbo.ts_number   (id INTEGER, ts BIGINT, val REAL, ack BIT, _from INTEGER, q INTEGER);`,
        `CREATE TABLE ${dbname}.dbo.ts_string   (id INTEGER, ts BIGINT, val TEXT, ack BIT, _from INTEGER, q INTEGER);`,
        `CREATE TABLE ${dbname}.dbo.ts_bool     (id INTEGER, ts BIGINT, val BIT,  ack BIT, _from INTEGER, q INTEGER);`,
        `CREATE TABLE ${dbname}.dbo.ts_counter  (id INTEGER, ts BIGINT, val REAL);`
    ];
};

exports.destroy = function (dbname) {
    return [
        `DROP TABLE ${dbname}.dbo.ts_counter;`,
        `DROP TABLE ${dbname}.dbo.ts_number;`,
        `DROP TABLE ${dbname}.dbo.ts_string;`,
        `DROP TABLE ${dbname}.dbo.ts_bool;`,
        `DROP TABLE ${dbname}.dbo.sources;`,
        `DROP TABLE ${dbname}.dbo.datapoints;`,
        `DROP DATABASE ${dbname};`,
        `DBCC FREEPROCCACHE;`
    ];
};

exports.getFirstTs = function (dbname, db) {
    return `SELECT id, MIN(ts) AS ts FROM ${dbname}.dbo.${db} GROUP BY id;`;
};

exports.insert = function (dbname, index, state, from, db) {
    if (!state || state.val === null || state.val === undefined) {
        state.val = 'NULL';
    } else if (db === 'ts_bool') {
        state.val = state.val ? 1 : 0;
    }else if (db === 'ts_string') {
        state.val = `'${state.val.toString().replace(/'/g, '')}'`;
    }

    if (db === 'ts_counter') {
        return `INSERT INTO ${dbname}.dbo.${db} (id, ts, val, ack, _from, q) VALUES(` + index + `, ` + state.ts + `, ` + state.val + `);`;
    } else {
        return `INSERT INTO ${dbname}.dbo.${db} (id, ts, val, ack, _from, q) VALUES(${index}, ${state.ts}, ${state.val}, ${state.ack ? 1 : 0}, ${from || 0}, ${state.q || 0});`;
    }
};

exports.retention = function (dbname, index, db, retention) {
    const d = new Date();
    d.setSeconds(-retention);
    let query = `DELETE FROM ${dbname}.dbo.${db} WHERE`;
    query += ` id=` + index;
    query += ` AND ts < ` + d.getTime();
    query += `;`;
    return query;
};

exports.getIdSelect = function (dbname, name) {
    if (!name) {
        return `SELECT id, type, name FROM ${dbname}.dbo.datapoints;`;
    } else {
        return `SELECT id, type, name FROM ${dbname}.dbo.datapoints WHERE name='${name}';`;
    }
};

exports.getIdInsert = function (dbname, name, type) {
    return  `INSERT INTO ${dbname}.dbo.datapoints (name, type) VALUES('${name}', ${type});`;
};

exports.getIdUpdate = function (dbname, id, type) {
    return  `UPDATE ${dbname}.dbo.datapoints SET type=${type} WHERE id=${id};`;
};

exports.getFromSelect = function (dbname, from) {
    if (from) {
        return `SELECT id FROM ${dbname}.dbo.sources WHERE name='${from}';`;
    } else {
        return `SELECT id, name FROM ${dbname}.dbo.sources;`;

    }
};

exports.getFromInsert = function (dbname, from) {
    return `INSERT INTO ${dbname}.dbo.sources (name) VALUES('${from}');`;
};

exports.getCounterDiff = function (dbname, options) {
    // Take first real value after start
    const subQueryStart =          `SELECT TOP 1 val, ts FROM ${dbname}.dbo.ts_number  WHERE ${dbname}.dbo.ts_number.id=${options.id} AND ${dbname}.dbo.ts_number.ts>=${options.start} AND ${dbname}.dbo.ts_number.ts<${options.end} AND ${dbname}.dbo.ts_number.val IS NOT NULL ORDER BY ${dbname}.dbo.ts_number.ts ASC`;
    // Take last real value before end
    const subQueryEnd   =          `SELECT TOP 1 val, ts FROM ${dbname}.dbo.ts_number  WHERE ${dbname}.dbo.ts_number.id=${options.id} AND ${dbname}.dbo.ts_number.ts>=${options.start} AND ${dbname}.dbo.ts_number.ts<${options.end} AND ${dbname}.dbo.ts_number.val IS NOT NULL ORDER BY ${dbname}.dbo.ts_number.ts DESC`;
    // Take last value before start
    const subQueryFirst =          `SELECT TOP 1 val, ts FROM ${dbname}.dbo.ts_number  WHERE ${dbname}.dbo.ts_number.id=${options.id} AND ${dbname}.dbo.ts_number.ts< ${options.start} ORDER BY ${dbname}.dbo.ts_number.ts DESC`;
    // Take next value after end
    const subQueryLast =           `SELECT TOP 1 val, ts FROM ${dbname}.dbo.ts_number  WHERE ${dbname}.dbo.ts_number.id=${options.id} AND ${dbname}.dbo.ts_number.ts>=${options.end}   ORDER BY ${dbname}.dbo.ts_number.ts ASC`;
    // get values from counters where counter values changed
    const subQueryCounterChanges = `SELECT       val, ts FROM ${dbname}.dbo.ts_counter WHERE ${dbname}.dbo.ts_number.id=${options.id} AND ${dbname}.dbo.ts_number.ts>${options.start} AND ${dbname}.dbo.ts_number.ts<${options.end} AND ${dbname}.dbo.ts_number.val IS NOT NULL ORDER BY ${dbname}.dbo.ts_number.ts ASC`;

    return `${subQueryFirst} ` +
        `UNION ALL (${subQueryStart}) a ` +
        `UNION ALL (${subQueryEnd}) b ` +
        `UNION ALL (${subQueryLast}) c` +
        `UNION ALL (${subQueryCounterChanges}) d`;
};

exports.getHistory = function (dbname, db, options) {
    let query = `SELECT `;
    if (!options.start && options.count) {
        query += ` TOP ` + options.count;
    }
    query += ` ts, val` +
        (!options.id  ? (`, ${db}.id as id`) : '') +
        (options.ack  ? `, ack` : '') +
        (options.from ? (`, ${dbname}.dbo.sources.name as 'from'`) : '') +
        (options.q    ? `, q` : '') + ` FROM ${dbname}.dbo.` + db;

    if (options.from) {
        query += ` INNER JOIN ${dbname}.dbo.sources ON ${dbname}.dbo.sources.id=${dbname}.dbo.${db}._from`;
    }

    let where = '';

    if (options.id) {
        where += ` ${dbname}.dbo.${db}.id=` + options.id;
    }
    if (options.end) {
        where += (where ? ` AND` : '') + ` ${dbname}.dbo.${db}.ts < ` + options.end;
    }
    if (options.start) {
        where += (where ? ` AND` : '') + ` ${dbname}.dbo.${db}.ts >= ` + options.start;

        // add last value before start
        let subQuery;
        let subWhere;
        subQuery = ` SELECT TOP 1 ts, val` +
            (!options.id  ? (`, ${db}.id as id`) : '') +
            (options.ack  ? `, ack` : '') +
            (options.from ? (`, ${dbname}.dbo.sources.name as 'from'`) : '') +
            (options.q    ? `, q` : '') + ` FROM ${dbname}.dbo.` + db;
        if (options.from) {
            subQuery += ` INNER JOIN ${dbname}.dbo.sources ON ${dbname}.dbo.sources.id=${dbname}.dbo.${db}._from`;
        }
        subWhere = '';
        if (options.id) {
            subWhere += ` ${dbname}.dbo.${db}.id=` + options.id;
        }
        if (options.ignoreNull) {
            //subWhere += (subWhere ? ` AND` : '') + ` val <> NULL`;
        }
        subWhere += `${subWhere ? ` AND` : ''} ${dbname}.dbo.${db}.ts < ` + options.start;
        if (subWhere) subQuery += ` WHERE ` + subWhere;
        subQuery += ` ORDER BY ${dbname}.dbo.${db}.ts DESC`;
        where += ` UNION SELECT * FROM (${subQuery}) a`;

        // add next value after end
        subQuery = ` SELECT TOP 1 ts, val` +
            (!options.id  ? (`, ${db}.id as id`) : '') +
            (options.ack  ? `, ack` : '') +
            (options.from ? (`, ${dbname}.dbo.sources.name as 'from'`) : '') +
            (options.q    ? `, q` : '') + ` FROM ${dbname}.dbo.${db}`;
        if (options.from) {
            subQuery += ` INNER JOIN ${dbname}.dbo.sources ON ${dbname}.dbo.sources.id=${dbname}.dbo.${db}._from`;
        }
        subWhere = '';
        if (options.id) {
            subWhere += ` ${dbname}.dbo.${db}.id=${options.id}`;
        }
        if (options.ignoreNull) {
            //subWhere += (subWhere ? ` AND` : '') + ` val <> NULL`;
        }
        subWhere += (subWhere ? ` AND` : '') + ` ${dbname}.dbo.${db}.ts >= ${options.end}`;
        if (subWhere) subQuery += ` WHERE ${subWhere}`;
        subQuery += ` ORDER BY ${dbname}.dbo.${db}.ts ASC`;
        where += ` UNION SELECT * FROM (${subQuery}) b`;
    }

    if (where) query += ` WHERE ` + where;

    query += ` ORDER BY ts`;
    if (!options.start && options.count) {
        query += ` DESC`;
    } else {
        query += ` ASC`;
    }
    query += `;`;

    return query;
};
<|endoftext|>


<commit_before>(function(env) {
var quixey_image_domain = "d1z22zla46lb9g.cloudfront.net";

// spice callback function
env.ddg_spice_quixey = function(api_result) {

    var q = api_result.q.replace(/\s/g, '+');

    var category_regexp = new RegExp([
            "action",
            "adventure",
            "arcade",
            "board",
            "business",
            "casino",
            // "calculator",
            "design",
            "developer tools",
            "dice",
            "education",
            "educational",
            "entertainment",
            "family",
            "finance",
            "fitness",
            "graphics and design",
            "graphics",
            "health and fitness",
            "health",
            "kids",
            "lifestyle",
            "map",
            "medical",
            "music",
            "navigation",
            "networking",
            "news",
            "photography",
            "productivity",
            "puzzle",
            "racing",
            "role playing",
            "simulation",
            "social networking",
            "social",
            "sports",
            "strategy",
            "travel",
            "trivia",
            "utilities",
            "video",
            "weather"
        ].join("|"), "i");

    Spice.add({
        id: 'quixey',
        name: 'Apps',

        data: api_result.results,   //relevants,

        meta: {
            // count is now computed by Spice
            // count: relevants.length,
            total: api_result.results.length,
            itemType: 'Apps',
            sourceName: 'Quixey',
            sourceUrl: 'https://www.quixey.com/search?q=' + q,
            sourceLogo: {
                url: DDG.get_asset_path('quixey','quixey_logo.png'),
                width: '45',
                height: '12'
            }
        },

        normalize : function(item) {

            // relevancy pre-filter: skip ones that have less than three reviews
            if (!item.rating_count || item.rating_count < 3)
                return null;

            var icon_url = make_icon_url(item), screenshot;

            if (!icon_url)
                return null;

            screenshot = DDG.getProperty(item, 'editions.0.screenshots.0.image_url');

            if (!screenshot)
                return null;

            return {
                'img':           icon_url,
                'title':         item.name,
                'heading':       item.name,
                'ratingData':    {
                                   stars: item.rating,
                                   reviews: item.rating_count
                               },
                'url_review':    item.dir_url,
                'price':         pricerange(item),
                'abstract':      item.short_desc || "",
                'brand':         (item.developer && item.developer.name) || "",
                'products_buy':  Spice.quixey.quixey_buy,

                // this should be the array of screenshots with captions
                // and check for the existence of them
                'img_m': quixey_image(screenshot)
            };
        },

        relevancy: {
            type: DDG.get_query().match(category_regexp) ? "category" : "primary",

            skip_words: [
                "android",
                "app",
                "apple app store",
                "apple app",
                "application",
                "applications",
                "apps",
                "best",
                "blackberry",
                "download",
                "downloaded",
                "droid",
                "enhanced",
                "free",
                "fully featured",
                "google play store",
                "google play",
                "ios",
                "ipad",
                "iphone",
                "ipod touch",
                "ipod",
                "most",
                "playbook",
                "popular",
                "release data",
                "release",
                "sale",
                "search",
                "windows mobile",
                "windows phone 8",
                "windows phone"
            ],

            category: [
                { required: 'icon_url' },
                { key: 'short_desc' },
                { key: 'name' },
                { key: 'custom.features.category', match: category_regexp, strict:false }    // strict means this key has to contain a category phrase or we reject
            ],

            primary: [
                { required: 'icon_url' }, // would like to add  alt: 'platforms.0.icon_url'
                { key: 'name' },
                { key: 'short_desc', strict: false }
            ],

            // secondary:  [
            //     { key: 'short_desc' },
            //     { key: 'name' },
            //     { key: 'custom.features.category' },
            //     { required: 'icon_url' }
            // ],

            // field for de-duplication
            // currently single value, could be differentiated by type
            // eg { 'category': 'name', 'primary': 'id' }
            dup: 'name'

        },

        // sort field definition: how this data can be sorted. each field may appear in a UI drop-down.
        sort_fields: {
            'rating': //Spice.rating_comparator('rating', 'rating_count')
                function(a,b) {
                    var rank = function(r,count) {  // will be moved up
                        return r*r*r*count/5;
                    };
                    return rank(a.rating,a.rating_count) > rank(b.rating, b.rating_count) ? -1: 1;
                }
            // 'platform': function(a,b) { } ,
            // 'name':
            // 'price':
        },

        // default sorting- how the data will be initially arranged.
        // depends on the type of search. here only a 'category' search will
        // perform a default initial sort.
        // 'primary' is not here, meaning will remain in api_result order.
        // sort_default: { <sorttype>:<field>, <sorttype>:<field>, ... }
        sort_default: { 'category': 'rating' },

        templates: {
            item: DDG.templates.products_item,
            item_variant: 'short',
            detail: DDG.templates.products_detail,
            item_detail: DDG.templates.products_item_detail
        }
    });

}

// format a price
// p is expected to be a number
function qprice(p) {
    "use strict";

    if (p == 0) {    // == type coercion is ok here
        return "FREE";
    }

    return "$" + (p/100).toFixed(2).toString();
}

// template helper for price formatting
// {{price x}}
Handlebars.registerHelper("qprice", function(obj) {
    "use strict";

    return qprice(obj);
});

var quixey_image = function(image_url) {
    return "http://" + quixey_image_domain + image_url.match(/\/image\/.+/)[0];
}

var make_icon_url = function(item) {
    var domain = quixey_image_domain,
        icon_url = item.icon_url;

    if (!icon_url) {
        // console.warn("quixey: icon_url is null for %o", item);
        if (item.editions && item.editions[0].icon_url)
            icon_url = item.editions[0].icon_url;
        else
            return null;
    }

    // Get the image server that the icon_url in platforms is pointing to.
    // It's not ideal, but the link to the app's image still has to redirect
    // and it redirects to HTTPS. What we want is an HTTP link (for speed).
    if (item.platforms && item.platforms.length > 0 && item.platforms[0].icon_url) {
        domain = item.platforms[0].icon_url.match(/https?:\/\/([^\/]+)/)[1];
    }

    // Replace the domain in our icon_url to the one that we got from
    // the platforms array.
    // return "/iu/?u=http://" + domain + item.icon_url.match(/\/image\/.+/)[0] + "&f=1";
    return "http://" + domain + icon_url.match(/\/image\/.+/)[0];
};

// template helper to format a price range
var pricerange = function(item) {

    if (!item || !item.editions)
        return "";

    var low  = item.editions[0].cents;
    var high = item.editions[0].cents;
    var tmp, range, lowp, highp;

    for (var i in item.editions) {
        tmp = item.editions[i].cents;
        if (tmp < low) low = tmp;
        if (tmp > high) high = tmp;
    }

    lowp = qprice(low);

    if (high > low) {
       highp = qprice(high);
       range = lowp + " - " + highp;
       item.hasPricerange = true;
    } else {
        range = lowp;
    }

    return range;
};

Handlebars.registerHelper("pricerange", function() {
    return pricerange(this);
});

// template helper to replace iphone and ipod icons with
// smaller 'Apple' icons
Handlebars.registerHelper("platform_icon", function(icon_url) {
    "use strict";

    if (this.id === 2004 || this.id === 2015) {
        return "https://icons.duckduckgo.com/i/itunes.apple.com.ico";
    }

    // return "/iu/?u=" + icon_url + "&f=1";
    return icon_url;
});

// template helper that returns and unifies platform names
Handlebars.registerHelper("platform_name", function() {
    "use strict";

    var name;
    var platforms = this.platforms;

    name = platforms[0].name;

    if (platforms.length > 1) {
        switch (platforms[0].name) {
            case "iPhone" :
            case "iPad" :
                name = "iOS";
                break;

            case "Blackberry":
            case "Blackberry 10":
                name = "Blackberry";
                break;
        }
    }

    return name;
});

})(this);
<commit_msg>quixey: use quoted built-in templates<commit_after>(function(env) {
var quixey_image_domain = "d1z22zla46lb9g.cloudfront.net";

// spice callback function
env.ddg_spice_quixey = function(api_result) {

    var q = api_result.q.replace(/\s/g, '+');

    var category_regexp = new RegExp([
            "action",
            "adventure",
            "arcade",
            "board",
            "business",
            "casino",
            // "calculator",
            "design",
            "developer tools",
            "dice",
            "education",
            "educational",
            "entertainment",
            "family",
            "finance",
            "fitness",
            "graphics and design",
            "graphics",
            "health and fitness",
            "health",
            "kids",
            "lifestyle",
            "map",
            "medical",
            "music",
            "navigation",
            "networking",
            "news",
            "photography",
            "productivity",
            "puzzle",
            "racing",
            "role playing",
            "simulation",
            "social networking",
            "social",
            "sports",
            "strategy",
            "travel",
            "trivia",
            "utilities",
            "video",
            "weather"
        ].join("|"), "i");

    Spice.add({
        id: 'quixey',
        name: 'Apps',

        data: api_result.results,   //relevants,

        meta: {
            // count is now computed by Spice
            // count: relevants.length,
            total: api_result.results.length,
            itemType: 'Apps',
            sourceName: 'Quixey',
            sourceUrl: 'https://www.quixey.com/search?q=' + q,
            sourceLogo: {
                url: DDG.get_asset_path('quixey','quixey_logo.png'),
                width: '45',
                height: '12'
            }
        },

        normalize : function(item) {

            // relevancy pre-filter: skip ones that have less than three reviews
            if (!item.rating_count || item.rating_count < 3)
                return null;

            var icon_url = make_icon_url(item), screenshot;

            if (!icon_url)
                return null;

            screenshot = DDG.getProperty(item, 'editions.0.screenshots.0.image_url');

            if (!screenshot)
                return null;

            return {
                'img':           icon_url,
                'title':         item.name,
                'heading':       item.name,
                'ratingData':    {
                                   stars: item.rating,
                                   reviews: item.rating_count
                               },
                'url_review':    item.dir_url,
                'price':         pricerange(item),
                'abstract':      item.short_desc || "",
                'brand':         (item.developer && item.developer.name) || "",
                'products_buy':  Spice.quixey.quixey_buy,

                // this should be the array of screenshots with captions
                // and check for the existence of them
                'img_m': quixey_image(screenshot)
            };
        },

        relevancy: {
            type: DDG.get_query().match(category_regexp) ? "category" : "primary",

            skip_words: [
                "android",
                "app",
                "apple app store",
                "apple app",
                "application",
                "applications",
                "apps",
                "best",
                "blackberry",
                "download",
                "downloaded",
                "droid",
                "enhanced",
                "free",
                "fully featured",
                "google play store",
                "google play",
                "ios",
                "ipad",
                "iphone",
                "ipod touch",
                "ipod",
                "most",
                "playbook",
                "popular",
                "release data",
                "release",
                "sale",
                "search",
                "windows mobile",
                "windows phone 8",
                "windows phone"
            ],

            category: [
                { required: 'icon_url' },
                { key: 'short_desc' },
                { key: 'name' },
                { key: 'custom.features.category', match: category_regexp, strict:false }    // strict means this key has to contain a category phrase or we reject
            ],

            primary: [
                { required: 'icon_url' }, // would like to add  alt: 'platforms.0.icon_url'
                { key: 'name' },
                { key: 'short_desc', strict: false }
            ],

            // secondary:  [
            //     { key: 'short_desc' },
            //     { key: 'name' },
            //     { key: 'custom.features.category' },
            //     { required: 'icon_url' }
            // ],

            // field for de-duplication
            // currently single value, could be differentiated by type
            // eg { 'category': 'name', 'primary': 'id' }
            dup: 'name'

        },

        // sort field definition: how this data can be sorted. each field may appear in a UI drop-down.
        sort_fields: {
            'rating': //Spice.rating_comparator('rating', 'rating_count')
                function(a,b) {
                    var rank = function(r,count) {  // will be moved up
                        return r*r*r*count/5;
                    };
                    return rank(a.rating,a.rating_count) > rank(b.rating, b.rating_count) ? -1: 1;
                }
            // 'platform': function(a,b) { } ,
            // 'name':
            // 'price':
        },

        // default sorting- how the data will be initially arranged.
        // depends on the type of search. here only a 'category' search will
        // perform a default initial sort.
        // 'primary' is not here, meaning will remain in api_result order.
        // sort_default: { <sorttype>:<field>, <sorttype>:<field>, ... }
        sort_default: { 'category': 'rating' },

        templates: {
            item: 'products_item',
            item_variant: 'short',
            detail: 'products_detail',
            item_detail: 'products_item_detail'
        }
    });

}

// format a price
// p is expected to be a number
function qprice(p) {
    "use strict";

    if (p == 0) {    // == type coercion is ok here
        return "FREE";
    }

    return "$" + (p/100).toFixed(2).toString();
}

// template helper for price formatting
// {{price x}}
Handlebars.registerHelper("qprice", function(obj) {
    "use strict";

    return qprice(obj);
});

var quixey_image = function(image_url) {
    return "http://" + quixey_image_domain + image_url.match(/\/image\/.+/)[0];
}

var make_icon_url = function(item) {
    var domain = quixey_image_domain,
        icon_url = item.icon_url;

    if (!icon_url) {
        // console.warn("quixey: icon_url is null for %o", item);
        if (item.editions && item.editions[0].icon_url)
            icon_url = item.editions[0].icon_url;
        else
            return null;
    }

    // Get the image server that the icon_url in platforms is pointing to.
    // It's not ideal, but the link to the app's image still has to redirect
    // and it redirects to HTTPS. What we want is an HTTP link (for speed).
    if (item.platforms && item.platforms.length > 0 && item.platforms[0].icon_url) {
        domain = item.platforms[0].icon_url.match(/https?:\/\/([^\/]+)/)[1];
    }

    // Replace the domain in our icon_url to the one that we got from
    // the platforms array.
    // return "/iu/?u=http://" + domain + item.icon_url.match(/\/image\/.+/)[0] + "&f=1";
    return "http://" + domain + icon_url.match(/\/image\/.+/)[0];
};

// template helper to format a price range
var pricerange = function(item) {

    if (!item || !item.editions)
        return "";

    var low  = item.editions[0].cents;
    var high = item.editions[0].cents;
    var tmp, range, lowp, highp;

    for (var i in item.editions) {
        tmp = item.editions[i].cents;
        if (tmp < low) low = tmp;
        if (tmp > high) high = tmp;
    }

    lowp = qprice(low);

    if (high > low) {
       highp = qprice(high);
       range = lowp + " - " + highp;
       item.hasPricerange = true;
    } else {
        range = lowp;
    }

    return range;
};

Handlebars.registerHelper("pricerange", function() {
    return pricerange(this);
});

// template helper to replace iphone and ipod icons with
// smaller 'Apple' icons
Handlebars.registerHelper("platform_icon", function(icon_url) {
    "use strict";

    if (this.id === 2004 || this.id === 2015) {
        return "https://icons.duckduckgo.com/i/itunes.apple.com.ico";
    }

    // return "/iu/?u=" + icon_url + "&f=1";
    return icon_url;
});

// template helper that returns and unifies platform names
Handlebars.registerHelper("platform_name", function() {
    "use strict";

    var name;
    var platforms = this.platforms;

    name = platforms[0].name;

    if (platforms.length > 1) {
        switch (platforms[0].name) {
            case "iPhone" :
            case "iPad" :
                name = "iOS";
                break;

            case "Blackberry":
            case "Blackberry 10":
                name = "Blackberry";
                break;
        }
    }

    return name;
});

})(this);
<|endoftext|>


<commit_before>'use strict';

// VARIABLES //

var MAX_ITER = 1075; // 1023+52 (subnormals)
var MAX_BITS = 54; // only 53 bits for fraction

// MULT2 //

/**
* FUNCTION: mult2( x )
*	Converts a fraction to a literal bit representation using the multiply-by-2 algorithm.
*
* @param {Number} x - number less than 1
* @returns {String} bit representation
*/
function mult2( x ) {
	var str;
	var y;
	var i;
	var j;

	str = '';
	if ( x === 0 ) {
		return str;
	}
	j = MAX_ITER;

	// Each time we multiply by 2 and find a ones digit, add a '1'; otherwise, add a '0'..
	for ( i = 0; i < MAX_ITER; i++ ) {
		y = x * 2;
		if ( y >= 1 ) {
			x = y - 1;
			str += '1';
			if ( j === MAX_ITER ) {
				j = i; // first '1'
			}
		} else {
			x = y;
			str += '0';
		}
		if ( y === 1 || i-j > MAX_BITS ) {
			break;
		}
	}
	return str;
} // end FUNCTION mult2()

// EXPORTS //

module.exports = mult2;
<commit_msg>Add code comment<commit_after>'use strict';

// VARIABLES //

var MAX_ITER = 1075; // 1023+52 (subnormals)
var MAX_BITS = 54; // only 53 bits for fraction

// MULT2 //

/**
* FUNCTION: mult2( x )
*	Converts a fraction to a literal bit representation using the multiply-by-2 algorithm.
*
* @param {Number} x - number less than 1
* @returns {String} bit representation
*/
function mult2( x ) {
	var str;
	var y;
	var i;
	var j;

	str = '';
	if ( x === 0 ) {
		return str;
	}
	j = MAX_ITER;

	// Each time we multiply by 2 and find a ones digit, add a '1'; otherwise, add a '0'..
	for ( i = 0; i < MAX_ITER; i++ ) {
		y = x * 2;
		if ( y >= 1 ) {
			x = y - 1;
			str += '1';
			if ( j === MAX_ITER ) {
				j = i; // first '1'
			}
		} else {
			x = y;
			str += '0';
		}
		// Stop when we have no more decimals to process or in the event we found a fraction which cannot be represented in a finite number of bits...
		if ( y === 1 || i-j > MAX_BITS ) {
			break;
		}
	}
	return str;
} // end FUNCTION mult2()

// EXPORTS //

module.exports = mult2;
<|endoftext|>


<commit_before>'use strict';

foo bar // A test of Travis and gitter

let math = require('./math');

module.exports = {
    get Number () {
        if (typeof this._Number === 'undefined')
            this.Number = Number;
        return this._Number;
    },
    set Number (v) {
        math.applyNames(v);
        this._Number = v;
    },

    get UncertainNumber () {
        if (typeof this._UncertainNumber === 'undefined')
            this.UncertainNumber = require('./UncertainNumber');
        return this._UncertainNumber;
    },
    set UncertainNumber (v) {
        math.applyNames(v);
        this._UncertainNumber = v;
    },

    get Quantity () {
        if (typeof this._Quantity === 'undefined')
            this.Quantity = require('./quantity');
        return this._Quantity;
    },
    set Quantity (v) {
        math.applyNames(v);
        this._Quantity = v;
    },

    get Expression () {
        if (typeof this._Expression === 'undefined')
            this.Expression = require('./expression');
        return this._Expression;
    },
    set Expression (v) {
        this._Expression = v;
    },
};
<commit_msg>Gitter is now reporting travis failures<commit_after>'use strict';

let math = require('./math');

module.exports = {
    get Number () {
        if (typeof this._Number === 'undefined')
            this.Number = Number;
        return this._Number;
    },
    set Number (v) {
        math.applyNames(v);
        this._Number = v;
    },

    get UncertainNumber () {
        if (typeof this._UncertainNumber === 'undefined')
            this.UncertainNumber = require('./UncertainNumber');
        return this._UncertainNumber;
    },
    set UncertainNumber (v) {
        math.applyNames(v);
        this._UncertainNumber = v;
    },

    get Quantity () {
        if (typeof this._Quantity === 'undefined')
            this.Quantity = require('./quantity');
        return this._Quantity;
    },
    set Quantity (v) {
        math.applyNames(v);
        this._Quantity = v;
    },

    get Expression () {
        if (typeof this._Expression === 'undefined')
            this.Expression = require('./expression');
        return this._Expression;
    },
    set Expression (v) {
        this._Expression = v;
    },
};
<|endoftext|>