<commit_before>module.exports = {
  Colors: {
    // GRAYSCALE
    ASH: '#ACB0B3',
    CHARCOAL: '#56595A',
    FOG: '#E3E6E7',
    PORCELAIN: '#F7F8F8',
    WHITE: '#FFFFFF',

    // ACCENTS
    PRIMARY: '#359BCF',
    BANANA: '#FBB600',
    ORANGE: '#EF7625',
    LIME: '#2EBE51',
    STRAWBERRY: '#E22727',

    // CHART COLORS
    BASE_ARC: '#F5F5F5',
    YELLOW: '#f6a01e',
    GREEN: '#00a89c',
    BLUE: '#359BCF',
    RED: '#EE4235',

    // CATEGORY COLORS
    AUTO_TRANSPORT: '#4B9DBC',
    BILLS_UTILITIES: '#EF9B2C',
    BUSINESS: '#B3DE8C',
    EDUCATION: '#F8AB3A',
    ENTERTAINMENT: '#AB5B89',
    FEES: '#FF9696',
    FINANCIAL: '#6BCDDB',
    FOOD_DINING: '#58AC7B',
    GIFTS_CHARITY: '#347AA5',
    HEALTH_FITNESS: '#5C446E',
    HOME: '#FFD84D',
    INCOME: '#133F49'
    INVESTMENTS: '#FF7070',
    KIDS: '#82D196',
    PETS: '#85507B',
    PERSONAL_CARE: '#338B7A',
    SHOPPING: '#CF5F84',
    TAXES: '#32588D',
    TRAVEL: '#e37434',
    UNCATEGORIZED: '#FA5555',

    // SCRIM
    SCRIM: 'rgba(247,248,248,0.9)'
  },

  FontSizes: {
    JUMBO: 30,
    XXLARGE: 21,
    XLARGE: 17,
    LARGE: 15,
    MEDIUM: 13,
    SMALL: 11,
    TINY: 10
  },

  Fonts: {
    THIN: 'ProximaNovaThin, Helvetica, Arial, sans-serif',
    LIGHT: 'ProximaNovaLight, Helvetica, Arial, sans-serif',
    REGULAR: 'ProximaNovaRegular, Helvetica, Arial, sans-serif',
    ITALIC: 'ProximaNovaRegularItalic, Helvetica, Arial, sans-serif',
    SEMIBOLD: 'ProximaNovaSemibold, Helvetica, Arial, sans-serif'
  },

  //Box Shadows
  ShadowLow: '0 1px 2px rgba(0,0,0,0.1)',
  ShadowMed: '0 2px 6px rgba(0,0,0,0.1)',
  ShadowHigh: '0 10px 30px 5px rgba(0,0,0,0.1)',

  adjustColor (col, amt) {
    let color = col;
    let usePound = false;

    if (color[0] === '#') {
      color = color.slice(1);
      usePound = true;
    }

    const num = parseInt(color, 16);

    let r = (num >> 16) + amt;

    if (r > 255) {
      r = 255;
    } else if (r < 0) {
      r = 0;
    }

    let b = ((num >> 8) & 0x00FF) + amt;

    if (b > 255) {
      b = 255;
    } else if (b < 0) {
      b = 0;
    }

    let g = (num & 0x0000FF) + amt;

    if (g > 255) {
      g = 255;
    } else if (g < 0) {
      g = 0;
    }

    return (usePound ? '#' : '') + (g | (b << 8) | (r << 16)).toString(16);
  },

  adjustHexOpacity (color, opacity) {
    const r = parseInt(color.slice(1, 3), 16);
    const g = parseInt(color.slice(3, 5), 16);
    const b = parseInt(color.slice(5, 7), 16);

    return 'rgba(' + r + ', ' + g + ', ' + b + ', ' + opacity + ')';
  }
};
<commit_msg>Missed a comma. ;-(<commit_after>module.exports = {
  Colors: {
    // GRAYSCALE
    ASH: '#ACB0B3',
    CHARCOAL: '#56595A',
    FOG: '#E3E6E7',
    PORCELAIN: '#F7F8F8',
    WHITE: '#FFFFFF',

    // ACCENTS
    PRIMARY: '#359BCF',
    BANANA: '#FBB600',
    ORANGE: '#EF7625',
    LIME: '#2EBE51',
    STRAWBERRY: '#E22727',

    // CHART COLORS
    BASE_ARC: '#F5F5F5',
    YELLOW: '#f6a01e',
    GREEN: '#00a89c',
    BLUE: '#359BCF',
    RED: '#EE4235',

    // CATEGORY COLORS
    AUTO_TRANSPORT: '#4B9DBC',
    BILLS_UTILITIES: '#EF9B2C',
    BUSINESS: '#B3DE8C',
    EDUCATION: '#F8AB3A',
    ENTERTAINMENT: '#AB5B89',
    FEES: '#FF9696',
    FINANCIAL: '#6BCDDB',
    FOOD_DINING: '#58AC7B',
    GIFTS_CHARITY: '#347AA5',
    HEALTH_FITNESS: '#5C446E',
    HOME: '#FFD84D',
    INCOME: '#133F49',
    INVESTMENTS: '#FF7070',
    KIDS: '#82D196',
    PETS: '#85507B',
    PERSONAL_CARE: '#338B7A',
    SHOPPING: '#CF5F84',
    TAXES: '#32588D',
    TRAVEL: '#e37434',
    UNCATEGORIZED: '#FA5555',

    // SCRIM
    SCRIM: 'rgba(247,248,248,0.9)'
  },

  FontSizes: {
    JUMBO: 30,
    XXLARGE: 21,
    XLARGE: 17,
    LARGE: 15,
    MEDIUM: 13,
    SMALL: 11,
    TINY: 10
  },

  Fonts: {
    THIN: 'ProximaNovaThin, Helvetica, Arial, sans-serif',
    LIGHT: 'ProximaNovaLight, Helvetica, Arial, sans-serif',
    REGULAR: 'ProximaNovaRegular, Helvetica, Arial, sans-serif',
    ITALIC: 'ProximaNovaRegularItalic, Helvetica, Arial, sans-serif',
    SEMIBOLD: 'ProximaNovaSemibold, Helvetica, Arial, sans-serif'
  },

  //Box Shadows
  ShadowLow: '0 1px 2px rgba(0,0,0,0.1)',
  ShadowMed: '0 2px 6px rgba(0,0,0,0.1)',
  ShadowHigh: '0 10px 30px 5px rgba(0,0,0,0.1)',

  adjustColor (col, amt) {
    let color = col;
    let usePound = false;

    if (color[0] === '#') {
      color = color.slice(1);
      usePound = true;
    }

    const num = parseInt(color, 16);

    let r = (num >> 16) + amt;

    if (r > 255) {
      r = 255;
    } else if (r < 0) {
      r = 0;
    }

    let b = ((num >> 8) & 0x00FF) + amt;

    if (b > 255) {
      b = 255;
    } else if (b < 0) {
      b = 0;
    }

    let g = (num & 0x0000FF) + amt;

    if (g > 255) {
      g = 255;
    } else if (g < 0) {
      g = 0;
    }

    return (usePound ? '#' : '') + (g | (b << 8) | (r << 16)).toString(16);
  },

  adjustHexOpacity (color, opacity) {
    const r = parseInt(color.slice(1, 3), 16);
    const g = parseInt(color.slice(3, 5), 16);
    const b = parseInt(color.slice(5, 7), 16);

    return 'rgba(' + r + ', ' + g + ', ' + b + ', ' + opacity + ')';
  }
};
<|endoftext|>


<commit_before>// export your constants
const types = {
    LOGIN_REQUEST: 'LOGIN_REQUEST',
    LOGIN_SUCCESS: 'LOGIN_SUCCESS',
    LOGIN_FAILURE: 'LOGIN_FAILURE',
    LOGOUT_REQUEST: 'LOGOUT_REQUEST',
    LOGOUT_SUCCESS: 'LOGOUT_SUCCESS',
    ADD_SHIPPING: 'ADD_SHIPPING',
    ADD_PAYMENT: 'ADD_PAYMENT',
    SAME_AS_SHIPPING: 'SAME_AS_SHIPPING',
    SUBMIT_REQUEST: 'SUBMIT_REQUEST',
    SUBMIT_SUCCESS: 'SUBMIT_SUCCESS',
    SUBMIT_FAILURE: 'SUBMIT_FAILURE',
    CREATE_PAGE: 'CREATE_PAGE',
    READ_PAGE: 'READ_PAGE',
    EDIT_PAGE: 'EDIT_PAGE',
    DELETE_PAGE: 'DELETE_PAGE',
    REQUEST_PAGE: 'REQUEST_PAGE',
    RECEIVE_PAGE: 'RECEIVE_PAGE'
}

export default types
<commit_msg>adding types for fetching actions<commit_after>// export your constants
const types = {
    LOGIN_REQUEST: 'LOGIN_REQUEST',
    LOGIN_SUCCESS: 'LOGIN_SUCCESS',
    LOGIN_FAILURE: 'LOGIN_FAILURE',
    LOGOUT_REQUEST: 'LOGOUT_REQUEST',
    LOGOUT_SUCCESS: 'LOGOUT_SUCCESS',
    ADD_SHIPPING: 'ADD_SHIPPING',
    ADD_PAYMENT: 'ADD_PAYMENT',
    SAME_AS_SHIPPING: 'SAME_AS_SHIPPING',
    SUBMIT_REQUEST: 'SUBMIT_REQUEST',
    SUBMIT_SUCCESS: 'SUBMIT_SUCCESS',
    SUBMIT_FAILURE: 'SUBMIT_FAILURE',
    CREATE_PAGE: 'CREATE_PAGE',
    READ_PAGE: 'READ_PAGE',
    EDIT_PAGE: 'EDIT_PAGE',
    DELETE_PAGE: 'DELETE_PAGE',
    AVAILABILITY_FETCH_REQUEST: 'AVAILABILITY_FETCH_REQUEST',
    AVAILABILITY_FETCH_SUCCESS: 'AVAILABILITY_FETCH_SUCCESS',
    AVAILABILITY_FETCH_FAILURE: 'AVAILABILITY_FETCH_FAILURE'
}

export default types
<|endoftext|>


<commit_before>var currentPullRequests = [];

function getData(){
	chrome.storage.sync.get("data", function (obj) {
		obj.data.forEach(function(project){
			if(project.org !== undefined && project.repo !== undefined){
			    var request = new XMLHttpRequest();

				request.open('GET', 'http://github.com/' + project.org + '/' + project.repo + '/pulls', true);

				request.onload = function() {
				  if (this.status >= 200 && this.status < 400) {
				  	parseData(this.response, project.org, project.repo);
				  }
				};

				request.send();
			};
		})
	});
}

function parseData(resp, org, repo){
    var container = document.implementation.createHTMLDocument().documentElement;
    container.innerHTML = resp;
    var nodeList = container.querySelectorAll('.table-list-issues .js-issue-row .issue-title-link');

   	[].forEach.call(nodeList, function(div, i) {
		currentPullRequests.length = 1;
		if(currentPullRequests.length !== 0){

			if(currentPullRequests.indexOf(div.text.trim()) === -1){
				chrome.storage.sync.get("data", function (obj) {
					chrome.notifications.create(
				        org + ':' + repo  + ':' + div.text.trim(),{
				            type:"basic",
				            title:org + '/' + repo,
				            message: div.text.trim(),
				            iconUrl:"../icons/512.png",
							buttons: [
								{
					            	title: "View open pull requests"
					        	}
							]
				        }, function() { }
				    );
				});
			};

			currentPullRequests.push(div.text.trim());
		}
	});

	chrome.notifications.onButtonClicked.addListener(function(notifId, btnIdx) {
		var notificatoinInformation = notifId.split(':');

		chrome.tabs.create(
			{
				url: 'http://github.com/' + notificatoinInformation[0] + '/' + notificatoinInformation[1] + '/pulls'
			}
		);
	});
}

getData();

setInterval(function(){
    getData();
}, 5000);
<commit_msg>Temp removing rich buttons<commit_after>var currentPullRequests = [];

function getData(){
	chrome.storage.sync.get("data", function (obj) {
		obj.data.forEach(function(project){
			if(project.org !== undefined && project.repo !== undefined){
			    var request = new XMLHttpRequest();

				request.open('GET', 'http://github.com/' + project.org + '/' + project.repo + '/pulls', true);

				request.onload = function() {
				  if (this.status >= 200 && this.status < 400) {
				  	parseData(this.response, project.org, project.repo);
				  }
				};

				request.send();
			};
		})
	});
}

function parseData(resp, org, repo){
    var container = document.implementation.createHTMLDocument().documentElement;
    container.innerHTML = resp;
    var nodeList = container.querySelectorAll('.table-list-issues .js-issue-row .issue-title-link');

   	[].forEach.call(nodeList, function(div, i) {
		currentPullRequests.length = 1;
		if(currentPullRequests.length !== 0){

			if(currentPullRequests.indexOf(div.text.trim()) === -1){
				chrome.notifications.create(
			        org + ':' + repo  + ':' + div.text.trim(),{
			            type:"basic",
			            title:org + '/' + repo,
			            message: div.text.trim(),
			            iconUrl:"../icons/512.png"
			        }, function() {
					}
			    );
			};

			currentPullRequests.push(div.text.trim());
		}
	});
}

getData();

setInterval(function(){
    getData();
}, 5000);
<|endoftext|>


<commit_before>const
	log			= require('log'),
	transform	= require ( 'babel-core' ).transform;

module.exports = function babel ( buffer, path ) {

	log.processing( path , 'babel.transform' );

	try { return transform( buffer , { presets : ['es2015'] } ).code; }
	catch ( error ) {

		log.error( `babel.transform has failed to parse the buffer for ${path}` , __filename , error );

		process.exit(1);

	}

};<commit_msg>Fixed compact error<commit_after>const
	log			= require('log'),
	transform	= require ( 'babel-core' ).transform;

module.exports = function babel ( buffer, path ) {

	log.processing( path , 'babel.transform' );

	try { return transform( buffer , { presets : ['es2015'] , compact : false } ).code; }
	catch ( error ) {

		log.error( `babel.transform has failed to parse the buffer for ${path}` , __filename , error );

		process.exit(1);

	}

};
<|endoftext|>


<commit_before>/*
 * Part of: "grunt-html-head-urls-min-toggle"
 * https://github.com/pinestec/grunt-html-head-urls-min-toggle
 *
 * Copyright (c) 2017 Michael Hartung
 * Licensed under the MIT license.
 */

'use strict';

var object_to_be_written = {
  action : [ 'switch', 'clean' ],
  chattiness : [ 'true', 'false' ],
  direction : [ 'min', 'regular' ],
  file_source : [ 'wildcard', 'list' ],
};

function global_help_output() {
	var plain_file_name_extractor_RegExp = new RegExp("([^/\\\\]*)$");
	var result_array = plain_file_name_extractor_RegExp.exec(process.argv[1]);
	if (result_array !== null) {
		console.log("usage:\t" + result_array[1] + " entire_filename [-h|--help] \n");
		console.log("Writing internally defined \"reference objects\" to disk with the help of \"JSON.stringify\"...");
		console.log("Just for later use with other packages.\n");
		console.log("optional arguments:");
		console.log("-h, --help\t\tShow this help message and exit");
	}
}

var help_needed__pattern = /^--?h(elp)?$/i;
var file_name__pattern = /^--?f$/i;
function scan_arguments(value, index, array) {
	if (index > 1) {
		if (value.search(help_needed__pattern) !== -1) {
			throw 'help';
		}
		if (value.search(file_name__pattern) !== -1) {
			throw 'file';
		}
	}
}
try {
	process.argv.forEach(scan_arguments);
} catch (exception) {
	switch (exception) {
	case 'help':
		global_help_output();
		return false;
	case 'file':
		console.log("FILE NAME FOUND...!");
		break;
	default:
		break;
	}
}

var global_functions = {};
global_functions = require('../tasks/html_head_urls_min_toggle__global_functions.js');
var fileSystem_Module = global_functions.globalModule_Try('fs');
var currentFileHandle = fileSystem_Module.openSync("./etc/global_options.json", 'w');
fileSystem_Module.writeFileSync(currentFileHandle, JSON.stringify(object_to_be_written));
fileSystem_Module.closeSync(currentFileHandle);
<commit_msg>Getting the filenames easy and nicely... - MH - PT<commit_after>/*
 * Part of: "grunt-html-head-urls-min-toggle"
 * https://github.com/pinestec/grunt-html-head-urls-min-toggle
 *
 * Copyright (c) 2017 Michael Hartung
 * Licensed under the MIT license.
 */

'use strict';

var object_to_be_written = {
  action : [ 'switch', 'clean' ],
  chattiness : [ 'true', 'false' ],
  direction : [ 'min', 'regular' ],
  file_source : [ 'wildcard', 'list' ],
};

var global_file_name = '';

function global_help_output() {
	var plain_file_name_extractor_RegExp = new RegExp("([^/\\\\]*)$");
	var result_array = plain_file_name_extractor_RegExp.exec(process.argv[1]);
	if (result_array !== null) {
		console.log("usage:\t" + result_array[1] + " entire_filename [-h|--help] \n");
		console.log("Writing internally defined \"reference objects\" to disk with the help of \"JSON.stringify\"...");
		console.log("Just for later use with other packages.\n");
		console.log("optional arguments:");
		console.log("-h, --help\t\tShow this help message and exit");
	}
}

var help_needed__pattern = /^--?h(elp)?$/i;
var file_name__pattern = /^--?f$/i;
function scan_arguments(value, index, array) {
	if (index > 1) {
		if (value.search(help_needed__pattern) !== -1) {
			throw 'help';
		}
		if (value.search(file_name__pattern) !== -1) {
			global_file_name = array[index + 1];
			throw 'file';
		}
	}
}
try {
	process.argv.forEach(scan_arguments);
} catch (exception) {
	switch (exception) {
	case 'help':
		global_help_output();
		return false;
	case 'file':
		console.log(global_file_name);
		break;
	default:
		console.log("DEFAULT: " + exception);
		break;
	}
}

var global_functions = {};
global_functions = require('../tasks/html_head_urls_min_toggle__global_functions.js');
var fileSystem_Module = global_functions.globalModule_Try('fs');
var currentFileHandle = fileSystem_Module.openSync("./etc/global_options.json", 'w');
fileSystem_Module.writeFileSync(currentFileHandle, JSON.stringify(object_to_be_written));
fileSystem_Module.closeSync(currentFileHandle);
<|endoftext|>


<commit_before>#!/usr/bin/env node

'use strict';

// description: `git clone` a project
// arguments: <project>

var async = require('async');
var chalk = require('chalk');
var fs = require('fs');
var path = require('path');
var spawn = require('child_process').spawn;

var config = require('../lib/config.js');
var paths = require('../lib/paths.js');
var storage = require('../lib/storage.js');
var utilities = require('../lib/utilities.js');

var program = utilities.programDefaults('clone', '<project>');

program.option('-a, --all', 'clone all projects with a repository');
program.option('-d, --directory', 'the directory to clone into');

program.parse(process.argv);
program.handleColor();

function clone(project, cb) {
  var directory;

  directory = project.directory ||
    path.join(program.directory || config.projects.directory, project.name);

  if (!directory) {
    console.error('Please specify a projects directory in',
      paths.CONFIG_FILE, 'or via the -d, --directory flag.');

    process.exit(1);
  }

  fs.exists(utilities.expand(directory), function (exists) {
    if (exists) {
      console.warn(chalk.yellow('The path "%s" already exists, skipping.'),
        directory);

      return cb();
    }

    // Store the directory we resolved for the project in its entry for use by
    // other scripts
    storage.updateProject(project.name, {directory: directory}, function () {
      directory = utilities.expand(directory);

      spawn('git',
        ['clone', project.repository, directory],
        {stdio: 'inherit'}).on('close', cb);
    });
  });
}

storage.setup(function () {
  if (program.all) {
    if (program.directory) {
      console.error('Error: --all and --directory are mutually exclusive.');

      process.exit(1);
    }

    var projects = storage.query({repository: {$has: true}},
      {sortBy: storage.sortByName});

    async.eachSeries(projects, function (project, cbEach) {
      clone(project, cbEach);
    }, function (err) {
      if (err) {
        console.error('Error cloning projects:', err);
      }
    });
  } else {
    var name = program.args[0];

    if (!name) {
      console.error('Please specify a project.');

      process.exit(1);
    }

    var project = storage.getProjectOrDie(name);

    clone(project, process.exit);
  }
});
<commit_msg>clone: refactor & add glob support<commit_after>#!/usr/bin/env node

'use strict';

// description: `git clone` a project
// arguments: <project>

var async = require('async');
var chalk = require('chalk');
var config = require('../lib/config.js');
var fs = require('fs');
var path = require('path');
var paths = require('../lib/paths.js');
var spawn = require('child_process').spawn;
var storage = require('../lib/storage.js');
var utilities = require('../lib/utilities.js');
var _ = require('lodash');

var program = utilities.programDefaults('clone', '<project>');

program.option('-a, --all', 'clone all projects with a repository');
program.option('-d, --directory', 'the directory to clone into');

program.parse(process.argv);
program.handleColor();

function clone(project, cb) {
  var directory;

  directory = project.directory ||
    path.join(program.directory || config.projects.directory, project.name);

  if (!directory) {
    console.error('Please specify a projects directory in',
      paths.CONFIG_FILE, 'or via the -d, --directory flag.');

    process.exit(1);
  }

  fs.exists(utilities.expand(directory), function (exists) {
    if (exists) {
      console.warn(chalk.yellow('The path "%s" already exists, skipping.'),
        directory);

      return cb();
    }

    // Store the directory we resolved for the project in its entry for use by
    // other scripts
    storage.updateProject(project.name, {directory: directory}, function () {
      directory = utilities.expand(directory);

      spawn('git',
        ['clone', project.repository, directory],
        {stdio: 'inherit'}).on('close', cb);
    });
  });
}

function cloneSeries(projects, cb) {
  async.eachSeries(projects, function (project, cbEach) {
    clone(project, cbEach);
  }, function (err) {
    if (err) {
      console.error('Error cloning projects:', err);
    }

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

function collectProjects(names) {
  _(names).map(function (name) {
    if (name.indexOf('*') !== -1) {
      return storage.minimatch(name);
    }

    return storage.getProjectOrDie(name);
  })
  .flatten()
  .uniq(_.pluck('name'))
  .valueOf();
}

storage.setup(function () {
  if (program.all) {
    if (program.directory) {
      console.error('Error: --all and --directory are mutually exclusive.');

      process.exit(1);
    }

    var projects = storage.query({repository: {$has: true}},
      {sortBy: storage.sortByName});

    cloneSeries(projects, process.exit);
  } else {
    if (!program.args.length) {
      console.error('Please specify a project.');

      process.exit(1);
    }

    cloneSeries(collectProjects(program.args), process.exit);
  }
});
<|endoftext|>


<commit_before>/* jshint browser: true, jquery: true */
/* global Panel:false, console:false, server:false, addWidget:false */

function addPanel(panel) {
  // TODO: determine the correct row (row of last element for column + 1)
  sendAddPanel(panel);
}

function processPanelMessages(server, id, content) {
  var processed = false;

  switch (id) {
    case Panel.MESSAGE_PANELS:
      receivedPanels(server, content);
      processed = true;
      break;

    case Panel.MESSAGE_ADD_PANEL:
      receivedAddPanel(server, new Panel(content));
      processed = true;
      break;

    case Panel.MESSAGE_DELETE_PANEL:
      receivedDeletePanel(server, content);
      processed = true;
      break;

    case Panel.MESSAGE_UPDATE_PANEL:
      receivedUpdatePanel(server, new Panel(content));
      processed = true;
      break;

    case Panel.MESSAGE_MOVE_PANEL_UP:
      console.log("content: " + content);
      receivedMovePanelUp(server, content);
      processed = true;
      break;

    case Panel.MESSAGE_MOVE_PANEL_DOWN:
      receivedMovePanelDown(server, content);
      processed = true;
      break;

    case Panel.MESSAGE_MOVE_PANEL_LEFT:
      receivedMovePanelLeft(server, content);
      processed = true;
      break;

    case Panel.MESSAGE_MOVE_PANEL_RIGHT:
      receivedMovePanelRight(server, content);
      processed = true;
      break;
  }

  return processed;
}

function isPanelAddMode() {
  // TODO: addPanelMode is global (refer main.js), not really happy about this
  return addPanelMode;
}

function receivedAddPanel(server, d) {
  addPanelToDom(d, isPanelAddMode());
}

function receivedDeletePanel(server, d) {
  deletePanel(d.id);
}

function receivedMovePanelUp(server, id) {
  movePanelUp(id);
}

function receivedMovePanelDown(server, id) {
  movePanelDown(id);
}

function receivedMovePanelLeft(server, panel) {
  changePanelColumn(panel);
}

function receivedMovePanelRight(server, panel) {
  changePanelColumn(panel);
}

function receivedUpdatePanel(server, panel) {
  // get current data
  var currentPanelElement = $('#panel' + panel.id);

  var currentPanel = new Panel(currentPanelElement.data());

  console.log("current data for " + panel.id + " is: " + JSON.stringify(currentPanel, null, " "));

  // apply changes to DOM
  if(currentPanel.name !== panel.name) {

  }

  if(currentPanel.column !== panel.column) {
    changePanelColumn(panel);
  }

  if(currentPanel.iconName !== panel.iconName) {

  }

  // update data attributes for id

}

function receivedPanels(server, d) {
  // TODO: logic to handle resend of panels
  // $(".panelRow").remove();

  var buttonsVisible = isPanelAddMode();

  var panelsArray = [];

  for (var item in d) {
   panelsArray.push(new Panel(d[item]));
  }

  panelsArray.sort(function (a, b) {
      if (a.row  b.row) {
          return 1;
      }
      return 0;
  });

  panelsArray.forEach(function (p) {
    addPanelToDom(p, buttonsVisible);
  });
}

function sendAddPanel(panel) {
  server.sendMessage(Panel.MESSAGE_ADD_PANEL, panel);
}

function sendDeletePanel(panel) {
  server.sendMessage(Panel.MESSAGE_DELETE_PANEL, panel);
}

function sendUpdatePanel(panel) {
  server.sendMessage(Panel.MESSAGE_UPDATE_PANEL, panel);
}

function sendMovePanelUp(id) {
  server.sendMessage(Panel.MESSAGE_MOVE_PANEL_UP, id);
}

function sendMovePanelDown(id) {
  server.sendMessage(Panel.MESSAGE_MOVE_PANEL_DOWN, id);
}

function sendMovePanelLeft(id) {
  server.sendMessage(Panel.MESSAGE_MOVE_PANEL_LEFT, id);
}

function sendMovePanelRight(id) {
  server.sendMessage(Panel.MESSAGE_MOVE_PANEL_RIGHT, id);
}

function addPanelToDom(panel, buttonsVisible) {
  var displayButtonClass = buttonsVisible ? "displayButton" : "";
  var displayStyle = buttonsVisible ? "block" : "none";

  $("#columnContainer" + panel.column).append(
    '
' +
    '
' +
    '
' +
    '        <button id="panelBtnAddWidget' + panel.id + '" type="button" class="configButtons btn btn-default btn-sml ' + displayButtonClass + '" style="display: ' + displayStyle + ';">' +
    '          ' +
    '        </button>' +
    '        <button id="panelBtnRemoveWidget' + panel.id + '" type="button" class="configButtons btn btn-default btn-sml ' + displayButtonClass + '" style="display: ' + displayStyle + ';">' +
    '          ' +
    '        </button>' +
    '        <button id="panelBtnDelete' + panel.id + '" type="button" class="configButtons btn btn-default btn-sml ' + displayButtonClass + '" style="display: ' + displayStyle + ';">' +
    '          ' +
    '        </button>' +
    '        <button id="panelBtnMoveDown' + panel.id + '" type="button" class="configButtons btn btn-default btn-sml ' + displayButtonClass + '" style="display: ' + displayStyle + ';">' +
    '          ' +
    '        </button>' +
    '        <button id="panelBtnMoveUp' + panel.id + '" type="button" class="configButtons btn btn-default btn-sml ' + displayButtonClass + '" style="display: ' + displayStyle + ';">' +
    '          ' +
    '        </button>' +
    '        <button id="panelBtnMoveLeft' + panel.id + '" type="button" class="configButtons btn btn-default btn-sml ' + displayButtonClass + '" style="display: ' + displayStyle + ';">' +
    '          ' +
    '        </button>' +
    '        <button id="panelBtnMoveRight' + panel.id + '" type="button" class="configButtons btn btn-default btn-sml ' + displayButtonClass + '" style="display: ' + displayStyle + ';">' +
    '          ' +
    '        </button>' +
    '
' +
    '     ' + panel.name +
    '
' +
    '
' +
    '
' +
    '
'
  );

  $('#panel' + panel.id).hide().fadeIn('slow');

  $('#panelBtnAddWidget' + panel.id).on('click', function() {
    addWidget('#panel' + panel.id);
  });

  $('#panelBtnRemoveWidget' + panel.id).on('click', function() {
  });

  $('#panelBtnDelete' + panel.id).on('click', function() {
    sendDeletePanel(panel);
  });

  $('#panelBtnMoveDown' + panel.id).on('click', function() {
    sendMovePanelDown(panel.id);
  });

  $('#panelBtnMoveUp' + panel.id).on('click', function() {
    sendMovePanelUp(panel.id);
  });

  $('#panelBtnMoveLeft' + panel.id).on('click', function() {
    sendMovePanelLeft(panel.id);
  });

  $('#panelBtnMoveRight' + panel.id).on('click', function() {
    sendMovePanelRight(panel.id);
  });
}

function deletePanel(id) {
  $('#panel' + id)
    .animate({height: 0, padding: 0, margin: 0}, {easing: "linear", duration: "slow", queue: false})
    .fadeOut('slow', function() {
    this.remove();
  });
}

function movePanelDown(id) {
  var panelList = getPanelList(id);

  if(panelList['panel' + id].position < (Object.keys(panelList).length - 1)) {
    // move down
    swapPanelVertically(panelList['panel' + id].id, panelList['panel' + id].nextId);
  }
}

function movePanelUp(id) {
  var panelList = getPanelList(id);

  if(panelList['panel' + id].position > 0) {
    // move up
    swapPanelVertically(panelList['panel' + id].id, panelList['panel' + id].prevId);
  }
}

function changePanelColumn(panelData) {
  var panel = $('#panel' + panelData.id);

  $('#panel' + panelData.id)
    .animate({height: 0, padding: 0, margin: 0}, {easing: "linear", duration: "fast", queue: false})
    .fadeOut('fast', function() {
      panel
        .appendTo("#columnContainer" + panelData.column)
        .hide()
        .css('height', '')
        .css('padding', '')
        .css('margin', '')
        .fadeIn('fast');

  });
}

function getPanelList(id) {
  var prevPanel = null;
  var nextPanel = null;
  var counter = 0;
  var panelList = {};

  $('#panel' + id).parent().children().each(function() {
    panelList[$(this).attr('id')] = {
      id: $(this).attr('id'),
      position: counter++,
      value: this,
      prevId: null,
      nextId: null
    };

    if(prevPanel !== null) {
      panelList[$(this).attr('id')].prevId = prevPanel.id;
      prevPanel.nextId = $(this).attr('id');
    }

    prevPanel = panelList[$(this).attr('id')];
  });

  return panelList;
}

function swapPanelVertically(a, b){
    a = $('#' + a)[0];
    b = $('#' + b)[0];

    var t = a.parentNode.insertBefore(document.createTextNode(''), a);

    b.parentNode.insertBefore(a, b);
    t.parentNode.insertBefore(b, t);
    t.parentNode.removeChild(t);
}

<commit_msg>deletePanel now works by id<commit_after>/* jshint browser: true, jquery: true */
/* global Panel:false, console:false, server:false, addWidget:false */

function addPanel(panel) {
  // TODO: determine the correct row (row of last element for column + 1)
  sendAddPanel(panel);
}

function processPanelMessages(server, id, content) {
  var processed = false;

  switch (id) {
    case Panel.MESSAGE_PANELS:
      receivedPanels(server, content);
      processed = true;
      break;

    case Panel.MESSAGE_ADD_PANEL:
      receivedAddPanel(server, new Panel(content));
      processed = true;
      break;

    case Panel.MESSAGE_DELETE_PANEL:
      receivedDeletePanel(server, content);
      processed = true;
      break;

    case Panel.MESSAGE_UPDATE_PANEL:
      receivedUpdatePanel(server, new Panel(content));
      processed = true;
      break;

    case Panel.MESSAGE_MOVE_PANEL_UP:
      console.log("content: " + content);
      receivedMovePanelUp(server, content);
      processed = true;
      break;

    case Panel.MESSAGE_MOVE_PANEL_DOWN:
      receivedMovePanelDown(server, content);
      processed = true;
      break;

    case Panel.MESSAGE_MOVE_PANEL_LEFT:
      receivedMovePanelLeft(server, content);
      processed = true;
      break;

    case Panel.MESSAGE_MOVE_PANEL_RIGHT:
      receivedMovePanelRight(server, content);
      processed = true;
      break;
  }

  return processed;
}

function isPanelAddMode() {
  // TODO: addPanelMode is global (refer main.js), not really happy about this
  return addPanelMode;
}

function receivedAddPanel(server, d) {
  addPanelToDom(d, isPanelAddMode());
}

function receivedDeletePanel(server, id) {
  deletePanel(id);
}

function receivedMovePanelUp(server, id) {
  movePanelUp(id);
}

function receivedMovePanelDown(server, id) {
  movePanelDown(id);
}

function receivedMovePanelLeft(server, panel) {
  changePanelColumn(panel);
}

function receivedMovePanelRight(server, panel) {
  changePanelColumn(panel);
}

function receivedUpdatePanel(server, panel) {
  // get current data
  var currentPanelElement = $('#panel' + panel.id);

  var currentPanel = new Panel(currentPanelElement.data());

  console.log("current data for " + panel.id + " is: " + JSON.stringify(currentPanel, null, " "));

  // apply changes to DOM
  if(currentPanel.name !== panel.name) {

  }

  if(currentPanel.column !== panel.column) {
    changePanelColumn(panel);
  }

  if(currentPanel.iconName !== panel.iconName) {

  }

  // update data attributes for id

}

function receivedPanels(server, d) {
  // TODO: logic to handle resend of panels
  // $(".panelRow").remove();

  var buttonsVisible = isPanelAddMode();

  var panelsArray = [];

  for (var item in d) {
   panelsArray.push(new Panel(d[item]));
  }

  panelsArray.sort(function (a, b) {
      if (a.row  b.row) {
          return 1;
      }
      return 0;
  });

  panelsArray.forEach(function (p) {
    addPanelToDom(p, buttonsVisible);
  });
}

function sendAddPanel(panel) {
  server.sendMessage(Panel.MESSAGE_ADD_PANEL, panel);
}

function sendDeletePanel(id) {
  server.sendMessage(Panel.MESSAGE_DELETE_PANEL, id);
}

function sendUpdatePanel(panel) {
  server.sendMessage(Panel.MESSAGE_UPDATE_PANEL, panel);
}

function sendMovePanelUp(id) {
  server.sendMessage(Panel.MESSAGE_MOVE_PANEL_UP, id);
}

function sendMovePanelDown(id) {
  server.sendMessage(Panel.MESSAGE_MOVE_PANEL_DOWN, id);
}

function sendMovePanelLeft(id) {
  server.sendMessage(Panel.MESSAGE_MOVE_PANEL_LEFT, id);
}

function sendMovePanelRight(id) {
  server.sendMessage(Panel.MESSAGE_MOVE_PANEL_RIGHT, id);
}

function addPanelToDom(panel, buttonsVisible) {
  var displayButtonClass = buttonsVisible ? "displayButton" : "";
  var displayStyle = buttonsVisible ? "block" : "none";

  $("#columnContainer" + panel.column).append(
    '
' +
    '
' +
    '
' +
    '        <button id="panelBtnAddWidget' + panel.id + '" type="button" class="configButtons btn btn-default btn-sml ' + displayButtonClass + '" style="display: ' + displayStyle + ';">' +
    '          ' +
    '        </button>' +
    '        <button id="panelBtnRemoveWidget' + panel.id + '" type="button" class="configButtons btn btn-default btn-sml ' + displayButtonClass + '" style="display: ' + displayStyle + ';">' +
    '          ' +
    '        </button>' +
    '        <button id="panelBtnDelete' + panel.id + '" type="button" class="configButtons btn btn-default btn-sml ' + displayButtonClass + '" style="display: ' + displayStyle + ';">' +
    '          ' +
    '        </button>' +
    '        <button id="panelBtnMoveDown' + panel.id + '" type="button" class="configButtons btn btn-default btn-sml ' + displayButtonClass + '" style="display: ' + displayStyle + ';">' +
    '          ' +
    '        </button>' +
    '        <button id="panelBtnMoveUp' + panel.id + '" type="button" class="configButtons btn btn-default btn-sml ' + displayButtonClass + '" style="display: ' + displayStyle + ';">' +
    '          ' +
    '        </button>' +
    '        <button id="panelBtnMoveLeft' + panel.id + '" type="button" class="configButtons btn btn-default btn-sml ' + displayButtonClass + '" style="display: ' + displayStyle + ';">' +
    '          ' +
    '        </button>' +
    '        <button id="panelBtnMoveRight' + panel.id + '" type="button" class="configButtons btn btn-default btn-sml ' + displayButtonClass + '" style="display: ' + displayStyle + ';">' +
    '          ' +
    '        </button>' +
    '
' +
    '     ' + panel.name +
    '
' +
    '
' +
    '
' +
    '
'
  );

  $('#panel' + panel.id).hide().fadeIn('slow');

  $('#panelBtnAddWidget' + panel.id).on('click', function() {
    addWidget('#panel' + panel.id);
  });

  $('#panelBtnRemoveWidget' + panel.id).on('click', function() {
  });

  $('#panelBtnDelete' + panel.id).on('click', function() {
    sendDeletePanel(panel.id);
  });

  $('#panelBtnMoveDown' + panel.id).on('click', function() {
    sendMovePanelDown(panel.id);
  });

  $('#panelBtnMoveUp' + panel.id).on('click', function() {
    sendMovePanelUp(panel.id);
  });

  $('#panelBtnMoveLeft' + panel.id).on('click', function() {
    sendMovePanelLeft(panel.id);
  });

  $('#panelBtnMoveRight' + panel.id).on('click', function() {
    sendMovePanelRight(panel.id);
  });
}

function deletePanel(id) {
  $('#panel' + id)
    .animate({height: 0, padding: 0, margin: 0}, {easing: "linear", duration: "slow", queue: false})
    .fadeOut('slow', function() {
    this.remove();
  });
}

function movePanelDown(id) {
  var panelList = getPanelList(id);

  if(panelList['panel' + id].position < (Object.keys(panelList).length - 1)) {
    // move down
    swapPanelVertically(panelList['panel' + id].id, panelList['panel' + id].nextId);
  }
}

function movePanelUp(id) {
  var panelList = getPanelList(id);

  if(panelList['panel' + id].position > 0) {
    // move up
    swapPanelVertically(panelList['panel' + id].id, panelList['panel' + id].prevId);
  }
}

function changePanelColumn(panelData) {
  var panel = $('#panel' + panelData.id);

  $('#panel' + panelData.id)
    .animate({height: 0, padding: 0, margin: 0}, {easing: "linear", duration: "fast", queue: false})
    .fadeOut('fast', function() {
      panel
        .appendTo("#columnContainer" + panelData.column)
        .hide()
        .css('height', '')
        .css('padding', '')
        .css('margin', '')
        .fadeIn('fast');

  });
}

function getPanelList(id) {
  var prevPanel = null;
  var nextPanel = null;
  var counter = 0;
  var panelList = {};

  $('#panel' + id).parent().children().each(function() {
    panelList[$(this).attr('id')] = {
      id: $(this).attr('id'),
      position: counter++,
      value: this,
      prevId: null,
      nextId: null
    };

    if(prevPanel !== null) {
      panelList[$(this).attr('id')].prevId = prevPanel.id;
      prevPanel.nextId = $(this).attr('id');
    }

    prevPanel = panelList[$(this).attr('id')];
  });

  return panelList;
}

function swapPanelVertically(a, b){
    a = $('#' + a)[0];
    b = $('#' + b)[0];

    var t = a.parentNode.insertBefore(document.createTextNode(''), a);

    b.parentNode.insertBefore(a, b);
    t.parentNode.insertBefore(b, t);
    t.parentNode.removeChild(t);
}

<|endoftext|>


<commit_before>#!/usr/bin/env node
var base        = require('taskcluster-base');
var data        = require('../hooks/data');
var debug       = require('debug')('hooks:schedule-hooks');
var path        = require('path');
var Promise     = require('promise');
var Scheduler   = require('../hooks/scheduler');
var taskcluster = require('taskcluster-client');

/* Launch schedule-hooks */
var launch = async function(profile) {
  debug("Launching with profile: %s", profile);

  // Load configuration
  var cfg = base.config({
    defaults:  require('../config/defaults'),
    profile:   require('../config/' + profile),
    envs: [
      'pulse_username',
      'pulse_password',
      'taskcluster_credentials_clientId',
      'taskcluster_credentials_accessToken',
      'azure_accountName',
      'azure_accountKey'
    ],
    filename:  'taskcluster-hooks'
  });

  // Create Hooks table
  var Hook = data.Hook.setup({
    table:        cfg.get('hooks:hookTableName'),
    credentials:  cfg.get('azure'),
    process:      'schedule-hooks'
  });

  // Create a validator
  debug("Waiting for resources to be created");
  var validator;
  await Promise.all([
      (async () => {
        validator = await base.validator({
          folder:        path.join(__dirname, '..', 'schemas'),
          constants:     require('../schemas/constants'),
          publish:       cfg.get('hooks:publishMetaData') == 'true',
          schemaPrefix:  'hooks/v1/',
          preload: [
            'http://schemas.taskcluster.net/queue/v1/create-task-request.json',
            'http://schemas.taskcluster.net/queue/v1/task-status.json'
          ]
        });
      })(),
      Hook.ensureTable(),
  ]);

  // Create scheduler
  var scheduler = new Scheduler({
    Hook:           Hook,
    queue:          new taskcluster.Queue({
      credentials:  cfg.get('taskcluster:credentials'),
      baseUrl:      cfg.get('taskcluster:queueBaseUrl')
    }),
    pollingDelay:   cfg.get('hooks:schedule:pollingDelay')
  });

  // Start scheduler
  scheduler.start();

  // Notify the parent process, so this worker can run using LocalApp
  base.app.notifyLocalAppInParentProcess();

  return scheduler;
};

// If schedule-hooks.js is executed run launch
if (!module.parent) {
  // Find configuration profile
  var profile = process.argv[2];
  if (!profile) {
    console.log("Usage: schedule-hooks.js [profile]");
    console.error("ERROR: No configuration profile is provided");
  }
  // Launch with given profile
  launch(profile).then(function() {
    debug("Launched schedule-hooks sucessfully");
  }).catch(function(err) {
    debug("Failed to start schedule-hooks, err: %s, as JSON: %j",
          err, err, err.stack);
    process.exit(1);
  });
}

module.exports = launch;
<commit_msg>remove unused validator<commit_after>#!/usr/bin/env node
var base        = require('taskcluster-base');
var data        = require('../hooks/data');
var debug       = require('debug')('hooks:schedule-hooks');
var path        = require('path');
var Promise     = require('promise');
var Scheduler   = require('../hooks/scheduler');
var taskcluster = require('taskcluster-client');

/* Launch schedule-hooks */
var launch = async function(profile) {
  debug("Launching with profile: %s", profile);

  // Load configuration
  var cfg = base.config({
    defaults:  require('../config/defaults'),
    profile:   require('../config/' + profile),
    envs: [
      'pulse_username',
      'pulse_password',
      'taskcluster_credentials_clientId',
      'taskcluster_credentials_accessToken',
      'azure_accountName',
      'azure_accountKey'
    ],
    filename:  'taskcluster-hooks'
  });

  // Create Hooks table
  var Hook = data.Hook.setup({
    table:        cfg.get('hooks:hookTableName'),
    credentials:  cfg.get('azure'),
    process:      'schedule-hooks'
  });

  // Create a validator
  debug("Waiting for resources to be created");
  await Hook.ensureTable();

  // Create scheduler
  var scheduler = new Scheduler({
    Hook:           Hook,
    queue:          new taskcluster.Queue({
      credentials:  cfg.get('taskcluster:credentials'),
      baseUrl:      cfg.get('taskcluster:queueBaseUrl')
    }),
    pollingDelay:   cfg.get('hooks:schedule:pollingDelay')
  });

  // Start scheduler
  scheduler.start();

  // Notify the parent process, so this worker can run using LocalApp
  base.app.notifyLocalAppInParentProcess();

  return scheduler;
};

// If schedule-hooks.js is executed run launch
if (!module.parent) {
  // Find configuration profile
  var profile = process.argv[2];
  if (!profile) {
    console.log("Usage: schedule-hooks.js [profile]");
    console.error("ERROR: No configuration profile is provided");
  }
  // Launch with given profile
  launch(profile).then(function() {
    debug("Launched schedule-hooks sucessfully");
  }).catch(function(err) {
    debug("Failed to start schedule-hooks, err: %s, as JSON: %j",
          err, err, err.stack);
    process.exit(1);
  });
}

module.exports = launch;
<|endoftext|>


<commit_before>const _ = require('lodash/fp')

const configManager = require('../new-config-manager')
const logger = require('../logger')
const queries = require('./queries')
const settingsLoader = require('../new-settings-loader')
const customers = require('../customers')

const notificationCenter = require('./notificationCenter')
const utils = require('./utils')
const emailFuncs = require('./email')
const smsFuncs = require('./sms')
const { STALE, STALE_STATE } = require('./codes')

function buildMessage (alerts, notifications) {
  const smsEnabled = utils.isActive(notifications.sms)
  const emailEnabled = utils.isActive(notifications.email)

  let rec = {}
  if (smsEnabled) {
    rec = _.set(['sms', 'body'])(
      smsFuncs.printSmsAlerts(alerts, notifications.sms)
    )(rec)
  }
  if (emailEnabled) {
    rec = _.set(['email', 'subject'])(
      emailFuncs.alertSubject(alerts, notifications.email)
    )(rec)
    rec = _.set(['email', 'body'])(
      emailFuncs.printEmailAlerts(alerts, notifications.email)
    )(rec)
  }

  return rec
}

function checkNotification (plugins) {
  const notifications = plugins.getNotificationConfig()
  const smsEnabled = utils.isActive(notifications.sms)
  const emailEnabled = utils.isActive(notifications.email)
  const notificationCenterEnabled = utils.isActive(notifications.notificationCenter)

  if (!(notificationCenterEnabled || smsEnabled || emailEnabled)) return Promise.resolve()

  return getAlerts(plugins)
    .then(alerts => {
      notifyIfActive('errors', 'errorAlertsNotify', alerts)
      const currentAlertFingerprint = utils.buildAlertFingerprint(
        alerts,
        notifications
      )
      if (!currentAlertFingerprint) {
        const inAlert = !!utils.getAlertFingerprint()
        // variables for setAlertFingerprint: (fingerprint = null, lastAlertTime = null)
        utils.setAlertFingerprint(null, null)
        if (inAlert) return utils.sendNoAlerts(plugins, smsEnabled, emailEnabled)
      }
      if (utils.shouldNotAlert(currentAlertFingerprint)) return

      const message = buildMessage(alerts, notifications)
      utils.setAlertFingerprint(currentAlertFingerprint, Date.now())
      return plugins.sendMessage(message)
    })
    .then(results => {
      if (results && results.length > 0) {
        logger.debug('Successfully sent alerts')
      }
    })
    .catch(logger.error)
}

function getAlerts (plugins) {
  return Promise.all([
    plugins.checkBalances(),
    queries.machineEvents(),
    plugins.getMachineNames()
  ]).then(([balances, events, devices]) => {
    notifyIfActive('balance', 'balancesNotify', balances)
    return buildAlerts(checkPings(devices), balances, events, devices)
  })
}

function buildAlerts (pings, balances, events, devices) {
  const alerts = { devices: {}, deviceNames: {} }
  alerts.general = _.filter(r => !r.deviceId, balances)
  _.forEach(device => {
    const deviceId = device.deviceId
    const ping = pings[deviceId] || []
    const stuckScreen = checkStuckScreen(events, device)

    alerts.devices = _.set([deviceId, 'balanceAlerts'], _.filter(
      ['deviceId', deviceId],
      balances
    ), alerts.devices)
    alerts.devices[deviceId].deviceAlerts = _.isEmpty(ping) ? stuckScreen : ping

    alerts.deviceNames[deviceId] = device.name
  }, devices)

  return alerts
}

function checkPings (devices) {
  const deviceIds = _.map('deviceId', devices)
  const pings = _.map(utils.checkPing, devices)
  return _.zipObject(deviceIds)(pings)
}

function checkStuckScreen (deviceEvents, machine) {
  const lastEvent = _.pipe(
    _.filter(e => e.device_id === machine.deviceId),
    _.sortBy(utils.getDeviceTime),
    _.map(utils.parseEventNote),
    _.last
  )(deviceEvents)

  if (!lastEvent) return []

  const state = lastEvent.note.state
  const isIdle = lastEvent.note.isIdle

  if (isIdle) return []

  const age = Math.floor(lastEvent.age)
  const machineName = machine.name
  if (age > STALE_STATE) return [{ code: STALE, state, age, machineName }]

  return []
}

function transactionNotify (tx, rec) {
  return settingsLoader.loadLatest().then(settings => {
    const notifSettings = configManager.getGlobalNotifications(settings.config)
    const highValueTx = tx.fiat.gt(notifSettings.highValueTransaction || Infinity)
    const isCashOut = tx.direction === 'cashOut'

    // for notification center
    const directionDisplay = isCashOut ? 'cash-out' : 'cash-in'
    const readyToNotify = _.defaultTo(false, notifSettings.notificationCenter.transactions) && (!isCashOut || (isCashOut && rec.isRedemption))
    // awaiting for redesign. notification should not be sent if toggle in the settings table is disabled,
    // but currently we're sending notifications of high value tx even with the toggle disabled
    if (readyToNotify && !highValueTx) {
      notifyIfActive('transactions', 'notifCenterTransactionNotify', highValueTx, directionDisplay, tx.fiat, tx.fiatCode, tx.deviceId, tx.toAddress)
    } else if (readyToNotify && highValueTx) {
      notificationCenter.notifCenterTransactionNotify(highValueTx, directionDisplay, tx.fiat, tx.fiatCode, tx.deviceId, tx.toAddress)
    }

    // alert through sms or email any transaction or high value transaction, if SMS || email alerts are enabled
    const walletSettings = configManager.getWalletSettings(tx.cryptoCode, settings.config)
    const zeroConfLimit = walletSettings.zeroConfLimit || 0
    const zeroConf = isCashOut && tx.fiat.lte(zeroConfLimit)
    const notificationsEnabled = notifSettings.sms.transactions || notifSettings.email.transactions
    const customerPromise = tx.customerId ? customers.getById(tx.customerId) : Promise.resolve({})

    if (!notificationsEnabled && !highValueTx) return Promise.resolve()
    if (zeroConf && isCashOut && !rec.isRedemption && !rec.error) return Promise.resolve()
    if (!zeroConf && rec.isRedemption) return sendRedemptionMessage(tx.id, rec.error)

    return Promise.all([
      queries.getMachineName(tx.deviceId),
      customerPromise
    ]).then(([machineName, customer]) => {
      return utils.buildTransactionMessage(tx, rec, highValueTx, machineName, customer)
    }).then(([msg, highValueTx]) => sendTransactionMessage(msg, highValueTx))
  })
}

function complianceNotify (customer, deviceId, action, period) {
  return Promise.all([
    settingsLoader.loadLatest(),
    queries.getMachineName(deviceId)
  ])
    .then(([settings, machineName]) => {
      const notifications = configManager.getGlobalNotifications(settings.config)

      const msgCore = {
        BLOCKED: `was blocked`,
        SUSPENDED: `was suspended for ${!!period && period} days`
      }

      const rec = {
        sms: {
          body: `Customer ${customer.phone} ${msgCore[action]} - ${machineName}`
        },
        email: {
          subject: `Customer compliance`,
          body: `Customer ${customer.phone} ${msgCore[action]} in machine ${machineName}`
        }
      }

      const promises = []

      const emailActive =
        notifications.email.active &&
        notifications.email.compliance

      const smsActive =
        notifications.sms.active &&
        notifications.sms.compliance

      if (emailActive) promises.push(emailFuncs.sendMessage(settings, rec))
      if (smsActive) promises.push(smsFuncs.sendMessage(settings, rec))

      notifyIfActive('compliance', 'customerComplianceNotify', customer, deviceId, action, period)

      return Promise.all(promises)
    })
}

function sendRedemptionMessage (txId, error) {
  const subject = `Here's an update on transaction ${txId}`
  const body = error
    ? `Error: ${error}`
    : 'It was just dispensed successfully'

  const rec = {
    sms: {
      body: `${subject} - ${body}`
    },
    email: {
      subject,
      body
    }
  }
  return sendTransactionMessage(rec)
}

function sendTransactionMessage (rec, isHighValueTx) {
  return settingsLoader.loadLatest().then(settings => {
    const notifications = configManager.getGlobalNotifications(settings.config)

    const promises = []

    const emailActive =
      notifications.email.active &&
      (notifications.email.transactions || isHighValueTx)
    if (emailActive) promises.push(emailFuncs.sendMessage(settings, rec))

    const smsActive =
      notifications.sms.active &&
      (notifications.sms.transactions || isHighValueTx)
    if (smsActive) promises.push(smsFuncs.sendMessage(settings, rec))

    return Promise.all(promises)
  })
}

function cashboxNotify (deviceId) {
  return Promise.all([
    settingsLoader.loadLatest(),
    queries.getMachineName(deviceId)
  ])
    .then(([settings, machineName]) => {
      const notifications = configManager.getGlobalNotifications(settings.config)
      const rec = {
        sms: {
          body: `Cashbox removed - ${machineName}`
        },
        email: {
          subject: `Cashbox removal`,
          body: `Cashbox removed in machine ${machineName}`
        }
      }

      const promises = []

      const emailActive =
        notifications.email.active &&
        notifications.email.security

      const smsActive =
        notifications.sms.active &&
        notifications.sms.security

      if (emailActive) promises.push(emailFuncs.sendMessage(settings, rec))
      if (smsActive) promises.push(smsFuncs.sendMessage(settings, rec))
      notifyIfActive('security', 'cashboxNotify', deviceId)

      return Promise.all(promises)
    })
}

// for notification center, check if type of notification is active before calling the respective notify function
const notifyIfActive = (type, fnName, ...args) => {
  return settingsLoader.loadLatest().then(settings => {
    const notificationSettings = configManager.getGlobalNotifications(settings.config).notificationCenter
    if (!notificationCenter[fnName]) return Promise.reject(new Error(`Notification function ${fnName} for type ${type} does not exist`))
    if (!(notificationSettings.active && notificationSettings[type])) return Promise.resolve()
    return notificationCenter[fnName](...args)
  }).catch(logger.error)
}

module.exports = {
  transactionNotify,
  complianceNotify,
  checkNotification,
  checkPings,
  checkStuckScreen,
  sendRedemptionMessage,
  cashboxNotify,
  notifyIfActive
}
<commit_msg>fix: notification center call condition for transaction-related notifications<commit_after>const _ = require('lodash/fp')

const configManager = require('../new-config-manager')
const logger = require('../logger')
const queries = require('./queries')
const settingsLoader = require('../new-settings-loader')
const customers = require('../customers')

const notificationCenter = require('./notificationCenter')
const utils = require('./utils')
const emailFuncs = require('./email')
const smsFuncs = require('./sms')
const { STALE, STALE_STATE } = require('./codes')

function buildMessage (alerts, notifications) {
  const smsEnabled = utils.isActive(notifications.sms)
  const emailEnabled = utils.isActive(notifications.email)

  let rec = {}
  if (smsEnabled) {
    rec = _.set(['sms', 'body'])(
      smsFuncs.printSmsAlerts(alerts, notifications.sms)
    )(rec)
  }
  if (emailEnabled) {
    rec = _.set(['email', 'subject'])(
      emailFuncs.alertSubject(alerts, notifications.email)
    )(rec)
    rec = _.set(['email', 'body'])(
      emailFuncs.printEmailAlerts(alerts, notifications.email)
    )(rec)
  }

  return rec
}

function checkNotification (plugins) {
  const notifications = plugins.getNotificationConfig()
  const smsEnabled = utils.isActive(notifications.sms)
  const emailEnabled = utils.isActive(notifications.email)
  const notificationCenterEnabled = utils.isActive(notifications.notificationCenter)

  if (!(notificationCenterEnabled || smsEnabled || emailEnabled)) return Promise.resolve()

  return getAlerts(plugins)
    .then(alerts => {
      notifyIfActive('errors', 'errorAlertsNotify', alerts)
      const currentAlertFingerprint = utils.buildAlertFingerprint(
        alerts,
        notifications
      )
      if (!currentAlertFingerprint) {
        const inAlert = !!utils.getAlertFingerprint()
        // variables for setAlertFingerprint: (fingerprint = null, lastAlertTime = null)
        utils.setAlertFingerprint(null, null)
        if (inAlert) return utils.sendNoAlerts(plugins, smsEnabled, emailEnabled)
      }
      if (utils.shouldNotAlert(currentAlertFingerprint)) return

      const message = buildMessage(alerts, notifications)
      utils.setAlertFingerprint(currentAlertFingerprint, Date.now())
      return plugins.sendMessage(message)
    })
    .then(results => {
      if (results && results.length > 0) {
        logger.debug('Successfully sent alerts')
      }
    })
    .catch(logger.error)
}

function getAlerts (plugins) {
  return Promise.all([
    plugins.checkBalances(),
    queries.machineEvents(),
    plugins.getMachineNames()
  ]).then(([balances, events, devices]) => {
    notifyIfActive('balance', 'balancesNotify', balances)
    return buildAlerts(checkPings(devices), balances, events, devices)
  })
}

function buildAlerts (pings, balances, events, devices) {
  const alerts = { devices: {}, deviceNames: {} }
  alerts.general = _.filter(r => !r.deviceId, balances)
  _.forEach(device => {
    const deviceId = device.deviceId
    const ping = pings[deviceId] || []
    const stuckScreen = checkStuckScreen(events, device)

    alerts.devices = _.set([deviceId, 'balanceAlerts'], _.filter(
      ['deviceId', deviceId],
      balances
    ), alerts.devices)
    alerts.devices[deviceId].deviceAlerts = _.isEmpty(ping) ? stuckScreen : ping

    alerts.deviceNames[deviceId] = device.name
  }, devices)

  return alerts
}

function checkPings (devices) {
  const deviceIds = _.map('deviceId', devices)
  const pings = _.map(utils.checkPing, devices)
  return _.zipObject(deviceIds)(pings)
}

function checkStuckScreen (deviceEvents, machine) {
  const lastEvent = _.pipe(
    _.filter(e => e.device_id === machine.deviceId),
    _.sortBy(utils.getDeviceTime),
    _.map(utils.parseEventNote),
    _.last
  )(deviceEvents)

  if (!lastEvent) return []

  const state = lastEvent.note.state
  const isIdle = lastEvent.note.isIdle

  if (isIdle) return []

  const age = Math.floor(lastEvent.age)
  const machineName = machine.name
  if (age > STALE_STATE) return [{ code: STALE, state, age, machineName }]

  return []
}

function transactionNotify (tx, rec) {
  return settingsLoader.loadLatest().then(settings => {
    const notifSettings = configManager.getGlobalNotifications(settings.config)
    const highValueTx = tx.fiat.gt(notifSettings.highValueTransaction || Infinity)
    const isCashOut = tx.direction === 'cashOut'

    // for notification center
    const directionDisplay = isCashOut ? 'cash-out' : 'cash-in'
    const readyToNotify = _.defaultTo(false, notifSettings.notificationCenter.transactions) && (!isCashOut || (isCashOut && rec.isRedemption))
    if (readyToNotify && highValueTx) {
      notifyIfActive('transactions', 'notifCenterTransactionNotify', highValueTx, directionDisplay, tx.fiat, tx.fiatCode, tx.deviceId, tx.toAddress)
    }

    // alert through sms or email any transaction or high value transaction, if SMS || email alerts are enabled
    const walletSettings = configManager.getWalletSettings(tx.cryptoCode, settings.config)
    const zeroConfLimit = walletSettings.zeroConfLimit || 0
    const zeroConf = isCashOut && tx.fiat.lte(zeroConfLimit)
    const notificationsEnabled = notifSettings.sms.transactions || notifSettings.email.transactions
    const customerPromise = tx.customerId ? customers.getById(tx.customerId) : Promise.resolve({})

    if (!notificationsEnabled && !highValueTx) return Promise.resolve()
    if (zeroConf && isCashOut && !rec.isRedemption && !rec.error) return Promise.resolve()
    if (!zeroConf && rec.isRedemption) return sendRedemptionMessage(tx.id, rec.error)

    return Promise.all([
      queries.getMachineName(tx.deviceId),
      customerPromise
    ]).then(([machineName, customer]) => {
      return utils.buildTransactionMessage(tx, rec, highValueTx, machineName, customer)
    }).then(([msg, highValueTx]) => sendTransactionMessage(msg, highValueTx))
  })
}

function complianceNotify (customer, deviceId, action, period) {
  return Promise.all([
    settingsLoader.loadLatest(),
    queries.getMachineName(deviceId)
  ])
    .then(([settings, machineName]) => {
      const notifications = configManager.getGlobalNotifications(settings.config)

      const msgCore = {
        BLOCKED: `was blocked`,
        SUSPENDED: `was suspended for ${!!period && period} days`
      }

      const rec = {
        sms: {
          body: `Customer ${customer.phone} ${msgCore[action]} - ${machineName}`
        },
        email: {
          subject: `Customer compliance`,
          body: `Customer ${customer.phone} ${msgCore[action]} in machine ${machineName}`
        }
      }

      const promises = []

      const emailActive =
        notifications.email.active &&
        notifications.email.compliance

      const smsActive =
        notifications.sms.active &&
        notifications.sms.compliance

      if (emailActive) promises.push(emailFuncs.sendMessage(settings, rec))
      if (smsActive) promises.push(smsFuncs.sendMessage(settings, rec))

      notifyIfActive('compliance', 'customerComplianceNotify', customer, deviceId, action, period)

      return Promise.all(promises)
    })
}

function sendRedemptionMessage (txId, error) {
  const subject = `Here's an update on transaction ${txId}`
  const body = error
    ? `Error: ${error}`
    : 'It was just dispensed successfully'

  const rec = {
    sms: {
      body: `${subject} - ${body}`
    },
    email: {
      subject,
      body
    }
  }
  return sendTransactionMessage(rec)
}

function sendTransactionMessage (rec, isHighValueTx) {
  return settingsLoader.loadLatest().then(settings => {
    const notifications = configManager.getGlobalNotifications(settings.config)

    const promises = []

    const emailActive =
      notifications.email.active &&
      (notifications.email.transactions || isHighValueTx)
    if (emailActive) promises.push(emailFuncs.sendMessage(settings, rec))

    const smsActive =
      notifications.sms.active &&
      (notifications.sms.transactions || isHighValueTx)
    if (smsActive) promises.push(smsFuncs.sendMessage(settings, rec))

    return Promise.all(promises)
  })
}

function cashboxNotify (deviceId) {
  return Promise.all([
    settingsLoader.loadLatest(),
    queries.getMachineName(deviceId)
  ])
    .then(([settings, machineName]) => {
      const notifications = configManager.getGlobalNotifications(settings.config)
      const rec = {
        sms: {
          body: `Cashbox removed - ${machineName}`
        },
        email: {
          subject: `Cashbox removal`,
          body: `Cashbox removed in machine ${machineName}`
        }
      }

      const promises = []

      const emailActive =
        notifications.email.active &&
        notifications.email.security

      const smsActive =
        notifications.sms.active &&
        notifications.sms.security

      if (emailActive) promises.push(emailFuncs.sendMessage(settings, rec))
      if (smsActive) promises.push(smsFuncs.sendMessage(settings, rec))
      notifyIfActive('security', 'cashboxNotify', deviceId)

      return Promise.all(promises)
    })
}

// for notification center, check if type of notification is active before calling the respective notify function
const notifyIfActive = (type, fnName, ...args) => {
  return settingsLoader.loadLatest().then(settings => {
    const notificationSettings = configManager.getGlobalNotifications(settings.config).notificationCenter
    if (!notificationCenter[fnName]) return Promise.reject(new Error(`Notification function ${fnName} for type ${type} does not exist`))
    if (!(notificationSettings.active && notificationSettings[type])) return Promise.resolve()
    return notificationCenter[fnName](...args)
  }).catch(logger.error)
}

module.exports = {
  transactionNotify,
  complianceNotify,
  checkNotification,
  checkPings,
  checkStuckScreen,
  sendRedemptionMessage,
  cashboxNotify,
  notifyIfActive
}
<|endoftext|>


<commit_before>/**
* Set Timeout
* (c) 2013 Bill, BunKat LLC.
*
* Works similar to setTimeout() but allows you to specify a Later schedule
* instead of milliseconds.
*
* Later is freely distributable under the MIT license.
* For all details and documentation:
*     http://github.com/bunkat/later
*/

later.setTimeout = function(fn, sched) {

  var s = later.schedule(sched), t;
  scheduleTimeout();

  /**
  * Schedules the timeout to occur. If the next occurrence is greater than the
  * max supported delay (2147483647 ms) than we delay for that amount before
  * attempting to schedule the timeout again.
  */
  function scheduleTimeout() {
    var now = Date.now(),
        next = s.next(2, now),
        diff = next[0].getTime() - now;

    // minimum time to fire is one second, use next occurrence instead
    if(diff < 1000) {
      diff = next[1].getTime() - now;
    }

    if(diff < 2147483647) {
      t = setTimeout(fn, diff);
    }
    else {
      t = setTimeout(scheduleTimeout, 2147483647);
    }
  }

  return {

    /**
    * Clears the timeout.
    */
    clear: function() {
      clearTimeout(t);
    }

  };

};<commit_msg>fixes #63<commit_after>/**
* Set Timeout
* (c) 2013 Bill, BunKat LLC.
*
* Works similar to setTimeout() but allows you to specify a Later schedule
* instead of milliseconds.
*
* Later is freely distributable under the MIT license.
* For all details and documentation:
*     http://github.com/bunkat/later
*/

later.setTimeout = function(fn, sched) {

  var s = later.schedule(sched), t;
  scheduleTimeout();

  /**
  * Schedules the timeout to occur. If the next occurrence is greater than the
  * max supported delay (2147483647 ms) than we delay for that amount before
  * attempting to schedule the timeout again.
  */
  function scheduleTimeout() {
    var now = Date.now(),
        next = s.next(2, now);

    if (!next[0] || !next[1]) {
      return;
    }

    var diff = next[0].getTime() - now;

    // minimum time to fire is one second, use next occurrence instead
    if(diff < 1000) {
      diff = next[1].getTime() - now;
    }

    if(diff < 2147483647) {
      t = setTimeout(fn, diff);
    }
    else {
      t = setTimeout(scheduleTimeout, 2147483647);
    }
  }

  return {

    /**
    * Clears the timeout.
    */
    clear: function() {
      clearTimeout(t);
    }

  };

};<|endoftext|>


<commit_before>#!/usr/bin/env node
var fs = require('fs');
var path = require('path');

var async = require('async');
var _ = require("underscore");

var watchers = {};
var current_state = {};

var main = function(){
  var cwd = process.cwd();
  console.log('watching cwd', cwd);
  watch(cwd);
};

var watch = function(to_watch){
  var sub_dirs = [],
    files = [],
    iter,
    children = fs.readdirSync(to_watch).map(function(child){
      return path.join(to_watch, child);
    });

  iter = function(p, cb){
    console.log(p);
    fs.lstat(p, function(err, stats){
      if (stats.isDirectory()){
        sub_dirs.push(p);
      } else if (stats.isFile()) {
        files.push(p);
      }
      return cb();
    });
  };

  async.eachLimit(children, 10, iter, function(err){
    sub_dirs.forEach(watch);
    files.forEach(add_listener);
    add_listener(to_watch, true);
  });
};

var add_listener = function(f, is_dir){
  is_dir = is_dir === true ? true : false;
  fs.readFile(f, function(err, buf){
    current_state[f] = buf;
    // note sure possibly 50K closures is a good idea, but it works for HN...
    fs.watch(f, listener.bind(null, f, is_dir));
  });

};

var listener = function(original_path, is_dir, event, filename){
  if (!is_dir && event != 'rename'){
    buf = fs.readFileSync(original_path);
    console.log(buf.toString(), current_state[original_path].toString());
  }
  console.log(event, original_path, is_dir);
};
main();<commit_msg>Jslint<commit_after>#!/usr/bin/env node
var fs = require('fs');
var path = require('path');

var async = require('async');
var _ = require("underscore");

var watchers = {};
var current_state = {};

var listener = function (original_path, is_dir, event, filename) {
  var buf;
  if (!is_dir && event !== 'rename') {
    buf = fs.readFileSync(original_path);
    console.log(buf.toString(), current_state[original_path].toString());
  }
  console.log(event, original_path, is_dir);
};

var add_listener = function (f, is_dir) {
  is_dir = is_dir === true ? true : false;
  fs.readFile(f, function (err, buf) {
    current_state[f] = buf;
    // note sure possibly 50K closures is a good idea, but it works for HN...
    fs.watch(f, listener.bind(null, f, is_dir));
  });

};

var watch = function (to_watch) {
  var sub_dirs = [],
    files = [],
    iter,
    children = fs.readdirSync(to_watch).map(function (child) {
      return path.join(to_watch, child);
    });

  iter = function (p, cb) {
    console.log(p);
    fs.lstat(p, function (err, stats) {
      if (stats.isDirectory()) {
        sub_dirs.push(p);
      } else if (stats.isFile()) {
        files.push(p);
      }
      return cb();
    });
  };

  async.eachLimit(children, 10, iter, function (err) {
    sub_dirs.forEach(watch);
    files.forEach(add_listener);
    add_listener(to_watch, true);
  });
};

var main = function () {
  var cwd = process.cwd();
  console.log('watching cwd', cwd);
  watch(cwd);
};

main();
<|endoftext|>


<commit_before>(function() {
  var Q = require('q');

  // Sickbeard shows class
  var SickbeardHistory = function(delegate) {
    this.delegate = delegate;
  };

  module.exports = SickbeardHistory;

})();<commit_msg>Added history methods.<commit_after>(function() {
  var Q = require('q');

  // Sickbeard shows class
  var SickbeardHistory = function(delegate) {
    this.delegate = delegate;
  };

  /**
   * Call the 'history' method of the API, and return the data
   * from the call.
   *
   * @param object args
   *   Optional arguments to be passed with the call.
   *
   * @return array
   *
   * @see http://sickbeard.com/api/#history
   */
  SickbeardHistory.prototype.self = function(args) {
    var delegate  = this.delegate;

    return delegate.cmd('history', args).then(function(response){
      return response.data;
    });
  };

  /**
   * Call the 'history.trim' method of the API, and return the data
   * from the call.
   *
   * @return object
   *
   * @see http://sickbeard.com/api/#history.trim
   */
  SickbeardHistory.prototype.trim = function() {
    var delegate  = this.delegate;

    return delegate.cmd('history.trim').then(function(response){
      return response;
    });
  };

  /**
   * Call the 'history.clear' method of the API, and return the data
   * from the call.
   *
   * @return object
   *
   * @see http://sickbeard.com/api/#history.clear
   */
  SickbeardHistory.prototype.clear = function() {
    var delegate  = this.delegate;

    return delegate.cmd('history.clear').then(function(response){
      return response;
    });
  };

  module.exports = SickbeardHistory;

})();<|endoftext|>


<commit_before>/**
 * bootbox.js v4.0.0
 *
 * http://bootboxjs.com/license.txt
 */
// @see https://github.com/makeusabrew/bootbox/issues/71
window.bootbox = window.bootbox || (function init($, undefined) {
  "use strict";

  // the base DOM structure needed to create a modal
  var templates = {
    dialog:
      "
" +
        "
" +
          "
" +
            "

" +
          "
" +
        "
" +
      "
",
    header:
      "
" +
        "

" +
      "
",
    footer:
      "

",
    closeButton:
      "<button type='button' class='bootbox-close-button close'>×</button>",
    form:
      "<form class='bootbox-form'></form>",
    inputs: {
      text:
        "<input class='bootbox-input form-control' autocomplete=off type=text />"
    }
  };

  // cache a reference to the jQueryfied body element
  var appendTo = $("body");

  var defaults = {
    // default language
    locale: "en",
    // show backdrop or not
    backdrop: true,
    // animate the modal in/out
    animate: true,
    // additional class string applied to the top level dialog
    className: null,
    // whether or not to include a close button
    closeButton: true,
    // show the dialog immediately by default
    show: true
  };

  // our public object; augmented after our private API
  var exports = {};

  /**
   * @private
   */
  function _t(key) {
    var locale = locales[defaults.locale];
    return locale ? locale[key] : locales.en[key];
  }

  function processCallback(e, dialog, callback) {
    e.preventDefault();

    // by default we assume a callback will get rid of the dialog,
    // although it is given the opportunity to override this

    // so, if the callback can be invoked and it *explicitly returns false*
    // then we'll set a flag to keep the dialog active...
    var preserveDialog = $.isFunction(callback) && callback(e) === false;

    // ... otherwise we'll bin it
    if (!preserveDialog) {
      dialog.modal("hide");
    }
  }

  function getKeyLength(obj) {
    // @TODO defer to Object.keys(x).length if available?
    var k, t = 0;
    for (k in obj) {
      t ++;
    }
    return t;
  }

  function each(collection, iterator) {
    var index = 0;
    $.each(collection, function(key, value) {
      iterator(key, value, index++);
    });
  }

  function sanitize(options) {
    var buttons;
    var total;

    if (typeof options !== "object") {
      throw new Error("Please supply an object of options");
    }

    if (!options.message) {
      throw new Error("Please specify a message");
    }

    // make sure any supplied options take precedence over defaults
    options = $.extend({}, defaults, options);

    if (!options.buttons) {
      options.buttons = {};
    }

    // we only support Bootstrap's "static" and false backdrop args
    // supporting true would mean you could dismiss the dialog without
    // explicitly interacting with it
    options.backdrop = options.backdrop ? "static" : false;

    buttons = options.buttons;

    total = getKeyLength(buttons);

    each(buttons, function(key, button, index) {

      if ($.isFunction(button)) {
        // short form, assume value is our callback. Since button
        // isn't an object it isn't a reference either so re-assign it
        button = buttons[key] = {
          callback: button
        };
      }

      // before any further checks make sure by now button is the correct type
      if ($.type(button) !== "object") {
        throw new Error("button with key " + key + " must be an object");
      }

      if (!button.label) {
        // the lack of an explicit label means we'll assume the key is good enough
        button.label = key;
      }

      if (!button.className) {
        if (total <= 2 && index === total-1) {
          // always add a primary to the main option in a two-button dialog
          button.className = "btn-primary";
        } else {
          button.className = "btn-default";
        }
      }
    });

    return options;
  }

  function mapArguments(args, properties) {
    var argn = args.length;
    var options = {};

    if (argn < 1 || argn > 2) {
      throw new Error("Invalid argument length");
    }

    if (argn === 2 || typeof args[0] === "string") {
      options[properties[0]] = args[0];
      options[properties[1]] = args[1];
    } else {
      options = args[0];
    }

    return options;
  }

  function mergeArguments(defaults, args, properties) {
    return $.extend(true, {}, defaults, mapArguments(args, properties));
  }

  function mergeButtons(labels, args, properties) {
    return validateButtons(
      mergeArguments(createButtons.apply(null, labels), args, properties),
      labels
    );
  }

  function createLabels() {
    var buttons = {};

    for (var i = 0, j = arguments.length; i < j; i++) {
      var argument = arguments[i];
      var key = argument.toLowerCase();
      var value = argument.toUpperCase();

      buttons[key] = {
        label: _t(value)
      };
    }

    return buttons;
  }

  function createButtons() {
    return {
      buttons: createLabels.apply(null, arguments)
    };
  }

  function validateButtons(options, buttons) {
    var allowedButtons = {};
    each(buttons, function(key, value) {
      allowedButtons[value] = true;
    });

    each(options.buttons, function(key) {
      if (allowedButtons[key] === undefined) {
        throw new Error("button key " + key + " is not allowed (options are " + buttons.join("\n") + ")");
      }
    });

    return options;
  }

  exports.alert = function() {
    var options;

    options = mergeButtons(["ok"], arguments, ["message", "callback"]);

    if (options.callback && !$.isFunction(options.callback)) {
      throw new Error("alert requires callback property to be a function when provided");
    }

    /**
     * overrides
     */
    options.buttons.ok.callback = options.onEscape = function() {
      if ($.isFunction(options.callback)) {
        return options.callback();
      }
      return true;
    };

    return exports.dialog(options);
  };

  exports.confirm = function() {
    var options;

    options = mergeButtons(["cancel", "confirm"], arguments, ["message", "callback"]);

    /**
     * overrides; undo anything the user tried to set they shouldn't have
     */
    options.buttons.cancel.callback = options.onEscape = function() {
      return options.callback(false);
    };

    options.buttons.confirm.callback = function() {
      return options.callback(true);
    };

    // confirm specific validation
    if (!$.isFunction(options.callback)) {
      throw new Error("confirm requires a callback");
    }

    return exports.dialog(options);
  };

  exports.prompt = function() {
    var options;
    var defaults;
    var dialog;
    var form;
    var input;
    var shouldShow;

    // we have to create our form first otherwise
    // its value is undefined when gearing up our options
    // @TODO this could be solved by allowing message to
    // be a function instead...
    form = $(templates.form);

    defaults = {
      buttons: createLabels("cancel", "confirm"),
      value: ""
    };

    options = validateButtons(
      mergeArguments(defaults, arguments, ["title", "callback"]),
      ["cancel", "confirm"]
    );

    // capture the user's show value; we always set this to false before
    // spawning the dialog to give us a chance to attach some handlers to
    // it, but we need to make sure we respect a preference not to show it
    shouldShow = (options.show === undefined) ? true : options.show;

    /**
     * overrides; undo anything the user tried to set they shouldn't have
     */
    options.message = form;

    options.buttons.cancel.callback = options.onEscape = function() {
      return options.callback(null);
    };

    options.buttons.confirm.callback = function() {
      return options.callback(input.val());
    };

    options.show = false;

    // prompt specific validation
    if (!options.title) {
      throw new Error("prompt requires a title");
    }

    if (!$.isFunction(options.callback)) {
      throw new Error("prompt requires a callback");
    }

    // create the input
    input = $(templates.inputs.text);
    input.val(options.value);

    // now place it in our form
    form.append(input);

    form.on("submit", function(e) {
      e.preventDefault();
      // @TODO can we actually click *the* button object instead?
      // e.g. buttons.confirm.click() or similar
      dialog.find(".btn-primary").click();
    });

    dialog = exports.dialog(options);

    // clear the existing handler focusing the submit button...
    dialog.off("shown.bs.modal");

    // ...and replace it with one focusing our input, if possible
    dialog.on("shown.bs.modal", function() {
      input.focus();
    });

    if (shouldShow === true) {
      dialog.modal("show");
    }

    return dialog;
  };

  exports.dialog = function(options) {
    options = sanitize(options);

    var dialog = $(templates.dialog);
    var body = dialog.find(".modal-body");
    var buttons = options.buttons;
    var buttonStr = "";
    var callbacks = {
      onEscape: options.onEscape
    };

    each(buttons, function(key, button) {

      // @TODO I don't like this string appending to itself; bit dirty. Needs reworking
      // can we just build up button elements instead? slower but neater. Then button
      // can just become a template too
      buttonStr += "<button data-bb-handler='" + key + "' type='button' class='btn " + button.className + "'>" + button.label + "</button>";
      callbacks[key] = button.callback;
    });

    body.find(".bootbox-body").html(options.message);

    if (options.animate === true) {
      dialog.addClass("fade");
    }

    if (options.className) {
      dialog.addClass(options.className);
    }

    if (options.title) {
      body.before(templates.header);
    }

    if (options.closeButton) {
      var closeButton = $(templates.closeButton);

      if (options.title) {
        dialog.find(".modal-header").prepend(closeButton);
      } else {
        closeButton.css("margin-top", "-10px").prependTo(body);
      }
    }

    if (options.title) {
      dialog.find(".modal-title").html(options.title);
    }

    if (buttonStr.length) {
      body.after(templates.footer);
      dialog.find(".modal-footer").html(buttonStr);
    }

    /**
     * Bootstrap event listeners; used handle extra
     * setup & teardown required after the underlying
     * modal has performed certain actions
     */

    dialog.on("hidden.bs.modal", function(e) {
      // ensure we don't accidentally intercept hidden events triggered
      // by children of the current dialog. We shouldn't anymore now BS
      // namespaces its events; but still worth doing
      if (e.target === this) {
        dialog.remove();
      }
    });

    /*
    dialog.on("show.bs.modal", function() {
      // sadly this doesn't work; show is called *just* before
      // the backdrop is added so we'd need a setTimeout hack or
      // otherwise... leaving in as would be nice
      if (options.backdrop) {
        dialog.next(".modal-backdrop").addClass("bootbox-backdrop");
      }
    });
    */

    dialog.on("shown.bs.modal", function() {
      dialog.find(".btn-primary:first").focus();
    });

    /**
     * Bootbox event listeners; experimental and may not last
     * just an attempt to decouple some behaviours from their
     * respective triggers
     */

    dialog.on("escape.close.bb", function(e) {
      if (callbacks.onEscape) {
        processCallback(e, dialog, callbacks.onEscape);
      }
    });

    /**
     * Standard jQuery event listeners; used to handle user
     * interaction with our dialog
     */

    dialog.on("click", ".modal-footer button", function(e) {
      var callbackKey = $(this).data("bb-handler");

      processCallback(e, dialog, callbacks[callbackKey]);

    });

    dialog.on("click", ".bootbox-close-button", function(e) {
      // onEscape might be falsy but that's fine; the fact is
      // if the user has managed to click the close button we
      // have to close the dialog, callback or not
      processCallback(e, dialog, callbacks.onEscape);
    });

    dialog.on("keyup", function(e) {
      if (e.which === 27) {
        dialog.trigger("escape.close.bb");
      }
    });

    // the remainder of this method simply deals with adding our
    // dialogent to the DOM, augmenting it with Bootstrap's modal
    // functionality and then giving the resulting object back
    // to our caller

    appendTo.append(dialog);

    dialog.modal({
      backdrop: options.backdrop,
      keyboard: false,
      show: false
    });

    if (options.show) {
      dialog.modal("show");
    }

    // @TODO should we return the raw element here or should
    // we wrap it in an object on which we can expose some neater
    // methods, e.g. var d = bootbox.alert(); d.hide(); instead
    // of d.modal("hide");

   /*
    function BBDialog(elem) {
      this.elem = elem;
    }

    BBDialog.prototype = {
      hide: function() {
        return this.elem.modal("hide");
      },
      show: function() {
        return this.elem.modal("show");
      }
    };
    */

    return dialog;

  };

  exports.setDefaults = function(values) {
    $.extend(defaults, values);
  };

  exports.hideAll = function() {
    $(".bootbox").modal("hide");
  };

  /**
   * standard locales. Please add more according to ISO 639-1 standard. Multiple language variants are
   * unlikely to be required. If this gets too large it can be split out into separate JS files.
   */
  var locales = {
    br : {
      OK      : "OK",
      CANCEL  : "Cancelar",
      CONFIRM : "Sim"
    },
    da : {
      OK      : "OK",
      CANCEL  : "Annuller",
      CONFIRM : "Accepter"
    },
    de : {
      OK      : "OK",
      CANCEL  : "Abbrechen",
      CONFIRM : "Akzeptieren"
    },
    en : {
      OK      : "OK",
      CANCEL  : "Cancel",
      CONFIRM : "OK"
    },
    es : {
      OK      : "OK",
      CANCEL  : "Cancelar",
      CONFIRM : "Aceptar"
    },
    fi : {
      OK      : "OK",
      CANCEL  : "Peruuta",
      CONFIRM : "OK"
    },
    fr : {
      OK      : "OK",
      CANCEL  : "Annuler",
      CONFIRM : "D'accord"
    },
    it : {
      OK      : "OK",
      CANCEL  : "Annulla",
      CONFIRM : "Conferma"
    },
    nl : {
      OK      : "OK",
      CANCEL  : "Annuleren",
      CONFIRM : "Accepteren"
    },
    pl : {
      OK      : "OK",
      CANCEL  : "Anuluj",
      CONFIRM : "Potwierdź"
    },
    ru : {
      OK      : "OK",
      CANCEL  : "Отмена",
      CONFIRM : "Применить"
    },
    zh_CN : {
      OK      : "OK",
      CANCEL  : "取消",
      CONFIRM : "确认"
    },
    zh_TW : {
      OK      : "OK",
      CANCEL  : "取消",
      CONFIRM : "確認"
    }
  };

  exports.init = function(_$) {
    window.bootbox = init(_$ || $);
  };

  return exports;

}(window.jQuery));
<commit_msg>scrub version from working copy header<commit_after>/**
 * bootbox.js [master branch]
 *
 * http://bootboxjs.com/license.txt
 */
// @see https://github.com/makeusabrew/bootbox/issues/71
window.bootbox = window.bootbox || (function init($, undefined) {
  "use strict";

  // the base DOM structure needed to create a modal
  var templates = {
    dialog:
      "
" +
        "
" +
          "
" +
            "

" +
          "
" +
        "
" +
      "
",
    header:
      "
" +
        "

" +
      "
",
    footer:
      "

",
    closeButton:
      "<button type='button' class='bootbox-close-button close'>×</button>",
    form:
      "<form class='bootbox-form'></form>",
    inputs: {
      text:
        "<input class='bootbox-input form-control' autocomplete=off type=text />"
    }
  };

  // cache a reference to the jQueryfied body element
  var appendTo = $("body");

  var defaults = {
    // default language
    locale: "en",
    // show backdrop or not
    backdrop: true,
    // animate the modal in/out
    animate: true,
    // additional class string applied to the top level dialog
    className: null,
    // whether or not to include a close button
    closeButton: true,
    // show the dialog immediately by default
    show: true
  };

  // our public object; augmented after our private API
  var exports = {};

  /**
   * @private
   */
  function _t(key) {
    var locale = locales[defaults.locale];
    return locale ? locale[key] : locales.en[key];
  }

  function processCallback(e, dialog, callback) {
    e.preventDefault();

    // by default we assume a callback will get rid of the dialog,
    // although it is given the opportunity to override this

    // so, if the callback can be invoked and it *explicitly returns false*
    // then we'll set a flag to keep the dialog active...
    var preserveDialog = $.isFunction(callback) && callback(e) === false;

    // ... otherwise we'll bin it
    if (!preserveDialog) {
      dialog.modal("hide");
    }
  }

  function getKeyLength(obj) {
    // @TODO defer to Object.keys(x).length if available?
    var k, t = 0;
    for (k in obj) {
      t ++;
    }
    return t;
  }

  function each(collection, iterator) {
    var index = 0;
    $.each(collection, function(key, value) {
      iterator(key, value, index++);
    });
  }

  function sanitize(options) {
    var buttons;
    var total;

    if (typeof options !== "object") {
      throw new Error("Please supply an object of options");
    }

    if (!options.message) {
      throw new Error("Please specify a message");
    }

    // make sure any supplied options take precedence over defaults
    options = $.extend({}, defaults, options);

    if (!options.buttons) {
      options.buttons = {};
    }

    // we only support Bootstrap's "static" and false backdrop args
    // supporting true would mean you could dismiss the dialog without
    // explicitly interacting with it
    options.backdrop = options.backdrop ? "static" : false;

    buttons = options.buttons;

    total = getKeyLength(buttons);

    each(buttons, function(key, button, index) {

      if ($.isFunction(button)) {
        // short form, assume value is our callback. Since button
        // isn't an object it isn't a reference either so re-assign it
        button = buttons[key] = {
          callback: button
        };
      }

      // before any further checks make sure by now button is the correct type
      if ($.type(button) !== "object") {
        throw new Error("button with key " + key + " must be an object");
      }

      if (!button.label) {
        // the lack of an explicit label means we'll assume the key is good enough
        button.label = key;
      }

      if (!button.className) {
        if (total <= 2 && index === total-1) {
          // always add a primary to the main option in a two-button dialog
          button.className = "btn-primary";
        } else {
          button.className = "btn-default";
        }
      }
    });

    return options;
  }

  function mapArguments(args, properties) {
    var argn = args.length;
    var options = {};

    if (argn < 1 || argn > 2) {
      throw new Error("Invalid argument length");
    }

    if (argn === 2 || typeof args[0] === "string") {
      options[properties[0]] = args[0];
      options[properties[1]] = args[1];
    } else {
      options = args[0];
    }

    return options;
  }

  function mergeArguments(defaults, args, properties) {
    return $.extend(true, {}, defaults, mapArguments(args, properties));
  }

  function mergeButtons(labels, args, properties) {
    return validateButtons(
      mergeArguments(createButtons.apply(null, labels), args, properties),
      labels
    );
  }

  function createLabels() {
    var buttons = {};

    for (var i = 0, j = arguments.length; i < j; i++) {
      var argument = arguments[i];
      var key = argument.toLowerCase();
      var value = argument.toUpperCase();

      buttons[key] = {
        label: _t(value)
      };
    }

    return buttons;
  }

  function createButtons() {
    return {
      buttons: createLabels.apply(null, arguments)
    };
  }

  function validateButtons(options, buttons) {
    var allowedButtons = {};
    each(buttons, function(key, value) {
      allowedButtons[value] = true;
    });

    each(options.buttons, function(key) {
      if (allowedButtons[key] === undefined) {
        throw new Error("button key " + key + " is not allowed (options are " + buttons.join("\n") + ")");
      }
    });

    return options;
  }

  exports.alert = function() {
    var options;

    options = mergeButtons(["ok"], arguments, ["message", "callback"]);

    if (options.callback && !$.isFunction(options.callback)) {
      throw new Error("alert requires callback property to be a function when provided");
    }

    /**
     * overrides
     */
    options.buttons.ok.callback = options.onEscape = function() {
      if ($.isFunction(options.callback)) {
        return options.callback();
      }
      return true;
    };

    return exports.dialog(options);
  };

  exports.confirm = function() {
    var options;

    options = mergeButtons(["cancel", "confirm"], arguments, ["message", "callback"]);

    /**
     * overrides; undo anything the user tried to set they shouldn't have
     */
    options.buttons.cancel.callback = options.onEscape = function() {
      return options.callback(false);
    };

    options.buttons.confirm.callback = function() {
      return options.callback(true);
    };

    // confirm specific validation
    if (!$.isFunction(options.callback)) {
      throw new Error("confirm requires a callback");
    }

    return exports.dialog(options);
  };

  exports.prompt = function() {
    var options;
    var defaults;
    var dialog;
    var form;
    var input;
    var shouldShow;

    // we have to create our form first otherwise
    // its value is undefined when gearing up our options
    // @TODO this could be solved by allowing message to
    // be a function instead...
    form = $(templates.form);

    defaults = {
      buttons: createLabels("cancel", "confirm"),
      value: ""
    };

    options = validateButtons(
      mergeArguments(defaults, arguments, ["title", "callback"]),
      ["cancel", "confirm"]
    );

    // capture the user's show value; we always set this to false before
    // spawning the dialog to give us a chance to attach some handlers to
    // it, but we need to make sure we respect a preference not to show it
    shouldShow = (options.show === undefined) ? true : options.show;

    /**
     * overrides; undo anything the user tried to set they shouldn't have
     */
    options.message = form;

    options.buttons.cancel.callback = options.onEscape = function() {
      return options.callback(null);
    };

    options.buttons.confirm.callback = function() {
      return options.callback(input.val());
    };

    options.show = false;

    // prompt specific validation
    if (!options.title) {
      throw new Error("prompt requires a title");
    }

    if (!$.isFunction(options.callback)) {
      throw new Error("prompt requires a callback");
    }

    // create the input
    input = $(templates.inputs.text);
    input.val(options.value);

    // now place it in our form
    form.append(input);

    form.on("submit", function(e) {
      e.preventDefault();
      // @TODO can we actually click *the* button object instead?
      // e.g. buttons.confirm.click() or similar
      dialog.find(".btn-primary").click();
    });

    dialog = exports.dialog(options);

    // clear the existing handler focusing the submit button...
    dialog.off("shown.bs.modal");

    // ...and replace it with one focusing our input, if possible
    dialog.on("shown.bs.modal", function() {
      input.focus();
    });

    if (shouldShow === true) {
      dialog.modal("show");
    }

    return dialog;
  };

  exports.dialog = function(options) {
    options = sanitize(options);

    var dialog = $(templates.dialog);
    var body = dialog.find(".modal-body");
    var buttons = options.buttons;
    var buttonStr = "";
    var callbacks = {
      onEscape: options.onEscape
    };

    each(buttons, function(key, button) {

      // @TODO I don't like this string appending to itself; bit dirty. Needs reworking
      // can we just build up button elements instead? slower but neater. Then button
      // can just become a template too
      buttonStr += "<button data-bb-handler='" + key + "' type='button' class='btn " + button.className + "'>" + button.label + "</button>";
      callbacks[key] = button.callback;
    });

    body.find(".bootbox-body").html(options.message);

    if (options.animate === true) {
      dialog.addClass("fade");
    }

    if (options.className) {
      dialog.addClass(options.className);
    }

    if (options.title) {
      body.before(templates.header);
    }

    if (options.closeButton) {
      var closeButton = $(templates.closeButton);

      if (options.title) {
        dialog.find(".modal-header").prepend(closeButton);
      } else {
        closeButton.css("margin-top", "-10px").prependTo(body);
      }
    }

    if (options.title) {
      dialog.find(".modal-title").html(options.title);
    }

    if (buttonStr.length) {
      body.after(templates.footer);
      dialog.find(".modal-footer").html(buttonStr);
    }

    /**
     * Bootstrap event listeners; used handle extra
     * setup & teardown required after the underlying
     * modal has performed certain actions
     */

    dialog.on("hidden.bs.modal", function(e) {
      // ensure we don't accidentally intercept hidden events triggered
      // by children of the current dialog. We shouldn't anymore now BS
      // namespaces its events; but still worth doing
      if (e.target === this) {
        dialog.remove();
      }
    });

    /*
    dialog.on("show.bs.modal", function() {
      // sadly this doesn't work; show is called *just* before
      // the backdrop is added so we'd need a setTimeout hack or
      // otherwise... leaving in as would be nice
      if (options.backdrop) {
        dialog.next(".modal-backdrop").addClass("bootbox-backdrop");
      }
    });
    */

    dialog.on("shown.bs.modal", function() {
      dialog.find(".btn-primary:first").focus();
    });

    /**
     * Bootbox event listeners; experimental and may not last
     * just an attempt to decouple some behaviours from their
     * respective triggers
     */

    dialog.on("escape.close.bb", function(e) {
      if (callbacks.onEscape) {
        processCallback(e, dialog, callbacks.onEscape);
      }
    });

    /**
     * Standard jQuery event listeners; used to handle user
     * interaction with our dialog
     */

    dialog.on("click", ".modal-footer button", function(e) {
      var callbackKey = $(this).data("bb-handler");

      processCallback(e, dialog, callbacks[callbackKey]);

    });

    dialog.on("click", ".bootbox-close-button", function(e) {
      // onEscape might be falsy but that's fine; the fact is
      // if the user has managed to click the close button we
      // have to close the dialog, callback or not
      processCallback(e, dialog, callbacks.onEscape);
    });

    dialog.on("keyup", function(e) {
      if (e.which === 27) {
        dialog.trigger("escape.close.bb");
      }
    });

    // the remainder of this method simply deals with adding our
    // dialogent to the DOM, augmenting it with Bootstrap's modal
    // functionality and then giving the resulting object back
    // to our caller

    appendTo.append(dialog);

    dialog.modal({
      backdrop: options.backdrop,
      keyboard: false,
      show: false
    });

    if (options.show) {
      dialog.modal("show");
    }

    // @TODO should we return the raw element here or should
    // we wrap it in an object on which we can expose some neater
    // methods, e.g. var d = bootbox.alert(); d.hide(); instead
    // of d.modal("hide");

   /*
    function BBDialog(elem) {
      this.elem = elem;
    }

    BBDialog.prototype = {
      hide: function() {
        return this.elem.modal("hide");
      },
      show: function() {
        return this.elem.modal("show");
      }
    };
    */

    return dialog;

  };

  exports.setDefaults = function(values) {
    $.extend(defaults, values);
  };

  exports.hideAll = function() {
    $(".bootbox").modal("hide");
  };

  /**
   * standard locales. Please add more according to ISO 639-1 standard. Multiple language variants are
   * unlikely to be required. If this gets too large it can be split out into separate JS files.
   */
  var locales = {
    br : {
      OK      : "OK",
      CANCEL  : "Cancelar",
      CONFIRM : "Sim"
    },
    da : {
      OK      : "OK",
      CANCEL  : "Annuller",
      CONFIRM : "Accepter"
    },
    de : {
      OK      : "OK",
      CANCEL  : "Abbrechen",
      CONFIRM : "Akzeptieren"
    },
    en : {
      OK      : "OK",
      CANCEL  : "Cancel",
      CONFIRM : "OK"
    },
    es : {
      OK      : "OK",
      CANCEL  : "Cancelar",
      CONFIRM : "Aceptar"
    },
    fi : {
      OK      : "OK",
      CANCEL  : "Peruuta",
      CONFIRM : "OK"
    },
    fr : {
      OK      : "OK",
      CANCEL  : "Annuler",
      CONFIRM : "D'accord"
    },
    it : {
      OK      : "OK",
      CANCEL  : "Annulla",
      CONFIRM : "Conferma"
    },
    nl : {
      OK      : "OK",
      CANCEL  : "Annuleren",
      CONFIRM : "Accepteren"
    },
    pl : {
      OK      : "OK",
      CANCEL  : "Anuluj",
      CONFIRM : "Potwierdź"
    },
    ru : {
      OK      : "OK",
      CANCEL  : "Отмена",
      CONFIRM : "Применить"
    },
    zh_CN : {
      OK      : "OK",
      CANCEL  : "取消",
      CONFIRM : "确认"
    },
    zh_TW : {
      OK      : "OK",
      CANCEL  : "取消",
      CONFIRM : "確認"
    }
  };

  exports.init = function(_$) {
    window.bootbox = init(_$ || $);
  };

  return exports;

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