<commit_before>/*eslint quotes: [2, "single"]*/

/*global document, window */

/*global utils */

/*global Mousetrap */

'use strict';

import storage from 'lib/storage';
import editor from 'lib/editor';
import terminal from 'lib/terminal';

// Not the real module name to avoid name clash with 'commands' object which
// contains all the commands.
// // TODO rename both?
var com = {};

com.init = function () {
    window.commands = {};
    var commands = window.commands;

    commands.prev = editor.offsetCurrent.bind(null, -1);
    commands.next = editor.offsetCurrent.bind(null, 1);
    commands.add = editor.add;
    commands.remove = editor.remove;
    commands.inputs = editor.port.bind(null, 'input');
    commands.outputs = editor.port.bind(null, 'output');
    commands.block = editor.block;
    commands.fire = editor.fire;
    commands.set = editor.set;
    commands.move = editor.move;
    commands.offset = editor.moveBy;

    var editBlock = function (block) {
        Mousetrap.reset();
        Mousetrap.bind('esc', commands.escape);
        block.content.focus();
    };

    commands.edit = function () {
        if (editor.context === 'block') {
            var block = editor.getCurrentBlock();
            editBlock(block);
            editor.stopBlinking();
            // Prevent default when this function is used with Moustrap.
            return false;
        }
    };

    commands.addButton = commands.add.bind(null, 'html', 'button', 'go', 0, 1, undefined, undefined);
    commands.addScript = commands.add.bind(null, 'html', 'script', 'return in1 + 2;', 1, 1, undefined, undefined);
    commands.addText = commands.add.bind(null, 'html', 'span', 'empty', 1, 1, undefined, undefined);
    commands.addNumber = commands.add.bind(null, 'zed', 'number', '42', 1, 1, undefined, undefined);
    var bindKeysForMainMode = function () {
        Mousetrap.reset();
        Mousetrap.bind('K', commands.offset.bind(null, 0, -10));
        Mousetrap.bind('J', commands.offset.bind(null, 0, 10));
        Mousetrap.bind('H', commands.offset.bind(null, -10, 0));
        Mousetrap.bind('L', commands.offset.bind(null, 10, 0));
        Mousetrap.bind('k', commands.prev);
        Mousetrap.bind('j', commands.next);
        Mousetrap.bind('a n', commands.add.bind(null, 'New'));
        Mousetrap.bind('a h b', commands.addButton);
        Mousetrap.bind('a h s', commands.addScript);
        Mousetrap.bind('a h t', commands.addText);
        Mousetrap.bind('a h n', commands.addNumber);
        Mousetrap.bind('r', commands.remove);
        Mousetrap.bind('i', commands.inputs);
        Mousetrap.bind('o', commands.outputs);
        Mousetrap.bind('b', commands.block);
        Mousetrap.bind('c', commands.goToCommandLine);
        Mousetrap.bind('l', commands.link);
        Mousetrap.bind('g', commands.goToBlock);
        Mousetrap.bind('e', commands.edit);
        Mousetrap.bind('space', commands.fire);
    };
    window.bindKeysForMainMode = bindKeysForMainMode;

    commands.escape = function () {
        if (editor.context === 'block') {
            var currentlyEditingElement = utils.dom.getSelectionStart();
            if (currentlyEditingElement !== null) {
                currentlyEditingElement.blur();
                editor.startBlinking();
            }
            bindKeysForMainMode();
        }
    };

    var switchDeemphasisAllBlocks = function () {
        var blocks = document.querySelectorAll('z-block');
        [].forEach.call(blocks, function (b) {
            b.classList.toggle('de-emphasis');
        });
    };

    var hideAllPorts = function () {
        var ports = document.querySelectorAll('z-port');
        [].forEach.call(ports, function (port) {
            port.hideKey();
        });
        switchDeemphasisAllBlocks();
    };

    var firstPort;
    var selectPort = function (port) {
        if (firstPort === undefined) {
            firstPort = port;
        } else {
            if (port.connectable(port, firstPort)) {
                port.connect(port, firstPort);
                firstPort = undefined;
                hideAllPorts();
                bindKeysForMainMode();
            }
        }
    };

    var portToLinkTo;
    commands.link = function () {
        if (editor.context === 'block') {
            var keys = utils.createKeysGenerator();
            firstPort = undefined;
            Mousetrap.reset();
            var ports = document.querySelectorAll('z-port');
            var index = 1;
            [].forEach.call(ports, function (port) {
                var key = keys.next();
                port.querySelector('span.port-key').innerHTML = key;
                port.showKey();
                // Convert 'aae' into 'a a e'.
                key = key.split('').join(' ');
                Mousetrap.bind(key, selectPort.bind(null, port));
                index++;
            });
            Mousetrap.bind('esc', function () {
                bindKeysForMainMode();
                hideAllPorts();
            });
            switchDeemphasisAllBlocks();
        } else {
            var port = editor.getCurrentPort();
            if (port !== null) {
                if (portToLinkTo === undefined) {
                    portToLinkTo = port;
                    portToLinkTo.classList.toggle('to-link-to');
                } else if (port.connectable(port, portToLinkTo)) {
                    port.connect(port, portToLinkTo);
                    portToLinkTo.classList.toggle('to-link-to');
                    portToLinkTo = undefined;
                } else {
                    portToLinkTo.classList.toggle('to-link-to');
                    portToLinkTo = port;
                    portToLinkTo.classList.toggle('to-link-to');
                }
            }
        }
    };

    var setCurrentBlockAndBackToMainMode = function (block) {
        editor.setCurrentBlock(block);
        var blocks = document.querySelectorAll('z-block');
        [].forEach.call(blocks, function (block) {
            block.hideKey();
        });
        bindKeysForMainMode();
    };

    commands.goToBlock = function () {
        Mousetrap.reset();
        var blocks = document.querySelectorAll('z-block');
        var index = 0;
        var keys = utils.createKeysGenerator();
        [].forEach.call(blocks, function (block) {
            var key = keys.next();
            block.querySelector('span.id').innerHTML = key;
            block.showKey();
            // Convert 'aae' into 'a a e'.
            key = key.split('').join(' ');
            Mousetrap.bind(key, setCurrentBlockAndBackToMainMode.bind(null, block));
            index++;
        });
        Mousetrap.bind('esc', bindKeysForMainMode);
    };

    // Set a new stopCallback for Moustrap to avoid stopping when we start
    // editing a contenteditable, so that we can use escape to leave editing.
    Mousetrap.stopCallback = function(e, element, combo) {
        // if the element has the class "mousetrap" then no need to stop
        if ((' ' + element.className + ' ').indexOf(' mousetrap ') > -1) {
            return false;
        }

         // stop for input, select, and textarea
         return element.tagName == 'INPUT' || element.tagName == 'SELECT' || element.tagName == 'TEXTAREA';
     };

    commands.save = storage.savePatch;
    commands.load = storage.loadPatch;
    commands.rm = storage.removePatch;
    commands.list = storage.getPatchNames;
    commands.clear = storage.clearAll;

    var terminalOnblur = function () {
        window.bindKeysForMainMode();
        editor.startBlinking();
    };

    var term = terminal.create(commands, terminalOnblur);

    commands.goToCommandLine = function () {
        term.focus();
        Mousetrap.reset();
        editor.stopBlinking();
    };

    // TODO create a term.write(multiLineString) and use it.
    commands.help = function (subject) {
        if (subject === undefined) {
            term.term.write('Press Esc to leave the command line and go back to normal mode.');
            term.term.newLine();
            term.term.newLine();
            term.term.write('Commands: next, prev, remove, add and set content.');
        } else if (subject === 'add') {
            term.term.write('Add a new block just below the current block.');
            term.term.newLine();
            term.term.newLine();
            term.term.write('add html <what> <content> <nb inputs> <nb outputs>');
            term.term.newLine();
            term.term.write('  <what>    is either "button", "script", "text", "number" or a HTML tag.');
            term.term.newLine();
            term.term.write('  <content> is the content of the block (i.e. the button name, the');
            term.term.newLine();
            term.term.write('            script code, the text or number value, etc.).');
        } else {
            term.term.write('No help for "' + subject + '".');
        }
    };

    bindKeysForMainMode();

};
export default com;
<commit_msg>Fix, keys don't hide when cancelling block navigation<commit_after>/*eslint quotes: [2, "single"]*/

/*global document, window */

/*global utils */

/*global Mousetrap */

'use strict';

import storage from 'lib/storage';
import editor from 'lib/editor';
import terminal from 'lib/terminal';

// Not the real module name to avoid name clash with 'commands' object which
// contains all the commands.
// // TODO rename both?
var com = {};

com.init = function () {
    window.commands = {};
    var commands = window.commands;

    commands.prev = editor.offsetCurrent.bind(null, -1);
    commands.next = editor.offsetCurrent.bind(null, 1);
    commands.add = editor.add;
    commands.remove = editor.remove;
    commands.inputs = editor.port.bind(null, 'input');
    commands.outputs = editor.port.bind(null, 'output');
    commands.block = editor.block;
    commands.fire = editor.fire;
    commands.set = editor.set;
    commands.move = editor.move;
    commands.offset = editor.moveBy;

    var editBlock = function (block) {
        Mousetrap.reset();
        Mousetrap.bind('esc', commands.escape);
        block.content.focus();
    };

    commands.edit = function () {
        if (editor.context === 'block') {
            var block = editor.getCurrentBlock();
            editBlock(block);
            editor.stopBlinking();
            // Prevent default when this function is used with Moustrap.
            return false;
        }
    };

    commands.addButton = commands.add.bind(null, 'html', 'button', 'go', 0, 1, undefined, undefined);
    commands.addScript = commands.add.bind(null, 'html', 'script', 'return in1 + 2;', 1, 1, undefined, undefined);
    commands.addText = commands.add.bind(null, 'html', 'span', 'empty', 1, 1, undefined, undefined);
    commands.addNumber = commands.add.bind(null, 'zed', 'number', '42', 1, 1, undefined, undefined);
    var bindKeysForMainMode = function () {
        Mousetrap.reset();
        Mousetrap.bind('K', commands.offset.bind(null, 0, -10));
        Mousetrap.bind('J', commands.offset.bind(null, 0, 10));
        Mousetrap.bind('H', commands.offset.bind(null, -10, 0));
        Mousetrap.bind('L', commands.offset.bind(null, 10, 0));
        Mousetrap.bind('k', commands.prev);
        Mousetrap.bind('j', commands.next);
        Mousetrap.bind('a n', commands.add.bind(null, 'New'));
        Mousetrap.bind('a h b', commands.addButton);
        Mousetrap.bind('a h s', commands.addScript);
        Mousetrap.bind('a h t', commands.addText);
        Mousetrap.bind('a h n', commands.addNumber);
        Mousetrap.bind('r', commands.remove);
        Mousetrap.bind('i', commands.inputs);
        Mousetrap.bind('o', commands.outputs);
        Mousetrap.bind('b', commands.block);
        Mousetrap.bind('c', commands.goToCommandLine);
        Mousetrap.bind('l', commands.link);
        Mousetrap.bind('g', commands.goToBlock);
        Mousetrap.bind('e', commands.edit);
        Mousetrap.bind('space', commands.fire);
    };
    window.bindKeysForMainMode = bindKeysForMainMode;

    commands.escape = function () {
        if (editor.context === 'block') {
            var currentlyEditingElement = utils.dom.getSelectionStart();
            if (currentlyEditingElement !== null) {
                currentlyEditingElement.blur();
                editor.startBlinking();
            }
            bindKeysForMainMode();
        }
    };

    var switchDeemphasisAllBlocks = function () {
        var blocks = document.querySelectorAll('z-block');
        [].forEach.call(blocks, function (b) {
            b.classList.toggle('de-emphasis');
        });
    };

    var hideAllPorts = function () {
        var ports = document.querySelectorAll('z-port');
        [].forEach.call(ports, function (port) {
            port.hideKey();
        });
        switchDeemphasisAllBlocks();
    };

    var firstPort;
    var selectPort = function (port) {
        if (firstPort === undefined) {
            firstPort = port;
        } else {
            if (port.connectable(port, firstPort)) {
                port.connect(port, firstPort);
                firstPort = undefined;
                hideAllPorts();
                bindKeysForMainMode();
            }
        }
    };

    var portToLinkTo;
    commands.link = function () {
        if (editor.context === 'block') {
            var keys = utils.createKeysGenerator();
            firstPort = undefined;
            Mousetrap.reset();
            var ports = document.querySelectorAll('z-port');
            var index = 1;
            [].forEach.call(ports, function (port) {
                var key = keys.next();
                port.querySelector('span.port-key').innerHTML = key;
                port.showKey();
                // Convert 'aae' into 'a a e'.
                key = key.split('').join(' ');
                Mousetrap.bind(key, selectPort.bind(null, port));
                index++;
            });
            Mousetrap.bind('esc', function () {
                bindKeysForMainMode();
                hideAllPorts();
            });
            switchDeemphasisAllBlocks();
        } else {
            var port = editor.getCurrentPort();
            if (port !== null) {
                if (portToLinkTo === undefined) {
                    portToLinkTo = port;
                    portToLinkTo.classList.toggle('to-link-to');
                } else if (port.connectable(port, portToLinkTo)) {
                    port.connect(port, portToLinkTo);
                    portToLinkTo.classList.toggle('to-link-to');
                    portToLinkTo = undefined;
                } else {
                    portToLinkTo.classList.toggle('to-link-to');
                    portToLinkTo = port;
                    portToLinkTo.classList.toggle('to-link-to');
                }
            }
        }
    };

    var hidelAllBlocksKeys = function () {
        var blocks = document.querySelectorAll('z-block');
        [].forEach.call(blocks, function (block) {
            block.hideKey();
        });
    };

    var setCurrentBlockAndBackToMainMode = function (block) {
        editor.setCurrentBlock(block);
        hidelAllBlocksKeys();
        bindKeysForMainMode();
    };

    commands.goToBlock = function () {
        Mousetrap.reset();
        var blocks = document.querySelectorAll('z-block');
        var index = 0;
        var keys = utils.createKeysGenerator();
        [].forEach.call(blocks, function (block) {
            var key = keys.next();
            block.querySelector('span.id').innerHTML = key;
            block.showKey();
            // Convert 'aae' into 'a a e'.
            key = key.split('').join(' ');
            Mousetrap.bind(key, setCurrentBlockAndBackToMainMode.bind(null, block));
            index++;
        });
        Mousetrap.bind('esc', function () {
            hidelAllBlocksKeys();
            bindKeysForMainMode();
        });
    };

    // Set a new stopCallback for Moustrap to avoid stopping when we start
    // editing a contenteditable, so that we can use escape to leave editing.
    Mousetrap.stopCallback = function(e, element, combo) {
        // if the element has the class "mousetrap" then no need to stop
        if ((' ' + element.className + ' ').indexOf(' mousetrap ') > -1) {
            return false;
        }

         // stop for input, select, and textarea
         return element.tagName == 'INPUT' || element.tagName == 'SELECT' || element.tagName == 'TEXTAREA';
     };

    commands.save = storage.savePatch;
    commands.load = storage.loadPatch;
    commands.rm = storage.removePatch;
    commands.list = storage.getPatchNames;
    commands.clear = storage.clearAll;

    var terminalOnblur = function () {
        window.bindKeysForMainMode();
        editor.startBlinking();
    };

    var term = terminal.create(commands, terminalOnblur);

    commands.goToCommandLine = function () {
        term.focus();
        Mousetrap.reset();
        editor.stopBlinking();
    };

    // TODO create a term.write(multiLineString) and use it.
    commands.help = function (subject) {
        if (subject === undefined) {
            term.term.write('Press Esc to leave the command line and go back to normal mode.');
            term.term.newLine();
            term.term.newLine();
            term.term.write('Commands: next, prev, remove, add and set content.');
        } else if (subject === 'add') {
            term.term.write('Add a new block just below the current block.');
            term.term.newLine();
            term.term.newLine();
            term.term.write('add html <what> <content> <nb inputs> <nb outputs>');
            term.term.newLine();
            term.term.write('  <what>    is either "button", "script", "text", "number" or a HTML tag.');
            term.term.newLine();
            term.term.write('  <content> is the content of the block (i.e. the button name, the');
            term.term.newLine();
            term.term.write('            script code, the text or number value, etc.).');
        } else {
            term.term.write('No help for "' + subject + '".');
        }
    };

    bindKeysForMainMode();

};
export default com;
<|endoftext|>


