<commit_before>import and from './and';
import bitAnd from './bitAnd';
import bitOr from './bitOr';
import by from './by';
import compose from './compose';
import converge from './converge';
import curry from './curry';
import curryRight from './curryRight';
import dec from './dec';
import equal from './equal';
import every from './every';
import explode from './explode';
import filter from './filter';
import flip from './flip';
import forEach from './forEach';
import head from './head';
import ifElse from './ifElse';
import implode from './implode';
import inc from './inc';
import isFalsy from './isFalsy';
import isTruthy from './isTruthy';
import join from './join';
import length from './length';
import looseEqual from './looseEqual';
import map from './map';
import match from './match';
import max from './max';
import method from './method';
import min from './min';
import minus from './minus';
import nand from './nand';
import noop from './noop';
import nor from './nor';
import not from './not';
import nth from './nth';
import or from './or';
import pipe from './pipe';
import plus from './plus';
import property from './property';
import reduce from './reduce';
import reduceFrom from './reduceFrom';
import reduceFromRight from './reduceFromRight';
import reduceRight from './reduceRight';
import replace from './replace';
import shave from './shave';
import some from './some';
import split from './split';
import tail from './tail';
import take from './take';
import takeUntil from './takeUntil';
import takeWhile from './takeWhile';
import times from './times';
import uncurry from './uncurry';
import uncurry3 from './uncurry3';
import xor from './xor';

export {
  and,
  bitAnd,
  bitOr,
  by,
  compose,
  converge,
  curry,
  curryRight,
  dec,
  equal,
  every,
  explode,
  filter,
  flip,
  forEach,
  head,
  ifElse,
  implode,
  inc,
  isFalsy,
  isTruthy,
  join,
  length,
  looseEqual,
  map,
  match,
  max,
  method,
  min,
  minus,
  nand,
  noop,
  nor,
  not,
  nth,
  or,
  pipe,
  plus,
  property,
  reduce,
  reduceFrom,
  reduceFromRight,
  reduceRight,
  replace,
  shave,
  some,
  split,
  tail,
  take,
  takeUntil,
  takeWhile,
  times,
  uncurry,
  uncurry3,
  xor
};<commit_msg>Update the index<commit_after>import and from './and';
import bitAnd from './bitAnd';
import bitOr from './bitOr';
import by from './by';
import compose from './compose';
import converge from './converge';
import curry from './curry';
import curryRight from './curryRight';
import dec from './dec';
import equal from './equal';
import every from './every';
import explode from './explode';
import filter from './filter';
import flip from './flip';
import forEach from './forEach';
import head from './head';
import ifElse from './ifElse';
import implode from './implode';
import inc from './inc';
import isFalsy from './isFalsy';
import isTruthy from './isTruthy';
import join from './join';
import length from './length';
import looseEqual from './looseEqual';
import map from './map';
import match from './match';
import max from './max';
import method from './method';
import min from './min';
import minus from './minus';
import nand from './nand';
import noop from './noop';
import nor from './nor';
import not from './not';
import nth from './nth';
import or from './or';
import pick from './pick';
import pipe from './pipe';
import plus from './plus';
import property from './property';
import reduce from './reduce';
import reduceFrom from './reduceFrom';
import reduceFromRight from './reduceFromRight';
import reduceRight from './reduceRight';
import replace from './replace';
import shave from './shave';
import some from './some';
import split from './split';
import tail from './tail';
import take from './take';
import takeUntil from './takeUntil';
import takeWhile from './takeWhile';
import times from './times';
import uncurry from './uncurry';
import uncurry3 from './uncurry3';
import xor from './xor';

export {
  and,
  bitAnd,
  bitOr,
  by,
  compose,
  converge,
  curry,
  curryRight,
  dec,
  equal,
  every,
  explode,
  filter,
  flip,
  forEach,
  head,
  ifElse,
  implode,
  inc,
  isFalsy,
  isTruthy,
  join,
  length,
  looseEqual,
  map,
  match,
  max,
  method,
  min,
  minus,
  nand,
  noop,
  nor,
  not,
  nth,
  or,
  pick,
  pipe,
  plus,
  property,
  reduce,
  reduceFrom,
  reduceFromRight,
  reduceRight,
  replace,
  shave,
  some,
  split,
  tail,
  take,
  takeUntil,
  takeWhile,
  times,
  uncurry,
  uncurry3,
  xor
};<|endoftext|>


<commit_before>'use strict';

const SUPPORTED_MESSAGE_PLATFORM_TYPE = ["line","facebook"];
const REQUIRED_OPTIONS = {
    line: ["line_channel_id", "line_channel_secret", "line_channel_access_token", "apiai_client_access_token", "default_skill"],
    facebook: ["facebook_page_access_token"]
}
//const DEFAULT_MESSAGE_PLATFORM_TYPE = "line";
const DEFAULT_MEMORY_RETENTION = 60000;
const DEFAULT_SKILL_PATH = "../../../../skill/";
const DEFAULT_INTENT = "input.unknown";

module.exports = (options) => {
    let express = require("express");
    let router = express.Router();
    let body_parser = require("body-parser");
    let Webhook = require("./webhook");

    router.use(body_parser.json({
        verify: (req, res, buf, encoding) => {
            req.raw_body = buf;
        }
    }));

    this.options = options;

    // Set optional options.
    this.options.memory_retention = this.options.memory_retention || DEFAULT_MEMORY_RETENTION;
    this.options.default_intent = this.options.default_intent || DEFAULT_INTENT;
    if (!!this.options.skill_path){
        this.options.skill_path = "../../../../" + this.options.skill_path;
    } else if (process.env.BOT_EXPRESS_ENV == "development"){
        // This is for Bot Express development environment only.
        this.options.skill_path = "../../sample_skill/";
    } else {
        this.options.skill_path = DEFAULT_SKILL_PATH;
    }
    if (this.options.enable_ask_retry === null){
        this.options.enable_ask_retry = false;
    }
    this.options.message_to_ask_retry = this.options.message_to_ask_retry || "ごめんなさい、もうちょっと正確にお願いできますか？";

    // Check if Message Platform Type is provided
    if (!this.options.message_platform_type){
        throw(`Required option: "message_platform_type" not set`);
    }

    // Check if Message Platform Type is supported
    if (SUPPORTED_MESSAGE_PLATFORM_TYPE.indexOf(this.options.message_platform_type) === -1){
        throw(`Specified message_platform_type: "${this.options.message_platform_type}" is not supported.`);
    }

    // Check if required options are set.
    for (let req_opt of REQUIRED_OPTIONS[this.options.message_platform_type]){
        if (typeof this.options[req_opt] == "undefined"){
            throw(`Required option: "${req_opt}" not set`);
        }
    }
    console.log("Required options all set.");

    // Webhook Process
    router.post('/', (req, res, next) => {
        res.status(200).end();

        let webhook = new Webhook(this.options);
        webhook.run(req).then(
            (response) => {
                console.log("Successful End of Webhook.");
                console.log(response);
            },
            (response) => {
                console.log("Abnormal End of Webhook.");
                console.log(response);
            }
        );
    });

    // Verify Facebook Webhook
    if (this.options.message_platform_type == "facebook"){
        router.get("/", (req, res, next) => {
            if (req.query['hub.mode'] === 'subscribe' && req.query['hub.verify_token'] === this.options.facebook_page_access_token) {
                console.log("Validating webhook");
                res.status(200).send(req.query['hub.challenge']);
            } else {
                console.error("Failed validation. Make sure the validation tokens match.");
                res.sendStatus(403);
            }
        });
    }
    return router;
}
<commit_msg>added facebook support<commit_after>'use strict';

const SUPPORTED_MESSAGE_PLATFORM_TYPE = ["line","facebook"];
const REQUIRED_OPTIONS = {
    line: ["line_channel_id", "line_channel_secret", "line_channel_access_token", "apiai_client_access_token", "default_skill"],
    facebook: ["facebook_page_access_token"]
}
//const DEFAULT_MESSAGE_PLATFORM_TYPE = "line";
const DEFAULT_MEMORY_RETENTION = 60000;
const DEFAULT_SKILL_PATH = "../../../../skill/";
const DEFAULT_INTENT = "input.unknown";

let express = require("express");
let router = express.Router();
let body_parser = require("body-parser");
let Webhook = require("./webhook");

router.use(body_parser.json({
    verify: (req, res, buf, encoding) => {
        req.raw_body = buf;
    }
}));

module.exports = (options) => {

    this.options = JSON.parse(JSON.stringify(options));

    // Set optional options.
    this.options.memory_retention = this.options.memory_retention || DEFAULT_MEMORY_RETENTION;
    this.options.default_intent = this.options.default_intent || DEFAULT_INTENT;
    if (!!this.options.skill_path){
        this.options.skill_path = "../../../../" + this.options.skill_path;
    } else if (process.env.BOT_EXPRESS_ENV == "development"){
        // This is for Bot Express development environment only.
        this.options.skill_path = "../../sample_skill/";
    } else {
        this.options.skill_path = DEFAULT_SKILL_PATH;
    }
    if (this.options.enable_ask_retry === null){
        this.options.enable_ask_retry = false;
    }
    this.options.message_to_ask_retry = this.options.message_to_ask_retry || "ごめんなさい、もうちょっと正確にお願いできますか？";

    // Check if Message Platform Type is provided
    if (!this.options.message_platform_type){
        throw(`Required option: "message_platform_type" not set`);
    }

    // Check if Message Platform Type is supported
    if (SUPPORTED_MESSAGE_PLATFORM_TYPE.indexOf(this.options.message_platform_type) === -1){
        throw(`Specified message_platform_type: "${this.options.message_platform_type}" is not supported.`);
    }

    // Check if required options are set.
    for (let req_opt of REQUIRED_OPTIONS[this.options.message_platform_type]){
        if (typeof this.options[req_opt] == "undefined"){
            throw(`Required option: "${req_opt}" not set`);
        }
    }
    console.log("Required options all set.");

    // Webhook Process
    router.post('/', (req, res, next) => {
        res.status(200).end();

        let webhook = new Webhook(this.options);
        webhook.run(req).then(
            (response) => {
                console.log("Successful End of Webhook.");
                console.log(response);
            },
            (response) => {
                console.log("Abnormal End of Webhook.");
                console.log(response);
            }
        );
    });

    // Verify Facebook Webhook
    if (this.options.message_platform_type == "facebook"){
        router.get("/", (req, res, next) => {
            if (req.query['hub.mode'] === 'subscribe' && req.query['hub.verify_token'] === this.options.facebook_page_access_token) {
                console.log("Validating webhook");
                res.status(200).send(req.query['hub.challenge']);
            } else {
                console.error("Failed validation. Make sure the validation tokens match.");
                res.sendStatus(403);
            }
        });
    }
    return router;
}
<|endoftext|>


<commit_before>//Tell jshint (atom package) to stop showing certain irrelevent warnings.
/*jshint esversion: 6 */

// Constants

/**Critical number of harvesters. We need to have at least this many
 * harvesters before building bigger creeps
 */
let CRITICAL_HARVESTER_COUNT = 3;

/**Minimum number of harvesters.*/
let HARVESTER_MINIMUM = 6;

/**Minimum number of upgraders.*/
let UPGRADER_MINIMUM = 2;

/**Minimum number of builders.*/
let BUILDER_MINIMUM = 2;

/**Minimum number of builders.*/
let REPAIRER_MINIMUM = 2;

/**Only targets with less that this much health will be repaired by towers.*/
let TOWER_REPAIR_MAX_HEALTH = 200000;

/**Minimum energy for towers to save for attacking hostile targets.*/
let TOWER_MINIMUM_ENERGY = 800;

/**filter for helping a tower find a target to repair.*/
let TOWER_REPAIR_TARGET = {
  filter: (structure) => structure.hits < structure.hitsMax &&
    //structure.structureType != STRUCTURE_WALL &&
    structure.hits < TOWER_REPAIR_MAX_HEALTH
};

/**Base Worker Body. 2W, 1C, 1M.*/
let BASE_WORKER_BODY = [WORK, WORK, WORK, CARRY, MOVE, MOVE];

// Require other modules and prototypes.

/**Harvester role.*/
let roleHarvester = require('role.harvester');

/**Upgrader role.*/
let roleUpgrader = require('role.upgrader');

/**Builder role.*/
let roleBuilder = require('role.builder');

/**Repairer role.*/
let roleRepairer = require('role.repairer');

/**Prototype for spawn objects.*/
require('prototype.spawn')();

/**Functions that are used across different modules.*/
let functions = require('functions');

