<commit_before>/**
 * List object
 */

 /**
  * @public
  * @constructor
  */
 function List() {
     try {
         var self_ = this;
         self_.id_ = 'sosimplist-list' + (new Date().getTime());
         self_.view_ = null;
         self_.title_ = '';
         self_.mapOfItem_ = {};
         self_.drag = {
            dragged: null
         };
         self_.checkedVisible_ = false;
         self_.dropdownButtonVisible_ = false;

         //add default item
         self_.addItem();
    }
    catch (e) {
        console.error(e.name + ': ' + e.message);
    }
 }

 /**
 * @public
 */
List.prototype.buildView = function() {
    try {
        var self_ = this;
        if (self_.view_ === null) {
             self_.view_ = document.createElement('div');
             self_.view_.id = self_.id_;
             self_.view_.className = 'sosimplist-list';
           //  self_.view_.draggable = true;

             var inputTitle = document.createElement('input');
             inputTitle.id = 'sosimplist-title' + self_.id_;
             inputTitle.className = 'sosimplist-title';
             inputTitle.type = 'text';
             inputTitle.placeholder = 'Title';
             inputTitle.addEventListener(
                 'keyup',
                 function() { self_.title_ = this.value;},
                 false
             );
             if (self_.title_ !== '') {
                inputTitle.value = self_.title_;
             }else {}
             self_.view_.appendChild(inputTitle);

             self_.itemContainer_ = document.createElement('div');
             self_.itemContainer_.id = 'sosimplist-container-item';
             self_.itemContainer_.addEventListener(
                 'dragstart',
                 function(event) {
                    self_.drag.dragged = event.target;
                 },
                 false
             );
             self_.itemContainer_.addEventListener(
                 'dragenter',
                 function(event) {
                    if(self_.drag.dragged){
                       // event.target.style.visibility = 'hidden';
                        //swap two elements when dragenter event is fire
                        self_.drag.dragged.nextSibling === event.target.parentNode ?
                        event.target.parentNode.parentNode.insertBefore(self_.drag.dragged, event.target.parentNode.nextSibling) :
                        event.target.parentNode.parentNode.insertBefore(self_.drag.dragged, event.target.parentNode);
                        event.stopPropagation();
                    }else{}
                 },
                 false
             );
             self_.itemContainer_.addEventListener(
                'dragleave',
                function(event) {
                   // event.target.style.visibility = 'visible';
                },
                false
             );
             self_.view_.appendChild(self_.itemContainer_);

             var buttonAddItem = document.createElement('input');
             buttonAddItem.className = 'sosimplist-button';
             buttonAddItem.type = 'button';
             buttonAddItem.value = 'Add item';
             buttonAddItem.addEventListener(
                 'click',
                 function() { self_.addItem(); },
                 false
             );
             self_.view_.appendChild(buttonAddItem);

             var dropdownList = document.createElement('div');
             dropdownList.className = 'sosimplist-list-dropdown-checked';
             dropdownList.value = 'click';

             dropdownList.addEventListener(
                 'click',
                 function() {
                    self_.checkedVisible_ = !self_.checkedVisible_;
                    self_.setVisibility_();
                 },
                 false
             );
             var pin = document.createElement('div');
             pin.className = 'sosimplist-list-pin';
             dropdownList.appendChild(pin);
             var pinLabel = document.createElement('label');
             pinLabel.className = 'sosimplist-list-pin-label';
             pinLabel.innerHTML = 'Selected items';
             dropdownList.appendChild(pinLabel);
             self_.view_.appendChild(dropdownList);

             self_.itemContainerChecked_ = document.createElement('div');
             self_.itemContainerChecked_.className = 'sosimplist-container-item-checked';
             self_.view_.appendChild(self_.itemContainerChecked_);

             //Fill view view items
             for (var itemId in self_.mapOfItem_) {
                if (self_.mapOfItem_[itemId].isChecked()) {
                    self_.itemContainerChecked_.appendChild(self_.mapOfItem_[itemId].getView());
                }
                else {
                    self_.itemContainer_.appendChild(self_.mapOfItem_[itemId].getView());
                }
             }

             //Customize view depending on private members
             self_.setVisibility_();
         }
         else {
            console.log('List ID = ' + self_.id_ + ', View already builded !');
         }
    }
    catch (e) {
        console.error(e.name + ': ' + e.message);
    }
};

/**
* @public
* @return {string} return content serialize in a base64 string
*/
List.prototype.serialize = function() {
    try {
        var self_ = this;
        var content = {
            id_: self_.id_,
            title_: self_.title_,
            arrayOfItem_: [] // parse element array to save item order
        };
        for (var i = 0; i < self_.itemContainer_.children.length; i++) {
            content.arrayOfItem_.push(self_.mapOfItem_[self_.itemContainer_.children[i].id].serialize());
        }
        for (var i = 0; i < self_.itemContainerChecked_.children.length; i++) {
            content.arrayOfItem_.push(self_.mapOfItem_[self_.itemContainerChecked_.children[i].id].serialize());
        }
        return btoa(JSON.stringify(content));
    }
    catch (e) {
        console.error(e.name + ': ' + e.message);
    }
};

/**
 * @public
 * @param {string} str content serialized in a base64 string to decode
 */
List.prototype.unserialize = function(str) {
    try {
        var self_ = this;
        var content = JSON.parse(atob(str));
        self_.id_ = content.id_;
        self_.title_ = content.title_;
        self_.mapOfItem_ = {};
        for (var i = 0; i < content.arrayOfItem_.length; i++) {
            var myItem = new ItemSimple(self_);
            myItem.unserialize(content.arrayOfItem_[i]);
            myItem.buildView();
            self_.mapOfItem_[myItem.getId()] = myItem;
        }
    }
    catch (e) {
        console.error(e.name + ': ' + e.message);
    }
};

/**
* @public
* @return {string} return list id
*/
List.prototype.getListId = function() {
    return this.id_;
};

/**
* @public
* Add item to this list
*/
List.prototype.addItem = function() {
    try {
        var self_ = this;
        var myItem = new ItemSimple(self_);
        myItem.buildView();
        self_.mapOfItem_[myItem.getId()] = myItem;
        if (self_.view_ !== null) {
            self_.itemContainer_.appendChild(myItem.getView());
        }else {}
    }
    catch (e) {
        console.error(e.name + ': ' + e.message);
    }
};

/**
* @public
* Remove given item from this list
* @param {object} item
*/
List.prototype.removeItem = function(item) {
    try {
        var self_ = this;
        self_.mapOfItem_[item.getId()] = undefined;
        if(self_.itemContainer_.contains(document.getElementById(item.getId()))){
            self_.itemContainer_.removeChild(document.getElementById(item.getId()));
        }
        else{
            self_.itemContainerChecked_.removeChild(document.getElementById(item.getId()));
        }
        self_.setVisibility_();
    }
    catch (e) {
        console.error(e.name + ': ' + e.message);
    }
};

/**
* @public
* Move given item to the right list container (checked container or default container)
* @param {object} item
*/
List.prototype.moveItem = function(item) {
    try {
        var self_ = this;
        if (item.isChecked()) {
            self_.itemContainerChecked_.appendChild(document.getElementById(item.getId()));
        }
        else {
            self_.itemContainer_.appendChild(document.getElementById(item.getId()));
        }
        self_.setVisibility_();
    }
    catch (e) {
        console.error(e.name + ': ' + e.message);
    }
};

 /**
  * @public
  * @return {string} return view as Element object to be placed in the view
  */
 List.prototype.getView = function() {
     return this.view_;
 };

 /**
  * @private
  */
 List.prototype.setVisibility_ = function() {
    try {
        var self_ = this;
         //hide/show list of item checked
        if (self_.checkedVisible_) {
            self_.view_.getElementsByClassName('sosimplist-container-item-checked')[0].style.display = '';
        }
        else {
            self_.view_.getElementsByClassName('sosimplist-container-item-checked')[0].style.display = 'none';
        }

        self_.dropdownButtonVisible_ = self_.itemContainerChecked_.hasChildNodes();
        if (self_.dropdownButtonVisible_) {
            self_.view_.getElementsByClassName('sosimplist-list-dropdown-checked')[0].style.display = '';
        }
        else {
            self_.view_.getElementsByClassName('sosimplist-list-dropdown-checked')[0].style.display = 'none';
        }
    }
    catch (e) {
        console.error(e.name + ': ' + e.message);
    }
 };

<commit_msg>Description: Fix and improve dragndrop items<commit_after>/**
 * List object
 */

 /**
  * @public
  * @constructor
  */
 function List() {
     try {
         var self_ = this;
         self_.id_ = 'sosimplist-list' + (new Date().getTime());
         self_.view_ = null;
         self_.title_ = '';
         self_.mapOfItem_ = {};
         self_.checkedVisible_ = false;
         self_.dropdownButtonVisible_ = false;

         //add default item
         self_.addItem();
    }
    catch (e) {
        console.error(e.name + ': ' + e.message);
    }
 }

 /**
 * @public
 */