<commit_before>const Promise          = require('bluebird');
const parseBoolean     = (value) => value === 'on' ? true : value === 'off' ? false : null;
const parseProgramData = (data)  => {
  return data.filter((point) => point.active === 'on').map((point) => {
    var hour   = (point.t / 60) | 0;
    var minute = point.t % 60;
    return {
      dow  : [ 'Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa' ].indexOf(point.d),
      time : ('00' + hour).slice(-2) + ':' + ('00' + minute).slice(-2),
      temp : point.T
    };
  })
};

module.exports = {
  pressure() {
    return this.get('/system/appliance/systemPressure').then((r) => {
      return { pressure : r.value, unit : r.unitOfMeasure };
    });
  },

  status() {
    return Promise.all([
      this.get('/ecus/rrc/uiStatus'),
      this.get('/system/sensors/temperatures/outdoor_t1'),
    ]).spread((status, outdoor) => {
      var v = status.value;
      return {
        'user mode'                   : v.UMD,
        'clock program'               : v.CPM,
        'in house status'             : v.IHS,
        'in house temp'               : Number(v.IHT),
        'boiler indicator'            : { 'CH' : 'central heating', 'HW' : 'hot water', 'No' : 'off' }[v.BAI] || null,
        'control'                     : v.CTR,
        'temp override duration'      : Number(v.TOD),
        'current switchpoint'         : Number(v.CSP),
        'ps active'                   : parseBoolean(v.ESI),
        'fp active'                   : parseBoolean(v.FPA),
        'temp override'               : parseBoolean(v.TOR),
        'holiday mode'                : parseBoolean(v.HMD),
        'boiler block'                : parseBoolean(v.BBE),
        'boiler lock'                 : parseBoolean(v.BLE),
        'boiler maintainance'         : parseBoolean(v.BMR),
        'temp setpoint'               : Number(v.TSP),
        'temp override temp setpoint' : Number(v.TOT),
        'temp manual setpoint'        : Number(v.MMT),
        'hed enabled'                 : parseBoolean(v.HED_EN),
        'hed device at home'          : parseBoolean(v.HED_DEV),
        'outdoor temp'                : outdoor.value,
        'outdoor source type'         : outdoor.srcType,
      };
    });
  },

  location() {
    return Promise.props({
      lat : this.get('/system/location/latitude') .then((r) => Number(r.value)),
      lng : this.get('/system/location/longitude').then((r) => Number(r.value)),
    });
  },

  program() {
    return Promise.props({
      active   : this.get('/ecus/rrc/userprogram/activeprogram').then((r) => r.value),
      program1 : this.get('/ecus/rrc/userprogram/program1').then((r) => parseProgramData(r.value)),
      program2 : this.get('/ecus/rrc/userprogram/program2').then((r) => parseProgramData(r.value)),
    });
  },

  displayCode() {
    return this.get('/system/appliance/displaycode').then((r) => {
      return {
        code       : r.value,
        description: {
          '-H'  : 'central heating active',
          '=H'  : 'hot water active',
          '0C'  : 'system starting',
          '0L'  : 'system starting',
          '0U'  : 'system starting',
          '0E'  : 'system waiting',
          '0H'  : 'system standby',
          '0A'  : 'system waiting (boiler cannot transfer heat to central heating)',
          '0Y'  : 'system waiting (boiler cannot transfer heat to central heating)',
          '0E'  : 'system waiting (boiler cannot transfer heat to central heating)',
          '2E'  : 'boiler water pressure too low',
          'H07' : 'boiler water pressure too low',
          '2F'  : 'sensors measured abnormal temperature',
          '2L'  : 'sensors measured abnormal temperature',
          '2P'  : 'sensors measured abnormal temperature',
          '2U'  : 'sensors measured abnormal temperature',
          '4F'  : 'sensors measured abnormal temperature',
          '4L'  : 'sensors measured abnormal temperature',
          '6A'  : 'burner doesn\'t ignite',
          '6C'  : 'burner doesn\'t ignite',
          'rE'  : 'system restarting',
        }[r.value]
      };
    });
  },

  setTemperature(value) {
    var data = { value : Number(value) };
    return Promise.all([
      this.put('/heatingCircuits/hc1/temperatureRoomManual',          data),
      this.put('/heatingCircuits/hc1/manualTempOverride/temperature', data),
    ]).then(() => {
      return { status : 'ok' };
    });
  }
};
<commit_msg>Fixed "set temperature" command<commit_after>const Promise          = require('bluebird');
const parseBoolean     = (value) => value === 'on' ? true : value === 'off' ? false : null;
const parseProgramData = (data)  => {
  return data.filter((point) => point.active === 'on').map((point) => {
    var hour   = (point.t / 60) | 0;
    var minute = point.t % 60;
    return {
      dow  : [ 'Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa' ].indexOf(point.d),
      time : ('00' + hour).slice(-2) + ':' + ('00' + minute).slice(-2),
      temp : point.T
    };
  })
};

module.exports = {
  pressure() {
    return this.get('/system/appliance/systemPressure').then((r) => {
      return { pressure : r.value, unit : r.unitOfMeasure };
    });
  },

  status() {
    return Promise.all([
      this.get('/ecus/rrc/uiStatus'),
      this.get('/system/sensors/temperatures/outdoor_t1'),
    ]).spread((status, outdoor) => {
      var v = status.value;
      return {
        'user mode'                   : v.UMD,
        'clock program'               : v.CPM,
        'in house status'             : v.IHS,
        'in house temp'               : Number(v.IHT),
        'boiler indicator'            : { 'CH' : 'central heating', 'HW' : 'hot water', 'No' : 'off' }[v.BAI] || null,
        'control'                     : v.CTR,
        'temp override duration'      : Number(v.TOD),
        'current switchpoint'         : Number(v.CSP),
        'ps active'                   : parseBoolean(v.ESI),
        'fp active'                   : parseBoolean(v.FPA),
        'temp override'               : parseBoolean(v.TOR),
        'holiday mode'                : parseBoolean(v.HMD),
        'boiler block'                : parseBoolean(v.BBE),
        'boiler lock'                 : parseBoolean(v.BLE),
        'boiler maintainance'         : parseBoolean(v.BMR),
        'temp setpoint'               : Number(v.TSP),
        'temp override temp setpoint' : Number(v.TOT),
        'temp manual setpoint'        : Number(v.MMT),
        'hed enabled'                 : parseBoolean(v.HED_EN),
        'hed device at home'          : parseBoolean(v.HED_DEV),
        'outdoor temp'                : outdoor.value,
        'outdoor source type'         : outdoor.srcType,
      };
    });
  },

  location() {
    return Promise.props({
      lat : this.get('/system/location/latitude') .then((r) => Number(r.value)),
      lng : this.get('/system/location/longitude').then((r) => Number(r.value)),
    });
  },

  program() {
    return Promise.props({
      active   : this.get('/ecus/rrc/userprogram/activeprogram').then((r) => r.value),
      program1 : this.get('/ecus/rrc/userprogram/program1').then((r) => parseProgramData(r.value)),
      program2 : this.get('/ecus/rrc/userprogram/program2').then((r) => parseProgramData(r.value)),
    });
  },

  displayCode() {
    return this.get('/system/appliance/displaycode').then((r) => {
      return {
        code       : r.value,
        description: {
          '-H'  : 'central heating active',
          '=H'  : 'hot water active',
          '0C'  : 'system starting',
          '0L'  : 'system starting',
          '0U'  : 'system starting',
          '0E'  : 'system waiting',
          '0H'  : 'system standby',
          '0A'  : 'system waiting (boiler cannot transfer heat to central heating)',
          '0Y'  : 'system waiting (boiler cannot transfer heat to central heating)',
          '0E'  : 'system waiting (boiler cannot transfer heat to central heating)',
          '2E'  : 'boiler water pressure too low',
          'H07' : 'boiler water pressure too low',
          '2F'  : 'sensors measured abnormal temperature',
          '2L'  : 'sensors measured abnormal temperature',
          '2P'  : 'sensors measured abnormal temperature',
          '2U'  : 'sensors measured abnormal temperature',
          '4F'  : 'sensors measured abnormal temperature',
          '4L'  : 'sensors measured abnormal temperature',
          '6A'  : 'burner doesn\'t ignite',
          '6C'  : 'burner doesn\'t ignite',
          'rE'  : 'system restarting',
        }[r.value]
      };
    });
  },

  setTemperature(value) {
    var data = { value : Number(value) };
    return Promise.all([
      this.put('/heatingCircuits/hc1/temperatureRoomManual',          data),
      this.put('/heatingCircuits/hc1/manualTempOverride/status',      { value : 'on' }),
      this.put('/heatingCircuits/hc1/manualTempOverride/temperature', data),
    ]).then(() => {
      return { status : 'ok' };
    });
  }
};
<|endoftext|>


