<commit_before>'use strict';

var et = require('elementtree');
var kml = require('gtran-kml');
var JSZip = require('jszip');
var promiseLib = require('./promise.js');
var fs = require('fs');

module.exports = function(layers, fileName, options) {
    var Promise = promiseLib.set(options.promiseLib);
    var taskList = [];
    generateTaskList('doc', layers, taskList);

    return Promise.all(taskList)
    .then(function(results) {
        var zip = JSZip();

        var root = et.Element('kml');
        root.attrib.xmlns = 'http://www.opengis.net/kml/2.2';

        var doc = et.SubElement(root, 'Document');

        results.forEach(function(result) {
            var fullPath = result.directory + '/' + result.fileName + '.kml';

            zip.file(fullPath, result.data);

            var folderNode = getFolder(doc, result.directory.substring(4));
            if(!folderNode) {
                folderNode = createFolder(doc, result.directory.substring(4));
            }

            var networkLink = et.SubElement(folderNode, 'NetworkLink');

            var name = et.SubElement(networkLink, 'name');
            name.text = result.fileName;

            var link = et.SubElement(networkLink, 'Link');
            var href = et.SubElement(link, 'href');
            href.text = fullPath;
        });

        var xmlTree = new et.ElementTree(root);

        zip.file('doc.kml', xmlTree.write());
        var buffer = zip.generate({type:"nodebuffer"});

        return Promise.resolve(buffer);
    })
    .then(function(zip) {
        if(fileName) {
            fs.writeFile(fileName, zip, function(err) {
                if(err) { return Promise.reject(err); }

                return Promise.resolve(fileName);
            });
        } else {
            return Promise.resolve(zip);
        }
    });
};

function generateTaskList(filePath, layers, taskList) {
    layers.forEach(function(item) {
        if(item.type === 'layer') {
            var task = kml.fromGeoJson(item.features, null, {
                symbol: item.symbol
            })
            .then(function(result) {
                return Promise.resolve({
                    directory: filePath,
                    fileName: item.name,
                    data: result.data
                });
            });

            taskList.push(task);
        } else {
            generateTaskList(filePath + '/' + item.name, item.content, taskList);
        }
    });
}

function getFolder(node, path) {
    if(!path) { return node; }

    var xpath = '.';
    path.split('/').forEach(function(folder) {
        xpath += "Folder[@name='" + folder + "']";
    });

    return node.find(xpath);
}

function createFolder(root, path) {
    var folders = path.split('/');

    var node = root;
    for(var i = 0, maxLen = folders.length; i < maxLen; i++) {
        var matchNode = node.find("./Folder[@name='" + folders[i] + "']");

        if(!matchNode) {
            matchNode = et.SubElement(node, 'Folder');
            matchNode.attrib.name = folders[i];

            var name = et.SubElement(matchNode, 'name');
            name.text = folders[i];
        }

        node = matchNode;
    }

    return node;
}
<commit_msg>fix promise lib bug<commit_after>'use strict';

var et = require('elementtree');
var kml = require('gtran-kml');
var JSZip = require('jszip');
var promiseLib = require('./promise.js');
var fs = require('fs');

module.exports = function(layers, fileName, options) {
    if(options) {
        var Promise = promiseLib.set(options.promiseLib);
    }

    var taskList = [];
    generateTaskList('doc', layers, taskList);

    return Promise.all(taskList)
    .then(function(results) {
        var zip = JSZip();

        var root = et.Element('kml');
        root.attrib.xmlns = 'http://www.opengis.net/kml/2.2';

        var doc = et.SubElement(root, 'Document');

        results.forEach(function(result) {
            var fullPath = result.directory + '/' + result.fileName + '.kml';

            zip.file(fullPath, result.data);

            var folderNode = getFolder(doc, result.directory.substring(4));
            if(!folderNode) {
                folderNode = createFolder(doc, result.directory.substring(4));
            }

            var networkLink = et.SubElement(folderNode, 'NetworkLink');

            var name = et.SubElement(networkLink, 'name');
            name.text = result.fileName;

            var link = et.SubElement(networkLink, 'Link');
            var href = et.SubElement(link, 'href');
            href.text = fullPath;
        });

        var xmlTree = new et.ElementTree(root);

        zip.file('doc.kml', xmlTree.write());
        var buffer = zip.generate({type:"nodebuffer"});

        return Promise.resolve(buffer);
    })
    .then(function(zip) {
        if(fileName) {
            fs.writeFile(fileName, zip, function(err) {
                if(err) { return Promise.reject(err); }

                return Promise.resolve(fileName);
            });
        } else {
            return Promise.resolve(zip);
        }
    });
};

function generateTaskList(filePath, layers, taskList) {
    layers.forEach(function(item) {
        if(item.type === 'layer') {
            var task = kml.fromGeoJson(item.features, null, {
                symbol: item.symbol
            })
            .then(function(result) {
                return Promise.resolve({
                    directory: filePath,
                    fileName: item.name,
                    data: result.data
                });
            });

            taskList.push(task);
        } else {
            generateTaskList(filePath + '/' + item.name, item.content, taskList);
        }
    });
}

function getFolder(node, path) {
    if(!path) { return node; }

    var xpath = '.';
    path.split('/').forEach(function(folder) {
        xpath += "Folder[@name='" + folder + "']";
    });

    return node.find(xpath);
}

function createFolder(root, path) {
    var folders = path.split('/');

    var node = root;
    for(var i = 0, maxLen = folders.length; i < maxLen; i++) {
        var matchNode = node.find("./Folder[@name='" + folders[i] + "']");

        if(!matchNode) {
            matchNode = et.SubElement(node, 'Folder');
            matchNode.attrib.name = folders[i];

            var name = et.SubElement(matchNode, 'name');
            name.text = folders[i];
        }

        node = matchNode;
    }

    return node;
}
<|endoftext|>


<commit_before>/**
 * Defines custom events for touch related gusters.
 * Following events are defined:
 * tap, dbltap, taphold, swipe, swipeleft, swiperight
 * @author aniketn3@gmail.com
 */

/**
 * Tap event definition
 */
(function($, undefn) {
   var undef = undefn, state = {/* id, x, y, target */};

   function clearState() {
      state.id = state.x = state.y = state.moved = state.target = undef;
   }

   function hasMoved(x1, y1, x2, y2) {
      var dx = x1 - x2, dy = y1 - y2;
      return Math.abs(dx) > 30 || Math.abs(dy) > 30;
   }

   function handler(te) {
      var type = te.type, touch, touches = te.touches, cTouches = te.changedTouches, target = te.target;
      switch(type) {
         case "touchstart":
            if(touches.length !== 1) {
               return;
            }
            touch = touches[0];
            state.id = touch.identifier;
            state.x = touch.pageX;
            state.y = touch.pageY;
            state.target = target;
            break;
         case "touchmove":
            touch = cTouches[0];
            if(!state.moved && touch.identifier === state.id) {
               state.moved = hasMoved(state.x, state.y, touch.pageX, touch.pageY);
            }
            break;
         case "touchend":
            if(cTouches.length === 0 || state.moved) {
               clearState();
               return;
            }
            touch = cTouches[0];
            if(touch.identifier === state.id && !state.moved &&
                  // !hasMoved(state.x, state.y, touch.pageX, touch.pageY) &&
                  state.target === target) {
               $(target).dispatch("tap");
            }
            break;
         case "touchcancel":
            clearState();
            break;
      }
   }

   $.defineEvent({
      type: "tap",
      setup: function() {
         $(document).on("touchstart", handler).on("touchmove", handler)
            .on("touchend", handler).on("touchcancel", handler);
      },
      destroy: function() {
         $(document).un("touchstart", handler).un("touchmove", handler)
            .un("touchend", handler).un("touchcancel", handler);
      }
   });
})(h5);

/**
 * Double Tap event definition
 */
(function($) {
   var state = {/* last, target */};

   function handler(te) {
      var now = Date.now(), elapsed = now - (state.last || now), target = te.target;
      if(elapsed > 0 && elapsed < 300 && state.target === target) {
         $(target).dispatch("dbltap");
         state.last = state.target = null;
      }else {
         state.last = now;
         state.target = te.target;
      }
   }

   $.defineEvent({
      type: "dbltap",
      setup: function() {
         $(document).on("tap", handler);
      },
      destroy: function() {
         $(document).un("tap", handler);
      }
   });
})(h5);

/**
 * Tap hold event
 */
(function($, undefn) {
      var undef = undefn, state = {/* moved, x, y */}, timer;

   function hasMoved(x1, y1, x2, y2) {
      var dx = x1 - x2, dy = y1 - y2;
      return Math.abs(dx) > 30 || Math.abs(dy) > 30;
   }

   function clearState() {
      state.moved = state.x = state.y = undef;
   }

   function handler(te) {
      var type = te.type, target = te.target;
      switch(type) {
         case "touchstart":
            if(te.touches.length !== 1) {
               return;
            }
            state.x = te.pageX;
            state.y = te.pageY;
            timer = setTimeout(function() {
               if(!state.moved) {
                  $(target).dispatch("taphold");
               }
            }, 700);
            break;
         case "touchmove":
            if(!state.moved) {
               state.moved = hasMoved(state.x, state.y, te.pageX, te.pageY);
            }
            break;
         case "touchend":
         case "touchcancel":
         default:
            clearTimeout(timer);
            clearState();
            break;
      }
   }

   $.defineEvent({
      type: "taphold",
      setup: function() {
         $(document).on("touchstart", handler).on("touchmove", handler).on("touchend", handler)
            .on("touchcancel", handler);
      },
      destroy: function() {
         $(document).un("touchstart", handler).un("touchmove", handler).un("touchend", handler)
            .un("touchcancel", handler);
      }
   });
})(h5);

/**
 * Swipe event
 */
(function($, undefn) {
   var undef = undefn, state = {};

   /**
    * Calculate the delta difference between two points (x1,y1) and (x2,y2)
    * @return A delta object {x: xdelta, y: ydelta} if the difference is more
    * than 30 pixels or null if its less. The values x and y can be -ve
    */
   function getMovement(x1, y1, x2, y2) {
      var dx = x1 - x2, dy = y1 - y2, xa, ya;
      if((xa = Math.abs(dx)) < 30 & (ya = Math.abs(dy)) < 30) {
         return null;
      }
      return {
         startX: x2,
         startY: y2,
         endX: x1,
         endY: y1,
         dir: xa >= ya ? (dx < 0 ? "left" : "right") : (dy < 0 ? "up" : "down")
      };
   }

   function clearState() {
      state.id = state.x = state.y = state.movement = undef;
   }

   function handler(te) {
      var type = te.type, touches = te.touches, touch, target, m, evtData;

      switch(type) {
         case "touchstart":
            touches = te.touches;
            if(touches.length > 1) {
               return;
            }
            touch = touches[0];
            state.id = touch.identifier;
            state.x = touch.pageX;
            state.y = touch.pageY;
            break;
         case "touchmove":
            touches = te.changedTouches;
            touch = touches[0];
            if(touch.identifier === state.id && te.touches.length === 1) {
               state.movement = getMovement(touch.pageX, touch.pageY, state.x, state.y);
            }
            break;
         case "touchend":
            touches = te.changedTouches;
            touch = touches[0];
            if(state.id === touch.identifier && (m = state.movement)) {
               evtData = {movement: m};
               $(te.target).dispatch("swipe", evtData); // available as event.movement
               clearState();
            }
            break;
         default:
            clearState();
            break;
      }
   }

   $.defineEvent({
      type: "swipe",
      setup: function() {
         $(document).on("touchstart", handler).on("touchmove", handler).on("touchend", handler)
            .on("touchcancel", handler);
      },
      destroy: function() {
         $(document).un("touchstart", handler).un("touchmove", handler).un("touchend", handler)
            .un("touchcancel", handler);
      }
   });
})(h5);

<commit_msg>Reduced the movement thresh hold for touch events<commit_after>/**
 * Defines custom events for touch related gusters.
 * Following events are defined:
 * tap, dbltap, taphold, swipe, swipeleft, swiperight
 * @author aniketn3@gmail.com
 */

/**
 * Tap event definition
 */
