<commit_before>const assert = require('assert');
const events = require('events');
const Entity = require('azure-entities');
const data = require('./data');
const debug = require('debug')('hooks:scheduler');
const Promise = require('promise');
const taskcluster = require('taskcluster-client');
const nextDate = require('./nextdate');
const taskcreator = require('./taskcreator');
const _ = require('lodash');

/**
 * The Scheduler will periodically check for tasks in azure storage that are
 * in need of scheduling, by polling at some periodic rate. Hooks that have
 * a defined schedule will be run if it's schedule is valid and the next
 * scheduled date is in the past.
 */
class Scheduler extends events.EventEmitter {
  /** Create a Scheduler instance.
   *
   * options:
   * {
   *   Hook:          // instance of data.Hook
   *   taskcreator:   // instance of taskcreator.TaskCreator
   *   pollingDelay:  // number of ms to sleep between polling
   * }
   * */
  constructor(options) {
    super();
    assert(options, 'options must be given');
    assert(options.Hook.prototype instanceof data.Hook,
      'Expected data.Hook instance');
    assert(options.taskcreator instanceof taskcreator.TaskCreator,
      'An instance of taskcreator.TaskCreator is required');
    assert(typeof options.pollingDelay === 'number',
      'Expected pollingDelay to be a number');
    // Store options on this for use in event handlers
    this.Hook         = options.Hook;
    this.taskcreator  = options.taskcreator;
    this.notify          = options.notify;
    this.pollingDelay = options.pollingDelay;

    // Promise that the polling is done
    this.done         = null;

    // Boolean that the polling should stop
    this.stopping     = false;
  }

  /** Start polling */
  start() {
    if (this.done) {
      return;
    }
    this.stopping = false;

    // Create a promise that we're done looping
    this.done = this.loopUntilStopped().catch((err) => {
      debug('Error: %s, as JSON: %j', err, err, err.stack);
      this.emit('error', err);
    }).then(() => {
      this.done = null;
    });
  }

  /** Terminate iteration, returns a promise that polling is stopped */
  terminate() {
    this.stopping = true;
    return this.done;
  }

  async poll() {
    // Get all hooks that have a scheduled date that is earlier than now
    await this.Hook.scan({
      nextScheduledDate:  Entity.op.lessThan(new Date()),
    }, {
      limit: 100,
      handler: (hook) => this.handleHook(hook),
    });
  }

  /** Polls for hooks that need to be scheduled and handles them in a loop */
  async loopUntilStopped() {
    while (!this.stopping) {
      await this.poll();
      await this.sleep(this.pollingDelay);
    }
  }

  /** Sleep for `delay` ms, returns a promise */
  sleep(delay) {
    return new Promise(accept => setTimeout(accept, delay));
  }

  /** Handle spawning a new task for a given hook that needs to be scheduled */
  async handleHook(hook) {
    let lastFire;
    console.log('firing hook %s/%s with taskId %s', hook.hookGroupId, hook.hookId, hook.nextTaskId);
    try {
      await this.taskcreator.fire(hook, {firedBy: 'schedule'}, {
        taskId: hook.nextTaskId,
        // use the next scheduled date as task.created, to ensure idempotency
        created: hook.nextScheduledDate,
        // don't retry, as a 5xx error will cause a retry on the next scheduler
        // polling interval, and we do not want to get behind waiting for each
        // createTask operation to time out
        retry: false,
      });

      lastFire = {
        result: 'success',
        taskId: hook.nextTaskId,
        time: new Date(),
      };
    } catch (err) {
      console.log('Failed to handle hook: %s/%s' +
                  ', with err: %s', hook.hookGroupId, hook.hookId, err);

      // for 500's, pretend nothing happend and we'll try again on the next go-round.
      if (err.statusCode >= 500) {
        return;
      }

      // In the case of a 4xx error, retrying on the next scheduler loop is a
      // waste of time, so consider the hook fired (and failed)
      await this.sendFailureEmail(hook, err);
      lastFire = {
        result: 'error',
        error: err,
        time: new Date(),
      };
    }

    try {
      let oldTaskId = hook.nextTaskId;
      await hook.modify((hook) => {
        // only modify if another scheduler isn't racing with us
        if (hook.nextTaskId === oldTaskId) {
          hook.nextTaskId = taskcluster.slugid();
          hook.nextScheduledDate = nextDate(hook.schedule);
          if (lastFire) {
            hook.lastFire = lastFire;
          }
        }
      });
    } catch (err) {
      console.log('Failed to update hook (will re-fire): %s/%s' +
                  ', with err: %s', hook.hookGroupId, hook.hookId, err);
      return;
    }
  }

  async sendFailureEmail(hook, err) {
    if (!hook.metadata.emailOnError) {
      return;
    }

    let errJson;
    try {
      errJson = JSON.stringify(err, null, 2);
    } catch (e) {
      errJson = `(error formatting JSON: ${e})`;
    }

    let email = this.createEmail(hook, err, errJson);
    await this.notify.email(email);
  }

  createEmail(hook, err, errJson) {
    return {
      address: hook.metadata.owner,
      subject: `[Taskcluster Hooks] Scheduled Hook failure: ${hook.hookGroupId}/${hook.hookId}`,
      content: `The hooks service was unable to create a task for hook ${hook.hookGroupId}/${hook.hookId},
  for which you are listed as owner.

  The error was:
    ${err}

  Details:

    ${errJson}

  The service will try again to create the task on the next iteration.

  Thanks,
  TaskCluster Automation

  P.S. If you believe you have received this email in error, please hit reply to let us know.`,

    };
  }
}

// Export Scheduler
module.exports = Scheduler;
<commit_msg>Bug 1446966 - change console.log() to debug()<commit_after>const assert = require('assert');
const events = require('events');
const Entity = require('azure-entities');
const data = require('./data');
const debug = require('debug')('hooks:scheduler');
const Promise = require('promise');
const taskcluster = require('taskcluster-client');
const nextDate = require('./nextdate');
const taskcreator = require('./taskcreator');
const _ = require('lodash');

/**
 * The Scheduler will periodically check for tasks in azure storage that are
 * in need of scheduling, by polling at some periodic rate. Hooks that have
 * a defined schedule will be run if it's schedule is valid and the next
 * scheduled date is in the past.
 */
class Scheduler extends events.EventEmitter {
  /** Create a Scheduler instance.
   *
   * options:
   * {
   *   Hook:          // instance of data.Hook
   *   taskcreator:   // instance of taskcreator.TaskCreator
   *   pollingDelay:  // number of ms to sleep between polling
   * }
   * */
  constructor(options) {
    super();
    assert(options, 'options must be given');
    assert(options.Hook.prototype instanceof data.Hook,
      'Expected data.Hook instance');
    assert(options.taskcreator instanceof taskcreator.TaskCreator,
      'An instance of taskcreator.TaskCreator is required');
    assert(typeof options.pollingDelay === 'number',
      'Expected pollingDelay to be a number');
    // Store options on this for use in event handlers
    this.Hook         = options.Hook;
    this.taskcreator  = options.taskcreator;
    this.notify          = options.notify;
    this.pollingDelay = options.pollingDelay;

    // Promise that the polling is done
    this.done         = null;

    // Boolean that the polling should stop
    this.stopping     = false;
  }

  /** Start polling */
  start() {
    if (this.done) {
      return;
    }
    this.stopping = false;

    // Create a promise that we're done looping
    this.done = this.loopUntilStopped().catch((err) => {
      debug('Error: %s, as JSON: %j', err, err, err.stack);
      this.emit('error', err);
    }).then(() => {
      this.done = null;
    });
  }

  /** Terminate iteration, returns a promise that polling is stopped */
  terminate() {
    this.stopping = true;
    return this.done;
  }

  async poll() {
    // Get all hooks that have a scheduled date that is earlier than now
    await this.Hook.scan({
      nextScheduledDate:  Entity.op.lessThan(new Date()),
    }, {
      limit: 100,
      handler: (hook) => this.handleHook(hook),
    });
  }

  /** Polls for hooks that need to be scheduled and handles them in a loop */
  async loopUntilStopped() {
    while (!this.stopping) {
      await this.poll();
      await this.sleep(this.pollingDelay);
    }
  }

  /** Sleep for `delay` ms, returns a promise */
  sleep(delay) {
    return new Promise(accept => setTimeout(accept, delay));
  }

  /** Handle spawning a new task for a given hook that needs to be scheduled */
  async handleHook(hook) {
    let lastFire;
    debug('firing hook %s/%s with taskId %s', hook.hookGroupId, hook.hookId, hook.nextTaskId);
    try {
      await this.taskcreator.fire(hook, {firedBy: 'schedule'}, {
        taskId: hook.nextTaskId,
        // use the next scheduled date as task.created, to ensure idempotency
        created: hook.nextScheduledDate,
        // don't retry, as a 5xx error will cause a retry on the next scheduler
        // polling interval, and we do not want to get behind waiting for each
        // createTask operation to time out
        retry: false,
      });

      lastFire = {
        result: 'success',
        taskId: hook.nextTaskId,
        time: new Date(),
      };
    } catch (err) {
      debug('Failed to handle hook: %s/%s, with err: %s', hook.hookGroupId, hook.hookId, err);

      // for 500's, pretend nothing happend and we'll try again on the next go-round.
      if (err.statusCode >= 500) {
        return;
      }

      // In the case of a 4xx error, retrying on the next scheduler loop is a
      // waste of time, so consider the hook fired (and failed)
      await this.sendFailureEmail(hook, err);
      lastFire = {
        result: 'error',
        error: err,
        time: new Date(),
      };
    }

    try {
      let oldTaskId = hook.nextTaskId;
      await hook.modify((hook) => {
        // only modify if another scheduler isn't racing with us
        if (hook.nextTaskId === oldTaskId) {
          hook.nextTaskId = taskcluster.slugid();
          hook.nextScheduledDate = nextDate(hook.schedule);
          if (lastFire) {
            hook.lastFire = lastFire;
          }
        }
      });
    } catch (err) {
      debug('Failed to update hook (will re-fire): %s/%s, with err: %s', hook.hookGroupId, hook.hookId, err);
      return;
    }
  }

  async sendFailureEmail(hook, err) {
    if (!hook.metadata.emailOnError) {
      return;
    }

    let errJson;
    try {
      errJson = JSON.stringify(err, null, 2);
    } catch (e) {
      errJson = `(error formatting JSON: ${e})`;
    }

    let email = this.createEmail(hook, err, errJson);
    await this.notify.email(email);
  }

  createEmail(hook, err, errJson) {
    return {
      address: hook.metadata.owner,
      subject: `[Taskcluster Hooks] Scheduled Hook failure: ${hook.hookGroupId}/${hook.hookId}`,
      content: `The hooks service was unable to create a task for hook ${hook.hookGroupId}/${hook.hookId},
  for which you are listed as owner.

  The error was:
    ${err}

  Details:

    ${errJson}

  The service will try again to create the task on the next iteration.

  Thanks,
  TaskCluster Automation

  P.S. If you believe you have received this email in error, please hit reply to let us know.`,

    };
  }
}

// Export Scheduler
module.exports = Scheduler;
<|endoftext|>


<commit_before>import 'rxjs';  // required to enable all Observable operators
import { combineEpics, createEpicMiddleware } from 'redux-observable';

import getSyncEpic from '../epics/sync';
import getCacheEpic from '../epics/cache';
import getPostEpic from '../epics/post';

/**
 * The middleware is exported as a getter because it needs the application's
 * routes in order to set up the nav-related epic(s) that are part of the
 * final middleware
 */
const getPlatformMiddleware = (routes, fetchQueries) => createEpicMiddleware(
	combineEpics(
		getSyncEpic(routes, fetchQueries),
		getCacheEpic(),
		getPostEpic(fetchQueries)
	)
);

export default getPlatformMiddleware;

<commit_msg>note about server epics<commit_after>import 'rxjs';  // required to enable all Observable operators
import { combineEpics, createEpicMiddleware } from 'redux-observable';