<commit_before>'use strict';

const VDOM_CONFIG = require(`./config`).VDOM_CONFIG;

const OVERRIDABLE_CONFIG_OPTIONS = [
  `marshalDataset`,
  `propsWrapper`,
  `rawProps`,
];

/**
 * Compilation style.
 *
 * We rely on minification, so don't bother with styling.
 * For example, trailling commas on object and arrays are _preferred_.
 *
 * We rely on beautification for "pretty" mode,
 * so don't bother handling indentations and so on.
 */

const debug = require(`debug`)(`virtual-jade:compiler`);

function assertWithDetailedError(node, assertion, message) {
  if (!assertion) {
    const err = new Error(message);
    err.line = node.line;
    err.filename = node.filename;
    throw err;
  }
}

function Compiler(node, options) {
  this.node = node;
  this.options = options;

  // set options from vdom config + overrides
  this.vdom = VDOM_CONFIG[options.vdom || `virtual-dom`];
  for (let optionName of OVERRIDABLE_CONFIG_OPTIONS) {
    const optionSource = this.options.hasOwnProperty(optionName) ? this.options : this.vdom;
    this[optionName] = optionSource[optionName];
  }
}

/**
 * Actually compile the tokens.
 * We want to match the Jade API as much as possible here.
 */

Compiler.prototype.compile = function() {
  let tagged = null;
  let js = ``;

  this.hasObjAttrs = false;
  this.mixins = [];

  for (const node of this.node.nodes) {
    switch (node.type) {
      case `Block`:
        this.visitBlock(node);
        break;
      case `Code`:
        assertWithDetailedError(node, !tagged, `Code must exist before the tag.`);
        js += node.val + `\n`;
        break;
      case `Mixin`:
        this.visitMixin(node);
        break;
      case `Tag`:
        assertWithDetailedError(node, !tagged, `You can only have one top-level tag!`);
        tagged = true;
        js += `return ` + this.visitTag(node);
        break;
    }
  }

  assertWithDetailedError(this.node, tagged, `Exactly a single root element is required!`);

  if (this.hasCode) {
    js = `
      var __vjadeSafeCode = function(code) {
        return code === 0 ? '0' : (code || '');
      };
      ${js}
    `;
  }
  if (this.hasObjAttrs) {
    js = `
      var __objToAttrs = function(o) {
        return Object.keys(o).map(function(k) { return o[k] ? k : false});
      };
      ${js}
    `;
  }

  if (this.mixins.length > 0) {
    js = `var jade_mixins = {};${this.mixins.join(``)}${js}`;
  }

  return js;
};

/**
 * Visit a node, though this is really just meant for the first tag.
 */

Compiler.prototype.visit = function(node) {
  const method = `visit` + node.type;
  assertWithDetailedError(node,
    typeof this[method] === `function`,
    `Node type "${node.type}" is not implemented!`);
  return this[method](node);
};

/**
 * TODO
 */

function toVal(x) {
  return x.val;
}

Compiler.prototype.visitComment =
Compiler.prototype.visitBlockComment = function(node) {
  if (!node.block) return `/* ${node.val} */\n`;
  return `/* ` + node.block.nodes.map(toVal).join(`\n`) + `*/\n`;
};

/**
 * Create basic text nodes.
 */

Compiler.prototype.visitText = function(node) {
  return JSON.stringify(node.val)
    .replace(/#\{(.+?)\}/g, `" + ($1) + "`);
};

/**
 * Wrap props dictionary if necessary
 * e.g. {foo: 'bar'} => {props: {foo: 'bar'}}
 */
Compiler.prototype.wrapProps = function(propsOut, propsWrapper) {
  if (propsOut.length) {
    if (arguments.length < 2 && !this.rawProps) {
      propsWrapper = this.propsWrapper;
    }
    if (propsWrapper) {
      propsOut = `{${propsWrapper}:${propsOut}}`;
    }
  }

  return propsOut;
};

/**
 * Handle a "Literal" include such as an SVG or HTML file
 */
Compiler.prototype.visitLiteral = function(node) {
  const escapedSource = node.str.replace(/'/g, `\\'`).replace(/\n/g, ``);
  const propsOut = this.wrapProps(`{innerHTML: '${escapedSource}'}`, this.propsWrapper);
  return `h('div', ${propsOut})`;
};

/**
 * Build a single HTML element.
 */

Compiler.prototype.visitTag = function(tag) {
  let buf = `h(` + (tag.buffer ? `(${tag.name})` : JSON.stringify(tag.name));
  let propsOut = this.wrapProps(this.visitAttributes(tag.attrs, tag.attributeBlocks));
  if (propsOut.length) {
    buf += `,${propsOut}`;
  }
  // TODO: handle cases when there is both code and blocks
  if (tag.code) {
    this.hasCode = true;
    buf += `, [__vjadeSafeCode(${tag.code.val})]`;
  } else {
    let buf2 = this.visitBlock(tag.block);
    if (buf2) {
      buf += `, ${buf2.trim()}.filter(Boolean)`;
    }
  }
  buf += `)`;
  return buf;
};

/**
 * Visit each attribute in a tag.
 * Note that each property of the attribute is already JS-ready,
 * so don't just JSON.stringify() the entire thing.
 * Also, `virtual-dom` handles properties differently.
 */

// attribute -> property transformations
const ATTRS_TO_PROPS = {
  'class': `className`,
  'for': `htmlFor`,
  'http-equiv': `httpEquiv`,
};
const LOWERCASE_ATTRS = new Set([
  `acceptCharset`,
  `accessKey`,
  `allowFullScreen`,
  `allowTransparency`,
  `cellPadding`,
  `cellSpacing`,
  `colSpan`,
  `contentEditable`,
  `contextMenu`,
  `crossOrigin`,
  `dateTime`,
  `formAction`,
  `formEncType`,
  `formMethod`,
  `formNoValidate`,
  `formTarget`,
  `frameBorder`,
  `marginHeight`,
  `marginWidth`,
  `maxLength`,
  `mediaGroup`,
  `noValidate`,
  `readOnly`,
  `rowSpan`,
  `tabIndex`,
  `useMap`,
]);
for (const a of LOWERCASE_ATTRS) {
  ATTRS_TO_PROPS[a.toLowerCase()] = a;
}

// convert this-type-of-string to thisTypeOfString
function dashedToCamel(s) {
  return s.replace(/-([a-z])/g, group => group[1].toUpperCase());
}

// JSON-stringify keys but not vals, for JS evaluation in output
function outputObj(obj) {
  let buf = ``;
  for (const key of Object.keys(obj)) {
    buf += JSON.stringify(key) + `:` + obj[key] + `,`;
  }
  return buf ? `{${buf}}` : ``;
}

Compiler.prototype.visitAttributes = function(attrs) {
  // first, we need to format the attributes
  const props = Object.create(null);

  if (this.rawProps) {

    // easy-peasy, pass through everything directly except class/id

    const jadeClasses = [];
    let rawClassObj = ``;
    let hasClasses = false;
    let id = ``;
    for (const attr of attrs) {
      if (attr.name === `class`) {
        hasClasses = true;
        if (attr.val[0] === `'`) {
          jadeClasses.push(attr.val);
        } else {
          rawClassObj = attr.val;
        }
      } else if (attr.name === `id` && attr.val[0] === `'`) {
        id = attr.val;
      } else {
        props[attr.name] = attr.val;
      }
    }

    // generate class code
    if (hasClasses) {
      const jadeClassObj = `{${jadeClasses.map(cls => `${cls}:true`).join(`,`)}}`;
      if (rawClassObj) {
        if (jadeClasses.length) {
          props.class = `Object.assign({}, ${rawClassObj}, ${jadeClassObj})`;
        } else {
          props.class = rawClassObj;
        }
      } else {
        props.class = jadeClassObj;
      }
    }

    // generate id code
    if (id) {
      const idObj = `{id: ${id}}`;
      const propsObj = props[this.propsWrapper];
      props[this.propsWrapper] = propsObj ? `Object.assign(${idObj}, ${propsObj})` : idObj;
    }

  } else {

    // translate jade attrs to the appropriate vdom props

    const classExprs = [];
    const dataset = {};
    for (const attr of attrs) {
      let name = ATTRS_TO_PROPS[attr.name] || attr.name;
      let val = attr.val;

      if (name === `className`) {
        /**
         * We need to handle `class` separately because
         * it can be defined multiple times.
         */
        const isObj = !!val.match(/^\s*\{(.|[\r\n])+\}\s*$/);
        if (isObj) {
          this.hasObjAttrs = true;
          val = `__objToAttrs(${val})`;
        }
        classExprs.push(val);
      } else if (this.marshalDataset !== false && name.startsWith(`data-`)) {
        // strip data- and camelcase remainder for dataset
        dataset[dashedToCamel(name.slice(5))] = val;
      } else {
        props[name] = val;
      }
    }
    if (classExprs.length) {
      const classConcat = classExprs.map(e => `.concat(${e})`).join(``);
      props.className = `[]${classConcat}.filter(Boolean).join(' ')`;
    }
    if (Object.keys(dataset).length) {
      props.dataset = outputObj(dataset);
    }
  }

  debug(`properties: %o`, props);

  return outputObj(props);
};

Compiler.prototype.visitBlock = function(block) {
  const nodes = block.nodes;
  const length = nodes.length;
  if (!length) return ``;

  let buf = ``;

  for (let i = 0; i < nodes.length; i++) {
    let curNodeBuf = ``;
    const node = nodes[i];
    switch (node.type) {
      case `Code`:
        assertWithDetailedError(node, !/^\s*else/.test(node.val), `Hanging else statement!`);

        if (/^\s*if\s+/.test(node.val)) {
          // handle if statements
          let ended = false;

          curNodeBuf += node.val.replace(/^\s*if\s+/, ``);
          curNodeBuf += ` ? (${this.visitBlock(node.block)})`;

          for (let j = i + 1; j < nodes.length; j++) {
            const next = nodes[j];
            if (/^\s*else\s+if\s+/.test(next.val)) {
              i++;
              curNodeBuf += ` : ` + next.val.replace(/^\s*else\s+if\s+/, ``);
              curNodeBuf += ` ? (${this.visitBlock(next.block)})`;
            } else if (/^\s*else\s*$/.test(next.val)) {
              i++;
              ended = true;
              curNodeBuf += ` : (${this.visitBlock(next.block)})`;
              break;
            } else {
              break;
            }
          }

          if (!ended) curNodeBuf += ` : undefined`;
          buf += `__jade_nodes = __jade_nodes.concat(${curNodeBuf});`;
        } else if (/^\s*while/.test(node.val)) {
          // handle while loops
          curNodeBuf += `(function(){
            var buf = [];
            ${node.val} {
              buf = buf.concat(${this.visitBlock(node.block)})
            }
            return buf
          }).call(this)`;
          buf += `__jade_nodes = __jade_nodes.concat(${curNodeBuf});`;
        } else if (node.val) {
          // arbitrary code
          buf += node.buffer ? `__jade_nodes = __jade_nodes.concat(${node.val});` : `${node.val};`;
        }

        break;
      case `Comment`:
      case `BlockComment`:
        buf += this.visitComment(node);
        break;
      case `Each`:
        curNodeBuf = this.visitEach(node);
        buf += `__jade_nodes = __jade_nodes.concat(${curNodeBuf});`;
        break;
      default: {
        curNodeBuf = this.visit(node);
        if (curNodeBuf) {
          buf += `__jade_nodes = __jade_nodes.concat(${curNodeBuf});`;
        }
      }
    }
  }

  return buf ? `(function() {var __jade_nodes = [];${buf};return __jade_nodes}).call(this)` : ``;
};

/**
 * Handles jade's special case statements,
 * which breaks it down into a bunch of `if` statements.
 *
 * TODO: maybe memoize the main expression
 */

Compiler.prototype.visitCase = function(code) {
  let str = ``;
  let defaulted = false;
  let conditions = [];

  for (const node of code.block.nodes) {
    if (node.expr === `default`) {
      defaulted = true;
      str += `(${this.visitBlock(node.block)})`;
      break;
    }

    conditions.push(`((${code.expr}) == (${node.expr}))`);
    if (node.block) {
      str += `(${conditions.join(` || `)})
        ? (${this.visitBlock(node.block)})
        : `;
      conditions = [];
    }
  }

  if (!defaulted) str += `undefined`;
  return str;
};

/**
 * Handle each statements
 * uses Array.concat() with result of each iteration to ensure flattening
 */