List.prototype.buildView = function() {
    try {
        var self_ = this;
        if (self_.view_ === null) {
             self_.view_ = document.createElement('div');
             self_.view_.id = self_.id_;
             self_.view_.className = 'sosimplist-list';
           //  self_.view_.draggable = true;

             var inputTitle = document.createElement('input');
             inputTitle.id = 'sosimplist-title' + self_.id_;
             inputTitle.className = 'sosimplist-title';
             inputTitle.type = 'text';
             inputTitle.placeholder = 'Title';
             inputTitle.addEventListener(
                 'keyup',
                 function() { self_.title_ = this.value;},
                 false
             );
             if (self_.title_ !== '') {
                inputTitle.value = self_.title_;
             }else {}
             self_.view_.appendChild(inputTitle);

             self_.itemContainer_ = document.createElement('div');
             self_.itemContainer_.id = 'sosimplist-container-item';
             self_.itemContainer_.addEventListener(
                 'dragstart',
                 function(event) {
                    var parentToDrag = event.target.closest('.sosimplist-item');
                    parentToDrag.style.zIndex = 1;
                    parentToDrag.style.boxShadow = '3px 3px 3px grey';
                    event.dataTransfer.setData('elementId', parentToDrag.id);

                    var mask = document.createElement('div');
                    mask.id = 'mask';
                    mask.style.backgroundColor = 'red'; //To check if opacity is working
                    mask.style.opacity = 0; // Should be not visible with opacity = 0
                    mask.style.width = parentToDrag.clientWidth;
                    mask.style.height = parentToDrag.clientHeight;
                    mask.style.cursor = 'move';
                    document.body.appendChild(mask);
                    event.dataTransfer.setDragImage(mask, 0, 0);
                 },
                 false
             );
             self_.itemContainer_.addEventListener(
                 'dragenter',
                 function(event) {
                    event.preventDefault();
                    var elementDragged = document.getElementById(event.dataTransfer.getData('elementId'));
                    if(elementDragged){
                        var parentTarget = event.target.closest('.sosimplist-item');
                        var isContainInThisList = parentTarget.parentNode.contains(elementDragged);
                        if(isContainInThisList){
                            elementDragged.nextSibling === parentTarget ?
                            elementDragged.parentNode.insertBefore(elementDragged, parentTarget.nextSibling) :
                            elementDragged.parentNode.insertBefore(elementDragged, parentTarget);
                        }else{}
                    }else{}
                 },
                 false
             );
             self_.itemContainer_.addEventListener(
                'drop',
                function(event) {
                    var elementDragged = document.getElementById(event.dataTransfer.getData('elementId'));
                    if(elementDragged){
                        document.body.removeChild(document.getElementById('mask'));
                        elementDragged.style.boxShadow = '';
                        elementDragged.style.zIndex = '0';
                    }else{}
                },
                false
             );
             self_.itemContainer_.addEventListener(
                'dragover',
                function(event) {
                    event.preventDefault();
                },
                false
             );
             self_.view_.appendChild(self_.itemContainer_);

             var buttonAddItem = document.createElement('input');
             buttonAddItem.className = 'sosimplist-button';
             buttonAddItem.type = 'button';
             buttonAddItem.value = 'Add item';
             buttonAddItem.addEventListener(
                 'click',
                 function() { self_.addItem(); },
                 false
             );
             self_.view_.appendChild(buttonAddItem);

             var dropdownList = document.createElement('div');
             dropdownList.className = 'sosimplist-list-dropdown-checked';
             dropdownList.value = 'click';

             dropdownList.addEventListener(
                 'click',
                 function() {
                    self_.checkedVisible_ = !self_.checkedVisible_;
                    self_.setVisibility_();
                 },
                 false
             );
             var pin = document.createElement('div');
             pin.className = 'sosimplist-list-pin';
             dropdownList.appendChild(pin);
             var pinLabel = document.createElement('label');
             pinLabel.className = 'sosimplist-list-pin-label';
             pinLabel.innerHTML = 'Selected items';
             dropdownList.appendChild(pinLabel);
             self_.view_.appendChild(dropdownList);

             self_.itemContainerChecked_ = document.createElement('div');
             self_.itemContainerChecked_.className = 'sosimplist-container-item-checked';
             self_.view_.appendChild(self_.itemContainerChecked_);

             //Fill view view items
             for (var itemId in self_.mapOfItem_) {
                if (self_.mapOfItem_[itemId].isChecked()) {
                    self_.itemContainerChecked_.appendChild(self_.mapOfItem_[itemId].getView());
                }
                else {
                    self_.itemContainer_.appendChild(self_.mapOfItem_[itemId].getView());
                }
             }

             //Customize view depending on private members
             self_.setVisibility_();
         }
         else {
            console.log('List ID = ' + self_.id_ + ', View already builded !');
         }
    }
    catch (e) {
        console.error(e.name + ': ' + e.message);
    }
};

/**
* @public
* @return {string} return content serialize in a base64 string
*/
List.prototype.serialize = function() {
    try {
        var self_ = this;
        var content = {
            id_: self_.id_,
            title_: self_.title_,
            arrayOfItem_: [] // parse element array to save item order
        };
        for (var i = 0; i < self_.itemContainer_.children.length; i++) {
            content.arrayOfItem_.push(self_.mapOfItem_[self_.itemContainer_.children[i].id].serialize());
        }
        for (var i = 0; i < self_.itemContainerChecked_.children.length; i++) {
            content.arrayOfItem_.push(self_.mapOfItem_[self_.itemContainerChecked_.children[i].id].serialize());
        }
        return btoa(JSON.stringify(content));
    }
    catch (e) {
        console.error(e.name + ': ' + e.message);
    }
};

/**
 * @public
 * @param {string} str content serialized in a base64 string to decode
 */
List.prototype.unserialize = function(str) {
    try {
        var self_ = this;
        var content = JSON.parse(atob(str));
        self_.id_ = content.id_;
        self_.title_ = content.title_;
        self_.mapOfItem_ = {};
        for (var i = 0; i < content.arrayOfItem_.length; i++) {
            var myItem = new ItemSimple(self_);
            myItem.unserialize(content.arrayOfItem_[i]);
            myItem.buildView();
            self_.mapOfItem_[myItem.getId()] = myItem;
        }
    }
    catch (e) {
        console.error(e.name + ': ' + e.message);
    }
};

/**
* @public
* @return {string} return list id
*/
List.prototype.getListId = function() {
    return this.id_;
};

/**
* @public
* Add item to this list
*/
List.prototype.addItem = function() {
    try {
        var self_ = this;
        var myItem = new ItemSimple(self_);
        myItem.buildView();
        self_.mapOfItem_[myItem.getId()] = myItem;
        if (self_.view_ !== null) {
            self_.itemContainer_.appendChild(myItem.getView());
        }else {}
    }
    catch (e) {
        console.error(e.name + ': ' + e.message);
    }
};

/**
* @public
* Remove given item from this list
* @param {object} item
*/
List.prototype.removeItem = function(item) {
    try {
        var self_ = this;
        self_.mapOfItem_[item.getId()] = undefined;
        if(self_.itemContainer_.contains(document.getElementById(item.getId()))){
            self_.itemContainer_.removeChild(document.getElementById(item.getId()));
        }
        else{
            self_.itemContainerChecked_.removeChild(document.getElementById(item.getId()));
        }
        self_.setVisibility_();
    }
    catch (e) {
        console.error(e.name + ': ' + e.message);
    }
};

/**
* @public
* Move given item to the right list container (checked container or default container)
* @param {object} item
*/
List.prototype.moveItem = function(item) {
    try {
        var self_ = this;
        if (item.isChecked()) {
            self_.itemContainerChecked_.appendChild(document.getElementById(item.getId()));
        }
        else {
            self_.itemContainer_.appendChild(document.getElementById(item.getId()));
        }
        self_.setVisibility_();
    }
    catch (e) {
        console.error(e.name + ': ' + e.message);
    }
};

 /**
  * @public
  * @return {string} return view as Element object to be placed in the view
  */
 List.prototype.getView = function() {
     return this.view_;
 };

 /**
  * @private
  */
 List.prototype.setVisibility_ = function() {
    try {
        var self_ = this;
         //hide/show list of item checked
        if (self_.checkedVisible_) {
            self_.view_.getElementsByClassName('sosimplist-container-item-checked')[0].style.display = '';
        }
        else {
            self_.view_.getElementsByClassName('sosimplist-container-item-checked')[0].style.display = 'none';
        }

        self_.dropdownButtonVisible_ = self_.itemContainerChecked_.hasChildNodes();
        if (self_.dropdownButtonVisible_) {
            self_.view_.getElementsByClassName('sosimplist-list-dropdown-checked')[0].style.display = '';
        }
        else {
            self_.view_.getElementsByClassName('sosimplist-list-dropdown-checked')[0].style.display = 'none';
        }
    }
    catch (e) {
        console.error(e.name + ': ' + e.message);
    }
 };

<|endoftext|>


<commit_before>var async = require("async"),
    fs = require("fs"),
    merge = require("deepmerge"),
    underscore = require("underscore");

/**
 * Main component, renders data from parser.
 * @param {Object} configuration
 */
var main = module.exports = function (configuration) {
    this.configuration = underscore.extend({
        header: "",
        footer: "",
        groups: {},
        tables: {},
        template: __dirname + "/template.md",
        user: null,
        pass: null,
        database: ""
    }, configuration);

    this.loader = new (require(__dirname + "/loader"))(configuration);
    this.parser = new (require(__dirname + "/parser"))(this.loader);
    this.template = underscore.template(fs.readFileSync(this.configuration.template, {encoding: "utf8"}));
};

/**
 * Loads and parses data from all tables.
 * @param {function} callBack Call back function.
 */
main.prototype.load = function (callBack) {
    var self = this,
        series = [
            function (seriesCallBack) {
                return self.loader.all(seriesCallBack);
            },
            function (seriesCallBack) {
                return self.parser.all(seriesCallBack);
            }
        ];

    async.series(series, callBack);
};

/**
 * Extending default table list data by parsed data.
 * @param {Object} tables List of table data.
 * @returns {Object}
 */
main.prototype.merge = function (tables) {
    var self = this;
    underscore.each(tables, function (value, key) {
        if ((self.parser[key] === null) && (self.parser[key] === undefined)) {
            console.error("Unknown table: " + key);
            return;
        }
        tables[key] = merge(tables[key], self.parser.data[key]);
    });

    return tables;
};

/**
 * Extending all tables information from configuration by parsed data.
 */
main.prototype.mergeAll = function () {
    var self = this,
        groups = self.configuration.groups;

    self.configuration.tables = this.merge(self.configuration.tables);

    underscore.each(groups, function (group, key) {
        groups[key].tables = self.merge(groups[key].tables);
    });
};

/**
 * If **tables** directive is string - translates as RegExp of table names, to groups and tables.
 */
main.prototype.prepareAllTables = function () {
    var self = this,
        groups = self.configuration.groups;

    self.configuration.tables = this.prepareTables(self.configuration.tables);

    underscore.each(groups, function (group, key) {
        groups[key].tables = self.prepareTables(groups[key].tables);
    });
};

/**
 * If **tables** directive is string - translates as RegExp of table names.
 * @param {*} tables Tables directive from configuration.
 * @returns {Object}
 */
main.prototype.prepareTables = function (tables) {
    if (typeof tables !== "string") {
        return tables;
    }
    var pattern = new RegExp(tables),
        result = {},
        allTables = underscore.keys(this.loader.data);

    underscore.each(allTables, function (name) {
        if (pattern.test(name) === true) {
            result[name] = {};
        }
    });

    return result;
};

/**
 * Enter point.
 * @param {function} callBack Call back function.
 */
main.prototype.run = function (callBack) {
    var self = this;
    this.load(function (errors) {
        self.prepareAllTables();
        self.mergeAll();
        callBack(errors, self.template(self.configuration));
    });
};
<commit_msg>fix codeclimate issues<commit_after>var async = require("async"),
    fs = require("fs"),
    merge = require("deepmerge"),
    underscore = require("underscore");

/**
 * Main component, renders data from parser.
 * @param {Object} configuration
 */
var main = module.exports = function (configuration) {
    this.configuration = underscore.extend({
        header: "",
        footer: "",
        groups: {},
        tables: {},
        template: __dirname + "/template.md",
        user: null,
        pass: null,
        database: ""
    }, configuration);

    this.loader = new (require(__dirname + "/loader"))(configuration);
    this.parser = new (require(__dirname + "/parser"))(this.loader);
    this.template = underscore.template(fs.readFileSync(this.configuration.template, {encoding: "utf8"}));
};

/**
 * Loads and parses data from all tables.
 * @param {function} callBack Call back function.
 */