import getSyncEpic from '../epics/sync';
import getCacheEpic from '../epics/cache';
import getPostEpic from '../epics/post';

/**
 * The middleware is exported as a getter because it needs the application's
 * routes in order to set up the nav-related epic(s) that are part of the
 * final middleware
 *
 * **Note** it's unlikely that the server needs any epics other than `sync` in
 * order to render the application. We may want to write a server-specific
 * middleware that doesn't include the other epics if performance is an issue
 */
const getPlatformMiddleware = (routes, fetchQueries) => createEpicMiddleware(
	combineEpics(
		getSyncEpic(routes, fetchQueries),
		getCacheEpic(),
		getPostEpic(fetchQueries)
	)
);

export default getPlatformMiddleware;

<|endoftext|>


<commit_before>function reloadExtensions()
{
	// find all unpacked extensions and reload them
	chrome.management.getAll(function(a) {
		var ext = {};
		for (var i = 0; i < a.length; i++) {
			ext = a[i];
			if ((ext.installType=="development") &&
				(ext.enabled == true) &&
				(ext.name != "Extensions Reloader")) {
					console.log(ext.name + " reloaded");
					(function(extensionId) {
						// disable
						chrome.management.setEnabled(ext.id, false, function() {
							// re-enable
							chrome.management.setEnabled(extensionId, true);
						});
					})(ext.id);
			}
		}
	});

	// show an "OK" badge
	chrome.browserAction.setBadgeText({text: "OK"});
	chrome.browserAction.setBadgeBackgroundColor({color: "#4cb749"});
	setTimeout(function() {
		chrome.browserAction.setBadgeText({text: ""});
	}, 1000);
}

// intercept url
chrome.webRequest.onBeforeRequest.addListener(
  function(details) {
    if (details.url.indexOf("http://reload.extensions") >= 0) {
		reloadExtensions();
		chrome.tabs.get(details.tabId, function(tab) {
			if (tab.selected == false) {
				chrome.tabs.remove(details.tabId);
			}
		});
		return {
			// close the newly opened window
			redirectUrl: chrome.extension.getURL("close.html")
		};
    }

	return {cancel: false};
  },
  {
    urls: ["http://reload.extensions/"],
    types: ["main_frame"]
  },
  ["blocking"]
);

chrome.browserAction.onClicked.addListener(function(tab) {
	reloadExtensions();
});<commit_msg>Re-launch packaged apps.<commit_after>function reloadExtensions()
{
	// find all unpacked extensions and reload them
	chrome.management.getAll(function(a) {
		var ext = {};
		for (var i = 0; i < a.length; i++) {
			ext = a[i];
			if ((ext.installType=="development") &&
				(ext.enabled == true) &&
				(ext.name != "Extensions Reloader")) {
					console.log(ext.name + " reloaded");
                    (function(extensionId, extensionType) {
                        // disable
					    chrome.management.setEnabled(extensionId, false, function () {
					        // re-enable
					        chrome.management.setEnabled(extensionId, true, function () {
					            // re-launch packaged app
					            if (extensionType == "packaged_app") {
					                chrome.management.launchApp(extensionId);
					            }
                            });
                        });
                    })(ext.id, ext.type);
			}
		}
	});

	// show an "OK" badge
	chrome.browserAction.setBadgeText({text: "OK"});
	chrome.browserAction.setBadgeBackgroundColor({color: "#4cb749"});
	setTimeout(function() {
		chrome.browserAction.setBadgeText({text: ""});
	}, 1000);
}

// intercept url
chrome.webRequest.onBeforeRequest.addListener(
  function(details) {
    if (details.url.indexOf("http://reload.extensions") >= 0) {
		reloadExtensions();
		chrome.tabs.get(details.tabId, function(tab) {
			if (tab.selected == false) {
				chrome.tabs.remove(details.tabId);
			}
		});
		return {
			// close the newly opened window
			redirectUrl: chrome.extension.getURL("close.html")
		};
    }

	return {cancel: false};
  },
  {
    urls: ["http://reload.extensions/"],
    types: ["main_frame"]
  },
  ["blocking"]
);

chrome.browserAction.onClicked.addListener(function(tab) {
	reloadExtensions();
});<|endoftext|>


<commit_before>var usedTraffic = null;

var dormitories = {
	"HSS": {
		"dormitoryHome": "http://wh12.tu-dresden.de",
		"dormitoryTrafficSite": "http://wh12.tu-dresden.de/traffic-o-meter.html",
		"dormitoryTraffic": "https://wh12.tu-dresden.de/tom.addon2.php"
		},

	"WU": {
		"dormitoryHome": "http://www.wh2.tu-dresden.de",
		"dormitoryTrafficSite": "http://www.wh2.tu-dresden.de/de/usertraffic",
		"dormitoryTraffic": "http://www.wh2.tu-dresden.de/traffic/getMyTraffic.php"
	},

	"ZEU": {
		"dormitoryHome": "http://zeus.wh25.tu-dresden.de",
		"dormitoryTrafficSite": "http://zeus.wh25.tu-dresden.de/zeuser/agdsnISAPI.php?site=traffic",
		"dormitoryTraffic": "http://zeus.wh25.tu-dresden.de/traffic.php"
	},

	"BOR": {
		"dormitoryHome": "http://wh10.tu-dresden.de",
		"dormitoryTrafficSite": "http://www.wh10.tu-dresden.de/index.php/traffic.html",
		"dormitoryTraffic": "http://wh10.tu-dresden.de/phpskripte/getMyTraffic.php"
	},

	"GER": {
		"dormitoryHome": "http://www.wh17.tu-dresden.de/",
		"dormitoryTrafficSite": "http://www.wh17.tu-dresden.de/traffic/uebersicht",
		"dormitoryTraffic": "http://www.wh17.tu-dresden.de/traffic/prozent"
	}
};

/** Finds the dormitory this computer is in and updates the localStorage.
 *
 * @param cb - Callback that is called with the dormitory's acronym on success. */
function updateDormitory(cb){
	var done = 0;
	var works = null;

	$.each(dormitories, function(key, dormitory){
		/* Try to get traffic status from each dormitory - if server responds with a valid value,
		we can assume that we belong to its network */

		$.get(dormitory.dormitoryTraffic, function(result){
			var value = parseFloat(result);
			if(typeof value === 'number' && value >= 0){
				if(works) throw "Multiple dormitories reported a valid traffic status. Can only belong to one network!";

				works = key;
				localStorage["dormitory"] = key;
			}
		}).always(function(){
			done++;

			// Check if all servers have been asked
			if(done === Object.keys(dormitories).length){
				if(works){
					cb(null, works);
				} else {
					clearState();
					cb("__MSG_nothing__");
				}
			}
		});
	});
}

/** Resets all modified values to their defaults. */
function clearState() {
	chrome.browserAction.setIcon({path:"icon/hide.png"});
	localStorage["dormitory"] = "";
	usedTraffic = null;
}

/** Requests the current traffic usage and updates the icon. */
function updateTraffic(){
	var dorm = localStorage["dormitory"];

	if(!dorm){
		updateDormitory(function(err){
			if(!err){
				updateTraffic();
			} else {
				clearState();
			}
		});
		return;
	}

	$.getJSON(dormitories[dorm].dormitoryTraffic, function(value){
		var value = parseFloat(value);
		if(typeof value === 'number' && value >= 0){
			var name = Math.round((32.0/100) * (value < 100 ? value : 100));
			chrome.browserAction.setIcon({path:"icon/" + name + ".png"});
			usedTraffic = value;
		} else {
			clearState();
			updateTraffic();
		}
	}).fail(function(){
		clearState();
		updateTraffic();
	});
}

chrome.browserAction.setBadgeBackgroundColor({color: [1, 1, 1, 1]});
updateTraffic();
chrome.alarms.onAlarm.addListener(updateTraffic);
chrome.alarms.create("updateTraffic", {periodInMinutes: 2});
<commit_msg>Fixed updateDormitory throwing nonlocalized error<commit_after>var usedTraffic = null;

var dormitories = {
	"HSS": {
		"dormitoryHome": "http://wh12.tu-dresden.de",
		"dormitoryTrafficSite": "http://wh12.tu-dresden.de/traffic-o-meter.html",
		"dormitoryTraffic": "https://wh12.tu-dresden.de/tom.addon2.php"
		},

	"WU": {
		"dormitoryHome": "http://www.wh2.tu-dresden.de",
		"dormitoryTrafficSite": "http://www.wh2.tu-dresden.de/de/usertraffic",
		"dormitoryTraffic": "http://www.wh2.tu-dresden.de/traffic/getMyTraffic.php"
	},

	"ZEU": {
		"dormitoryHome": "http://zeus.wh25.tu-dresden.de",
		"dormitoryTrafficSite": "http://zeus.wh25.tu-dresden.de/zeuser/agdsnISAPI.php?site=traffic",
		"dormitoryTraffic": "http://zeus.wh25.tu-dresden.de/traffic.php"
	},

	"BOR": {
		"dormitoryHome": "http://wh10.tu-dresden.de",
		"dormitoryTrafficSite": "http://www.wh10.tu-dresden.de/index.php/traffic.html",
		"dormitoryTraffic": "http://wh10.tu-dresden.de/phpskripte/getMyTraffic.php"
	},

	"GER": {
		"dormitoryHome": "http://www.wh17.tu-dresden.de/",
		"dormitoryTrafficSite": "http://www.wh17.tu-dresden.de/traffic/uebersicht",
		"dormitoryTraffic": "http://www.wh17.tu-dresden.de/traffic/prozent"
	}
};

/** Finds the dormitory this computer is in and updates the localStorage.
 *
 * @param cb - Callback that is called with the dormitory's acronym on success. */
function updateDormitory(cb){
	var done = 0;
	var works = null;

	$.each(dormitories, function(key, dormitory){
		/* Try to get traffic status from each dormitory - if server responds with a valid value,
		we can assume that we belong to its network */

		$.get(dormitory.dormitoryTraffic, function(result){
			var value = parseFloat(result);
			if(typeof value === 'number' && value >= 0){
				if(works) throw "Multiple dormitories reported a valid traffic status. Can only belong to one network!";

				works = key;
				localStorage["dormitory"] = key;
			}
		}).always(function(){
			done++;

			// Check if all servers have been asked
			if(done === Object.keys(dormitories).length){
				if(works){
					cb(null, works);
				} else {
					clearState();
					cb(chrome.i18n.getMessage("nothing"));
				}
			}
		});
	});
}

/** Resets all modified values to their defaults. */
function clearState() {
	chrome.browserAction.setIcon({path:"icon/hide.png"});
	localStorage["dormitory"] = "";
	usedTraffic = null;
}

/** Requests the current traffic usage and updates the icon. */
function updateTraffic(){
	var dorm = localStorage["dormitory"];

	if(!dorm){
		updateDormitory(function(err){
			if(!err){
				updateTraffic();
			} else {
				clearState();
			}
		});
		return;
	}

	$.getJSON(dormitories[dorm].dormitoryTraffic, function(value){
		var value = parseFloat(value);
		if(typeof value === 'number' && value >= 0){
			var name = Math.round((32.0/100) * (value < 100 ? value : 100));
			chrome.browserAction.setIcon({path:"icon/" + name + ".png"});
			usedTraffic = value;
		} else {
			clearState();
			updateTraffic();
		}
	}).fail(function(){
		clearState();
		updateTraffic();
	});
}

chrome.browserAction.setBadgeBackgroundColor({color: [1, 1, 1, 1]});
updateTraffic();
chrome.alarms.onAlarm.addListener(updateTraffic);
chrome.alarms.create("updateTraffic", {periodInMinutes: 2});
<|endoftext|>


<commit_before>const Sequelize = require('sequelize');

