<commit_before>import Character from './CharacterClass';

function getCaptainList(characters) {
  let list = [];
  characters.forEach(character => {
    if (character.captainEffect === true) {
      list.push(Character.get(character.no));
    }
  });
  return list;
}

function getSpecialAbilityList(characters) {
  let list = [];
  characters.forEach(character => {
    if (character.specialAbility !== true) {
      return;
    }

    const characterInfo = Character.get(character.no);
    if (characterInfo.specialAbility.magnification !== null) {
      list.push(characterInfo);
    }
  });
  return list;
}

function getTypeMagnification(character, enemy) {
  if (character.type === '力' && enemy.type === '技') return 2;
  if (character.type === '技' && enemy.type === '速') return 2;
  if (character.type === '速' && enemy.type === '力') return 2;
  if (character.type === '心' && enemy.type === '知') return 2;
  if (character.type === '知' && enemy.type === '心') return 2;
  if (character.type === '技' && enemy.type === '力') return 0.5;
  if (character.type === '速' && enemy.type === '技') return 0.5;
  if (character.type === '力' && enemy.type === '速') return 0.5;
  return 1;
}

function getCaptainMagnification(currentCharacterList, captainList) {
  let current = currentCharacterList.slice();   // Array copy
  const character = current.pop();
  const characterInfo = Character.get(character.no);
  const timingHistory = current.map(character => {
    return character.timing;
  });

  const magni = captainList.map(character => {
    return character.captainEffect.magnification;
  })
  .reduce((magni, fn) => {
    return magni * fn({
      character: characterInfo,
      timingHistory
    });
  }, 1);
  return Math.round(magni * 100) / 100;
}

function getSpecialMagnification(currentCharacterList, specialAbilityList) {
  let current = currentCharacterList.slice();   // Array copy
  const character = current.pop();
  const characterInfo = Character.get(character.no);

  const magni = specialAbilityList.map(character => {
    return character.specialAbility.magnification;
  })
  .reduce((magni, fn) => {
    return magni * fn({
      character: characterInfo,
      bead: character.bead
    })
  }, 1);
  return Math.round(magni * 100) / 100;
}

export default function attackAnalysis({ enemy, boat, characters }) {
  // get captains
  const captains = getCaptainList(characters);
  if (captains.length > 2) {
    throw new Error('It should not have more than 2 captain at the same time');
  }

  const specialAbilities = getSpecialAbilityList(characters);

  let analysis = [];
  let chainMagnification = 1;
  let total = {
    attack: 0,
    combo: 0
  };
  let current = [];
  characters.forEach(function(character) {
    current.push(character);
    const characterInfo = Character.get(character.no);

    const magnification = {
      boat,
      bead: character.bead,
      chain: chainMagnification,
      type: getTypeMagnification(characterInfo, enemy),
      captain: getCaptainMagnification(current, captains),
      special: getSpecialMagnification(current, specialAbilities)
    };

    let attack = {
      original: character.attack,
      basic: 0,
      singal: 0,
      timing: character.timing,
      combo: characterInfo.combo
    }

    // Basic Attack
    attack.basic = Math.round(
      character.attack
      * magnification.boat
      * magnification.type
      * magnification.captain
      * magnification.bead
      * magnification.chain
      * magnification.special
    );

    // Singal Attack
    attack.singal = Math.round(attack.basic / characterInfo.combo);   // 四捨五入

    // Timing
    let timingMagnification = 0;
    switch (character.timing) {
        case 'bad':     attack.combo -= 3;  chainMagnification =  1;    timingMagnification = 0;       break;
        case 'good':    attack.combo -= 3;  chainMagnification += 0;    timingMagnification = 0.3;     break;
        case 'great':   attack.combo -= 1;  chainMagnification += 0.1;  timingMagnification = 0.5939;  break;
        case 'perfect': attack.combo -= 0;  chainMagnification += 0.3;  timingMagnification = 0.8999;  break;
        case 'miss':    attack.combo -= 0;  chainMagnification =  1;    timingMagnification = 0;       break;
    }
    chainMagnification = Math.round(chainMagnification * 10) / 10;

    // Damage
    let damage = {
      singal: attack.singal - enemy.defense,
      final: Math.round(attack.singal * (characterInfo.combo * timingMagnification + 1) - enemy.defense),
      history: [],
      total: 0
    };
    if (damage.singal < 1) damage.singal = 1;
    if (damage.final < 1)  damage.final  = 1;

    // Total Damage
    for (let i = 0; i < attack.combo; i++) {
        damage.total += damage.singal;
        damage.history.push(damage.total);
    }
    damage.total += damage.final - damage.singal;

    // Total
    total.attack += damage.total;
    total.combo += attack.combo;

    // add stucture to analysis
    analysis.push({
      character: characterInfo,
      magnification,
      attack,
      damage,
      total: {
        attack: total.attack,
        combo: total.combo
      }
    });
  });

  return { captains, specialAbilities, analysis, total };
}
<commit_msg>[lib] implement subfunction getFinalAttack<commit_after>import Character from './CharacterClass';

function getCaptainList(characters) {
  let list = [];
  characters.forEach(character => {
    if (character.captainEffect === true) {
      list.push(Character.get(character.no));
    }
  });
  return list;
}

function getSpecialAbilityList(characters) {
  let list = [];
  characters.forEach(character => {
    if (character.specialAbility !== true) {
      return;
    }

    const characterInfo = Character.get(character.no);
    if (characterInfo.specialAbility.magnification !== null) {
      list.push(characterInfo);
    }
  });
  return list;
}

function getTypeMagnification(character, enemy) {
  if (character.type === '力' && enemy.type === '技') return 2;
  if (character.type === '技' && enemy.type === '速') return 2;
  if (character.type === '速' && enemy.type === '力') return 2;
  if (character.type === '心' && enemy.type === '知') return 2;
  if (character.type === '知' && enemy.type === '心') return 2;
  if (character.type === '技' && enemy.type === '力') return 0.5;
  if (character.type === '速' && enemy.type === '技') return 0.5;
  if (character.type === '力' && enemy.type === '速') return 0.5;
  return 1;
}

function getCaptainMagnification(currentCharacterList, captainList) {
  let current = currentCharacterList.slice();   // Array copy
  const character = current.pop();
  const characterInfo = Character.get(character.no);
  const timingHistory = current.map(character => {
    return character.timing;
  });

  const magni = captainList.map(character => {
    return character.captainEffect.magnification;
  })
  .reduce((magni, fn) => {
    return magni * fn({
      character: characterInfo,
      timingHistory
    });
  }, 1);
  return Math.round(magni * 100) / 100;
}

function getSpecialMagnification(currentCharacterList, specialAbilityList) {
  let current = currentCharacterList.slice();   // Array copy
  const character = current.pop();
  const characterInfo = Character.get(character.no);

  const magni = specialAbilityList.map(character => {
    return character.specialAbility.magnification;
  })
  .reduce((magni, fn) => {
    return magni * fn({
      character: characterInfo,
      bead: character.bead
    })
  }, 1);
  return Math.round(magni * 100) / 100;
}

function getFinalAttack(basicAttack, combo, timing) {
  let magni = 0;
  if (timing === 'good')    magni = 0.3;
  if (timing === 'great')   magni = 0.5939;
  if (timing === 'perfect') magni = 0.8999;

  const final = (basicAttack / combo) * (combo * magni + 1)
  return Math.round(final);
}

export default function attackAnalysis({ enemy, boat, characters }) {
  // get captains
  const captains = getCaptainList(characters);
  if (captains.length > 2) {
    throw new Error('It should not have more than 2 captain at the same time');
  }

  const specialAbilities = getSpecialAbilityList(characters);

  let analysis = [];
  let chainMagnification = 1;
  let total = {
    attack: 0,
    combo: 0
  };
  let current = [];
  characters.forEach(function(character) {
    current.push(character);
    const characterInfo = Character.get(character.no);

    const magnification = {
      boat,
      bead: character.bead,
      chain: chainMagnification,
      type: getTypeMagnification(characterInfo, enemy),
      captain: getCaptainMagnification(current, captains),
      special: getSpecialMagnification(current, specialAbilities)
    };

    // Basic Attack
    const basic = Math.round(
      character.attack
      * magnification.boat
      * magnification.type
      * magnification.captain
      * magnification.bead
      * magnification.chain
      * magnification.special
    );

    let attack = {
      original: character.attack,
      basic: basic,
      singal: Math.round(basic / characterInfo.combo),
      final: getFinalAttack(basic, characterInfo.combo, character.timing),
      timing: character.timing,
      combo: characterInfo.combo
    };

    // Timing
    switch (character.timing) {
        case 'bad':     attack.combo -= 3;  chainMagnification =  1;    break;
        case 'good':    attack.combo -= 3;  chainMagnification += 0;    break;
        case 'great':   attack.combo -= 1;  chainMagnification += 0.1;  break;
        case 'perfect': attack.combo -= 0;  chainMagnification += 0.3;  break;
        case 'miss':    attack.combo -= 0;  chainMagnification =  1;    break;
    }
    chainMagnification = Math.round(chainMagnification * 10) / 10;

    // Damage
    let damage = {
      singal: attack.singal - enemy.defense,
      final: attack.final - enemy.defense,
      history: [],
      total: 0
    };
    if (damage.singal < 1) damage.singal = 1;
    if (damage.final < 1)  damage.final  = 1;

    // Total Damage
    for (let i = 0; i < attack.combo; i++) {
        damage.total += damage.singal;
        damage.history.push(damage.total);
    }
    damage.total += damage.final - damage.singal;

    // Total
    total.attack += damage.total;
    total.combo += attack.combo;

    // add stucture to analysis
    analysis.push({
      character: characterInfo,
      magnification,
      attack,
      damage,
      total: {
        attack: total.attack,
        combo: total.combo
      }
    });
  });

  return { captains, specialAbilities, analysis, total };
}
<|endoftext|>


<commit_before>/* eslint no-console: 0 */

const fs = require("fs");
const path = require("path");
const TelegramBot = require("node-telegram-bot-api");
const Log = require("./Log");
const Config = JSON.parse(fs.readFileSync("./config.json", "utf8"));
const PluginManager = require("./PluginManager");
const Auth = require("./helpers/Auth");
const auth = new Auth(Config);

if (typeof Config.TELEGRAM_TOKEN !== "string" || Config.TELEGRAM_TOKEN === "") {
    console.log("You must provide a Telegram bot token in config.json. Try running \"npm run firstrun\".");
    process.exit(1);
}

const log = Log.get("Bot", Config);

// Version reporting, useful for bug reports
let commit = "";
if (fs.existsSync(path.join(__dirname, "../.git")))
    commit = fs.readFileSync(path.join(__dirname, "../.git/refs/heads/es6"), "utf8").substr(0, 7);
log.info(`Telegram-Bot-Node version ${require('../package.json').version}` + (commit ? `, commit ${commit}` : ""));

log.verbose(`Creating a TelegramBot instance...`);
const bot = new TelegramBot(Config.TELEGRAM_TOKEN, {polling: true});
log.info("Instance created.");

log.verbose("Loading plugins...");
const pluginManager = new PluginManager(bot, Config, auth);
pluginManager.loadPlugins(Config.activePlugins);
log.info("Plugins loaded.");