main.prototype.load = function (callBack) {
    var self = this,
        series = [
            function (seriesCallBack) {
                return self.loader.all(seriesCallBack);
            },
            function (seriesCallBack) {
                return self.parser.all(seriesCallBack);
            }
        ];

    async.series(series, callBack);
};

/**
 * Extending default table list data by parsed data.
 * @param {Object} tables List of table data.
 * @returns {Object}
 */
main.prototype.merge = function (tables) {
    var self = this;
    underscore.each(tables, function (value, key) {
        if ((self.parser[key] === null) && (self.parser[key] === undefined)) {
            console.error("Unknown table: " + key);
            return;
        }
        tables[key] = merge(tables[key], self.parser.data[key]);
    });

    return tables;
};

/**
 * Extending all tables information from configuration by parsed data.
 */
main.prototype.mergeAll = function () {
    this.runToAllTables(this.merge);
};

/**
 * If **tables** directive is string - translates as RegExp of table names, to groups and tables.
 */
main.prototype.prepareAllTables = function () {
    this.runToAllTables(this.prepareTables);
};

/**
 * @param {Function} method Function that will be called for all tables in configuration.
 */
main.prototype.runToAllTables = function (method) {
    var self = this,
        groups = self.configuration.groups;

    self.configuration.tables = method(self.configuration.tables);

    underscore.each(groups, function (group, key) {
        groups[key].tables = self.prepareTables(groups[key].tables);
    });
};

/**
 * If **tables** directive is string - translates as RegExp of table names.
 * @param {*} tables Tables directive from configuration.
 * @returns {Object}
 */
main.prototype.prepareTables = function (tables) {
    if (typeof tables !== "string") {
        return tables;
    }
    var pattern = new RegExp(tables),
        result = {},
        allTables = underscore.keys(this.loader.data);

    underscore.each(allTables, function (name) {
        if (pattern.test(name) === true) {
            result[name] = {};
        }
    });

    return result;
};

/**
 * Enter point.
 * @param {function} callBack Call back function.
 */
main.prototype.run = function (callBack) {
    var self = this;
    this.load(function (errors) {
        self.prepareAllTables();
        self.mergeAll();
        callBack(errors, self.template(self.configuration));
    });
};
<|endoftext|>


<commit_before>//     node-email-templates
//     Copyright (c) 2012 Nick Baugh <niftylettuce@gmail.com>
//     MIT Licensed

// Node.js module for rendering beautiful emails with [ejs][1] templates
//  and email-friendly inline CSS using [juice][2].

// * Author: [@niftylettuce](https://twitter.com/#!/niftylettuce)
// * Source: <https://github.com/niftylettuce/node-email-templates>

// # node-email-templates

var path  = require('path')
  , fs    = require('fs')
  , async = require('async')
  , juice = require('juice')
  , glob  = require('glob')
  , zlib  = require('zlib')
  , _     = require('underscore')
  , tm    = require('./templateManager')

var validBufferTypes = [ 'deflate', 'deflateRaw', 'gzip' ]

// # Email Template
var EmailTemplate = function(templateDirectory, defaults, done) {

  if (typeof defaults === 'function') done = defaults;

  var that = this;

  this.html = '';
  this.text = '';
  this.stylesheet = '';
  this.bufferType = '';
  this.render = function(locals, templatePath, html, text, stylesheet, bufferType, callback) {
    // Check if `bufferType` is a function (callback), or string
    var isBuffer = false
    if (typeof bufferType === 'function') {
      callback = bufferType
    } else if (typeof bufferType === 'string') {
      // Ensure that `bufferType` is a valid `zlib` compression type
      if (_.indexOf(validBufferTypes, bufferType) === -1) {
        throw new Error('`zlib.' + bufferType + '` does not exist or is not a valid buffer type')
      } else {
        isBuffer = true
      }
    }

    // Check if `that.bufferType` is not an empty string, so we'll use the
    // global bufferType instead.
    if (that.bufferType !== '') {
      isBuffer = true
      bufferType = that.bufferType
    }

    if (typeof html === 'function') {
      callback = html;
      html     = that.html;
    }
    if (!html) html = that.html
    if (!text) text = that.text
    if (!stylesheet) stylesheet = that.stylesheet
    locals = _.defaults(locals, (typeof defaults === 'object') ? defaults : {});
    locals.templatePath = templatePath

    // Configure options for template and stylesheet files.
    var htmlOpts = { source: html, locals: locals }
      , textOpts = { source: text, locals: locals }
      , cssOpts  = { source: stylesheet, locals: locals };
    htmlOpts.filename = glob.sync(path.join(locals.templatePath, 'html*'))[0];
    textOpts.filename = glob.sync(path.join(locals.templatePath, 'text*'))[0];
    cssOpts.filename  = glob.sync(path.join(locals.templatePath, 'style*'))[0];

    // Render all templates and stylesheets.
    async.map([htmlOpts, textOpts, cssOpts], tm.render.bind(tm), function(err, results) {
      if (err) return callback(err)
      html = results[0]
      text = results[1]
      stylesheet = results[2]

      // Inject available styles into HTML.
      html = (stylesheet) ? juice(html, stylesheet) : html

      // Return a compressed buffer if needed.
      if (isBuffer) {
        async.map([ new Buffer(html), new Buffer(text) ], zlib[bufferType], function(err, buffers) {
          if (err) return callback(err)
          html = buffers[0]
          text = buffers[1]
          callback(null, html, text)
        })
      }else {
        callback(null, html, text)
      }
    })
  };

  // Ensure the `templateDirectory` is not undefined
  if (typeof templateDirectory === 'undefined')
    return done(new Error('templateDirectory is undefined'));

  // Ensure the `templateDirectory` is a valid directory path
  fs.stat(templateDirectory, function(err, stats) {

    if (err) return done(new Error(err));

    if (!stats.isDirectory())
      return done(new Error('templateDirectory is not a valid directory path'));

    done(null, function(templateName, locals, bufferType, callback) {

      // Check if `bufferType` is a function (callback), or string
      var isBuffer = false
      if (typeof bufferType === 'function') {
        callback = bufferType
      } else if (typeof bufferType === 'string') {
        // Ensure that `bufferType` is a valid `zlib` compression type
        if (_.indexOf(validBufferTypes, bufferType) === -1) {
          throw new Error('`zlib.' + bufferType + '` does not exist or is not a valid buffer type')
        } else {
          isBuffer = true
        }
      }

      // Fallback if user doesn't pass all the args
      var batchEmail = false;
      if (typeof locals !== 'undefined' && typeof locals === 'function') {
        callback = locals;
        locals = {};
      } else if (typeof callback !== 'undefined' && typeof callback === 'function') {
        if (typeof locals === 'undefined' || !locals instanceof Object) {
          locals = {};
        } else if (locals === true) {
          // We are trying to load the template into memory, but not render it
          // This lets us send batches of emails using the same template
          //  and we only are loading the assets once
          batchEmail = true;
        }
      }

      // Batch load function

      var batchCheck = function() {
        if (batchEmail) {
          if (isBuffer) that.bufferType = bufferType
          return callback(null, that.render);
        }
        if (isBuffer) {
          that.render(locals, templatePath, null, null, null, bufferType, callback)
        } else {
          that.render(locals, templatePath, null, null, null, callback)
        }
        //that.render(locals, callback);
      };

      // Ensure the `templateName` is valid
      if (templateName === 'undefined') return callback('templateName was not defined');

      // Set the full path to the template
      var templatePath = path.join(templateDirectory, templateName);

      // Ensure the `templateName` is a valid directory path
      fs.stat(templatePath, function(err, stats) {

        if (err) return callback(err);

        if (!stats.isDirectory())
          return callback(templatePath + ' is not a valid directory path');

        // Ensure that at least the html.ejs file exists inside
        that.html = glob.sync(templatePath + '/html*')[0];
        //that.html = path.join(templatePath, 'html.ejs');
        fs.stat(that.html, function(err, stats) {

          if (err) return callback(err);

          if (!stats.isFile()) return callback(that.html + ' is not a valid file path');

          // Read the html file
          fs.readFile(that.html, 'utf8', function(err, data) {

            if (err) return callback(err);

            if (data === '') return callback(that.html + ' was an empty file');
            else that.html = data;

            // Set asset paths
            that.text = glob.sync(templatePath + '/text*')[0] || '';
            that.stylesheet = glob.sync(templatePath + '/style*')[0] || '';

            async.map([that.text, that.stylesheet], checkExists, function(err, results) {

              if (err) return callback(err);

              if (!results[0]) that.text = false
              if (!results[1]) that.stylesheet = false

              if (that.text && that.stylesheet) {
                async.map([that.text, that.stylesheet], fs.readFile, function(err, results) {
                  if (err) return callback(err);
                  results[0] = results[0].toString('utf8');
                  results[1] = results[1].toString('utf8');
                  if (results[0] === '') that.text = false;
                  if (results[1] === '') that.stylesheet = false;
                  that.text = results[0];
                  that.stylesheet = results[1];
                  batchCheck();
                });
              } else if (that.text && !that.stylesheet) {
                fs.readFile(that.text, 'utf8', function(err, data) {
                  if (err) return callback(err);
                  if (data === '') return callback(that.text + ' was empty');
                  that.text = data;
                  batchCheck();
                });
              } else if (!that.text && that.stylesheet) {
                fs.readFile(that.stylesheet, 'utf8', function(err, data) {
                  if (err) return callback(err);
                  if (data === '') return callback(that.stylesheet + ' was empty');
                  that.stylesheet = data;
                  batchCheck();
                });
              } else {
                batchCheck();
              }

            });

          });
        });
      });
    });

    function checkExists(item, callback) {
      function cb(exists) {
        return callback(null, exists)
      }
      // node >= v0.8.x
      if (fs.exists) return fs.exists(item, cb)
      // other versions of node fallback to path.exists
      return path.exists(item, cb)
    }

  });
};

module.exports = function(templateDirectory, defaults, done) {
  return new EmailTemplate(templateDirectory, defaults, done);
};
<commit_msg>Fix html exists bug<commit_after>//     node-email-templates
//     Copyright (c) 2012 Nick Baugh <niftylettuce@gmail.com>
//     MIT Licensed

// Node.js module for rendering beautiful emails with [ejs][1] templates
//  and email-friendly inline CSS using [juice][2].

// * Author: [@niftylettuce](https://twitter.com/#!/niftylettuce)
// * Source: <https://github.com/niftylettuce/node-email-templates>

// # node-email-templates

var path  = require('path')
  , fs    = require('fs')
  , async = require('async')
  , juice = require('juice')
  , glob  = require('glob')
  , zlib  = require('zlib')
  , _     = require('underscore')
  , tm    = require('./templateManager')

var validBufferTypes = [ 'deflate', 'deflateRaw', 'gzip' ]