Compiler.prototype.visitEach = function(code) {
  return `(${code.obj}).reduce(function(__each_nodes, ${code.val}, ${code.key}) {
    return __each_nodes.concat(${this.visitBlock(code.block)});
  }, [])`;
};

/**
 * Handle mixin declarations and calls
 */

Compiler.prototype.visitMixin = function(mixin) {
  let str = ``;
  let args = mixin.args || ``;
  let rest = ``;
  let initRest = ``;

  if (mixin.call) {

    // mixin call

    let ctx;
    const ctxItems = [];
    if (mixin.block) {
      ctxItems.push(`block: function() { return ${this.visitBlock(mixin.block)}; }`);
    }
    if (mixin.attrs.length) {
      ctxItems.push(`attributes: ` + this.visitAttributes(mixin.attrs));
    }
    if (ctxItems.length) {
      ctx = `{${ctxItems.join(`,`)}}`;
    } else {
      ctx = `this`;
    }
    if (args) {
      args = `, ${args}`;
    }
    str = `jade_mixins['${mixin.name}'].call(${ctx}${args})`;

  } else {

    // mixin declaration

    args = args ? args.split(`,`) : [];
    if (args.length && /^\.\.\./.test(args[args.length - 1].trim())) {
      rest = args.pop().trim().replace(/^\.\.\./, ``);
    }
    if (rest) {
      initRest = `
        var ${rest} = [];
        for (var jade_interp = ${args.length}; jade_interp < arguments.length; jade_interp++) {
          ${rest}.push(arguments[jade_interp]);
        }
      `;
    }
    this.mixins.push(`
      jade_mixins['${mixin.name}'] = function(${args.join(`,`)}) {
        var block = (this && this.block), attributes = (this && this.attributes) || {};
        ${initRest}
        return ${this.visitBlock(mixin.block)};
      };
    `);

  }

  return str;
};

/**
 * Handle mixin's `block` keyword
 */

Compiler.prototype.visitMixinBlock = function() {
  return `block && block()`;
};

module.exports = Compiler;
<commit_msg>more namespacing for __objToAttrs helper<commit_after>'use strict';

const VDOM_CONFIG = require(`./config`).VDOM_CONFIG;

const OVERRIDABLE_CONFIG_OPTIONS = [
  `marshalDataset`,
  `propsWrapper`,
  `rawProps`,
];

/**
 * Compilation style.
 *
 * We rely on minification, so don't bother with styling.
 * For example, trailling commas on object and arrays are _preferred_.
 *
 * We rely on beautification for "pretty" mode,
 * so don't bother handling indentations and so on.
 */

const debug = require(`debug`)(`virtual-jade:compiler`);

function assertWithDetailedError(node, assertion, message) {
  if (!assertion) {
    const err = new Error(message);
    err.line = node.line;
    err.filename = node.filename;
    throw err;
  }
}

function Compiler(node, options) {
  this.node = node;
  this.options = options;

  // set options from vdom config + overrides
  this.vdom = VDOM_CONFIG[options.vdom || `virtual-dom`];
  for (let optionName of OVERRIDABLE_CONFIG_OPTIONS) {
    const optionSource = this.options.hasOwnProperty(optionName) ? this.options : this.vdom;
    this[optionName] = optionSource[optionName];
  }
}

/**
 * Actually compile the tokens.
 * We want to match the Jade API as much as possible here.
 */

Compiler.prototype.compile = function() {
  let tagged = null;
  let js = ``;

  this.hasObjAttrs = false;
  this.mixins = [];

  for (const node of this.node.nodes) {
    switch (node.type) {
      case `Block`:
        this.visitBlock(node);
        break;
      case `Code`:
        assertWithDetailedError(node, !tagged, `Code must exist before the tag.`);
        js += node.val + `\n`;
        break;
      case `Mixin`:
        this.visitMixin(node);
        break;
      case `Tag`:
        assertWithDetailedError(node, !tagged, `You can only have one top-level tag!`);
        tagged = true;
        js += `return ` + this.visitTag(node);
        break;
    }
  }

  assertWithDetailedError(this.node, tagged, `Exactly a single root element is required!`);

  if (this.hasCode) {
    js = `
      var __vjadeSafeCode = function(code) {
        return code === 0 ? '0' : (code || '');
      };
      ${js}
    `;
  }
  if (this.hasObjAttrs) {
    js = `
      var __vjadeObjToAttrs = function(o) {
        return Object.keys(o).map(function(k) { return o[k] ? k : false});
      };
      ${js}
    `;
  }

  if (this.mixins.length > 0) {
    js = `var jade_mixins = {};${this.mixins.join(``)}${js}`;
  }

  return js;
};

/**
 * Visit a node, though this is really just meant for the first tag.
 */

Compiler.prototype.visit = function(node) {
  const method = `visit` + node.type;
  assertWithDetailedError(node,
    typeof this[method] === `function`,
    `Node type "${node.type}" is not implemented!`);
  return this[method](node);
};

/**
 * TODO
 */

function toVal(x) {
  return x.val;
}

Compiler.prototype.visitComment =
Compiler.prototype.visitBlockComment = function(node) {
  if (!node.block) return `/* ${node.val} */\n`;
  return `/* ` + node.block.nodes.map(toVal).join(`\n`) + `*/\n`;
};

/**
 * Create basic text nodes.
 */

Compiler.prototype.visitText = function(node) {
  return JSON.stringify(node.val)
    .replace(/#\{(.+?)\}/g, `" + ($1) + "`);
};

/**
 * Wrap props dictionary if necessary
 * e.g. {foo: 'bar'} => {props: {foo: 'bar'}}
 */
Compiler.prototype.wrapProps = function(propsOut, propsWrapper) {
  if (propsOut.length) {
    if (arguments.length < 2 && !this.rawProps) {
      propsWrapper = this.propsWrapper;
    }
    if (propsWrapper) {
      propsOut = `{${propsWrapper}:${propsOut}}`;
    }
  }

  return propsOut;
};

/**
 * Handle a "Literal" include such as an SVG or HTML file
 */
Compiler.prototype.visitLiteral = function(node) {
  const escapedSource = node.str.replace(/'/g, `\\'`).replace(/\n/g, ``);
  const propsOut = this.wrapProps(`{innerHTML: '${escapedSource}'}`, this.propsWrapper);
  return `h('div', ${propsOut})`;
};

/**
 * Build a single HTML element.
 */

Compiler.prototype.visitTag = function(tag) {
  let buf = `h(` + (tag.buffer ? `(${tag.name})` : JSON.stringify(tag.name));
  let propsOut = this.wrapProps(this.visitAttributes(tag.attrs, tag.attributeBlocks));
  if (propsOut.length) {
    buf += `,${propsOut}`;
  }
  // TODO: handle cases when there is both code and blocks
  if (tag.code) {
    this.hasCode = true;
    buf += `, [__vjadeSafeCode(${tag.code.val})]`;
  } else {
    let buf2 = this.visitBlock(tag.block);
    if (buf2) {
      buf += `, ${buf2.trim()}.filter(Boolean)`;
    }
  }
  buf += `)`;
  return buf;
};

/**
 * Visit each attribute in a tag.
 * Note that each property of the attribute is already JS-ready,
 * so don't just JSON.stringify() the entire thing.
 * Also, `virtual-dom` handles properties differently.
 */

// attribute -> property transformations
const ATTRS_TO_PROPS = {
  'class': `className`,
  'for': `htmlFor`,
  'http-equiv': `httpEquiv`,
};
const LOWERCASE_ATTRS = new Set([
  `acceptCharset`,
  `accessKey`,
  `allowFullScreen`,
  `allowTransparency`,
  `cellPadding`,
  `cellSpacing`,
  `colSpan`,
  `contentEditable`,
  `contextMenu`,
  `crossOrigin`,
  `dateTime`,
  `formAction`,
  `formEncType`,
  `formMethod`,
  `formNoValidate`,
  `formTarget`,
  `frameBorder`,
  `marginHeight`,
  `marginWidth`,
  `maxLength`,
  `mediaGroup`,
  `noValidate`,
  `readOnly`,
  `rowSpan`,
  `tabIndex`,
  `useMap`,
]);
for (const a of LOWERCASE_ATTRS) {
  ATTRS_TO_PROPS[a.toLowerCase()] = a;
}

// convert this-type-of-string to thisTypeOfString
function dashedToCamel(s) {
  return s.replace(/-([a-z])/g, group => group[1].toUpperCase());
}

// JSON-stringify keys but not vals, for JS evaluation in output
function outputObj(obj) {
  let buf = ``;
  for (const key of Object.keys(obj)) {
    buf += JSON.stringify(key) + `:` + obj[key] + `,`;
  }
  return buf ? `{${buf}}` : ``;
}

Compiler.prototype.visitAttributes = function(attrs) {
  // first, we need to format the attributes
  const props = Object.create(null);

  if (this.rawProps) {

    // easy-peasy, pass through everything directly except class/id

    const jadeClasses = [];
    let rawClassObj = ``;
    let hasClasses = false;
    let id = ``;
    for (const attr of attrs) {
      if (attr.name === `class`) {
        hasClasses = true;
        if (attr.val[0] === `'`) {
          jadeClasses.push(attr.val);
        } else {
          rawClassObj = attr.val;
        }
      } else if (attr.name === `id` && attr.val[0] === `'`) {
        id = attr.val;
      } else {
        props[attr.name] = attr.val;
      }
    }

    // generate class code
    if (hasClasses) {
      const jadeClassObj = `{${jadeClasses.map(cls => `${cls}:true`).join(`,`)}}`;
      if (rawClassObj) {
        if (jadeClasses.length) {
          props.class = `Object.assign({}, ${rawClassObj}, ${jadeClassObj})`;
        } else {
          props.class = rawClassObj;
        }
      } else {
        props.class = jadeClassObj;
      }
    }

    // generate id code
    if (id) {
      const idObj = `{id: ${id}}`;
      const propsObj = props[this.propsWrapper];
      props[this.propsWrapper] = propsObj ? `Object.assign(${idObj}, ${propsObj})` : idObj;
    }

  } else {

    // translate jade attrs to the appropriate vdom props

    const classExprs = [];
    const dataset = {};
    for (const attr of attrs) {
      let name = ATTRS_TO_PROPS[attr.name] || attr.name;
      let val = attr.val;

      if (name === `className`) {
        /**
         * We need to handle `class` separately because
         * it can be defined multiple times.
         */
        const isObj = !!val.match(/^\s*\{(.|[\r\n])+\}\s*$/);
        if (isObj) {
          this.hasObjAttrs = true;
          val = `__vjadeObjToAttrs(${val})`;
        }
        classExprs.push(val);
      } else if (this.marshalDataset !== false && name.startsWith(`data-`)) {
        // strip data- and camelcase remainder for dataset
        dataset[dashedToCamel(name.slice(5))] = val;
      } else {
        props[name] = val;
      }
    }
    if (classExprs.length) {
      const classConcat = classExprs.map(e => `.concat(${e})`).join(``);
      props.className = `[]${classConcat}.filter(Boolean).join(' ')`;
    }
    if (Object.keys(dataset).length) {
      props.dataset = outputObj(dataset);
    }
  }

  debug(`properties: %o`, props);

  return outputObj(props);
};

Compiler.prototype.visitBlock = function(block) {
  const nodes = block.nodes;
  const length = nodes.length;
  if (!length) return ``;

  let buf = ``;

  for (let i = 0; i < nodes.length; i++) {
    let curNodeBuf = ``;
    const node = nodes[i];
    switch (node.type) {
      case `Code`:
        assertWithDetailedError(node, !/^\s*else/.test(node.val), `Hanging else statement!`);

        if (/^\s*if\s+/.test(node.val)) {
          // handle if statements
          let ended = false;

          curNodeBuf += node.val.replace(/^\s*if\s+/, ``);
          curNodeBuf += ` ? (${this.visitBlock(node.block)})`;

          for (let j = i + 1; j < nodes.length; j++) {
            const next = nodes[j];
            if (/^\s*else\s+if\s+/.test(next.val)) {
              i++;
              curNodeBuf += ` : ` + next.val.replace(/^\s*else\s+if\s+/, ``);
              curNodeBuf += ` ? (${this.visitBlock(next.block)})`;
            } else if (/^\s*else\s*$/.test(next.val)) {
              i++;
              ended = true;
              curNodeBuf += ` : (${this.visitBlock(next.block)})`;
              break;
            } else {
              break;
            }
          }

          if (!ended) curNodeBuf += ` : undefined`;
          buf += `__jade_nodes = __jade_nodes.concat(${curNodeBuf});`;
        } else if (/^\s*while/.test(node.val)) {
          // handle while loops
          curNodeBuf += `(function(){
            var buf = [];
            ${node.val} {
              buf = buf.concat(${this.visitBlock(node.block)})
            }
            return buf
          }).call(this)`;
          buf += `__jade_nodes = __jade_nodes.concat(${curNodeBuf});`;
        } else if (node.val) {
          // arbitrary code
          buf += node.buffer ? `__jade_nodes = __jade_nodes.concat(${node.val});` : `${node.val};`;
        }

        break;
      case `Comment`:
      case `BlockComment`:
        buf += this.visitComment(node);
        break;
      case `Each`:
        curNodeBuf = this.visitEach(node);
        buf += `__jade_nodes = __jade_nodes.concat(${curNodeBuf});`;
        break;
      default: {
        curNodeBuf = this.visit(node);
        if (curNodeBuf) {
          buf += `__jade_nodes = __jade_nodes.concat(${curNodeBuf});`;
        }
      }
    }
  }

  return buf ? `(function() {var __jade_nodes = [];${buf};return __jade_nodes}).call(this)` : ``;
};