(function($, undefn) {
   var undef = undefn, state = {/* id, x, y, target */};

   function clearState() {
      state.id = state.x = state.y = state.moved = state.target = undef;
   }

   function hasMoved(x1, y1, x2, y2) {
      var dx = x1 - x2, dy = y1 - y2;
      return Math.abs(dx) > 10 || Math.abs(dy) > 10;
   }

   function handler(te) {
      var type = te.type, touch, touches = te.touches, cTouches = te.changedTouches, target = te.target;
      switch(type) {
         case "touchstart":
            if(touches.length !== 1) {
               return;
            }
            touch = touches[0];
            state.id = touch.identifier;
            state.x = touch.pageX;
            state.y = touch.pageY;
            state.target = target;
            break;
         case "touchmove":
            touch = cTouches[0];
            if(!state.moved && touch.identifier === state.id) {
               state.moved = hasMoved(state.x, state.y, touch.pageX, touch.pageY);
            }
            break;
         case "touchend":
            if(cTouches.length === 0 || state.moved) {
               clearState();
               return;
            }
            touch = cTouches[0];
            if(touch.identifier === state.id && !state.moved &&
                  // !hasMoved(state.x, state.y, touch.pageX, touch.pageY) &&
                  state.target === target) {
               $(target).dispatch("tap");
            }
            break;
         case "touchcancel":
            clearState();
            break;
      }
   }

   $.defineEvent({
      type: "tap",
      setup: function() {
         $(document).on("touchstart", handler).on("touchmove", handler)
            .on("touchend", handler).on("touchcancel", handler);
      },
      destroy: function() {
         $(document).un("touchstart", handler).un("touchmove", handler)
            .un("touchend", handler).un("touchcancel", handler);
      }
   });
})(h5);

/**
 * Double Tap event definition
 */
(function($) {
   var state = {/* last, target */};

   function handler(te) {
      var now = Date.now(), elapsed = now - (state.last || now), target = te.target;
      if(elapsed > 0 && elapsed < 300 && state.target === target) {
         $(target).dispatch("dbltap");
         state.last = state.target = null;
      }else {
         state.last = now;
         state.target = te.target;
      }
   }

   $.defineEvent({
      type: "dbltap",
      setup: function() {
         $(document).on("tap", handler);
      },
      destroy: function() {
         $(document).un("tap", handler);
      }
   });
})(h5);

/**
 * Tap hold event
 */
(function($, undefn) {
      var undef = undefn, state = {/* moved, x, y */}, timer;

   function hasMoved(x1, y1, x2, y2) {
      var dx = x1 - x2, dy = y1 - y2;
      return Math.abs(dx) > 20 || Math.abs(dy) > 20;
   }

   function clearState() {
      state.moved = state.x = state.y = undef;
   }

   function handler(te) {
      var type = te.type, target = te.target;
      switch(type) {
         case "touchstart":
            if(te.touches.length !== 1) {
               return;
            }
            state.x = te.pageX;
            state.y = te.pageY;
            timer = setTimeout(function() {
               if(!state.moved) {
                  $(target).dispatch("taphold");
               }
            }, 700);
            break;
         case "touchmove":
            if(!state.moved) {
               state.moved = hasMoved(state.x, state.y, te.pageX, te.pageY);
            }
            break;
         case "touchend":
         case "touchcancel":
         default:
            clearTimeout(timer);
            clearState();
            break;
      }
   }

   $.defineEvent({
      type: "taphold",
      setup: function() {
         $(document).on("touchstart", handler).on("touchmove", handler).on("touchend", handler)
            .on("touchcancel", handler);
      },
      destroy: function() {
         $(document).un("touchstart", handler).un("touchmove", handler).un("touchend", handler)
            .un("touchcancel", handler);
      }
   });
})(h5);

/**
 * Swipe event
 */
(function($, undefn) {
   var undef = undefn, state = {};

   /**
    * Calculate the delta difference between two points (x1,y1) and (x2,y2)
    * @return A delta object {x: xdelta, y: ydelta} if the difference is more
    * than 30 pixels or null if its less. The values x and y can be -ve
    */
   function getMovement(x1, y1, x2, y2) {
      var dx = x1 - x2, dy = y1 - y2, xa, ya;
      if((xa = Math.abs(dx)) < 30 & (ya = Math.abs(dy)) < 30) {
         return null;
      }
      return {
         startX: x2,
         startY: y2,
         endX: x1,
         endY: y1,
         dir: xa >= ya ? (dx < 0 ? "left" : "right") : (dy < 0 ? "up" : "down")
      };
   }

   function clearState() {
      state.id = state.x = state.y = state.movement = undef;
   }

   function handler(te) {
      var type = te.type, touches = te.touches, touch, target, m, evtData;

      switch(type) {
         case "touchstart":
            touches = te.touches;
            if(touches.length > 1) {
               return;
            }
            touch = touches[0];
            state.id = touch.identifier;
            state.x = touch.pageX;
            state.y = touch.pageY;
            break;
         case "touchmove":
            touches = te.changedTouches;
            touch = touches[0];
            if(touch.identifier === state.id && te.touches.length === 1) {
               state.movement = getMovement(touch.pageX, touch.pageY, state.x, state.y);
            }
            break;
         case "touchend":
            touches = te.changedTouches;
            touch = touches[0];
            if(state.id === touch.identifier && (m = state.movement)) {
               evtData = {movement: m};
               $(te.target).dispatch("swipe", evtData); // available as event.movement
               clearState();
            }
            break;
         default:
            clearState();
            break;
      }
   }

   $.defineEvent({
      type: "swipe",
      setup: function() {
         $(document).on("touchstart", handler).on("touchmove", handler).on("touchend", handler)
            .on("touchcancel", handler);
      },
      destroy: function() {
         $(document).un("touchstart", handler).un("touchmove", handler).un("touchend", handler)
            .un("touchcancel", handler);
      }
   });
})(h5);

<|endoftext|>


<commit_before>/*
 * tpl3d.js (javascript library, compatible with jQuery, nodejs ant others)
 * version: 0.1.0 (2012-12-12)
 *
 * This work is licensed under the termis of the MIT License:
 * http://www.opensource.org/licenses/mit-license.php
 *
 * Copyright (c) 2012 Vladimir Chizhov <master@mirlord.com>
 *
 */

(function (undefined) {
  'use strict';

  var $;

  // to make easy to use it with jQuery & jQuery-csv
  if (typeof jQuery !== 'undefined' && jQuery) {
    $ = jQuery;
  } else {
    $ = {};
  }

  $.tpl3d = {

    // PRIVATE:

    class2type: {},

    /**
     * Taken from jQuery sources.
     */
    typeOf: function typeOf(obj) {
      return (obj == null) // ignore warnings about non-strict comparison here
            ? String( obj )
            : $.tpl3d.class2type[ Object.prototype.toString.call( obj ) ] || "object";
    },

    // PUBLIC:

    Marshaller: function Marshaller(options) {

      this.mappings = [];
      this.taken = [];
      this.mapDefault = options.mapDefault || false;

      options.init.call(this);
    },

    Unmarshaller: function Unmarshaller(options) {

      this.mappings = [];
      this.taken = [];
      this.available = [];
      this.mapDefault = options.mapDefault || true;

      options.init.call(this);
    },

    MappingFrom: function MappingFrom(from, conv, to) {
      this.from = from;
      this.conv = conv;
      this.to = to;
    },

    helpers: {

    }

  };

  (function initClasses(c2t) {
    var classes = ['Boolean', 'Number', 'String', 'Function', 'Array',
                   'Date', 'RegExp', 'Object', 'Error'];
    var i;
    for (i = 0; i < classes.length; i++) {
      c2t[ "[object " + classes[i] + "]" ] = classes[i].toLowerCase();
    }
  }( $.tpl3d.class2type ));

  /*
   * Unmarshaller
   */
  $.tpl3d.Unmarshaller.prototype.map = function map(from, to) {

    to = to || from;
    this.mappings.push(new $.tpl3d.MappingFrom(from, null, to));
  };

  $.tpl3d.Unmarshaller.prototype.unmarshal = function unmarshal(obj) {

    var out = {};

    // collect obj properties names
    var p;
    for (p in obj) {
      if (obj.hasOwnProperty(p)) {
        this.available.push(p);
      }
    }

    var i;
    for (i = 0; i < this.mappings.length; i++) {
      var m = this.mappings[i];

      if (typeof m.from === 'string') {

        m.from = [ m.from ];
        this.taken.push(m.from);

      } else if ($.tpl3d.typeOf(m.from) === 'regexp') {

        var j, fromArray = [];
        for (j = 0; j < this.available.length; j++) {
          if (m.from.test( this.available[j] )) {
            fromArray.push( this.available[j] );
            this.taken.push( this.available[j] );
          }
        }
        m.from = fromArray;
      }

      if (typeof m.to === 'string') {

        out[m.to] = obj[ m.from[0] ];

      } else if (typeof m.to === 'function') {

        var h;
        for (h = 0; h < m.from.length; h++) {
          var keySeq = m.to(m.from[h]);
          var levelObj = out;
          var k;

          for (k = 0; k < keySeq.length - 1; k++) {
            var key = keySeq[k];
            if (levelObj[ key ] === undefined) {
              levelObj[ key ] = {};
            }
            levelObj = levelObj[ key ];
          }
          levelObj[ keySeq[ keySeq.length - 1 ] ] = obj[ m.from[h] ];
        }
      }
    }
    return out;
  };

  /*
   * Marshaller
   */
  $.tpl3d.Marshaller.prototype.map = function map(to, from) {

    from = from || to;
    //TODO: start here
  };

  $.tpl3d.Marshaller.prototype.marshal = function marshal(obj) {

  };

  // CommonJS module is defined
  if (typeof module !== 'undefined' && module.exports) {
    module.exports = $.tpl3d;
  } else if (this) {
    // in a browser this should point to a window
    this['tpl3d'] = $.tpl3d;
  }

}).call( this );

<commit_msg>Marshaller implemented and API changed to be "chainlike"<commit_after>/*
 * tpl3d.js (javascript library, compatible with jQuery, nodejs ant others)
 * version: 0.1.0 (2012-12-12)
 *
 * This work is licensed under the termis of the MIT License:
 * http://www.opensource.org/licenses/mit-license.php
 *
 * Copyright (c) 2012 Vladimir Chizhov <master@mirlord.com>
 *
 */