log.verbose("Configuring permissions...");
log.info("Permissions configured.");
log.info("The bot is online!");

// If `CTRL+C` is pressed we stop the bot safely.
process.on("SIGINT", handleShutdown("SIGINT received"));

// Stop safely in case of `uncaughtException`.
process.on("uncaughtException", handleShutdown("Uncaught exception"));

function handleShutdown(reason) {
    return err => {
        if (err) log.error(err);
        log.warn("Shutting down, reason: " + reason);
        log.info("Stopping safely all the plugins...");
        pluginManager.stopPlugins().then(function() {
            log.info("All plugins stopped correctly.");
            process.exit();
        });
    };
}

process.on('unhandledRejection', (reason, p) => {
    log.error("Unhandled rejection at Promise ", p, " with reason ", reason);
});<commit_msg>Use synchronization method for plugin manager<commit_after>/* eslint no-console: 0 */

const fs = require("fs");
const path = require("path");
const TelegramBot = require("node-telegram-bot-api");
const Log = require("./Log");
const Config = JSON.parse(fs.readFileSync("./config.json", "utf8"));
const PluginManager = require("./PluginManager");
const Auth = require("./helpers/Auth");
const auth = new Auth(Config);

if (typeof Config.TELEGRAM_TOKEN !== "string" || Config.TELEGRAM_TOKEN === "") {
    console.log("You must provide a Telegram bot token in config.json. Try running \"npm run firstrun\".");
    process.exit(1);
}

const log = Log.get("Bot", Config);

// Version reporting, useful for bug reports
let commit = "";
if (fs.existsSync(path.join(__dirname, "../.git")))
    commit = fs.readFileSync(path.join(__dirname, "../.git/refs/heads/es6"), "utf8").substr(0, 7);
log.info(`Telegram-Bot-Node version ${require('../package.json').version}` + (commit ? `, commit ${commit}` : ""));

log.verbose(`Creating a TelegramBot instance...`);
const bot = new TelegramBot(Config.TELEGRAM_TOKEN, {polling: true});
log.info("Instance created.");

log.verbose("Loading plugins...");
const pluginManager = new PluginManager(bot, Config, auth);
pluginManager.loadPlugins(Config.activePlugins);
pluginManager.startSynchronization();
log.info("Plugins loaded.");

log.verbose("Configuring permissions...");
log.info("Permissions configured.");
log.info("The bot is online!");

// If `CTRL+C` is pressed we stop the bot safely.
process.on("SIGINT", handleShutdown("SIGINT received"));

// Stop safely in case of `uncaughtException`.
process.on("uncaughtException", handleShutdown("Uncaught exception"));

function handleShutdown(reason) {
    return err => {
        if (err) log.error(err);
        log.warn("Shutting down, reason: " + reason);
        log.info("Stopping safely all the plugins...");
        pluginManager.stopSynchronization();
        pluginManager.stopPlugins().then(function() {
            log.info("All plugins stopped correctly.");
            process.exit();
        });
    };
}

process.on('unhandledRejection', (reason, p) => {
    log.error("Unhandled rejection at Promise ", p, " with reason ", reason);
});<|endoftext|>


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

var flags = require('./gap/flags.js');
var uuid = require('./gap/uuid.js');
var localname = require('./gap/localname.js');
var txpower = require('./gap/txpower.js');
var slaveconnectionintervalrange = require('./gap/slaveconnectionintervalrange.js');
var solicitation = require('./gap/solicitation.js');
var servicedata = require('./gap/servicedata.js');
var genericdata = require('./gap/genericdata.js');
var manufacturerspecificdata = require('./gap/manufacturerspecificdata.js');

/**
 * Convert a raw Bluetooth Low Energy advertiser header into its meaningful
 * parts.
 * https://www.bluetooth.org/en-us/specification/assigned-numbers/generic-access-profile
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @return {Object} Semantically meaningful advertiser header.
 */
function process(payload) {
  var advertiserDataLength = payload.length;
  var cursor = 0;
  var advertiserData = {};
  while(cursor < advertiserDataLength) {
    var length = (parseInt(payload.substr(cursor,2),16) + 1) * 2;
    var tag = payload.substr(cursor+2,2);
    switch(tag) {
      case("01"):
        flags.process(payload, cursor, advertiserData);
        break;
      case("02"):
         uuid.nonComplete16BitUUIDs.process(payload, cursor, advertiserData);
        break;
      case("03"):
        uuid.complete16BitUUIDs(payload, cursor, advertiserData);
        break;
      case("04"):
        genericdata.process(payload, cursor, advertiserData, "nonComplete32BitUUIDs");
        break;
      case("05"):
        genericdata.process(payload, cursor, advertiserData, "complete32BitUUIDs");
        break;
      case("06"):
        uuid.nonComplete128BitUUIDs(payload, cursor, advertiserData);
        break;
      case("07"):
        uuid.complete128BitUUIDs(payload, cursor, advertiserData);
        break;
      case("08"):
        localname.shortenedLocalName(payload, cursor, advertiserData);
        break;
      case("09"):
        localname.completeLocalName(payload, cursor, advertiserData);
        break;
      case("0a"):
        txpower.process(payload, cursor, advertiserData);
        break;
      case("0d"):
        genericdata.process(payload, cursor, advertiserData, "classOfDevice");
        break;
      case("0e"):
        genericdata.process(payload, cursor, advertiserData, "simplePairingHashC");
        break;
      case("0f"):
        genericdata.process(payload, cursor, advertiserData,
                         "simplePairingRandomizerR");
        break;
      case("10"):
        genericdata.process(payload, cursor, advertiserData,
                         "securityManagerTKValue");
        break;
      case("11"):
        genericdata.process(payload, cursor, advertiserData,
                         "securityManagerOOBFlags");
        break;
      case("12"):
        slaveconnectionintervalrange.process(payload, cursor, advertiserData);
        break;
      case("14"):
        solicitation.solicitation16BitUUIDs(payload, cursor, advertiserData);
        break;
      case("15"):
        solicitation.licitation128BitUUIDs(payload, cursor, advertiserData);(payload, cursor, advertiserData);
        break;
      case("16"):
        servicedata.process(payload, cursor, advertiserData);
        break;
      case("17"):
        genericdata.process(payload, cursor, advertiserData,
                         "publicTargetAddress");
        break;
      case("18"):
        genericdata.process(payload, cursor, advertiserData,
                         "randomTargetAddress");
        break;
      case("19"):
        genericdata.process(payload, cursor, advertiserData, "appearance");
        break;
      case("1a"):
        genericdata.process(payload, cursor, advertiserData,
                         "advertisingInterval");
        break;
      case("1b"):
        genericdata.process(payload, cursor, advertiserData,
                         "leBluetoothDeviceAddress");
        break;
      case("1c"):
        genericdata.process(payload, cursor, advertiserData, "leRole");
        break;
      case("1d"):
        genericdata.process(payload, cursor, advertiserData,
                         "simplePairingHashC256");
        break;
      case("1e"):
        genericdata.process(payload, cursor, advertiserData,
                         "simplePairingRandomizerR256");
        break;
      case("1f"):
        genericdata.process(payload, cursor, advertiserData,
                         "solicitation32BitUUIDs");
        break;
      case("3d"):
        genericdata.process(payload, cursor, advertiserData,
                         "informationData3D");
        break;
      case("ff"):
        manufacturerspecificdata.process(payload, cursor, advertiserData);
        break;
      default:
        //console.log("Unhandled BLE tag " + tag);
    }
    cursor += length;
  }
  return advertiserData;
}

module.exports.process = process;<commit_msg>Corrected copy-paste error on solicitation128BitUUIDs and corrected indents<commit_after>/**
 * Copyright reelyActive 2015
 * We believe in an open Internet of Things
 */

var flags = require('./gap/flags.js');
var uuid = require('./gap/uuid.js');
var localname = require('./gap/localname.js');
var txpower = require('./gap/txpower.js');
var slaveconnectionintervalrange =
                             require('./gap/slaveconnectionintervalrange.js');
var solicitation = require('./gap/solicitation.js');
var servicedata = require('./gap/servicedata.js');
var genericdata = require('./gap/genericdata.js');
var manufacturerspecificdata = require('./gap/manufacturerspecificdata.js');

/**
 * Convert a raw Bluetooth Low Energy advertising packet into its meaningful
 * parts.
 * https://www.bluetooth.org/en-us/specification/assigned-numbers/generic-access-profile
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @return {Object} Semantically meaningful advertising packet.
 */
function process(payload) {
  var advertiserDataLength = payload.length;
  var cursor = 0;
  var advertiserData = {};
  while(cursor < advertiserDataLength) {
    var length = (parseInt(payload.substr(cursor,2),16) + 1) * 2;
    var tag = payload.substr(cursor + 2, 2);
    switch(tag) {
      case("01"):
        flags.process(payload, cursor, advertiserData);
        break;
      case("02"):
         uuid.nonComplete16BitUUIDs.process(payload, cursor, advertiserData);
        break;
      case("03"):
        uuid.complete16BitUUIDs(payload, cursor, advertiserData);
        break;
      case("04"):
        genericdata.process(payload, cursor, advertiserData,
                            "nonComplete32BitUUIDs");
        break;
      case("05"):
        genericdata.process(payload, cursor, advertiserData,
                            "complete32BitUUIDs");
        break;
      case("06"):
        uuid.nonComplete128BitUUIDs(payload, cursor, advertiserData);
        break;
      case("07"):
        uuid.complete128BitUUIDs(payload, cursor, advertiserData);
        break;
      case("08"):
        localname.shortenedLocalName(payload, cursor, advertiserData);
        break;
      case("09"):
        localname.completeLocalName(payload, cursor, advertiserData);
        break;
      case("0a"):
        txpower.process(payload, cursor, advertiserData);
        break;
      case("0d"):
        genericdata.process(payload, cursor, advertiserData, "classOfDevice");
        break;
      case("0e"):
        genericdata.process(payload, cursor, advertiserData,
                            "simplePairingHashC");
        break;
      case("0f"):
        genericdata.process(payload, cursor, advertiserData,
                            "simplePairingRandomizerR");
        break;
      case("10"):
        genericdata.process(payload, cursor, advertiserData,
                            "securityManagerTKValue");
        break;
      case("11"):
        genericdata.process(payload, cursor, advertiserData,
                            "securityManagerOOBFlags");
        break;
      case("12"):
        slaveconnectionintervalrange.process(payload, cursor, advertiserData);
        break;
      case("14"):
        solicitation.solicitation16BitUUIDs(payload, cursor, advertiserData);
        break;
      case("15"):
        solicitation.solicitation128BitUUIDs(payload, cursor, advertiserData);
        break;
      case("16"):
        servicedata.process(payload, cursor, advertiserData);
        break;
      case("17"):
        genericdata.process(payload, cursor, advertiserData,
                            "publicTargetAddress");
        break;
      case("18"):
        genericdata.process(payload, cursor, advertiserData,
                            "randomTargetAddress");
        break;
      case("19"):
        genericdata.process(payload, cursor, advertiserData, "appearance");
        break;
      case("1a"):
        genericdata.process(payload, cursor, advertiserData,
                            "advertisingInterval");
        break;
      case("1b"):
        genericdata.process(payload, cursor, advertiserData,
                            "leBluetoothDeviceAddress");
        break;
      case("1c"):
        genericdata.process(payload, cursor, advertiserData, "leRole");
        break;
      case("1d"):
        genericdata.process(payload, cursor, advertiserData,
                            "simplePairingHashC256");
        break;
      case("1e"):
        genericdata.process(payload, cursor, advertiserData,
                            "simplePairingRandomizerR256");
        break;
      case("1f"):
        genericdata.process(payload, cursor, advertiserData,
                            "solicitation32BitUUIDs");
        break;
      case("3d"):
        genericdata.process(payload, cursor, advertiserData,
                            "informationData3D");
        break;
      case("ff"):
        manufacturerspecificdata.process(payload, cursor, advertiserData);
        break;
      default:
        //console.log("Unhandled BLE tag " + tag);
    }
    cursor += length;
  }
  return advertiserData;
}