//Begin main loop.
module.exports.loop = function() {

  //Cleanup memory.
  functions.clearDeadCreepMemory();

  let spawn = Game.spawns.Spawn1;

  //Count the number of each creep role.
  let upgraders = _.filter(Game.creeps, (creep) =>
    creep.memory.role == 'upgrader');
  console.log('Upgraders: ' + upgraders.length);

  let builders = _.filter(Game.creeps, (creep) =>
    creep.memory.role == 'builder');
  console.log('Builders: ' + builders.length);

  let harvesters = _.filter(Game.creeps, (creep) =>
    creep.memory.role == 'harvester');
  console.log('Harvesters: ' + harvesters.length);

  let repairers = _.filter(Game.creeps, (creep) =>
    creep.memory.role == 'repairer');
  console.log('Repairers: ' + repairers.length);

  let name;
  let energyCapacity = spawn.room.energyCapacityAvailable;

  //Autobuild creeps:
  //Prioritize maintaining the minimum number of each creep in order.
  if (harvesters.length < HARVESTER_MINIMUM) {
    name = spawn.createBiggestWorkerCreep(energyCapacity, 'harvester');
    if (name == ERR_NOT_ENOUGH_ENERGY &&
      harvesters.length < CRITICAL_HARVESTER_COUNT) {
      //If very few harvesters are alive, build one
      //using whatever energy is available.
      spawn.createBiggestWorkerCreep(spawn.room.energyAvailable, 'harvester');
    }
  } else if (repairers.length < REPAIRER_MINIMUM) {
    name = spawn.createBiggestWorkerCreep(energyCapacity, 'repairer');
  } else if (builders.length < BUILDER_MINIMUM) {
    name = spawn.createBiggestWorkerCreep(energyCapacity, 'builder');
  } else if (upgraders.length < UPGRADER_MINIMUM) {
    name = spawn.createBiggestWorkerCreep(energyCapacity, 'upgrader');
  } else {
    //Excess creeps will be of this role.
    name = spawn.createBiggestWorkerCreep(energyCapacity, 'builder');
  }

  //For each creep, have it act (run) according to its role.
  for (let name in Game.creeps) {
    let creep = Game.creeps[name];
    if (creep.memory.role == 'harvester') {
      roleHarvester.run(creep);
    } else if (creep.memory.role == 'upgrader') {
      roleUpgrader.run(creep);
    } else if (creep.memory.role == 'builder') {
      roleBuilder.run(creep);
    } else if (creep.memory.role == 'repairer') {
      roleRepairer.run(creep);
    }
  }

  //Tower logic

  //get towers.
  let towers = _.filter(Game.structures, (structure) =>
    structure.structureType == STRUCTURE_TOWER);
  for (let tower of towers) {

    //find closest damaged structure.
    let closestDamagedStructure = tower.pos
      .findClosestByRange(FIND_STRUCTURES, TOWER_REPAIR_TARGET);

    //find closest hostile creep.
    let closestHostile = tower.pos.findClosestByRange(FIND_HOSTILE_CREEPS);

    //prioritize shooting enemy creeps over repairing structures.
    if (closestHostile) {
      tower.attack(closestHostile);
    } else if (closestDamagedStructure &&
      tower.energy > TOWER_MINIMUM_ENERGY) {
      //Only repair if enough energy is saved up (in case of attacks).
      tower.repair(closestDamagedStructure);
    }
  }
}; //End main loop.
<commit_msg>Adjusted tower logic to only repair if there are a minimum number of workers, so that it does not slowly drain the base's energy.<commit_after>//Tell jshint (atom package) to stop showing certain irrelevent warnings.
/*jshint esversion: 6 */

// Constants

/**Critical number of harvesters. We need to have at least this many
 * harvesters before building bigger creeps
 */
let CRITICAL_HARVESTER_COUNT = 3;

/**Minimum number of harvesters.*/
let HARVESTER_MINIMUM = 10;

/**Minimum number of upgraders.*/
let UPGRADER_MINIMUM = 2;

/**Minimum number of builders.*/
let BUILDER_MINIMUM = 2;

/**Minimum number of builders.*/
let REPAIRER_MINIMUM = 2;

/**Minimum number of creeps.*/
let WORKERS_MINIMUM = HARVESTER_MINIMUM +
  UPGRADER_MINIMUM +
  BUILDER_MINIMUM +
  REPAIRER_MINIMUM;

/**Only targets with less that this much health will be repaired by towers.*/
let TOWER_REPAIR_MAX_HEALTH = 200000;

/**Minimum energy for towers to save for attacking hostile targets.*/
let TOWER_MINIMUM_ENERGY = 800;

/**filter for helping a tower find a target to repair.*/
let TOWER_REPAIR_TARGET = {
  filter: (structure) => structure.hits < structure.hitsMax &&
    //structure.structureType != STRUCTURE_WALL &&
    structure.hits < TOWER_REPAIR_MAX_HEALTH
};

/**Base Worker Body. 2W, 1C, 1M.*/
let BASE_WORKER_BODY = [WORK, WORK, WORK, CARRY, MOVE, MOVE];

// Require other modules and prototypes.

/**Harvester role.*/
let roleHarvester = require('role.harvester');

/**Upgrader role.*/
let roleUpgrader = require('role.upgrader');

/**Builder role.*/
let roleBuilder = require('role.builder');

/**Repairer role.*/
let roleRepairer = require('role.repairer');

/**Prototype for spawn objects.*/
require('prototype.spawn')();

/**Functions that are used across different modules.*/
let functions = require('functions');

//Begin main loop.
module.exports.loop = function() {

  //Cleanup memory.
  functions.clearDeadCreepMemory();

  let spawn = Game.spawns.Spawn1;

  //Count the number of each creep role.
  let upgraders = _.filter(Game.creeps, (creep) =>
    creep.memory.role == 'upgrader');
  console.log('Upgraders: ' + upgraders.length);

  let builders = _.filter(Game.creeps, (creep) =>
    creep.memory.role == 'builder');
  console.log('Builders: ' + builders.length);

  let harvesters = _.filter(Game.creeps, (creep) =>
    creep.memory.role == 'harvester');
  console.log('Harvesters: ' + harvesters.length);

  let repairers = _.filter(Game.creeps, (creep) =>
    creep.memory.role == 'repairer');
  console.log('Repairers: ' + repairers.length);

  let name;
  let energyCapacity = spawn.room.energyCapacityAvailable;

  //Autobuild creeps:
  //Prioritize maintaining the minimum number of each creep in order.
  if (harvesters.length < HARVESTER_MINIMUM) {
    name = spawn.createBiggestWorkerCreep(energyCapacity, 'harvester');
    if (name == ERR_NOT_ENOUGH_ENERGY &&
      harvesters.length < CRITICAL_HARVESTER_COUNT) {
      //If very few harvesters are alive, build one
      //using whatever energy is available.
      spawn.createBiggestWorkerCreep(spawn.room.energyAvailable, 'harvester');
    }
  } else if (repairers.length < REPAIRER_MINIMUM) {
    name = spawn.createBiggestWorkerCreep(energyCapacity, 'repairer');
  } else if (builders.length < BUILDER_MINIMUM) {
    name = spawn.createBiggestWorkerCreep(energyCapacity, 'builder');
  } else if (upgraders.length < UPGRADER_MINIMUM) {
    name = spawn.createBiggestWorkerCreep(energyCapacity, 'upgrader');
  } else {
    //Excess creeps will be of this role.
    name = spawn.createBiggestWorkerCreep(energyCapacity, 'builder');
  }

  //For each creep, have it act (run) according to its role.
  for (let name in Game.creeps) {
    let creep = Game.creeps[name];
    if (creep.memory.role == 'harvester') {
      roleHarvester.run(creep);
    } else if (creep.memory.role == 'upgrader') {
      roleUpgrader.run(creep);
    } else if (creep.memory.role == 'builder') {
      roleBuilder.run(creep);
    } else if (creep.memory.role == 'repairer') {
      roleRepairer.run(creep);
    }
  }

  //Tower logic

  //get towers.
  let towers = _.filter(Game.structures, (structure) =>
    structure.structureType == STRUCTURE_TOWER);
  for (let tower of towers) {

    //find closest damaged structure.
    let closestDamagedStructure = tower.pos
      .findClosestByRange(FIND_STRUCTURES, TOWER_REPAIR_TARGET);

    //find closest hostile creep.
    let closestHostile = tower.pos.findClosestByRange(FIND_HOSTILE_CREEPS);

    //prioritize shooting enemy creeps over repairing structures.
    if (closestHostile) {
      tower.attack(closestHostile);
    } else if (closestDamagedStructure &&
      tower.energy > TOWER_MINIMUM_ENERGY &&
      Game.creeps.length >= WORKERS_MINIMUM) {
      //Only repair if enough energy is saved up (in case of attacks)
      //and enough workers are supplying the base.
      tower.repair(closestDamagedStructure);
    }
  }
}; //End main loop.
<|endoftext|>