/**
 * Handles jade's special case statements,
 * which breaks it down into a bunch of `if` statements.
 *
 * TODO: maybe memoize the main expression
 */

Compiler.prototype.visitCase = function(code) {
  let str = ``;
  let defaulted = false;
  let conditions = [];

  for (const node of code.block.nodes) {
    if (node.expr === `default`) {
      defaulted = true;
      str += `(${this.visitBlock(node.block)})`;
      break;
    }

    conditions.push(`((${code.expr}) == (${node.expr}))`);
    if (node.block) {
      str += `(${conditions.join(` || `)})
        ? (${this.visitBlock(node.block)})
        : `;
      conditions = [];
    }
  }

  if (!defaulted) str += `undefined`;
  return str;
};

/**
 * Handle each statements
 * uses Array.concat() with result of each iteration to ensure flattening
 */

Compiler.prototype.visitEach = function(code) {
  return `(${code.obj}).reduce(function(__each_nodes, ${code.val}, ${code.key}) {
    return __each_nodes.concat(${this.visitBlock(code.block)});
  }, [])`;
};

/**
 * Handle mixin declarations and calls
 */

Compiler.prototype.visitMixin = function(mixin) {
  let str = ``;
  let args = mixin.args || ``;
  let rest = ``;
  let initRest = ``;

  if (mixin.call) {

    // mixin call

    let ctx;
    const ctxItems = [];
    if (mixin.block) {
      ctxItems.push(`block: function() { return ${this.visitBlock(mixin.block)}; }`);
    }
    if (mixin.attrs.length) {
      ctxItems.push(`attributes: ` + this.visitAttributes(mixin.attrs));
    }
    if (ctxItems.length) {
      ctx = `{${ctxItems.join(`,`)}}`;
    } else {
      ctx = `this`;
    }
    if (args) {
      args = `, ${args}`;
    }
    str = `jade_mixins['${mixin.name}'].call(${ctx}${args})`;

  } else {

    // mixin declaration

    args = args ? args.split(`,`) : [];
    if (args.length && /^\.\.\./.test(args[args.length - 1].trim())) {
      rest = args.pop().trim().replace(/^\.\.\./, ``);
    }
    if (rest) {
      initRest = `
        var ${rest} = [];
        for (var jade_interp = ${args.length}; jade_interp < arguments.length; jade_interp++) {
          ${rest}.push(arguments[jade_interp]);
        }
      `;
    }
    this.mixins.push(`
      jade_mixins['${mixin.name}'] = function(${args.join(`,`)}) {
        var block = (this && this.block), attributes = (this && this.attributes) || {};
        ${initRest}
        return ${this.visitBlock(mixin.block)};
      };
    `);

  }

  return str;
};

/**
 * Handle mixin's `block` keyword
 */

Compiler.prototype.visitMixinBlock = function() {
  return `block && block()`;
};

module.exports = Compiler;
<|endoftext|>


<commit_before>var path = require('path');
var os = require('os');
var child = require('child_process');
var mkdirp = require('mkdirp');

module.exports.build = function(config, callback) {
  _prepare(config, function(error, options) {
    _compile(options, function(error) {
      return callback(null, error);
    });
  });
}

function _compile(options, callback) {
  // assemble all options and flags for Arduino Builder based on the facts
  var builderString = [
    options.paths.builderExec,
    '-compile',
    '-hardware="' + options.paths.hardware + '"',
    '-hardware="' + path.dirname(options.paths.jewelbotsLib) + '"',
    '-tools="' + options.paths.tools + '"',
    '-tools="' + options.paths.toolsBuilder + '"',
    '-tools="' + path.dirname(options.paths.jewelbotsLib) + '"',
    '-fqbn="Jewelbots_Arduino_Library:nRF51822:nRF51822"',
    '-built-in-libraries="' + options.paths.libs + '"',
    // customLibsArgs.join(' '),
    '-built-in-libraries="' + path.dirname(options.paths.jewelbotsLib) + '"',
    '-ide-version="10609"',
    '-build-path="' + options.paths.dest + '"',
    '-debug-level="2"',
    '-warnings=none',
    '"' + options.paths.sketchfile + '"'
  ].join(' ');

  // console.log(builderString);

  mkdirp(options.paths.dest, function(error) {
    if (error) return callback(error);
    var cp = child.exec(builderString, function(error, stdout, stderr) {
      return callback(null, error);
    });
    cp.stdout.pipe(process.stdout);
  });
}

function _prepare(config, callback) {
  var options = {
    paths: {}
  };

  var builderPath = config['arduino-app'];

  switch (os.platform()) {

    // this is not so DRY
    case 'darwin': {
      // theoretically the user would supply the direct path to the Arduino.app location, including the app file name
      builderPath = path.join(config['arduino-app'], 'Contents', 'Java');
      options.paths.builderExec = path.join(builderPath, 'arduino-builder');
      break;
    }

    case 'linux': {
      options.paths.builderExec = path.join(builderPath, 'arduino-builder');
      break;
    }

    case 'win32': {
      options.paths.builderExec = '"' + path.join(builderPath, 'arduino-builder') + '"';
      break;
    }
  }
    options.paths.tools = path.join(builderPath, 'hardware', 'tools');
    options.paths.libs =  path.join(builderPath, 'libraries');
    options.paths.hardware = path.join(builderPath, 'hardware');
    options.paths.toolsBuilder = path.join(builderPath, 'tools-builder');
    options.paths.jewelbotsLib = path.resolve(config['jewelbots-lib']);
    options.paths.dest = path.resolve(config['build-destination']);
    options.paths.sketchfile = path.resolve(config['sketch-file']);

  // make a new temp directory here, push it to the options, and return the callback.

  return callback(null, options);
}
<commit_msg>turn up debug level to 10<commit_after>var path = require('path');
var os = require('os');
var child = require('child_process');
var mkdirp = require('mkdirp');

module.exports.build = function(config, callback) {
  _prepare(config, function(error, options) {
    _compile(options, function(error) {
      return callback(null, error);
    });
  });
}

function _compile(options, callback) {
  // assemble all options and flags for Arduino Builder based on the facts
  var builderString = [
    options.paths.builderExec,
    '-compile',
    '-hardware="' + options.paths.hardware + '"',
    '-hardware="' + path.dirname(options.paths.jewelbotsLib) + '"',
    '-tools="' + options.paths.tools + '"',
    '-tools="' + options.paths.toolsBuilder + '"',
    '-tools="' + path.dirname(options.paths.jewelbotsLib) + '"',
    '-fqbn="Jewelbots_Arduino_Library:nRF51822:nRF51822"',
    '-built-in-libraries="' + options.paths.libs + '"',
    // customLibsArgs.join(' '),
    '-built-in-libraries="' + options.paths.jewelbotsLib + '"',
    '-ide-version="10609"',
    '-build-path="' + options.paths.dest + '"',
    '-debug-level="10"',
    '-warnings=none',
    '"' + options.paths.sketchfile + '"'
  ].join(' ');

  // console.log(builderString);

  mkdirp(options.paths.dest, function(error) {
    if (error) return callback(error);
    var cp = child.exec(builderString, function(error, stdout, stderr) {
      return callback(null, error);
    });
    cp.stdout.pipe(process.stdout);
  });
}

function _prepare(config, callback) {
  var options = {
    paths: {}
  };

  var builderPath = config['arduino-app'];

  switch (os.platform()) {

    // this is not so DRY
    case 'darwin': {
      // theoretically the user would supply the direct path to the Arduino.app location, including the app file name
      builderPath = path.join(config['arduino-app'], 'Contents', 'Java');
      options.paths.builderExec = path.join(builderPath, 'arduino-builder');
      break;
    }

    case 'linux': {
      options.paths.builderExec = path.join(builderPath, 'arduino-builder');
      break;
    }

    case 'win32': {
      options.paths.builderExec = '"' + path.join(builderPath, 'arduino-builder') + '"';
      break;
    }
  }
    options.paths.tools = path.join(builderPath, 'hardware', 'tools');
    options.paths.libs =  path.join(builderPath, 'libraries');
    options.paths.hardware = path.join(builderPath, 'hardware');
    options.paths.toolsBuilder = path.join(builderPath, 'tools-builder');
    options.paths.jewelbotsLib = path.resolve(config['jewelbots-lib']);
    options.paths.dest = path.resolve(config['build-destination']);
    options.paths.sketchfile = path.resolve(config['sketch-file']);

  // make a new temp directory here, push it to the options, and return the callback.

  return callback(null, options);
}
<|endoftext|>


<commit_before>var EventEmitter = require('events').EventEmitter;
var util = require('util');

var DEFAULT_MAX_LENGTH = 1 << 14;	// default max package size: 16KB
var LEFT_SHIFT_BITS = 1 << 7;

var ST_LENGTH = 1;	// state that we should read length
var ST_DATA = 2;	// state that we should read data
var ST_ERROR = 3;	// state that something wrong has happened

var Composer = function(opts) {
	EventEmitter.call(this);

	opts = opts || {};
	this.maxLength = opts.maxLength || DEFAULT_MAX_LENGTH;

	this.offset = 0;
	this.left = 0;
	this.length = 0;
	this.buf = null;

	this.state = ST_LENGTH;
};

module.exports = Composer;

util.inherits(Composer, EventEmitter);

var pro = Composer.prototype;

/**
 * Compose data into package.
 *
 * @param  {String|Buffer}	data data that would be composed.
 * @return {Buffer}      	compose result in Buffer.
 */
pro.compose = function(data) {
	if(typeof data === 'string') {
		data = new Buffer(data, 'utf-8');
	}

	if(!(data instanceof Buffer)) {
		throw new Error('data should be an instance of String or Buffer');
	}

	if(data.length === 0) {
		throw new Error('data should not be empty.');
	}

	if(data.length > this.maxLength) {
		throw new Error('data length exceeds the limitation:' + this.maxLength);
	}

	var lsize = calLengthSize(data.length);
	var buf = new Buffer(lsize + data.length);
	fillLength(buf, data.length, lsize);
	data.copy(buf, lsize);

	return buf;
};

/**
 * Feed data into composer. It would emit the package by an event when the package finished.
 *
 * @param  {Buffer} data   next chunk of data read from stream.
 * @param  {Number} offset (Optional) offset index of the data Buffer. 0 by default.
 * @param  {Number} end    (Optional) end index (not includ) of the data Buffer. data.lenght by default.
 * @return {Void}
 */
pro.feed = function(data, offset, end) {
	if(!data) {
		return;
	}

	if(this.state === ST_ERROR) {
		throw new Error('compose in error state, reset it first');
	}

	offset = offset || 0;
	end = end || data.length;
	while(offset < end) {
		if(this.state === ST_LENGTH) {
			offset = this._readLength(data, offset, end);
		}

		if(this.state === ST_DATA) {
			offset = this._readData(data, offset, end);
		}

		if(this.state === ST_ERROR) {
			break;
		}
	}
};

/**
 * Reset composer to the init status.
 */
pro.reset = function() {
	this.state = ST_LENGTH;
	this.buf = null;
	this.length = 0;
	this.offset = 0;
	this.left = 0;
};

// read length part of package
pro._readLength = function(data, offset, end) {
	var b, i, length = this.length, finish;
	for(i=0; i<end; i++) {
		b = data.readUInt8(i + offset);
		length *= LEFT_SHIFT_BITS;		// left shift only within 32 bits
		length += (b & 0x7f);

		if(length > this.maxLength) {
			this.state = ST_ERROR;
			this.emit('length_limit', this, data, offset);
			return -1;
		}

		if(!(b & 0x80)) {
			i++;
			finish = true;
			break;
		}
	}

	this.length = length;

	if(finish) {
		this.state = ST_DATA;
		this.offset = 0;
		this.left = this.length;
		this.buf = new Buffer(this.length);
	}
	return i + offset;
};