(function (undefined) {
  'use strict';

  var $;

  // to make easy to use it with jQuery & jQuery-csv
  if (typeof jQuery !== 'undefined' && jQuery) {
    $ = jQuery;
  } else {
    $ = {};
  }

  $.tpl3d = {

    // PRIVATE:

    class2type: {},

    /**
     * Taken from jQuery sources.
     */
    typeOf: function typeOf(obj) {
      return (obj == null) // ignore warnings about non-strict comparison here
            ? String( obj )
            : $.tpl3d.class2type[ Object.prototype.toString.call( obj ) ] || "object";
    },

    // PUBLIC:

    Marshaller: function Marshaller(options) {

      this.mappings = [];
      this.taken = [];
      this.mapDefault = options.mapDefault || false;

      options.init.call(this);
    },

    Unmarshaller: function Unmarshaller(options) {

      this.mappings = [];
      this.taken = [];
      this.available = [];
      this.mapDefault = options.mapDefault || true;

      options.init.call(this);
    },

    MappingFrom: function MappingFrom(from, conv, to) {
      this.m_from = from;
      this.m_conv = conv;
      this.m_to = to;
    },

    MappingTo: function MappingFrom(to, conv, from) {
      this.m_to = to;
      this.m_conv = conv;
      this.m_from = from;
    },

    helpers: {

    }

  };

  (function initClasses(c2t) {
    var classes = ['Boolean', 'Number', 'String', 'Function', 'Array',
                   'Date', 'RegExp', 'Object', 'Error'];
    var i;
    for (i = 0; i < classes.length; i++) {
      c2t[ "[object " + classes[i] + "]" ] = classes[i].toLowerCase();
    }
  }( $.tpl3d.class2type ));

  /*
   * Mappings
   */
  $.tpl3d.MappingFrom.prototype.to = function to(toValue) {
    this.m_to = toValue;
    return this;
  };

  $.tpl3d.MappingFrom.prototype.conv = function conv(convFn) {
    this.m_conv = convFn;
    return this;
  };

  $.tpl3d.MappingTo.prototype.from = function from(fromValue) {
    this.m_from = fromValue;
    return this;
  };

  $.tpl3d.MappingTo.prototype.conv = function conv(convFn) {
    this.m_conv = convFn;
    return this;
  };

  /*
   * Unmarshaller
   */
  $.tpl3d.Unmarshaller.prototype.map = function map(from) {

    var mapping = new $.tpl3d.MappingFrom(from, null, from);
    this.mappings.push(mapping);
    return mapping;
  };

  $.tpl3d.Unmarshaller.prototype.unmarshal = function unmarshal(obj) {

    var out = {};

    // collect obj properties names
    var p;
    for (p in obj) {
      if (obj.hasOwnProperty(p)) {
        this.available.push(p);
      }
    }

    var i;
    for (i = 0; i < this.mappings.length; i++) {
      var m = this.mappings[i];

      if (typeof m.m_from === 'string') {

        m.m_from = [ m.m_from ];
        this.taken.push(m.m_from);

      } else if ($.tpl3d.typeOf(m.m_from) === 'regexp') {

        var j, fromArray = [];
        for (j = 0; j < this.available.length; j++) {
          if (m.m_from.test( this.available[j] )) {
            fromArray.push( this.available[j] );
            this.taken.push( this.available[j] );
          }
        }
        m.m_from = fromArray;
      }

      if (typeof m.m_to === 'string') {

        out[m.m_to] = obj[ m.m_from[0] ];

      } else if (typeof m.m_to === 'function') {

        var h;
        for (h = 0; h < m.m_from.length; h++) {
          var keySeq = m.m_to(m.m_from[h]);
          var levelObj = out;
          var k;

          for (k = 0; k < keySeq.length - 1; k++) {
            var key = keySeq[k];
            if (levelObj[ key ] === undefined) {
              levelObj[ key ] = {};
            }
            levelObj = levelObj[ key ];
          }
          levelObj[ keySeq[ keySeq.length - 1 ] ] = obj[ m.m_from[h] ];
        }
      }
    }
    return out;
  };

  /*
   * Marshaller
   */
  $.tpl3d.Marshaller.prototype.map = function map(to) {

    var mapping = new $.tpl3d.MappingTo(to, null, to);
    this.mappings.push(mapping);
    return mapping;
  };

  $.tpl3d.Marshaller.prototype.marshalAll = function marshalAll(objArray) {

    var out = [],
        i;
    for (i = 0; i < objArray.length; i++) {
      out.push(this.marshal(objArray[i]));
    }
    return out;
  };

  $.tpl3d.Marshaller.prototype.marshal = function marshal(obj) {

    var out = {};

    var i;
    for (i = 0; i < this.mappings.length; i++) {
      var m = this.mappings[i];
      if (typeof m.m_from === 'string') {
        out[m.m_to] = obj[m.m_from];
        if (m.m_conv) {
          out[m.m_to] = m.m_conv.call(undefined, out[m.m_to]);
        }
      } else if (typeof m.m_from === 'array') {
        throw new Error('Not implemented yet');
      } else if (typeof m.m_from === 'function') {
        out[m.m_to] = m.m_from.call(undefined, obj);
      } else {
        throw new Error('Incorrect mapping');
      }
    }

    return out;
  };

  // CommonJS module is defined
  if (typeof module !== 'undefined' && module.exports) {
    module.exports = $.tpl3d;
  } else if (this) {
    // in a browser this should point to a window
    this['tpl3d'] = $.tpl3d;
  }

}).call( this );

<|endoftext|>


<commit_before>import { Beat } from './elements'
import { Howl } from 'howler'
import { setStatefulDynterval } from 'stateful-dynamic-interval'
import fs from 'fs'

/**
 * Represents a musical song/track that can be synchronized with arbitrary behavior and data in real-time
 */
export class Track {

  /**
   * @param {Object} source track represented in Bach.JSON
   * @param {Audio} [audio] track audio
   * @param {boolean} [loop] enable track looping
   * @param {number} [delay] wait a duration before starting to play
   * @param {Object} [timer] alternative timer/interval API
   * @param {Object} [on] event hooks
   */
  constructor ({ source, audio, loop, volume, tempo, delay, timer, on }) {
    this.source = source
    this.audio  = audio
    this.loop   = loop
    this.volume = volume
    this.tempo  = tempo
    this.delay  = delay
    this.timer  = timer
    this.on     = on || { step: { } }

    this.index = { measure: 0, beat: 0 }
    this.music = new Howl({ src: audio, loop })

    // this.listen()
  }

  /**
   * Parses and returns the source track data as Beat elements
   *
   * @returns {Array<Object>}
   */
  get data () {
    return this.source.data.map(Beat.from)
  }

  /**
   * Provides the source header data of the track
   *
   * @returns {Object}
   */
  get headers () {
    return this.source.headers
  }

  /**
   * Provides the measure and beat found at the track's cursor
   *
   * @returns {Object}
   */
  get state () {
    return this.at(this.cursor.measure, this.cursor.beat)
  }

  /**
   * Provides the measure and beat found one step behind the track's cursor
   *
   * @returns {Object}
   */
  get before () {
    const measure = Math.min(0, this.cursor.measure - 1)
    const beat = Math.min(0, this.cursor.beat - 1)

    return this.at(measure, beat)
  }

  /**
   * Provides the measure and beat found one step ahead of the track's cursor
   *
   * @returns {Object}
   */
  get after () {
    const measure = Math.min(0, this.cursor.measure + 1)
    const beat = Math.min(0, this.cursor.beat + 1)

    return this.at(measure, beat)
  }

  /**
   * Determines the cursors of both the current measure and beat of the track
   *
   * @returns {Object}
   */
  get cursor () {
    return {
      measure : Math.min(Math.max(this.index.measure, 0), this.data.length),
      beat    : Math.min(Math.max(this.index.beat,    0), this.data[0].length)
    }
  }

  /**
   * Determines how long to wait between each interval based on the track and user tempo
   *
   * @returns {number}
   */
  get interval () {
    const global = this.headers.tempo
    const tempos = {
      init: global,
      user: this.tempo || global
    }

    const diff = tempos.user / tempos.init

    return this.headers['ms-per-beat'] / diff
  }

  /**
   * Determines the measure and beat found at the provided indices
   *
   * @returns {Object}
   */
  at (measureIndex, beatIndex) {
    const measure = this.data[measureIndex || 0]
    const beat = measure[beatIndex || 0]

    return { measure, beat }
  }

  /**
   * Synchronizes track with the Howler API
   */
  listen () {
    this.music.on('play',  this.play)
    this.music.on('pause', this.pause)
    this.music.on('stop',  this.stop)
    this.music.on('rate',  this.rate)
    this.music.on('seek',  this.seek)
  }

  /**
   * Emits an event to a topic
   *
   * @param {string} topic calls an event (by key) defined in `this.on`
   * @param {*} data
   */
  // TODO: integrate core Howler event handler
  // load, loaderror, play, end, pause, stop, mute, volume, rate, seek, fade
  emit (topic, data) {
    const action = this.on instanceof Object && this.on[topic]

    if (action instanceof Function) {
      action(data)
    }
  }

  /**
   * Initializes a new stateful dynamic interval, the primary synchronization mechanism
   */
  start () {
    const delay  = this.delay * this.interval
    const config = { wait: this.interval, defer: false }

    setTimeout(() => {
      this.clock = setStatefulDynterval(this.step.bind(this), config, this.timer)
    }, delay || 0)
  }

  /**
   * Loads the audio data and kicks off the synchronization clock
   */
  play () {
    this.music.once('load', () => {
      if (!this.clock) this.start()

      this.music.play()
      this.emit('play')
    })
  }

  /**
   * Stops the audio and the synchronization clock (no resume)
   */
  stop () {
    this.music.stop()
    this.clock.clear()
  }

  /**
   * Pauses the audio and the synchronization clock
   */
  pause () {
    this.music.pause()
    this.clock.pause()
  }

  /**
   * Resumes the audio and the synchronization clock
   */
  resume () {
    this.music.play()
    this.clock.resume()
  }

  /**
   * Mutes the track audio
   */
  mute () {
    this.music.mute()
  }

  /**
   * Seek to a new position in the track
   *
   * @param {number} to position in the track in seconds
   */
  // WARN: probably can't even support this because of dynamic interval (step can change arbitrarily)
  // NOTE: if we assume every interval is the same, relative to tempo, this could work
  seek (to) {
    this.music.seek(to)
  }

  /**
   * Invokes the action to perform on each interval
   *
   * @param {Object} interval context of the stateful dynamic interval
   * @returns {Object} updated interval context
   */
  // TODO: support `bach.Set` (i.e. concurrent elements)
  step (interval) {
    const beat  = this.state.beat
    const next  = this.next.bind(this)
    const play  = this.on.step.play
    const start = this.on.step.start
    const stop  = this.on.step.stop
    const wait  = this.interval * beat.duration

    if (start instanceof Function) {
      start(beat)
    }

    if (beat.exists && play instanceof Function) {
      play(beat)
    }

    // FIXME: a way simpler way to do this is to just see if the index is greater than the first
    // if so, just call stop with the previous beat
    const finish = () => {
      if (stop instanceof Function) {
        const after = setStatefulDynterval(() => {
          stop(beat)

          after.clear()
        }, { wait, defer: false })

        this.clock.add(after)
      }
    }

    next()
    finish()

    return Object.assign(interval || {}, { wait })
  }

  /**
   * Bumps the cursor to the next measure and beat of the track
   */
  next () {
    const limit = {
      measure : Math.max(this.data.length,    1),
      beat    : Math.max(this.data[0].length, 1)
    }

    this.index.measure = (this.index.measure + 1) % limit.measure
    this.index.beat    = (this.index.beat    + 1) % limit.beat
  }

  /**
   * Reads and parses Bach.JSON data from the file system
   *
   * @param {string} path
   */
  static read (path) {
    return new Track({ source: fs.readFileSync(path) })
  }

}
<commit_msg>:art: :checkered_flag: :sparkles: simplified step.stop functionality<commit_after>import { Beat } from './elements'
import { Howl } from 'howler'
import { setStatefulDynterval } from 'stateful-dynamic-interval'
import fs from 'fs'

/**
 * Represents a musical song/track that can be synchronized with arbitrary behavior and data in real-time
 */
export class Track {

  /**
   * @param {Object} source track represented in Bach.JSON
   * @param {Audio} [audio] track audio
   * @param {boolean} [loop] enable track looping
   * @param {number} [delay] wait a duration before starting to play
   * @param {Object} [timer] alternative timer/interval API
   * @param {Object} [on] event hooks
   */
  constructor ({ source, audio, loop, volume, tempo, delay, timer, on }) {
    this.source = source
    this.audio  = audio
    this.loop   = loop
    this.volume = volume
    this.tempo  = tempo
    this.delay  = delay
    this.timer  = timer
    this.on     = on || { step: { } }

    this.index = { measure: 0, beat: 0 }
    this.music = new Howl({ src: audio, loop })

    // this.listen()
  }

  /**
   * Parses and returns the source track data as Beat elements
   *
   * @returns {Array<Object>}
   */
  get data () {
    return this.source.data.map(Beat.from)
  }

  /**
   * Provides the source header data of the track
   *
   * @returns {Object}
   */
  get headers () {
    return this.source.headers
  }

  /**
   * Provides the measure and beat found at the track's cursor
   *
   * @returns {Object}
   */
  get state () {
    return this.at(this.cursor.measure, this.cursor.beat)
  }

  /**
   * Provides the measure and beat found one step behind the track's cursor
   *
   * @returns {Object}
   */
  get before () {
    return this.at(this.cursor.measure - 1, this.cursor.beat - 1)
  }

  /**
   * Provides the measure and beat found one step ahead of the track's cursor
   *
   * @returns {Object}
   */
  get after () {
    return this.at(this.cursor.measure + 1, this.cursor.beat + 1)
  }