// # Email Template
var EmailTemplate = function(templateDirectory, defaults, done) {

  if (typeof defaults === 'function') done = defaults;

  var that = this;

  this.html = '';
  this.text = '';
  this.stylesheet = '';
  this.bufferType = '';
  this.render = function(locals, templatePath, html, text, stylesheet, bufferType, callback) {
    // Check if `bufferType` is a function (callback), or string
    var isBuffer = false
    if (typeof bufferType === 'function') {
      callback = bufferType
    } else if (typeof bufferType === 'string') {
      // Ensure that `bufferType` is a valid `zlib` compression type
      if (_.indexOf(validBufferTypes, bufferType) === -1) {
        throw new Error('`zlib.' + bufferType + '` does not exist or is not a valid buffer type')
      } else {
        isBuffer = true
      }
    }

    // Check if `that.bufferType` is not an empty string, so we'll use the
    // global bufferType instead.
    if (that.bufferType !== '') {
      isBuffer = true
      bufferType = that.bufferType
    }

    if (typeof html === 'function') {
      callback = html;
      html     = that.html;
    }
    if (!html) html = that.html
    if (!text) text = that.text
    if (!stylesheet) stylesheet = that.stylesheet
    locals = _.defaults(locals, (typeof defaults === 'object') ? defaults : {});
    locals.templatePath = templatePath

    // Configure options for template and stylesheet files.
    var htmlOpts = { source: html, locals: locals }
      , textOpts = { source: text, locals: locals }
      , cssOpts  = { source: stylesheet, locals: locals };
    htmlOpts.filename = glob.sync(path.join(locals.templatePath, 'html*'))[0];
    textOpts.filename = glob.sync(path.join(locals.templatePath, 'text*'))[0];
    cssOpts.filename  = glob.sync(path.join(locals.templatePath, 'style*'))[0];

    // Render all templates and stylesheets.
    async.map([htmlOpts, textOpts, cssOpts], tm.render.bind(tm), function(err, results) {
      if (err) return callback(err)
      html = results[0]
      text = results[1]
      stylesheet = results[2]

      // Inject available styles into HTML.
      html = (stylesheet) ? juice(html, stylesheet) : html

      // Return a compressed buffer if needed.
      if (isBuffer) {
        async.map([ new Buffer(html), new Buffer(text) ], zlib[bufferType], function(err, buffers) {
          if (err) return callback(err)
          html = buffers[0]
          text = buffers[1]
          callback(null, html, text)
        })
      }else {
        callback(null, html, text)
      }
    })
  };

  // Ensure the `templateDirectory` is not undefined
  if (typeof templateDirectory === 'undefined')
    return done(new Error('templateDirectory is undefined'));

  // Ensure the `templateDirectory` is a valid directory path
  fs.stat(templateDirectory, function(err, stats) {

    if (err) return done(new Error(err));

    if (!stats.isDirectory())
      return done(new Error('templateDirectory is not a valid directory path'));

    done(null, function(templateName, locals, bufferType, callback) {

      // Check if `bufferType` is a function (callback), or string
      var isBuffer = false
      if (typeof bufferType === 'function') {
        callback = bufferType
      } else if (typeof bufferType === 'string') {
        // Ensure that `bufferType` is a valid `zlib` compression type
        if (_.indexOf(validBufferTypes, bufferType) === -1) {
          throw new Error('`zlib.' + bufferType + '` does not exist or is not a valid buffer type')
        } else {
          isBuffer = true
        }
      }

      // Fallback if user doesn't pass all the args
      var batchEmail = false;
      if (typeof locals !== 'undefined' && typeof locals === 'function') {
        callback = locals;
        locals = {};
      } else if (typeof callback !== 'undefined' && typeof callback === 'function') {
        if (typeof locals === 'undefined' || !locals instanceof Object) {
          locals = {};
        } else if (locals === true) {
          // We are trying to load the template into memory, but not render it
          // This lets us send batches of emails using the same template
          //  and we only are loading the assets once
          batchEmail = true;
        }
      }

      // Batch load function

      var batchCheck = function() {
        if (batchEmail) {
          if (isBuffer) that.bufferType = bufferType
          return callback(null, that.render);
        }
        if (isBuffer) {
          that.render(locals, templatePath, null, null, null, bufferType, callback)
        } else {
          that.render(locals, templatePath, null, null, null, callback)
        }
        //that.render(locals, callback);
      };

      // Ensure the `templateName` is valid
      if (templateName === undefined) return callback('templateName was not defined');

      // Set the full path to the template
      var templatePath = path.join(templateDirectory, templateName);

      // Ensure the `templateName` is a valid directory path
      fs.stat(templatePath, function(err, stats) {

        if (err) return callback(err);

        if (!stats.isDirectory())
          return callback(templatePath + ' is not a valid directory path');

        // Ensure that at least an html file exists inside
        that.html = glob.sync(templatePath + '/html*')[0] || '';
        fs.stat(that.html, function(err, stats) {

          if (err) return callback(err);

          if (!stats.isFile()) return callback(that.html + ' is not a valid file path');

          // Read the html file
          fs.readFile(that.html, 'utf8', function(err, data) {

            if (err) return callback(err);

            if (data === '') return callback(that.html + ' was an empty file');
            else that.html = data;

            // Set asset paths
            that.text = glob.sync(templatePath + '/text*')[0] || '';
            that.stylesheet = glob.sync(templatePath + '/style*')[0] || '';

            async.map([that.text, that.stylesheet], checkExists, function(err, results) {

              if (err) return callback(err);

              if (!results[0]) that.text = false
              if (!results[1]) that.stylesheet = false

              if (that.text && that.stylesheet) {
                async.map([that.text, that.stylesheet], fs.readFile, function(err, results) {
                  if (err) return callback(err);
                  results[0] = results[0].toString('utf8');
                  results[1] = results[1].toString('utf8');
                  if (results[0] === '') that.text = false;
                  if (results[1] === '') that.stylesheet = false;
                  that.text = results[0];
                  that.stylesheet = results[1];
                  batchCheck();
                });
              } else if (that.text && !that.stylesheet) {
                fs.readFile(that.text, 'utf8', function(err, data) {
                  if (err) return callback(err);
                  if (data === '') return callback(that.text + ' was empty');
                  that.text = data;
                  batchCheck();
                });
              } else if (!that.text && that.stylesheet) {
                fs.readFile(that.stylesheet, 'utf8', function(err, data) {
                  if (err) return callback(err);
                  if (data === '') return callback(that.stylesheet + ' was empty');
                  that.stylesheet = data;
                  batchCheck();
                });
              } else {
                batchCheck();
              }

            });

          });
        });
      });
    });

    function checkExists(item, callback) {
      function cb(exists) {
        return callback(null, exists)
      }
      // node >= v0.8.x
      if (fs.exists) return fs.exists(item, cb)
      // other versions of node fallback to path.exists
      return path.exists(item, cb)
    }

  });
};

module.exports = function(templateDirectory, defaults, done) {
  return new EmailTemplate(templateDirectory, defaults, done);
};
<|endoftext|>