// read data part of package
pro._readData = function(data, offset, end) {
	var left = end - offset;
	var size = Math.min(left, this.left);
	data.copy(this.buf, this.offset, offset, offset + size);
	this.left -= size;
	this.offset += size;

	if(this.left === 0) {
		var buf = this.buf;
		this.reset();
		this.emit('data', buf);
	}

	return offset + size;
};

var calLengthSize = function(length) {
	var res = 0;
	while(length > 0) {
		length >>>= 7;
		res++;
	}

	return res;
};

var fillLength = function(buf, data, size) {
	var offset = size - 1, b;
	for(; offset >= 0; offset--) {
		b = data % LEFT_SHIFT_BITS;
		if(offset < size - 1) {
			b |= 0x80;
		}
		buf.writeUInt8(b, offset);
		data >>>= 7;
	}
};<commit_msg>modify defalut package limit<commit_after>var EventEmitter = require('events').EventEmitter;
var util = require('util');

var DEFAULT_MAX_LENGTH = -1;	// default max package size: unlimited
var LEFT_SHIFT_BITS = 1 << 7;

var ST_LENGTH = 1;	// state that we should read length
var ST_DATA = 2;	// state that we should read data
var ST_ERROR = 3;	// state that something wrong has happened

var Composer = function(opts) {
	EventEmitter.call(this);

	opts = opts || {};
	this.maxLength = opts.maxLength || DEFAULT_MAX_LENGTH;

	this.offset = 0;
	this.left = 0;
	this.length = 0;
	this.buf = null;

	this.state = ST_LENGTH;
};

module.exports = Composer;

util.inherits(Composer, EventEmitter);

var pro = Composer.prototype;

/**
 * Compose data into package.
 *
 * @param  {String|Buffer}	data data that would be composed.
 * @return {Buffer}      	compose result in Buffer.
 */
pro.compose = function(data) {
	if(typeof data === 'string') {
		data = new Buffer(data, 'utf-8');
	}

	if(!(data instanceof Buffer)) {
		throw new Error('data should be an instance of String or Buffer');
	}

	if(data.length === 0) {
		throw new Error('data should not be empty.');
	}

	if(this.maxLength > 0 && data.length > this.maxLength) {
		throw new Error('data length exceeds the limitation:' + this.maxLength);
	}

	var lsize = calLengthSize(data.length);
	var buf = new Buffer(lsize + data.length);
	fillLength(buf, data.length, lsize);
	data.copy(buf, lsize);

	return buf;
};

/**
 * Feed data into composer. It would emit the package by an event when the package finished.
 *
 * @param  {Buffer} data   next chunk of data read from stream.
 * @param  {Number} offset (Optional) offset index of the data Buffer. 0 by default.
 * @param  {Number} end    (Optional) end index (not includ) of the data Buffer. data.lenght by default.
 * @return {Void}
 */
pro.feed = function(data, offset, end) {
	if(!data) {
		return;
	}

	if(this.state === ST_ERROR) {
		throw new Error('compose in error state, reset it first');
	}

	offset = offset || 0;
	end = end || data.length;
	while(offset < end) {
		if(this.state === ST_LENGTH) {
			offset = this._readLength(data, offset, end);
		}

		if(this.state === ST_DATA) {
			offset = this._readData(data, offset, end);
		}

		if(this.state === ST_ERROR) {
			break;
		}
	}
};

/**
 * Reset composer to the init status.
 */
pro.reset = function() {
	this.state = ST_LENGTH;
	this.buf = null;
	this.length = 0;
	this.offset = 0;
	this.left = 0;
};

// read length part of package
pro._readLength = function(data, offset, end) {
	var b, i, length = this.length, finish;
	for(i=0; i<end; i++) {
		b = data.readUInt8(i + offset);
		length *= LEFT_SHIFT_BITS;		// left shift only within 32 bits
		length += (b & 0x7f);

		if(length > this.maxLength) {
			this.state = ST_ERROR;
			this.emit('length_limit', this, data, offset);
			return -1;
		}

		if(!(b & 0x80)) {
			i++;
			finish = true;
			break;
		}
	}

	this.length = length;

	if(finish) {
		this.state = ST_DATA;
		this.offset = 0;
		this.left = this.length;
		this.buf = new Buffer(this.length);
	}
	return i + offset;
};

// read data part of package
pro._readData = function(data, offset, end) {
	var left = end - offset;
	var size = Math.min(left, this.left);
	data.copy(this.buf, this.offset, offset, offset + size);
	this.left -= size;
	this.offset += size;

	if(this.left === 0) {
		var buf = this.buf;
		this.reset();
		this.emit('data', buf);
	}

	return offset + size;
};

var calLengthSize = function(length) {
	var res = 0;
	while(length > 0) {
		length >>>= 7;
		res++;
	}

	return res;
};

var fillLength = function(buf, data, size) {
	var offset = size - 1, b;
	for(; offset >= 0; offset--) {
		b = data % LEFT_SHIFT_BITS;
		if(offset < size - 1) {
			b |= 0x80;
		}
		buf.writeUInt8(b, offset);
		data >>>= 7;
	}
};<|endoftext|>


<commit_before>'use strict';

const fs = require('fs');
const path = require('path');
const fileConfig = require('./file.config.js');
const _ = require('lodash');
const co = require('co');
const thunkify = require('thunkify');
/**
 * 写数据操作
 * @param  {String}   fd   路径
 * @param  {String}   data 数据内容
 * @param  {Function} cb   [description]
 * @return {[type]}        [description]
 */
function writeFile(fd, data, cb) {
    fs.writeFile(fd, data, function(err) {
        if (err) {
            throw err;
        }
        cb();
    });
}
/**
 * 过滤文件内容
 * @param  {[type]} fData             [description]
 * @param  {[type]} key               [description]
 * @param  {[type]} svnConfig         [description]
 * @param  {[type]} webpackConfigInfo [description]
 * @return {[type]}                   [description]
 */
function filterFileContent(fData, key, svnConfig, webpackConfigInfo) {
    switch (key) {
        case 'qb.html':
            //没有svnConfig的配置信息的时候不生成qb.html
            if (!svnConfig) {
                return false;
            }
            //svn 的trunk目录下的assets文件目录
            fData = fData.replace('onlineSvnPathStr', svnConfig.onlinePath);
            //svn qb上线到的tag目录
            fData = fData.replace('tagPathStr', svnConfig.tagPath);
            //标签名称  这里应该是文件的名字
            fData = fData.replace('tagNameStr', svnConfig.tagName);
            //标签内新增目录
            fData = fData.replace('addPathStr', svnConfig.addPath);
            break;
        case 'config.js':
            fData = fData.replace('{{publicPath}}', webpackConfigInfo.devPubilcPath);
            fData = fData.replace('{{onlinePath}}', webpackConfigInfo.onLinePubilcPath);
            break;
    }
    return fData;
}
/**
 * 拷贝文件工作
 * @param  {[type]} webpackConfigInfo [description]
 * @param  {[type]} svnConfig         [description]
 * @param  {[type]} rootDir           [description]
 * @return {[type]}                   [description]
 */
function copyFile(webpackConfigInfo, svnConfig, rootDir) {
    return co(function*() {
        for (let key in fileConfig) {
            let filePath = path.join(__dirname, '..', 'config', key);
            let fileContent = yield thunkify(fs.readFile)(filePath, 'utf-8');
            fileContent = filterFileContent(fileContent, key, svnConfig, webpackConfigInfo);
            if(!fileContent){
                continue;
            }
            yield thunkify(fs.writeFile)(path.resolve(rootDir, fileConfig[key], key), fileContent);
        }
    });
}

exports.copyFile = copyFile;

// module.exports = {
//     /**
//      * 复制文件
//      * @param  {Object} webpackConfigInfo webpack相关动态配置
//      * @param  {Object} svnConfig         SVN相关配置
//      * @param  {String} rootDir           生成项目的根路径
//      * @param  {Function} cb              文件复制完毕的回调函数
//      */
//     copy: function(webpackConfigInfo, svnConfig, rootDir, cb) {
//         let count = _.size(fileConfig);
//         _.each(fileConfig, function(val, key) {
//
//             fs.readFile(path.resolve(__dirname, '..', 'config', key), 'utf-8', function(err, fData) {
//                 if (err) {
//                     throw err;
//                 }
//                 switch (key) {
//                     case 'qb.html':
//                         //没有svnConfig的配置信息的时候不生成qb.html
//                         if (!svnConfig) {
//                             count--;
//                             return;
//                         }
//                         //svn 的trunk目录下的assets文件目录
//                         fData = fData.replace('onlineSvnPathStr', svnConfig.onlinePath);
//                         //svn qb上线到的tag目录
//                         fData = fData.replace('tagPathStr', svnConfig.tagPath);
//                         //标签名称  这里应该是文件的名字
//                         fData = fData.replace('tagNameStr', svnConfig.tagName);
//                         //标签内新增目录
//                         fData = fData.replace('addPathStr', svnConfig.addPath);
//                         break;
//                     case 'config.js':
//                         fData = fData.replace('{{publicPath}}', webpackConfigInfo.devPubilcPath);
//                         fData = fData.replace('{{onlinePath}}', webpackConfigInfo.onLinePubilcPath);
//                         break;
//                 }
//                 writeFile(path.resolve(rootDir, val, key), fData, function() {
//                     count--;
//                     if (count === 0) {
//                         cb();
//                     }
//                 });
//             });
//         });
//     }
// }
<commit_msg>删除多于的code<commit_after>'use strict';

const fs = require('fs');
const path = require('path');
const fileConfig = require('./file.config.js');
const _ = require('lodash');
const co = require('co');
const thunkify = require('thunkify');

/**
 * 过滤文件内容
 * @param  {[type]} fData             [description]
 * @param  {[type]} key               [description]
 * @param  {[type]} svnConfig         [description]
 * @param  {[type]} webpackConfigInfo [description]
 * @return {[type]}                   [description]
 */
function filterFileContent(fData, key, svnConfig, webpackConfigInfo) {
    switch (key) {
        case 'qb.html':
            //没有svnConfig的配置信息的时候不生成qb.html
            if (!svnConfig) {
                return false;
            }
            //svn 的trunk目录下的assets文件目录
            fData = fData.replace('onlineSvnPathStr', svnConfig.onlinePath);
            //svn qb上线到的tag目录
            fData = fData.replace('tagPathStr', svnConfig.tagPath);
            //标签名称  这里应该是文件的名字
            fData = fData.replace('tagNameStr', svnConfig.tagName);
            //标签内新增目录
            fData = fData.replace('addPathStr', svnConfig.addPath);
            break;
        case 'config.js':
            fData = fData.replace('{{publicPath}}', webpackConfigInfo.devPubilcPath);
            fData = fData.replace('{{onlinePath}}', webpackConfigInfo.onLinePubilcPath);
            break;
    }
    return fData;
}
/**
 * 拷贝文件工作
 * @param  {Object} webpackConfigInfo webpack相关动态配置
 * @param  {Object} svnConfig         SVN相关配置
 * @param  {String} rootDir           生成项目的根路径
 * @return {Promise}                  返回promise对象
 */
function copyFile(webpackConfigInfo, svnConfig, rootDir) {
    return co(function*() {
        for (let key in fileConfig) {
            let filePath = path.join(__dirname, '..', 'config', key);
            let fileContent = yield thunkify(fs.readFile)(filePath, 'utf-8');
            fileContent = filterFileContent(fileContent, key, svnConfig, webpackConfigInfo);
            if(!fileContent){
                continue;
            }
            yield thunkify(fs.writeFile)(path.resolve(rootDir, fileConfig[key], key), fileContent);
        }
    });
}

exports.copyFile = copyFile;
<|endoftext|>


<commit_before>var soap   = require('soap'),
    events = require('events'),
    util   = require('util');