module.exports = function () {
  const app = this;
  const connectionString = app.get('postgres');
  const sequelize = new Sequelize(connectionString, {
    dialect: 'postgres',
    logging: false,
    define: {
      freezeTableName: true
    }
  });
  const oldSetup = app.setup;

  app.set('sequelizeClient', sequelize);

  app.setup = function (...args) {
    const result = oldSetup.apply(this, args);

    // Set up data relationships
    const models = sequelize.models;
    Object.keys(models).forEach(name => {
      if ('associate' in models[name]) {
        models[name].associate(models);
      }
    });

    // Sync to the database
    sequelize.sync({force: true});

    return result;
  };
};
<commit_msg>removed db sync force true<commit_after>const Sequelize = require('sequelize');

module.exports = function () {
  const app = this;
  const connectionString = app.get('postgres');
  const sequelize = new Sequelize(connectionString, {
    dialect: 'postgres',
    logging: false,
    define: {
      freezeTableName: true
    }
  });
  const oldSetup = app.setup;

  app.set('sequelizeClient', sequelize);

  app.setup = function (...args) {
    const result = oldSetup.apply(this, args);

    // Set up data relationships
    const models = sequelize.models;
    Object.keys(models).forEach(name => {
      if ('associate' in models[name]) {
        models[name].associate(models);
      }
    });

    // Sync to the database
    sequelize.sync();

    return result;
  };
};
<|endoftext|>


<commit_before>var FF_MAX_SUGGESTIONS = 5;
var FF_MAX_MATCHLENGTH = 1000;
var FF_DEBUGGING = false;
var FF_INCLUDE_HISTORY = true;
var ffHistory = [];

function ffEscapeRegExp(str) {
  return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
}

function ffActivateTag(tab) {
  if(tab.url) {
    chrome.tabs.update({
      url: tab.url
    });
  }
  if(tab.tabId) {
    chrome.tabs.update(tab.tabId, {active: true});
  }
  if(tab.windowId) {
    chrome.windows.update(tab.windowId, {focused: true});
  }
}