  /**
   * Determines the cursors of both the current measure and beat of the track
   *
   * @returns {Object}
   */
  get cursor () {
    return {
      measure : Math.min(Math.max(this.index.measure, 0), this.data.length),
      beat    : Math.min(Math.max(this.index.beat,    0), this.data[0].length)
    }
  }

  /**
   * Determines how long to wait between each interval based on the track and user tempo
   *
   * @returns {number}
   */
  get interval () {
    const global = this.headers.tempo
    const tempos = {
      init: global,
      user: this.tempo || global
    }

    const diff = tempos.user / tempos.init

    return this.headers['ms-per-beat'] / diff
  }

  /**
   * Determines the measure and beat found at the provided indices
   *
   * @returns {Object}
   */
  at (measureIndex, beatIndex) {
    try {
      const measure = this.data[measureIndex]
      const beat = measure[beatIndex]

      return { measure, beat }
    } catch (e) {
      return null
    }
  }

  /**
   * Synchronizes track with the Howler API
   */
  listen () {
    this.music.on('play',  this.play)
    this.music.on('pause', this.pause)
    this.music.on('stop',  this.stop)
    this.music.on('rate',  this.rate)
    this.music.on('seek',  this.seek)
  }

  /**
   * Emits an event to a topic
   *
   * @param {string} topic calls an event (by key) defined in `this.on`
   * @param {*} data
   */
  // TODO: integrate core Howler event handler
  // load, loaderror, play, end, pause, stop, mute, volume, rate, seek, fade
  emit (topic, data) {
    const action = this.on instanceof Object && this.on[topic]

    if (action instanceof Function) {
      action(data)
    }
  }

  /**
   * Initializes a new stateful dynamic interval, the primary synchronization mechanism
   */
  start () {
    const delay  = this.delay * this.interval
    const config = { wait: this.interval, defer: false }

    setTimeout(() => {
      this.clock = setStatefulDynterval(this.step.bind(this), config, this.timer)
    }, delay || 0)
  }

  /**
   * Loads the audio data and kicks off the synchronization clock
   */
  play () {
    this.music.once('load', () => {
      if (!this.clock) this.start()

      this.music.play()
      this.emit('play')
    })
  }

  /**
   * Stops the audio and the synchronization clock (no resume)
   */
  stop () {
    this.music.stop()
    this.clock.clear()
  }

  /**
   * Pauses the audio and the synchronization clock
   */
  pause () {
    this.music.pause()
    this.clock.pause()
  }

  /**
   * Resumes the audio and the synchronization clock
   */
  resume () {
    this.music.play()
    this.clock.resume()
  }

  /**
   * Mutes the track audio
   */
  mute () {
    this.music.mute()
  }

  /**
   * Seek to a new position in the track
   *
   * @param {number} to position in the track in seconds
   */
  // WARN: probably can't even support this because of dynamic interval (step can change arbitrarily)
  // NOTE: if we assume every interval is the same, relative to tempo, this could work
  seek (to) {
    this.music.seek(to)
  }

  /**
   * Invokes the action to perform on each interval
   *
   * @param {Object} interval context of the stateful dynamic interval
   * @returns {Object} updated interval context
   */
  // TODO: support `bach.Set` (i.e. concurrent elements)
  step (interval) {
    const beat = this.state.beat
    const last = this.before
    const next = this.next.bind(this)
    const wait = this.interval * beat.duration
    const { play, start, stop } = this.on.step

    if (stop instanceof Function && last) {
      stop(last)
    }

    if (start instanceof Function) {
      start(beat)
    }

    if (beat.exists && play instanceof Function) {
      play(beat)
    }

    next()

    return Object.assign(interval || {}, { wait })
  }

  /**
   * Bumps the cursor to the next measure and beat of the track
   */
  next () {
    const limit = {
      measure : Math.max(this.data.length,    1),
      beat    : Math.max(this.data[0].length, 1)
    }

    this.index.measure = (this.index.measure + 1) % limit.measure
    this.index.beat    = (this.index.beat    + 1) % limit.beat
  }

  /**
   * Reads and parses Bach.JSON data from the file system
   *
   * @param {string} path
   */
  static read (path) {
    return new Track({ source: fs.readFileSync(path) })
  }

}
<|endoftext|>


<commit_before>'use strict';

const glob = require('glob'),
    isHTML = require('is-html'),
    isURL = require('is-absolute-url'),
    jsdom = require('./jsdom.js'),
    postcss = require('postcss'),
    uncss = require('./lib.js'),
    utility = require('./utility.js'),
    _ = require('lodash');

/**
 * Get the contents of HTML pages through jsdom.
 * @param  {Array}   files   List of HTML files
 * @param  {Object}  options UnCSS options
 * @return {Array|Promise}
 */
function getHTML(files, options) {
    if (_.isString(files)) {
        files = [files];
    }

    files = _.flatten(files.map((file) => {
        if (!isURL(file) && !isHTML(file)) {
            return glob.sync(file);
        }
        return file;
    }));

    if (!files.length) {
        return Promise.reject(new Error('UnCSS: no HTML files found'));
    }

    // Save files for later reference.
    options.files = files;
    return Promise.all(files.map((file) => jsdom.fromSource(file, options)));
}

/**
 * Get the contents of CSS files.
 * @param  {Array}   files   List of HTML files
 * @param  {Object}  options UnCSS options
 * @param  {Array}   pages   Pages opened by jsdom
 * @return {Promise}
 */
function getStylesheets(files, options, pages) {
    if (options.stylesheets && options.stylesheets.length) {
        /* Simulate the behavior below */
        return Promise.resolve([files, options, pages, [options.stylesheets]]);
    }
    /* Extract the stylesheets from the HTML */
    return Promise.all(pages.map((page) => jsdom.getStylesheets(page, options)))
    .then((stylesheets) => [files, options, pages, stylesheets]);
}

/**
 * Get the contents of CSS files.
 * @param  {Array}   files       List of HTML files
 * @param  {Object}  options     UnCSS options
 * @param  {Array}   pages       Pages opened by jsdom
 * @param  {Array}   stylesheets List of CSS files
 * @return {Array}
 */
function getCSS([files, options, pages, stylesheets]) {
    /* Ignore specified stylesheets */
    if (options.ignoreSheets.length) {
        stylesheets = stylesheets
        .map((arr) => {
            return arr.filter((sheet) => {
                return _.every(options.ignoreSheets, (ignore) => {
                    if (_.isRegExp(ignore)) {
                        return !ignore.test(sheet);
                    }
                    return sheet !== ignore;
                });
            });
        });
    }

    if (_.flatten(stylesheets).length) {
        /* Only run this if we found links to stylesheets (there may be none...)
         *  files       = ['some_file.html', 'some_other_file.html']
         *  stylesheets = [['relative_css_path.css', ...],
         *                 ['maybe_a_duplicate.css', ...]]
         * We need to - make the stylesheets' paths relative to the HTML files,
         *            - flatten the array,
         *            - remove duplicates
         */
        stylesheets =
            _.chain(stylesheets)
                .map((sheets, i) => utility.parsePaths(files[i], sheets, options))
                .flatten()
                .uniq()
                .value();
    } else {
        /* Reset the array if we didn't find any link tags */
        stylesheets = [];
    }
    return Promise.all([options, pages, utility.readStylesheets(stylesheets, options.banner)]);
}

/**
 * Do the actual work
 * @param  {Array}   files       List of HTML files
 * @param  {Object}  options     UnCSS options
 * @param  {Array}   pages       Pages opened by jsdom
 * @param  {Array}   stylesheets List of CSS files
 * @return {Promise}
 */
function processWithTextApi([options, pages, stylesheets]) {
    /* If we specified a raw string of CSS, add it to the stylesheets array */
    if (options.raw) {
        if (_.isString(options.raw)) {
            stylesheets.push(options.raw);
        } else {
            throw new Error('UnCSS: options.raw - expected a string');
        }
    }

    /* At this point, there isn't any point in running the rest of the task if:
     * - We didn't specify any stylesheet links in the options object
     * - We couldn't find any stylesheet links in the HTML itself
     * - We weren't passed a string of raw CSS in addition to, or to replace
     *     either of the above
     */
    if (!_.flatten(stylesheets).length) {
        throw new Error('UnCSS: no stylesheets found');
    }

    /* OK, so we have some CSS to work with!
     * Three steps:
     * - Parse the CSS
     * - Remove the unused rules
     * - Return the optimized CSS as a string
     */
    const cssStr = stylesheets.join(' \n');
    let pcss,
        report;

    try {
        pcss = postcss.parse(cssStr);
    } catch (err) {
        /* Try and construct a helpful error message */
        throw utility.parseErrorMessage(err, cssStr);
    }
    return uncss(pages, pcss, options.ignore).then(([css, rep]) => {
        let newCssStr = '';
        postcss.stringify(css, (result) => {
            newCssStr += result;
        });

        if (options.report) {
            report = {
                original: cssStr,
                selectors: rep
            };
        }
        return [newCssStr, report];
    });
}

/**
 * Main exposed function.
 * Here we check the options and callback, then run the files through jsdom.
 * @param  {Array}    files     Array of filenames
 * @param  {Object}   [options] options
 * @param  {Function} callback(Error, String, Object)
 */
function init(files, options, callback) {

    if (_.isFunction(options)) {
        /* There were no options, this argument is actually the callback */
        callback = options;
        options = {};
    } else if (!_.isFunction(callback)) {
        throw new TypeError('UnCSS: expected a callback');
    }

    /* Try and read options from the specified uncssrc file */
    if (options.uncssrc) {
        try {
            /* Manually-specified options take precedence over uncssrc options */
            options = _.merge(utility.parseUncssrc(options.uncssrc), options);
        } catch (err) {
            if (err instanceof SyntaxError) {
                callback(new SyntaxError('UnCSS: uncssrc file is invalid JSON.'));
                return;
            }
            callback(err);
            return;
        }
    }

    /* Assign default values to options, unless specified */
    options = _.defaults(options, {
        csspath: '',
        ignore: [],
        media: [],
        timeout: 0,
        report: false,
        ignoreSheets: [],
        html: files,
        banner: true,
        // gulp-uncss parameters:
        raw: null,
        userAgent: 'uncss',
        inject: null
    });

    process(options).then(([css, report]) => callback(null, css, report), callback);
}

function processAsPostCss(files, options, pages) {
    return uncss(pages, options.rawPostCss, options.ignore);
}

function process(opts) {
    const resource = getHTML(opts.html, opts);
    return resource.then((pages) => {
        function cleanup (result) {
            pages.forEach((page) => page.close());
            return result;
        }

        if (opts.usePostCssInternal) {
            return processAsPostCss(opts.files, opts, pages)
            .then(cleanup);
        }
        return getStylesheets(opts.files, opts, pages)
          .then(getCSS)
          .then(processWithTextApi)
          .then(cleanup);
    });
}

const postcssPlugin = postcss.plugin('uncss', (opts) => {
    opts = _.defaults(opts, {
        usePostCssInternal: true,
        // Ignore stylesheets in the HTML files; only use those from the stream
        ignoreSheets: [/\s*/],
        html: [],
        ignore: []
    });

    return function (css, result) { // eslint-disable-line no-unused-vars
        opts = Object.assign(opts, {
            // This is used to pass the css object in to processAsPostCSS
            rawPostCss: css
        });

        return process(opts);
    };
});