module.exports.process = process;
<|endoftext|>


<commit_before>/**
 * @fileoverview Apply the middleware pattern to a given Object.
 */

const __ = require('lodash');

const middlewarify = (module.exports = {});

const noopMidd = function(cb) {
  if (__.isFunction(cb)) {
    cb();
  }
};

/** @enum {string} middleware types */
middlewarify.Type = {
  BEFORE: 'before',
  AFTER: 'after',
  LAST: 'last',
  USE: 'use',
};

/**
 * Apply the middleware pattern to the provided object's propert.
 *
 * @param {Object} obj An Object.
 * @param {string} prop The property to apply the middleware pattern on.
 * @param {Function=} optFinalCb Last middleware to call.
 * @param {Object=} optParams Optional parameters.
 * @param {boolean=} optParams.beforeAfter set to true to add Before/After hooks
 *     instead of the single use hook.
 * @param {Function=} optParams.catchAll Error catchall function.
 * @param {boolean=} optParams.async Set to true to enable async mode.
 */
middlewarify.make = function(obj, prop, optFinalCb, optParams) {
  const middObj = middlewarify.newMidd();

  if (__.isFunction(optFinalCb)) {
    middObj.mainCallback = optFinalCb;
    middObj.mainCallback.isMain = true;
  }

  let params;
  if (__.isObject(optFinalCb)) {
    params = optFinalCb;
  }
  if (__.isObject(optParams)) {
    params = optParams;
  }

  /**
   * The default parameters object.
   *
   * @type {Object}
   */
  const defaultParams = {
    beforeAfter: false,
    catchAll: null,
    async: false,
  };
  middObj.params = __.extend(defaultParams, params);

  obj[prop] = middlewarify._invokeMiddleware.bind(null, middObj);

  if (middObj.params.beforeAfter) {
    middObj.beforeMidds = [];
    middObj.afterMidds = [];
    middObj.lastMidds = [];
    obj[prop].before = middlewarify._use.bind(
      null,
      middObj,
      middlewarify.Type.BEFORE,
    );
    obj[prop].after = middlewarify._use.bind(
      null,
      middObj,
      middlewarify.Type.AFTER,
    );
    obj[prop].last = middlewarify._use.bind(
      null,
      middObj,
      middlewarify.Type.LAST,
    );
  } else {
    middObj.midds = [];
    obj[prop].use = middlewarify._use.bind(
      null,
      middObj,
      middlewarify.Type.USE,
    );
  }
};

/**
 * Create and initialize a new Middleware Object.
 *
 * @return {Object} A new Middleware Object.
 */
middlewarify.newMidd = function() {
  const middObj = Object.create(null);
  middObj.mainCallback = noopMidd;
  middObj.mainCallback.isMain = true;

  return middObj;
};

/**
 * Invokes all the middleware.
 *
 * @param  {Object} middObj Internal midd object.
 * @param  {...*} args Any number of arguments
 * @return {*|Promise} Middleware value or A promise.
 * @private
 */
middlewarify._invokeMiddleware = (middObj, ...args) => {
  const midds = middlewarify._prepareMiddleware(middObj);

  const invokeState = {
    mainCallbackReturnValue: null,
  };
  if (middObj.params.async === true) {
    try {
      return middlewarify
        ._asyncShiftAndInvoke(midds, args, invokeState)
        .catch(middlewarify._handleInvokeError.bind(null, middObj));
    } catch (ex) {
      middlewarify._handleInvokeError(middObj, ex);
    }
  }

  try {
    return middlewarify._syncShiftAndInvoke(midds, args, invokeState);
  } catch (ex) {
    middlewarify._handleInvokeError(middObj, ex);
  }
};

/**
 * Handles invokation error, will check if a catchAll exists.
 *
 * @param {Object} middObj Internal middleware state.
 * @param {Error} ex Error cought.
 * @throws {Error} if no error catchAll was found.
 * @private
 */
middlewarify._handleInvokeError = (middObj, ex) => {
  if (typeof middObj.params.catchAll === 'function') {
    middObj.params.catchAll(ex);
  } else {
    throw ex;
  }
};

/**
 * Prepares the sequence of middleware to be invoked and returns them in
 * order of invocation in an array.
 *
 * @param {Object} middObj Internal middleware state.
 * @return {Array.<Function>} The middleware to be invoked in sequence.
 * @private
 */
middlewarify._prepareMiddleware = middObj => {
  let midds;
  if (middObj.params.beforeAfter) {
    midds = Array.prototype.slice.call(middObj.beforeMidds);
    midds.push(middObj.mainCallback);
    midds = midds.concat(middObj.afterMidds, middObj.lastMidds);
  } else {
    midds = Array.prototype.slice.call(middObj.midds);
    midds.push(middObj.mainCallback);
  }

  return midds;
};

/**
 * SYNCHRONOUS & RECURSIVE.
 * Shifts one middleware from the array ensuring FIFO and invokes it.
 *
 * @param {Array.<Function>} midds The middleware.
 * @param {Array} args An array of arbitrary arguments, can be empty.
 * @param {Object} invokeState The current invocation state.
 * @param {boolean=} optAfter If next middleware is after the main callback.
 * @return {Promise} A promise.
 * @private
 */
middlewarify._syncShiftAndInvoke = function(
  midds,
  args,
  invokeState,
  optAfter,
) {
  if (!midds.length) {
    return invokeState.mainCallbackReturnValue;
  }

  let isAfter = !!optAfter;

  const midd = midds.shift();

  const retVal = midd(...args);

  // If a function is of type "after" (invoked after the main fn)
  // then we use its return value -if one exists- as the value to be returned
  // for the entire middleware invocation.
  if (isAfter && typeof val !== 'undefined') {
    invokeState.mainCallbackReturnValue = retVal;
    args.splice(-1, 1, retVal);
  }

  if (midd.isMain) {
    invokeState.mainCallbackReturnValue = retVal;
    args.push(retVal);
    isAfter = true;
  }

  middlewarify._syncShiftAndInvoke(midds, args, invokeState, isAfter);
};

/**
 * ASYNCHRONOUS & RECURSIVE
 * Shifts one middleware from the array ensuring FIFO and invokes it.
 *
 * @param {Array.<Function>} midds The middleware.
 * @param {Array} args An array of arbitrary arguments, can be empty.
 * @param {Object} invokeState The current invocation state.
 * @param {boolean=} optAfter If next middleware is after the main callback.
 * @return {Promise} A promise with the ultimate response.
 * @private
 */
middlewarify._asyncShiftAndInvoke = async function(
  midds,
  args,
  invokeState,
  optAfter,
) {
  if (!midds.length) {
    return invokeState.mainCallbackReturnValue;
  }

  let isAfter = !!optAfter;

  const midd = midds.shift();

  const retVal = await midd(...args);

  // If a function is of type "after" (invoked after the main fn)
  // then we use its return value -if it exists- as the value to be returned
  // for the entire middleware invocation.
  if (isAfter && typeof val !== 'undefined') {
    invokeState.mainCallbackReturnValue = retVal;
    args.splice(-1, 1, retVal);
  }

  if (midd.isMain) {
    invokeState.mainCallbackReturnValue = retVal;
    args.push(retVal);
    isAfter = true;
  }

  return middlewarify._asyncShiftAndInvoke(midds, args, invokeState, isAfter);
};

/**
 * Add middleware.
 *
 * @param {Object} middObj Internal midd object.
 * @param {middlewarify.Type} middType Middleware type.
 * @param {Function|Array.<Function>...} middlewares Any combination of
 *    function containers.
 * @private
 */
middlewarify._use = function(middObj, middType, ...middlewares) {
  const len = middlewares.length;
  if (len === 0) {
    return;
  }

  /**
   * @param {Function} fn Middleware function.
   */
  function pushMidd(fn) {
    switch (middType) {
      case middlewarify.Type.BEFORE:
        middObj.beforeMidds.push(fn);
        break;
      case middlewarify.Type.AFTER:
        middObj.afterMidds.push(fn);
        break;
      case middlewarify.Type.LAST:
        middObj.lastMidds.push(fn);
        break;
      case middlewarify.Type.USE:
        middObj.midds.push(fn);
        break;
      default:
        break;
    }
  }

  middlewares.forEach(function(middleware) {
    if (Array.isArray(middleware)) {
      middleware.forEach(function(argFn) {
        if (__.isFunction(argFn)) {
          pushMidd(argFn);
        }
      });
    } else if (__.isFunction(middleware)) {
      pushMidd(middleware);
    }
  });
};
<commit_msg>fix issues found with sync beforeAfter invocation<commit_after>/**
 * @fileoverview Apply the middleware pattern to a given Object.
 */

const __ = require('lodash');

const middlewarify = (module.exports = {});

const noopMidd = function(cb) {
  if (__.isFunction(cb)) {
    cb();
  }
};

/** @enum {string} middleware types */
middlewarify.Type = {
  BEFORE: 'before',
  AFTER: 'after',
  LAST: 'last',
  USE: 'use',
};

/**
 * Apply the middleware pattern to the provided object's propert.
 *
 * @param {Object} obj An Object.
 * @param {string} prop The property to apply the middleware pattern on.
 * @param {Function=} optFinalCb Last middleware to call.
 * @param {Object=} optParams Optional parameters.
 * @param {boolean=} optParams.beforeAfter set to true to add Before/After hooks
 *     instead of the single use hook.
 * @param {Function=} optParams.catchAll Error catchall function.
 * @param {boolean=} optParams.async Set to true to enable async mode.
 */
middlewarify.make = function(obj, prop, optFinalCb, optParams) {
  const middObj = middlewarify.newMidd();

  if (__.isFunction(optFinalCb)) {
    middObj.mainCallback = optFinalCb;
    middObj.mainCallback.isMain = true;
  }

  let params;
  if (__.isObject(optFinalCb)) {
    params = optFinalCb;
  }
  if (__.isObject(optParams)) {
    params = optParams;
  }

  /**
   * The default parameters object.
   *
   * @type {Object}
   */
  const defaultParams = {
    beforeAfter: false,
    catchAll: null,
    async: false,
  };
  middObj.params = __.extend(defaultParams, params);

  obj[prop] = middlewarify._invokeMiddleware.bind(null, middObj);

  if (middObj.params.beforeAfter) {
    middObj.beforeMidds = [];
    middObj.afterMidds = [];
    middObj.lastMidds = [];
    obj[prop].before = middlewarify._use.bind(
      null,
      middObj,
      middlewarify.Type.BEFORE,
    );
    obj[prop].after = middlewarify._use.bind(
      null,
      middObj,
      middlewarify.Type.AFTER,
    );
    obj[prop].last = middlewarify._use.bind(
      null,
      middObj,
      middlewarify.Type.LAST,
    );
  } else {
    middObj.midds = [];
    obj[prop].use = middlewarify._use.bind(
      null,
      middObj,
      middlewarify.Type.USE,
    );
  }
};