function ffEscapeHtml(unsafe) {
  return unsafe.replace(/&/g, "&")
               .replace(/</g, "<")
               .replace(/>/g, ">")
               .replace(/"/g, """)
               .replace(/'/g, "'");
}

function ffGetHostname(url) {
  var a  = document.createElement('a');
  a.href = url;
  return a.hostname;
}

function ffHighlightText(text, words) {
  words
  .map(function(word) { return ffRegexpExactHl(word); })
  .forEach(function(word) {
    if(text.match(word)) {
      text = text.replace(word, "\0$1\1")
    }
  });

  words
  .map(function(word) { return ffRegexpFuzzy(word); })
  .forEach(function(word) {
    if(text.match(word)) {
      text = text.replace(word, function(m) {
        return "\0" + m + "\1";
      });
    }
  });
  return ffEscapeHtml(text).replace(new RegExp("\0", "g"), "<match>")
                           .replace(new RegExp("\1", "g"), "</match>");
}

function ffCalculateScoreWords(tab, words) {
  var score = 0;
  var hostname = ffGetHostname(tab.url);

  var found_words = words.map(function() { return false; })

  words
  .map(function(word) { return ffRegexpExact(word); })
  .forEach(function(word, i) {
    if(hostname.match(word)) { score += 100; found_words[i] = true; }
    if(tab.title.match(word)) { score += 100; found_words[i] = true; }
    if(tab.url.match(word)) { score += 100; found_words[i] = true; }
  });

  words
  .map(function(word) { return ffRegexpFuzzy(word); })
  .forEach(function(word, i) {
    if(found_words[i]) { return; }
    if(hostname.match(word)) { score += 20; found_words[i] = true; }
    if(tab.title.match(word)) { score += 20; found_words[i] = true; }
    if(tab.url.match(word)) { score += 10; found_words[i] = true; }
  });

  if(tab.visitCount) {
    score += tab.visitCount * 10;
  }

  if(tab.lastVisitTime) {
    score += tab.lastVisitTime / (new Date().getTime());
  }

  if(found_words.filter(function(x) { return x; }).length !== words.length) { score = 0; }
  if(score > 0 && tab.pinned) { score += 1000; }

  if(FF_DEBUGGING && score > 0) { console.debug("tab", tab.title); }
  return score;
}

function ffRegexpExact(word) {
  return new RegExp(ffEscapeRegExp(word), 'i');
}

function ffRegexpExactHl(word) {
  return new RegExp("(" + ffEscapeRegExp(word) + ")", 'ig');
}

function ffRegexpFuzzy(word) {
  return new RegExp(word.split('').map(function(ch) { return ffEscapeRegExp(ch); }).join('.{0,10}?'), 'i');
}

function ffPrepareTab(tab, words) {
  var content = JSON.stringify({tabId: tab.id, windowId: tab.windowId});
  var desc = ffHighlightText(tab.title, words) + " <url>" +  ffHighlightText(ffGetHostname(tab.url), words) + "</url>";

  if(FF_DEBUGGING) {
    desc = "score:" + tab.score + " - " + desc;
  }

  if(tab.status && tab.status !== "complete") {
    desc = "[" + tab.status + "] " + desc;
  }

  if(tab.incognito) {
    desc = "<url>[Incognito]</url> " + desc;
  }

  if(tab.pinned) {
    desc = "<url>[Pinned]</url> " + desc;
  }

  if(tab.audible) {
    desc = "<url>[Audible]</url> " + desc;
  }

  if(tab.lastVisitTime) {
    content = JSON.stringify({url: tab.url});
    desc = "<url>[History]</url> " + desc;
  }

  return {content: content, description: desc};
}

function ffFilter(tabs, words) {
  return tabs.map(function(tab) { tab.score = ffCalculateScoreWords(tab, words); return tab; })
             .filter(function(tab) { return tab.score >= 10 && tab.title.length > 0; })
             .sort(function(tab1, tab2) {
                if(tab1.score < tab2.score) return 1;
                if(tab1.score > tab2.score) return -1;
                return 0;
              })
             .slice(0, FF_MAX_SUGGESTIONS)
             ;
}

function ffConcat(tabs1, tabs2) {
  return tabs1.concat(ffTabsWithout(tabs2, tabs1));
}

function ffTabsWithout(whiteTabs, blackTabs) {
  return whiteTabs.filter(function (whiteTab) {
    return 0 === blackTabs.filter(function (blackTab) { return blackTab.url === whiteTab.url; }).length;
  });
}

function ffSearchFor(text, callback) {
  text = text.trim();
  var words = text.split(/\s+/);

  var words_exact_hl = text.split(/\s+/).map(function(word) {
    return ffRegexpExactHl(word);
  });

  chrome.tabs.query({}, function(array_of_tabs) {
    var matching_tabs = ffFilter(array_of_tabs, words);

    if(FF_INCLUDE_HISTORY && matching_tabs.length < FF_MAX_SUGGESTIONS) {
      chrome.history.search({text: "", maxResults: 1000}, function(array_of_history_items) {
        var matching_histories = ffFilter(array_of_history_items, words);
        callback(ffConcat(matching_tabs, matching_histories).map(function(tab) { return ffPrepareTab(tab, words); } ));
      });
      return;
    }

    callback(matching_tabs.map(function(tab) { return ffPrepareTab(tab, words); }));
  });
}

chrome.omnibox.onInputChanged.addListener(
  function(text, suggest) {
    ffSearchFor(text, suggest);
  }
);

chrome.omnibox.onInputEntered.addListener(
  function(text) {
    if(FF_DEBUGGING) {
      console.debug("entered:", text);
      console.debug("history:", ffHistory);
    }

    var selected = {};

    if(text.length === 0) {
      if(ffHistory.length >= 2) {
        selected = ffHistory[ffHistory.length - 2];
      } else {
        return;
      }
    } else {
      try {
        selected = JSON.parse(text);
      } catch(e) {
        // User probably typed something but selected the first default option,
        // i.e., "Run ff command: query"
        ffSearchFor(text, function(suggestions) {
          if(suggestions.length === 0) { return; }
          var selected = JSON.parse(suggestions[0].content);
          ffHistory.push(selected);
          ffActivateTag(selected);
        });
        return;
      }
    }

    ffHistory.push(selected);
    ffActivateTag(selected);
  }
);
<commit_msg>Move tab to 0 index on activation.<commit_after>var FF_MAX_SUGGESTIONS = 5;
var FF_MAX_MATCHLENGTH = 1000;
var FF_DEBUGGING = false;
var FF_INCLUDE_HISTORY = true;
var FF_MOVE_TAB_TO_FIRST = true;
var ffHistory = [];

function ffEscapeRegExp(str) {
  return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
}

function ffActivateTag(tab) {
  if(tab.url) {
    chrome.tabs.update({url: tab.url});
  }
  if(tab.tabId) {
    chrome.tabs.update(tab.tabId, {active: true});
    if(FF_MOVE_TAB_TO_FIRST) {
      chrome.tabs.move(tab.tabId, {index: 0});
    }
  }
  if(tab.windowId) {
    chrome.windows.update(tab.windowId, {focused: true});
  }
}

function ffEscapeHtml(unsafe) {
  return unsafe.replace(/&/g, "&")
               .replace(/</g, "<")
               .replace(/>/g, ">")
               .replace(/"/g, """)
               .replace(/'/g, "'");
}

function ffGetHostname(url) {
  var a  = document.createElement('a');
  a.href = url;
  return a.hostname;
}

function ffHighlightText(text, words) {
  words
  .map(function(word) { return ffRegexpExactHl(word); })
  .forEach(function(word) {
    if(text.match(word)) {
      text = text.replace(word, "\0$1\1")
    }
  });

  words
  .map(function(word) { return ffRegexpFuzzy(word); })
  .forEach(function(word) {
    if(text.match(word)) {
      text = text.replace(word, function(m) {
        return "\0" + m + "\1";
      });
    }
  });
  return ffEscapeHtml(text).replace(new RegExp("\0", "g"), "<match>")
                           .replace(new RegExp("\1", "g"), "</match>");
}

function ffCalculateScoreWords(tab, words) {
  var score = 0;
  var hostname = ffGetHostname(tab.url);

  var found_words = words.map(function() { return false; })

  words
  .map(function(word) { return ffRegexpExact(word); })
  .forEach(function(word, i) {
    if(hostname.match(word)) { score += 100; found_words[i] = true; }
    if(tab.title.match(word)) { score += 100; found_words[i] = true; }
    if(tab.url.match(word)) { score += 100; found_words[i] = true; }
  });

  words
  .map(function(word) { return ffRegexpFuzzy(word); })
  .forEach(function(word, i) {
    if(found_words[i]) { return; }
    if(hostname.match(word)) { score += 20; found_words[i] = true; }
    if(tab.title.match(word)) { score += 20; found_words[i] = true; }
    if(tab.url.match(word)) { score += 10; found_words[i] = true; }
  });

  if(tab.visitCount) {
    score += tab.visitCount * 10;
  }

  if(tab.lastVisitTime) {
    score += tab.lastVisitTime / (new Date().getTime());
  }

  if(found_words.filter(function(x) { return x; }).length !== words.length) { score = 0; }
  if(score > 0 && tab.pinned) { score += 1000; }

  if(FF_DEBUGGING && score > 0) { console.debug("matching tab", hostname, tab); }
  return score;
}

function ffRegexpExact(word) {
  return new RegExp(ffEscapeRegExp(word), 'i');
}

function ffRegexpExactHl(word) {
  return new RegExp("(" + ffEscapeRegExp(word) + ")", 'ig');
}

function ffRegexpFuzzy(word) {
  return new RegExp(word.split('').map(function(ch) { return ffEscapeRegExp(ch); }).join('.{0,10}?'), 'i');
}

function ffPrepareTab(tab, words) {
  var content = JSON.stringify({tabId: tab.id, windowId: tab.windowId});
  var desc = ffHighlightText(tab.title, words) + " <url>" +  ffHighlightText(ffGetHostname(tab.url), words) + "</url>";

  if(FF_DEBUGGING) {
    desc = "score:" + tab.score + " - " + desc;
  }

  if(tab.status && tab.status !== "complete") {
    desc = "[" + tab.status + "] " + desc;
  }

  if(tab.incognito) {
    desc = "<url>[Incognito]</url> " + desc;
  }

  if(tab.pinned) {
    desc = "<url>[Pinned]</url> " + desc;
  }

  if(tab.audible) {
    desc = "<url>[Audible]</url> " + desc;
  }

  if(tab.lastVisitTime) {
    content = JSON.stringify({url: tab.url});
    desc = "<url>[History]</url> " + desc;
  }

  return {content: content, description: desc};
}

function ffFilter(tabs, words) {
  return tabs.map(function(tab) { tab.score = ffCalculateScoreWords(tab, words); return tab; })
             .filter(function(tab) { return tab.score >= 10 && tab.title.length > 0; })
             .sort(function(tab1, tab2) {
                if(tab1.score < tab2.score) return 1;
                if(tab1.score > tab2.score) return -1;
                return 0;
              })
             .slice(0, FF_MAX_SUGGESTIONS)
             ;
}

function ffConcat(tabs1, tabs2) {
  return tabs1.concat(ffTabsWithout(tabs2, tabs1));
}

function ffTabsWithout(whiteTabs, blackTabs) {
  return whiteTabs.filter(function (whiteTab) {
    return 0 === blackTabs.filter(function (blackTab) { return blackTab.url === whiteTab.url; }).length;
  });
}

function ffSearchFor(text, callback) {
  text = text.trim();
  var words = text.split(/\s+/);

  var words_exact_hl = text.split(/\s+/).map(function(word) {
    return ffRegexpExactHl(word);
  });

  chrome.tabs.query({}, function(array_of_tabs) {
    var matching_tabs = ffFilter(array_of_tabs, words);

    if(FF_INCLUDE_HISTORY && matching_tabs.length < FF_MAX_SUGGESTIONS) {
      chrome.history.search({text: "", maxResults: 1000}, function(array_of_history_items) {
        var matching_histories = ffFilter(array_of_history_items, words);
        callback(ffConcat(matching_tabs, matching_histories).map(function(tab) { return ffPrepareTab(tab, words); } ));
      });
      return;
    }

    callback(matching_tabs.map(function(tab) { return ffPrepareTab(tab, words); }));
  });
}

chrome.omnibox.onInputChanged.addListener(
  function(text, suggest) {
    ffSearchFor(text, suggest);
  }
);

chrome.omnibox.onInputEntered.addListener(
  function(text) {
    if(FF_DEBUGGING) {
      console.debug("entered:", text);
      console.debug("history:", ffHistory);
    }

    var selected = {};

    if(text.length === 0) {
      if(ffHistory.length >= 2) {
        selected = ffHistory[ffHistory.length - 2];
      } else {
        return;
      }
    } else {
      try {
        selected = JSON.parse(text);
      } catch(e) {
        // User probably typed something but selected the first default option,
        // i.e., "Run ff command: query"
        ffSearchFor(text, function(suggestions) {
          if(suggestions.length === 0) { return; }
          var selected = JSON.parse(suggestions[0].content);
          ffHistory.push(selected);
          ffActivateTag(selected);
        });
        return;
      }
    }

    ffHistory.push(selected);
    ffActivateTag(selected);
  }
);
<|endoftext|>


<commit_before>/*
Eventually this should become an event driven page instead of a pure background
page. However, only webRequest is currently not availble on event driven pages
for builds outside of the dev channel.
*/

//Used for logging to the background page (bkg.Console.log('foo'))
var bkg = chrome.extension.getBackgroundPage();

//Core data
var data = {
    lastUpdated: {},
    baseURL: "http://bitcoincharts.com/charts/chart.png?",
    options: {
        width: "660",
        height: "192",
        m: "bitfinexUSD",
        SubmitButton: "Draw",
        c: "",
        s: "",
        e: "",
        Prev: "",
        Next: "",
        t: "M",
        b: "",
        a1: "",
        m1: "10",
        a2: "",
        m2: "25",
        x: "0",
        i1: "",
        i2: "",
        i3: "",
        i4: "",
        v: "1",
        cv: "0",
        ps: "0",
        l: "0",
        p: "0",
        ranges: {
            d1: {
                i: "",
                r: ""
            },
            d5: {
                i: "",
                r: ""
            },
            m1: {
                i: "",
                r: ""
            },
            m6: {
                i: "",
                r: ""
            },
            y1: {
                i: "",
                r: ""
            },
            y5: {
                i: "",
                r: ""
            },
            max: {
                i: "",
                r: ""
            }
        }
    },
    result: {}
};

/*
Don't try to do any binds until the page is ready
*/
$(document).ready(function() {
    $(document).bind("ajaxStop", function() {
        bkg.console.log("Starting to build");
        buildContainer();
        bkg.console.log("Finished");
    });

    /*
    Listener for requests coming from Google
    */
    chrome.webRequest.onBeforeRequest.addListener(
        function(details){
        var subs;
            var positive = false;
            var index = details.url.search(/q=/);
            if(index != -1){
                subs = details.url.substring(index);
                subs = subs.substring(2, subs.indexOf('&'));
                positive = checkForBitcoin(subs);
            }
            if(positive){
                //Positive bitcoin match
                updateData();
                bkg.console.log("Bazinga: " + subs);
            //buildContainer();
            }
            return;
        },
        {urls: ["https://www.google.com/*"]} //Do this for all google urls
    );
});

/*
Call all the data update AJAX calls
*/
function updateData() {
    getHashRate();
    getDifficulity();
    getCardData();
    getYesterdayAvg();
}

/*
Generate a URL to use to get an image of BTC market data for the desired range
*/
function buildURL(range) {
    var url = data.baseURL;

    var first = true; //Control the placement of '&'
    for(var key in data.options) {
        if(data.options.hasOwnProperty(key) && key) {
            if(key == "ranges") {
                if(first) {
                    url += "i";
                    first = false;
                } else {
                    url += "&i";
                }

                url += "=" + data.options.ranges[range].i;
                url += "&r" + "=" + data.options.ranges[range].r;
            }

            if(!first) {
                url += "&" + key + "=" + data.options.key;
            } else {
                url += key + "=" + data.options.key;
                first = false;
            }
        }
    }
    return url;
}

/*
Function to be called when the user selects a range button. Causes the image to
be updated by replacing the image src URL with a new one
*/
function updateRange(range) {
    $('#fmob_chart').attr('src', buildURL(range));
}

/*
Checks strings for matches to bitcoin
*/
function checkForBitcoin(subs){
    subs = subs.toLowerCase();
    if( (subs == 'bit%20coin') ||
        (subs == 'bit+coin')   ||
        (subs == 'bitcoin')    ||
        (subs == 'btc')){
        return true;
    }
    return false;
}

/**
 * Fetches the data for the card population
 * returns an array with values
 * avg, cvolume (currency volume), difficulty, ghashrate, high, low, and btc volume
 *
 **/
function getCardData(){
    $.ajax({
    	url:"http://api.bitcoincharts.com/v1/markets.json",
    	dataType: 'json',
    	success : function( json ){
    	    $.each( json , function(key,val){
        		if(val['symbol'] == data.options.m){
        		    data.result = json[val];
        		}
    	    });
    	},
        error: ajaxError
    });
}

function formatDate(val){
	if(val < 10){
        return "0" + val;

    } else {
        return val;
	}
}
function getYesterdayAvg() {
	$.ajax({
		url:"http://api.coindesk.com/v1/bpi/historical/close.json?for=yesterday",
		dataType: 'json',
		success: function ( json ){
			var d = new Date();
			d.setDate(d.getDate()-1);
			var formatted = d.getFullYear()+'-'+formatDate(d.getMonth()+1)+'-'+formatDate((d.getDay()-1));
			bkg.console.log(json['bpi']);
			data.result.yesterday_avg = json['bpi'][formatted];

			getChange();
		},
		error: ajaxError
	});
}
function getChange(){
	data.result.change = data.result.close - data.result.yesterday_avg;
}
function getHashRate() {
    $.ajax({
        url:"http://blockchain.info/q/hashrate",
        success: function( text ){
            data.result.hashrate = Number(text).toPrecision(3);
        },
        error: ajaxError
    });
}

/**
 *Returns the Difficulity rate currently for bitcoin
 **/
function getDifficulity() {
    $.ajax({
        url:"http://blockchain.info/q/getdifficulty",
        success: function( text ){
           data.result.difficulity = Number(text).toPrecision(2);
        },
        error: ajaxError
    });
}

function ajaxError(jqXHR, textStatus, errorThrown) {
    bkg.console.log("AJAX error: " + errorThrown + ": " + textStatus);
}

function buildContainer() {
    var d = new Date();
    var burl = buildURL("d1");
    var domElement = [
        "
",
        "",
        "
",
        "
",
        "
",
        "
",
        "
",
        "
",
        "

",
        "
",
        "
Bitcoin
",
        "" + data.options.m + ": ",
        "BTC",
        " -  ",
        "" + d.toLocalString() + " ",
        "ET",
        "
",
        "
",
        "
",
        "
",
        " " + data.result.close + "",
        "-" + data.result.change + " (" + ((data.result.change >= 0) ? "+" : "-") + data.result.change + "%)",
        "
",
        "
",
        "
",
        "
" ,
        "
",
        "1d",
        "1d",
        "
",
        "
",
        "5d",
        "5d",
        "
",
        "
",
        "1m",
        "1m",
        "
",
        "
",
        "6m",
        "6m",
        "
",
        "
",
        "1y",
        "1y",
        "
",
        "
",
        "5y",
        "5y",
        "
",
        "
",
        "max",
        "max",
        "
",
        "
",
        "
",
        "
",
        "
",
        "
",
        "
Avg
",
        "
High
",
        "
Low
",
        "
",
        "
",
        "
" + data.result.avg + "
",
        "
" + data.result.high + "
",
        "
" + data.result.low + "
",
        "
",
        "
",
        "
",
        "
",
        "
Volume
",
        "
Currency Volume
",
        "
Network Total
",
        "
",
        "
",
        "
" + data.result.volume + "
",
        "
" + data.result.currency_volume + "
",
        "
" + data.result.hashrate + " Ghash/s
",
        "
",
        "
",
        "
",
        "
",
        "
",
        "
",
        "

",
        "
",
        "
",
        "
",
        "
",
        "
",
        "
",
        "Data is not promised to be accurate. Most data delayed 15 or more minutes.",
        "
",
        "
",
        "",
        "
"
    ].join('\n');

    $('#rso').prepend(domElement);
}<commit_msg>lots of logging<commit_after>/*
Eventually this should become an event driven page instead of a pure background
page. However, only webRequest is currently not availble on event driven pages
for builds outside of the dev channel.
*/

//Used for logging to the background page (bkg.Console.log('foo'))
var bkg = chrome.extension.getBackgroundPage();

//Core data
var data = {
    lastUpdated: {},
    baseURL: "http://bitcoincharts.com/charts/chart.png?",
    options: {
        width: "660",
        height: "192",
        m: "bitfinexUSD",
        SubmitButton: "Draw",
        c: "",
        s: "",
        e: "",
        Prev: "",
        Next: "",
        t: "M",
        b: "",
        a1: "",
        m1: "10",
        a2: "",
        m2: "25",
        x: "0",
        i1: "",
        i2: "",
        i3: "",
        i4: "",
        v: "1",
        cv: "0",
        ps: "0",
        l: "0",
        p: "0",
        ranges: {
            d1: {
                i: "",
                r: ""
            },
            d5: {
                i: "",
                r: ""
            },
            m1: {
                i: "",
                r: ""
            },
            m6: {
                i: "",
                r: ""
            },
            y1: {
                i: "",
                r: ""
            },
            y5: {
                i: "",
                r: ""
            },
            max: {
                i: "",
                r: ""
            }
        }
    },
    result: {}
};

/*
Don't try to do any binds until the page is ready
*/
$(document).ready(function() {
    bkg.console.log("Document ready. Attaching listeners");

    $(document).bind("ajaxStop", function() {
        bkg.console.log("Starting to build");
        buildContainer();
        bkg.console.log("Finished");
    });

    /*
    Listener for requests coming from Google
    */
    chrome.webRequest.onBeforeRequest.addListener(
        function(details){
            bkg.console.log("Detected request to be sent to google");
            var subs;
            var positive = false;
            var index = details.url.search(/q=/);
            if(index != -1) {
                subs = details.url.substring(index);
                subs = subs.substring(2, subs.indexOf('&'));
                positive = checkForBitcoin(subs);
            }
            if(positive){ //Positive bitcoin match
                bkg.console.log("Request contained match for bitcoin or btc");
                updateData();
                bkg.console.log("Bazinga: " + subs);
            }
            return;
        },
        {urls: ["https://www.google.com/*"]} //Do this for all google urls
    );
});

/*
Call all the data update AJAX calls
*/
function updateData() {
    bkg.console.log("Starting to fire AJAX data updates in updateData()");
    getHashRate();
    getDifficulity();
    getCardData();
    getYesterdayAvg();
    bkg.console.log("Done firing AJAX data updates in updateData()");
}

/*
Generate a URL to use to get an image of BTC market data for the desired range
*/
function buildURL(range) {
    bkg.console.log("Entered buildURL()");
    var url = data.baseURL;

    var first = true; //Control the placement of '&'
    for(var key in data.options) {
        if(data.options.hasOwnProperty(key) && key) {
            if(key == "ranges") {
                if(first) {
                    url += "i";
                    first = false;
                } else {
                    url += "&i";
                }

                url += "=" + data.options.ranges[range].i;
                url += "&r" + "=" + data.options.ranges[range].r;
            }

            if(!first) {
                url += "&" + key + "=" + data.options.key;
            } else {
                url += key + "=" + data.options.key;
                first = false;
            }
        }
    }

    bkg.console.log("Leaving buildURL() and returning URL: " + url);
    return url;
}

/*
Function to be called when the user selects a range button. Causes the image to
be updated by replacing the image src URL with a new one
*/
function updateRange(range) {
    bkg.console.log("Entered updateRange() with new range: " + range);
    $('#fmob_chart').attr('src', buildURL(range));
    bkg.console.log("Range update complete");
}

/*
Checks strings for matches to bitcoin
*/
function checkForBitcoin(subs){
    subs = subs.toLowerCase();

    return  (
                (subs == 'bit%20coin') ||
                (subs == 'bit+coin')   ||
                (subs == 'bitcoin')    ||
                (subs == 'btc')
            );

    /*
    if( (subs == 'bit%20coin') ||
        (subs == 'bit+coin')   ||
        (subs == 'bitcoin')    ||
        (subs == 'btc')){
        return true;
    }
    return false;
    */
}

/**
 * Fetches the data for the card population
 * returns an array with values
 * avg, cvolume (currency volume), difficulty, ghashrate, high, low, and btc volume
 *
 **/
function getCardData(){
    $.ajax({
    	url:"http://api.bitcoincharts.com/v1/markets.json",
    	dataType: 'json',
    	success : function( json ){
    	    $.each( json , function(key,val){
        		if(val['symbol'] == data.options.m){
        		    data.result = json[val];
        		}
    	    });
    	},
        error: ajaxError
    });
}

function formatDate(val){
	if(val < 10){
        return "0" + val;

    } else {
        return val;
	}
}

function getYesterdayAvg() {
	$.ajax({
		url:"http://api.coindesk.com/v1/bpi/historical/close.json?for=yesterday",
		dataType: 'json',
		success: function ( json ){
			var d = new Date();
			d.setDate(d.getDate()-1);
			var formatted = d.getFullYear()+'-'+formatDate(d.getMonth()+1)+'-'+formatDate((d.getDay()-1));
			bkg.console.log(json['bpi']);
			data.result.yesterday_avg = json['bpi'][formatted];

			getChange();
		},
		error: ajaxError
	});
}
function getChange(){
	data.result.change = data.result.close - data.result.yesterday_avg;
}
function getHashRate() {
    $.ajax({
        url:"http://blockchain.info/q/hashrate",
        success: function( text ){
            data.result.hashrate = Number(text).toPrecision(3);
        },
        error: ajaxError
    });
}

/**
 *Returns the Difficulity rate currently for bitcoin
 **/
function getDifficulity() {
    $.ajax({
        url:"http://blockchain.info/q/getdifficulty",
        success: function( text ){
           data.result.difficulity = Number(text).toPrecision(2);
        },
        error: ajaxError
    });
}

function ajaxError(jqXHR, textStatus, errorThrown) {
    bkg.console.log("AJAX error: " + errorThrown + ": " + textStatus);
}

function buildContainer() {
    bkg.console.log("Inside of buildContainer()");

    var d = new Date();

    bkg.console.log("Date built");

    var domElement = [
        "
",
        "",
        "
",
        "
",
        "
",
        "
",
        "
",
        "
",
        "

",
        "
",
        "
Bitcoin
",
        "" + data.options.m + ": ",
        "BTC",
        " -  ",
        "" + d.toLocalString() + " ",
        "ET",
        "
",
        "
",
        "
",
        "
",
        " " + data.result.close + "",
        "-" + data.result.change + " (" + ((data.result.change >= 0) ? "+" : "-") + data.result.change + "%)",
        "
",
        "
",
        "
",
        "
" ,
        "
",
        "1d",
        "1d",
        "
",
        "
",
        "5d",
        "5d",
        "
",
        "
",
        "1m",
        "1m",
        "
",
        "
",
        "6m",
        "6m",
        "
",
        "
",
        "1y",
        "1y",
        "
",
        "
",
        "5y",
        "5y",
        "
",
        "
",
        "max",
        "max",
        "
",
        "
",
        "
",
        "
",
        "
",
        "
",
        "
Avg
",
        "
High
",
        "
Low
",
        "
",
        "
",
        "
" + data.result.avg + "
",
        "
" + data.result.high + "
",
        "
" + data.result.low + "
",
        "
",
        "
",
        "
",
        "
",
        "
Volume
",
        "
Currency Volume
",
        "
Network Total
",
        "
",
        "
",
        "
" + data.result.volume + "
",
        "
" + data.result.currency_volume + "
",
        "
" + data.result.hashrate + " Ghash/s
",
        "
",
        "
",
        "
",
        "
",
        "
",
        "
",
        "

",
        "
",
        "
",
        "
",
        "
",
        "
",
        "
",
        "Data is not promised to be accurate. Most data delayed 15 or more minutes.",
        "
",
        "
",
        "",
        "
"
    ].join('\n');

    bkg.console.log("object built. injecting...");

    $('#rso').prepend(domElement);

    bkg.console.log("object injected");
}<|endoftext|>


<commit_before>const BADGE_COLORS = {
	error: '#DD1616',
	success: '#717171'
};

(function init() {
	clearCache();
	let query = {
		options: { autorun: true }
	};
	getOptions(query).then(options => {
		if (options.autorun) {
			registerHandlers();
		}
	});
})();

function registerHandlers() {
	chrome.tabs.onCreated.addListener(tab => {
		autoFind(tab.id, tab.url);
	});

	chrome.tabs.onUpdated.addListener((tabId, info, tab) => {
		autoFind(tabId, tab.url);
	});

	chrome.tabs.onActivated.addListener(activeInfo => {
		let tabId = activeInfo.tabId;
		getTabById(tabId)
			.then(tab => autoFind(tab.id, tab.url));
	});
}

function autoFind(tabId, url) {
	if (isAllowed(url)) {
		return findOnReddit(processUrl(url))
			.then(posts => setResultsBadge(tabId, `${posts.length}`))
			.catch(e => handleError(e, tabId));
	}
}

function isAllowed(url) {
	return !isChromeUrl(url);
}

function handleError(e, tabId) {
	return setErrorBadge(tabId);
}

function isChromeUrl(url) {
	return /^chrome/.test(url);
}

function setErrorBadge(tabId) {
	return setBadge(tabId, 'X', BADGE_COLORS.error);
}

function setResultsBadge(tabId, text) {
	return setBadge(tabId, text, BADGE_COLORS.success);
}

function setBadge(tabId, text, color) {
	let badge = { text: text, tabId: tabId };
	let bgCol = { color: color, tabId: tabId };
	return getTabById(tabId).then(tab => {
		if (!chrome.runtime.lastError) {
			chrome.browserAction.setBadgeText(badge);
		}
		if (!chrome.runtime.lastError) {
			chrome.browserAction.setBadgeBackgroundColor(bgCol);
		}
	}).catch(ignoreRejection);
}
<commit_msg>finer event control<commit_after>const BADGE_COLORS = {
	error: '#DD1616',
	success: '#717171'
};

(function init() {
	clearCache();
	let query = {
		options: { autorun: true }
	};
		}
	});
	getOptions(query).then(registerHandlers);
})();