<commit_before>(function() {
  'use strict';
  var CHR, CND, FS, OT, PATH, SvgPath, alert, badge, debug, declare, echo, first_of, help, info, isa, jr, last_of, log, path_precision, rpr, size_of, spawn_sync, type_of, urge, validate, warn, whisper;

  //###########################################################################################################
  CND = require('cnd');

  CHR = require('coffeenode-chr');

  rpr = CND.rpr.bind(CND);

  badge = 'SVGTTF/MAIN';

  log = CND.get_logger('plain', badge);

  info = CND.get_logger('info', badge);

  alert = CND.get_logger('alert', badge);

  debug = CND.get_logger('debug', badge);

  warn = CND.get_logger('warn', badge);

  urge = CND.get_logger('urge', badge);

  whisper = CND.get_logger('whisper', badge);

  help = CND.get_logger('help', badge);

  echo = CND.echo.bind(CND);

  //...........................................................................................................
  FS = require('fs');

  PATH = require('path');

  // exec                      = ( require 'util' ).promisify ( require 'child_process' ).exec
  spawn_sync = (require('child_process')).spawnSync;

  // CP                        = require 'child_process'
  jr = JSON.stringify;

  //...........................................................................................................
  this.types = require('./types');

  ({isa, validate, declare, first_of, last_of, size_of, type_of} = this.types);

  //...........................................................................................................
  require('./exception-handler');

  //...........................................................................................................
  OT = this._OT = require('opentype.js');

  SvgPath = this._SvgPath = require('svgpath');

  // DUMBSVGPATH               = require './experiments/dumb-svg-parser'
  path_precision = 3;

  //===========================================================================================================
  // METRICS
  //-----------------------------------------------------------------------------------------------------------
  this.new_metrics = function() {
    var R;
    R = {
      em_size: 4096/* a.k.a. design size, grid size */,
      ascender: null,
      descender: null,
      font_size: 360/* in pixels */,
      scale_factor: null
    };
    // ### TAINT magic number
    // for whatever reason, we have to calculate advanceWidth with an additional tracking factor:
    // advanceWidth = glyph.advanceWidth * metrics.scale_factor * metrics.tracking_factor ###
    // tracking_factor:  256 / 182
    R.scale_factor = R.em_size / R.font_size;
    R.ascender = R.em_size / (256 / 220);
    R.descender = -R.em_size / 5;
    // R.global_glyph_scale  = 50 / 48.5 ### TAINT value must come from configuration ###
    R.global_glyph_scale = 256 / 248/* TAINT value must come from configuration */
    // R.global_glyph_scale  = 1 ### TAINT value must come from configuration ###
    return R;
  };

  //-----------------------------------------------------------------------------------------------------------
  this.new_otjs_font = function(me, name, glyphs) {
    validate.nonempty_text(name);
    return new OT.Font({
      familyName: name,
      styleName: 'Medium',
      unitsPerEm: me.em_size,
      ascender: me.ascender,
      descender: me.descender,
      glyphs: glyphs
    });
  };

  //-----------------------------------------------------------------------------------------------------------
  this._find_ideographic_advance_factor = function(otjsfont) {
    /* In some fonts, the advance width of CJK ideographs differs from the font design size; this is
    especially true for fonts from the `cwTeXQ` series. This routine probes the font with a number of CJK
    codepoints and returns the ratio of the font design size and the advance width of the first CJK glyph.
    The function always returns 1 for fonts that do not contain CJK characters. */
    var chr, cid, glyph, i, len, probe;
    probe = Array.from('一丁乘㐀㑔㙜𠀀𠀁𠀈𪜵𪝘𪜲𫝀𫝄𫠢𫡄𫡦𬺰𬻂');
    for (i = 0, len = probe.length; i < len; i++) {
      chr = probe[i];
      cid = chr.codePointAt(0);
      if ((glyph = this.glyph_from_cid(otjsfont, cid)) == null) {
        continue;
      }
      return otjsfont.unitsPerEm / glyph.advanceWidth;
    }
    return 1;
  };

  //===========================================================================================================
  // FONTFORGE
  //-----------------------------------------------------------------------------------------------------------
  this.exec_fontforge_script = function(script, parameters = null) {
    var command, settings, status, stderr;
    command = 'fontforge';
    parameters = ['--lang=ff', '-c', script, ...(parameters != null ? parameters : [])];
    settings = {
      cwd: process.cwd(),
      timeout: 3 * 60 * 1000/* TAINT make timeout configurable */,
      encoding: 'utf-8',
      shell: false
    };
    //.........................................................................................................
    ({status, stderr} = spawn_sync(command, parameters, settings));
    //.........................................................................................................
    if (status !== 0) {
      throw new Error(`^svgttf#3309 when trying to execute \`${command} ${jr(parameters)}\`, an error occurred:\n${stderr}`);
    }
    return null;
  };

  //-----------------------------------------------------------------------------------------------------------
  this.rewrite_with_fontforge = function(path) {
    var command, parameters, settings, status, stderr;
    /* TAINT rewrite using `exec_fontforge_script()` */
    help(`^svgttf#0091 size before normalisation: ${CND.format_number(this._size_from_path(path))} B`);
    //.........................................................................................................
    command = 'fontforge';
    parameters = ['--lang=ff', '-c', "Open($1); Generate($1);", path];
    settings = {
      cwd: process.cwd(),
      timeout: 3 * 60 * 1000,
      encoding: 'utf-8',
      shell: false
    };
    //.........................................................................................................
    ({status, stderr} = spawn_sync(command, parameters, settings));
    //.........................................................................................................
    if (status !== 0) {
      throw new Error(`^svgttf#3309 when trying to execute ${jr(command)} ${jr(parameters)}, an error occurred:\n${stderr}`);
    }
    //.........................................................................................................
    help(`^svgttf#0091 size  after normalisation: ${CND.format_number(this._size_from_path(path))} B`);
    return null;
  };

  //-----------------------------------------------------------------------------------------------------------
  this._size_from_path = function(path) {
    var error;
    try {
      return (FS.statSync(path)).size;
    } catch (error1) {
      error = error1;
      if (error.code === 'ENOENT') {
        return null;
      }
      throw error;
    }
  };

  //===========================================================================================================
  // OPENTYPE.JS
  //-----------------------------------------------------------------------------------------------------------
  this.otjsfont_from_path = function(path) {
    return OT.loadSync(path);
  };

  //-----------------------------------------------------------------------------------------------------------
  this.save_otjsfont = function(path, otjsfont) {
    var buffer;
    // FS.writeFileSync path, buffer = otjsfont.toBuffer() # deprecated
    // buffer = Buffer.from otjsfont.toArrayBuffer()
    buffer = Buffer.from(this._otjsfont_toArrayBuffer(otjsfont));
    FS.writeFileSync(path, buffer);
    return buffer.length;
  };

  this._otjsfont_toArrayBuffer = function(otjsfont) {
    var buffer, bytes, intArray, sfntTable;
    sfntTable = otjsfont.toTables();
    bytes = sfntTable.encode();
    buffer = new ArrayBuffer(bytes.length);
    intArray = new Uint8Array(buffer);

  for (let i = 0; i < bytes.length; i++) {
      intArray[i] = bytes[i];
  }
  ;
    return buffer;
  };

  this.list_glyphs_in_otjsfont = function(otjsfont) {
    var R, cid, glyph, i, idx, len, ref, ref1, unicodes;
    R = new Set();
    ref = otjsfont.glyphs.glyphs;
    //.........................................................................................................
    for (idx in ref) {
      glyph = ref[idx];
      if (((ref1 = glyph.name) === '.notdef') || (glyph.unicode == null) || (glyph.unicode < 0x20)) {
        warn(`skipping glyph ${rpr(glyph.name)}`);
        continue;
      }
      unicodes = glyph.unicodes;
      if ((unicodes == null) || (unicodes.length === 0)) {
        unicodes = [glyph.unicode];
      }
// debug rpr glyph
// debug rpr unicodes
      for (i = 0, len = unicodes.length; i < len; i++) {
        cid = unicodes[i];
        // debug rpr cid
        R.add(String.fromCodePoint(cid));
      }
    }
    //.........................................................................................................
    return [...R].sort();
  };

  //-----------------------------------------------------------------------------------------------------------
  this.svg_path_from_cid = function(otjsfont, cid) {
    var cid_hex, glyph, pathdata;
    pathdata = this.svg_pathdata_from_cid(otjsfont, cid);
    glyph = String.fromCodePoint(cid);
    cid_hex = `0x${cid.toString(16)}`;
    return `<!-- ${cid_hex} ${glyph} --><path d='${pathdata}'/>`;
  };

  //-----------------------------------------------------------------------------------------------------------
  this.glyph_from_cid = function(otjsfont, cid) {
    var R;
    validate.positive_integer(cid);
    R = otjsfont.charToGlyph(String.fromCodePoint(cid));
    if (R.unicode != null) {
      return R;
    } else {
      return null;
    }
  };

  //-----------------------------------------------------------------------------------------------------------
  this._quickscale = function(path_obj, scale_x, scale_y = null) {
    var command, i, key, len, ref, value;
    if (scale_y == null) {
      scale_y = scale_x;
    }
    ref = path_obj.commands;
    for (i = 0, len = ref.length; i < len; i++) {
      command = ref[i];
      for (key in command) {
        value = command[key];
        switch (key) {
          case 'x':
          case 'x1':
          case 'x2':
            command[key] *= scale_x;
            break;
          case 'y':
          case 'y1':
          case 'y2':
            command[key] *= scale_y;
        }
      }
    }
    return null;
  };

  //-----------------------------------------------------------------------------------------------------------
  this.glyph_and_pathdata_from_cid = function(me, otjsfont, cid, tag = 'use-quickscale') {
    var fglyph, global_glyph_scale, path_obj, pathdata, ref, scale_factor, svg_path;
    validate.positive_integer(cid);
    fglyph = this.glyph_from_cid(otjsfont, cid);
    if (fglyph == null) {
      return null;
    }
    path_obj = fglyph.getPath(0, 0, me.font_size);
    if (path_obj.commands.length === 0) {
      return null;
    }
    global_glyph_scale = (ref = me.global_glyph_scale) != null ? ref : 1;
    scale_factor = me.scale_factor * global_glyph_scale;
    switch (tag) {
      case 'use-quickscale':
        this._quickscale(path_obj, scale_factor, -scale_factor);
        pathdata = path_obj.toPathData(path_precision);
        return {
          glyph: fglyph,
          pathdata
        };
      case 'use-dumb-svg-parser':
        throw new Error("^svgttf@3223 dumb-svg-parser not yet supported");
        pathdata = path_obj.toPathData(path_precision);
        svg_path = DUMBSVGPATH.parse(pathdata);
        // debug '^3362^', svg_path
        DUMBSVGPATH.scale(scale_factor, -scale_factor);
        return {
          // debug '^3362^', svg_path
          glyph: fglyph,
          pathdata: DUMBSVGPATH.as_compressed_text(svg_path)
        };
      case 'use-svgpath':
        pathdata = path_obj.toPathData(path_precision);
        svg_path = new SvgPath(pathdata);
        svg_path = svg_path.scale(scale_factor, -scale_factor);
        svg_path = svg_path.round(path_precision);
        return {
          glyph: fglyph,
          pathdata: svg_path.toString()
        };
    }
    throw new Error(`^svgttf@4582^ unknown tag ${rpr(tag)}`);
  };

  //-----------------------------------------------------------------------------------------------------------
  this.otjspath_from_pathdata = function(pathdata) {
    var R, d, i, len, ref, svg_path, tail, type, x, x1, x2, y, y1, y2;
    validate.nonempty_text(pathdata);
    svg_path = new SvgPath(pathdata);
    R = new OT.Path();
    d = R.commands;
    ref = svg_path.segments;
    for (i = 0, len = ref.length; i < len; i++) {
      [type, ...tail] = ref[i];
      // debug '^svgttf#3342', [ type, tail..., ]
      /* TAINT consider to use API (moveTo, lineTo etc) */
      switch (type) {
        case 'M':
        case 'L':
          [x, y] = tail;
          d.push({type, x, y});
          break;
        case 'C':
          [x1, y1, x2, y2, x, y] = tail;
          d.push({type, x1, y1, x2, y2, x, y});
          break;
        case 'Q':
          [x1, y1, x, y] = tail;
          d.push({type, x1, y1, x, y});
          break;
        case 'Z':
          d.push({type});
          break;
        default:
          throw new Error(`^svgttf#2231 unknown SVG path element ${rpr(type)}`);
      }
    }
    return R;
  };

  //-----------------------------------------------------------------------------------------------------------
  this.get_fallback_glyph = function(me, shape = 'square') {
    // validate.svgttf_metrics me
    '❶❷❸❹❺❻❼❽❾❿';
    var advanceWidth, name, path, unicode, width, x0, x1, xm, y0, y1, ym;
    validate.nonempty_text(shape);
    //.........................................................................................................
    width = Math.floor(3 * me.em_size / 4);
    x0 = Math.floor(me.em_size / 8);
    x1 = x0 + width;
    y0 = 0;
    y1 = width;
    path = new OT.Path();
    //.........................................................................................................
    switch (shape) {
      case 'square':
        path.moveTo(x0, y0);
        path.lineTo(x1, y0);
        path.lineTo(x1, y1);
        path.lineTo(x0, y1);
        path.close();
        break;
      case 'uptriangle':
        xm = Math.floor((x0 + x1) / 2);
        path.moveTo(x0, y0);
        path.lineTo(x1, y0);
        path.lineTo(xm, y1);
        path.close();
        break;
      case 'round':
        xm = Math.floor((x0 + x1) / 2);
        ym = Math.floor((y0 + y1) / 2);
        path.moveTo(xm, y0);
        path.quadTo(x1, y0, x1, ym);
        path.quadTo(x1, y1, xm, y1);
        path.quadTo(x0, y1, x0, ym);
        path.quadTo(x0, y0, xm, y0);
        path.close();
        break;
      default:
        throw new Error(`^svgttf#3391 unknown shape ${rpr(shape)}`);
    }
    //.........................................................................................................
    name = '.notdef';
    unicode = 0;
    advanceWidth = me.em_size;
    return new OT.Glyph({name, unicode, advanceWidth, path});
  };

}).call(this);
<commit_msg>update<commit_after>(function() {
  'use strict';
  var CHR, CND, FS, OT, PATH, SvgPath, alert, badge, debug, declare, echo, first_of, help, info, isa, jr, last_of, log, path_precision, rpr, size_of, spawn_sync, type_of, urge, validate, warn, whisper;

  //###########################################################################################################
  CND = require('cnd');

  CHR = require('coffeenode-chr');

  rpr = CND.rpr.bind(CND);

  badge = 'SVGTTF/MAIN';

  log = CND.get_logger('plain', badge);

  info = CND.get_logger('info', badge);

  alert = CND.get_logger('alert', badge);

  debug = CND.get_logger('debug', badge);

  warn = CND.get_logger('warn', badge);

  urge = CND.get_logger('urge', badge);

  whisper = CND.get_logger('whisper', badge);

  help = CND.get_logger('help', badge);

  echo = CND.echo.bind(CND);

  //...........................................................................................................
  FS = require('fs');

  PATH = require('path');

  // exec                      = ( require 'util' ).promisify ( require 'child_process' ).exec
  spawn_sync = (require('child_process')).spawnSync;

  // CP                        = require 'child_process'
  jr = JSON.stringify;

  //...........................................................................................................
  this.types = require('./types');

  ({isa, validate, declare, first_of, last_of, size_of, type_of} = this.types);

  //...........................................................................................................
  require('./exception-handler');

  //...........................................................................................................
  OT = this._OT = require('opentype.js');

  SvgPath = this._SvgPath = require('svgpath');

  // DUMBSVGPATH               = require './experiments/dumb-svg-parser'
  path_precision = 3;

  //===========================================================================================================
  // METRICS
  //-----------------------------------------------------------------------------------------------------------
  this.new_metrics = function() {
    var R;
    R = {
      em_size: 4096/* a.k.a. design size, grid size */,
      ascender: null,
      descender: null,
      font_size: 360/* in pixels */,
      scale_factor: null
    };
    // ### TAINT magic number
    // for whatever reason, we have to calculate advanceWidth with an additional tracking factor:
    // advanceWidth = glyph.advanceWidth * metrics.scale_factor * metrics.tracking_factor ###
    // tracking_factor:  256 / 182
    R.scale_factor = R.em_size / R.font_size;
    R.ascender = R.em_size / (256 / 220);
    R.descender = -R.em_size / 5;
    // R.global_glyph_scale  = 50 / 48.5 ### TAINT value must come from configuration ###
    R.global_glyph_scale = 256 / 248/* TAINT value must come from configuration */
    // R.global_glyph_scale  = 1 ### TAINT value must come from configuration ###
    return R;
  };

  //-----------------------------------------------------------------------------------------------------------
  this.new_otjs_font = function(me, name, glyphs) {
    validate.nonempty_text(name);
    return new OT.Font({
      familyName: name,
      styleName: 'Medium',
      unitsPerEm: me.em_size,
      ascender: me.ascender,
      descender: me.descender,
      glyphs: glyphs
    });
  };

  //-----------------------------------------------------------------------------------------------------------
  this._find_ideographic_advance_factor = function(otjsfont) {
    /* In some fonts, the advance width of CJK ideographs differs from the font design size; this is
    especially true for fonts from the `cwTeXQ` series. This routine probes the font with a number of CJK
    codepoints and returns the ratio of the font design size and the advance width of the first CJK glyph.
    The function always returns 1 for fonts that do not contain CJK characters. */
    var chr, cid, glyph, i, len, probe;
    probe = Array.from('一丁乘㐀㑔㙜𠀀𠀁𠀈𪜵𪝘𪜲𫝀𫝄𫠢𫡄𫡦𬺰𬻂');
    for (i = 0, len = probe.length; i < len; i++) {
      chr = probe[i];
      cid = chr.codePointAt(0);
      if ((glyph = this.glyph_from_cid(otjsfont, cid)) == null) {
        continue;
      }
      return otjsfont.unitsPerEm / glyph.advanceWidth;
    }
    return 1;
  };

  //===========================================================================================================
  // FONTFORGE
  //-----------------------------------------------------------------------------------------------------------
  this.exec_fontforge_script = function(script, parameters = null) {
    var command, settings, status, stderr;
    command = 'fontforge';
    parameters = ['--lang=ff', '-c', script, ...(parameters != null ? parameters : [])];
    settings = {
      cwd: process.cwd(),
      timeout: 3 * 60 * 1000/* TAINT make timeout configurable */,
      encoding: 'utf-8',
      shell: false
    };
    //.........................................................................................................
    ({status, stderr} = spawn_sync(command, parameters, settings));
    //.........................................................................................................
    if (status !== 0) {
      throw new Error(`^svgttf#3309 when trying to execute \`${command} ${jr(parameters)}\`, an error occurred:\n${stderr}`);
    }
    return null;
  };

  //-----------------------------------------------------------------------------------------------------------
  this.rewrite_with_fontforge = function(path) {
    var command, parameters, settings, status, stderr;
    /* TAINT rewrite using `exec_fontforge_script()` */
    help(`^svgttf#0091 size before normalisation: ${CND.format_number(this._size_from_path(path))} B`);
    //.........................................................................................................
    command = 'fontforge';
    parameters = ['--lang=ff', '-c', "Open($1); Generate($1);", path];
    settings = {
      cwd: process.cwd(),
      timeout: 3 * 60 * 1000,
      encoding: 'utf-8',
      shell: false
    };
    //.........................................................................................................
    ({status, stderr} = spawn_sync(command, parameters, settings));
    //.........................................................................................................
    if (status !== 0) {
      throw new Error(`^svgttf#3309 when trying to execute ${jr(command)} ${jr(parameters)}, an error occurred:\n${stderr}`);
    }
    //.........................................................................................................
    help(`^svgttf#0091 size  after normalisation: ${CND.format_number(this._size_from_path(path))} B`);
    return null;
  };

  //-----------------------------------------------------------------------------------------------------------
  this._size_from_path = function(path) {
    var error;
    try {
      return (FS.statSync(path)).size;
    } catch (error1) {
      error = error1;
      if (error.code === 'ENOENT') {
        return null;
      }
      throw error;
    }
  };

  //===========================================================================================================
  // OPENTYPE.JS
  //-----------------------------------------------------------------------------------------------------------
  this.otjsfont_from_path = function(path) {
    return OT.loadSync(path);
  };

  //-----------------------------------------------------------------------------------------------------------
  this.save_otjsfont = function(path, otjsfont) {
    var buffer;
    // FS.writeFileSync path, buffer = otjsfont.toBuffer() # deprecated
    // buffer = Buffer.from otjsfont.toArrayBuffer()
    buffer = Buffer.from(this._otjsfont_toArrayBuffer(otjsfont));
    FS.writeFileSync(path, buffer);
    return buffer.length;
  };

  this._otjsfont_toArrayBuffer = function(otjsfont) {
    var buffer, bytes, intArray, sfntTable;
    sfntTable = otjsfont.toTables();
    bytes = sfntTable.encode();
    buffer = new ArrayBuffer(bytes.length);
    intArray = new Uint8Array(buffer);

  for (let i = 0; i < bytes.length; i++) {
      intArray[i] = bytes[i];
  }
  ;
    return buffer;
  };

  this.list_glyphs_in_otjsfont = function(otjsfont) {
    var R, cid, glyph, i, idx, len, ref, ref1, unicodes;
    R = new Set();
    ref = otjsfont.glyphs.glyphs;
    //.........................................................................................................
    for (idx in ref) {
      glyph = ref[idx];
      if (((ref1 = glyph.name) === '.notdef') || (glyph.unicode == null) || (glyph.unicode < 0x20)) {
        warn(`skipping glyph ${rpr(glyph.name)}`);
        continue;
      }
      unicodes = glyph.unicodes;
      if ((unicodes == null) || (unicodes.length === 0)) {
        unicodes = [glyph.unicode];
      }
// debug rpr glyph
// debug rpr unicodes
      for (i = 0, len = unicodes.length; i < len; i++) {
        cid = unicodes[i];
        // debug rpr cid
        R.add(String.fromCodePoint(cid));
      }
    }
    //.........................................................................................................
    return [...R].sort();
  };

  //-----------------------------------------------------------------------------------------------------------
  this.svg_path_from_cid = function(otjsfont, cid) {
    var cid_hex, glyph, pathdata;
    pathdata = this.svg_pathdata_from_cid(otjsfont, cid);
    glyph = String.fromCodePoint(cid);
    cid_hex = `0x${cid.toString(16)}`;
    return `<!-- ${cid_hex} ${glyph} --><path d='${pathdata}'/>`;
  };

  //-----------------------------------------------------------------------------------------------------------
  /* TAINT rename to something like `otjsglyph_from_...()` */
  this.glyph_from_cid = function(otjsfont, cid) {
    validate.positive_integer(cid);
    return this.glyph_from_glyph(otjsfont, String.fromCodePoint(cid));
  };

  //-----------------------------------------------------------------------------------------------------------
  /* TAINT rename to something like `otjsglyph_from_...()` */
  this.glyph_from_glyph = function(otjsfont, glyph) {
    var R;
    /* TAINT validate is character */
    // validate.positive_integer cid
    R = otjsfont.charToGlyph(glyph);
    if (R.unicode != null) {
      return R;
    } else {
      return null;
    }
  };

  //-----------------------------------------------------------------------------------------------------------
  this._quickscale = function(path_obj, scale_x, scale_y = null) {
    var command, i, key, len, ref, value;
    if (scale_y == null) {
      scale_y = scale_x;
    }
    ref = path_obj.commands;
    for (i = 0, len = ref.length; i < len; i++) {
      command = ref[i];
      for (key in command) {
        value = command[key];
        switch (key) {
          case 'x':
          case 'x1':
          case 'x2':
            command[key] *= scale_x;
            break;
          case 'y':
          case 'y1':
          case 'y2':
            command[key] *= scale_y;
        }
      }
    }
    return null;
  };

  //-----------------------------------------------------------------------------------------------------------
  this.glyph_and_pathdata_from_cid = function(me, otjsfont, cid, tag = 'use-quickscale') {
    var fglyph, global_glyph_scale, path_obj, pathdata, ref, scale_factor;
    validate.positive_integer(cid);
    fglyph = this.glyph_from_cid(otjsfont, cid);
    if ((fglyph == null) || (fglyph.name === '.notdef')) {
      return null;
    }
    path_obj = fglyph.getPath(0, 0, me.font_size);
    if (path_obj.commands.length === 0) {
      return null;
    }
    global_glyph_scale = (ref = me.global_glyph_scale) != null ? ref : 1;
    scale_factor = me.scale_factor * global_glyph_scale;
    switch (tag) {
      case 'use-quickscale':
        this._quickscale(path_obj, scale_factor, -scale_factor);
        pathdata = path_obj.toPathData(path_precision);
        return {
          glyph: fglyph,
          pathdata
        };
    }
    // when 'use-dumb-svg-parser'
    //   throw new Error "^svgttf@3223 dumb-svg-parser not yet supported"
    //   pathdata            = path_obj.toPathData path_precision
    //   svg_path            = DUMBSVGPATH.parse pathdata
    //   # debug '^3362^', svg_path
    //   DUMBSVGPATH.scale scale_factor, -scale_factor
    //   # debug '^3362^', svg_path
    //   return { glyph: fglyph, pathdata: ( DUMBSVGPATH.as_compressed_text svg_path ), }
    // when 'use-svgpath'
    //   pathdata            = path_obj.toPathData path_precision
    //   svg_path            = new SvgPath pathdata
    //   svg_path            = svg_path.scale scale_factor, -scale_factor
    //   svg_path            = svg_path.round path_precision
    //   return { glyph: fglyph, pathdata: svg_path.toString(), }
    throw new Error(`^svgttf@4582^ unknown tag ${rpr(tag)}`);
  };

  //-----------------------------------------------------------------------------------------------------------
  this.otjspath_from_pathdata = function(pathdata) {
    var R, d, i, len, ref, svg_path, tail, type, x, x1, x2, y, y1, y2;
    validate.nonempty_text(pathdata);
    svg_path = new SvgPath(pathdata);
    R = new OT.Path();
    d = R.commands;
    ref = svg_path.segments;
    for (i = 0, len = ref.length; i < len; i++) {
      [type, ...tail] = ref[i];
      // debug '^svgttf#3342', [ type, tail..., ]
      /* TAINT consider to use API (moveTo, lineTo etc) */
      switch (type) {
        case 'M':
        case 'L':
          [x, y] = tail;
          d.push({type, x, y});
          break;
        case 'C':
          [x1, y1, x2, y2, x, y] = tail;
          d.push({type, x1, y1, x2, y2, x, y});
          break;
        case 'Q':
          [x1, y1, x, y] = tail;
          d.push({type, x1, y1, x, y});
          break;
        case 'Z':
          d.push({type});
          break;
        default:
          throw new Error(`^svgttf#2231 unknown SVG path element ${rpr(type)}`);
      }
    }
    return R;
  };

  //-----------------------------------------------------------------------------------------------------------
  this.get_fallback_glyph = function(me, shape = 'square') {
    // validate.svgttf_metrics me
    '❶❷❸❹❺❻❼❽❾❿';
    var advanceWidth, name, path, unicode, width, x0, x1, xm, y0, y1, ym;
    validate.nonempty_text(shape);
    //.........................................................................................................
    width = Math.floor(3 * me.em_size / 4);
    x0 = Math.floor(me.em_size / 8);
    x1 = x0 + width;
    y0 = 0;
    y1 = width;
    path = new OT.Path();
    //.........................................................................................................
    switch (shape) {
      case 'square':
        path.moveTo(x0, y0);
        path.lineTo(x1, y0);
        path.lineTo(x1, y1);
        path.lineTo(x0, y1);
        path.close();
        break;
      case 'uptriangle':
        xm = Math.floor((x0 + x1) / 2);
        path.moveTo(x0, y0);
        path.lineTo(x1, y0);
        path.lineTo(xm, y1);
        path.close();
        break;
      case 'round':
        xm = Math.floor((x0 + x1) / 2);
        ym = Math.floor((y0 + y1) / 2);
        path.moveTo(xm, y0);
        path.quadTo(x1, y0, x1, ym);
        path.quadTo(x1, y1, xm, y1);
        path.quadTo(x0, y1, x0, ym);
        path.quadTo(x0, y0, xm, y0);
        path.close();
        break;
      default:
        throw new Error(`^svgttf#3391 unknown shape ${rpr(shape)}`);
    }
    //.........................................................................................................
    name = '.notdef';
    unicode = 0;
    advanceWidth = me.em_size;
    return new OT.Glyph({name, unicode, advanceWidth, path});
  };

}).call(this);
<|endoftext|>