/**
 * Create and initialize a new Middleware Object.
 *
 * @return {Object} A new Middleware Object.
 */
middlewarify.newMidd = function() {
  const middObj = Object.create(null);
  middObj.mainCallback = noopMidd;
  middObj.mainCallback.isMain = true;

  return middObj;
};

/**
 * Invokes all the middleware.
 *
 * @param  {Object} middObj Internal midd object.
 * @param  {...*} args Any number of arguments
 * @return {*|Promise} Middleware value or A promise.
 * @private
 */
middlewarify._invokeMiddleware = (middObj, ...args) => {
  const midds = middlewarify._prepareMiddleware(middObj);

  const invokeState = {
    mainCallbackReturnValue: null,
  };
  if (middObj.params.async === true) {
    try {
      return middlewarify
        ._asyncShiftAndInvoke(midds, args, invokeState)
        .catch(middlewarify._handleInvokeError.bind(null, middObj));
    } catch (ex) {
      middlewarify._handleInvokeError(middObj, ex);
    }
  }

  try {
    return middlewarify._syncShiftAndInvoke(midds, args, invokeState);
  } catch (ex) {
    middlewarify._handleInvokeError(middObj, ex);
  }
};

/**
 * Handles invokation error, will check if a catchAll exists.
 *
 * @param {Object} middObj Internal middleware state.
 * @param {Error} ex Error cought.
 * @throws {Error} if no error catchAll was found.
 * @private
 */
middlewarify._handleInvokeError = (middObj, ex) => {
  if (typeof middObj.params.catchAll === 'function') {
    middObj.params.catchAll(ex);
  } else {
    throw ex;
  }
};

/**
 * Prepares the sequence of middleware to be invoked and returns them in
 * order of invocation in an array.
 *
 * @param {Object} middObj Internal middleware state.
 * @return {Array.<Function>} The middleware to be invoked in sequence.
 * @private
 */
middlewarify._prepareMiddleware = middObj => {
  let midds;
  if (middObj.params.beforeAfter) {
    midds = Array.prototype.slice.call(middObj.beforeMidds);
    midds.push(middObj.mainCallback);
    midds = midds.concat(middObj.afterMidds, middObj.lastMidds);
  } else {
    midds = Array.prototype.slice.call(middObj.midds);
    midds.push(middObj.mainCallback);
  }

  return midds;
};

/**
 * SYNCHRONOUS & RECURSIVE.
 * Shifts one middleware from the array ensuring FIFO and invokes it.
 *
 * @param {Array.<Function>} midds The middleware.
 * @param {Array} args An array of arbitrary arguments, can be empty.
 * @param {Object} invokeState The current invocation state.
 * @param {boolean=} optAfter If next middleware is after the main callback.
 * @return {Promise} A promise.
 * @private
 */
middlewarify._syncShiftAndInvoke = function(
  midds,
  args,
  invokeState,
  optAfter,
) {
  if (!midds.length) {
    return invokeState.mainCallbackReturnValue;
  }

  let isAfter = !!optAfter;

  const midd = midds.shift();

  const retVal = midd(...args);

  // If a function is of type "after" (invoked after the main fn)
  // then we use its return value -if one exists- as the value to be returned
  // for the entire middleware invocation.
  if (isAfter && typeof retVal !== 'undefined') {
    invokeState.mainCallbackReturnValue = retVal;
    args.splice(-1, 1, retVal);
  }

  if (midd.isMain) {
    invokeState.mainCallbackReturnValue = retVal;
    args.push(retVal);
    isAfter = true;
  }

  return middlewarify._syncShiftAndInvoke(midds, args, invokeState, isAfter);
};

/**
 * ASYNCHRONOUS & RECURSIVE
 * Shifts one middleware from the array ensuring FIFO and invokes it.
 *
 * @param {Array.<Function>} midds The middleware.
 * @param {Array} args An array of arbitrary arguments, can be empty.
 * @param {Object} invokeState The current invocation state.
 * @param {boolean=} optAfter If next middleware is after the main callback.
 * @return {Promise} A promise with the ultimate response.
 * @private
 */
middlewarify._asyncShiftAndInvoke = async function(
  midds,
  args,
  invokeState,
  optAfter,
) {
  if (!midds.length) {
    return invokeState.mainCallbackReturnValue;
  }

  let isAfter = !!optAfter;

  const midd = midds.shift();

  const retVal = await midd(...args);

  // If a function is of type "after" (invoked after the main fn)
  // then we use its return value -if it exists- as the value to be returned
  // for the entire middleware invocation.
  if (isAfter && typeof val !== 'undefined') {
    invokeState.mainCallbackReturnValue = retVal;
    args.splice(-1, 1, retVal);
  }

  if (midd.isMain) {
    invokeState.mainCallbackReturnValue = retVal;
    args.push(retVal);
    isAfter = true;
  }

  return middlewarify._asyncShiftAndInvoke(midds, args, invokeState, isAfter);
};

/**
 * Add middleware.
 *
 * @param {Object} middObj Internal midd object.
 * @param {middlewarify.Type} middType Middleware type.
 * @param {Function|Array.<Function>...} middlewares Any combination of
 *    function containers.
 * @private
 */
middlewarify._use = function(middObj, middType, ...middlewares) {
  const len = middlewares.length;
  if (len === 0) {
    return;
  }

  /**
   * @param {Function} fn Middleware function.
   */
  function pushMidd(fn) {
    switch (middType) {
      case middlewarify.Type.BEFORE:
        middObj.beforeMidds.push(fn);
        break;
      case middlewarify.Type.AFTER:
        middObj.afterMidds.push(fn);
        break;
      case middlewarify.Type.LAST:
        middObj.lastMidds.push(fn);
        break;
      case middlewarify.Type.USE:
        middObj.midds.push(fn);
        break;
      default:
        break;
    }
  }

  middlewares.forEach(function(middleware) {
    if (Array.isArray(middleware)) {
      middleware.forEach(function(argFn) {
        if (__.isFunction(argFn)) {
          pushMidd(argFn);
        }
      });
    } else if (__.isFunction(middleware)) {
      pushMidd(middleware);
    }
  });
};
<|endoftext|>


<commit_before>/**
 * @overview Book-Generator Lib
 * @module bookGenerator
 * @author Dominik Sigmund
 * @version 0.1
 * @description Creates pdfs from Images
 * @memberof resilienz
 * @requires module:fs
 * @requires module:sync
 * @requires module:child_process
 * @requires bin:imagemagick
 */
 var fs = require('fs')
 var async = require('async')
 const execSync = require('child_process').execSync
 function BookGenerator (layouter, pagespath, bookpath, bookpages) {
   var self = {}
   self.pagespath = pagespath
   self.bookpath = bookpath
   self.pages = bookpages
   self.layouter = layouter
   self.createBook = createBook
   self.getBook = getBook
   return self
 }

 function createBook (actionid, language, callback) {
   var self = this
   fs.readdir(self.pagespath + '/' + actionid, function (error, files) {
     if (error) {
       fs.mkdir(self.pagespath + '/' + actionid, function (error) {
         if (error) {
           callback(error)
         } else {
           fs.readdir(self.pagespath + '/' + actionid, function (error, files) {
             if (error) {
               callback(error)
             } else {
               composeFiles(self.layouter, self.pagespath, actionid, language, self.pages, function (error, parm) {
                 if (error) {
                   callback(error)
                 } else {
                   makePDF(self.bookpath, parm, actionid, function (error) {
                     if (error) {
                       callback(error)
                     } else {
                       callback(null, self.bookpath + '/' + actionid + '.pdf')
                     }
                   })
                 }
               })
             }
           })
         }
       })
     } else {
       composeFiles(self.layouter, self.pagespath, actionid, language, self.pages, function (error, parm) {
         if (error) {
           callback(error)
         } else {
           makePDF(self.bookpath, parm, actionid, function (error) {
             if (error) {
               callback(error)
             } else {
               callback(null, self.bookpath + '/' + actionid + '.pdf')
             }
           })
         }
       })
     }
   })
 }

 function createPage (layouter, actionid, language, pagespath, page, callback) {
   var split = false
   var target = ''
   if (page === 1 || page === 44) {
     target = pagespath + '/' + actionid + '/' + page + '.png'
   } else {
     target = pagespath + '/' + actionid + '/' + page + '_two.png'
     split = true
   }
   var parameter = ''
   layouter.createPage(actionid, language, page, function () {
     if (split) {
       // convert -crop 720x1040 image.png cropped_%d.png
       execSync('convert -crop 720x1040 ' + target + ' /tmp/splitted_$d.png') // to tmp
       execSync('mv /tmp/splitted_1.png ' + pagespath + '/' + actionid + '/' + page + '.png')
       execSync('mv /tmp/splitted_2.png ' + pagespath + '/' + actionid + '/' + (page + 1) + '.png')
       parameter = pagespath + '/' + actionid + '/' + page + '.png'
       parameter += pagespath + '/' + actionid + '/' + (page + 1) + '.png'
     } else {
       parameter += target
     }
     callback(null, parameter)
   })
 }

 function composeFiles (layouter, pagespath, actionid, language, maxPages, callback) {
   var listOfTasks = []
   for (var i = 1; i <= maxPages; i++) {
     if (i !== 1 && i % 2 !== 0) continue
     var page = i
     listOfTasks.push(function (callback) { createPage(layouter, actionid, language, pagespath, page, callback) })
   }
   async.series(listOfTasks, function (error, parameters) {
     if (error) {
       callback(error)
     } else {
       callback(null, parameters.join(' '))
     }
   })
 }

 function makePDF (bookpath, parameter, actionid, callback) {
   var cmd = 'convert ' + parameter + '-compress Zip ' + bookpath + '/' + actionid + '.pdf'
   console.log(cmd)
   var output = execSync(cmd, true)
   if (output.stderr || output.stdout) {
     console.log(output.stdout)
     console.error(output.stderr)
     callback(output)
   } else {
     callback(null)
   }
 }

 function getBook (actionid, callback) {
   var self = this
   fs.access(self.bookpath + '/' + actionid + '.pdf', fs.constants.R_OK | fs.constants.W_OK, function (error) {
     if (error) {
       self.createBook(actionid, function (error) {
         if (error) {
           callback(error)
         } else {
           callback(null, self.bookpath + '/' + actionid + '.pdf')
         }
       })
     } else {
       callback(null, self.bookpath + '/' + actionid + '.pdf')
     }
   })
 }

 module.exports = BookGenerator