function registerHandlers(options) {
	if (options.autorun.updated) {
		chrome.tabs.onUpdated.addListener((tabId, info, tab) => {
			autoFind(tabId, tab.url);
		});
	}

	if (options.autorun.activated) {
		chrome.tabs.onActivated.addListener(activeInfo => {
			let tabId = activeInfo.tabId;
			getTabById(tabId).then(tab => autoFind(tabId, tab.url));
		});
	}
}

function autoFind(tabId, url) {
	if (isAllowed(url)) {
		return findOnReddit(processUrl(url))
			.then(posts => setResultsBadge(tabId, `${posts.length}`))
			.catch(e => handleError(e, tabId));
	}
}

function isAllowed(url) {
	return !isChromeUrl(url);
}

function handleError(e, tabId) {
	return setErrorBadge(tabId);
}

function isChromeUrl(url) {
	return /^chrome/.test(url);
}

function setErrorBadge(tabId) {
	return setBadge(tabId, 'X', BADGE_COLORS.error);
}

function setResultsBadge(tabId, text) {
	return setBadge(tabId, text, BADGE_COLORS.success);
}

function setBadge(tabId, text, color) {
	let badge = { text: text, tabId: tabId };
	let bgCol = { color: color, tabId: tabId };
	return getTabById(tabId).then(tab => {
		if (!chrome.runtime.lastError) {
			chrome.browserAction.setBadgeText(badge);
		}
		if (!chrome.runtime.lastError) {
			chrome.browserAction.setBadgeBackgroundColor(bgCol);
		}
	}).catch(ignoreRejection);
}
<|endoftext|>


<commit_before>/*! videojs-markers !*/
'use strict';