<commit_before>/* Copyright 2011 Ask Bjørn Hansen, see LICENSE */
"use strict";

var https = require('https');
var querystring = require('querystring');

function setup_response_handler(req, callback) {
    if (typeof callback !== "function") {
        //console.log("missing callback");
        return;
    }
    req.on('response',
        function(res) {
            var response = '';
            res.setEncoding('utf8');
            res.on('data',
                function(chunk) {
                    response += chunk;
            });
            res.on('end',
                function() {
                    var err = null;
                    try {
                        response = JSON.parse(response);
                        if(200 != res.statusCode) {
                            err = new Error(response.error.message);
                            err.name = response.error.type;
                            err.code = response.error.code;
                            err.param = response.error.param;
                            response = null;
                        }
                    }
                    catch(e) {
                        err = new Error("Invalid JSON from stripe.com");
                        response = null;
                    }
                    callback(err, response);
            });
        });
    req.on('error', function(error){
        callback(error);
    });
}

module.exports = function (api_key, options) {
    var defaults = options || {};

    var auth = 'Basic ' + new Buffer(api_key + ":").toString('base64');

    function _request(method, path, data, callback) {

        //console.log("data", typeof data, data);

        // convert first level of deep data structures to foo[bar]=baz syntax
        Object.keys(data).forEach(function(key) {
            if (typeof data[key] === 'object' && data[key] !== null) {
                var o = data[key];
                delete data[key];
                Object.keys(o).forEach(function(k) {
                    var new_key = key + "[" + k + "]";
                    data[new_key] = o[k];
                });
            }
        });

        var request_data = querystring.stringify(data);

        //console.log(method, "request for", path);
        //console.log("http request", request_data);

        var request_options = {
              host: 'api.stripe.com',
              port: '443',
              path: path,
              method: method,
              headers: {
                  'Authorization': auth,
                  'Accept': 'application/json',
                  'Content-Type': 'application/x-www-form-urlencoded',
                  'Content-Length': request_data.length
              }
          };

        var req = https.request(request_options);
        setup_response_handler(req, callback);
        req.write(request_data);
        req.end();
    }

    function post(path, data, callback) {
        _request('POST', path, data, callback);
    }

    function get(path, data, callback) {
        _request('GET', path, data, callback);
    }

    function del(path, data, callback) {
        _request('DELETE', path, data, callback);
    }

    function normalizeArguments() {
        var args = arguments[0];
        if(typeof args[0] == 'object' && typeof args[1] == 'function' && !args[2])
            return { count: args[0].count, offset: args[0].offset, cb: args[1] };
        else
            return { count: args[0], offset: args[1], cb: args[2] };
    }

    return {
        charges: {
            capture: function (charge_id, data, cb) {
                if (typeof data === 'function') {
                    cb = data;
                    data = {};
                }
                post("/v1/charges/" + charge_id + "/capture", data, cb);
            },
            create: function (data, cb) {
                post("/v1/charges", data, cb);
            },
            retrieve: function(charge_id, cb) {
                if(!(charge_id && typeof charge_id === 'string')) {
                    cb("charge_id required");
                }
                get("/v1/charges/" + charge_id, {}, cb);
            },
            refund: function(charge_id, amount, cb) {
                var requestParams = {};
                if(!(charge_id && typeof charge_id === 'string')) {
                    cb("charge_id required");
                }
                if (typeof amount === 'function') {
                    cb = amount;
                    amount = null;
                }
                if (amount) {
                    requestParams.amount = amount;
                }
                post("/v1/charges/" + charge_id + "/refund", requestParams, cb);
            },
            list: function(data, cb) {
                get("/v1/charges", data, cb);
            },
        },
        customers: {
            create: function (data, cb) {
                post("/v1/customers", data, cb);
            },
            retrieve: function(customer_id, cb) {
                if (!(customer_id && typeof customer_id === 'string')) {
                    return cb("customer_id required");
                }
                get("/v1/customers/" + customer_id, {}, cb);
            },
            update: function(customer_id, data, cb) {
                post("/v1/customers/" + customer_id, data, cb);
            },
            del: function(customer_id, cb) {
                del("/v1/customers/" + customer_id, {}, cb);
            },
            list: function(count, offset, cb) {
                var nArgs = normalizeArguments(arguments);
                get("/v1/customers", { count: nArgs.count, offset: nArgs.offset }, nArgs.cb);
            },
            update_subscription: function(customer_id, data, cb) {
                post("/v1/customers/" + customer_id + '/subscription', data, cb);
            },
            cancel_subscription: function(customer_id, at_period_end, cb) {
                del("/v1/customers/" + customer_id + '/subscription', { at_period_end: at_period_end }, cb);
            }
        },
        plans: {
            create: function (data, cb) {
                post("/v1/plans", data, cb);
            },
            retrieve: function(plan_id, cb) {
                if (!(plan_id && typeof plan_id === 'string')) {
                    return cb("plan_id required");
                }
                get("/v1/plans/" + plan_id, {}, cb);
            },
            del: function(plan_id, cb) {
                del("/v1/plans/" + plan_id, {}, cb);
            },
            list: function(count, offset, cb) {
                var nArgs = normalizeArguments(arguments);
                get("/v1/plans", { count: nArgs.count, offset: nArgs.offset }, nArgs.cb);
            },
            update: function (plan_id, data, cb) {
                post("/v1/plans/" + plan_id, data, cb);
            }
        },
        invoices: {
            retrieve: function(invoice_id, cb) {
                get("/v1/invoices/" + invoice_id, {}, cb);
            },
            list: function(data, cb) {
                get("/v1/invoices", data, cb);
            },
            upcoming: function(customer_id, cb) {
                get("/v1/invoices/upcoming", { customer: customer_id }, cb);
            },
        },
        invoice_items: {
            create: function(data, cb) {
                post("/v1/invoiceitems", data, cb);
            },
            retrieve: function(invoice_item_id, cb) {
                if (!(invoice_item_id && typeof invoice_item_id === 'string')) {
                    return cb("invoice_item_id required");
                }
                get("/v1/invoiceitems/" + invoice_item_id, {}, cb);
            },
            update: function(invoice_item_id, data, cb) {
                post("/v1/invoiceitems/" + invoice_item_id, data, cb);
            },
            del: function(invoice_item_id, cb) {
                del("/v1/invoiceitems/" + invoice_item_id, {}, cb);
            },
            list: function(customer_id, count, offset, cb) {
                var nArgs = normalizeArguments({count: count, offset: offset }, cb);
                get("/v1/invoiceitems", { customer: customer_id, count: count, offset: offset}, cb );
            }
        },
        coupons: {
            create: function (data, cb) {
                post("/v1/coupons", data, cb);
            },
            retrieve: function(coupon_id, cb) {
                if (!(coupon_id && typeof coupon_id === 'string')) {
                    cb("coupon_id required");
                }
                get("/v1/coupons/" + coupon_id, {}, cb);
            },
            del: function(coupon_id, cb) {
                del("/v1/coupons/" + coupon_id, {}, cb);
            },
            list: function(count, offset, cb) {
                var nArgs = normalizeArguments(arguments);
                get("/v1/coupons", { count: nArgs.count, offset: nArgs.offset}, nArgs.cb );
            }
        },
        token: {
            create: function (data, cb) {
                post("/v1/tokens", data, cb)
            },
            retrieve: function (token_id, cb) {
                get("/v1/tokens/" + token_id, {}, cb)
            }
        },
        account: {
            retrieve: function (cb) {
                get("/v1/account", {}, cb)
            }
        },
        events: {
            retrieve: function (token_id, cb) {
                get("/v1/events/" + token_id, {}, cb)
            },
            list: function (cb) {
                get("/v1/events/", {}, cb)
            }
        },
        recipients: {
            create: function(data, cb) {
                post("/v1/recipients", data, cb);
            },
            retrieve: function(recipient_id, cb) {
                get("/v1/recipients/" + recipient_id, cb);
            },
            update: function(recipient_id, data, cb) {
                post("/v1/recipients/" + recipient_id, data, cb);
            },
            del: function(recipient_id, cb) {
                del("/v1/recipients/" + recipient_id, cb);
            },
            list: function(count, offset, cb) {
                var nArgs = normalizeArguments(arguments);
                get("/v1/recipients", { count: nArgs.count, offset: nArgs.offset }, nArgs.cb);
            }
        },
        transfers: {
            create: function(data, cb) {
                post("/v1/transfers", data, cb);
            },
            retrieve: function(transfer_id, cb) {
                get("/v1/transfers/" + transfer_id, cb);
            },
            cancel: function(transfer_id, cb) {
                post("/v1/transfers/" + transfer_id + "/cancel", cb);
            },
            list: function(count, offset, cb) {
                var nArgs = normalizeArguments(arguments);
                get("/v1/transfers", { count: nArgs.count, offset: nArgs.offset }, nArgs.cb);
            }
        },
        balance: {
            retrieve: function(cb) {
                get("/v1/balance", cb);
            },
            list: function(count, offset, cb) {
                var nArgs = normalizeArguments(arguments);
                get("/v1/balance/history", { count: nArgs.count, offset: nArgs.offset }, nArgs.cb);
            }
        }
    };
}
<commit_msg>Fix some transfer/recipient requests<commit_after>/* Copyright 2011 Ask Bjørn Hansen, see LICENSE */
"use strict";