<commit_msg>script-error (space missing)<commit_after>/**
 * @overview Book-Generator Lib
 * @module bookGenerator
 * @author Dominik Sigmund
 * @version 0.1
 * @description Creates pdfs from Images
 * @memberof resilienz
 * @requires module:fs
 * @requires module:sync
 * @requires module:child_process
 * @requires bin:imagemagick
 */
 var fs = require('fs')
 var async = require('async')
 const execSync = require('child_process').execSync
 function BookGenerator (layouter, pagespath, bookpath, bookpages) {
   var self = {}
   self.pagespath = pagespath
   self.bookpath = bookpath
   self.pages = bookpages
   self.layouter = layouter
   self.createBook = createBook
   self.getBook = getBook
   return self
 }

 function createBook (actionid, language, callback) {
   var self = this
   fs.readdir(self.pagespath + '/' + actionid, function (error, files) {
     if (error) {
       fs.mkdir(self.pagespath + '/' + actionid, function (error) {
         if (error) {
           callback(error)
         } else {
           fs.readdir(self.pagespath + '/' + actionid, function (error, files) {
             if (error) {
               callback(error)
             } else {
               composeFiles(self.layouter, self.pagespath, actionid, language, self.pages, function (error, parm) {
                 if (error) {
                   callback(error)
                 } else {
                   makePDF(self.bookpath, parm, actionid, function (error) {
                     if (error) {
                       callback(error)
                     } else {
                       callback(null, self.bookpath + '/' + actionid + '.pdf')
                     }
                   })
                 }
               })
             }
           })
         }
       })
     } else {
       composeFiles(self.layouter, self.pagespath, actionid, language, self.pages, function (error, parm) {
         if (error) {
           callback(error)
         } else {
           makePDF(self.bookpath, parm, actionid, function (error) {
             if (error) {
               callback(error)
             } else {
               callback(null, self.bookpath + '/' + actionid + '.pdf')
             }
           })
         }
       })
     }
   })
 }

 function createPage (layouter, actionid, language, pagespath, page, callback) {
   var split = false
   var target = ''
   if (page === 1 || page === 44) {
     target = pagespath + '/' + actionid + '/' + page + '.png'
   } else {
     target = pagespath + '/' + actionid + '/' + page + '_two.png'
     split = true
   }
   var parameter = ''
   layouter.createPage(actionid, language, page, function () {
     if (split) {
       // convert -crop 720x1040 image.png cropped_%d.png
       execSync('convert -crop 720x1040 ' + target + ' /tmp/splitted_$d.png') // to tmp
       execSync('mv /tmp/splitted_1.png ' + pagespath + '/' + actionid + '/' + page + '.png')
       execSync('mv /tmp/splitted_2.png ' + pagespath + '/' + actionid + '/' + (page + 1) + '.png')
       parameter = pagespath + '/' + actionid + '/' + page + '.png'
       parameter += pagespath + '/' + actionid + '/' + (page + 1) + '.png'
     } else {
       parameter += target
     }
     callback(null, parameter)
   })
 }

 function composeFiles (layouter, pagespath, actionid, language, maxPages, callback) {
   var listOfTasks = []
   for (var i = 1; i <= maxPages; i++) {
     if (i !== 1 && i % 2 !== 0) continue
     var page = i
     listOfTasks.push(function (callback) { createPage(layouter, actionid, language, pagespath, page, callback) })
   }
   async.series(listOfTasks, function (error, parameters) {
     if (error) {
       callback(error)
     } else {
       callback(null, parameters.join(' '))
     }
   })
 }

 function makePDF (bookpath, parameter, actionid, callback) {
   var cmd = 'convert ' + parameter + ' -compress Zip ' + bookpath + '/' + actionid + '.pdf'
   console.log(cmd)
   var output = execSync(cmd, true)
   if (output.stderr || output.stdout) {
     console.log(output.stdout)
     console.error(output.stderr)
     callback(output)
   } else {
     callback(null)
   }
 }

 function getBook (actionid, callback) {
   var self = this
   fs.access(self.bookpath + '/' + actionid + '.pdf', fs.constants.R_OK | fs.constants.W_OK, function (error) {
     if (error) {
       self.createBook(actionid, function (error) {
         if (error) {
           callback(error)
         } else {
           callback(null, self.bookpath + '/' + actionid + '.pdf')
         }
       })
     } else {
       callback(null, self.bookpath + '/' + actionid + '.pdf')
     }
   })
 }

 module.exports = BookGenerator
<|endoftext|>


<commit_before>var _ = require('underscore');
var carto = require('./index');
var tree = require('./tree');

carto.Renderer = function Renderer(env, options) {
    this.env = env || {};
    this.options = options || {};
    this.options.mapnik_version = this.options.mapnik_version || 'latest';
};

// Prepare a MML document (given as an object) into a
// fully-localized XML file ready for Mapnik2 consumption
//
// - @param {String} str the JSON file as a string.
// - @param {Object} env renderer environment options.
carto.Renderer.prototype.render = function render(m, callback) {
    // effects is a container for side-effects, which currently
    // are limited to FontSets.
    var env = _(this.env).defaults({
        benchmark: false,
        validation_data: false,
        effects: []
    });

    tree.Reference.setVersion(this.options.mapnik_version);

    var output = [];

    // Transform stylesheets into rulesets.
    var rulesets = _(m.Stylesheet).chain()
        .map(function(s) {
            // Passing the environment from stylesheet to stylesheet,
            // allows frames and effects to be maintained.
            env = _(env).extend({filename:s.id});

            // @TODO try/catch?
            var time = +new Date(),
                root = (carto.Parser(env)).parse(s.data);
            if (env.benchmark)
                console.warn('Parsing time: ' + (new Date() - time) + 'ms');
            return root.toList(env);
        })
        .flatten()
        .value();

    // Iterate through layers and create styles custom-built
    // for each of them, and apply those styles to the layers.
    m.Layer.forEach(function(l) {
        l.styles = [];
        // Classes are given as space-separated alphanumeric strings.
        var classes = (l['class'] || '').split(/\s+/g);
        var matching = rulesets.filter(function(definition) {
            return definition.appliesTo(l.name, classes);
        });
        var rules = inheritRules(matching, env);
        var sorted = sortStyles(rules, env);
        _(sorted).each(function(rule) {
            var style = new tree.Style(l.name, rule.attachment, rule);
            if (style) {
                l.styles.push(style.name);

                // env.effects can be modified by this call
                output.push(style.toXML(env));
            }
        });
        if (l.styles.length) {
            output.push((new carto.tree.Layer(l)).toXML());
        }
    });

    output.unshift(env.effects.map(function(e) {
        return e.toXML(env);
    }).join('\n'));

    var map_properties;
    try {
        map_properties = getMapProperties(m, rulesets, env);
    } catch (err) {
        env.error(err);
        return callback(err);
    }

    // Exit on errors.
    if (env.errors) return callback(env.errors);

    // Pass TileJSON and other custom parameters through to Mapnik XML.
    var parameters = _(m).reduce(function(memo, v, k) {
        if (!v && v !== 0) return memo;

        switch (k) {
        // Non URL-bound TileJSON properties.
        case 'bounds':
        case 'center':
        case 'minzoom':
        case 'maxzoom':
        case 'version':
            memo.push('  <Parameter name="' + k + '">' + v + '</Parameter>');
            break;
        // Properties that require CDATA.
        case 'name':
        case 'description':
        case 'legend':
        case 'attribution':
        case 'template':
            memo.push('  <Parameter name="' + k + '"><![CDATA[' + v + ']]></Parameter>');
            break;
        // Mapnik image format.
        case 'format':
            memo.push('  <Parameter name="' + k + '">' + v + '</Parameter>');
            break;
        // Mapnik interactivity settings.
        case 'interactivity':
            memo.push('  <Parameter name="interactivity_layer">' + v.layer + '</Parameter>');
            memo.push('  <Parameter name="interactivity_fields">' + v.fields + '</Parameter>');
            break;
        }
        return memo;
    }, []);
    if (parameters.length) output.unshift(
        '<Parameters>\n' +
        parameters.join('\n') +
        '\n</Parameters>\n'
    );

    var properties = _(map_properties).map(function(v) { return ' ' + v; }).join('');
    output.unshift(
        '<?xml version="1.0" ' +
        'encoding="utf-8"?>\n' +
        '<!DOCTYPE Map[]>\n' +
        '<Map' + properties + ' maximum-extent="-20037508.34,-20037508.34,20037508.34,20037508.34">\n');
    output.push('</Map>');
    return callback(null, output.join('\n'));
};

// This function currently modifies 'current'
function addRules(current, definition, existing) {
    var newFilters = definition.filters;
    var newRules = definition.rules;
    var updatedFilters, clone, previous;

    // The current definition might have been split up into
    // multiple definitions already.
    for (var k = 0; k < current.length; k++) {
        updatedFilters = current[k].filters.cloneWith(newFilters);
        if (updatedFilters) {
            previous = existing[updatedFilters];
            if (previous) {
                // There's already a definition with those exact
                // filters. Add the current definitions' rules
                // and stop processing it as the existing rule
                // has already gone down the inheritance chain.
                previous.addRules(newRules);
            } else {
                clone = current[k].clone(updatedFilters);
                // Make sure that we're only maintaining the clone
                // when we did actually add rules. If not, there's
                // no need to keep the clone around.
                if (clone.addRules(newRules)) {
                    // We inserted an element before this one, so we need
                    // to make sure that in the next loop iteration, we're
                    // not performing the same task for this element again,
                    // hence the k++.
                    current.splice(k, 0, clone);
                    k++;
                }
            }
        } else if (updatedFilters === null) {
            // Filters can be added, but they don't change the
            // filters. This means we don't have to split the
            // definition.
            current[k] = current[k].clone();
            current[k].addRules(newRules);
        }
    }
    return current;
}

// Apply inherited styles from their ancestors to them.
function inheritRules(definitions, env) {
    var inheritTime = +new Date();
    // definitions are ordered by specificity,
    // high (index 0) to low
    var byAttachment = {}, byFilter = {};
    var result = [];
    var current, previous, attachment;

    for (var i = 0; i < definitions.length; i++) {
        attachment = definitions[i].attachment;
        if (!byAttachment[attachment]) {
            byAttachment[attachment] = [];
            byAttachment[attachment].attachment = attachment;
            byFilter[attachment] = {};
            result.push(byAttachment[attachment]);
        }

        current = [definitions[i]];
        // Iterate over all subsequent rules.
        for (var j = i + 1; j < definitions.length; j++) {
            if (definitions[j].attachment === attachment) {
                // Only inherit rules from the same attachment.
                current = addRules(current, definitions[j], byFilter);
            }
        }

        for (var k = 0; k < current.length; k++) {
            byFilter[attachment][current[k].filters] = current[k];
            byAttachment[attachment].push(current[k]);
        }
    }

    if (env.benchmark) console.warn('Inheritance time: ' + ((new Date() - inheritTime)) + 'ms');

    return result;
}

function sortStyles(styles, env) {
    styles.forEach(function(style) {
        style.index = Infinity;
        style.forEach(function(block) {
            block.rules.forEach(function(rule) {
                if (rule.index < style.index) {
                    style.index = rule.index;
                }
            });
        });
    });

    var result = styles.slice();
    result.sort(function(a, b) {
        return b.index - a.index;
    });
    return result;
}