<commit_before>/*
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

var BASE = 'extensions.auto-confirm@myokoym.net.';
var prefs = require('lib/prefs').prefs;
{
  if (prefs.getDefaultPref(BASE + 'debug') === null)
    prefs.setDefaultPref(BASE + 'debug', false);
}

function log(message) {
  if (prefs.getPref(BASE + 'debug')) {
    console.log("auto-confirm: " + message);
  }
}

var generalUrls = [];
var generalConfigs = [];
var configs = prefs.getChildren(BASE + 'general');
for (let config of configs) {
  log(config);
  let url = prefs.getPref(config + '.url');
  generalUrls.push(url);
  generalConfigs.push({
    text:    prefs.getPref(config + '.text'),
    title:   prefs.getPref(config + '.title'),
    action:  prefs.getPref(config + '.action'),
    actions: prefs.getPref(config + '.actions')
  });
}

load('lib/WindowManager');

var global = this;
function handleWindow(aWindow)
{
  log("handleWindow");
  var doc = aWindow.document;
  if (doc.documentElement.localName === 'dialog' &&
      doc.documentElement.id === 'commonDialog') {
    log("commonDialog");
    handleCommonDialog(aWindow);
    return;
  } else {
    log("generalWindow");
    aWindow.addEventListener('load', function onload() {
      aWindow.removeEventListener('load', onload);
      handleGeneralWindow(aWindow);
    });
    return;
  }

}

function handleCommonDialog(aWindow)
{
  var doc = aWindow.document;
  var args = aWindow.args;
  log("args: " + JSON.stringify(args));
  var configs = prefs.getChildren(BASE + 'common');
  log("commonDialog: " + configs);
  var matched = false;
  for (let config of configs) {
    let typeMatcher = prefs.getPref(config + '.type');
    if (typeMatcher !== args.promptType)
      continue;
    let textMatcher = prefs.getPref(config + '.text');
    if (textMatcher && !args.text.match(textMatcher))
      continue;
    let titleMatcher = prefs.getPref(config + '.title');
    if (titleMatcher && !args.title.match(titleMatcher))
      continue;

    matched = true;
    log("config: " + config);
    let action = prefs.getPref(config + '.action');
    if (action)
      processAction(aWindow, action);
    let actions = prefs.getPref(config + '.actions');
    if (actions)
      processActions(aWindow, actions);
  }
  if (!matched)
    log("no match");
}

function handleGeneralWindow(aWindow)
{
  var doc = aWindow.document;
  var url = aWindow.location.href;
  log("url: " + url);
  var fromIndex = 0;
  while (true) {
    log("fromIndex: " + fromIndex);
    let index = generalUrls.indexOf(url, fromIndex);
    log("index: " + index);
    if (index === -1)
      return;
    let config = generalConfigs[index];
    log("config: " + config);
    if (matchedWindow(aWindow, config)) {
      aWindow.setTimeout(function() {
        let action = config.action;
        if (action)
          processAction(aWindow, action);
        let actions = config.actions;
        if (actions)
          processActions(aWindow, actions);
      }, 0);
      return;
    }
    fromIndex = index + 1;
  }
}

function processActions(aWindow, aActions)
{
  var doc = aWindow.document;
  log("actions: " + aActions);
  for (let action of JSON.parse(aActions)) {
    log("action: " + action);
    processAction(aWindow, action);
  }
}

function processAction(aWindow, aAction)
{
  var doc = aWindow.document;
  log("action: " + aAction);
  var actions = aAction.match(/^([^;]+);?(.*)/);
  if (actions === null)
    return;
  var action = actions[1];
  var value = actions[2];
  switch (action) {
  case 'accept':
    doc.documentElement.acceptDialog();
    log("accept");
    return;
  case 'cancel':
    doc.documentElement.cancelDialog();
    log("cancel");
    return;
  case 'click':
    log("click");
    {
      let element = findVisibleElementByLabel(aWindow, value);
      log(element);
      if (typeof element.click === "function") {
        log("element.click(): ready");
        element.click();
        log("element.click(): done");
      } else {
        log("element is not clickable");
        Cu.reportError(new Error("found element is not clickable."));
      }
    }
    return;
  case 'push':
    var buttons = doc.documentElement._buttons;
    for (let dlgtype in buttons) {
      var button = buttons[dlgtype];
      log("label: " + button.label);
      if (button.label.match(value)) {
        button.click();
        log("push");
        return;
      }
    }
    log("push: no match");
    return;
  case 'input':
    doc.getElementById("loginTextbox").value = value;
    log("input");
    return;
  case 'check':
    log("check");
    if (value) {
      let element = findVisibleElementByLabel(aWindow, value);
      log("  element: " + element);
      log("  element.checked: ready");
      element.checked = true;
      log("  element.checked: done");
    } else {
      // For commonDialog
      doc.getElementById("checkbox").checked = true;
      aWindow.args.checked = true;
    }
    return;
  case 'uncheck':
    log("uncheck");
    if (value) {
      let element = findVisibleElementByLabel(aWindow, value);
      log(element);
      log("  element.checked: ready");
      element.checked = false;
      log("  element.checked: done");
    } else {
      // For commonDialog
      doc.getElementById("checkbox").checked = false;
      aWindow.args.checked = false;
    }
    return;
  default:
    log("no action");
    return;
  }
}

function matchedWindow(aWindow, aConfig) {
  log("matchedWindow");
  let textMatcher = aConfig.text;
  log("  textMatcher: " + textMatcher);
  if (textMatcher && !findVisibleElementByLabel(aWindow, textMatcher))
    return false;
  let titleMatcher = aConfig.title;
  log("  titleMatcher: " + titleMatcher);
  let title = aWindow.document.title;
  if (titleMatcher && !title.match(titleMatcher))
    return false;

  log("  match");
  return  true;
}

function findVisibleElementByLabel(aWindow, text) {
  log("findVisibleElementByLabel");
  if (text.indexOf('"') !== -1) {
    text = 'concat("' + text.replace(/"/g, '", \'"\', "') + '")';
  } else {
    text = '"' + text + '"';
  }
  var expression = '/descendant::*[contains(@label, ' + text + ')] | ' +
                   '/descendant::*[local-name()="label" or local-name()="description"][contains(@value, ' + text + ')] | ' +
                   '/descendant::*[contains(text(), ' + text + ')]';
  log("  expression: " + expression);
  try {
  var elements = aWindow.document.evaluate(
                   expression,
                   aWindow.document,
                   null,
                   aWindow.XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
                   null
                 );
  } catch(e) {
    log("  error: " + e);
  }
  log("  elements.length: " + elements.snapshotLength);
  for (var i = 0, maxi = elements.snapshotLength; i < maxi; i++)
  {
    let element = elements.snapshotItem(i);
    if (element.clientHeight > 0 &&
        element.clientWidth > 0) {
      return element;
    }
  }

  log("  no visible element");
  return null;
}

WindowManager.addHandler(handleWindow);

function shutdown()
{
  WindowManager = undefined;
  global = undefined;
}
<commit_msg>Observe preferences change<commit_after>/*
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

var BASE = 'extensions.auto-confirm@myokoym.net.';
var prefs = require('lib/prefs').prefs;
{
  if (prefs.getDefaultPref(BASE + 'debug') === null)
    prefs.setDefaultPref(BASE + 'debug', false);
}

function log(message) {
  if (prefs.getPref(BASE + 'debug')) {
    console.log("auto-confirm: " + message);
  }
}

var generalUrls = [];
var generalConfigs = [];

function updateConfigs() {
  log("updateConfigs");
  generalUrls = [];
  generalConfigs = [];
  var configs = prefs.getChildren(BASE + 'general');
  for (let config of configs) {
    log(config);
    let url = prefs.getPref(config + '.url');
    generalUrls.push(url);
    generalConfigs.push({
      text:    prefs.getPref(config + '.text'),
      title:   prefs.getPref(config + '.title'),
      action:  prefs.getPref(config + '.action'),
      actions: prefs.getPref(config + '.actions')
    });
  }
  log(JSON.stringify(generalUrls));
  log(JSON.stringify(generalConfigs));
}
updateConfigs();

var listener = {
  domains : [
    BASE + 'general'
  ],
  observe : function(aSubject, aTopic, aData)
  {
    log("observe");
    if (aTopic != 'nsPref:changed')
      return;
    updateConfigs();
  }
};
prefs.addPrefListener(listener);

load('lib/WindowManager');

var global = this;
function handleWindow(aWindow)
{
  log("handleWindow");
  var doc = aWindow.document;
  if (doc.documentElement.localName === 'dialog' &&
      doc.documentElement.id === 'commonDialog') {
    log("commonDialog");
    handleCommonDialog(aWindow);
    return;
  } else {
    log("generalWindow");
    aWindow.addEventListener('load', function onload() {
      aWindow.removeEventListener('load', onload);
      handleGeneralWindow(aWindow);
    });
    return;
  }

}

function handleCommonDialog(aWindow)
{
  var doc = aWindow.document;
  var args = aWindow.args;
  log("args: " + JSON.stringify(args));
  var configs = prefs.getChildren(BASE + 'common');
  log("commonDialog: " + configs);
  var matched = false;
  for (let config of configs) {
    let typeMatcher = prefs.getPref(config + '.type');
    if (typeMatcher !== args.promptType)
      continue;
    let textMatcher = prefs.getPref(config + '.text');
    if (textMatcher && !args.text.match(textMatcher))
      continue;
    let titleMatcher = prefs.getPref(config + '.title');
    if (titleMatcher && !args.title.match(titleMatcher))
      continue;

    matched = true;
    log("config: " + config);
    let action = prefs.getPref(config + '.action');
    if (action)
      processAction(aWindow, action);
    let actions = prefs.getPref(config + '.actions');
    if (actions)
      processActions(aWindow, actions);
  }
  if (!matched)
    log("no match");
}

function handleGeneralWindow(aWindow)
{
  var doc = aWindow.document;
  var url = aWindow.location.href;
  log("url: " + url);
  var fromIndex = 0;
  while (true) {
    log("fromIndex: " + fromIndex);
    let index = generalUrls.indexOf(url, fromIndex);
    log("index: " + index);
    if (index === -1)
      return;
    let config = generalConfigs[index];
    log("config: " + config);
    if (matchedWindow(aWindow, config)) {
      aWindow.setTimeout(function() {
        let action = config.action;
        if (action)
          processAction(aWindow, action);
        let actions = config.actions;
        if (actions)
          processActions(aWindow, actions);
      }, 0);
      return;
    }
    fromIndex = index + 1;
  }
}

function processActions(aWindow, aActions)
{
  var doc = aWindow.document;
  log("actions: " + aActions);
  for (let action of JSON.parse(aActions)) {
    log("action: " + action);
    processAction(aWindow, action);
  }
}

function processAction(aWindow, aAction)
{
  var doc = aWindow.document;
  log("action: " + aAction);
  var actions = aAction.match(/^([^;]+);?(.*)/);
  if (actions === null)
    return;
  var action = actions[1];
  var value = actions[2];
  switch (action) {
  case 'accept':
    doc.documentElement.acceptDialog();
    log("accept");
    return;
  case 'cancel':
    doc.documentElement.cancelDialog();
    log("cancel");
    return;
  case 'click':
    log("click");
    {
      let element = findVisibleElementByLabel(aWindow, value);
      log(element);
      if (typeof element.click === "function") {
        log("element.click(): ready");
        element.click();
        log("element.click(): done");
      } else {
        log("element is not clickable");
        Cu.reportError(new Error("found element is not clickable."));
      }
    }
    return;
  case 'push':
    var buttons = doc.documentElement._buttons;
    for (let dlgtype in buttons) {
      var button = buttons[dlgtype];
      log("label: " + button.label);
      if (button.label.match(value)) {
        button.click();
        log("push");
        return;
      }
    }
    log("push: no match");
    return;
  case 'input':
    doc.getElementById("loginTextbox").value = value;
    log("input");
    return;
  case 'check':
    log("check");
    if (value) {
      let element = findVisibleElementByLabel(aWindow, value);
      log("  element: " + element);
      log("  element.checked: ready");
      element.checked = true;
      log("  element.checked: done");
    } else {
      // For commonDialog
      doc.getElementById("checkbox").checked = true;
      aWindow.args.checked = true;
    }
    return;
  case 'uncheck':
    log("uncheck");
    if (value) {
      let element = findVisibleElementByLabel(aWindow, value);
      log(element);
      log("  element.checked: ready");
      element.checked = false;
      log("  element.checked: done");
    } else {
      // For commonDialog
      doc.getElementById("checkbox").checked = false;
      aWindow.args.checked = false;
    }
    return;
  default:
    log("no action");
    return;
  }
}

function matchedWindow(aWindow, aConfig) {
  log("matchedWindow");
  let textMatcher = aConfig.text;
  log("  textMatcher: " + textMatcher);
  if (textMatcher && !findVisibleElementByLabel(aWindow, textMatcher))
    return false;
  let titleMatcher = aConfig.title;
  log("  titleMatcher: " + titleMatcher);
  let title = aWindow.document.title;
  if (titleMatcher && !title.match(titleMatcher))
    return false;

  log("  match");
  return  true;
}

function findVisibleElementByLabel(aWindow, text) {
  log("findVisibleElementByLabel");
  if (text.indexOf('"') !== -1) {
    text = 'concat("' + text.replace(/"/g, '", \'"\', "') + '")';
  } else {
    text = '"' + text + '"';
  }
  var expression = '/descendant::*[contains(@label, ' + text + ')] | ' +
                   '/descendant::*[local-name()="label" or local-name()="description"][contains(@value, ' + text + ')] | ' +
                   '/descendant::*[contains(text(), ' + text + ')]';
  log("  expression: " + expression);
  try {
  var elements = aWindow.document.evaluate(
                   expression,
                   aWindow.document,
                   null,
                   aWindow.XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
                   null
                 );
  } catch(e) {
    log("  error: " + e);
  }
  log("  elements.length: " + elements.snapshotLength);
  for (var i = 0, maxi = elements.snapshotLength; i < maxi; i++)
  {
    let element = elements.snapshotItem(i);
    if (element.clientHeight > 0 &&
        element.clientWidth > 0) {
      return element;
    }
  }

  log("  no visible element");
  return null;
}

WindowManager.addHandler(handleWindow);

function shutdown()
{
  prefs.removePrefListener(listener);
  WindowManager = undefined;
  global = undefined;
}
<|endoftext|>


<commit_before>//! nmea.js
//! parse and format NMEA 0183 sentences.
//! version : 0.1
//! homegrownmarine.com

var winston = require('winston');
var moment = require('moment');
var _ = require('lodash');

//TODO: move to util class
var coordinate = module.exports.coordinate = {
    parse: function(string, cardinalDir) {
        var value = +string;
        var degrees = parseInt(value/100);
        value -= degrees*100;
        degrees += value / 60.0;
        return (cardinalDir == 'S' || cardinalDir == 'W'?-1:1) * degrees;
    },
    format: function(value, latOrLon) {
        var direction = (value >= 0 ? 'E':'W');
        if (latOrLon === 'lat') {
            direction = (value >= 0 ? 'N':'S');
        }

        value = Math.abs(value);
        var degrees = parseInt(value);

        //TODO: pad?
        var string = ''+degrees+((value-degrees)*60).toFixed(6);

        return [string, direction];
    }
};

var parsers = {};
module.exports.parsers = parsers;

/*Parse RMC Message - Recommended Minimum Navigation Information

    Expected format:
                                                               12
                   1 2       3 4        5 6   7   8    9  10 11 |
                   | |       | |        | |   |   |    |   | |  |
    $--RMC,hhmmss.ss,A,llll.ll,a,yyyyy.yy,a,x.x,x.x,xxxx,x.x,a*hh

    1) Time (UTC)
    2) Status, V = Navigation receiver warning
    3) Latitude
    4) N or S
    5) Longitude
    6) E or W
    7) Speed over ground, knots
    8) Track made good, degrees true
    9) Date, ddmmyy
    10) Magnetic Variation, degrees
    11) E or W
    12) Checksum

    Example Messages:
    $GPRMC,204658,A,4740.2949,N,12228.5660,W,005.4,300.8,100410,018.2,E*63
    $GPRMC,225426,A,4741.9098,N,12225,2008,W,005.2,318.7,220412,018.0,E*65
    $GPRMC,152337,V,4741.0220,N,12224.3803,W,,,210412,018.2,E*74
    $GPRMC,030000.6,A,4740.36415,N,12225.35953,W,000.02,059.5,160612,016.6,E*4A   //GPS 18X 5Hz
    $GPRMC,030944.8,A,4738.3356,N,12221.6469,W,0.01,305.20,031014,018.2,E*75      //GPS 19X 10Hz

    -- NMEA 2.3 adds another column
    $GPRMC,214930.000,A,4741.1764,N,12224.1899,W,0.02,0.00,010614,,,D*78
*/
parsers.RMC = {
    parse: function(message,parts) {
        var data = {
            msg: message,
            type: 'rmc',
            lat: coordinate.parse(parts[3],parts[4]),
            latStr: parts[3]+','+parts[4],
            lon: coordinate.parse(parts[5],parts[6]),
            lonStr: parts[5]+','+parts[6],
            time: moment.utc(parts[9]+' '+parts[1], "DDMMYY HHmmssS"),
            variation: +parts[10] * (parts[11] == 'E'?-1:1)
        };

        if (parts[7])
            data.sog = +parts[7];
        if (parts[8])
            data.cog = +parts[8];

        return data;
    }
};