module.exports = init;
module.exports.postcssPlugin = postcssPlugin;
<commit_msg>Avoid of options mutation (#391)<commit_after>'use strict';

const glob = require('glob'),
    isHTML = require('is-html'),
    isURL = require('is-absolute-url'),
    jsdom = require('./jsdom.js'),
    postcss = require('postcss'),
    uncss = require('./lib.js'),
    utility = require('./utility.js'),
    _ = require('lodash');

/**
 * Get the contents of HTML pages through jsdom.
 * @param  {Array}   files   List of HTML files
 * @param  {Object}  options UnCSS options
 * @return {Array|Promise}
 */
function getHTML(files, options) {
    if (_.isString(files)) {
        files = [files];
    }

    files = _.flatten(files.map((file) => {
        if (!isURL(file) && !isHTML(file)) {
            return glob.sync(file);
        }
        return file;
    }));

    if (!files.length) {
        return Promise.reject(new Error('UnCSS: no HTML files found'));
    }

    // Save files for later reference.
    options.files = files;
    return Promise.all(files.map((file) => jsdom.fromSource(file, options)));
}

/**
 * Get the contents of CSS files.
 * @param  {Array}   files   List of HTML files
 * @param  {Object}  options UnCSS options
 * @param  {Array}   pages   Pages opened by jsdom
 * @return {Promise}
 */
function getStylesheets(files, options, pages) {
    if (options.stylesheets && options.stylesheets.length) {
        /* Simulate the behavior below */
        return Promise.resolve([files, options, pages, [options.stylesheets]]);
    }
    /* Extract the stylesheets from the HTML */
    return Promise.all(pages.map((page) => jsdom.getStylesheets(page, options)))
    .then((stylesheets) => [files, options, pages, stylesheets]);
}

/**
 * Get the contents of CSS files.
 * @param  {Array}   files       List of HTML files
 * @param  {Object}  options     UnCSS options
 * @param  {Array}   pages       Pages opened by jsdom
 * @param  {Array}   stylesheets List of CSS files
 * @return {Array}
 */
function getCSS([files, options, pages, stylesheets]) {
    /* Ignore specified stylesheets */
    if (options.ignoreSheets.length) {
        stylesheets = stylesheets
        .map((arr) => {
            return arr.filter((sheet) => {
                return _.every(options.ignoreSheets, (ignore) => {
                    if (_.isRegExp(ignore)) {
                        return !ignore.test(sheet);
                    }
                    return sheet !== ignore;
                });
            });
        });
    }

    if (_.flatten(stylesheets).length) {
        /* Only run this if we found links to stylesheets (there may be none...)
         *  files       = ['some_file.html', 'some_other_file.html']
         *  stylesheets = [['relative_css_path.css', ...],
         *                 ['maybe_a_duplicate.css', ...]]
         * We need to - make the stylesheets' paths relative to the HTML files,
         *            - flatten the array,
         *            - remove duplicates
         */
        stylesheets =
            _.chain(stylesheets)
                .map((sheets, i) => utility.parsePaths(files[i], sheets, options))
                .flatten()
                .uniq()
                .value();
    } else {
        /* Reset the array if we didn't find any link tags */
        stylesheets = [];
    }
    return Promise.all([options, pages, utility.readStylesheets(stylesheets, options.banner)]);
}

/**
 * Do the actual work
 * @param  {Array}   files       List of HTML files
 * @param  {Object}  options     UnCSS options
 * @param  {Array}   pages       Pages opened by jsdom
 * @param  {Array}   stylesheets List of CSS files
 * @return {Promise}
 */
function processWithTextApi([options, pages, stylesheets]) {
    /* If we specified a raw string of CSS, add it to the stylesheets array */
    if (options.raw) {
        if (_.isString(options.raw)) {
            stylesheets.push(options.raw);
        } else {
            throw new Error('UnCSS: options.raw - expected a string');
        }
    }

    /* At this point, there isn't any point in running the rest of the task if:
     * - We didn't specify any stylesheet links in the options object
     * - We couldn't find any stylesheet links in the HTML itself
     * - We weren't passed a string of raw CSS in addition to, or to replace
     *     either of the above
     */
    if (!_.flatten(stylesheets).length) {
        throw new Error('UnCSS: no stylesheets found');
    }

    /* OK, so we have some CSS to work with!
     * Three steps:
     * - Parse the CSS
     * - Remove the unused rules
     * - Return the optimized CSS as a string
     */
    const cssStr = stylesheets.join(' \n');
    let pcss,
        report;

    try {
        pcss = postcss.parse(cssStr);
    } catch (err) {
        /* Try and construct a helpful error message */
        throw utility.parseErrorMessage(err, cssStr);
    }
    return uncss(pages, pcss, options.ignore).then(([css, rep]) => {
        let newCssStr = '';
        postcss.stringify(css, (result) => {
            newCssStr += result;
        });

        if (options.report) {
            report = {
                original: cssStr,
                selectors: rep
            };
        }
        return [newCssStr, report];
    });
}

/**
 * Main exposed function.
 * Here we check the options and callback, then run the files through jsdom.
 * @param  {Array}    files     Array of filenames
 * @param  {Object}   [options] options
 * @param  {Function} callback(Error, String, Object)
 */
function init(files, options, callback) {

    if (_.isFunction(options)) {
        /* There were no options, this argument is actually the callback */
        callback = options;
        options = {};
    } else if (!_.isFunction(callback)) {
        throw new TypeError('UnCSS: expected a callback');
    }

    /* Try and read options from the specified uncssrc file */
    if (options.uncssrc) {
        try {
            /* Manually-specified options take precedence over uncssrc options */
            options = _.merge(utility.parseUncssrc(options.uncssrc), options);
        } catch (err) {
            if (err instanceof SyntaxError) {
                callback(new SyntaxError('UnCSS: uncssrc file is invalid JSON.'));
                return;
            }
            callback(err);
            return;
        }
    }

    /* Assign default values to options, unless specified */
    options = _.merge({
        csspath: '',
        ignore: [],
        media: [],
        timeout: 0,
        report: false,
        ignoreSheets: [],
        html: files,
        banner: true,
        // gulp-uncss parameters:
        raw: null,
        userAgent: 'uncss',
        inject: null
    }, options);

    process(options).then(([css, report]) => callback(null, css, report), callback);
}

function processAsPostCss(files, options, pages) {
    return uncss(pages, options.rawPostCss, options.ignore);
}

function process(opts) {
    const resource = getHTML(opts.html, opts);
    return resource.then((pages) => {
        function cleanup (result) {
            pages.forEach((page) => page.close());
            return result;
        }

        if (opts.usePostCssInternal) {
            return processAsPostCss(opts.files, opts, pages)
            .then(cleanup);
        }
        return getStylesheets(opts.files, opts, pages)
          .then(getCSS)
          .then(processWithTextApi)
          .then(cleanup);
    });
}

const postcssPlugin = postcss.plugin('uncss', (opts) => {
    opts = _.defaults(opts, {
        usePostCssInternal: true,
        // Ignore stylesheets in the HTML files; only use those from the stream
        ignoreSheets: [/\s*/],
        html: [],
        ignore: []
    });

    return function (css, result) { // eslint-disable-line no-unused-vars
        opts = Object.assign(opts, {
            // This is used to pass the css object in to processAsPostCSS
            rawPostCss: css
        });

        return process(opts);
    };
});

module.exports = init;
module.exports.postcssPlugin = postcssPlugin;
<|endoftext|>


<commit_before>/*
MagJS v0.23.8
http://github.com/magnumjs/mag.js
(c) Michael Glazer
License: MIT
*/

(function(mag) {

  'use strict';

  var utils = {};

  utils.callHook = function (hookins, key, name, i, data, before) {
    if (hookins[name][i].key == key) {
      before = data.value
      data.change = false;
      hookins[name][i].handler.call(hookins[name][i].context, data);
        //if any change
      if (before !==  data.value) {
        data.change = true
      }
    }
  }

  var handlers = []
  utils.onLCEvent = function(eventName, index, handler) {
    handlers[eventName + '-' + index] = handler
  }
  utils.callLCEvent = function(eventName, controller, node, index, once, extra) {
    var isPrevented = false,
      event = {
        preventDefault: function() {
          isPrevented = true
        }
      }

    if (controller && controller[eventName]) {
      controller[eventName].call(controller, event, node, mag.mod.getProps(index), index, extra)
      if (once) controller[eventName] = 0
    }

    // on Handlers
    handlers[eventName + '-' + index] && handlers[eventName + '-' + index](mag.mod.getState(index), mag.mod.getProps(index));

    return isPrevented
  }

  //UTILITY
  utils.copy = function(o) {
    return Object.assign({}, o);
  }

  utils.merge = function() {
    return Object.assign.apply({}, arguments);
  }

  var a = {
    i: [],
    isItem: function(id) {
      return a.i.indexOf(id) > -1
    },
    setItem: function(id) {
      a.i[a.i.length] = id
    },
    getItem: function(id) {
      return a.i.indexOf(id)
    },
    getItemVal: function(index) {
      return a.i[index]
    },
    removeItem: function(id) {
      a.i.splice(a.i.indexOf(id), 1)
    }
  }

  utils.items = a

  utils.getItemInstanceIdAll = function() {
    return a.i
  }

  utils.getItemInstanceId = function(id) {
    if (a.isItem(id)) {
      return a.getItem(id)
    } else {
      a.setItem(id)
      return a.getItem(id)
    }
  }

  mag.utils = utils

}(mag));
<commit_msg>Update utils.js<commit_after>/*
MagJS v0.23.8
http://github.com/magnumjs/mag.js
(c) Michael Glazer
License: MIT
*/

(function(mag) {

  'use strict';

  var utils = {};

  utils.callHook = function (hookins, key, name, i, data, before) {
    if (hookins[name][i].key == key) {
      before = {v: data.value, k: data.key}
      data.change = false;
      hookins[name][i].handler.call(hookins[name][i].context, data);
        //if any change
      if (before !==  {v: data.value, k: data.key}) {
        data.change = true
      }
    }
  }

  var handlers = []
  utils.onLCEvent = function(eventName, index, handler) {
    handlers[eventName + '-' + index] = handler
  }
  utils.callLCEvent = function(eventName, controller, node, index, once, extra) {
    var isPrevented = false,
      event = {
        preventDefault: function() {
          isPrevented = true
        }
      }

    if (controller && controller[eventName]) {
      controller[eventName].call(controller, event, node, mag.mod.getProps(index), index, extra)
      if (once) controller[eventName] = 0
    }

    // on Handlers
    handlers[eventName + '-' + index] && handlers[eventName + '-' + index](mag.mod.getState(index), mag.mod.getProps(index));

    return isPrevented
  }

  //UTILITY
  utils.copy = function(o) {
    return Object.assign({}, o);
  }

  utils.merge = function() {
    return Object.assign.apply({}, arguments);
  }

  var a = {
    i: [],
    isItem: function(id) {
      return a.i.indexOf(id) > -1
    },
    setItem: function(id) {
      a.i[a.i.length] = id
    },
    getItem: function(id) {
      return a.i.indexOf(id)
    },
    getItemVal: function(index) {
      return a.i[index]
    },
    removeItem: function(id) {
      a.i.splice(a.i.indexOf(id), 1)
    }
  }

  utils.items = a

  utils.getItemInstanceIdAll = function() {
    return a.i
  }

  utils.getItemInstanceId = function(id) {
    if (a.isItem(id)) {
      return a.getItem(id)
    } else {
      a.setItem(id)
      return a.getItem(id)
    }
  }

  mag.utils = utils

}(mag));
<|endoftext|>


<commit_before>import ExpressionEvaluator from 'ExpressionEvaluator';

/** Objects & Classes **/

export let DefaultTemplates = {
	isEmpty() {
		return "
A template has not been initialized for this page yet
";
	}
	hasError() {
		return "
There was an error finding the template for this page
";
	}
}

export class CollectionQuerier {
	constructor() {
		this.context = null;
	}
	with(collectionName) {
		let collection;
		if (typeof collectionName != "string" || !(collection = getSafe(global, `bruteframework.collections.${collectionName}`)))
			throw new Error("Collection with name '${collectionName}' does not exist.")

		this.context = collection;
	}
	find(queryObj) {
		if (this.context instanceof Colection && typeof queryObj == "object" && Object.keys(queryObj).length === 1) {
			const key = Object.keys(queryObj)[0];
			let expression = queryObj[key];
			if (expression == '*') {
				return this.context.findAll();
			} else {
				ExpressionEvaluator[(expression.find('{{') == -1? 'simple': 'compound')](expression, key, this.context);
			};
		};

		return [];
	}
};

/** Methods **/
export function println(str) {
	console.log(`\n${str}\n`);
}

export function findByProp(arr, prop, val, getIndex) {
	for (let i = 0; i < arr.length; i++) {
		if(arr[i][prop] == val)
			return getIndex? arr[i]: i;
	};
	return getIndex && -1;
}

export function weaveQuery(className, classId) {
	return getSafe(global, `bruteframework.weaveClasses.${className}`, (classInstancesMap) => {
		return classInstancesMap.get(classId);
	});
}

export function getSafe(o, str, executeOnSuccess) {
	let runner = o;
	const props = str.split('.');
	for (const prop of props) {
		if (!(prop in runner)) return;
		runner = runner[prop];
	};
	if (executeOnSuccess) return executeOnSuccess(runner);
	return runner;
}

export function deepMatch() {
	for(let i = 1; arguments[i]; i++) {
		const temp1 = arguments[i-1];
		const temp2 = arguments[i];

		if (Object.keys(temp2).length != Object.keys(temp1).length)
			return false;
		for (prop in temp2) {
			if (temp2[prop] != temp1[prop])
				return false;
		}
	}
	return true;
}

export function getConfigs() {
	return getSafe(global, 'bruteframework.configs');
}

export function easymerge() {
	let toRet = [];
	for (let arg of arguments) {
		if (Array.isArray(arg)) {
			arg.forEach((item) => toRet.push(item));
		} else {
			toRet.push(arg);
		};
	}
	return toRet;
}
<commit_msg>sorted utils alphabetically<commit_after>import ExpressionEvaluator from 'ExpressionEvaluator';

/** Objects & Classes **/

export let DefaultTemplates = {
	isEmpty() {
		return "
A template has not been initialized for this page yet
";
	}
	hasError() {
		return "
There was an error finding the template for this page
";
	}
}

export class CollectionQuerier {
	constructor() {
		this.context = null;
	}
	with(collectionName) {
		let collection;
		if (typeof collectionName != "string" || !(collection = getSafe(global, `bruteframework.collections.${collectionName}`)))
			throw new Error("Collection with name '${collectionName}' does not exist.")

		this.context = collection;
	}
	find(queryObj) {
		if (this.context instanceof Colection && typeof queryObj == "object" && Object.keys(queryObj).length === 1) {
			const key = Object.keys(queryObj)[0];
			let expression = queryObj[key];
			if (expression == '*') {
				return this.context.findAll();
			} else {
				ExpressionEvaluator[(expression.find('{{') == -1? 'simple': 'compound')](expression, key, this.context);
			};
		};

		return [];
	}
};

/** Methods **/

export function deepMatch() {
	for(let i = 1; arguments[i]; i++) {
		const temp1 = arguments[i-1];
		const temp2 = arguments[i];

		if (Object.keys(temp2).length != Object.keys(temp1).length)
			return false;
		for (prop in temp2) {
			if (temp2[prop] != temp1[prop])
				return false;
		}
	}
	return true;
}

export function easymerge() {
	let toRet = [];
	for (let arg of arguments) {
		if (Array.isArray(arg)) {
			arg.forEach((item) => toRet.push(item));
		} else {
			toRet.push(arg);
		};
	}
	return toRet;
}

export function findByProp(arr, prop, val, getIndex) {
	for (let i = 0; i < arr.length; i++) {
		if(arr[i][prop] == val)
			return getIndex? arr[i]: i;
	};
	return getIndex && -1;
}

export function getConfigs() {
	return getSafe(global, 'bruteframework.configs');
}

export function getSafe(o, str, executeOnSuccess) {
	let runner = o;
	const props = str.split('.');
	for (const prop of props) {
		if (!(prop in runner)) return;
		runner = runner[prop];
	};
	if (executeOnSuccess) return executeOnSuccess(runner);
	return runner;
}

export function println(str) {
	console.log(`\n${str}\n`);
}

export function weaveQuery(className, classId) {
	return getSafe(global, `bruteframework.weaveClasses.${className}`, (classInstancesMap) => {
		return classInstancesMap.get(classId);
	});
}
<|endoftext|>


<commit_before>import isFunction from 'lodash.isfunction'
import { NAMESPACE_SEP } from './constants'

export asign from 'object-assign'
export getByPath from 'lodash.get'
export flatten from 'lodash.flatten'
export minimatch from 'minimatch'
export isPlainObject from 'is-plain-object'
export isFunction from 'lodash.isfunction'
// Test generator function that compilied with babel(babel-profill)
export const isGeneratorFn = fn => (
  typeof fn === 'function' &&
  fn.constructor &&
  (
    fn.constructor.name === 'GeneratorFunction' ||
    fn.constructor.displayName === 'GeneratorFunction'
  )
)
export const isArray = Array.isArray.bind(Array)
export const isString = str => typeof str === 'string'
export const ucfirst = s => s.charAt(0).toUpperCase() + s.substr(1)
export const filename = file => file.match(/([^/]+)\.js$/)[1]
export const isAllFunction = obj => Object.keys(obj).every(key => isFunction(obj[key]))
export const isHTMLElement = node => !!(node && typeof node === 'object' && node.nodeType && node.nodeName)

export const unfixType = (namespace, type) => (type.replace(`${namespace}${NAMESPACE_SEP}`, ''))
export const prefixType = (namespace, type) => (`${namespace}${NAMESPACE_SEP}${type}`)

export const prefixObject = (namespace, obj) => Object.keys(obj).reduce((memo, type) => {
  memo[prefixType(namespace, type)] = obj[type]
  return memo
}, {})

export const prefixAndValidate = (type, model) => {
  const { effects, reducers, namespace } = model

  const prefixed = prefixType(namespace, type)
  const final = prefixed.replace(/\/@@[^/]+?$/, '')
  if (reducers[final] || effects[final]) {
    return final
  }
  return type
}

export const getEnhancer = (enhancers) => {
  if (enhancers && enhancers.length) {
    return reducer => enhancers.reduce((memo, reducerEnhancer) => reducerEnhancer(memo), reducer)
  }
  return f => f
}

export const getNamespaceFromPath = (path) => {
  const parts = path.split('/')
  const file = parts.pop()

  if (path[0] === '.' || path[0] === '/') {
    parts.shift()
  }

  parts.push(filename(file))

  return parts.join('.')
}

export const fixNamespace = namespace => namespace.replace(/\//g, '_').replace(/\./g, '/')
<commit_msg>update getEnhancer<commit_after>import isFunction from 'lodash.isfunction'
import { NAMESPACE_SEP } from './constants'

export asign from 'object-assign'
export getByPath from 'lodash.get'
export flatten from 'lodash.flatten'
export minimatch from 'minimatch'
export isPlainObject from 'is-plain-object'
export isFunction from 'lodash.isfunction'
// Test generator function that compilied with babel(babel-profill)
export const isGeneratorFn = fn => (
  typeof fn === 'function' &&
  fn.constructor &&
  (
    fn.constructor.name === 'GeneratorFunction' ||
    fn.constructor.displayName === 'GeneratorFunction'
  )
)
export const isArray = Array.isArray.bind(Array)
export const isString = str => typeof str === 'string'
export const ucfirst = s => s.charAt(0).toUpperCase() + s.substr(1)
export const filename = file => file.match(/([^/]+)\.js$/)[1]
export const isAllFunction = obj => Object.keys(obj).every(key => isFunction(obj[key]))
export const isHTMLElement = node => !!(node && typeof node === 'object' && node.nodeType && node.nodeName)

export const unfixType = (namespace, type) => (type.replace(`${namespace}${NAMESPACE_SEP}`, ''))
export const prefixType = (namespace, type) => (`${namespace}${NAMESPACE_SEP}${type}`)

export const prefixObject = (namespace, obj) => Object.keys(obj).reduce((memo, type) => {
  memo[prefixType(namespace, type)] = obj[type]
  return memo
}, {})

export const prefixAndValidate = (type, model) => {
  const { effects, reducers, namespace } = model

  const prefixed = prefixType(namespace, type)
  const final = prefixed.replace(/\/@@[^/]+?$/, '')
  if (reducers[final] || effects[final]) {
    return final
  }
  return type
}

export const getEnhancer = (enhancers) => {
  if (enhancers) {
    if (isArray(enhancers)) {
      return reducer => enhancers.reduce((memo, reducerEnhancer) => reducerEnhancer(memo), reducer)
    } else if (isFunction(enhancers)) {
      return reducer => enhancers(reducer)
    }
  }

  return f => f
}

export const getNamespaceFromPath = (path) => {
  const parts = path.split('/')
  const file = parts.pop()

  if (path[0] === '.' || path[0] === '/') {
    parts.shift()
  }

  parts.push(filename(file))

  return parts.join('.')
}

export const fixNamespace = namespace => namespace.replace(/\//g, '_').replace(/\./g, '/')
<|endoftext|>


<commit_before>// Import the neccesary modules.
import childProcess from 'child_process';
import fs from 'fs';
import path from 'path';

import { dbName, statusFile, tempDir, updatedFile } from './config/constants';
import { name } from '../package.json';

/**
 * Create an emty file.
 * @param {String} path - The path to the file to create.
 * @returns {void}
 */
function createEmptyFile(path) {
  fs.createWriteStream(path).end();
}

/**
 * Removes all the files in the temporary directory.
 * @param {String} [tmpPath=popcorn-api/tmp] - The path to remove all the files
 * within (Default is set in the `config/constants.js`).
 * @returns {void}
 */
function resetTemp(tmpPath = tempDir) {
  const files = fs.readdirSync(tmpPath);
  files.forEach(file => {
    const stats = fs.statSync(path.join(tmpPath, file));
    if (stats.isDirectory()) {
      resetTemp(file);
    } else if (stats.isFile()) {
      fs.unlinkSync(path.join(tmpPath, file));
    }
  });
}

/**
 * Create the temporary directory.
 * @returns {void}
 */
export function createTemp() {
  if (!fs.existsSync(tempDir)) fs.mkdirSync(tempDir);
  if (fs.existsSync(tempDir)) resetTemp();

  createEmptyFile(path.join(tempDir, statusFile));
  createEmptyFile(path.join(tempDir, updatedFile));
}

/**
 * Execute a command from within the root folder.
 * @param {String} cmd - The command to execute.
 * @returns {Promise} - The output of the command.
 */
export function executeCommand(cmd) {
  return new Promise((resolve, reject) => {
    childProcess.exec(cmd, {
      cwd: __dirname
    }, (err, stdout) => {
      if (err) return reject(err);
      return resolve(stdout.split('\n').join(''));
    });
  });
}

/**
 * Export a collection to a JSON file.
 * @param {String} collection - The collection to export.
 * @returns {Promise} - The output of the mongoexport command.
 */
export function exportCollection(collection) {
  const jsonFile = path.join(tempDir, `${collection}s.json`);
  logger.info(`Exporting collection: '${collection}s', to: '${jsonFile}'`);

  const cmd = `mongoexport --db ${dbName} --collection ${collection}s --out '${jsonFile}'`;
  return executeCommand(cmd);
}

/**
 * Import a json file to a collection.
 * @param {String} collection - The collection to import.
 * @param {String} jsonFile - The json file to import..
 * @returns {Promise} - The output of the mongoimport command.
 */
export function importCollection(collection, jsonFile) {
  if (!path.isAbsolute(jsonFile))
    jsonFile = path.join(process.cwd(), jsonFile); // eslint-disable-line no-param-reassign
  if (!fs.existsSync(jsonFile))
    throw new Error(`Error: no such file found for '${jsonFile}'`);

  logger.info(`Importing collection: '${collection}', from: '${jsonFile}'`);

  const cmd = `mongoimport --db ${dbName} --collection ${collection}s --file '${jsonFile}' --upsert`;
  return executeCommand(cmd);
}

/**
 * Error logger function.
 * @param {String} errorMessage - The error message you want to display.
 * @returns {Error} - A new error with the given error message.
 */
export function onError(errorMessage) {
  logger.error(errorMessage);
  return new Error(errorMessage);
}

/**
 * Reset the default log file.
 * @returns {void}
 */
export function resetLog() {
  const logFile = path.join(tempDir, `${name}.log`);
  if (fs.existsSync(logFile)) fs.unlinkSync(logFile);
}

/**
 * Search for a key in an array of object.
 * @param {String} key - The key to search for.
 * @param {String} value - The value of the key to search for.
 * @return {Object} - The object with the correct key-value pair.
 */
export function search(key, value) {
  return element => element[key] === value;
}

/**
 * Updates the `lastUpdated.json` file.
 * @param {String} [updated=Date.now()] - The epoch time when the API last
 * started scraping.
 * @returns {void}
 */
export function setLastUpdated(updated = (Math.floor(new Date().getTime() / 1000))) {
  fs.writeFile(path.join(tempDir, updatedFile), JSON.stringify({
    updated
  }), () => {});
}

/**
 * Updates the `status.json` file.
 * @param {String} [status=Idle] - The status which will be set to in the
 * `status.json` file.
 * @returns {void}
 */
export function setStatus(status = 'Idle') {
  fs.writeFile(path.join(tempDir, statusFile), JSON.stringify({
    status
  }), () => {});
}

/**
 * Check that all images are fetched from the provider.
 * @param {Object} images - The images.
 * @param {String} holder - The image holder.
 * @throws {Error} - 'An image could not been found'.
 * @returns {void}
 */
export function checkImages(images, holder) {
  for (const image of images) {
    if (image === holder) {
      throw new Error('An image could not been found');
    }
  }
}
<commit_msg>shortend utils.js and moved checkImages to BaseHelper<commit_after>// Import the neccesary modules.
import childProcess from 'child_process';
import fs from 'fs';
import path from 'path';

import {
  dbName,
  statusFile,
  tempDir,
  updatedFile
} from './config/constants';
import { name } from '../package.json';

/**
 * Create an emty file.
 * @param {String} path - The path to the file to create.
 * @returns {void}
 */
function _createEmptyFile(path) {
  fs.createWriteStream(path).end();
}

/**
 * Removes all the files in the temporary directory.
 * @param {String} [tmpPath=popcorn-api/tmp] - The path to remove all the files
 * within (Default is set in the `config/constants.js`).
 * @returns {void}
 */
function _resetTemp(tmpPath = tempDir) {
  const files = fs.readdirSync(tmpPath);
  files.forEach(file => {
    const stats = fs.statSync(path.join(tmpPath, file));
    if (stats.isDirectory()) {
      _resetTemp(file);
    } else if (stats.isFile()) {
      fs.unlinkSync(path.join(tmpPath, file));
    }
  });
}

/**
 * Returns the epoch time.
 * @returns {Number} - Epoch time.
 */
function _now() {
  return Math.floor(new Date().getTime() / 1000);
}

/**
 * Create the temporary directory.
 * @returns {void}
 */
export function createTemp() {
  if (!fs.existsSync(tempDir)) fs.mkdirSync(tempDir);
  if (fs.existsSync(tempDir)) _resetTemp();

  _createEmptyFile(path.join(tempDir, statusFile));
  _createEmptyFile(path.join(tempDir, updatedFile));
}

/**
 * Execute a command from within the root folder.
 * @param {String} cmd - The command to execute.
 * @returns {Promise} - The output of the command.
 */
export function executeCommand(cmd) {
  return new Promise((resolve, reject) => {
    childProcess.exec(cmd, {
      cwd: __dirname
    }, (err, stdout) => {
      if (err) return reject(err);
      return resolve(stdout.split('\n').join(''));
    });
  });
}

/**
 * Export a collection to a JSON file.
 * @param {String} collection - The collection to export.
 * @returns {Promise} - The output of the mongoexport command.
 */
export function exportCollection(collection) {
  const jsonFile = path.join(tempDir, `${collection}s.json`);
  logger.info(`Exporting collection: '${collection}s', to: '${jsonFile}'`);

  const cmd = `mongoexport -d ${dbName} -c ${collection}s -o '${jsonFile}'`;
  return executeCommand(cmd);
}

/**
 * Import a json file to a collection.
 * @param {String} collection - The collection to import.
 * @param {String} jsonFile - The json file to import..
 * @returns {Promise} - The output of the mongoimport command.
 */
export function importCollection(collection, jsonFile) {
  if (!path.isAbsolute(jsonFile))
    jsonFile = path.join(process.cwd(), jsonFile); // eslint-disable-line no-param-reassign
  if (!fs.existsSync(jsonFile))
    throw new Error(`Error: no such file found for '${jsonFile}'`);

  logger.info(`Importing collection: '${collection}', from: '${jsonFile}'`);

  const cmd = `mongoimport -d ${dbName} -c ${collection}s --file '${jsonFile}' --upsert`;
  return executeCommand(cmd);
}

/**
 * Error logger function.
 * @param {String} errorMessage - The error message you want to display.
 * @returns {Error} - A new error with the given error message.
 */
export function onError(errorMessage) {
  logger.error(errorMessage);
  return new Error(errorMessage);
}

/**
 * Reset the default log file.
 * @returns {void}
 */
export function resetLog() {
  const logFile = path.join(tempDir, `${name}.log`);
  if (fs.existsSync(logFile)) fs.unlinkSync(logFile);
}

/**
 * Search for a key in an array of object.
 * @param {String} key - The key to search for.
 * @param {String} value - The value of the key to search for.
 * @return {Object} - The object with the correct key-value pair.
 */
export function search(key, value) {
  return element => element[key] === value;
}

/**
 * Updates the `lastUpdated.json` file.
 * @param {String} [updated=Date.now()] - The epoch time when the API last
 * started scraping.
 * @returns {void}
 */
export function setLastUpdated(updated = _now()) {
  fs.writeFile(path.join(tempDir, updatedFile), JSON.stringify({
    updated
  }), () => {});
}

/**
 * Updates the `status.json` file.
 * @param {String} [status=Idle] - The status which will be set to in the
 * `status.json` file.
 * @returns {void}
 */
export function setStatus(status = 'Idle') {
  fs.writeFile(path.join(tempDir, statusFile), JSON.stringify({
    status
  }), () => {});
}
<|endoftext|>


<commit_before>/*
 * isObject, extend, isFunction, isArguments are taken from undescore/lodash in
 * order to remove the dependency
 */
var isObject = exports.isObject = function(obj) {
    var type = typeof obj;
    return type === 'function' || type === 'object' && !!obj;
};

exports.extend = function(obj) {
    if (!isObject(obj)) {
        return obj;
    }
    var source, prop;
    for (var i = 1, length = arguments.length; i < length; i++) {
        source = arguments[i];
        for (prop in source) {
            if (Object.getOwnPropertyDescriptor && Object.defineProperty) {
                var propertyDescriptor = Object.getOwnPropertyDescriptor(source, prop);
                Object.defineProperty(obj, prop, propertyDescriptor);
            } else {
                obj[prop] = source[prop];
            }
        }
    }
    return obj;
};

exports.isFunction = function(value) {
    return typeof value === 'function';
};

exports.EventEmitter = require('eventemitter3');

exports.nextTick = function(callback) {
    setTimeout(callback, 0);
};

exports.capitalize = function(string){
    return string.charAt(0).toUpperCase()+string.slice(1);
};

exports.callbackName = function(string){
    return "on"+exports.capitalize(string);
};

exports.object = function(keys,vals){
    var o={}, i=0;
    for(;i<keys.length;i++){
        o[keys[i]] = vals[i];
    }
    return o;
};

exports.Promise = require("native-promise-only");

exports.createPromise = function(resolver) {
    return new exports.Promise(resolver);
};

exports.isArguments = function(value) {
    return typeof value === 'object' && ('callee' in value) && typeof value.length === 'number';
};

exports.throwIf = function(val,msg){
    if (val){
        throw Error(msg||val);
    }
};
<commit_msg>fixing syntax bug in utils.js<commit_after>/*
 * isObject, extend, isFunction, isArguments are taken from undescore/lodash in
 * order to remove the dependency
 */
var isObject = exports.isObject = function(obj) {
    var type = typeof obj;
    return type === 'function' || type === 'object' && !!obj;
};

exports.extend = function(obj) {
    if (!isObject(obj)) {
        return obj;
    }
    var source, prop;
    for (var i = 1, length = arguments.length; i < length; i++) {
        source = arguments[i];
        for (prop in source) {
            if (Object.getOwnPropertyDescriptor && Object.defineProperty) {
                var propertyDescriptor = Object.getOwnPropertyDescriptor(source, prop);
                Object.defineProperty(obj, prop, propertyDescriptor);
            } else {
                obj[prop] = source[prop];
            }
        }
    }
    return obj;
};

exports.isFunction = function(value) {
    return typeof value === 'function';
};

exports.EventEmitter = require('eventemitter3');

exports.nextTick = function(callback) {
    setTimeout(callback, 0);
};

exports.capitalize = function(string){
    return string.charAt(0).toUpperCase()+string.slice(1);
};

exports.callbackName = function(string){
    return "on"+exports.capitalize(string);
};

exports.object = function(keys,vals){
    var o={}, i=0;
    for(;i < keys.length; i++){
        o[keys[i]] = vals[i];
    }
    return o;
};

exports.Promise = require("native-promise-only");

exports.createPromise = function(resolver) {
    return new exports.Promise(resolver);
};

exports.isArguments = function(value) {
    return typeof value === 'object' && ('callee' in value) && typeof value.length === 'number';
};

exports.throwIf = function(val,msg){
    if (val){
        throw Error(msg||val);
    }
};
<|endoftext|>


<commit_before>'use strict';

require('console-polyfill');

/*
 * Utily functions for Booking.js
 */

module.exports = {

  isFunction: function(object) {
   return !!(object && object.constructor && object.call && object.apply);
  },

  doCallback: function(hook, config, arg, deprecated) {
    if(this.isFunction(config.callbacks[hook])) {
      config.callbacks[hook](arg);
      if (deprecated) this.logDeprecated(hook + ' callback has been replaced, please see docs')
    }
  },

  logError: function(message) {
    console.error('TimekitBooking Error: ' + message);
  },

  logDeprecated: function(message) {
    console.warn('TimekitBooking Deprecated: ' + message);
  }

};
<commit_msg>style<commit_after>'use strict';

require('console-polyfill');

/*
 * Utily functions for Booking.js
 */

module.exports = {

  isFunction: function(object) {
   return !!(object && object.constructor && object.call && object.apply);
  },

  doCallback: function(hook, config, arg, deprecated) {
    if(this.isFunction(config.callbacks[hook])) {
      config.callbacks[hook](arg);
      if (deprecated) { this.logDeprecated(hook + ' callback has been replaced, please see docs'); }
    }
  },

  logError: function(message) {
    console.error('TimekitBooking Error: ' + message);
  },

  logDeprecated: function(message) {
    console.warn('TimekitBooking Deprecated: ' + message);
  }

};
<|endoftext|>


<commit_before>export function trace(clientId: ?string, title: string, value: Object | string) {
  let prefix = '';
  if (window.performance) {
    prefix = '[' + (window.performance.now() / 1000).toFixed(3) + ']';
  }
  if (clientId) {
    prefix = prefix + '[' + clientId + ']';
  }

  if (isEdge()) {
    console.log(prefix + ' ' + title + '\n', value); // eslint-disable-line
  }
  else {
    console.info(prefix + ' ' + title + '\n', value); // eslint-disable-line
  }
}

function userAgent() {
  return window.navigator.userAgent.toLocaleLowerCase();
}

function isPlanB() {
  if (userAgent().indexOf('chrome') !== -1 || userAgent().indexOf('safari') !== -1) {
    return true;
  } else {
    return false;
  }
}

export function isEdge() {
  return userAgent().indexOf('edge') !== -1;
}

export function isSafari() {
  return userAgent().indexOf('safari') !== -1;
}

export function createSignalingMessage(offerSDP, role, channelId, metadata, options) {
  const message = {
    type: 'connect',
    role: role,
    channel_id: channelId,
    metadata: metadata,
    sdp: offerSDP,
    userAgent: window.navigator.userAgent,
    audio: true,
    video: true
  };
  Object.keys(message).forEach(key => {
    if (message[key] === undefined) {
      message[key] = null;
    }
  });
  // multistream
  if ('multistream' in options && options.multistream === true) {
    message.multistream = true;
    message.plan_b = isPlanB();
  }
  // vad
  if ('vad' in options) {
    message.vad = options.vad;
  }
  // parse options
  const audioPropertyKeys = ['audioCodecType', 'audioBitRate'];
  const videoPropertyKeys = ['videoCodecType', 'videoBitRate', 'videoSnapshot'];
  const copyOptions = Object.assign({}, options);
  Object.keys(copyOptions).forEach(key => {
    if (key === 'audio' && typeof copyOptions[key] === 'boolean') return;
    if (key === 'video' && typeof copyOptions[key] === 'boolean') return;
    if (0 <= audioPropertyKeys.indexOf(key) && copyOptions[key] !== null) return;
    if (0 <= videoPropertyKeys.indexOf(key) && copyOptions[key] !== null) return;
    delete copyOptions[key];
  });

  if ('audio' in copyOptions) {
    message.audio = copyOptions.audio;
  }
  const hasAudioProperty = Object.keys(copyOptions).some(key => {
    return 0 <= audioPropertyKeys.indexOf(key);
  });
  if (message.audio && hasAudioProperty) {
    message.audio = {};
    if ('audioCodecType' in copyOptions) {
      message.audio['codec_type'] = copyOptions.audioCodecType;
    }
    if ('audioBitRate' in copyOptions) {
      message.audio['bit_rate'] = copyOptions.audioBitRate;
    }
  }

  if ('video' in copyOptions) {
    message.video = copyOptions.video;
  }
  const hasVideoProperty = Object.keys(copyOptions).some(key => {
    return 0 <= videoPropertyKeys.indexOf(key);
  });
  if (message.video && hasVideoProperty) {
    message.video = {};
    if ('videoCodecType' in copyOptions) {
      message.video['codec_type'] = copyOptions.videoCodecType;
    }
    if ('videoBitRate' in copyOptions) {
      message.video['bit_rate'] = copyOptions.videoBitRate;
    }
    if ('videoSnapshot' in copyOptions) {
      message.video['snapshot'] = copyOptions.videoSnapshot;
    }
  }

  return message;
}
<commit_msg>userAgent でのブラウザ判定の仕組みを修正する<commit_after>export function trace(clientId: ?string, title: string, value: Object | string) {
  let prefix = '';
  if (window.performance) {
    prefix = '[' + (window.performance.now() / 1000).toFixed(3) + ']';
  }
  if (clientId) {
    prefix = prefix + '[' + clientId + ']';
  }

  if (isEdge()) {
    console.log(prefix + ' ' + title + '\n', value); // eslint-disable-line
  }
  else {
    console.info(prefix + ' ' + title + '\n', value); // eslint-disable-line
  }
}

function browser() {
  const ua = window.navigator.userAgent.toLocaleLowerCase();
  if (ua.indexOf('chrome') !== -1) {
    return 'chrome';
  }
  else if (ua.indexOf('edge') !== -1) {
    return 'edge';
  }
  else if (ua.indexOf('firefox') !== -1) {
    return 'firefox';
  }
  else if (ua.indexOf('safari') !== -1) {
    return 'safari';
  }
  return ;
}

function isPlanB() {
  return browser() === 'chrome' || browser() === 'safari';
}

export function isEdge() {
  return browser() === 'edge';
}

export function isSafari() {
  return browser() === 'safari';
}

export function createSignalingMessage(offerSDP, role, channelId, metadata, options) {
  const message = {
    type: 'connect',
    role: role,
    channel_id: channelId,
    metadata: metadata,
    sdp: offerSDP,
    userAgent: window.navigator.userAgent,
    audio: true,
    video: true
  };
  Object.keys(message).forEach(key => {
    if (message[key] === undefined) {
      message[key] = null;
    }
  });
  // multistream
  if ('multistream' in options && options.multistream === true) {
    message.multistream = true;
    message.plan_b = isPlanB();
  }
  // vad
  if ('vad' in options) {
    message.vad = options.vad;
  }
  // parse options
  const audioPropertyKeys = ['audioCodecType', 'audioBitRate'];
  const videoPropertyKeys = ['videoCodecType', 'videoBitRate', 'videoSnapshot'];
  const copyOptions = Object.assign({}, options);
  Object.keys(copyOptions).forEach(key => {
    if (key === 'audio' && typeof copyOptions[key] === 'boolean') return;
    if (key === 'video' && typeof copyOptions[key] === 'boolean') return;
    if (0 <= audioPropertyKeys.indexOf(key) && copyOptions[key] !== null) return;
    if (0 <= videoPropertyKeys.indexOf(key) && copyOptions[key] !== null) return;
    delete copyOptions[key];
  });

  if ('audio' in copyOptions) {
    message.audio = copyOptions.audio;
  }
  const hasAudioProperty = Object.keys(copyOptions).some(key => {
    return 0 <= audioPropertyKeys.indexOf(key);
  });
  if (message.audio && hasAudioProperty) {
    message.audio = {};
    if ('audioCodecType' in copyOptions) {
      message.audio['codec_type'] = copyOptions.audioCodecType;
    }
    if ('audioBitRate' in copyOptions) {
      message.audio['bit_rate'] = copyOptions.audioBitRate;
    }
  }

  if ('video' in copyOptions) {
    message.video = copyOptions.video;
  }
  const hasVideoProperty = Object.keys(copyOptions).some(key => {
    return 0 <= videoPropertyKeys.indexOf(key);
  });
  if (message.video && hasVideoProperty) {
    message.video = {};
    if ('videoCodecType' in copyOptions) {
      message.video['codec_type'] = copyOptions.videoCodecType;
    }
    if ('videoBitRate' in copyOptions) {
      message.video['bit_rate'] = copyOptions.videoBitRate;
    }
    if ('videoSnapshot' in copyOptions) {
      message.video['snapshot'] = copyOptions.videoSnapshot;
    }
  }

  return message;
}
<|endoftext|>


<commit_before>//this is mainly so the name shows up when you look at the object in the console
var Communist = function(){};
//regex out the importScript call and move it up to the top out of the function.
function moveImports(string){
	var script;
	var match = string.match(/(importScripts\(.*\);)/);
	if(match){
		script = match[0].replace(/importScripts\((.*\.js\')\);?/,function(a,b){if(b){return "importScripts("+b.split(",").map(function(cc){return "'"+c.makeUrl(cc.slice(1,-1))+"'"})+");\n";}else{return "";}})+string.replace(/(importScripts\(.*\.js\'\);?)/,"\n");
	}else{
		script = string;
	}
	return script;
}

function getPath(){
	var scripts = document.getElementsByTagName("script");
		var len = scripts.length;
		var i = 0;
		while(i<len){
			if(/communist(\.min)?\.js/.test(scripts[i].src)){
			   return scripts[i].src;
			}
			i++;
		}
}
//accepts an array of strings, joins them, and turns them into a worker.
function makeWorker(strings){
	var worker;
	var script =moveImports(strings.join(""));
	c.URL = c.URL||window.URL || window.webkitURL;
	try{
		worker= new Worker(c.URL.createObjectURL(new Blob([script],{type: "text/javascript"})));
	}catch(e){
		c._noTransferable=true;
		worker = new Worker(getPath());
		worker.postMessage(script);
	}finally{
		return worker;
	}
}<commit_msg>added check<commit_after>//this is mainly so the name shows up when you look at the object in the console
var Communist = function(){};
//regex out the importScript call and move it up to the top out of the function.
function moveImports(string){
	var script;
	var match = string.match(/(importScripts\(.*\);)/);
	if(match){
		script = match[0].replace(/importScripts\((.*\.js\')\);?/,function(a,b){if(b){return "importScripts("+b.split(",").map(function(cc){return "'"+c.makeUrl(cc.slice(1,-1))+"'"})+");\n";}else{return "";}})+string.replace(/(importScripts\(.*\.js\'\);?)/,"\n");
	}else{
		script = string;
	}
	return script;
}

function getPath(){
	if(typeof SHIM_WORKER_PATH !== "undefined"){
		return SHIM_WORKER_PATH;
	}
	var scripts = document.getElementsByTagName("script");
		var len = scripts.length;
		var i = 0;
		while(i<len){
			if(/communist(\.min)?\.js/.test(scripts[i].src)){
			   return scripts[i].src;
			}
			i++;
		}
}
//accepts an array of strings, joins them, and turns them into a worker.
function makeWorker(strings){
	var worker;
	var script =moveImports(strings.join(""));
	c.URL = c.URL||window.URL || window.webkitURL;
	try{
		worker= new Worker(c.URL.createObjectURL(new Blob([script],{type: "text/javascript"})));
	}catch(e){
		c._noTransferable=true;
		worker = new Worker(getPath());
		worker.postMessage(script);
	}finally{
		return worker;
	}
}<|endoftext|>


<commit_before>import base64url from 'base64url';
import forEach from 'lodash.foreach';
import has from 'lodash.has';
import multiKeyHash from 'multikey-hash';
import {promisify, method} from 'bluebird';
import {randomBytes} from 'crypto';

randomBytes = promisify(randomBytes);

//====================================================================

// Generate a secure random Base64 string.
let generateToken = (n = 32) => randomBytes(n).then(base64url);
export {generateToken};

// Special value which can be returned to stop an iteration in map()
// and mapInPlace().
let done = {};
export {done};

// Similar to `lodash.map()` for array and `lodash.mapValues()` for
// objects.
//
// Note: can  be interrupted by returning the special value `done`
// provided as the forth argument.
function map(col, iterator, thisArg = this) {
	let result = has(col, 'length') ? [] : {};
	forEach(col, (item, i) => {
		let value = iterator.call(thisArg, item, i, done);
		if (value === done) {
			return false;
		}

		result[i] = value;
	});
	return result;
}
export {map};

// Create a hash from multiple values.
export var multiKeyHash = (function (multiKeyHash) {
  return method((...args) => {
    let hash = multiKeyHash(...args);

    let buf = new Buffer(4);
    buf.writeUInt32LE(hash, 0);

    return base64url(buf);
  });
})(multiKeyHash);

// Similar to `map()` but change the current collection.
//
// Note: can  be interrupted by returning the special value `done`
// provided as the forth argument.
function mapInPlace(col, iterator, thisArg = this) {
	forEach(col, (item, i) => {
		let value = iterator.call(thisArg, item, i, done);
		if (value === done) {
			return false;
		}

		col[i] = value;
	});

	return col;
}
export {mapInPlace};

// Wrap a value in a function.
let wrap = (value) => () => value;
export {wrap};
<commit_msg>Minor fix.<commit_after>import base64url from 'base64url';
import forEach from 'lodash.foreach';
import has from 'lodash.has';
import multiKeyHash from 'multikey-hash';
import {promisify, method} from 'bluebird';
import {randomBytes} from 'crypto';

randomBytes = promisify(randomBytes);

//====================================================================

// Generate a secure random Base64 string.
let generateToken = (n = 32) => randomBytes(n).then(base64url);
export {generateToken};

// Special value which can be returned to stop an iteration in map()
// and mapInPlace().
let done = {};
export {done};

// Similar to `lodash.map()` for array and `lodash.mapValues()` for
// objects.
//
// Note: can  be interrupted by returning the special value `done`
// provided as the forth argument.
function map(col, iterator, thisArg = this) {
	let result = has(col, 'length') ? [] : {};
	forEach(col, (item, i) => {
		let value = iterator.call(thisArg, item, i, done);
		if (value === done) {
			return false;
		}

		result[i] = value;
	});
	return result;
}
export {map};

// Create a hash from multiple values.
multiKeyHash = (function (multiKeyHash) {
  return method((...args) => {
    let hash = multiKeyHash(...args);

    let buf = new Buffer(4);
    buf.writeUInt32LE(hash, 0);

    return base64url(buf);
  });
})(multiKeyHash);
export {multiKeyHash};

// Similar to `map()` but change the current collection.
//
// Note: can  be interrupted by returning the special value `done`
// provided as the forth argument.
function mapInPlace(col, iterator, thisArg = this) {
	forEach(col, (item, i) => {
		let value = iterator.call(thisArg, item, i, done);
		if (value === done) {
			return false;
		}

		col[i] = value;
	});

	return col;
}
export {mapInPlace};

// Wrap a value in a function.
let wrap = (value) => () => value;
export {wrap};
<|endoftext|>