// Find a rule like Map { background-color: #fff; },
// if any, and return a list of properties to be inserted
// into the <Map element of the resulting XML. Translates
// properties of the mml object at `m` directly into XML
// properties.
//
// - @param {Object} m the mml object.
// - @param {Array} rulesets the output of toList.
// - @param {Object} env.
// - @return {String} rendered properties.
function getMapProperties(m, rulesets, env) {
    var rules = {};
    var symbolizers = tree.Reference.data.symbolizers.map;

    _(m).each(function(value, key) {
        if (key in symbolizers) rules[key] = key + '="' + value + '"';
    });

    rulesets.filter(function(r) {
        return r.elements.join('') === 'Map';
    }).forEach(function(r) {
        for (var i = 0; i < r.rules.length; i++) {
            var key = r.rules[i].name;
            if (!(key in symbolizers)) {
                env.error({
                    message: 'Rule ' + key + ' not allowed for Map.',
                    index: r.rules[i].index
                });
            }
            rules[key] = r.rules[i].eval(env).toXML(env);
        }
    });
    return rules;
}

module.exports = carto;
<commit_msg>Have render() catch Reference errors by invoking the user callback<commit_after>var _ = require('underscore');
var carto = require('./index');
var tree = require('./tree');

carto.Renderer = function Renderer(env, options) {
    this.env = env || {};
    this.options = options || {};
    this.options.mapnik_version = this.options.mapnik_version || 'latest';
};

// Prepare a MML document (given as an object) into a
// fully-localized XML file ready for Mapnik2 consumption
//
// - @param {String} str the JSON file as a string.
// - @param {Object} env renderer environment options.
carto.Renderer.prototype.render = function render(m, callback) {
    // effects is a container for side-effects, which currently
    // are limited to FontSets.
    var env = _(this.env).defaults({
        benchmark: false,
        validation_data: false,
        effects: []
    });

    try {
      tree.Reference.setVersion(this.options.mapnik_version)
    } catch (err) {
      callback(err, null);
    }

    var output = [];

    // Transform stylesheets into rulesets.
    var rulesets = _(m.Stylesheet).chain()
        .map(function(s) {
            // Passing the environment from stylesheet to stylesheet,
            // allows frames and effects to be maintained.
            env = _(env).extend({filename:s.id});

            // @TODO try/catch?
            var time = +new Date(),
                root = (carto.Parser(env)).parse(s.data);
            if (env.benchmark)
                console.warn('Parsing time: ' + (new Date() - time) + 'ms');
            return root.toList(env);
        })
        .flatten()
        .value();

    // Iterate through layers and create styles custom-built
    // for each of them, and apply those styles to the layers.
    m.Layer.forEach(function(l) {
        l.styles = [];
        // Classes are given as space-separated alphanumeric strings.
        var classes = (l['class'] || '').split(/\s+/g);
        var matching = rulesets.filter(function(definition) {
            return definition.appliesTo(l.name, classes);
        });
        var rules = inheritRules(matching, env);
        var sorted = sortStyles(rules, env);
        _(sorted).each(function(rule) {
            var style = new tree.Style(l.name, rule.attachment, rule);
            if (style) {
                l.styles.push(style.name);

                // env.effects can be modified by this call
                output.push(style.toXML(env));
            }
        });
        if (l.styles.length) {
            output.push((new carto.tree.Layer(l)).toXML());
        }
    });

    output.unshift(env.effects.map(function(e) {
        return e.toXML(env);
    }).join('\n'));

    var map_properties;
    try {
        map_properties = getMapProperties(m, rulesets, env);
    } catch (err) {
        env.error(err);
        return callback(err);
    }

    // Exit on errors.
    if (env.errors) return callback(env.errors);

    // Pass TileJSON and other custom parameters through to Mapnik XML.
    var parameters = _(m).reduce(function(memo, v, k) {
        if (!v && v !== 0) return memo;

        switch (k) {
        // Non URL-bound TileJSON properties.
        case 'bounds':
        case 'center':
        case 'minzoom':
        case 'maxzoom':
        case 'version':
            memo.push('  <Parameter name="' + k + '">' + v + '</Parameter>');
            break;
        // Properties that require CDATA.
        case 'name':
        case 'description':
        case 'legend':
        case 'attribution':
        case 'template':
            memo.push('  <Parameter name="' + k + '"><![CDATA[' + v + ']]></Parameter>');
            break;
        // Mapnik image format.
        case 'format':
            memo.push('  <Parameter name="' + k + '">' + v + '</Parameter>');
            break;
        // Mapnik interactivity settings.
        case 'interactivity':
            memo.push('  <Parameter name="interactivity_layer">' + v.layer + '</Parameter>');
            memo.push('  <Parameter name="interactivity_fields">' + v.fields + '</Parameter>');
            break;
        }
        return memo;
    }, []);
    if (parameters.length) output.unshift(
        '<Parameters>\n' +
        parameters.join('\n') +
        '\n</Parameters>\n'
    );

    var properties = _(map_properties).map(function(v) { return ' ' + v; }).join('');
    output.unshift(
        '<?xml version="1.0" ' +
        'encoding="utf-8"?>\n' +
        '<!DOCTYPE Map[]>\n' +
        '<Map' + properties + ' maximum-extent="-20037508.34,-20037508.34,20037508.34,20037508.34">\n');
    output.push('</Map>');
    return callback(null, output.join('\n'));
};

// This function currently modifies 'current'
function addRules(current, definition, existing) {
    var newFilters = definition.filters;
    var newRules = definition.rules;
    var updatedFilters, clone, previous;

    // The current definition might have been split up into
    // multiple definitions already.
    for (var k = 0; k < current.length; k++) {
        updatedFilters = current[k].filters.cloneWith(newFilters);
        if (updatedFilters) {
            previous = existing[updatedFilters];
            if (previous) {
                // There's already a definition with those exact
                // filters. Add the current definitions' rules
                // and stop processing it as the existing rule
                // has already gone down the inheritance chain.
                previous.addRules(newRules);
            } else {
                clone = current[k].clone(updatedFilters);
                // Make sure that we're only maintaining the clone
                // when we did actually add rules. If not, there's
                // no need to keep the clone around.
                if (clone.addRules(newRules)) {
                    // We inserted an element before this one, so we need
                    // to make sure that in the next loop iteration, we're
                    // not performing the same task for this element again,
                    // hence the k++.
                    current.splice(k, 0, clone);
                    k++;
                }
            }
        } else if (updatedFilters === null) {
            // Filters can be added, but they don't change the
            // filters. This means we don't have to split the
            // definition.
            current[k] = current[k].clone();
            current[k].addRules(newRules);
        }
    }
    return current;
}

// Apply inherited styles from their ancestors to them.
function inheritRules(definitions, env) {
    var inheritTime = +new Date();
    // definitions are ordered by specificity,
    // high (index 0) to low
    var byAttachment = {}, byFilter = {};
    var result = [];
    var current, previous, attachment;

    for (var i = 0; i < definitions.length; i++) {
        attachment = definitions[i].attachment;
        if (!byAttachment[attachment]) {
            byAttachment[attachment] = [];
            byAttachment[attachment].attachment = attachment;
            byFilter[attachment] = {};
            result.push(byAttachment[attachment]);
        }

        current = [definitions[i]];
        // Iterate over all subsequent rules.
        for (var j = i + 1; j < definitions.length; j++) {
            if (definitions[j].attachment === attachment) {
                // Only inherit rules from the same attachment.
                current = addRules(current, definitions[j], byFilter);
            }
        }

        for (var k = 0; k < current.length; k++) {
            byFilter[attachment][current[k].filters] = current[k];
            byAttachment[attachment].push(current[k]);
        }
    }

    if (env.benchmark) console.warn('Inheritance time: ' + ((new Date() - inheritTime)) + 'ms');

    return result;
}

function sortStyles(styles, env) {
    styles.forEach(function(style) {
        style.index = Infinity;
        style.forEach(function(block) {
            block.rules.forEach(function(rule) {
                if (rule.index < style.index) {
                    style.index = rule.index;
                }
            });
        });
    });

    var result = styles.slice();
    result.sort(function(a, b) {
        return b.index - a.index;
    });
    return result;
}

// Find a rule like Map { background-color: #fff; },
// if any, and return a list of properties to be inserted
// into the <Map element of the resulting XML. Translates
// properties of the mml object at `m` directly into XML
// properties.
//
// - @param {Object} m the mml object.
// - @param {Array} rulesets the output of toList.
// - @param {Object} env.
// - @return {String} rendered properties.
function getMapProperties(m, rulesets, env) {
    var rules = {};
    var symbolizers = tree.Reference.data.symbolizers.map;

    _(m).each(function(value, key) {
        if (key in symbolizers) rules[key] = key + '="' + value + '"';
    });

    rulesets.filter(function(r) {
        return r.elements.join('') === 'Map';
    }).forEach(function(r) {
        for (var i = 0; i < r.rules.length; i++) {
            var key = r.rules[i].name;
            if (!(key in symbolizers)) {
                env.error({
                    message: 'Rule ' + key + ' not allowed for Map.',
                    index: r.rules[i].index
                });
            }
            rules[key] = r.rules[i].eval(env).toXML(env);
        }
    });
    return rules;
}

module.exports = carto;
<|endoftext|>


<commit_before>// Test harnesses
// ==============

// A test is defined with 'test', which returns a function.
// A group of tests is defined with `testGroup`, which also returns a function.
// Either one is executed by calling it with a "reporter".
// HTML and console reporters are provided.

// Setup.
import {spawn, timeout} from '../src/Asy';
import {makeStopwatch}  from '../src/Utl';

var {assign, create} = Object;

// Assertion libraries.
// --------------------
var {expect, should, assert} = chai;

assign(chai.config, {
  includeStack: true,
  showDiff: true
});

// `should` requires initialization.
should = should();

// Reporters
// ---------

// Base class for reporters.
// Options include `hideCounts`, `hideTime`, `hidePasses`, and `hideSubtests`.
class Reporter {
  constructor(options = {}) {
    this.options = assign({}, options);
  }

  startGroup()     { return new this.constructor(options); }
  endGroup(group)  {
    this.passes += group.passes;
    this.fails  += group.fails;
  }
  pass()           { this.passes++; return this; }
  fail()           { this.fails++;  return this; }
}

assign(Reporter.prototype, {passes: 0, fails: 0});

// Console reporter, which reports results on the console.
class ConsoleReporter extends Reporter {
  constructor(options = {}) {
    super(options);
  }
  pass(msg, time) {
    if (!this.options.hidePasses) {
      let str = '%c✓' + msg;
      if (!this.options.hideTime) { str += ' (%sms)'; }
      console.log(str, "color: green", time);
    }
    return super();
  }
  log(msg) {
    console.log(msg);
    return this;
  }
  fail(msg) {
    console.error(msg);
    return super();
  }
  startGroup(desc, options = {}) {
    console[this.options.hideSubtests ? 'groupCollapsed' : 'group'](desc);
    return new ConsoleReporter(assign(options, this.options));
  }
  endGroup(group) {
    console.groupEnd();
    if (!this.options.hideCounts) {
      let color = group.fails ? 'red' : 'green';
      console.log(`%c${group.passes} passes, ${group.fails} fails`, `color: ${color}`);
    }
    return super(group);
  }
}