/*RMB Recommended Minimum Navigation Information

    Expected format:
                                                                14
           1   2 3    4    5       6 7        8 9  10  11  12 13 |
           |   | |    |    |       | |        | |   |   |   | |  |
    $--RMB,A,x.x,a,c--c,c--c,llll.ll,a,yyyyy.yy,a,x.x,x.x,x.x,A*hh

    1) Status, V = Navigation receiver warning
    2) Cross Track error - nautical miles
    3) Direction to Steer, Left or Right
    4) TO Waypoint ID
    5) FROM Waypoint ID
    6) Destination Waypoint Latitude
    7) N or S
    8) Destination Waypoint Longitude
    9) E or W
    10) Range to destination in nautical miles
    11) Bearing to destination in degrees True
    12) Destination closing velocity in knots
    13) Arrival Status, A = Arrival Circle Entered
    14) Checksum
*/
parsers.RMB = {
// GPRMB,A,{xte:.2f},{dts},{from_wp},{to_wp},{lat},{latDir},{lon},{lonDir},{dtw:.1f},{btw:.0f},{vmgwp:.2f},V

    format: function(data) {
        var toLat = coordinate.format(data.to.lat, 'lat');
        var toLon = coordinate.format(data.to.lon, 'lon');

        var parts = ["IIRMB",
            "A",
            data.xte.toFixed(2),
            data.dts,
            data.to.name,
            data.from.name,
            toLat[0],
            toLat[1],
            toLon[0],
            toLon[1],
            data.dtw.toFixed(1),
            data.btw.toFixed(0),
            data.vmgw.toFixed(2),
            'V'
        ];

        return parts.join(',');
    }
};

/*Parse HDG message - Heading, Deviation & Variation

    Expected format:

             1   2 3   4 5  6
             |   | |   | |  |
    $--HDG,x.x,x.x,a,x.x,a*hh

    1) Magnetic Sensor heading in degrees
    2) Magnetic Deviation, degrees
    3) Magnetic Deviation direction, E = Easterly, W = Westerly
    4) Magnetic Variation degrees
    5) Magnetic Variation direction, E = Easterly, W = Westerly
    6) Checksum

    Example Messages:
    $IIHDG,298,,,18,E*18
    $HCHDG,192.5,0.0,E,,*26
*/
parsers.HDG = {
    parse: function(message,parts) {
        return {
            msg: message,
            type: 'hdg',
            hdg: +parts[1]  //TODO: convert to true
        };
    }
};

/*
Parse MWV Message - Wind Speed and Angle

    Expected format:

             1 2   3 4  5
             | |   | |  |
    $--MWV,x.x,a,x.x,a*hh

    1) Wind Angle, 0 to 360 degrees
    2) Reference, R = Relative, T = True
    3) Wind Speed
    4) Wind Speed Units, K/M/N
    5) Status, A = Data Valid
    6) Checksum

    Example Messages:
    $CRMWV,35.5,R,,,A*7F
    $IIMWV,27.71,R,14.03,N,A*38
*/
parsers.MWV = {
    parse: function(message, parts) {
        var data = {
            msg: message,
            type: 'mwv',
            ref: parts[2]
        };

        if ( parts[2] == 'R' ) {
            angle = 'awa';
            speed = 'aws';
        }
        else {
            // angle = 'twa';
            // speed = 'tws';

            //not accepting twa from instruments
            return null;
        }

        data[angle] = +parts[1];     //assumes K, TODO: unit conversion
        data[speed] = +parts[3];

        if (data[angle] > 180) {
            data[angle] = -1 * (360 - data[angle]);
        }

        return data;
    },
    format: function() {
        var parts = [
            "IIMWV",
            data.twa.toFixed(1),
            "T",
            data.tws.toFixed(2),
            "N",
            "A"
        ];

        return parts.join(',');
    }
};

/*
Parse VHW Message - Water Speed and Heading

    Expected Format:

             1 2   3 4   5 6   7 8  9
             | |   | |   | |   | |  |
    $--VHW,x.x,T,x.x,M,x.x,N,x.x,K*hh

    1) Degress True
    2) T = True
    3) Degrees Magnetic
    4) M = Magnetic
    5) Knots (speed of vessel relative to the water)
    6) N = Knots
    7) Kilometers (speed of vessel relative to the water)
    8) K = Kilometres
    9) Checksum

    Example Messages:
    $CRVHW,,,,,6.1,N,,*3F
    $IIVHW,,,,,4.87,N,9.03,K*4D
*/
parsers.VHW = {
    parse: function(message,parts) {
        return {
            msg: message,
            type: 'vhw',
            speed: +parts[5]  //TODO: may need to unit convert if [5] isn't populated
        };
    }
};

/*
Parse or format VDR Message - Set and Drift

    Expected Format:
             1 2   3 4   5 6  7
             | |   | |   | |  |
    $--VDR,x.x,T,x.x,M,x.x,N*hh

    1) Degress True
    2) T = True
    3) Degrees Magnetic
    4) M = Magnetic
    5) Knots (speed of current)
    6) N = Knots
    7) Checksum
*/
parsers.VDR = {
    parse: function(message,parts) {
        return {
            msg: message,
            type: 'vdr',
            set: parts[1],
            drift: parts[5]
        };
    },
    format: function(data) {
        var parts = ["IIVDR",
            data.set.toFixed(2),
            "T",
            "",
            "",
            data.drift.toFixed(2),
            "N"
        ];

        return parts.join(',');
    }
};

/*Parse ROT Message - Rate Of Turn

    Expected Format:
             1 2  3
             | |  |
    $--ROT,x.x,A*hh

    1) Rate Of Turn, degrees per minute, "-" means bow turns to port
    2) Status, A means data is valid
    3) Checksum

    Example Messages:
    $TIROT,-20.3,A*27 */
parsers.ROT = {
    parse: function(message, parts) {
        return {
            msg: message,
            type: 'rot',
            rot: +parts[1]
        };
    }
};

// $IIDPT,075.1,-1.0,*43
parsers.DPT = {
    parse: function(message, parts) {
        return {
            msg: message,
            type: 'dpt',
            depth: +parts[1]        //TODO: more
        };
    }
};

// $YXXDR,A,0.4,D,PTCH,A,-4.0,D,ROLL*70
parsers.XDR = {
    parse: function(message, parts) {
        return {
            msg: message,
            type: 'xdr',
            pitch: +parts[2],
            heel: +parts[6]
        };
    }
};

// Calculate nmea checksum.  Assumes $ is not passed in
var checksum = module.exports.checksum = function(message) {
    var csum = 0;
    for ( var i=0; i < message.length; i++ ) {
        csum = csum ^ message.charCodeAt(i);
    }

    csum = csum.toString(16);
    if (csum.length == 1) {
        csum = '0'+csum;
    }
    return csum.toUpperCase();
};

// Validate nmea0183 message, assumes first character is $.
var validate = module.exports.validate = function(message) {
    if ( message.charAt(message.length-3) == '*' ) {
        return checksum(message.substring(1,message.length-3)) == message.substring(message.length-2,message.length);
    }
    return true;
};

module.exports.messageId = function(message) {
    var end = message.indexOf(',');
    return message.substring(1,end);
};

module.exports.parse = function(message) {
    message = message.trim(); //proper messages end in \r\n

    if ( !validate(message) ) {
        return null;
    }

    try {
        message = message.substring(1, message.length-3); //trim xsum and $
        var type = message.substring(2, 5);

        if ( type in parsers && parsers[type].parse ) {
            var parts = message.split(',');
            return parsers[type].parse(message, parts);
        }
    }
    catch(e) {
        winston.error('Error parsing NMEA message :', e);
    }

    return null;
};

//TODO : format string or object
module.exports.format = function(data) {
    var message;
    if ( _.isString(data) ) {
        message = data;
    }
    else if ( data.type && data.type.toUpperCase() in parsers && parsers[data.type.toUpperCase()].format ) {
        message = parsers[data.type.toUpperCase()].format(data);
    }

    if (message)
        return '$' + message + '*' + checksum(message);
    else
        return null;
};
<commit_msg>save hdg in true<commit_after>//! nmea.js
//! parse and format NMEA 0183 sentences.
//! version : 0.1
//! homegrownmarine.com

var winston = require('winston');
var moment = require('moment');
var _ = require('lodash');

//TODO: move to util class
var coordinate = module.exports.coordinate = {
    parse: function(string, cardinalDir) {
        var value = +string;
        var degrees = parseInt(value/100);
        value -= degrees*100;
        degrees += value / 60.0;
        return (cardinalDir == 'S' || cardinalDir == 'W'?-1:1) * degrees;
    },
    format: function(value, latOrLon) {
        var direction = (value >= 0 ? 'E':'W');
        if (latOrLon === 'lat') {
            direction = (value >= 0 ? 'N':'S');
        }

        value = Math.abs(value);
        var degrees = parseInt(value);

        //TODO: pad?
        var string = ''+degrees+((value-degrees)*60).toFixed(6);

        return [string, direction];
    }
};

module.exports.variation = 0;

var parsers = {};
module.exports.parsers = parsers;

/*Parse RMC Message - Recommended Minimum Navigation Information

    Expected format:
                                                               12
                   1 2       3 4        5 6   7   8    9  10 11 |
                   | |       | |        | |   |   |    |   | |  |
    $--RMC,hhmmss.ss,A,llll.ll,a,yyyyy.yy,a,x.x,x.x,xxxx,x.x,a*hh

    1) Time (UTC)
    2) Status, V = Navigation receiver warning
    3) Latitude
    4) N or S
    5) Longitude
    6) E or W
    7) Speed over ground, knots
    8) Track made good, degrees true
    9) Date, ddmmyy
    10) Magnetic Variation, degrees
    11) E or W
    12) Checksum

    Example Messages:
    $GPRMC,204658,A,4740.2949,N,12228.5660,W,005.4,300.8,100410,018.2,E*63
    $GPRMC,225426,A,4741.9098,N,12225,2008,W,005.2,318.7,220412,018.0,E*65
    $GPRMC,152337,V,4741.0220,N,12224.3803,W,,,210412,018.2,E*74
    $GPRMC,030000.6,A,4740.36415,N,12225.35953,W,000.02,059.5,160612,016.6,E*4A   //GPS 18X 5Hz
    $GPRMC,030944.8,A,4738.3356,N,12221.6469,W,0.01,305.20,031014,018.2,E*75      //GPS 19X 10Hz

    -- NMEA 2.3 adds another column
    $GPRMC,214930.000,A,4741.1764,N,12224.1899,W,0.02,0.00,010614,,,D*78
*/
parsers.RMC = {
    parse: function(message,parts) {
        var data = {
            msg: message,
            type: 'rmc',
            lat: coordinate.parse(parts[3],parts[4]),
            latStr: parts[3]+','+parts[4],
            lon: coordinate.parse(parts[5],parts[6]),
            lonStr: parts[5]+','+parts[6],
            time: moment.utc(parts[9]+' '+parts[1], "DDMMYY HHmmssS"),
            variation: +parts[10] * (parts[11] == 'E'?-1:1)
        };

        //TODO: this is a side effect. apparently, those are bad
        module.exports.variation = _.isNumber(data.variation)? data.variation:0;

        if (parts[7])
            data.sog = +parts[7];
        if (parts[8])
            data.cog = +parts[8];

        return data;
    }
};