//Correios class
var Correios = function () {
  'use strict';

  this.url = 'http://ws.correios.com.br/calculador/CalcPrecoPrazo.asmx?wsdl';

  //Services
  this.services = {
    sedex  : '40010',
    aCobrar: '40045',
    sedex10: '40215',
    sedexHj: '40290',
    pac    : '41106'
  };

  //Format
  this.format = {
    caixaPacote: 1,
    roloPrisma : 2,
    envelope   : 3
  };

  //Default Args
  this.args = {
    nCdEmpresa : '',
    sDsSenha   : '',
    nCdServico : '40010',
    sCepOrigem : '22041030',
    sCepDestino: '04569001',
    nVlPeso    : '1',
    nCdFormato : 1,
    nVlComprimento: 20,
    nVlAltura  : 4,
    nVlLargura : 10,
    nVlDiametro: 20,
    sCdMaoPropria: 'N',
    nVlValorDeclarado: 0,
    sCdAvisoRecebimento: 'N'
  };
};

//Inherits from event emitter
util.inherits(Correios, events.EventEmitter);

//Calculate price
Correios.prototype.getPrice = function (args) {
  'use strict';

  var self = this;

  //if not error and not required fields emit an error
  if (!args || !args.sCepOrigem) {
    throw new Error('Envie todos os campos obrigatórios');
  }

  //create the SOAP client
  soap.createClient(this.url, function (err, client) {
    if (err) {
      self.emit('error', err);
    }

    //call the calcPreco method from the api
    client.CalcPreco(self.args, function (err, result) {
      //if error
      if (err) {
        self.emit('error', err);

      } else {
        self.emit('result', result.CalcPrecoResult.Servicos.cServico);
      }
    });
  });
};

module.exports = Correios;
<commit_msg>releasing first version<commit_after>var soap   = require('soap'),
    events = require('events'),
    util   = require('util');

//Correios class
var Correios = function () {
  'use strict';

  this.url = 'http://ws.correios.com.br/calculador/CalcPrecoPrazo.asmx?wsdl';
};

//Inherits from event emitter
util.inherits(Correios, events.EventEmitter);

//Calculate price
Correios.prototype.getPrice = function (args) {
  'use strict';

  var self = this;

  //create the SOAP client
  soap.createClient(this.url, function (err, client) {
    if (!self.isNull(err)) {
      self.emit('error', { MsgErro: 'Erro ao criar SOAP client' });
      return;
    }

    //call the calcPreco method from the api
    client.CalcPreco(args, function (err, result) {
      if (!self.isNull(err)) {
        self.emit('error', { MsgErro: 'Erro no envio SOAP, verifique os campos obrigatórios' });

      } else {
        self.emit('result', result.CalcPrecoResult.Servicos.cServico);
      }
    });
  });
};

//Is null
Correios.prototype.isNull = function(e) {
  'use strict';
  return e === null;
};

module.exports = Correios;
<|endoftext|>


<commit_before>var istanbul = require('istanbul');
var Table = require('cli-table');
var util = require('../lib/index');
var fs = require('fs');
var path = require('path');
var existsSync = fs.existsSync || path.existsSync;
var coverageType = 'yuitest';
var coverageInfo = {};
var options;

exports.__defineGetter__('coverageInfo', function() {
        return coverageInfo;
});
exports.__defineSetter__('coverageInfo', function(o) {
    coverageInfo = o;
    return coverageInfo;
});

exports.options = function(o) {
    options = o;

};

var isIstanbul = function(json) {
    json = json || {};
    var first = Object.keys(json)[0],
        ret = false;

    if (first && json[first].s !== undefined && json[first].fnMap !== undefined) {
        ret = true;
    }

    if (json.s !== undefined && json.fnMap !== undefined) {
        ret = true;
    }
    if (ret) {
        coverageType = 'istanbul';
    }
    return ret;
};

exports.isIstanbul = isIstanbul;

var set = function(json) {
    var d = {}, i;

    for (i in json) {
        d[i] = json[i];
        delete d[i].code;
    }

    if (isIstanbul(json)) {
        if (!Array.isArray(coverageInfo)) {
            coverageInfo = [];
        }
        coverageInfo.push(json);
    } else {
        for (i in json) {
            if (json[i].coveredLines !== undefined) { //YUITest
                coverageInfo[i] = coverageInfo[i] || {};
                coverageInfo[i].path = i;
                coverageInfo[i].lines = json[i].lines;
                coverageInfo[i].functions = json[i].functions;
                /*jshint loopfunc: true */
                ['calledLines', 'calledFunctions', 'coveredLines', 'coveredFunctions'].forEach(function(prop) {
                    coverageInfo[i][prop] = coverageInfo[i][prop] || 0;
                    coverageInfo[i][prop] = Math.max(coverageInfo[i][prop], json[i][prop], 0);
                });
            }
        }
    }
};

exports.set = set;

var printYUITestReport = function() {
    var items = [],
        table = new Table({
            head: ['path', 'lines', '%', 'functions', '%' ],
            colAligns: [ 'left', 'center', 'right', 'center', 'right'  ],
            style: {
                'padding-left': 2,
                'padding-right': 2,
                head: ['blue']
            }
        }),
        totals = {
            coveredLines: 0,
            coveredFunctions: 0,
            calledLines: 0,
            calledFunctions: 0
        };
    Object.keys(coverageInfo).forEach(function(item) {
        items.push(coverageInfo[item]);
    });

    items.sort(function(a, b) {
        if (!a.path || !b.path) {
            return 0;
        }
        var an = a.path.toLowerCase(),
            bn = b.path.toLowerCase(),
            ret = 0;

        if (an < bn) {
            ret = -1;
        }
        if (an > bn) {
            ret =  1;
        }
        return ret;
    });

    items.forEach(function(row) {
        var err,
            percentLine = Math.floor((row.calledLines / row.coveredLines) * 100),
            percentFunction = Math.floor((row.calledFunctions / row.coveredFunctions) * 100),
            cell = [
                row.path,
                row.calledLines + '/' + row.coveredLines,
                percentLine + '%',
                row.calledFunctions + '/' + row.coveredFunctions,
                percentFunction + '%'
            ];

            ['calledLines', 'calledFunctions', 'coveredLines', 'coveredFunctions'].forEach(function(prop) {
                totals[prop] += row[prop];
            });

        if (percentLine <= options.coverageWarn) {
            err = true;
            cell[1] = util.color(String(cell[1]), 'red');
            cell[2] = util.color(String(cell[2]), 'red');
        }
        if (percentFunction <= options.coverageWarn) {
            err = true;
            cell[3] = util.color(String(cell[3]), 'red');
            cell[4] = util.color(String(cell[4]), 'red');
        }
        if (err) {
            cell[0] = util.color(util.bad, 'red') + ' ' + cell[0];
        } else {
            cell[0] = util.color(util.good, 'green') + ' ' + cell[0];
        }
        table.push(cell);
    });

    table.push([
        'total',
        totals.calledLines + '/' + totals.coveredLines,
        Math.floor((totals.calledLines / totals.coveredLines) * 100) + '%',
        totals.calledFunctions + '/' + totals.coveredFunctions,
        Math.floor((totals.calledFunctions / totals.coveredFunctions) * 100) + '%'
    ]);
    console.log(table.toString());
};

var printIstanbulReport = function() {
    var collect = new istanbul.Collector(),
        report = istanbul.Report.create('text'),
        summary = istanbul.Report.create('text-summary');

    coverageInfo.forEach(function(coverage) {
        collect.add(coverage);
    });

    console.log('');
    report.writeReport(collect);
    summary.writeReport(collect);

};

var report = function() {
    if (coverageType === 'istanbul') {
        printIstanbulReport();
    } else {
        printYUITestReport();
    }
};

exports.report = report;

var getYUIStatus = function(coverage) {
    var cov = {
        lines: 0,
        hit: 0,
        miss: 0,
        percent: 0
    }, i, str;
    for (i in coverage) {
        cov.lines = cov.lines + coverage[i].coveredLines;
        cov.miss = cov.miss + (coverage[i].coveredLines - coverage[i].calledLines);
        cov.hit = cov.hit + coverage[i].calledLines;
    }
    cov.percent = Math.floor((cov.hit / cov.lines) * 100);

    str = util.color(' ' + cov.percent + '%', 'blue');
    return str;
};

var getIstanbulStatus = function(coverage) {
    var collect = new istanbul.Collector(),
        lines = [],
        str = '',
        pct = 0;

    collect.add(coverage);
    collect.files().forEach(function(file) {
        var fileCoverage = collect.fileCoverageFor(file),
            summary = istanbul.utils.summarizeFileCoverage(fileCoverage);
        lines.push(summary.lines.pct);
        pct += summary.lines.pct;
    });

    str = util.color(' ' + Math.floor(pct / lines.length) + '%', 'blue');
    return str;
};

exports.status = function(json) {
    var out;
    if (isIstanbul(json)) {
        coverageType = 'istanbul';
        out = getIstanbulStatus(json);
    } else {
        out = getYUIStatus(json);
    }
    return out;
};

var generateIstanbulHTML = function(options) {
    var report = istanbul.Report.create('html', {
            dir: options.istanbulReport
        }),
        collector = new istanbul.Collector();

    coverageInfo.forEach(function (item) {
        collector.add(item);
    });
    report.writeReport(collector, true);

    util.exit(0);
};

var getIstanbulCoverageReport = function (options) {
    if (options.istanbulReport) {
        return generateIstanbulHTML(options);
    }
    var lcovFile = options.coverageFileName,
        prefix = options.sourceFilePrefix,
        report = istanbul.Report.create('lcovonly', {
            dir: path.dirname(lcovFile)
        }),
        str = '',
        parts = [],
        iFile,
        collector = new istanbul.Collector();

    coverageInfo.forEach(function (item) {
        collector.add(item);
    });
    report.writeReport(collector, true);
    iFile = path.join(path.dirname(lcovFile), 'lcov.info');
    str = fs.readFileSync(iFile, 'utf8');
    parts = str.split('\n');
    str = '';
    parts.forEach(function(line, num) {
        if (line.indexOf('SF:') === 0) {
            var file = line.replace('SF:', '');
            file = path.resolve(prefix, file);
            parts[num] = 'SF:' + file;
        }
    });
    //Remove the istanbul file
    fs.unlinkSync(iFile);
    return parts.join('\n');
};

exports.getIstanbulCoverageReport = getIstanbulCoverageReport;

var getYUITestLcov = function (testName, prefix, data) {
    var lcovreport = {},
        file = data.path,
        match,
        name,
        line,
        exec,
        fn,
        fnkeys,
        lineKeys,
        j = 0,
        fileName = (prefix ? path.resolve(prefix, file) : path.resolve(process.cwd(), file));

    if (!existsSync(fileName)) {
        console.log('Failed to find source file:', fileName);
        util.exit(1);
    }

    lcovreport.TN = 'lcov.info';
    lcovreport.SF = fileName;
    lcovreport.DA = [];
    lcovreport.FN = [];
    lcovreport.FNDA = [];
    lcovreport.FNF = data.coveredFunctions;
    lcovreport.FNH = data.calledFunctions;
    lcovreport.LF = data.coveredLines;
    lcovreport.LH = data.calledLines;

    fnkeys = Object.keys(data.functions);
    for (j; j < fnkeys.length; j += 1) {
        fn = fnkeys[j];
        exec = data.functions[fn];
        match = fn.match(/^([\w ()_]+):(\d)+$/);
        name = match[1];
        line = match[2];

        lcovreport.FN.push({name: name, line: line});
        lcovreport.FNDA.push({name: name, exec: exec});
    }

    lineKeys = Object.keys(data.lines);
    for (j = 0; j < lineKeys.length; j += 1) {
        line = lineKeys[j];
        exec = data.lines[line];
        lcovreport.DA.push({line: line, exec: exec});
    }
    return lcovreport;
};

exports.getYUITestLcov = getYUITestLcov;

var getYUITestLcovReport = function (report) {
    var text = '',
        i = 0,
        fn,
        line;

    text += "TN:" + report.TN + "\n";
    text += "SF:" + report.SF + "\n";

    for (i; i < report.FN.length; i += 1) {
        fn = report.FN[i];
        text += "FN:" + fn.line + "," + fn.name + "\n";
    }
    text += "\n";

    for (i = 0; i < report.FNDA.length; i += 1) {
        fn = report.FNDA[i];
        text += "FNDA:" + fn.exec + "," + fn.name + "\n";
    }
    text += "\n";

    text += "FNF:" + report.FNF + "\n";
    text += "FNH:" + report.FNH + "\n";

    for (i = 0; i < report.DA.length; i += 1) {
        line = report.DA[i];
        text += "DA:" + line.line + "," + line.exec + "\n";
    }
    text += "\n";
    text += "LF:" + report.LF + "\n";
    text += "LH:" + report.LH + "\n\n";
    text += "end_of_record\n\n";
    return text;
};
exports.getYUITestLcovReport = getYUITestLcovReport;