// HTML reporter inserts output into the DOM.
class HtmlReporter extends Reporter {
  constructor(parent, tag, options = {}) {
    assign(this, {parent, tag});
    super(options);
  }

  elt()     { return document.createElement(this.tag); }
  append(c) { return this.parent.appendChild(c); }
  text(t)   { return document.createTextNode(t); }

  pass(msg) {
    var t = this.text(msg);
    var e = this.elt();
    e.appendChild(t);
    this.append(e);
    return super();
  }
  log(msg) {
    var e = this.elt();
    var t = this.text("LOG: " + msg);
    e.appendChild(t);
    this.append(e);
  }
  fail(msg) {
    var t = this.text(msg);
    var e = this.elt();
    var span = document.createElement('span');
    span.setAttribute('style', "color: red");
    span.appendChild(t);
    e.appendChild(span);
    this.append(e);
    return super();
  }
  startGroup(desc) {
    // @TODO put out the name of the group
    var ul = document.createElement('ul');
    this.parent.appendChild(ul);
    return new HtmlReporter(ul, 'li', options);
  }
}

// Test creators
// -------------

// Return a function to run a group of tests.
function testGroup(desc, tests) {
  return function(reporter) {
    return spawn(
      function *() {
        var group;
        yield group = reporter.startGroup(desc);
        for (var t of tests) {
          yield t(group);
        }
        yield reporter.endGroup(group);
      }
    );
  };
}

// Return a function to run a single test.
function test(desc, when, then) {
  var stopwatch = makeStopwatch();
  return reporter => Promise
    .resolve()
  //    .then  (_ => reporter.log(desc))
    .then  (stopwatch.start)
    .then  (_ => when(reporter))
    .then  (timeout())
    .then  (_ => then(reporter))
    .then  (stopwatch.stop)
    .then  (_ => reporter.pass(desc, stopwatch.time))
    .catch (e => reporter.fail(e))
  ;
}

// Exports
// -------
export {
  // Reporters.
  ConsoleReporter,
  HtmlReporter,

  // Test creators.
  test,
  testGroup,

  // Assertion libraries.
  assert,
  should,
  expect
};
<commit_msg>Fix up test harness.<commit_after>// Test harnesses
// ==============

// A test is defined with 'test', which returns a function.
// A group of tests is defined with `testGroup`, which also returns a function.
// Either one is executed by calling it with a "reporter".
// HTML and console reporters are provided.

// Setup.
import {spawn, timeout} from '../src/Asy';
import {makeStopwatch}  from '../src/Utl';

var {assign, create} = Object;

// Assertion libraries.
// --------------------
var {expect, should, assert} = chai;

assign(chai.config, {
  includeStack: true,
  showDiff: true
});

// `should` requires initialization.
should = should();

// Reporters
// ---------

// Base class for reporters.
// Options include `hideCounts`, `hideTime`, `hidePasses`, and `hideSubtests`.
class Reporter {
  constructor(options = {}) {
    this.options = assign({}, options);
  }
  startGroup()     { return new this.constructor(options); }
  endGroup(group)  {
    this.passes += group.passes;
    this.fails  += group.fails;
    this.time   += group.time;
  }
  report(result)   {
    if (result.pass) { this.passes++; }
    else { this.fails++; }
    this.time += result.time;
  }
}

assign(Reporter.prototype, {passes: 0, fails: 0, time: 0});

// Console reporter, which reports results on the console.
class ConsoleReporter extends Reporter {
  constructor(options = {}) {
    super(options);
  }

  report(result) {
    var {msg, pass, time} = result;
    var {hideTime, hidePasses} = this.options;

    if (!hideTime) { msg = `${msg} (${time}ms)`; }
    if (pass) {
      if (!hidePasses) { console.log('%c✓' + msg, "color: green"); }
    } else { console.error(msg); }
    return super(result);
  }

  log(msg) {
    console.log(msg);
    return this;
  }

  startGroup(desc, options = {}) {
    console[this.options.hideSubtests ? 'groupCollapsed' : 'group'](desc);
    return new ConsoleReporter(assign(options, this.options));
  }

  endGroup(group) {
    console.groupEnd();
    super(group);
    if (!this.options.hideCounts) {
      let color = group.fails ? 'red' : 'green';
      let msg = `%c${group.passes} passes, ${group.fails} fails`;
      if (!group.hideTime) { msg += ` (${this.time}ms)`; }
      console.log(msg, `color: ${color}`);
    }
    return this;
  }
}

// HTML reporter inserts output into the DOM.
class HtmlReporter extends Reporter {
  constructor(parent, tag, options = {}) {
    assign(this, {parent, tag});
    super(options);
  }

  elt()     { return document.createElement(this.tag); }
  append(c) { return this.parent.appendChild(c); }
  text(t)   { return document.createTextNode(t); }

  pass(msg) {
    var t = this.text(msg);
    var e = this.elt();
    e.appendChild(t);
    this.append(e);
    return super();
  }
  log(msg) {
    var e = this.elt();
    var t = this.text("LOG: " + msg);
    e.appendChild(t);
    this.append(e);
  }
  fail(msg) {
    var t = this.text(msg);
    var e = this.elt();
    var span = document.createElement('span');
    span.setAttribute('style', "color: red");
    span.appendChild(t);
    e.appendChild(span);
    this.append(e);
    return super();
  }
  startGroup(desc) {
    // @TODO put out the name of the group
    var ul = document.createElement('ul');
    this.parent.appendChild(ul);
    return new HtmlReporter(ul, 'li', options);
  }
}

// Test creators
// -------------

// Return a function to run a group of tests.
function testGroup(desc, tests) {
  return function(reporter) {
    return spawn(

      function *() {
        var group;
        yield group = reporter.startGroup(desc);
        for (var t of tests) {
          yield t(group);
        }
        yield reporter.endGroup(group);
      }

    );
  };
}

// Return a function to run a single test.
function test(desc, when, then) {
  var stopwatch = makeStopwatch();
  var pass = true, msg = desc, time;

  return reporter => {
    return Promise
      .resolve()
      .then  (  stopwatch.start  )

      .then  (_ => when(reporter))
      .then  (     timeout()     )
      .then  (_ => then(reporter))

      .then  (null, e => { pass = false; msg += ": " + e; })

      .then  (_ => {
        stopwatch.stop();
        time = stopwatch.time;
        reporter.report({pass, msg, time});
      })
    ;
  }
}

// Exports
// -------
export {
  // Reporters.
  ConsoleReporter,
  HtmlReporter,

  // Test creators.
  test,
  testGroup,

  // Assertion libraries.
  assert,
  should,
  expect
};
<|endoftext|>


<commit_before>var $, io, skipfirst;

(function(window) {
    'use strict';

    window.Console = new ConsoleProto();

    function ConsoleProto() {
        var Spawn,
            ShortCuts = {},

            jqconsole;

        function Console(element, prefix, callback) {
            var el,
                type        = typeof element,
                isString    = type === 'string';

            if (!callback) {
                callback    = prefix;
                prefix      = '/console';
            }

            if (isString)
                el  = document.querySelector(element);
            else
                el  = element;

            load(prefix, function() {
                jqconsole   = $(el).jqconsole('', '> ');

                Spawn = new SpawnProto(jqconsole, prefix);
                addShortCuts(jqconsole);
                addKeyWhenNoPrompt(jqconsole);
                addOnMouseUp(jqconsole);

                if (typeof callback === 'function')
                    callback();
            });
        }

        Console.addShortCuts    = function(shortCuts) {
            if (shortCuts)
                ShortCuts = shortCuts;
        };

        Console.getPromptText   = function() {
            var text = jqconsole.GetPromptText();

            return text;
        };

        Console.setPromptText   = function(text) {
            jqconsole.SetPromptText(text);
        };

        Console.focus           = function() {
            if (jqconsole)
                jqconsole.Focus();
        };

        function addOnMouseUp(jqconsole) {
            var console     = jqconsole.$console;

            console.mouseup(function() {
                var top,
                    isSelection = '' + window.getSelection();

                if (!isSelection) {
                    top        = console.scrollTop();

                    Console.focus();
                    console.scrollTop(top);
                }
            });
        }

        function load(prefix, callback) {
            var scripts = [];

            if (!window.load)
                scripts.push('/modules/load/load.js');

            if (!window.join)
                scripts.push('/join/join.js');

            if (!scripts.length)
                after();
            else
                loadScript(scripts.map(function(name) {
                    return prefix + name;
                }), after);

            function after() {
                var load    = window.load,
                    join    = window.join,

                    css     = prefix + join([
                        '/css/console.css',
                        '/css/ansi.css',
                    ]);

                load.json(prefix + '/modules.json', function(error, remote) {
                    var reg,
                        name    = 'jQuery';

                    if (error) {
                        console.log(error);
                    } else {
                        /* do not load jquery if it is loaded */
                        if (window[name]) {
                            reg     = RegExp(name, 'i');
                            remote  = remote.filter(function(item) {
                                return !reg.test(item);
                            });
                        }

                        load.series(remote.concat(css), callback);
                    }
                });
            }
        }

        function loadScript(srcs, callback) {
            var i       = srcs.length,
                func    = function() {
                    --i;

                    if (!i)
                        callback();
                };

            srcs.forEach(function(src) {
                var element = document.createElement('script');

                element.src = src;
                element.addEventListener('load', function load() {
                    func();
                    element.removeEventListener('load', load);
                });

                document.body.appendChild(element);
            });
        }

        function isPrompt() {
            var state   = jqconsole.GetState(),
                is      = state === 'prompt';

            return is;
        }

        function SpawnProto(jqconsole, room) {
            var Buffer          = {
                    'log-msg': '',
                    'error-msg': ''
                },
                self            = this,
                socket,
                prompt          = function() {
                    var is = isPrompt();

                    if (!is)
                        jqconsole.Prompt(true, self.handler);
            };

            init(room);

            function init(room) {
                var href            = getHost(),
                    FIVE_SECONDS    = 5000;

                socket = io.connect(href + room, {
                    'max reconnection attempts' : Math.pow(2, 32),
                    'reconnection limit'        : FIVE_SECONDS
                });

                socket.on('err', function(data) {
                    error(data);
                });

                socket.on('data', function(data) {
                    log(data);
                });

                socket.on('prompt', function() {
                    forceWrite();
                    prompt();
                });

                socket.on('path', function(path) {
                     setPromptLabel(path + '> ');
                });

                socket.on('connect', function() {
                    log('console: connected\n');
                });

                socket.on('disconnect', function() {
                    error('console: disconnected\n');
                    abortPrompt();
                });
            }

            this.handler = function getHandler(command) {
                if (command)
                    socket.emit('command', command);
                else
                    jqconsole.Prompt(true, self.handler);
            };

            this.kill       = function() {
                socket.emit('kill');
            };

            this.write      = function(data) {
                socket.emit('write', data);
            };

            function getHost() {
                var l       = location,
                    href    = l.origin || l.protocol + '//' + l.host;

                return href;
            }

            function write(data, className) {
                var isContain;

                if (data) {
                    Buffer[className]   += data;
                    isContain           = ~Buffer[className].indexOf('\n');

                    if (isContain) {
                        jqconsole.Write(Buffer[className], className);
                        Buffer[className] = '';
                    }
                }
            }

            function forceWrite() {
                Object.keys(Buffer).forEach(function(name) {
                    if (Buffer[name]) {
                        jqconsole.Write(Buffer[name], name);
                        Buffer[name] = '';
                    }
                });
            }

            function log(data) {
                write(data, 'log-msg');
            }

            function error(data) {
                write(data, 'error-msg');
            }
        }

        function abortPrompt() {
             var is = isPrompt();

             if (is)
                jqconsole.AbortPrompt();
        }

        function setPromptLabel(prompt) {
            jqconsole.SetPromptLabel(prompt);
            jqconsole.UpdatePromptLabel();
        }

        function addShortCuts(jqconsole) {
            jqconsole.RegisterShortcut('Z', function() {
                jqconsole.SetPromptText('');
            });

            jqconsole.RegisterShortcut('L', clear);

            Object.keys(ShortCuts).forEach(function(key) {
                var func = ShortCuts[key];

                jqconsole.RegisterShortcut(key, func);
            });
        }

        function addKeyWhenNoPrompt(jqconsole) {
            var skip = skipfirst(readNoPrompt);

            jqconsole
                .$input_source[0]
                .addEventListener('keydown', function(event) {
                    var ENTER   = 13,
                        is      = isPrompt();

                    if (is)
                        skip.clear();
                    else
                        skip(event.keyCode === ENTER, event);
                });
        }

        function readNoPrompt(event) {
            var char,
                KEY_C   = 67,
                isCtrl  = event.ctrlKey || event.metaKey,
                isC     = event.keyCode === KEY_C;

            if (isC && isCtrl) {
                Spawn.kill();
            } else {
                char = fromCharCode(event);

                if (char) {
                    Spawn.write(char);
                    jqconsole.Write(char);
                }
            }
        }

        function fromCharCode(event) {
            var code, hex,
                char        = '',
                identifier  = event.keyIdentifier,
                shift       = event.shiftKey;

            if (identifier === 'Enter') {
                char = '\n';
            } else {
                code        = identifier.substring(2);
                hex         = parseInt(code, 16);

                if (!isNaN(hex))
                    char        = String.fromCharCode(hex);

                if (!shift && /[a-z]/i.test(char))
                    char = char.toLowerCase();
            }

            return char;
        }

        function clear() {
            jqconsole.Reset();
            addShortCuts(jqconsole);
            jqconsole.Prompt(true, Spawn.handler);
        }

        return Console;
    }

})(this);
<commit_msg>chore(console) add " "<commit_after>var $, io, skipfirst;