(function($, videojs, undefined) {
   //default setting
   var defaultSetting = {
      markerStyle: {
         'width':'7px',
         'border-radius': '30%',
         'background-color': 'red'
      },
      markerTip: {
         display: true,
         text: function(marker) {
            return "Break: "+ marker.text;
         }
      },
      breakOverlay:{
         display: false,
         displayTime: 3,
         text: function(marker) {
            return "Break overlay: " + marker.overlayText;
         },
         style: {
            'width':'100%',
            'height': '20%',
            'background-color': 'rgba(0,0,0,0.7)',
            'color': 'white',
            'font-size': '17px'
         }
      },
      onMarkerReached: function(marker) {},
      markers: []
   };

   // create a non-colliding random number
   function generateUUID() {
      var d = new Date().getTime();
      var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
         var r = (d + Math.random()*16)%16 | 0;
         d = Math.floor(d/16);
         return (c=='x' ? r : (r&0x3|0x8)).toString(16);
      });
      return uuid;
   };

   function registerVideoJsMarkersPlugin(options) {
      /**
       * register the markers plugin (dependent on jquery)
       */

      var setting      = $.extend(true, {}, defaultSetting, options),
          markers      = {},
          markersList  = [], // list of markers sorted by time
          videoWrapper = $(this.el()),
          currentMarkerIndex  = -1,
          player       = this,
          markerTip    = null,
          breakOverlay = null,
          overlayIndex = -1;

      function sortMarkersList() {
         // sort the list by time in asc order
         markersList.sort(function(a, b){return a.time - b.time});
      }

      function addMarkers(newMarkers) {
         // create the markers
         var duration = player.duration();

         $.each(newMarkers, function(index, marker) {
            marker.position = (marker.time / duration) * 100;
            marker.key = generateUUID();
            marker.div = $("

");

            marker.div.css(setting.markerStyle)
                      .css({"margin-left" : -parseFloat(marker.div.css("width"))/2 + 'px',
                            "left" : marker.position + '%'});

            // add user-defined class to marker
            if (marker.class) {
               marker.div.addClass(marker.class);
            }

            marker.text = marker.text || "";
            marker.overlayText = marker.overlayText || "";

            videoWrapper.find('.vjs-progress-control').append(marker.div);

            // store marker in an internal hash map
            markers[marker.key] = marker;
            markersList.push(marker);

            // register event handlers
            //bind click event to seek to marker time
            marker.div.on('click', function(e) {
               var key = $(this).data('marker-index');
               player.currentTime(markers[key].time);
            });

            if (setting.markerTip.display) {
               registerMarkerTipHandler(marker.div);
            }
         });

         sortMarkersList();
      }

      function removeMarkers(indexArray) {
         // reset overlay
         if (breakOverlay){
             overlayIndex = -1;
             breakOverlay.css("visibility", "hidden");
         }
         currentMarkerIndex = -1;

         for (var i = 0; i < indexArray.length; i++) {
            var index = indexArray[i];
            var marker = markersList[index];
            if (marker) {
               // delete from memory
               delete markers[marker.key];
               markersList[index] = null;

               // delete from dom
               videoWrapper.find(".vjs-marker[data-marker-index='" + marker.key +"']").remove();
            }
         }

         // clean up array
         for (var i = markersList.length - 1; i >=0; i--) {
            if (markersList[i] === null) {
               markersList.splice(i, 1);
            }
         }

         // sort again
         sortMarkersList();
      }

      // attach hover event handler
      function registerMarkerTipHandler(markerDiv) {

         markerDiv.on('mouseover', function(){
            var id = $(this).data('marker-index');

            markerTip.find('.vjs-tip-inner').text(setting.markerTip.text(markers[id]));

            // margin-left needs to minus the padding length to align correctly with the marker
            markerTip.css({"left" : markers[id].position + '%',
                           "margin-left" : -parseFloat(markerTip.css("width"))/2 - 5 + 'px',
                           "visibility"  : "visible"});

         }).on('mouseout',function(){
            markerTip.css("visibility", "hidden");
         });
      }

      function initializeMarkerTip() {
         markerTip = $("

");
         videoWrapper.find('.vjs-progress-control').append(markerTip);
      }

      // show or hide break overlays
      function updateBreakOverlay(currentTime) {
         if(currentMarkerIndex < 0){
            return;
         }

         var marker = markersList[currentMarkerIndex];

         if (currentTime >= marker.time &&
               currentTime <= (marker.time + setting.breakOverlay.displayTime)) {

            if (overlayIndex != currentMarkerIndex){
               overlayIndex = currentMarkerIndex;
               breakOverlay.find('.vjs-break-overlay-text').text(setting.breakOverlay.text(marker));
            }

            breakOverlay.css('visibility', "visible");

         } else {
            overlayIndex = -1;
            breakOverlay.css("visibility", "hidden");
         }
      }

      // problem when the next marker is within the overlay display time from the previous marker
      function initializeOverlay() {
         breakOverlay = $("

")
            .css(setting.breakOverlay.style);
         videoWrapper.append(breakOverlay);
         overlayIndex = -1;
      }

      function onTimeUpdate() {
         /*
             check marker reached in between markers
             the logic here is that it triggers a new marker reached event only if the player
             enters a new marker range (e.g. from marker 1 to marker 2). Thus, if player is on marker 1 and user clicked on marker 1 again, no new reached event is triggered)
         */
         var currentTime = player.currentTime();
         var newMarkerIndex;

         //check first marker, no marker is selected
         if (markersList.length > 0 && currentTime < markersList[0].time) {
            newMarkerIndex = -1;
         } else {
            for (var i = 0; i < markersList.length; i++) {
               // next marker time of last marker would be end of video time
               var nextMarkerTime = player.duration();
               if (i < markersList.length - 1) {
                  nextMarkerTime = markersList[i + 1].time;
               }

               if(currentTime >= markersList[i].time && currentTime < nextMarkerTime) {
                  newMarkerIndex = i;
               }
            }
         }

         // set marker index
         if (newMarkerIndex != currentMarkerIndex) {
            // trigger event
            if (newMarkerIndex != -1 && options.onMarkerReached) {
              options.onMarkerReached(markersList[newMarkerIndex]);
            }
            currentMarkerIndex = newMarkerIndex;
         }

         // update overlay
         if(setting.breakOverlay.display) {
            updateBreakOverlay(currentTime);
         }
      }

      // setup the whole thing
      function initialize() {
         if (setting.markerTip.display) {
            initializeMarkerTip();
         }

         // remove existing markers if already initialized
         player.markers.removeAll();
         addMarkers(options.markers);

         if (setting.breakOverlay.display) {
            initializeOverlay();

         }

         player.on("timeupdate", onTimeUpdate);
      }

      // setup the plugin after we loaded video's meta data
      player.on("loadedmetadata", function() {
         initialize();
      });

      // exposed plugin API
      player.markers = {
         next : function() {
            // go to the next marker from current timestamp
            var currentTime = player.currentTime();
            for (var i = 0; i < markersList.length; i++) {
               if (markersList[i].time > currentTime) {
                  player.currentTime(markersList[i].time);
                  return false;
               }
            }
         },
         prev : function() {
            // go to previous marker
            var currentTime = player.currentTime();
            for (var i = markersList.length - 1; i >=0 ; i--) {
               // add a threshold
               if (markersList[i].time + 0.5 < currentTime) {
                  player.currentTime(markersList[i].time);
                  return false;
               }
            }
         },
         add : function(newMarkers) {
            // add new markers given an array of index
            addMarkers(newMarkers);
         },
         remove: function(indexArray) {
            // remove markers given an array of index
            removeMarkers(indexArray);
         },
         removeAll: function(){
            var indexArray = [];
            for (var i = 0; i < markersList.length; i++) {
               indexArray.push(i);
            }
            removeMarkers(indexArray);
         },
         reset: function(newMarkers){
            // remove all the existing markers and add new ones
            player.markers.removeAll();
            addMarkers(newMarkers);
         },
         destroy: function(){
            // unregister the plugins and clean up even handlers
            player.markers.removeAll();
            breakOverlay.remove();
            markerTip.remove();
            player.off("timeupdate", updateBreakOverlay);
            delete player.markers;
            delete player.getMarkers;
         },
      };
   }

   videojs.plugin('markers', registerVideoJsMarkersPlugin);

})(jQuery, window.videojs);
<commit_msg>trigger custom click handler upon marker click<commit_after>/*! videojs-markers !*/
'use strict';

(function($, videojs, undefined) {
   //default setting
   var defaultSetting = {
      markerStyle: {
         'width':'7px',
         'border-radius': '30%',
         'background-color': 'red'
      },
      markerTip: {
         display: true,
         text: function(marker) {
            return "Break: "+ marker.text;
         }
      },
      breakOverlay:{
         display: false,
         displayTime: 3,
         text: function(marker) {
            return "Break overlay: " + marker.overlayText;
         },
         style: {
            'width':'100%',
            'height': '20%',
            'background-color': 'rgba(0,0,0,0.7)',
            'color': 'white',
            'font-size': '17px'
         }
      },
      onMarkerReached: function(marker) {},
      markers: []
   };

   // create a non-colliding random number
   function generateUUID() {
      var d = new Date().getTime();
      var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
         var r = (d + Math.random()*16)%16 | 0;
         d = Math.floor(d/16);
         return (c=='x' ? r : (r&0x3|0x8)).toString(16);
      });
      return uuid;
   };

   function registerVideoJsMarkersPlugin(options) {
      /**
       * register the markers plugin (dependent on jquery)
       */

      var setting      = $.extend(true, {}, defaultSetting, options),
          markers      = {},
          markersList  = [], // list of markers sorted by time
          videoWrapper = $(this.el()),
          currentMarkerIndex  = -1,
          player       = this,
          markerTip    = null,
          breakOverlay = null,
          overlayIndex = -1;

      function sortMarkersList() {
         // sort the list by time in asc order
         markersList.sort(function(a, b){return a.time - b.time});
      }

      function addMarkers(newMarkers) {
         // create the markers
         var duration = player.duration();

         $.each(newMarkers, function(index, marker) {
            marker.position = (marker.time / duration) * 100;
            marker.key = generateUUID();
            marker.div = $("

");

            marker.div.css(setting.markerStyle)
                      .css({"margin-left" : -parseFloat(marker.div.css("width"))/2 + 'px',
                            "left" : marker.position + '%'});

            // add user-defined class to marker
            if (marker.class) {
               marker.div.addClass(marker.class);
            }

            marker.text = marker.text || "";
            marker.overlayText = marker.overlayText || "";

            videoWrapper.find('.vjs-progress-control').append(marker.div);

            // store marker in an internal hash map
            markers[marker.key] = marker;
            markersList.push(marker);

            // register event handlers
            //bind click event to seek to marker time
            marker.div.on('click', function(e) {
               var key = $(this).data('marker-index');
               player.currentTime(markers[key].time);

               if (marker.onMarkerClicked)
                  marker.onMarkerClicked();
            });

            if (setting.markerTip.display) {
               registerMarkerTipHandler(marker.div);
            }
         });

         sortMarkersList();
      }

      function removeMarkers(indexArray) {
         // reset overlay
         if (breakOverlay){
             overlayIndex = -1;
             breakOverlay.css("visibility", "hidden");
         }
         currentMarkerIndex = -1;

         for (var i = 0; i < indexArray.length; i++) {
            var index = indexArray[i];
            var marker = markersList[index];
            if (marker) {
               // delete from memory
               delete markers[marker.key];
               markersList[index] = null;

               // delete from dom
               videoWrapper.find(".vjs-marker[data-marker-index='" + marker.key +"']").remove();
            }
         }

         // clean up array
         for (var i = markersList.length - 1; i >=0; i--) {
            if (markersList[i] === null) {
               markersList.splice(i, 1);
            }
         }

         // sort again
         sortMarkersList();
      }

      // attach hover event handler
      function registerMarkerTipHandler(markerDiv) {

         markerDiv.on('mouseover', function(){
            var id = $(this).data('marker-index');

            markerTip.find('.vjs-tip-inner').text(setting.markerTip.text(markers[id]));

            // margin-left needs to minus the padding length to align correctly with the marker
            markerTip.css({"left" : markers[id].position + '%',
                           "margin-left" : -parseFloat(markerTip.css("width"))/2 - 5 + 'px',
                           "visibility"  : "visible"});

         }).on('mouseout',function(){
            markerTip.css("visibility", "hidden");
         });
      }

      function initializeMarkerTip() {
         markerTip = $("

");
         videoWrapper.find('.vjs-progress-control').append(markerTip);
      }

      // show or hide break overlays
      function updateBreakOverlay(currentTime) {
         if(currentMarkerIndex < 0){
            return;
         }

         var marker = markersList[currentMarkerIndex];

         if (currentTime >= marker.time &&
               currentTime <= (marker.time + setting.breakOverlay.displayTime)) {

            if (overlayIndex != currentMarkerIndex){
               overlayIndex = currentMarkerIndex;
               breakOverlay.find('.vjs-break-overlay-text').text(setting.breakOverlay.text(marker));
            }

            breakOverlay.css('visibility', "visible");

         } else {
            overlayIndex = -1;
            breakOverlay.css("visibility", "hidden");
         }
      }

      // problem when the next marker is within the overlay display time from the previous marker
      function initializeOverlay() {
         breakOverlay = $("

")
            .css(setting.breakOverlay.style);
         videoWrapper.append(breakOverlay);
         overlayIndex = -1;
      }

      function onTimeUpdate() {
         /*
             check marker reached in between markers
             the logic here is that it triggers a new marker reached event only if the player
             enters a new marker range (e.g. from marker 1 to marker 2). Thus, if player is on marker 1 and user clicked on marker 1 again, no new reached event is triggered)
         */
         var currentTime = player.currentTime();
         var newMarkerIndex;

         //check first marker, no marker is selected
         if (markersList.length > 0 && currentTime < markersList[0].time) {
            newMarkerIndex = -1;
         } else {
            for (var i = 0; i < markersList.length; i++) {
               // next marker time of last marker would be end of video time
               var nextMarkerTime = player.duration();
               if (i < markersList.length - 1) {
                  nextMarkerTime = markersList[i + 1].time;
               }

               if(currentTime >= markersList[i].time && currentTime < nextMarkerTime) {
                  newMarkerIndex = i;
               }
            }
         }

         // set marker index
         if (newMarkerIndex != currentMarkerIndex) {
            // trigger event
            if (newMarkerIndex != -1 && options.onMarkerReached) {
              options.onMarkerReached(markersList[newMarkerIndex]);
            }
            currentMarkerIndex = newMarkerIndex;
         }

         // update overlay
         if(setting.breakOverlay.display) {
            updateBreakOverlay(currentTime);
         }
      }

      // setup the whole thing
      function initialize() {
         if (setting.markerTip.display) {
            initializeMarkerTip();
         }

         // remove existing markers if already initialized
         player.markers.removeAll();
         addMarkers(options.markers);

         if (setting.breakOverlay.display) {
            initializeOverlay();

         }

         player.on("timeupdate", onTimeUpdate);
      }

      // setup the plugin after we loaded video's meta data
      player.on("loadedmetadata", function() {
         initialize();
      });

      // exposed plugin API
      player.markers = {
         next : function() {
            // go to the next marker from current timestamp
            var currentTime = player.currentTime();
            for (var i = 0; i < markersList.length; i++) {
               if (markersList[i].time > currentTime) {
                  player.currentTime(markersList[i].time);
                  return false;
               }
            }
         },
         prev : function() {
            // go to previous marker
            var currentTime = player.currentTime();
            for (var i = markersList.length - 1; i >=0 ; i--) {
               // add a threshold
               if (markersList[i].time + 0.5 < currentTime) {
                  player.currentTime(markersList[i].time);
                  return false;
               }
            }
         },
         add : function(newMarkers) {
            // add new markers given an array of index
            addMarkers(newMarkers);
         },
         remove: function(indexArray) {
            // remove markers given an array of index
            removeMarkers(indexArray);
         },
         removeAll: function(){
            var indexArray = [];
            for (var i = 0; i < markersList.length; i++) {
               indexArray.push(i);
            }
            removeMarkers(indexArray);
         },
         reset: function(newMarkers){
            // remove all the existing markers and add new ones
            player.markers.removeAll();
            addMarkers(newMarkers);
         },
         destroy: function(){
            // unregister the plugins and clean up even handlers
            player.markers.removeAll();
            breakOverlay.remove();
            markerTip.remove();
            player.off("timeupdate", updateBreakOverlay);
            delete player.markers;
            delete player.getMarkers;
         },
      };
   }

   videojs.plugin('markers', registerVideoJsMarkersPlugin);

})(jQuery, window.videojs);
<|endoftext|>


<commit_before>//Router
Thorax.Router = Backbone.Router.extend({
  initialize: function() {
    Backbone.history || (Backbone.history = new Backbone.History);
    Backbone.history.on('route', onRoute, this);
    //router does not have a built in destroy event
    //but ViewController does
    this.on('destroyed', function() {
      Backbone.history.off('route', onRoute, this);
    });
  },
  route: function(route, name, callback) {
    //add a route:before event that is fired before the callback is called
    return Backbone.Router.prototype.route.call(this, route, name, function() {
      this.trigger.apply(this, ['route:before', name].concat(Array.prototype.slice.call(arguments)));
      return callback.apply(this, arguments);
    });
  }
});

Thorax.Routers = {};
Thorax.Util.createRegistryWrapper(Thorax.Router, Thorax.Routers);

function onRoute(router, name) {
  if (this === router) {
    this.trigger.apply(this, ['route'].concat(Array.prototype.slice.call(arguments, 1)));
  }
}

//layout
var layoutCidAttributeName = 'data-layout-cid';

Thorax.LayoutView = Thorax.View.extend({
  render: function(output) {
    //TODO: fixme, lumbar inserts templates after JS, most of the time this is fine
    //but Application will be created in init.js (unlike most views)
    //so need to put this here so the template will be picked up
    var layoutTemplate;
    if (this.name) {
      layoutTemplate = Thorax.Util.registryGet(Thorax, 'templates', this.name, true);
    }
    //a template is optional in a layout
    if (output || this.template || layoutTemplate) {
      //but if present, it must have embedded an element containing layoutCidAttributeName
      var response = Thorax.View.prototype.render.call(this, output || this.template || layoutTemplate);
      ensureLayoutViewsTargetElement.call(this);
      return response;
    } else {
      ensureLayoutCid.call(this);
    }
  },
  setView: function(view, options) {
    options = _.extend({
      scroll: true,
      destroy: true
    }, options || {});
    if (typeof view === 'string') {
      view = new (Thorax.Util.registryGet(Thorax, 'Views', view, false));
    }
    this.ensureRendered();
    var oldView = this._view;
    if (view == oldView){
      return false;
    }
    if (options.destroy) {
      view._shouldDestroyOnNextSetView = true;
    }
    this.trigger('change:view:start', view, oldView, options);
    oldView && oldView.trigger('deactivated', options);
    view && view.trigger('activated', options);
    if (oldView && oldView.el && oldView.el.parentNode) {
      oldView.$el.remove();
    }
    //make sure the view has been rendered at least once
    view && view.ensureRendered();
    view && getLayoutViewsTargetElement.call(this).appendChild(view.el);
    this._view = view;
    oldView && oldView._shouldDestroyOnNextSetView && oldView.destroy();
    this._view && this._view.trigger('ready', options);
    this.trigger('change:view:end', view, oldView, options);
    return view;
  },

  getView: function() {
    return this._view;
  }
});

Handlebars.registerHelper('layout', function(options) {
  options.hash[layoutCidAttributeName] = this._view.cid;
  return new Handlebars.SafeString(Thorax.Util.tag.call(this, options.hash, '', this));
});

function ensureLayoutCid() {
  ++this._renderCount;
  //set the layoutCidAttributeName on this.$el if there was no template
  this.$el.attr(layoutCidAttributeName, this.cid);
}

function ensureLayoutViewsTargetElement() {
  if (!this.$('[' + layoutCidAttributeName + '="' + this.cid + '"]')[0]) {
    throw new Error('No layout element found in ' + (this.name || this.cid));
  }
}

function getLayoutViewsTargetElement() {
  return this.$('[' + layoutCidAttributeName + '="' + this.cid + '"]')[0] || this.el[0] || this.el;
}

//ViewController
Thorax.ViewController = Thorax.LayoutView.extend();
_.extend(Thorax.ViewController.prototype, Thorax.Router.prototype, {
  initialize: function() {
    Thorax.Router.prototype.initialize.call(this);
    //set the ViewController as the view on the parent
    //if a parent was specified
    this.on('route:before', function(router, name) {
      if (this.parent && this.parent.getView) {
        if (this.parent.getView() !== this) {
          this.parent.setView(this, {
            destroy: false
          });
        }
      }
    }, this);
    this._bindRoutes();
  }
});
<commit_msg>add check for view to protect from error during setView(false)<commit_after>//Router
Thorax.Router = Backbone.Router.extend({
  initialize: function() {
    Backbone.history || (Backbone.history = new Backbone.History);
    Backbone.history.on('route', onRoute, this);
    //router does not have a built in destroy event
    //but ViewController does
    this.on('destroyed', function() {
      Backbone.history.off('route', onRoute, this);
    });
  },
  route: function(route, name, callback) {
    //add a route:before event that is fired before the callback is called
    return Backbone.Router.prototype.route.call(this, route, name, function() {
      this.trigger.apply(this, ['route:before', name].concat(Array.prototype.slice.call(arguments)));
      return callback.apply(this, arguments);
    });
  }
});

Thorax.Routers = {};
Thorax.Util.createRegistryWrapper(Thorax.Router, Thorax.Routers);

function onRoute(router, name) {
  if (this === router) {
    this.trigger.apply(this, ['route'].concat(Array.prototype.slice.call(arguments, 1)));
  }
}

//layout
var layoutCidAttributeName = 'data-layout-cid';

Thorax.LayoutView = Thorax.View.extend({
  render: function(output) {
    //TODO: fixme, lumbar inserts templates after JS, most of the time this is fine
    //but Application will be created in init.js (unlike most views)
    //so need to put this here so the template will be picked up
    var layoutTemplate;
    if (this.name) {
      layoutTemplate = Thorax.Util.registryGet(Thorax, 'templates', this.name, true);
    }
    //a template is optional in a layout
    if (output || this.template || layoutTemplate) {
      //but if present, it must have embedded an element containing layoutCidAttributeName
      var response = Thorax.View.prototype.render.call(this, output || this.template || layoutTemplate);
      ensureLayoutViewsTargetElement.call(this);
      return response;
    } else {
      ensureLayoutCid.call(this);
    }
  },
  setView: function(view, options) {
    options = _.extend({
      scroll: true,
      destroy: true
    }, options || {});
    if (typeof view === 'string') {
      view = new (Thorax.Util.registryGet(Thorax, 'Views', view, false));
    }
    this.ensureRendered();
    var oldView = this._view;
    if (view == oldView){
      return false;
    }
    if (options.destroy && view) {
      view._shouldDestroyOnNextSetView = true;
    }
    this.trigger('change:view:start', view, oldView, options);
    oldView && oldView.trigger('deactivated', options);
    view && view.trigger('activated', options);
    if (oldView && oldView.el && oldView.el.parentNode) {
      oldView.$el.remove();
    }
    //make sure the view has been rendered at least once
    view && view.ensureRendered();
    view && getLayoutViewsTargetElement.call(this).appendChild(view.el);
    this._view = view;
    oldView && oldView._shouldDestroyOnNextSetView && oldView.destroy();
    this._view && this._view.trigger('ready', options);
    this.trigger('change:view:end', view, oldView, options);
    return view;
  },

  getView: function() {
    return this._view;
  }
});

Handlebars.registerHelper('layout', function(options) {
  options.hash[layoutCidAttributeName] = this._view.cid;
  return new Handlebars.SafeString(Thorax.Util.tag.call(this, options.hash, '', this));
});

function ensureLayoutCid() {
  ++this._renderCount;
  //set the layoutCidAttributeName on this.$el if there was no template
  this.$el.attr(layoutCidAttributeName, this.cid);
}

function ensureLayoutViewsTargetElement() {
  if (!this.$('[' + layoutCidAttributeName + '="' + this.cid + '"]')[0]) {
    throw new Error('No layout element found in ' + (this.name || this.cid));
  }
}

function getLayoutViewsTargetElement() {
  return this.$('[' + layoutCidAttributeName + '="' + this.cid + '"]')[0] || this.el[0] || this.el;
}

//ViewController
Thorax.ViewController = Thorax.LayoutView.extend();
_.extend(Thorax.ViewController.prototype, Thorax.Router.prototype, {
  initialize: function() {
    Thorax.Router.prototype.initialize.call(this);
    //set the ViewController as the view on the parent
    //if a parent was specified
    this.on('route:before', function(router, name) {
      if (this.parent && this.parent.getView) {
        if (this.parent.getView() !== this) {
          this.parent.setView(this, {
            destroy: false
          });
        }
      }
    }, this);
    this._bindRoutes();
  }
});
<|endoftext|>


<commit_before>chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
    if(request.action == "GetInput") {
      console.log("Received!");
      console.log(request.url);
      var url = "url#"+request.url;
      console.log(request.type);
      console.log(request.data);
      console.log(request.time);

      var key = request.time.toString();
      var data = request.data;

      /*
       * test for appending JSON Object
       * added by bokor
      data.push({name: 'status', value: request.type});
      data.push({name: 'timestamp', value: request.time});

      console.log(data);
      */

      var arr ={};
      //arr[key] = data;
      //chrome.storage.sync.clear();
      chrome.storage.sync.get(null, function(obj) {
          //for(var i = 0; i < obj[key].length; i++) {
           // console.log(obj[key][i].name,":",obj[key][i].value);
          var allkeys = Object.keys(obj);

          if(allkeys.lastIndexOf(url) >= 0){
            key = url;
            data = {lastPass: request.time, count: obj[key].count+1};
            arr[key] = data;
            chrome.storage.sync.set(arr, function() {
              console.log("exist!");
            })
          }else
          {
            key = url;
            data = {lastPass: request.time, count: 1}
            arr[key] = data;
            chrome.storage.sync.set(arr, function() {
              console.log("not exist!");
            });
          }
          //}
          arr={};
          key = "time#"+request.time;
          data = request.data;
          arr[key] = data;
          chrome.storage.sync.set(arr, function() {
              console.log("Store!");
            });

          //chrome.storage.sync.clear();

          chrome.storage.sync.get(null, function(obj) {
            allkeys = Object.keys(obj);
            console.log(allkeys);
            console.log(obj["url#"+request.url]);
          });
      });

    }
});<commit_msg>Revert "store complete"<commit_after>chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
    if(request.action == "GetInput") {
      console.log("Received!");
      console.log(request.url);
      var url = "url#"+request.url;
      console.log(request.type);
      console.log(request.data);
      console.log(request.time);

      var key = request.time.toString();
      var data = request.data;

      /*
       * test for appending JSON Object
       * added by bokor
      data.push({name: 'status', value: request.type});
      data.push({name: 'timestamp', value: request.time});

      console.log(data);
      */

      var arr ={};
      //arr[key] = data;
      //chrome.storage.sync.clear();
      chrome.storage.sync.get(null, function(obj) {
          //for(var i = 0; i < obj[key].length; i++) {
           // console.log(obj[key][i].name,":",obj[key][i].value);
          var allkeys = Object.keys(obj);
          console.log(allkeys);

          if(allkeys.lastIndexOf(url) >= 0){
            key = url;
            data = {lastPass: request.time, count: obj[key].count+1};
            arr[key] = data;
            chrome.storage.sync.set(arr, function() {
              console.log("exist! and Store!");
            })
            chrome.storage.sync.get(null, function(obj){
              allkeys = Object.keys(obj);
              console.log(obj[key]);
            });

          }else
          {
            key = url;
            data = {lastPass: request.time, count: 1}
            arr[key] = data;
            chrome.storage.sync.set(arr, function() {
              console.log("not exist! and Store!");
            });
            chrome.storage.sync.get(null, function(obj) {
              allkeys = Object.keys(obj);
              console.log(obj[key]);
            });
          }
          //}
      });

    }
});<|endoftext|>