/*RMB Recommended Minimum Navigation Information

    Expected format:
                                                                14
           1   2 3    4    5       6 7        8 9  10  11  12 13 |
           |   | |    |    |       | |        | |   |   |   | |  |
    $--RMB,A,x.x,a,c--c,c--c,llll.ll,a,yyyyy.yy,a,x.x,x.x,x.x,A*hh

    1) Status, V = Navigation receiver warning
    2) Cross Track error - nautical miles
    3) Direction to Steer, Left or Right
    4) TO Waypoint ID
    5) FROM Waypoint ID
    6) Destination Waypoint Latitude
    7) N or S
    8) Destination Waypoint Longitude
    9) E or W
    10) Range to destination in nautical miles
    11) Bearing to destination in degrees True
    12) Destination closing velocity in knots
    13) Arrival Status, A = Arrival Circle Entered
    14) Checksum
*/
parsers.RMB = {
// GPRMB,A,{xte:.2f},{dts},{from_wp},{to_wp},{lat},{latDir},{lon},{lonDir},{dtw:.1f},{btw:.0f},{vmgwp:.2f},V

    format: function(data) {
        var toLat = coordinate.format(data.to.lat, 'lat');
        var toLon = coordinate.format(data.to.lon, 'lon');

        var parts = ["IIRMB",
            "A",
            data.xte.toFixed(2),
            data.dts,
            data.to.name,
            data.from.name,
            toLat[0],
            toLat[1],
            toLon[0],
            toLon[1],
            data.dtw.toFixed(1),
            data.btw.toFixed(0),
            data.vmgw.toFixed(2),
            'V'
        ];

        return parts.join(',');
    }
};

/*Parse HDG message - Heading, Deviation & Variation

    Expected format:

             1   2 3   4 5  6
             |   | |   | |  |
    $--HDG,x.x,x.x,a,x.x,a*hh

    1) Magnetic Sensor heading in degrees
    2) Magnetic Deviation, degrees
    3) Magnetic Deviation direction, E = Easterly, W = Westerly
    4) Magnetic Variation degrees
    5) Magnetic Variation direction, E = Easterly, W = Westerly
    6) Checksum

    Example Messages:
    $IIHDG,298,,,18,E*18
    $HCHDG,192.5,0.0,E,,*26
*/
parsers.HDG = {
    parse: function(message,parts) {
        return {
            msg: message,
            type: 'hdg',
            hdg: +parts[1] - module.exports.variation
        };
    }
};

/*
Parse MWV Message - Wind Speed and Angle

    Expected format:

             1 2   3 4  5
             | |   | |  |
    $--MWV,x.x,a,x.x,a*hh

    1) Wind Angle, 0 to 360 degrees
    2) Reference, R = Relative, T = True
    3) Wind Speed
    4) Wind Speed Units, K/M/N
    5) Status, A = Data Valid
    6) Checksum

    Example Messages:
    $CRMWV,35.5,R,,,A*7F
    $IIMWV,27.71,R,14.03,N,A*38
*/
parsers.MWV = {
    parse: function(message, parts) {
        var data = {
            msg: message,
            type: 'mwv',
            ref: parts[2]
        };

        if ( parts[2] == 'R' ) {
            angle = 'awa';
            speed = 'aws';
        }
        else {
            // angle = 'twa';
            // speed = 'tws';

            //not accepting twa from instruments
            return null;
        }

        data[angle] = +parts[1];     //assumes K, TODO: unit conversion
        data[speed] = +parts[3];

        if (data[angle] > 180) {
            data[angle] = -1 * (360 - data[angle]);
        }

        return data;
    },
    format: function() {
        var parts = [
            "IIMWV",
            data.twa.toFixed(1),
            "T",
            data.tws.toFixed(2),
            "N",
            "A"
        ];

        return parts.join(',');
    }
};

/*
Parse VHW Message - Water Speed and Heading

    Expected Format:

             1 2   3 4   5 6   7 8  9
             | |   | |   | |   | |  |
    $--VHW,x.x,T,x.x,M,x.x,N,x.x,K*hh

    1) Degress True
    2) T = True
    3) Degrees Magnetic
    4) M = Magnetic
    5) Knots (speed of vessel relative to the water)
    6) N = Knots
    7) Kilometers (speed of vessel relative to the water)
    8) K = Kilometres
    9) Checksum

    Example Messages:
    $CRVHW,,,,,6.1,N,,*3F
    $IIVHW,,,,,4.87,N,9.03,K*4D
*/
parsers.VHW = {
    parse: function(message,parts) {
        return {
            msg: message,
            type: 'vhw',
            speed: +parts[5]  //TODO: may need to unit convert if [5] isn't populated
        };
    }
};

/*
Parse or format VDR Message - Set and Drift

    Expected Format:
             1 2   3 4   5 6  7
             | |   | |   | |  |
    $--VDR,x.x,T,x.x,M,x.x,N*hh

    1) Degress True
    2) T = True
    3) Degrees Magnetic
    4) M = Magnetic
    5) Knots (speed of current)
    6) N = Knots
    7) Checksum
*/
parsers.VDR = {
    parse: function(message,parts) {
        return {
            msg: message,
            type: 'vdr',
            set: parts[1],
            drift: parts[5]
        };
    },
    format: function(data) {
        var parts = ["IIVDR",
            data.set.toFixed(2),
            "T",
            "",
            "",
            data.drift.toFixed(2),
            "N"
        ];

        return parts.join(',');
    }
};

/*Parse ROT Message - Rate Of Turn

    Expected Format:
             1 2  3
             | |  |
    $--ROT,x.x,A*hh

    1) Rate Of Turn, degrees per minute, "-" means bow turns to port
    2) Status, A means data is valid
    3) Checksum

    Example Messages:
    $TIROT,-20.3,A*27 */
parsers.ROT = {
    parse: function(message, parts) {
        return {
            msg: message,
            type: 'rot',
            rot: +parts[1]
        };
    }
};

// $IIDPT,075.1,-1.0,*43
parsers.DPT = {
    parse: function(message, parts) {
        return {
            msg: message,
            type: 'dpt',
            depth: +parts[1]        //TODO: more
        };
    }
};

// $YXXDR,A,0.4,D,PTCH,A,-4.0,D,ROLL*70
parsers.XDR = {
    parse: function(message, parts) {
        return {
            msg: message,
            type: 'xdr',
            pitch: +parts[2],
            heel: +parts[6]
        };
    }
};

// Calculate nmea checksum.  Assumes $ is not passed in
var checksum = module.exports.checksum = function(message) {
    var csum = 0;
    for ( var i=0; i < message.length; i++ ) {
        csum = csum ^ message.charCodeAt(i);
    }

    csum = csum.toString(16);
    if (csum.length == 1) {
        csum = '0'+csum;
    }
    return csum.toUpperCase();
};

// Validate nmea0183 message, assumes first character is $.
var validate = module.exports.validate = function(message) {
    if ( message.charAt(message.length-3) == '*' ) {
        return checksum(message.substring(1,message.length-3)) == message.substring(message.length-2,message.length);
    }
    return true;
};

module.exports.messageId = function(message) {
    var end = message.indexOf(',');
    return message.substring(1,end);
};

module.exports.parse = function(message) {
    message = message.trim(); //proper messages end in \r\n

    if ( !validate(message) ) {
        return null;
    }

    try {
        message = message.substring(1, message.length-3); //trim xsum and $
        var type = message.substring(2, 5);

        if ( type in parsers && parsers[type].parse ) {
            var parts = message.split(',');
            return parsers[type].parse(message, parts);
        }
    }
    catch(e) {
        winston.error('Error parsing NMEA message :', e);
    }

    return null;
};

//TODO : format string or object
module.exports.format = function(data) {
    var message;
    if ( _.isString(data) ) {
        message = data;
    }
    else if ( data.type && data.type.toUpperCase() in parsers && parsers[data.type.toUpperCase()].format ) {
        message = parsers[data.type.toUpperCase()].format(data);
    }

    if (message)
        return '$' + message + '*' + checksum(message);
    else
        return null;
};
<|endoftext|>


<commit_before>var EXPORTED_SYMBOLS = [];

var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;

Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
Cu.import('resource://services-crypto/utils.js');
Cu.import("resource://greasemonkey/miscapis.js");
Cu.import('resource://greasemonkey/prefmanager.js');
Cu.import('resource://greasemonkey/util.js');

var gSyncInitialized = false;

var gWeave = {};
Cu.import('resource://services-sync/engines.js', gWeave);
Cu.import('resource://services-sync/record.js', gWeave);
Cu.import('resource://services-sync/status.js', gWeave);
Cu.import('resource://services-sync/util.js', gWeave);

var SyncServiceObserver = {
  init: function() {
    if (gWeave.Status.ready) {
      this.initEngine();
    } else {
      Services.obs.addObserver(this, 'weave:service:ready', true);
    }
  },

  initEngine: function() {
    if (gSyncInitialized) return;
    gSyncInitialized = true;

    // This must be delayed until after the Greasemonkey service is set up.
    Cu.import('resource://greasemonkey/remoteScript.js');
    // Also delay importing the actual Sync service to prevent conflicts with
    // the master password dialog during browser startup. See #1852.
    Cu.import('resource://services-sync/service.js', gWeave);
    gWeave.Service.engineManager.register(ScriptEngine);
  },

  observe: function(aSubject, aTopic, aData) {
    switch (aTopic) {
    case 'weave:service:ready':
      this.initEngine();
      break;
    }
  },

  QueryInterface: XPCOMUtils.generateQI(
      [Ci.nsIObserver, Ci.nsISupportsWeakReference]),
};

function ScriptRecord(aCollection, aId) {
  gWeave.CryptoWrapper.call(this, aCollection, aId);
}
ScriptRecord.prototype = {
  __proto__: gWeave.CryptoWrapper.prototype,
  _logName: 'Record.GreasemonkeyScript',
};
gWeave.Utils.deferGetSet(
    ScriptRecord, 'cleartext',
    ['downloadURL', 'enabled', 'installed',
     'userExcludes', 'userIncludes',
     'values', 'valuesTooBig',
    ]);

function ScriptStore(aName, aEngine) {
  gWeave.Store.call(this, aName, aEngine);
}
ScriptStore.prototype = {
  __proto__: gWeave.Store.prototype,

  changeItemID: function(aOldId, aNewId) {
    dump('>>> ScriptStore.changeItemID() ... '
        + aOldId.substr(0, 8) + ' ' + aNewId.substr(0, 8) + '\n');
  },

  /// Incoming Sync record, create local version.
  create: function(aRecord) {
    if (aRecord.cleartext.installed) {
      var rs = new RemoteScript(aRecord.cleartext.downloadURL);
      rs.setSilent();
      rs.download(GM_util.hitch(this, function(aSuccess, aType) {
        if (aSuccess && 'dependencies' == aType) {
          rs.install();
          rs.script.enabled = aRecord.cleartext.enabled;
          rs.script.userExcludes = aRecord.cleartext.userExcludes;
          rs.script.userIncludes = aRecord.cleartext.userIncludes;
          setScriptValuesFromSyncRecord(rs.script, aRecord);
        }
      }));
    } else {
      var script = scriptForSyncId(aRecord.cleartext.id);
      if (!script) return;
      script.uninstall();
    }
  },

  /// New local item, create sync record.
  createRecord: function(aId, aCollection) {
    var script = scriptForSyncId(aId);
    if (script) {
      var record = new ScriptRecord();
      record.cleartext.id = aId;
      record.cleartext.downloadURL = script.downloadURL;
      record.cleartext.enabled = script.enabled;
      record.cleartext.installed = !script.needsUninstall;
      record.cleartext.userExcludes = script.userExcludes;
      record.cleartext.userIncludes = script.userIncludes;

      if (GM_prefRoot.getValue('sync.values')) {
        var storage = new GM_ScriptStorage(script);
        var totalSize = 0;
        var maxSize = GM_prefRoot.getValue('sync.values_max_size_per_script');
        record.cleartext.values = {};
        record.cleartext.valuesTooBig = false;
        var names = storage.listValues();
        for (var i = 0, name = null; name = names[i]; i++) {
          var val = storage.getValue(name);
          record.cleartext.values[name] = val;
          totalSize += name.length;
          totalSize += val.length || 4;  // 4 for number / bool (no length).

          if (totalSize > maxSize) {
            record.cleartext.values = [];
            record.cleartext.valuesTooBig = true;
            break;
          }
        }
      }

      return record;
    } else {
      // Assume this is an uninstalled script.
      var record = new ScriptRecord();
      record.cleartext.enabled = false;
      record.cleartext.installed = false;
      return record;
    }
  },

  getAllIDs: function() {
    var syncIds = {};
    var scripts = GM_util.getService().config.scripts;
    for (var i = 0, script = null; script = scripts[i]; i++) {
      if (!script.downloadURL) continue;
      if (script.downloadURL.match(/^file:/)) continue;
      syncIds[syncId(script)] = 1;
    }
    return syncIds;
  },

  itemExists: function(aId) {
    var script = scriptForSyncId(aId);
    return !!script;
  },

  remove: function(aRecord) {
    var script = scriptForSyncId(aRecord.cleartext.id);
    if (script) script.uninstall();
  },

  update: function(aRecord) {
    var script = scriptForSyncId(aRecord.cleartext.id);
    if (!script) {
      dump('Could not find script for record ' + aRecord.cleartext + '\n');
      return;
    }
    if (!aRecord.cleartext.installed) {
      script.uninstall();
    } else {
      script.enabled = aRecord.cleartext.enabled;
      script.userExcludes = aRecord.cleartext.userExcludes;
      script.userIncludes = aRecord.cleartext.userIncludes;
      setScriptValuesFromSyncRecord(script, aRecord);
    }
  },

  wipe: function() {
    dump('>>> ScriptStore.wipe() ...\n');
    // Delete everything!
  },
};