var getCoverageReport = function(options) {
    var prefix = options.sourceFilePrefix,
        lcovStr = '';

    if (coverageType === 'istanbul') {
        lcovStr = getIstanbulCoverageReport(options);
    } else {
        Object.keys(coverageInfo).forEach(function(file) {
            lcovStr += getYUITestLcovReport(getYUITestLcov(file, prefix, coverageInfo[file]));
        });
    }
    return lcovStr;
};

exports.getCoverageReport = getCoverageReport;

var save = function(options) {
    var lcovFile = options.coverageFileName,
        lcovStr = getCoverageReport(options),
        gen = '';

    if (coverageType === 'istanbul') {
        gen = 'genhtml -o ./out/ -s ' + path.basename(lcovFile);
    } else {
        gen = 'genhtml -o ./out/ -s --no-branch-coverage ' + path.basename(lcovFile);
    }
    if (lcovFile) {
        if (existsSync(lcovFile)) {
            //Remove the file before writing it
            fs.unlinkSync(lcovFile);
        }
        fs.writeFileSync(lcovFile, lcovStr, 'utf8');
        util.log('Saved LCOV data to: ' + lcovFile);
        util.log('To generate the report:');
        console.log('   ', 'cd ' + path.dirname(lcovFile) + ';');
        console.log('   ', gen);
    }
};

exports.save = save;

<commit_msg>More testing updates<commit_after>var istanbul = require('istanbul');
var Table = require('cli-table');
var util = require('../lib/index');
var fs = require('fs');
var path = require('path');
var coverageType = 'yuitest';
var coverageInfo = {};
var options;

exports.__defineGetter__('coverageInfo', function() {
        return coverageInfo;
});
exports.__defineSetter__('coverageInfo', function(o) {
    coverageInfo = o;
    return coverageInfo;
});

exports.options = function(o) {
    options = o;

};

var isIstanbul = function(json) {
    json = json || {};
    var first = Object.keys(json)[0],
        ret = false;

    if (first && json[first].s !== undefined && json[first].fnMap !== undefined) {
        ret = true;
    }

    if (json.s !== undefined && json.fnMap !== undefined) {
        ret = true;
    }
    if (ret) {
        coverageType = 'istanbul';
    }
    return ret;
};

exports.isIstanbul = isIstanbul;

var set = function(json) {
    var d = {}, i;

    for (i in json) {
        d[i] = json[i];
        delete d[i].code;
    }

    if (isIstanbul(json)) {
        if (!Array.isArray(coverageInfo)) {
            coverageInfo = [];
        }
        coverageInfo.push(json);
    } else {
        for (i in json) {
            coverageInfo[i] = coverageInfo[i] || {};
            coverageInfo[i].path = i;
            coverageInfo[i].lines = json[i].lines;
            coverageInfo[i].functions = json[i].functions;
            /*jshint loopfunc: true */
            ['calledLines', 'calledFunctions', 'coveredLines', 'coveredFunctions'].forEach(function(prop) {
                coverageInfo[i][prop] = coverageInfo[i][prop] || 0;
                coverageInfo[i][prop] = Math.max(coverageInfo[i][prop], json[i][prop], 0);
            });
        }
    }
};

exports.set = set;

var printYUITestReport = function() {
    var items = [],
        table = new Table({
            head: ['path', 'lines', '%', 'functions', '%' ],
            colAligns: [ 'left', 'center', 'right', 'center', 'right'  ],
            style: {
                'padding-left': 2,
                'padding-right': 2,
                head: ['blue']
            }
        }),
        totals = {
            coveredLines: 0,
            coveredFunctions: 0,
            calledLines: 0,
            calledFunctions: 0
        };
    Object.keys(coverageInfo).forEach(function(item) {
        items.push(coverageInfo[item]);
    });

    items.sort(function(a, b) {
        if (!a.path || !b.path) {
            return 0;
        }
        var an = a.path.toLowerCase(),
            bn = b.path.toLowerCase(),
            ret = 0;

        if (an < bn) {
            ret = -1;
        }
        if (an > bn) {
            ret =  1;
        }
        return ret;
    });

    items.forEach(function(row) {
        var err,
            percentLine = Math.floor((row.calledLines / row.coveredLines) * 100),
            percentFunction = Math.floor((row.calledFunctions / row.coveredFunctions) * 100),
            cell = [
                row.path,
                row.calledLines + '/' + row.coveredLines,
                percentLine + '%',
                row.calledFunctions + '/' + row.coveredFunctions,
                percentFunction + '%'
            ];

            ['calledLines', 'calledFunctions', 'coveredLines', 'coveredFunctions'].forEach(function(prop) {
                totals[prop] += row[prop];
            });

        if (percentLine <= options.coverageWarn) {
            err = true;
            cell[1] = util.color(String(cell[1]), 'red');
            cell[2] = util.color(String(cell[2]), 'red');
        }
        if (percentFunction <= options.coverageWarn) {
            err = true;
            cell[3] = util.color(String(cell[3]), 'red');
            cell[4] = util.color(String(cell[4]), 'red');
        }
        if (err) {
            cell[0] = util.color(util.bad, 'red') + ' ' + cell[0];
        } else {
            cell[0] = util.color(util.good, 'green') + ' ' + cell[0];
        }
        table.push(cell);
    });

    table.push([
        'total',
        totals.calledLines + '/' + totals.coveredLines,
        Math.floor((totals.calledLines / totals.coveredLines) * 100) + '%',
        totals.calledFunctions + '/' + totals.coveredFunctions,
        Math.floor((totals.calledFunctions / totals.coveredFunctions) * 100) + '%'
    ]);
    console.log(table.toString());
};

var printIstanbulReport = function() {
    var collect = new istanbul.Collector(),
        report = istanbul.Report.create('text'),
        summary = istanbul.Report.create('text-summary');

    coverageInfo.forEach(function(coverage) {
        collect.add(coverage);
    });

    console.log('');
    report.writeReport(collect);
    summary.writeReport(collect);

};

var report = function() {
    if (coverageType === 'istanbul') {
        printIstanbulReport();
    } else {
        printYUITestReport();
    }
};

exports.report = report;

var getYUIStatus = function(coverage) {
    var cov = {
        lines: 0,
        hit: 0,
        miss: 0,
        percent: 0
    }, i, str;
    for (i in coverage) {
        cov.lines = cov.lines + coverage[i].coveredLines;
        cov.miss = cov.miss + (coverage[i].coveredLines - coverage[i].calledLines);
        cov.hit = cov.hit + coverage[i].calledLines;
    }
    cov.percent = Math.floor((cov.hit / cov.lines) * 100);

    str = util.color(' ' + cov.percent + '%', 'blue');
    return str;
};

var getIstanbulStatus = function(coverage) {
    var collect = new istanbul.Collector(),
        lines = [],
        str = '',
        pct = 0;

    collect.add(coverage);
    collect.files().forEach(function(file) {
        var fileCoverage = collect.fileCoverageFor(file),
            summary = istanbul.utils.summarizeFileCoverage(fileCoverage);
        lines.push(summary.lines.pct);
        pct += summary.lines.pct;
    });

    str = util.color(' ' + Math.floor(pct / lines.length) + '%', 'blue');
    return str;
};

exports.status = function(json) {
    var out;
    if (isIstanbul(json)) {
        coverageType = 'istanbul';
        out = getIstanbulStatus(json);
    } else {
        out = getYUIStatus(json);
    }
    return out;
};

var generateIstanbulHTML = function(options) {
    var report = istanbul.Report.create('html', {
            dir: options.istanbulReport
        }),
        collector = new istanbul.Collector();

    coverageInfo.forEach(function (item) {
        collector.add(item);
    });
    report.writeReport(collector, true);

    util.exit(0);
};

var getIstanbulCoverageReport = function (options) {
    if (options.istanbulReport) {
        return generateIstanbulHTML(options);
    }
    var lcovFile = options.coverageFileName,
        prefix = options.sourceFilePrefix,
        report = istanbul.Report.create('lcovonly', {
            dir: path.dirname(lcovFile)
        }),
        str = '',
        parts = [],
        iFile,
        collector = new istanbul.Collector();

    coverageInfo.forEach(function (item) {
        collector.add(item);
    });
    report.writeReport(collector, true);
    iFile = path.join(path.dirname(lcovFile), 'lcov.info');
    str = fs.readFileSync(iFile, 'utf8');
    parts = str.split('\n');
    str = '';
    parts.forEach(function(line, num) {
        if (line.indexOf('SF:') === 0) {
            var file = line.replace('SF:', '');
            file = path.resolve(prefix, file);
            parts[num] = 'SF:' + file;
        }
    });
    //Remove the istanbul file
    fs.unlinkSync(iFile);
    return parts.join('\n');
};

exports.getIstanbulCoverageReport = getIstanbulCoverageReport;

var getYUITestLcov = function (testName, prefix, data) {
    var lcovreport = {},
        file = data.path,
        match,
        name,
        line,
        exec,
        fn,
        fnkeys,
        lineKeys,
        j = 0,
        fileName = (prefix ? path.resolve(prefix, file) : path.resolve(process.cwd(), file));

    if (!util.existsSync(fileName)) {
        console.log('Failed to find source file:', fileName);
        util.exit(1);
    }

    lcovreport.TN = 'lcov.info';
    lcovreport.SF = fileName;
    lcovreport.DA = [];
    lcovreport.FN = [];
    lcovreport.FNDA = [];
    lcovreport.FNF = data.coveredFunctions;
    lcovreport.FNH = data.calledFunctions;
    lcovreport.LF = data.coveredLines;
    lcovreport.LH = data.calledLines;

    fnkeys = Object.keys(data.functions);
    for (j; j < fnkeys.length; j += 1) {
        fn = fnkeys[j];
        exec = data.functions[fn];
        match = fn.match(/^([\w ()_]+):(\d)+$/);
        name = match[1];
        line = match[2];

        lcovreport.FN.push({name: name, line: line});
        lcovreport.FNDA.push({name: name, exec: exec});
    }

    lineKeys = Object.keys(data.lines);
    for (j = 0; j < lineKeys.length; j += 1) {
        line = lineKeys[j];
        exec = data.lines[line];
        lcovreport.DA.push({line: line, exec: exec});
    }
    return lcovreport;
};

exports.getYUITestLcov = getYUITestLcov;

var getYUITestLcovReport = function (report) {
    var text = '',
        i = 0,
        fn,
        line;

    text += "TN:" + report.TN + "\n";
    text += "SF:" + report.SF + "\n";

    for (i; i < report.FN.length; i += 1) {
        fn = report.FN[i];
        text += "FN:" + fn.line + "," + fn.name + "\n";
    }
    text += "\n";

    for (i = 0; i < report.FNDA.length; i += 1) {
        fn = report.FNDA[i];
        text += "FNDA:" + fn.exec + "," + fn.name + "\n";
    }
    text += "\n";

    text += "FNF:" + report.FNF + "\n";
    text += "FNH:" + report.FNH + "\n";

    for (i = 0; i < report.DA.length; i += 1) {
        line = report.DA[i];
        text += "DA:" + line.line + "," + line.exec + "\n";
    }
    text += "\n";
    text += "LF:" + report.LF + "\n";
    text += "LH:" + report.LH + "\n\n";
    text += "end_of_record\n\n";
    return text;
};
exports.getYUITestLcovReport = getYUITestLcovReport;

var getCoverageReport = function(options) {
    var prefix = options.sourceFilePrefix,
        lcovStr = '';

    if (coverageType === 'istanbul') {
        lcovStr = getIstanbulCoverageReport(options);
    } else {
        Object.keys(coverageInfo).forEach(function(file) {
            lcovStr += getYUITestLcovReport(getYUITestLcov(file, prefix, coverageInfo[file]));
        });
    }
    return lcovStr;
};

exports.getCoverageReport = getCoverageReport;

var save = function(options) {
    var lcovFile = options.coverageFileName,
        lcovStr = getCoverageReport(options),
        gen = '';

    if (coverageType === 'istanbul') {
        gen = 'genhtml -o ./out/ -s ' + path.basename(lcovFile);
    } else {
        gen = 'genhtml -o ./out/ -s --no-branch-coverage ' + path.basename(lcovFile);
    }
    if (lcovFile) {
        if (util.existsSync(lcovFile)) {
            //Remove the file before writing it
            fs.unlinkSync(lcovFile);
        }
        fs.writeFileSync(lcovFile, lcovStr, 'utf8');
        util.log('Saved LCOV data to: ' + lcovFile);
        util.log('To generate the report:');
        console.log('   ', 'cd ' + path.dirname(lcovFile) + ';');
        console.log('   ', gen);
    }
};

exports.save = save;

<|endoftext|>