(function(window) {
    'use strict';

    window.Console = new ConsoleProto();

    function ConsoleProto() {
        var Spawn,
            ShortCuts = {},

            jqconsole;

        function Console(element, prefix, callback) {
            var el,
                type        = typeof element,
                isString    = type === 'string';

            if (!callback) {
                callback    = prefix;
                prefix      = '/console';
            }

            if (isString)
                el  = document.querySelector(element);
            else
                el  = element;

            load(prefix, function() {
                jqconsole   = $(el).jqconsole('', '> ');

                Spawn = new SpawnProto(jqconsole, prefix);
                addShortCuts(jqconsole);
                addKeyWhenNoPrompt(jqconsole);
                addOnMouseUp(jqconsole);

                if (typeof callback === 'function')
                    callback();
            });
        }

        Console.addShortCuts    = function(shortCuts) {
            if (shortCuts)
                ShortCuts = shortCuts;
        };

        Console.getPromptText   = function() {
            var text = jqconsole.GetPromptText();

            return text;
        };

        Console.setPromptText   = function(text) {
            jqconsole.SetPromptText(text);
        };

        Console.focus           = function() {
            if (jqconsole)
                jqconsole.Focus();
        };

        function addOnMouseUp(jqconsole) {
            var console     = jqconsole.$console;

            console.mouseup(function() {
                var top,
                    isSelection = '' + window.getSelection();

                if (!isSelection) {
                    top        = console.scrollTop();

                    Console.focus();
                    console.scrollTop(top);
                }
            });
        }

        function load(prefix, callback) {
            var scripts = [];

            if (!window.load)
                scripts.push('/modules/load/load.js');

            if (!window.join)
                scripts.push('/join/join.js');

            if (!scripts.length)
                after();
            else
                loadScript(scripts.map(function(name) {
                    return prefix + name;
                }), after);

            function after() {
                var load    = window.load,
                    join    = window.join,

                    css     = prefix + join([
                        '/css/console.css',
                        '/css/ansi.css',
                    ]);

                load.json(prefix + '/modules.json', function(error, remote) {
                    var reg,
                        name    = 'jQuery';

                    if (error) {
                        console.log(error);
                    } else {
                        /* do not load jquery if it is loaded */
                        if (window[name]) {
                            reg     = RegExp(name, 'i');
                            remote  = remote.filter(function(item) {
                                return !reg.test(item);
                            });
                        }

                        load.series(remote.concat(css), callback);
                    }
                });
            }
        }

        function loadScript(srcs, callback) {
            var i       = srcs.length,
                func    = function() {
                    --i;

                    if (!i)
                        callback();
                };

            srcs.forEach(function(src) {
                var element = document.createElement('script');

                element.src = src;
                element.addEventListener('load', function load() {
                    func();
                    element.removeEventListener('load', load);
                });

                document.body.appendChild(element);
            });
        }

        function isPrompt() {
            var state   = jqconsole.GetState(),
                is      = state === 'prompt';

            return is;
        }

        function SpawnProto(jqconsole, room) {
            var Buffer          = {
                    'log-msg': '',
                    'error-msg': ''
                },
                self            = this,
                socket,
                prompt          = function() {
                    var is = isPrompt();

                    if (!is)
                        jqconsole.Prompt(true, self.handler);
                };

            init(room);

            function init(room) {
                var href            = getHost(),
                    FIVE_SECONDS    = 5000;

                socket = io.connect(href + room, {
                    'max reconnection attempts' : Math.pow(2, 32),
                    'reconnection limit'        : FIVE_SECONDS
                });

                socket.on('err', function(data) {
                    error(data);
                });

                socket.on('data', function(data) {
                    log(data);
                });

                socket.on('prompt', function() {
                    forceWrite();
                    prompt();
                });

                socket.on('path', function(path) {
                     setPromptLabel(path + '> ');
                });

                socket.on('connect', function() {
                    log('console: connected\n');
                });

                socket.on('disconnect', function() {
                    error('console: disconnected\n');
                    abortPrompt();
                });
            }

            this.handler = function getHandler(command) {
                if (command)
                    socket.emit('command', command);
                else
                    jqconsole.Prompt(true, self.handler);
            };

            this.kill       = function() {
                socket.emit('kill');
            };

            this.write      = function(data) {
                socket.emit('write', data);
            };

            function getHost() {
                var l       = location,
                    href    = l.origin || l.protocol + '//' + l.host;

                return href;
            }

            function write(data, className) {
                var isContain;

                if (data) {
                    Buffer[className]   += data;
                    isContain           = ~Buffer[className].indexOf('\n');

                    if (isContain) {
                        jqconsole.Write(Buffer[className], className);
                        Buffer[className] = '';
                    }
                }
            }

            function forceWrite() {
                Object.keys(Buffer).forEach(function(name) {
                    if (Buffer[name]) {
                        jqconsole.Write(Buffer[name], name);
                        Buffer[name] = '';
                    }
                });
            }

            function log(data) {
                write(data, 'log-msg');
            }

            function error(data) {
                write(data, 'error-msg');
            }
        }

        function abortPrompt() {
             var is = isPrompt();

             if (is)
                jqconsole.AbortPrompt();
        }

        function setPromptLabel(prompt) {
            jqconsole.SetPromptLabel(prompt);
            jqconsole.UpdatePromptLabel();
        }

        function addShortCuts(jqconsole) {
            jqconsole.RegisterShortcut('Z', function() {
                jqconsole.SetPromptText('');
            });

            jqconsole.RegisterShortcut('L', clear);

            Object.keys(ShortCuts).forEach(function(key) {
                var func = ShortCuts[key];

                jqconsole.RegisterShortcut(key, func);
            });
        }

        function addKeyWhenNoPrompt(jqconsole) {
            var skip = skipfirst(readNoPrompt);

            jqconsole
                .$input_source[0]
                .addEventListener('keydown', function(event) {
                    var ENTER   = 13,
                        is      = isPrompt();

                    if (is)
                        skip.clear();
                    else
                        skip(event.keyCode === ENTER, event);
                });
        }

        function readNoPrompt(event) {
            var char,
                KEY_C   = 67,
                isCtrl  = event.ctrlKey || event.metaKey,
                isC     = event.keyCode === KEY_C;

            if (isC && isCtrl) {
                Spawn.kill();
            } else {
                char = fromCharCode(event);

                if (char) {
                    Spawn.write(char);
                    jqconsole.Write(char);
                }
            }
        }

        function fromCharCode(event) {
            var code, hex,
                char        = '',
                identifier  = event.keyIdentifier,
                shift       = event.shiftKey;

            if (identifier === 'Enter') {
                char = '\n';
            } else {
                code        = identifier.substring(2);
                hex         = parseInt(code, 16);

                if (!isNaN(hex))
                    char        = String.fromCharCode(hex);

                if (!shift && /[a-z]/i.test(char))
                    char = char.toLowerCase();
            }

            return char;
        }

        function clear() {
            jqconsole.Reset();
            addShortCuts(jqconsole);
            jqconsole.Prompt(true, Spawn.handler);
        }

        return Console;
    }

})(this);
<|endoftext|>


<commit_before>var async   = require('async');
var path    = require('path');
var chalk   = require('chalk');
var fs      = require('fs');
var Config  = require('../utils/config');
var Command = require('../models/command');
var Project = require('../models/project');

module.exports = Command.extend({
  name: 'build',
  description: 'Build the ember and cordova project together running in the simulator or on a device',
  optionsHelp: [
    '--environment|-e  (Default: development)',
    '--platform|-p  (Default: ios)'
  ],
  parseOptions: function() {
    this.options.env    = this.options.environment || this.options.e || 'development'
    this.options.platform = this.options.platform || this.options.p || 'ios';
  },
  run: function() {
    var project = Project.closest();
    require('../tasks/build')(this.options.env, this.options.platform, project)()
  }
});
<commit_msg>add command suffix to build command<commit_after>var async   = require('async');
var path    = require('path');
var chalk   = require('chalk');
var fs      = require('fs');
var Config  = require('../utils/config');
var Command = require('../models/command');
var Project = require('../models/project');

module.exports = Command.extend({
  name: 'build',
  description: 'Build the ember and cordova project together running in the simulator or on a device',
  commandSuffix: '<options...>',
  optionsHelp: [
    '--environment|-e  (Default: development)',
    '--platform|-p  (Default: ios)'
  ],
  parseOptions: function() {
    this.options.env    = this.options.environment || this.options.e || 'development'
    this.options.platform = this.options.platform || this.options.p || 'ios';
  },
  run: function() {
    var project = Project.closest();
    require('../tasks/build')(this.options.env, this.options.platform, project)()
  }
});
<|endoftext|>