function ScriptTracker(aName, aEngine) {
  gWeave.Tracker.call(this, aName, aEngine);
  GM_util.getService().config.addObserver(this);
}
ScriptTracker.prototype = {
  __proto__: gWeave.Tracker.prototype,

  notifyEvent: function(aScript, aEvent, aData) {
    if (aEvent in {'install': 1, 'modified': 1, 'edit-enabled': 1}) {
      if (this.addChangedID(syncId(aScript))) {
        this.score = Math.min(100, this.score + 5);
      }
    } else if (aEvent in {'cludes': 1, 'val-set': 1, 'val-del': 1}) {
      if (this.addChangedID(syncId(aScript))) {
        this.score = Math.min(100, this.score + 1);
      }
    }
  }
};

function ScriptEngine() {
  gWeave.SyncEngine.call(this, 'Greasemonkey', gWeave.Service);

  this.enabled = GM_prefRoot.getValue('sync.enabled');
  GM_prefRoot.watch('sync.enabled', GM_util.hitch(this, function() {
    this.enabled = GM_prefRoot.getValue('sync.enabled');
  }));
}
ScriptEngine.prototype = {
  __proto__: gWeave.SyncEngine.prototype,
  _recordObj: ScriptRecord,
  _storeObj: ScriptStore,
  _trackerObj: ScriptTracker,
};

function scriptForSyncId(aSyncId) {
  var scripts = GM_util.getService().config.scripts;
  for (var i = 0, script = null; script = scripts[i]; i++) {
    if (syncId(script) == aSyncId) {
      return script;
    }
  }
}

// The sync ID for a given script.
function syncId(aScript) {
  // TODO: Salting?  e.g. btoa(CryptoUtils.generateRandomBytes(16));
  return GM_util.sha1(aScript.id);
}

function setScriptValuesFromSyncRecord(aScript, aRecord) {
  if (GM_prefRoot.getValue('sync.values')
      && !aRecord.cleartext.valuesTooBig
  ) {
    // TODO: Clear any locally set values not in the sync record?
    var storage = new GM_ScriptStorage(aScript);
    for (name in aRecord.cleartext.values) {
      storage.setValue(name, aRecord.cleartext.values[name]);
    }
  }
}

SyncServiceObserver.init()
<commit_msg>whitespace<commit_after>var EXPORTED_SYMBOLS = [];

var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;

Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
Cu.import('resource://services-crypto/utils.js');
Cu.import("resource://greasemonkey/miscapis.js");
Cu.import('resource://greasemonkey/prefmanager.js');
Cu.import('resource://greasemonkey/util.js');

var gSyncInitialized = false;

var gWeave = {};
Cu.import('resource://services-sync/engines.js', gWeave);
Cu.import('resource://services-sync/record.js', gWeave);
Cu.import('resource://services-sync/status.js', gWeave);
Cu.import('resource://services-sync/util.js', gWeave);

var SyncServiceObserver = {
  init: function() {
    if (gWeave.Status.ready) {
      this.initEngine();
    } else {
      Services.obs.addObserver(this, 'weave:service:ready', true);
    }
  },

  initEngine: function() {
    if (gSyncInitialized) return;
    gSyncInitialized = true;

    // This must be delayed until after the Greasemonkey service is set up.
    Cu.import('resource://greasemonkey/remoteScript.js');
    // Also delay importing the actual Sync service to prevent conflicts with
    // the master password dialog during browser startup. See #1852.
    Cu.import('resource://services-sync/service.js', gWeave);

    gWeave.Service.engineManager.register(ScriptEngine);
  },

  observe: function(aSubject, aTopic, aData) {
    switch (aTopic) {
    case 'weave:service:ready':
      this.initEngine();
      break;
    }
  },

  QueryInterface: XPCOMUtils.generateQI(
      [Ci.nsIObserver, Ci.nsISupportsWeakReference]),
};

function ScriptRecord(aCollection, aId) {
  gWeave.CryptoWrapper.call(this, aCollection, aId);
}
ScriptRecord.prototype = {
  __proto__: gWeave.CryptoWrapper.prototype,
  _logName: 'Record.GreasemonkeyScript',
};
gWeave.Utils.deferGetSet(
    ScriptRecord, 'cleartext',
    ['downloadURL', 'enabled', 'installed',
     'userExcludes', 'userIncludes',
     'values', 'valuesTooBig',
    ]);

function ScriptStore(aName, aEngine) {
  gWeave.Store.call(this, aName, aEngine);
}
ScriptStore.prototype = {
  __proto__: gWeave.Store.prototype,

  changeItemID: function(aOldId, aNewId) {
    dump('>>> ScriptStore.changeItemID() ... '
        + aOldId.substr(0, 8) + ' ' + aNewId.substr(0, 8) + '\n');
  },

  /// Incoming Sync record, create local version.
  create: function(aRecord) {
    if (aRecord.cleartext.installed) {
      var rs = new RemoteScript(aRecord.cleartext.downloadURL);
      rs.setSilent();
      rs.download(GM_util.hitch(this, function(aSuccess, aType) {
        if (aSuccess && 'dependencies' == aType) {
          rs.install();
          rs.script.enabled = aRecord.cleartext.enabled;
          rs.script.userExcludes = aRecord.cleartext.userExcludes;
          rs.script.userIncludes = aRecord.cleartext.userIncludes;
          setScriptValuesFromSyncRecord(rs.script, aRecord);
        }
      }));
    } else {
      var script = scriptForSyncId(aRecord.cleartext.id);
      if (!script) return;
      script.uninstall();
    }
  },

  /// New local item, create sync record.
  createRecord: function(aId, aCollection) {
    var script = scriptForSyncId(aId);
    if (script) {
      var record = new ScriptRecord();
      record.cleartext.id = aId;
      record.cleartext.downloadURL = script.downloadURL;
      record.cleartext.enabled = script.enabled;
      record.cleartext.installed = !script.needsUninstall;
      record.cleartext.userExcludes = script.userExcludes;
      record.cleartext.userIncludes = script.userIncludes;

      if (GM_prefRoot.getValue('sync.values')) {
        var storage = new GM_ScriptStorage(script);
        var totalSize = 0;
        var maxSize = GM_prefRoot.getValue('sync.values_max_size_per_script');
        record.cleartext.values = {};
        record.cleartext.valuesTooBig = false;
        var names = storage.listValues();
        for (var i = 0, name = null; name = names[i]; i++) {
          var val = storage.getValue(name);
          record.cleartext.values[name] = val;
          totalSize += name.length;
          totalSize += val.length || 4;  // 4 for number / bool (no length).

          if (totalSize > maxSize) {
            record.cleartext.values = [];
            record.cleartext.valuesTooBig = true;
            break;
          }
        }
      }

      return record;
    } else {
      // Assume this is an uninstalled script.
      var record = new ScriptRecord();
      record.cleartext.enabled = false;
      record.cleartext.installed = false;
      return record;
    }
  },

  getAllIDs: function() {
    var syncIds = {};
    var scripts = GM_util.getService().config.scripts;
    for (var i = 0, script = null; script = scripts[i]; i++) {
      if (!script.downloadURL) continue;
      if (script.downloadURL.match(/^file:/)) continue;
      syncIds[syncId(script)] = 1;
    }
    return syncIds;
  },

  itemExists: function(aId) {
    var script = scriptForSyncId(aId);
    return !!script;
  },

  remove: function(aRecord) {
    var script = scriptForSyncId(aRecord.cleartext.id);
    if (script) script.uninstall();
  },

  update: function(aRecord) {
    var script = scriptForSyncId(aRecord.cleartext.id);
    if (!script) {
      dump('Could not find script for record ' + aRecord.cleartext + '\n');
      return;
    }
    if (!aRecord.cleartext.installed) {
      script.uninstall();
    } else {
      script.enabled = aRecord.cleartext.enabled;
      script.userExcludes = aRecord.cleartext.userExcludes;
      script.userIncludes = aRecord.cleartext.userIncludes;
      setScriptValuesFromSyncRecord(script, aRecord);
    }
  },

  wipe: function() {
    dump('>>> ScriptStore.wipe() ...\n');
    // Delete everything!
  },
};

function ScriptTracker(aName, aEngine) {
  gWeave.Tracker.call(this, aName, aEngine);
  GM_util.getService().config.addObserver(this);
}
ScriptTracker.prototype = {
  __proto__: gWeave.Tracker.prototype,

  notifyEvent: function(aScript, aEvent, aData) {
    if (aEvent in {'install': 1, 'modified': 1, 'edit-enabled': 1}) {
      if (this.addChangedID(syncId(aScript))) {
        this.score = Math.min(100, this.score + 5);
      }
    } else if (aEvent in {'cludes': 1, 'val-set': 1, 'val-del': 1}) {
      if (this.addChangedID(syncId(aScript))) {
        this.score = Math.min(100, this.score + 1);
      }
    }
  }
};

function ScriptEngine() {
  gWeave.SyncEngine.call(this, 'Greasemonkey', gWeave.Service);

  this.enabled = GM_prefRoot.getValue('sync.enabled');
  GM_prefRoot.watch('sync.enabled', GM_util.hitch(this, function() {
    this.enabled = GM_prefRoot.getValue('sync.enabled');
  }));
}
ScriptEngine.prototype = {
  __proto__: gWeave.SyncEngine.prototype,
  _recordObj: ScriptRecord,
  _storeObj: ScriptStore,
  _trackerObj: ScriptTracker,
};

function scriptForSyncId(aSyncId) {
  var scripts = GM_util.getService().config.scripts;
  for (var i = 0, script = null; script = scripts[i]; i++) {
    if (syncId(script) == aSyncId) {
      return script;
    }
  }
}

// The sync ID for a given script.
function syncId(aScript) {
  // TODO: Salting?  e.g. btoa(CryptoUtils.generateRandomBytes(16));
  return GM_util.sha1(aScript.id);
}

function setScriptValuesFromSyncRecord(aScript, aRecord) {
  if (GM_prefRoot.getValue('sync.values')
      && !aRecord.cleartext.valuesTooBig
  ) {
    // TODO: Clear any locally set values not in the sync record?
    var storage = new GM_ScriptStorage(aScript);
    for (name in aRecord.cleartext.values) {
      storage.setValue(name, aRecord.cleartext.values[name]);
    }
  }
}

SyncServiceObserver.init()
<|endoftext|>