<commit_before>/**
 * background script for app.js code injection
 * @author Jesse Mu, Tyler Chen
 */

var script = document.createElement("script");
script.src = chrome.extension.getURL("app.js");
document.head.appendChild(s);
<commit_msg>Minor: fix 's' bug<commit_after>/**
 * background script for app.js code injection
 * @author Jesse Mu, Tyler Chen
 */

var script = document.createElement("script");
script.src = chrome.extension.getURL("app.js");
document.head.appendChild(script);
<|endoftext|>


<commit_before>var omnibox = chrome.omnibox;
var topMatch;

function escape(text) {
  return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
}

function escapeRegexp(text) {
  var specialChars = [
    '\\',
    '[',
    ']',
    '(',
    ')',
    '|',
    '.',
    '+',
    '?',
    '{',
    '}',
    '-'
  ];

  return text.split('').map(function(char) {
    if (specialChars.indexOf(char) > -1)
      return '\\' + char;
    else
      return char;
  }).join('');
}

function parseMatches(text, search) {
  console.log("parseMatches search=" + search + " text=" + text);
  var terms = escapeRegexp(search).split(/\s+/g);
  var termMatchCounts = [];
  terms.forEach(function() { termMatchCounts.push(0); });
  var re = new RegExp("(" + terms.join(")|(") + ")", "ig");
  var result = [];
  var lastIndex = 0;
  var match;
  while (match = re.exec(text)) {
    result.push({
      match: false,
      text: escape(text.substring(lastIndex, match.index))
    });

    lastIndex = match.index + match[0].length;
    result.push({
      match: true,
      text: escape(text.substring(match.index, lastIndex))
    });

    for (var i = 1; i < match.length; i++) {
      if (match[i])
        termMatchCounts[i - 1]++;
    }
  }

  // If any term found no matches, then we don't have a match.
  if (termMatchCounts.indexOf(0) > -1) {
    return [
      {
        match: false,
        text: escape(text)
      }
    ];
  }

  result.push({
    match: false,
    text: escape(text.substring(lastIndex))
  });

  return result;
}