var https = require('https');
var querystring = require('querystring');

function setup_response_handler(req, callback) {
    if (typeof callback !== "function") {
        //console.log("missing callback");
        return;
    }
    req.on('response',
        function(res) {
            var response = '';
            res.setEncoding('utf8');
            res.on('data',
                function(chunk) {
                    response += chunk;
            });
            res.on('end',
                function() {
                    var err = null;
                    try {
                        response = JSON.parse(response);
                        if(200 != res.statusCode) {
                            err = new Error(response.error.message);
                            err.name = response.error.type;
                            err.code = response.error.code;
                            err.param = response.error.param;
                            response = null;
                        }
                    }
                    catch(e) {
                        err = new Error("Invalid JSON from stripe.com");
                        response = null;
                    }
                    callback(err, response);
            });
        });
    req.on('error', function(error){
        callback(error);
    });
}

module.exports = function (api_key, options) {
    var defaults = options || {};

    var auth = 'Basic ' + new Buffer(api_key + ":").toString('base64');

    function _request(method, path, data, callback) {

        //console.log("data", typeof data, data);

        // convert first level of deep data structures to foo[bar]=baz syntax
        Object.keys(data).forEach(function(key) {
            if (typeof data[key] === 'object' && data[key] !== null) {
                var o = data[key];
                delete data[key];
                Object.keys(o).forEach(function(k) {
                    var new_key = key + "[" + k + "]";
                    data[new_key] = o[k];
                });
            }
        });

        var request_data = querystring.stringify(data);

        //console.log(method, "request for", path);
        //console.log("http request", request_data);

        var request_options = {
              host: 'api.stripe.com',
              port: '443',
              path: path,
              method: method,
              headers: {
                  'Authorization': auth,
                  'Accept': 'application/json',
                  'Content-Type': 'application/x-www-form-urlencoded',
                  'Content-Length': request_data.length
              }
          };

        var req = https.request(request_options);
        setup_response_handler(req, callback);
        req.write(request_data);
        req.end();
    }

    function post(path, data, callback) {
        _request('POST', path, data, callback);
    }

    function get(path, data, callback) {
        _request('GET', path, data, callback);
    }

    function del(path, data, callback) {
        _request('DELETE', path, data, callback);
    }

    function normalizeArguments() {
        var args = arguments[0];
        if(typeof args[0] == 'object' && typeof args[1] == 'function' && !args[2])
            return { count: args[0].count, offset: args[0].offset, cb: args[1] };
        else
            return { count: args[0], offset: args[1], cb: args[2] };
    }

    return {
        charges: {
            capture: function (charge_id, data, cb) {
                if (typeof data === 'function') {
                    cb = data;
                    data = {};
                }
                post("/v1/charges/" + charge_id + "/capture", data, cb);
            },
            create: function (data, cb) {
                post("/v1/charges", data, cb);
            },
            retrieve: function(charge_id, cb) {
                if(!(charge_id && typeof charge_id === 'string')) {
                    cb("charge_id required");
                }
                get("/v1/charges/" + charge_id, {}, cb);
            },
            refund: function(charge_id, amount, cb) {
                var requestParams = {};
                if(!(charge_id && typeof charge_id === 'string')) {
                    cb("charge_id required");
                }
                if (typeof amount === 'function') {
                    cb = amount;
                    amount = null;
                }
                if (amount) {
                    requestParams.amount = amount;
                }
                post("/v1/charges/" + charge_id + "/refund", requestParams, cb);
            },
            list: function(data, cb) {
                get("/v1/charges", data, cb);
            },
        },
        customers: {
            create: function (data, cb) {
                post("/v1/customers", data, cb);
            },
            retrieve: function(customer_id, cb) {
                if (!(customer_id && typeof customer_id === 'string')) {
                    return cb("customer_id required");
                }
                get("/v1/customers/" + customer_id, {}, cb);
            },
            update: function(customer_id, data, cb) {
                post("/v1/customers/" + customer_id, data, cb);
            },
            del: function(customer_id, cb) {
                del("/v1/customers/" + customer_id, {}, cb);
            },
            list: function(count, offset, cb) {
                var nArgs = normalizeArguments(arguments);
                get("/v1/customers", { count: nArgs.count, offset: nArgs.offset }, nArgs.cb);
            },
            update_subscription: function(customer_id, data, cb) {
                post("/v1/customers/" + customer_id + '/subscription', data, cb);
            },
            cancel_subscription: function(customer_id, at_period_end, cb) {
                del("/v1/customers/" + customer_id + '/subscription', { at_period_end: at_period_end }, cb);
            }
        },
        plans: {
            create: function (data, cb) {
                post("/v1/plans", data, cb);
            },
            retrieve: function(plan_id, cb) {
                if (!(plan_id && typeof plan_id === 'string')) {
                    return cb("plan_id required");
                }
                get("/v1/plans/" + plan_id, {}, cb);
            },
            del: function(plan_id, cb) {
                del("/v1/plans/" + plan_id, {}, cb);
            },
            list: function(count, offset, cb) {
                var nArgs = normalizeArguments(arguments);
                get("/v1/plans", { count: nArgs.count, offset: nArgs.offset }, nArgs.cb);
            },
            update: function (plan_id, data, cb) {
                post("/v1/plans/" + plan_id, data, cb);
            }
        },
        invoices: {
            retrieve: function(invoice_id, cb) {
                get("/v1/invoices/" + invoice_id, {}, cb);
            },
            list: function(data, cb) {
                get("/v1/invoices", data, cb);
            },
            upcoming: function(customer_id, cb) {
                get("/v1/invoices/upcoming", { customer: customer_id }, cb);
            },
        },
        invoice_items: {
            create: function(data, cb) {
                post("/v1/invoiceitems", data, cb);
            },
            retrieve: function(invoice_item_id, cb) {
                if (!(invoice_item_id && typeof invoice_item_id === 'string')) {
                    return cb("invoice_item_id required");
                }
                get("/v1/invoiceitems/" + invoice_item_id, {}, cb);
            },
            update: function(invoice_item_id, data, cb) {
                post("/v1/invoiceitems/" + invoice_item_id, data, cb);
            },
            del: function(invoice_item_id, cb) {
                del("/v1/invoiceitems/" + invoice_item_id, {}, cb);
            },
            list: function(customer_id, count, offset, cb) {
                var nArgs = normalizeArguments({count: count, offset: offset }, cb);
                get("/v1/invoiceitems", { customer: customer_id, count: count, offset: offset}, cb );
            }
        },
        coupons: {
            create: function (data, cb) {
                post("/v1/coupons", data, cb);
            },
            retrieve: function(coupon_id, cb) {
                if (!(coupon_id && typeof coupon_id === 'string')) {
                    cb("coupon_id required");
                }
                get("/v1/coupons/" + coupon_id, {}, cb);
            },
            del: function(coupon_id, cb) {
                del("/v1/coupons/" + coupon_id, {}, cb);
            },
            list: function(count, offset, cb) {
                var nArgs = normalizeArguments(arguments);
                get("/v1/coupons", { count: nArgs.count, offset: nArgs.offset}, nArgs.cb );
            }
        },
        token: {
            create: function (data, cb) {
                post("/v1/tokens", data, cb)
            },
            retrieve: function (token_id, cb) {
                get("/v1/tokens/" + token_id, {}, cb)
            }
        },
        account: {
            retrieve: function (cb) {
                get("/v1/account", {}, cb)
            }
        },
        events: {
            retrieve: function (token_id, cb) {
                get("/v1/events/" + token_id, {}, cb)
            },
            list: function (cb) {
                get("/v1/events/", {}, cb)
            }
        },
        recipients: {
            create: function(data, cb) {
                post("/v1/recipients", data, cb);
            },
            retrieve: function(recipient_id, cb) {
                get("/v1/recipients/" + recipient_id, {}, cb);
            },
            update: function(recipient_id, data, cb) {
                post("/v1/recipients/" + recipient_id, data, cb);
            },
            del: function(recipient_id, cb) {
                del("/v1/recipients/" + recipient_id, {}, cb);
            },
            list: function(count, offset, cb) {
                var nArgs = normalizeArguments(arguments);
                get("/v1/recipients", { count: nArgs.count, offset: nArgs.offset }, nArgs.cb);
            }
        },
        transfers: {
            create: function(data, cb) {
                post("/v1/transfers", data, cb);
            },
            retrieve: function(transfer_id, cb) {
                get("/v1/transfers/" + transfer_id, {}, cb);
            },
            cancel: function(transfer_id, cb) {
                post("/v1/transfers/" + transfer_id + "/cancel", {}, cb);
            },
            list: function(count, offset, cb) {
                var nArgs = normalizeArguments(arguments);
                get("/v1/transfers", { count: nArgs.count, offset: nArgs.offset }, nArgs.cb);
            }
        },
        balance: {
            retrieve: function(cb) {
                get("/v1/balance", {}, cb);
            },
            list: function(count, offset, cb) {
                var nArgs = normalizeArguments(arguments);
                get("/v1/balance/history", { count: nArgs.count, offset: nArgs.offset }, nArgs.cb);
            }
        }
    };
}
<|endoftext|>