<commit_before>exports.mod = function(context, server)
{
	this.lists = {};

	this.help$register = function()
	{
		server.do('help$register', 'todo');
	}

	this._help = function(topic, params)
	{
		switch(topic)
		{
			case 'todo':
				switch(params[0])
				{
					case '+':
						return 'Add an entry to your todo list.';
					case '-':
						return 'Remove an entry from your todo list.';
					case '?':
						return 'Display your todo list.';
					default:
						return {
							text: 'A simple account-based todo list.',
							sub: ['+', '-', '?']
						};
				}
				break;
		}
	}

	this.core$cmd = function(prefix, target, cmd, params, $core)
	{
		if(cmd === 'todo')
		{
			switch(params[0])
			{
				case '+':
					var syntax = 'Syntax: todo + <data>';
					var data = params.slice(1).join(' ');

					if(data === '') { $core._privmsg(target, syntax); break; }

					var login = server.do('account$getlogin', prefix);

					if(login === undefined)
					{
						$core._privmsg(target, 'You are not logged in.');
						break;
					}

					if(this.lists[login] === undefined) this.lists[login] = [];

					// TODO: make priority configurable
					this.lists[login].push({data: data, priority: 5});

					$core._privmsg(target, 'Added entry to todo list.');
					break;
				case '-':
					var syntax = 'Syntax: todo - <entry #>';
					var entry_no = parseInt(params[1]);

					if(isNaN(entry_no)) { $core._privmsg(target, syntax); break; }

					var login = server.do('account$getlogin', prefix);

					if(login === undefined)
					{
						$core._privmsg(target, 'You are not logged in.');
						break;
					}

					if(entry_no < 1 || this.lists[login] === undefined || this.lists[login].length < entry_no)
					{
						$core._privmsg(target, 'That entry does not exist on your todo list.');
						break;
					}

					var entry = this.lists[login].splice(entry_no - 1, 1);

					$core._privmsg(target, 'Removed entry from todo list. Sending removed entry via NOTICE.');
					$core._notice(prefix.nick, '#' + entry_no + ': ' + entry[0].data);
					break;
				case '?':
				default:
					var login = server.do('account$getlogin', prefix);

					if(login === undefined)
					{
						$core._privmsg(target, 'You are not logged in.');
						break;
					}

					if(this.lists[login] === undefined || this.lists[login].length === 0)
					{
						$core._privmsg(target, 'Your todo list is empty.');
						break;
					}

					$core._privmsg(target, 'Sending todo list via NOTICE.');

					for(var iTodo=0; iTodo<this.lists[login].length; ++iTodo)
					{
						$core._notice(prefix.nick, '#' + (iTodo + 1) + ': ' + this.lists[login][iTodo].data);
					}

					break;
			}
		}
	}
}
<commit_msg>Implemented todo list saving/loading, max todo list size, and removing more than one todo entry at once.<commit_after>exports.mod = function(context, server)
{
	this.max_length = 10;
	this.lists = {};

	this.help$register = function()
	{
		server.do('help$register', 'todo');
	}

	this._help = function(topic, params)
	{
		switch(topic)
		{
			case 'todo':
				switch(params[0])
				{
					case '+':
						return 'Add an entry to your todo list.';
					case '-':
						return 'Remove an entry from your todo list.';
					case '?':
						return 'Display your todo list.';
					default:
						return {
							text: 'A simple account-based todo list.',
							sub: ['+', '-', '?']
						};
				}
				break;
		}
	}

	this._load = function(data)
	{
		if(data.max_length !== undefined) this.max_length = data.max_length;
		if(data.lists !== undefined) this.lists = data.lists;
	}

	this._suspend = function()
	{
		return {
			max_length: this.max_length,
			lists: this.lists
		};
	}

	this._resume = function(data)
	{
		if(data.max_length !== undefined) this.max_length = data.max_length;
		if(data.lists !== undefined) this.lists = data.lists;
	}

	this._save = function()
	{
		return {
			max_length: this.max_length,
			lists: this.lists
		};
	}

	this.core$cmd = function(prefix, target, cmd, params, $core)
	{
		if(cmd === 'todo')
		{
			switch(params[0])
			{
				case '+':
					var syntax = 'Syntax: todo + <data>';
					var data = params.slice(1).join(' ');

					if(data === '') { $core._privmsg(target, syntax); break; }

					var login = server.do('account$getlogin', prefix);

					if(login === undefined)
					{
						$core._privmsg(target, 'You are not logged in.');
						break;
					}

					if(this.lists[login] === undefined) this.lists[login] = [];

					if(this.lists[login].length === this.max_length)
					{
						$core._privmsg(target, 'Too many entries on todo list (max=' + this.max_length + ').');
						break;
					}

					// TODO: make priority configurable
					this.lists[login].push({data: data, priority: 5});

					$core._privmsg(target, 'Added entry to todo list.');
					break;
				case '-':
					var syntax = 'Syntax: todo - <entry #> [num to remove]';
					var entry_no = parseInt(params[1]);
					var num_to_remove = parseInt(params[2]);

					if(isNaN(entry_no)) { $core._privmsg(target, syntax); break; }
					if(isNaN(num_to_remove)) num_to_remove = 1;

					var login = server.do('account$getlogin', prefix);

					if(login === undefined)
					{
						$core._privmsg(target, 'You are not logged in.');
						break;
					}

					if(entry_no < 1 || this.lists[login] === undefined || this.lists[login].length < entry_no)
					{
						$core._privmsg(target, 'That entry does not exist on your todo list.');
						break;
					}

					var entry = this.lists[login].splice(entry_no - 1, num_to_remove);

					var s = (num_to_remove === 1) ? 'entry' : 'entries';

					$core._privmsg(target, 'Removed ' + num_to_remove + ' ' + s + ' from todo list. Sending removed ' + s + ' via NOTICE.');

					for(var i=0; i<entry.length; ++i)
					{
						$core._notice(prefix.nick, '#' + entry_no + ': ' + entry[i].data);
					}
					break;
				case '?':
				default:
					var login = server.do('account$getlogin', prefix);

					if(login === undefined)
					{
						$core._privmsg(target, 'You are not logged in.');
						break;
					}

					if(this.lists[login] === undefined || this.lists[login].length === 0)
					{
						$core._privmsg(target, 'Your todo list is empty.');
						break;
					}

					$core._privmsg(target, 'Sending todo list via NOTICE.');

					for(var iTodo=0; iTodo<this.lists[login].length; ++iTodo)
					{
						$core._notice(prefix.nick, '#' + (iTodo + 1) + ': ' + this.lists[login][iTodo].data);
					}

					break;
			}
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2011 Google Inc. All Rights Reserved.

/**
 * @fileoverview Utility functions for generating native browser events.
 */
goog.provide('jsaction.testing.nativeEvents');
goog.setTestOnly('jsaction.testing.nativeEvents');

goog.require('goog.dom.NodeType');
goog.require('goog.events.BrowserEvent');
goog.require('goog.events.EventType');
goog.require('goog.style');
goog.require('goog.testing.events.Event');
goog.require('jsaction.createKeyboardEvent');
goog.require('jsaction.createMouseEvent');
goog.require('jsaction.createUiEvent');
goog.require('jsaction.triggerEvent');

/**
 * Simulates a blur event on the given target.
 * @param {!EventTarget} target The target for the event.
 * @return {boolean} The returnValue of the event: false if preventDefault() was
 *     called on it, true otherwise.
 */
jsaction.testing.nativeEvents.fireBlurEvent = function(target) {
  var e = new goog.testing.events.Event(goog.events.EventType.BLUR, target);
  return jsaction.triggerEvent(target, jsaction.createUiEvent(e));
};

/**
 * Simulates a focus event on the given target.
 * @param {!EventTarget} target The target for the event.
 * @return {boolean} The returnValue of the event: false if preventDefault() was
 *     called on it, true otherwise.
 */
jsaction.testing.nativeEvents.fireFocusEvent = function(target) {
  var e = new goog.testing.events.Event(goog.events.EventType.FOCUS, target);
  return jsaction.triggerEvent(target, jsaction.createUiEvent(e));
};

/**
 * Simulates a scroll event on the given target.
 * @param {!EventTarget} target The target for the event.
 * @return {boolean} The returnValue of the event: false if preventDefault() was
 *     called on it, true otherwise.
 */
jsaction.testing.nativeEvents.fireScrollEvent = function(target) {
  var e = new goog.testing.events.Event(goog.events.EventType.SCROLL, target);
  return jsaction.triggerEvent(target, jsaction.createUiEvent(e));
};

/**
 * Simulates a mousedown, mouseup, and then click on the given event target,
 * with the left mouse button.
 * @param {!EventTarget} target The target for the event.
 * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
 *     defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
 * @return {boolean} The returnValue of the sequence: false if preventDefault()
 *     was called on any of the events, true otherwise.
 */
jsaction.testing.nativeEvents.fireClickSequence = function(target, opt_button) {
  return !!(
      jsaction.testing.nativeEvents.fireMouseDownEvent(target, opt_button) &
      jsaction.testing.nativeEvents.fireMouseUpEvent(target, opt_button) &
      jsaction.testing.nativeEvents.fireClickEvent(target, opt_button));
};

/**
 * Simulates a mousedown event on the given target.
 * @param {!EventTarget} target The target for the event.
 * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
 *     defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
 * @return {boolean} false if preventDefault() was called, true otherwise.
 */
jsaction.testing.nativeEvents.fireMouseDownEvent = function(
    target, opt_button) {
  return jsaction.testing.nativeEvents.fireMouseButtonEvent_(
      goog.events.EventType.MOUSEDOWN, target, opt_button);
};

/**
 * Simulates a mouseup event on the given target.
 * @param {!EventTarget} target The target for the event.
 * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
 *     defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
 * @return {boolean} false if preventDefault() was called, true otherwise.
 */
jsaction.testing.nativeEvents.fireMouseUpEvent = function(target, opt_button) {
  return jsaction.testing.nativeEvents.fireMouseButtonEvent_(
      goog.events.EventType.MOUSEUP, target, opt_button);
};

/**
 * Simulates a click event on the given target.
 * @param {!EventTarget} target The target for the event.
 * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
 *     defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
 * @return {boolean} false if preventDefault() was called, true otherwise.
 */
jsaction.testing.nativeEvents.fireClickEvent = function(target, opt_button) {
  return jsaction.testing.nativeEvents.fireMouseButtonEvent_(
      goog.events.EventType.CLICK, target, opt_button);
};

/**
 * Simulates a mouseover event on the given target.
 * @param {!EventTarget} target The target for the event.
 * @return {boolean} false if preventDefault() was called, true otherwise.
 */
jsaction.testing.nativeEvents.fireMouseOverEvent = function(target) {
  return jsaction.testing.nativeEvents.fireMouseButtonEvent_(
      goog.events.EventType.MOUSEOVER, target);
};

/**
 * Simulates a mouseout event on the given target.
 * @param {!EventTarget} target The target for the event.
 * @return {boolean} false if preventDefault() was called, true otherwise.
 */
jsaction.testing.nativeEvents.fireMouseOutEvent = function(target) {
  return jsaction.testing.nativeEvents.fireMouseButtonEvent_(
      goog.events.EventType.MOUSEOUT, target);
};

/**
 * Simulates a mousemove event on the given target.
 * @param {!EventTarget} target The target for the event.
 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
 * target's position (if available), otherwise (0, 0).
 * @return {boolean} The returnValue of the event: false if preventDefault() was
 *     called on it, true otherwise.
 */
jsaction.testing.nativeEvents.fireMouseMoveEvent = function(
    target, opt_coords) {
  if (!opt_coords && target &&
      target.nodeType == goog.dom.NodeType.ELEMENT) {
    try {
      opt_coords =
          goog.style.getClientPosition(/** @type {Element} **/ (target));
    } catch (ex) {
      // IE sometimes throws if it can't get the position.
    }
  }

  var e = new goog.testing.events.Event(
      goog.events.EventType.MOUSEMOVE, target);
  var xPos = opt_coords ? opt_coords.x : 0;
  var yPos = opt_coords ? opt_coords.y : 0;
  e.clientX = e.screenX = xPos;
  e.clientY = e.screenY = yPos;

  return jsaction.triggerEvent(target, jsaction.createMouseEvent(e));
};

/**
 * Creates a mouse button event.
 * @param {string} type The event type.
 * @param {!EventTarget} target The target for the event.
 * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
 *     defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
 * @param {boolean=} opt_modifierKey Create the event with the modifier key
 *     registered as down.
 * @return {!Event} The created event.
 */
jsaction.testing.nativeEvents.createMouseButtonEvent = function(
    type, target, opt_button, opt_modifierKey) {
  var e = new goog.testing.events.Event(type, target);
  e.button = opt_button || goog.events.BrowserEvent.MouseButton.LEFT;
  if (opt_modifierKey) {
    e.ctrlKey = true;
    e.metaKey = true;
  }
  return jsaction.createMouseEvent(e);
};

/**
 * Helper function to fire a mouse event with a mouse button. IE < 9 only allows
 * firing events using the left mouse button.
 * @param {string} type The event type.
 * @param {!EventTarget} target The target for the event.
 * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
 *     defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
 * @return {boolean} The value returned by the browser event,
 *     which returns false iff 'preventDefault' was invoked.
 * @private
 */
jsaction.testing.nativeEvents.fireMouseButtonEvent_ = function(
    type, target, opt_button) {
  var e = jsaction.testing.nativeEvents.createMouseButtonEvent(
      type, target, opt_button);
  return jsaction.triggerEvent(target, e);
};

/**
 * Creates and initializes a key event.
 * @param {string} eventType The type of event to create ("keydown", "keyup",
 *     or "keypress").
 * @param {!HTMLElement} node The event target.
 * @param {number} keyCode The key code.
 * @param {number} charCode The character code produced by the key.
 * @return {Object} an initialized event object.
 */
jsaction.testing.nativeEvents.fireKeyEvent = function(
    eventType, node, keyCode, charCode) {
  var e = new goog.testing.events.Event(eventType, node);
  e.charCode = charCode;
  e.keyCode = keyCode;
  var nativeEvent = jsaction.createKeyboardEvent(e);
  jsaction.triggerEvent(node, nativeEvent);
  return nativeEvent;
};

/**
 * Generates a series of events simulating a key press on the given element.
 * @param {!HTMLElement} node The event target.
 * @param {number} keyCode The key code.
 * @param {number} charCode The character code produced by the key.
 */
jsaction.testing.nativeEvents.simulateKeyPress = function(
    node, keyCode, charCode) {
  var e;

  e = jsaction.testing.nativeEvents.fireKeyEvent(
      goog.events.EventType.KEYDOWN, node, keyCode, charCode);
  e = jsaction.testing.nativeEvents.fireKeyEvent(
      goog.events.EventType.KEYPRESS, node, keyCode, charCode);
  e = jsaction.testing.nativeEvents.fireKeyEvent(
      goog.events.EventType.KEYUP, node, keyCode, charCode);
};
<commit_msg>Adding mouse position support to native mousedown, mouseup and click events. This is done analog to javascript/closure/testing/events/events.js that seems to have been a template for nativeevents.js.<commit_after>// Copyright 2011 Google Inc. All Rights Reserved.

/**
 * @fileoverview Utility functions for generating native browser events.
 */
goog.provide('jsaction.testing.nativeEvents');
goog.setTestOnly('jsaction.testing.nativeEvents');

goog.require('goog.dom.NodeType');
goog.require('goog.events.BrowserEvent');
goog.require('goog.events.EventType');
goog.require('goog.style');
goog.require('goog.testing.events.Event');
goog.require('jsaction.createKeyboardEvent');
goog.require('jsaction.createMouseEvent');
goog.require('jsaction.createUiEvent');
goog.require('jsaction.triggerEvent');

/**
 * Simulates a blur event on the given target.
 * @param {!EventTarget} target The target for the event.
 * @return {boolean} The returnValue of the event: false if preventDefault() was
 *     called on it, true otherwise.
 */
jsaction.testing.nativeEvents.fireBlurEvent = function(target) {
  var e = new goog.testing.events.Event(goog.events.EventType.BLUR, target);
  return jsaction.triggerEvent(target, jsaction.createUiEvent(e));
};

/**
 * Simulates a focus event on the given target.
 * @param {!EventTarget} target The target for the event.
 * @return {boolean} The returnValue of the event: false if preventDefault() was
 *     called on it, true otherwise.
 */
jsaction.testing.nativeEvents.fireFocusEvent = function(target) {
  var e = new goog.testing.events.Event(goog.events.EventType.FOCUS, target);
  return jsaction.triggerEvent(target, jsaction.createUiEvent(e));
};

/**
 * Simulates a scroll event on the given target.
 * @param {!EventTarget} target The target for the event.
 * @return {boolean} The returnValue of the event: false if preventDefault() was
 *     called on it, true otherwise.
 */
jsaction.testing.nativeEvents.fireScrollEvent = function(target) {
  var e = new goog.testing.events.Event(goog.events.EventType.SCROLL, target);
  return jsaction.triggerEvent(target, jsaction.createUiEvent(e));
};

/**
 * Simulates a mousedown, mouseup, and then click on the given event target,
 * with the left mouse button.
 * @param {!EventTarget} target The target for the event.
 * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
 *     defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
 * @param {?goog.math.Coordinate=} opt_coords Mouse position. Defaults to
 *     event's target's position (if available), otherwise (0, 0).
 * @param {?Object=} opt_eventProperties Event properties to be mixed into the
 *     BrowserEvent.
 * @return {boolean} The returnValue of the sequence: false if preventDefault()
 *     was called on any of the events, true otherwise.
 */
jsaction.testing.nativeEvents.fireClickSequence =
    function(target, opt_button, opt_coords, opt_eventProperties) {
  return !!(
      jsaction.testing.nativeEvents.fireMouseDownEvent(
          target, opt_button, opt_coords, opt_eventProperties) &
      jsaction.testing.nativeEvents.fireMouseUpEvent(
          target, opt_button, opt_coords, opt_eventProperties) &
      jsaction.testing.nativeEvents.fireClickEvent(
          target, opt_button, opt_coords, opt_eventProperties));
};

/**
 * Simulates a mousedown event on the given target.
 * @param {!EventTarget} target The target for the event.
 * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
 *     defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
 * @param {?goog.math.Coordinate=} opt_coords Mouse position. Defaults to
 *     event's target's position (if available), otherwise (0, 0).
 * @param {?Object=} opt_eventProperties Event properties to be mixed into the
 *     BrowserEvent.
 * @return {boolean} false if preventDefault() was called, true otherwise.
 */
jsaction.testing.nativeEvents.fireMouseDownEvent = function(
    target, opt_button, opt_coords, opt_eventProperties) {
  return jsaction.testing.nativeEvents.fireMouseButtonEvent_(
      goog.events.EventType.MOUSEDOWN, target, opt_button, opt_coords,
      opt_eventProperties);
};

/**
 * Simulates a mouseup event on the given target.
 * @param {!EventTarget} target The target for the event.
 * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
 *     defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
 * @param {?goog.math.Coordinate=} opt_coords Mouse position. Defaults to
 *     event's target's position (if available), otherwise (0, 0).
 * @param {?Object=} opt_eventProperties Event properties to be mixed into the
 *     BrowserEvent.
 * @return {boolean} false if preventDefault() was called, true otherwise.
 */
jsaction.testing.nativeEvents.fireMouseUpEvent = function(
    target, opt_button, opt_coords, opt_eventProperties) {
  return jsaction.testing.nativeEvents.fireMouseButtonEvent_(
      goog.events.EventType.MOUSEUP, target, opt_button, opt_coords,
      opt_eventProperties);
};

/**
 * Simulates a click event on the given target.
 * @param {!EventTarget} target The target for the event.
 * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
 *     defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
 * @param {?goog.math.Coordinate=} opt_coords Mouse position. Defaults to
 *     event's target's position (if available), otherwise (0, 0).
 * @param {?Object=} opt_eventProperties Event properties to be mixed into the
 *     BrowserEvent.
 * @return {boolean} false if preventDefault() was called, true otherwise.
 */
jsaction.testing.nativeEvents.fireClickEvent =
    function(target, opt_button, opt_coords, opt_eventProperties) {
  return jsaction.testing.nativeEvents.fireMouseButtonEvent_(
      goog.events.EventType.CLICK, target, opt_button, opt_coords,
      opt_eventProperties);
};

/**
 * Simulates a mouseover event on the given target.
 * @param {!EventTarget} target The target for the event.
 * @return {boolean} false if preventDefault() was called, true otherwise.
 */
jsaction.testing.nativeEvents.fireMouseOverEvent = function(target) {
  return jsaction.testing.nativeEvents.fireMouseButtonEvent_(
      goog.events.EventType.MOUSEOVER, target);
};

/**
 * Simulates a mouseout event on the given target.
 * @param {!EventTarget} target The target for the event.
 * @return {boolean} false if preventDefault() was called, true otherwise.
 */
jsaction.testing.nativeEvents.fireMouseOutEvent = function(target) {
  return jsaction.testing.nativeEvents.fireMouseButtonEvent_(
      goog.events.EventType.MOUSEOUT, target);
};

/**
 * Simulates a mousemove event on the given target.
 * @param {!EventTarget} target The target for the event.
 * @param {?goog.math.Coordinate=} opt_coords Mouse position. Defaults to
 *     event's target's position (if available), otherwise (0, 0).
 * @return {boolean} The returnValue of the event: false if preventDefault() was
 *     called on it, true otherwise.
 */
jsaction.testing.nativeEvents.fireMouseMoveEvent = function(
    target, opt_coords) {
  var e = new goog.testing.events.Event(
      goog.events.EventType.MOUSEMOVE, target);
  jsaction.testing.nativeEvents.setEventClientXY_(e, opt_coords);
  return jsaction.triggerEvent(target, jsaction.createMouseEvent(e));
};

/**
 * Creates a mouse button event.
 * @param {string} type The event type.
 * @param {!EventTarget} target The target for the event.
 * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
 *     defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
 * @param {boolean=} opt_modifierKey Create the event with the modifier key
 *     registered as down.
 * @param {?goog.math.Coordinate=} opt_coords Mouse position. Defaults to
 *     event's target's position (if available), otherwise (0, 0).
 * @param {?Object=} opt_eventProperties Event properties to be mixed into the
 *     BrowserEvent.
 * @return {!Event} The created event.
 */
jsaction.testing.nativeEvents.createMouseButtonEvent = function(
    type, target, opt_button, opt_modifierKey, opt_coords,
    opt_eventProperties) {
  var e = new goog.testing.events.Event(type, target);
  e.button = opt_button || goog.events.BrowserEvent.MouseButton.LEFT;
  jsaction.testing.nativeEvents.setEventClientXY_(e, opt_coords);
  if (opt_eventProperties) {
    goog.object.extend(e, opt_eventProperties);
  }
  if (opt_modifierKey) {
    e.ctrlKey = true;
    e.metaKey = true;
  }
  return jsaction.createMouseEvent(e);
};

/**
 * A static helper function that sets the mouse position to the event.
 * @param {!Event} event A simulated native event.
 * @param {?goog.math.Coordinate=} opt_coords Mouse position. Defaults to
 *     event's target's position (if available), otherwise (0, 0).
 * @private
 */
jsaction.testing.nativeEvents.setEventClientXY_ = function(event, opt_coords) {
  if (!opt_coords && event.target &&
      event.target.nodeType == goog.dom.NodeType.ELEMENT) {
    try {
      opt_coords =
          goog.style.getClientPosition(/** @type {!Element} **/ (event.target));
    } catch (ex) {
      // IE sometimes throws if it can't get the position.
    }
  }
  event.clientX = opt_coords ? opt_coords.x : 0;
  event.clientY = opt_coords ? opt_coords.y : 0;

  // Pretend the browser window is at (0, 0).
  event.screenX = event.clientX;
  event.screenY = event.clientY;
};

/**
 * Helper function to fire a mouse event with a mouse button. IE < 9 only allows
 * firing events using the left mouse button.
 * @param {string} type The event type.
 * @param {!EventTarget} target The target for the event.
 * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
 *     defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
 * @param {?goog.math.Coordinate=} opt_coords Mouse position. Defaults to
 *     event's target's position (if available), otherwise (0, 0).
 * @param {?Object=} opt_eventProperties Event properties to be mixed into the
 *     BrowserEvent.
 * @return {boolean} The value returned by the browser event,
 *     which returns false iff 'preventDefault' was invoked.
 * @private
 */
jsaction.testing.nativeEvents.fireMouseButtonEvent_ = function(
    type, target, opt_button, opt_coords, opt_eventProperties) {
  var e = jsaction.testing.nativeEvents.createMouseButtonEvent(
      type, target, opt_button, undefined, opt_coords, opt_eventProperties);
  return jsaction.triggerEvent(target, e);
};

/**
 * Creates and initializes a key event.
 * @param {string} eventType The type of event to create ("keydown", "keyup",
 *     or "keypress").
 * @param {!HTMLElement} node The event target.
 * @param {number} keyCode The key code.
 * @param {number} charCode The character code produced by the key.
 * @return {!Object} an initialized event object.
 */
jsaction.testing.nativeEvents.fireKeyEvent = function(
    eventType, node, keyCode, charCode) {
  var e = new goog.testing.events.Event(eventType, node);
  e.charCode = charCode;
  e.keyCode = keyCode;
  var nativeEvent = jsaction.createKeyboardEvent(e);
  jsaction.triggerEvent(node, nativeEvent);
  return nativeEvent;
};

/**
 * Generates a series of events simulating a key press on the given element.
 * @param {!HTMLElement} node The event target.
 * @param {number} keyCode The key code.
 * @param {number} charCode The character code produced by the key.
 */
jsaction.testing.nativeEvents.simulateKeyPress = function(
    node, keyCode, charCode) {
  var e;

  e = jsaction.testing.nativeEvents.fireKeyEvent(
      goog.events.EventType.KEYDOWN, node, keyCode, charCode);
  e = jsaction.testing.nativeEvents.fireKeyEvent(
      goog.events.EventType.KEYPRESS, node, keyCode, charCode);
  e = jsaction.testing.nativeEvents.fireKeyEvent(
      goog.events.EventType.KEYUP, node, keyCode, charCode);
};
<|endoftext|>