function formatMatches(parsed) {
  console.log("formatMatches parsed.match=" + parsed.match + " parsed.text=" + parsed.text);
  return parsed.reduce(function(s, piece) {
    if (piece.match)
      return s + "<match>" + piece.text + "</match>";
    else
      return s + piece.text;
  }, "");
}

omnibox.onInputChanged.addListener(function(text, suggest) {
  console.log("onInputChanged text=" + text + " suggest=" + suggest);

  text = text.toLowerCase().replace(/\W+/g, ' ').trim();
  if (!text)
    return;

  chrome.windows.getAll({populate: true}, function(windows) {
    topMatch = -1;

    var tabs = windows.reduce(function(arr, win) {
      return arr.concat(win.tabs);
    }, []);

    var suggestions = tabs.map(function(tab) {
      return {
        tab: tab,
        title: parseMatches(tab.title, text),
        url: parseMatches(tab.url, text)
      };
    }).filter(function(item) {
      if (item.title.length > 1 || item.url.length > 1) {
        if (topMatch == -1)
          topMatch = item.tab.id;
        return true;
      } else {
        return false;
      }
    }).map(function(item) {
      return {
        content: item.tab.title + ' - ' + item.tab.url + '#' + item.tab.id,
        description: formatMatches(item.title) + ' - ' +
                     '<url>' + formatMatches(item.url) + '</url>'
      };
    });

    if (suggestions && suggestions.length == 1 && text.length >= 4 && (""+localStorage["autoswitch"] == "true")) {
        tabsearch_onInputEntered(suggestions[0].content);
        return;
    }

    suggest(suggestions);
  });
});

var tabsearch_onInputEntered = function(url) {
  console.log("tabsearch_onInputEntered url=" + url);

  var tabId = url.match(/#(\d+)$/);
  if (tabId) {
    tabId = parseInt(tabId[1]);
    console.log("Jump to tabId=" + tabId );
  } else if (topMatch != -1) {
    console.log("Jump to topMatch=" + topMatch );
    tabId = topMatch;
  } else {
    console.log("Nothing found for url=" + url);
    return;
  }

  chrome.tabs.getSelected(null, function(selected) {
    // if the selected tab was the new tab page,
    // assume that it was blank, and close it!
    if (selected.url == 'chrome://newtab/')
      chrome.tabs.remove(selected.id);
  });

  chrome.tabs.get(tabId, function(tab) {
    if (tab) {
      chrome.windows.get(tab.windowId, function (window) {
        if (!(window.focused && tab.selected)) {
          chrome.tabs.update(tabId, { active: true }, function() {
            chrome.windows.update(tab.windowId, { focused: true });
          });
        }
      });
    }
  });

};

omnibox.onInputEntered.addListener(tabsearch_onInputEntered);

omnibox.onInputCancelled.addListener(function() {
  topMatch = -1;
});
<commit_msg>debug<commit_after>var omnibox = chrome.omnibox;
var topMatch;

function escape(text) {
  return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
}

function escapeRegexp(text) {
  var specialChars = [
    '\\',
    '[',
    ']',
    '(',
    ')',
    '|',
    '.',
    '+',
    '?',
    '{',
    '}',
    '-'
  ];

  return text.split('').map(function(char) {
    if (specialChars.indexOf(char) > -1)
      return '\\' + char;
    else
      return char;
  }).join('');
}

function parseMatches(text, search) {
  //console.log("parseMatches search=" + search + " text=" + text);
  var terms = escapeRegexp(search).split(/\s+/g);
  var termMatchCounts = [];
  terms.forEach(function() { termMatchCounts.push(0); });
  var re = new RegExp("(" + terms.join(")|(") + ")", "ig");
  var result = [];
  var lastIndex = 0;
  var match;
  while (match = re.exec(text)) {
    result.push({
      match: false,
      text: escape(text.substring(lastIndex, match.index))
    });

    lastIndex = match.index + match[0].length;
    result.push({
      match: true,
      text: escape(text.substring(match.index, lastIndex))
    });

    for (var i = 1; i < match.length; i++) {
      if (match[i])
        termMatchCounts[i - 1]++;
    }
  }

  // If any term found no matches, then we don't have a match.
  if (termMatchCounts.indexOf(0) > -1) {
    return [
      {
        match: false,
        text: escape(text)
      }
    ];
  }

  result.push({
    match: false,
    text: escape(text.substring(lastIndex))
  });

  return result;
}

function formatMatches(parsed) {
  //console.log("formatMatches parsed.match=" + parsed.match + " parsed.text=" + parsed.text);
  return parsed.reduce(function(s, piece) {
    if (piece.match)
      return s + "<match>" + piece.text + "</match>";
    else
      return s + piece.text;
  }, "");
}

omnibox.onInputChanged.addListener(function(text, suggest) {
  console.log("onInputChanged text=" + text );

  text = text.toLowerCase().replace(/\W+/g, ' ').trim();
  if (!text)
    return; //FIXME : jump to last tab

  chrome.windows.getAll({populate: true}, function(windows) {
    topMatch = -1;

    var tabs = windows.reduce(function(arr, win) {
      return arr.concat(win.tabs);
    }, []);

    var suggestions = tabs.map(function(tab) {
      return {
        tab: tab,
        title: parseMatches(tab.title, text),
        url: parseMatches(tab.url, text)
      };
    }).filter(function(item) {
      if (item.title.length > 1 || item.url.length > 1) {
        if (topMatch == -1)
          topMatch = item.tab.id;
        return true;
      } else {
        return false;
      }
    }).map(function(item) {
      return {
        content: item.tab.title + ' - ' + item.tab.url + '#' + item.tab.id,
        description: formatMatches(item.title) + ' - ' +
                     '<url>' + formatMatches(item.url) + '</url>'
      };
    });

    console.log("onInputChanged suggestions: length=" + suggestions.length + " first=" + suggestions[0].content);
    if (suggestions && suggestions.length == 1 && text.length >= 4 && (""+localStorage["autoswitch"] == "true")) {
        tabsearch_onInputEntered(suggestions[0].content);
        return;
    }

    suggest(suggestions);
  });
});

var tabsearch_onInputEntered = function(url) {
  console.log("tabsearch_onInputEntered url=" + url + " topMatch=" + topMatch);

  var tabId = url.match(/#(\d+)$/);
  if (tabId) {
    tabId = parseInt(tabId[1]);
    console.log("Jump to tabId=" + tabId );
  } else if (topMatch != -1) {
    console.log("Jump to topMatch=" + topMatch );
    tabId = topMatch;
  } else {
    console.log("Nothing found for url=" + url);
    return;
  }

  chrome.tabs.getSelected(null, function(selected) {
    // if the selected tab was the new tab page,
    // assume that it was blank, and close it!
    if (selected.url == 'chrome://newtab/')
      chrome.tabs.remove(selected.id);
  });

  chrome.tabs.get(tabId, function(tab) {
    if (tab) {
      chrome.windows.get(tab.windowId, function (window) {
        if (!(window.focused && tab.selected)) {
          chrome.tabs.update(tabId, { active: true }, function() {
            chrome.windows.update(tab.windowId, { focused: true });
          });
        }
      });
    }
  });

};

omnibox.onInputEntered.addListener(tabsearch_onInputEntered);

omnibox.onInputCancelled.addListener(function() {
  topMatch = -1;
});
<|endoftext|>