<commit_before>var _ = this._ || require("underscore");

var cull = (function () {
    "use strict";

    var slice = Array.prototype.slice;
    var toString = Object.prototype.toString;

    function _assert(pred, msg) {
        if (!pred) { throw new TypeError(msg); }
    }

    function _refute(pred, msg) {
        _assert(!pred, msg);
    }

    function isSeq(seq) {
        return !!seq &&
            typeof seq === "object" &&
            typeof seq.length === "number";
    }

    function toSeq(value) {
        if (toString.call(value) === "[object Array]") { return value; }
        if (isSeq(value)) { return slice.call(value); }
        if (typeof value === "undefined" || value === null) { return []; }
        return slice.call(arguments);
    }

    function doall(fn, seq) {
        var i, l;
        for (i = 0, l = seq.length; i < l; ++i) {
            fn(seq[i], i, seq);
        }
    }

    function isFunction(fn) {
        return typeof fn === "function";
    }

    function reduce(fn, initial, seq) {
        if (arguments.length === 3) {
            return _.reduce(seq, fn, initial);
        }
        return _.reduce(initial, fn);
    }

    function all(fn, seq) {
        var i, l;
        for (i = 0, l = seq.length; i < l; ++i) {
            if (!fn(seq[i])) { return false; }
        }
        return true;
    }

    function some(fn, seq) {
        var i, l;
        for (i = 0, l = seq.length; i < l; ++i) {
            if (fn(seq[i])) { return true; }
        }
        return false;
    }

    function trim(string) {
        return (string.trim && string.trim()) ||
            string.replace(/^\s+|\s+$/, "");
    }

    function identity(arg) { return arg; }

    function defined(o) { return typeof o !== "undefined" && o !== null; }

    cull = {
        _assert: _assert,
        _refute: _refute,
        trim: trim,
        doall: doall,
        reduce: reduce,
        all: all,
        some: some,
        isFunction: isFunction,
        isSeq: isSeq,
        toSeq: toSeq,
        identity: identity,
        defined: defined
    };

    function unary(fn) {
        return function (arg) {
            return fn.call(this, arg);
        };
    }

    function prop(name) {
        return function (object) {
            return object[name];
        };
    }

    function func(name, args) {
        return function (object) {
            return object[name].apply(object, toSeq(args));
        };
    }

    function eq(one) {
        return function (other) { return one === other; };
    }

    var composeSignature = "compose takes func|[funcs] " +
            "and optional thisp object";

    function compose(fns, thisp) {
        _refute(isFunction(thisp) || arguments.length > 2, composeSignature);

        fns = toSeq(fns);

        _assert(all(isFunction, fns), composeSignature);

        return function () {
            var i = fns.length;
            var result = arguments;
            while (i--) {
                result = [fns[i].apply(thisp || this, result)];
            }
            return result[0];
        };
    }

    function partial(fn) {
        var args = slice.call(arguments, 1);
        return function () {
            return fn.apply(this, args.concat(slice.call(arguments)));
        };
    }

    function bind(obj, methOrProp) {
        var fn = typeof methOrProp === "string" ? obj[methOrProp] : methOrProp;
        var args = slice.call(arguments, 2);
        return function () {
            return fn.apply(obj, args.concat(slice.call(arguments)));
        };
    }

    function handler(handlr, type) {
        if (typeof handlr === "function") { return handlr; }
        _assert(isFunction(handlr[type]),
                "Handler has no method '" + type + "'");
        return bind.apply(null, arguments);
    }

    _.extend(cull, {
        unary: unary,
        prop: prop,
        func: func,
        eq: eq,
        compose: compose,
        partial: partial,
        bind: bind,
        handler: handler,
        keys: _.keys,
        values: _.values
    });

    var switchArgs = function (obj, name) {
        return function (fn, seq) {
            var args = [seq, fn].concat(slice.call(arguments, 2));
            return obj[name].apply(obj, args);
        };
    };

    var flatten = _.flatten;
    var uniq = _.uniq;
    var select = switchArgs(_, "filter");
    var indexOf = switchArgs(_, "indexOf");
    var seldef = partial(select, defined);

    function map(fn, coll) {
        var result = [], i, l;
        for (i = 0, l = coll.length; i < l; i++) {
            result.push(fn(coll[i]));
        }
        return result;
    }

    function negate(pred) {
        return function () {
            return !pred.apply(this, arguments);
        };
    }

    function reject(pred, seq) {
        return select(negate(pred), seq);
    }

    function concat(seq, items) {
        return toSeq(seq).concat(toSeq(items));
    }

    function partition(n, seq) {
        var result = [], i, l;
        for (i = 0, l = seq.length; i < l; i += n) {
            result.push(seq.slice(i, i + n));
        }
        return result;
    }

    function mapdef(fn, coll) {
        return seldef(map(fn, seldef(coll)));
    }

    function mapcat(fn, coll) {
        return reduce(concat, map(fn, coll));
    }

    _.extend(cull, {
        concat: concat,
        flatten: flatten,
        uniq: uniq,
        select: select,
        reject: reject,
        seldef: seldef,
        map: map,
        doall: doall,
        mapdef: mapdef,
        mapcat: mapcat,
        partition: partition,
        indexOf: indexOf
    });

    // cull.dom

    (function () {
        function children(elements) {
            if (isSeq(elements)) { return flatten(map(children, elements)); }
            var results = [], child = elements.firstChild;
            while (child) {
                if (child.nodeType === 1) { results.push(child); }
                child = child.nextSibling;
            }
            return results;
        }

        function id(idStr) {
            return document.getElementById(idStr);
        }

        function get(selectors, parent) {
            if (!parent) { parent = document; }
            var select = function (seq, selector) {
                if (/^#/.test(selector)) {
                    return seq.concat(id(selector.slice(1)));
                } else if (/^\./.test(selector)) {
                    var cn = selector.slice(1);
                    return concat(seq, parent.getElementsByClassName(cn));
                } else {
                    return concat(seq, parent.getElementsByTagName(selector));
                }
            };
            return uniq(reduce(select, [], cull.toSeq(selectors)));
        }

        function remove(element) {
            element.parentNode.removeChild(element);
        }

        function hasClassName(className, element) {
            var regexp = new RegExp("(^|\\s)" + className + "(\\s|$)");
            return regexp.test(element.className);
        }

        function addClassName(cn, element) {
            if (isSeq(element)) {
                return doall(partial(addClassName, cn), element);
            }
            if (hasClassName(cn, element)) { return; }
            element.className = trim(element.className + " " + cn);
        }

        function removeClassName(cn, element) {
            if (isSeq(element)) {
                return doall(partial(removeClassName, cn), element);
            }
            if (!hasClassName(cn, element)) { return; }
            var regexp = new RegExp("(^|\\s)" + cn + "(\\s|$)");
            element.className = trim(element.className.replace(regexp, " "));
        }

        function frag(items) {
            var fragment = document.createDocumentFragment();
            doall(bind(fragment, "appendChild"), toSeq(items));
            return fragment;
        }

        cull.dom = {
            children: children,
            id: id,
            get: get,
            remove: remove,
            frag: frag,
            cn: {
                add: addClassName,
                rm: removeClassName
            }
        };

        var el;

        var isContent = function (content) {
            return content !== null && typeof content !== "undefined" &&
                (typeof content.nodeType !== "undefined" ||
                 typeof content === "string" ||
                 cull.isSeq(content));
        };

        function setProp(properties, element) {
            var name, mapper;
            properties = properties || {};

            for (name in properties) {
                if (properties.hasOwnProperty(name)) {
                    mapper = el.propmap[name];
                    if (mapper) {
                        mapper(element, properties[name]);
                    } else {
                        element[name] = properties[name];
                    }
                }
            }
        }

        function append(content, element) {
            _assert(isContent(content),
                    "Content should be one or a list of [string, DOM element]");
            content = cull.toSeq(content);
            var i, l;
            for (i = 0, l = content.length; i < l; ++i) {
                if (typeof content[i] === "string") {
                    element.appendChild(document.createTextNode(content[i]));
                } else {
                    element.appendChild(content[i]);
                }
            }
        }

        function content(children, element) {
            _assert(element && typeof element.innerHTML !== "undefined",
                    "content() needs element");
            element.innerHTML = "";
            append(children, element);
        }

        el = function (tagName, attrProps, content) {
            _refute(arguments.length > 3,
                    "Content should be one or a list of [string, DOM element]");
            if (!content && isContent(attrProps)) {
                content = attrProps;
                attrProps = {};
            }
            _refute(attrProps && attrProps.tagName,
                    "Cannot set attribute property tagName. Use a list when " +
                    "adding multiple content elements.");
            var element = document.createElement(tagName);
            setProp(attrProps, element);
            append(content || [], element);
            return element;
        };

        el.toString = function () {
            return "cull.dom.el()";
        };

        el.propmap = {
            style: function (el, styles) {
                var property;
                for (property in styles) {
                    if (styles.hasOwnProperty(property)) {
                        el.style[property] = styles[property];
                    }
                }
            }
        };

        doall(function (tagName) { el[tagName] = partial(el, tagName); }, [
            "a", "br", "div", "fieldset", "form", "h2", "h3", "h4", "h5",
            "img", "input", "label", "li", "p", "span", "strong", "textarea",
            "ul", "span", "select", "option", "ol", "iframe"
        ]);

        el.setProp = setProp;
        el.append = append;
        el.content = content;
        cull.dom.el = el;

    }());

    return cull;
}());

if (typeof require === "function" && typeof module !== "undefined") {
    module.exports = cull;
}
<commit_msg>Move _ declaration into closure to avoid IE issues.<commit_after>var cull = (function () {
    "use strict";

    var _ = this._ || require("underscore");
    var slice = Array.prototype.slice;
    var toString = Object.prototype.toString;

    function _assert(pred, msg) {
        if (!pred) { throw new TypeError(msg); }
    }

    function _refute(pred, msg) {
        _assert(!pred, msg);
    }

    function isSeq(seq) {
        return !!seq &&
            typeof seq === "object" &&
            typeof seq.length === "number";
    }

    function toSeq(value) {
        if (toString.call(value) === "[object Array]") { return value; }
        if (isSeq(value)) { return slice.call(value); }
        if (typeof value === "undefined" || value === null) { return []; }
        return slice.call(arguments);
    }

    function doall(fn, seq) {
        var i, l;
        for (i = 0, l = seq.length; i < l; ++i) {
            fn(seq[i], i, seq);
        }
    }

    function isFunction(fn) {
        return typeof fn === "function";
    }

    function reduce(fn, initial, seq) {
        if (arguments.length === 3) {
            return _.reduce(seq, fn, initial);
        }
        return _.reduce(initial, fn);
    }

    function all(fn, seq) {
        var i, l;
        for (i = 0, l = seq.length; i < l; ++i) {
            if (!fn(seq[i])) { return false; }
        }
        return true;
    }

    function some(fn, seq) {
        var i, l;
        for (i = 0, l = seq.length; i < l; ++i) {
            if (fn(seq[i])) { return true; }
        }
        return false;
    }

    function trim(string) {
        return (string.trim && string.trim()) ||
            string.replace(/^\s+|\s+$/, "");
    }

    function identity(arg) { return arg; }

    function defined(o) { return typeof o !== "undefined" && o !== null; }

    cull = {
        _assert: _assert,
        _refute: _refute,
        trim: trim,
        doall: doall,
        reduce: reduce,
        all: all,
        some: some,
        isFunction: isFunction,
        isSeq: isSeq,
        toSeq: toSeq,
        identity: identity,
        defined: defined
    };

    function unary(fn) {
        return function (arg) {
            return fn.call(this, arg);
        };
    }

    function prop(name) {
        return function (object) {
            return object[name];
        };
    }

    function func(name, args) {
        return function (object) {
            return object[name].apply(object, toSeq(args));
        };
    }

    function eq(one) {
        return function (other) { return one === other; };
    }

    var composeSignature = "compose takes func|[funcs] " +
            "and optional thisp object";

    function compose(fns, thisp) {
        _refute(isFunction(thisp) || arguments.length > 2, composeSignature);

        fns = toSeq(fns);

        _assert(all(isFunction, fns), composeSignature);

        return function () {
            var i = fns.length;
            var result = arguments;
            while (i--) {
                result = [fns[i].apply(thisp || this, result)];
            }
            return result[0];
        };
    }

    function partial(fn) {
        var args = slice.call(arguments, 1);
        return function () {
            return fn.apply(this, args.concat(slice.call(arguments)));
        };
    }

    function bind(obj, methOrProp) {
        var fn = typeof methOrProp === "string" ? obj[methOrProp] : methOrProp;
        var args = slice.call(arguments, 2);
        return function () {
            return fn.apply(obj, args.concat(slice.call(arguments)));
        };
    }

    function handler(handlr, type) {
        if (typeof handlr === "function") { return handlr; }
        _assert(isFunction(handlr[type]),
                "Handler has no method '" + type + "'");
        return bind.apply(null, arguments);
    }

    _.extend(cull, {
        unary: unary,
        prop: prop,
        func: func,
        eq: eq,
        compose: compose,
        partial: partial,
        bind: bind,
        handler: handler,
        keys: _.keys,
        values: _.values
    });

    var switchArgs = function (obj, name) {
        return function (fn, seq) {
            var args = [seq, fn].concat(slice.call(arguments, 2));
            return obj[name].apply(obj, args);
        };
    };

    var flatten = _.flatten;
    var uniq = _.uniq;
    var select = switchArgs(_, "filter");
    var indexOf = switchArgs(_, "indexOf");
    var seldef = partial(select, defined);

    function map(fn, coll) {
        var result = [], i, l;
        for (i = 0, l = coll.length; i < l; i++) {
            result.push(fn(coll[i]));
        }
        return result;
    }

    function negate(pred) {
        return function () {
            return !pred.apply(this, arguments);
        };
    }

    function reject(pred, seq) {
        return select(negate(pred), seq);
    }

    function concat(seq, items) {
        return toSeq(seq).concat(toSeq(items));
    }

    function partition(n, seq) {
        var result = [], i, l;
        for (i = 0, l = seq.length; i < l; i += n) {
            result.push(seq.slice(i, i + n));
        }
        return result;
    }

    function mapdef(fn, coll) {
        return seldef(map(fn, seldef(coll)));
    }

    function mapcat(fn, coll) {
        return reduce(concat, map(fn, coll));
    }

    _.extend(cull, {
        concat: concat,
        flatten: flatten,
        uniq: uniq,
        select: select,
        reject: reject,
        seldef: seldef,
        map: map,
        doall: doall,
        mapdef: mapdef,
        mapcat: mapcat,
        partition: partition,
        indexOf: indexOf
    });

    // cull.dom

    (function () {
        function children(elements) {
            if (isSeq(elements)) { return flatten(map(children, elements)); }
            var results = [], child = elements.firstChild;
            while (child) {
                if (child.nodeType === 1) { results.push(child); }
                child = child.nextSibling;
            }
            return results;
        }

        function id(idStr) {
            return document.getElementById(idStr);
        }

        function get(selectors, parent) {
            if (!parent) { parent = document; }
            var select = function (seq, selector) {
                if (/^#/.test(selector)) {
                    return seq.concat(id(selector.slice(1)));
                } else if (/^\./.test(selector)) {
                    var cn = selector.slice(1);
                    return concat(seq, parent.getElementsByClassName(cn));
                } else {
                    return concat(seq, parent.getElementsByTagName(selector));
                }
            };
            return uniq(reduce(select, [], cull.toSeq(selectors)));
        }

        function remove(element) {
            element.parentNode.removeChild(element);
        }

        function hasClassName(className, element) {
            var regexp = new RegExp("(^|\\s)" + className + "(\\s|$)");
            return regexp.test(element.className);
        }

        function addClassName(cn, element) {
            if (isSeq(element)) {
                return doall(partial(addClassName, cn), element);
            }
            if (hasClassName(cn, element)) { return; }
            element.className = trim(element.className + " " + cn);
        }

        function removeClassName(cn, element) {
            if (isSeq(element)) {
                return doall(partial(removeClassName, cn), element);
            }
            if (!hasClassName(cn, element)) { return; }
            var regexp = new RegExp("(^|\\s)" + cn + "(\\s|$)");
            element.className = trim(element.className.replace(regexp, " "));
        }

        function frag(items) {
            var fragment = document.createDocumentFragment();
            doall(bind(fragment, "appendChild"), toSeq(items));
            return fragment;
        }

        cull.dom = {
            children: children,
            id: id,
            get: get,
            remove: remove,
            frag: frag,
            cn: {
                add: addClassName,
                rm: removeClassName
            }
        };

        var el;

        var isContent = function (content) {
            return content !== null && typeof content !== "undefined" &&
                (typeof content.nodeType !== "undefined" ||
                 typeof content === "string" ||
                 cull.isSeq(content));
        };

        function setProp(properties, element) {
            var name, mapper;
            properties = properties || {};

            for (name in properties) {
                if (properties.hasOwnProperty(name)) {
                    mapper = el.propmap[name];
                    if (mapper) {
                        mapper(element, properties[name]);
                    } else {
                        element[name] = properties[name];
                    }
                }
            }
        }

        function append(content, element) {
            _assert(isContent(content),
                    "Content should be one or a list of [string, DOM element]");
            content = cull.toSeq(content);
            var i, l;
            for (i = 0, l = content.length; i < l; ++i) {
                if (typeof content[i] === "string") {
                    element.appendChild(document.createTextNode(content[i]));
                } else {
                    element.appendChild(content[i]);
                }
            }
        }

        function content(children, element) {
            _assert(element && typeof element.innerHTML !== "undefined",
                    "content() needs element");
            element.innerHTML = "";
            append(children, element);
        }

        el = function (tagName, attrProps, content) {
            _refute(arguments.length > 3,
                    "Content should be one or a list of [string, DOM element]");
            if (!content && isContent(attrProps)) {
                content = attrProps;
                attrProps = {};
            }
            _refute(attrProps && attrProps.tagName,
                    "Cannot set attribute property tagName. Use a list when " +
                    "adding multiple content elements.");
            var element = document.createElement(tagName);
            setProp(attrProps, element);
            append(content || [], element);
            return element;
        };

        el.toString = function () {
            return "cull.dom.el()";
        };

        el.propmap = {
            style: function (el, styles) {
                var property;
                for (property in styles) {
                    if (styles.hasOwnProperty(property)) {
                        el.style[property] = styles[property];
                    }
                }
            }
        };

        doall(function (tagName) { el[tagName] = partial(el, tagName); }, [
            "a", "br", "div", "fieldset", "form", "h2", "h3", "h4", "h5",
            "img", "input", "label", "li", "p", "span", "strong", "textarea",
            "ul", "span", "select", "option", "ol", "iframe"
        ]);

        el.setProp = setProp;
        el.append = append;
        el.content = content;
        cull.dom.el = el;

    }());

    return cull;
}());

if (typeof require === "function" && typeof module !== "undefined") {
    module.exports = cull;
}
<|endoftext|>


<commit_before>'use strict';

import mapboxgl from 'mapbox-gl';

import Line from './geometries/line';
import Point from './geometries/point';
import Polygon from './geometries/polygon';

export default class API extends mapboxgl.Control {

  constructor() {
    super();
  }

  /**
   * add a feature
   * @param {Object} feature - GeoJSON feature
   * @param {Object} [options]
   * @pata {Boolean} [options.permanent=false] - disable selection for feature
   * @returns {Number} draw id of the set feature or an array of
   * draw ids if a feature collection was added
   */
  add(feature, options) {
    feature = JSON.parse(JSON.stringify(feature));
    if (feature.type === 'FeatureCollection') {
      return feature.features.map(subFeature => this.set(subFeature, options));
    }

    if (!feature.geometry) {
      feature = {
        type: 'Feature',
        id: feature.id,
        geometry: feature
      };
    }

    if (!options) {
      options = {};
    }
    options.map = this._map;
    options.data = feature;

    var internalFeature;
    switch (feature.geometry.type) {
      case 'Point':
        internalFeature = new Point(options);
        break;
      case 'LineString':
        internalFeature = new Line(options);
        break;
      case 'Polygon':
        internalFeature = new Polygon(options);
        break;
      default:
        throw new Error('MapboxGL Draw: Unsupported geometry type "' + feature.geometry.type + '"');
    }

    this._store.set(internalFeature);
    if (this.options.interactive) {
      this._select(internalFeature.drawId);
    }

    return internalFeature.drawId;
  }

  /**
   * Updates an existing feature
   * @param {String} id - the drawId of the feature to update
   * @param {Object} feature - a GeoJSON feature
   * @returns {Draw} this
   */
  update(id, feature) {
    feature = JSON.parse(JSON.stringify(feature));
    var newFeatureType = feature.type === 'Feature' ? feature.geometry.type : feature.type;
    var _feature = this._store.get(id);
    if (_feature.geojson.geometry.type !== newFeatureType || _feature.getType() === 'square') {
      throw 'Can not update feature to different type and can not update squares';
    }
    _feature.setCoordinates(feature.coordinates || feature.geometry.coordinates);
    if (feature.properties) _feature.setProperties(feature.properties);
    return this;
  }

  /**
   * get a geometry by its draw id
   * @param {String} id - the draw id of the geometry
   */
  get(id) {
    var feature = this._store.get(id);
    return feature && feature.toGeoJSON();
  }

  /**
   * get all draw geometries
   * @returns {Object} a GeoJSON feature collection
   */
  getAll() {
    return this._store.getAllIds()
      .map(id => this._store.get(id).toGeoJSON())
      .reduce(function(featureCollection, feature) {
        featureCollection.features.push(feature);
        return featureCollection;
      }, {
        type: 'FeatureCollection',
        features: []
      });
  }

  /**
   * select a feature
   * @param {String} id - the drawId of the feature
   * @returns {Draw} this
   */
  select(id) {
    this._store.select(id);
    return this;
  }

  /**
   * select all features
   * @param {String} id - the drawId of the feature
   * @returns {Draw} this
   */
  selectAll() {
    this._store.getAllIds()
      .forEach(id => this.select(id));
    return this;
  }

  /**
   * deselect a feature
   * @param {String} id - the drawId of the feature
   * @returns {Draw} this
   */
  deselect(id) {
    this._store.commit(id);
    return this;
  }

  /**
   * deselect all features
   * @param {String} id - the drawId of the feature
   * @returns {Draw} this
   */
  deselectAll() {
    this._store.getSelectedIds()
      .forEach(id => this.commit(id));
    return this;
  }

  /**
   * get selected feature collection of features
   * @return {Object} a feature collection of geometries that are selected
   */
  getSelected() {
    return this._store.getSelectedIds()
      .map(id => this._store.get(id).toGeoJSON())
      .reduce(function(featureCollection, feature) {
        featureCollection.features.push(feature);
        return featureCollection;
      }, {
        type: 'FeatureCollection',
        features: []
      });
  }

  /**
   * remove a geometry by its draw id
   * @param {String} id - the drawid of the geometry
   * @returns {Draw} this
   */
  remove(id) {
    this._store.delete(id);
    return this;
  }

  /**
   * remove all geometries
   * @returns {Draw} this
   */
  clear() {
    this._store.deleteAll();
    return this;
  }

}
<commit_msg>set feature ready for rendering on add<commit_after>'use strict';

import mapboxgl from 'mapbox-gl';

import Line from './geometries/line';
import Point from './geometries/point';
import Polygon from './geometries/polygon';

export default class API extends mapboxgl.Control {

  constructor() {
    super();
  }

  /**
   * add a feature
   * @param {Object} feature - GeoJSON feature
   * @param {Object} [options]
   * @pata {Boolean} [options.permanent=false] - disable selection for feature
   * @returns {Number} draw id of the set feature or an array of
   * draw ids if a feature collection was added
   */
  add(feature, options) {
    feature = JSON.parse(JSON.stringify(feature));
    if (feature.type === 'FeatureCollection') {
      return feature.features.map(subFeature => this.set(subFeature, options));
    }

    if (!feature.geometry) {
      feature = {
        type: 'Feature',
        id: feature.id,
        geometry: feature
      };
    }

    if (!options) {
      options = {};
    }
    options.map = this._map;
    options.data = feature;

    var internalFeature;
    switch (feature.geometry.type) {
      case 'Point':
        internalFeature = new Point(options);
        break;
      case 'LineString':
        internalFeature = new Line(options);
        break;
      case 'Polygon':
        internalFeature = new Polygon(options);
        break;
      default:
        throw new Error('MapboxGL Draw: Unsupported geometry type "' + feature.geometry.type + '"');
    }

    internalFeature.ready = true;

    this._store.set(internalFeature);
    if (this.options.interactive) {
      this._select(internalFeature.drawId);
    }

    return internalFeature.drawId;
  }

  /**
   * Updates an existing feature
   * @param {String} id - the drawId of the feature to update
   * @param {Object} feature - a GeoJSON feature
   * @returns {Draw} this
   */
  update(id, feature) {
    feature = JSON.parse(JSON.stringify(feature));
    var newFeatureType = feature.type === 'Feature' ? feature.geometry.type : feature.type;
    var _feature = this._store.get(id);
    if (_feature.geojson.geometry.type !== newFeatureType || _feature.getType() === 'square') {
      throw 'Can not update feature to different type and can not update squares';
    }
    _feature.setCoordinates(feature.coordinates || feature.geometry.coordinates);
    if (feature.properties) _feature.setProperties(feature.properties);
    return this;
  }

  /**
   * get a geometry by its draw id
   * @param {String} id - the draw id of the geometry
   */
  get(id) {
    var feature = this._store.get(id);
    return feature && feature.toGeoJSON();
  }

  /**
   * get all draw geometries
   * @returns {Object} a GeoJSON feature collection
   */
  getAll() {
    return this._store.getAllIds()
      .map(id => this._store.get(id).toGeoJSON())
      .reduce(function(featureCollection, feature) {
        featureCollection.features.push(feature);
        return featureCollection;
      }, {
        type: 'FeatureCollection',
        features: []
      });
  }

  /**
   * select a feature
   * @param {String} id - the drawId of the feature
   * @returns {Draw} this
   */
  select(id) {
    this._store.select(id);
    return this;
  }

  /**
   * select all features
   * @param {String} id - the drawId of the feature
   * @returns {Draw} this
   */
  selectAll() {
    this._store.getAllIds()
      .forEach(id => this.select(id));
    return this;
  }

  /**
   * deselect a feature
   * @param {String} id - the drawId of the feature
   * @returns {Draw} this
   */
  deselect(id) {
    this._store.commit(id);
    return this;
  }

  /**
   * deselect all features
   * @param {String} id - the drawId of the feature
   * @returns {Draw} this
   */
  deselectAll() {
    this._store.getSelectedIds()
      .forEach(id => this.commit(id));
    return this;
  }

  /**
   * get selected feature collection of features
   * @return {Object} a feature collection of geometries that are selected
   */
  getSelected() {
    return this._store.getSelectedIds()
      .map(id => this._store.get(id).toGeoJSON())
      .reduce(function(featureCollection, feature) {
        featureCollection.features.push(feature);
        return featureCollection;
      }, {
        type: 'FeatureCollection',
        features: []
      });
  }

  /**
   * remove a geometry by its draw id
   * @param {String} id - the drawid of the geometry
   * @returns {Draw} this
   */
  remove(id) {
    this._store.delete(id);
    return this;
  }

  /**
   * remove all geometries
   * @returns {Draw} this
   */
  clear() {
    this._store.deleteAll();
    return this;
  }

}
<|endoftext|>


<commit_before>'use strict';

const _ = require('lodash');
const bodyParser = require('body-parser');
const express = require('express');
const fs = require('fs');
const multer = require('multer');
const path = require('path');

const backgroundStorage = multer.diskStorage({
    destination: function (req, file, cb) {
        cb(null, './public/backgrounds');
    },
    filename: function (req, file, cb) {
        cb(null, `background_${Date.now()}${path.extname(file.fieldname)}`);
    }
});

const backgroundUpload = multer({ storage: backgroundStorage });

const carStorage = multer.diskStorage({
    destination: function (req, file, cb) {
        cb(null, './public/cars');
    },
    filename: function (req, file, cb) {
        cb(null, `car_${Date.now()}${path.extname(file.fieldname)}`);
    }
});

const carUpload = multer({ storage: carStorage });

const app = express();
const port = 3033;
const router = express.Router();

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

app.use(function (req, res, next) {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Methods', 'GET,POST,PUT,HEAD,DELETE,OPTIONS');
    res.header('Access-Control-Allow-Headers', 'content-Type,x-requested-with');
    next();
});

const handleErrors = function (req, res, action) {
    try {
        action();
    } catch (error) {
        console.log(error.toString());
        res.sendStatus(500);
        return;
    }

    res.sendStatus(200);
};

router.get('/', function (req, res) {
    console.log('GET: /');

    handleErrors(req, res, () => {});
});

router.get('/background', function (req, res) {
    console.log('GET: /background');

    fs.readdir('./public/backgrounds', function (err, files) {
        if (err) {
            console.log(err);
        }

        res.json(files);
    });
});

router.post('/background', backgroundUpload.any(), function (req, res) {
    console.log('POST: /background');

    res.sendStatus(200);
});

router.delete('/background', function (req, res) {
    console.log(`DELETE: /background ${JSON.stringify(req.body)}`);

    fs.unlink(`./public/backgrounds/${req.body.name}`, (err) => {
        if (err) {
            console.log('ERROR', err);
        }
    });

    res.sendStatus(200);
});

router.get('/car', function (req, res) {
    console.log('GET: /car');

    fs.readdir('./public/cars', function (err, files) {
        if (err) {
            console.log(err);
        }

        res.json(files);
    });
});

router.post('/car', carUpload.any(), function (req, res) {
    console.log('POST: /car');

    res.sendStatus(200);
});

router.delete('/car', function (req, res) {
    console.log(`DELETE: /car ${JSON.stringify(req.body)}`);

    fs.unlink(`./public/cars/${req.body.name}`, (err) => {
        if (err) {
            console.log('ERROR', err);
        }
    });

    res.sendStatus(200);
});

router.get('/song', function (req, res) {
    console.log('GET: /song');

    fs.readFile('./public/songs.json', 'utf8', function (err, data) {
        if (err) {
            console.log(err);
        }

        var songs = JSON.parse(data);

        res.json(songs);
    });
});

app.use('/', router);

app.listen(port);
console.log('Api is running on port: ' + port);
<commit_msg>Changed api to use everywhere handleErrors method<commit_after>'use strict';

const _ = require('lodash');
const bodyParser = require('body-parser');
const express = require('express');
const fs = require('fs');
const multer = require('multer');
const path = require('path');

const backgroundStorage = multer.diskStorage({
    destination: function (req, file, cb) {
        cb(null, './public/backgrounds');
    },
    filename: function (req, file, cb) {
        cb(null, `background_${Date.now()}${path.extname(file.fieldname)}`);
    }
});

const backgroundUpload = multer({ storage: backgroundStorage });

const carStorage = multer.diskStorage({
    destination: function (req, file, cb) {
        cb(null, './public/cars');
    },
    filename: function (req, file, cb) {
        cb(null, `car_${Date.now()}${path.extname(file.fieldname)}`);
    }
});

const carUpload = multer({ storage: carStorage });

const app = express();
const port = 3033;
const router = express.Router();

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

app.use(function (req, res, next) {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Methods', 'GET,POST,PUT,HEAD,DELETE,OPTIONS');
    res.header('Access-Control-Allow-Headers', 'content-Type,x-requested-with');
    next();
});

const handleErrors = function (req, res, action) {
    try {
        action();
    } catch (error) {
        console.log('myerror');
        console.log(error.toString());
        res.sendStatus(500);
    }
};

router.get('/', function (req, res) {
    console.log('GET: /');

    handleErrors(req, res, () => { res.sendStatus(200); });
});

router.get('/background', function (req, res) {
    console.log('GET: /background');

    handleErrors(req, res, () => {
        fs.readdir('./public/backgrounds', function (err, files) {
            if (err) {
                throw err;
            }

            res.json(files);
        });
    });
});

router.post('/background', backgroundUpload.any(), function (req, res) {
    console.log('POST: /background');

    handleErrors(req, res, () => { res.sendStatus(200); });
});

router.delete('/background', function (req, res) {
    console.log(`DELETE: /background ${JSON.stringify(req.body)}`);

    handleErrors(req, res, () => {
        fs.unlink(`./public/backgrounds/${req.body.name}`, (err) => {
            if (err) {
                throw err;
            }
        });

        res.sendStatus(200);
    });
});

router.get('/car', function (req, res) {
    console.log('GET: /car');

    handleErrors(req, res, () => {
        fs.readdir('./public/cars', function (err, files) {
            if (err) {
                throw err;
            }

            res.json(files);
        });
    });
});

router.post('/car', carUpload.any(), function (req, res) {
    console.log('POST: /car');

    handleErrors(req, res, () => { res.sendStatus(200); });
});

router.delete('/car', function (req, res) {
    console.log(`DELETE: /car ${JSON.stringify(req.body)}`);

    handleErrors(req, res, () => {
        fs.unlink(`./public/cars/${req.body.name}`, (err) => {
            if (err) {
                throw err;
            }
        });

        res.sendStatus(200);
    });
});

router.get('/song', function (req, res) {
    console.log('GET: /song');

    handleErrors(req, res, () => {
        fs.readFile('./public/songs.json', 'utf8', function (err, data) {
            if (err) {
                throw err;
            }

            var songs = JSON.parse(data);

            res.json(songs);
        });
    });
});

app.use('/', router);

app.listen(port);
console.log('Api is running on port: ' + port);
<|endoftext|>


<commit_before>/**
 * Created by apizzimenti on 12/29/15.
 */

var clc = require('cli-color');

function collectDates(data) {
    var dates = [];

    data.forEach(function (obj) {
        var date = new Date(obj.time * 1000);
        dates.push(date.toDateString());
    });

    return dates;
}

function collectHighs(data) {
    var highs = [];

    data.forEach(function (obj) {
        highs.push('high: ' + Math.floor(obj.temperatureMax).toString() + '˚');
    });

    return highs;
}

function collectLows(data) {
    var lows = [];

    data.forEach(function (obj) {
        lows.push('low: ' + Math.floor(obj.temperatureMin).toString() + '˚');
    });

    return lows;
}

function collectSummary(data) {
    var summaries = [];

    data.forEach(function (obj) {
        summaries.push(obj.summary.toLowerCase());
    });

    return summaries;
}

function display(now, dates, max, min, summaries, icons) {

    var current = '\nCurrent Conditions:\n------------------\n';

    current += (now.temperature >= 32 ? clc.red(Math.floor(now.temperature)) : clc.blue(Math.floor(now.temperature)) + clc.blue('˚')) +
            ' - ' + (now.windSpeed % 1 ? Math.floor(now.windSpeed) : Math.ceil(now.windSpeed)) + 'mph wind - ' +
            now.summary.toLowerCase() + '\n';

    var extended = '3-day forecast:\n';

    for(var i = 0; i < 3; i++) {
        extended += '---------------\n' + dates[i] + '\n' +
                (max[i] >= 32 ? clc.red(max[i]) : clc.blue(max[i])) + '\n' +
                (min[i] >= 32 ? clc.red(min[i]) : clc.blue(min[i])) + '\n' +
                summaries[i] + '\n' +
                icons[i] + '\n---------------\n';
    }

    return current + extended;

}

function icon(array) {
    var icons = [];

    array.forEach(function (name) {
        switch (name) {
            case 'clear-day':case 'clear-night':
            icons.push('☀ clear');
            break;
            case 'rain':
                icons.push('☂ rain');
                break;
            case 'snow':
                icons.push('❄ snow');
                break;
            case 'sleet':
                icons.push('☂❄ sleet');
                break;
            case 'wind':
                icons.push(name);
                break;
            default:
                icons.push('☁ clouds');
                break;
        }
    });

    return icons;
}

module.exports = {
    collectDates: collectDates,
    collectHighs: collectHighs,
    collectLows: collectLows,
    collectSummary: collectSummary,
    display: display,
    icon: icon
};<commit_msg>add displayTable() function<commit_after>/**
 * Created by apizzimenti on 12/29/15.
 */

var clc = require('cli-color'),
    table = require('cli-table');

function collectDates(data) {
    var dates = [];

    data.forEach(function (obj) {
        var date = new Date(obj.time * 1000);
        dates.push(date.toDateString());
    });

    return dates;
}

function collectHighs(data) {
    var highs = [];

    data.forEach(function (obj) {
        highs.push('high: ' + Math.floor(obj.temperatureMax).toString() + '˚');
    });

    return highs;
}

function collectLows(data) {
    var lows = [];

    data.forEach(function (obj) {
        lows.push('low: ' + Math.floor(obj.temperatureMin).toString() + '˚');
    });

    return lows;
}

function collectSummary(data) {
    var summaries = [];

    data.forEach(function (obj) {
        summaries.push(obj.summary.toLowerCase());
    });

    return summaries;
}

function display(now) {

    var current = '\nCurrent Conditions:\n------------------\n';

    current += (now.temperature >= 32 ? clc.red(Math.floor(now.temperature) + '˚') : clc.blue(Math.floor(now.temperature) + '˚')) +
            ' - ' + (now.windSpeed % 1 ? Math.floor(now.windSpeed) : Math.ceil(now.windSpeed)) + 'mph wind - ' +
            now.summary.toLowerCase() + '\n';

    return current;

}

function displayTable(headers, highs, lows, icons) {
    var Table = new table({
        head: headers,
        chars: {
            'mid': '', 'left-mid': '', 'mid-mid': '', 'right-mid': ''
        }
    });

    Table.push(highs, lows, icons);

    return Table.toString()
}

function icon(array) {
    var icons = [];

    array.forEach(function (name) {
        switch (name) {
            case 'clear-day':case 'clear-night':
            icons.push('☀ clear');
            break;
            case 'rain':
                icons.push('☂ rain');
                break;
            case 'snow':
                icons.push('❄ snow');
                break;
            case 'sleet':
                icons.push('☂❄ sleet');
                break;
            case 'wind':
                icons.push(name);
                break;
            default:
                icons.push('☁ clouds');
                break;
        }
    });

    return icons;
}

module.exports = {
    collectDates: collectDates,
    collectHighs: collectHighs,
    collectLows: collectLows,
    collectSummary: collectSummary,
    display: display,
    displayTable: displayTable,
    icon: icon
};<|endoftext|>


<commit_before>'use strict'
import 'dotenv/config'
import Koa from 'koa'
import middlewares from './middlewares'
import routes from './routes'
import db from './config/db'
/* eslint-disable */
require('manakin').global // output colors in Development
/* eslint-enable */
const app = new Koa()
const PORT = process.env.PORT

app.use(middlewares())
app.use(routes())

// #### START APP ####
db.on('connected', () => {
  console.success('Mongo db connected successfully')
  app.listen(PORT, () => {
    console.info(`App listening on localhost:${PORT}`)
  })
})
<commit_msg>chore(jwt-error): custom handling error for jwt missing authentication (401)<commit_after>'use strict'
import 'dotenv/config'
import Koa from 'koa'
import middlewares from './middlewares'
import routes from './routes'
import db from './config/db'
import BoomError from './services/boom-error.service'
/* eslint-disable */
require('manakin').global // output colors in Development
/* eslint-enable */
const app = new Koa()
const PORT = process.env.PORT

app.use((ctx, next) => {
  return next().catch((err) => {
    if (err.status === 401) {
      BoomError.sendError(401, ctx, 'Protected resource, you need to authenticate the request')
    } else {
      throw err
    }
  })
})

app.use(middlewares())
app.use(routes())

// #### START APP ####
db.on('connected', () => {
  console.success('Mongo db connected successfully')
  app.listen(PORT, () => {
    console.info(`App listening on localhost:${PORT}`)
  })
})
<|endoftext|>


<commit_before>/*
 * NTwitBot - data.js
 * Copyright (C) 2016 Jordan Sne
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

const fs = require('fs');

module.exports = class Data {

    constructor(twitterPkg, utils) {
        this.twitterPkg = twitterPkg;
        this.utils = utils;

        this.DATABASE_DIR = './data';

        if (!fs.existsSync(this.DATABASE_DIR)) {
            fs.mkdirSync(this.DATABASE_DIR);
        }
    }

    /*
     * Processes tracked tweets of the bot. (Saves in database, etc.)
     *   tweets  - Array (Strings): The Tweets to be processed.
     *   done    - Function:        Function to call when done processing & saving tweets.
     */
    processTrackedTweets(tweets, done) {
        const newData = {};

        this.utils.log("Processing " + tweets.length + " new tweets (which do not include RTs)");

        // Process tweets
        for (let i = 0; i < tweets.length; i++) {
            const tweet = tweets[i];
            const tweetWords = tweet.text.split(" ");
            const filteredWords = [];

            for (let i = 0; i < tweetWords.length; i++) {
                let word = tweetWords[i];

                // Remove mentions & links
                if (word.startsWith("@") || word.startsWith("http")) {
                    continue;
                }

                word = word.toLowerCase();
                filteredWords.push(word);
            }

            if (filteredWords.length >= 3) {
                // Capitalize the first word
                filteredWords[0] = this.utils.capitalize(filteredWords[0]);

                // Capitalize any words after a sentence
                for (let i = 1; i < filteredWords.length; i++) {
                    if (this.utils.endsWithPunc(filteredWords[i - 1])) {
                        filteredWords[i] = this.utils.capitalize(filteredWords[i]);
                    }
                }

                // Append period if no punctuation present
                let lastWord = filteredWords[filteredWords.length - 1];
                if (!this.utils.endsWithPunc(lastWord) && !lastWord.endsWith(",")) {
                    filteredWords[filteredWords.length - 1] += ".";
                }

                // Process words into data objects
                for (let i = 0; i < filteredWords.length - 2; i++) {
                    let key = filteredWords[i] + " " + filteredWords[i + 1];

                    // If the key doesn't exist, create a new array..
                    if (!(key in newData)) {
                        newData[key] = [{
                            word: filteredWords[i + 2],
                            time: this.utils.getTime()
                        }];
                    // .. otherwise add it to the existing array
                    } else {
                        newData[key].push({
                            word: filteredWords[i + 2],
                            time: this.utils.getTime()
                        });
                    }
                }
            }
        }

        // Save the data in the database
        fs.readFile(this.DATABASE_DIR + '/db.json', (error, data) => {
            if (error) {
                if (error.code === "ENOENT") {
                    this.utils.log("I/O: No database found, creating a new one");
                    this.inputToEmpty(newData, done);
                } else {
                    this.utils.logError("I/O: Failed to open database file (" + this.DATABASE_DIR + "/db.json)");
                    this.utils.logError("I/O:    Error: " + error.Error);
                }
            } else {
                this.inputToExisting(JSON.parse(data), newData, done);
            }
        });
    }

    /*
     * Saves the new data into a blank database.
     *   data - Object:   Data to be saved.
     *   done - Function: Function to be called when done saving tweets.
     */
    inputToEmpty(data, done) {
        // Create an empty database
        fs.open(this.DATABASE_DIR + '/db.json', 'w', (error, fd) => {
            if (error) {
                this.utils.logError("I/O: Failed create new database file (" + this.DATABASE_DIR + "/db.json)");
                this.utils.logError("I/O:    Error: " + error.code);
                return;
            }

            fs.closeSync(fd);

            // Save data to the database
            fs.writeFile(this.DATABASE_DIR + '/db.json', JSON.stringify(data), (error, data) => {
                if (error) {
                    this.utils.logError("I/O: Failed to write new data to database file (" + this.DATABASE_DIR + "/db.json)");
                    this.utils.logError("I/O:    Error code: " + error.code);
                    return;
                }

                this.utils.log("I/O: Saved tweet data in database");
                done();
            });
        });
    }

    /*
     * Adds the data to an existing database.
     *   data    - Object:   The data that was in the database.
     *   newData - Object:   The data to be appended to the database.
     *   done    - Function: Function to be called when done saving tweets.
     */
    inputToExisting(data, newData, done) {
        // For each key in the new data..
        for (let key in newData) {
            // If the it exists in existing data
            if (key in data) {
                // Push (append), new data to exist array
                for (let i = 0; i < newData[key].length; i++) {
                    data[key].push(newData[key][i]);
                }
            // .. if not, declare new array in existing data
            } else {
                data[key] = newData[key];
            }
        }

        // Save data to database
        fs.writeFile(this.DATABASE_DIR + '/db.json', JSON.stringify(data), (error, data) => {
            if (error) {
                this.utils.logError("I/O: Failed to append new data to database file (" + this.DATABASE_DIR + "/db.json)");
                this.utils.logError("I/O:    Error code: " + error.code);
                return;
            }

            this.utils.log("I/O: Appended new tweet data to database");
            done();
        });
    }

    /*
     * Saves the botStatus in file.
     *   botStatus - Object: Data to be saved.
     */
    saveStatus(botStatus) {
        fs.writeFile(this.DATABASE_DIR + '/status.json', JSON.stringify(botStatus), (error, botStatus) => {
            if (error) {
                this.utils.logError("I/O: Failed to save bot status to file (" + this.DATABASE_DIR + "/status.json)");
                this.utils.logError("I/O:    Error code: " +  error.code);
                return;
            }

            this.utils.log("I/O: Saved bot status to file");
        });
    }

    /*
     * Gets the botStatus from the file.
     *   Returns - Object: Object containing current bot status.
     */
    readStatus() {
        let botStatus;

        try {
            botStatus = fs.readFileSync(this.DATABASE_DIR + '/status.json', 'utf8');
        } catch (error) {
            if (error.code == 'ENOENT') {
                return null;
            } else {
                this.utils.logError("I/O: FATAL: Failed to read botStatus file (" + this.DATABASE_DIR + "/status.json)");
                this.utils.logError("I/O: FATAL: Exiting..");
                process.exit(1);
            }
        }

        this.utils.log("I/O: Read bot status from file");
        return JSON.parse(botStatus);
    }

    /*
     * Gets word data from the database file.
     *   Returns - Object: Object containing word dictionary.
     */
    readDatabase() {
        let data;

        // Read data from tweet database
        try {
            data = fs.readFileSync(this.DATABASE_DIR + '/db.json', 'utf8');
        } catch (error) {
            if (error.code === "ENOENT") {
                this.utils.logError("I/O: No database found, could not generate tweet");
            } else {
                this.utils.logError("I/O: Failed to open database file (" + this.DATABASE_DIR + "/db.json)");
                this.utils.logError("I/O:    Error: ", error.Error);
            }
        }

        return JSON.parse(data);
    }

};
<commit_msg>Decode HTML entities before processing<commit_after>/*
 * NTwitBot - data.js
 * Copyright (C) 2016 Jordan Sne
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

const entities = require('html-entities').AllHtmlEntities;
const fs       = require('fs');

module.exports = class Data {

    constructor(twitterPkg, utils) {
        this.twitterPkg = twitterPkg;
        this.utils = utils;

        this.DATABASE_DIR = './data';

        if (!fs.existsSync(this.DATABASE_DIR)) {
            fs.mkdirSync(this.DATABASE_DIR);
        }
    }

    /*
     * Processes tracked tweets of the bot. (Saves in database, etc.)
     *   tweets  - Array (Strings): The Tweets to be processed.
     *   done    - Function:        Function to call when done processing & saving tweets.
     */
    processTrackedTweets(tweets, done) {
        const newData = {};

        this.utils.log("Processing " + tweets.length + " new tweets (which do not include RTs)");

        // Process tweets
        for (let i = 0; i < tweets.length; i++) {
            const tweet = tweets[i];

            // Process "< > &" symbols
            tweet.text = entities.decode(tweet.text);

            const tweetWords = tweet.text.split(" ");
            const filteredWords = [];

            for (let i = 0; i < tweetWords.length; i++) {
                let word = tweetWords[i];

                // Remove mentions & links
                if (word.startsWith("@") || word.startsWith("http")) {
                    continue;
                }

                word = word.toLowerCase();
                filteredWords.push(word);
            }

            if (filteredWords.length >= 3) {
                // Capitalize the first word
                filteredWords[0] = this.utils.capitalize(filteredWords[0]);

                // Capitalize any words after a sentence
                for (let i = 1; i < filteredWords.length; i++) {
                    if (this.utils.endsWithPunc(filteredWords[i - 1])) {
                        filteredWords[i] = this.utils.capitalize(filteredWords[i]);
                    }
                }

                // Append period if no punctuation present
                let lastWord = filteredWords[filteredWords.length - 1];
                if (!this.utils.endsWithPunc(lastWord) && !lastWord.endsWith(",")) {
                    filteredWords[filteredWords.length - 1] += ".";
                }

                // Process words into data objects
                for (let i = 0; i < filteredWords.length - 2; i++) {
                    let key = filteredWords[i] + " " + filteredWords[i + 1];

                    // If the key doesn't exist, create a new array..
                    if (!(key in newData)) {
                        newData[key] = [{
                            word: filteredWords[i + 2],
                            time: this.utils.getTime()
                        }];
                    // .. otherwise add it to the existing array
                    } else {
                        newData[key].push({
                            word: filteredWords[i + 2],
                            time: this.utils.getTime()
                        });
                    }
                }
            }
        }

        // Save the data in the database
        fs.readFile(this.DATABASE_DIR + '/db.json', (error, data) => {
            if (error) {
                if (error.code === "ENOENT") {
                    this.utils.log("I/O: No database found, creating a new one");
                    this.inputToEmpty(newData, done);
                } else {
                    this.utils.logError("I/O: Failed to open database file (" + this.DATABASE_DIR + "/db.json)");
                    this.utils.logError("I/O:    Error: " + error.Error);
                }
            } else {
                this.inputToExisting(JSON.parse(data), newData, done);
            }
        });
    }

    /*
     * Saves the new data into a blank database.
     *   data - Object:   Data to be saved.
     *   done - Function: Function to be called when done saving tweets.
     */
    inputToEmpty(data, done) {
        // Create an empty database
        fs.open(this.DATABASE_DIR + '/db.json', 'w', (error, fd) => {
            if (error) {
                this.utils.logError("I/O: Failed create new database file (" + this.DATABASE_DIR + "/db.json)");
                this.utils.logError("I/O:    Error: " + error.code);
                return;
            }

            fs.closeSync(fd);

            // Save data to the database
            fs.writeFile(this.DATABASE_DIR + '/db.json', JSON.stringify(data), (error, data) => {
                if (error) {
                    this.utils.logError("I/O: Failed to write new data to database file (" + this.DATABASE_DIR + "/db.json)");
                    this.utils.logError("I/O:    Error code: " + error.code);
                    return;
                }

                this.utils.log("I/O: Saved tweet data in database");
                done();
            });
        });
    }

    /*
     * Adds the data to an existing database.
     *   data    - Object:   The data that was in the database.
     *   newData - Object:   The data to be appended to the database.
     *   done    - Function: Function to be called when done saving tweets.
     */
    inputToExisting(data, newData, done) {
        // For each key in the new data..
        for (let key in newData) {
            // If the it exists in existing data
            if (key in data) {
                // Push (append), new data to exist array
                for (let i = 0; i < newData[key].length; i++) {
                    data[key].push(newData[key][i]);
                }
            // .. if not, declare new array in existing data
            } else {
                data[key] = newData[key];
            }
        }

        // Save data to database
        fs.writeFile(this.DATABASE_DIR + '/db.json', JSON.stringify(data), (error, data) => {
            if (error) {
                this.utils.logError("I/O: Failed to append new data to database file (" + this.DATABASE_DIR + "/db.json)");
                this.utils.logError("I/O:    Error code: " + error.code);
                return;
            }

            this.utils.log("I/O: Appended new tweet data to database");
            done();
        });
    }

    /*
     * Saves the botStatus in file.
     *   botStatus - Object: Data to be saved.
     */
    saveStatus(botStatus) {
        fs.writeFile(this.DATABASE_DIR + '/status.json', JSON.stringify(botStatus), (error, botStatus) => {
            if (error) {
                this.utils.logError("I/O: Failed to save bot status to file (" + this.DATABASE_DIR + "/status.json)");
                this.utils.logError("I/O:    Error code: " +  error.code);
                return;
            }

            this.utils.log("I/O: Saved bot status to file");
        });
    }

    /*
     * Gets the botStatus from the file.
     *   Returns - Object: Object containing current bot status.
     */
    readStatus() {
        let botStatus;

        try {
            botStatus = fs.readFileSync(this.DATABASE_DIR + '/status.json', 'utf8');
        } catch (error) {
            if (error.code == 'ENOENT') {
                return null;
            } else {
                this.utils.logError("I/O: FATAL: Failed to read botStatus file (" + this.DATABASE_DIR + "/status.json)");
                this.utils.logError("I/O: FATAL: Exiting..");
                process.exit(1);
            }
        }

        this.utils.log("I/O: Read bot status from file");
        return JSON.parse(botStatus);
    }

    /*
     * Gets word data from the database file.
     *   Returns - Object: Object containing word dictionary.
     */
    readDatabase() {
        let data;

        // Read data from tweet database
        try {
            data = fs.readFileSync(this.DATABASE_DIR + '/db.json', 'utf8');
        } catch (error) {
            if (error.code === "ENOENT") {
                this.utils.logError("I/O: No database found, could not generate tweet");
            } else {
                this.utils.logError("I/O: Failed to open database file (" + this.DATABASE_DIR + "/db.json)");
                this.utils.logError("I/O:    Error: ", error.Error);
            }
        }

        return JSON.parse(data);
    }

};
<|endoftext|>


<commit_before>import 'bootstrap';
import 'bootstrap/css/bootstrap.css!';
import underscore from 'underscore';

export class App {
  configureRouter(config, router){
    config.title = 'Flash Cards';
    config.map([
      { route: ['','welcome'],  moduleId: './welcome',      nav: true, title:'Welcome' },
	  { route: 'Notebox',  moduleId: './notebox',      nav: true, title:'Notebox' }
    ]);

    this.router = router;
  }
}<commit_msg>clean up underscore ref<commit_after>import 'bootstrap';
import 'bootstrap/css/bootstrap.css!';

export class App {
  configureRouter(config, router){
    config.title = 'Flash Cards';
    config.map([
      { route: ['','welcome'],  moduleId: './welcome',      nav: true, title:'Welcome' },
	  { route: 'Notebox',  moduleId: './notebox',      nav: true, title:'Notebox' }
    ]);

    this.router = router;
  }
}<|endoftext|>


<commit_before>// SocialMap app uses LeafletJS for map visualization.
// More info on LeafletJS can be found here: http://leafletjs.com
(function($) {
"use strict";

var socialmap = Echo.App.manifest("Echo.Apps.SocialMap");

if (Echo.App.isDefined(socialmap)) return;

socialmap.vars = {
	// events list to maintain a number of last seen events to make
	// a map feel real-time in case of slow incoming data rate
	"pins": [],
	"timeouts": {}
};

socialmap.config = {
	"targetURL": undefined,
	"viewportChangeTimeout": 50,
	"minHistoricalPinsQueue": 5,    // min queue size to start historical pins
					// carousel (in case "renderHistoricalData" = true)
	"maxHistoricalPinsQueue": 50,
	"presentation": {
		"visualization": "us",
		"mapColor": "#D8D8D8",
		"pinBodyColor": "#3C3C3C",
		"pinBorderColor": "#3C3C3C",
		"pinShowSpeed": "100",
		"pinFadeOutSpeed": "2000",
		"pinDisplayTime": "5000",
		"pinDefaultSize": 8, // in px, pin size at 1x map scale
		"renderHistoricalData": true,
		"pinsRenderingTimeoutRange": [1, 3] // [min, max] in seconds
	},
	"dependencies": {
		"StreamServer": {
			"appkey": undefined,
			"apiBaseURL": "//api.echoenabled.com/v1/",
			"liveUpdates": {
				"transport": "websockets",
				"enabled": true,
				"websockets": {
					"URL": "//live.echoenabled.com/v1/"
				}
			}
		}
	}
};

socialmap.dependencies = [
	{"url": "{config:cdnBaseURL.sdk}/api.pack.js", "control": "Echo.StreamServer.API"},
	{"url": "{%= baseURLs.prod %}/third-party/leaflet.css"},
	{"url": "{%= baseURLs.prod %}/third-party/leaflet.js"}
];

socialmap.init = function() {
	var app = this;

	// check for "targetURL" field, without
	// this field we are unable to retrieve any data
	if (!this.config.get("targetURL")) {
		this.showMessage({
			"type": "error",
			"message": "Unable to retrieve data, target URL is not specified."
		});
		return;
	}

	var map = this.config.get("presentation.visualization");
	this._loadGeoJSON(map, function() {
		// save reference to GEO data object
		app.set("geo", Echo.Variables.GEO[app.config.get("presentation.visualization")]);
		app._requestData();
	});
};

socialmap.destroy = function() {
	// cleanup pin rendering timeouts
	var timeouts = this.get("timeouts");
	$.each(timeouts, function(id) {
		clearTimeout(id);
		delete timeouts[id];
	});

	var map = this.get("map");
	if (map) {
		map.remove();
	}
	$(window).off("resize", this._viewportResizeHandler);
};

socialmap.templates.main =
	'
' +
		'

' +
	'
';

socialmap.methods._assembleQuery = function() {
	var query = "childrenof:{config:targetURL} " +
		"itemsPerPage:{config:maxHistoricalPinsQueue} " +
		"markers:geo.marker children markers:geo.marker";
	return this.substitute({"template": query});
};

socialmap.methods._loadGeoJSON = function(visualization, callback) {
	var url = "{%= baseURLs.prod %}/third-party/geo." + visualization + ".js";
	Echo.Loader.download([{"url": url}], callback);
};

socialmap.methods._requestData = function() {
	var app = this;
	var ssConfig = this.config.get("dependencies.StreamServer");
	// keep a reference to a request object in "this" to trigger its
	// automatic sweeping out on Echo.Control level at app destory time
	this.request = Echo.StreamServer.API.request({
		"endpoint": "search",
		"secure": this.config.get("useSecureAPI"),
		"apiBaseURL": ssConfig.apiBaseURL,
		"data": {
			"q": this._assembleQuery(),
			"appkey": ssConfig.appkey
		},
		"liveUpdates": $.extend(ssConfig.liveUpdates, {
			"onData": function(data) {
				app._renderNewPins(app._extractValidPins(data));
			}
		}),
		"onError": function(data, options) {
			var isCriticalError =
				typeof options.critical === "undefined" ||
				options.critical && options.requestType === "initial";

			if (isCriticalError) {
				app.showError(data, $.extend(options, {
					"request": app.request
				}));
			}
		},
		"onData": function(data, options) {
			app.render();
			// we need to wait until the "render" function is executed
			// for elements to be appended into the DOM tree and be
			// laid out by the browser. Otherwise it wouldn't have a
			// width/height yet and Leaflet does NOT like that
			app._renderMap();
			if (app.config.get("presentation.renderHistoricalData")) {
				app._carouselHistoricalPins(app._extractValidPins(data));
			}
			app.ready();
		}
	});
	this.request.send();
};

socialmap.methods._extractGeoLocationInfo = function(entry) {
	var latlng;
	// A geo-location marker looks like this,
	// using the standard rule in DataServer:
	//    geo.location:-117.57980013;33.46756302
	$.map(entry.object.markers || [], function(marker) {
		if (!/^geo.location:/.test(marker)) return;
		var parts = marker.split(":")[1].split(";");
		latlng = [parseFloat(parts[1]), parseFloat(parts[0])];
	});
	return latlng;
};

socialmap.methods._getPinTransitionCSS = function(transform, duration) {
	var prefixes = ["-o-", "-ms-", "-webkit-", "-moz-", ""];
	return Echo.Utils.foldl({}, prefixes, function(prefix, acc) {
		acc[prefix + "transition-timing-function"] = "ease-out";
		acc[prefix + "transition-duration"] = duration + "ms";
		acc[prefix + "transform"] = transform;
	});
};

socialmap.methods._log2 = function(value) {
	return Math.log2 ? Math.log2(value) : (Math.log(value) / Math.log(2));
};

socialmap.methods._getMapZoom = function(mapWidth) {
	var baseWidth = this.get("geo.defaultSize")[0];
	// note: adding 0.8 to start with 1x scale (vs 0x),
	//       adding 0.8, not 1 to fit map on a screen
	//       and center it (making zoom a bit smaller)
	return parseFloat((this._log2(mapWidth / baseWidth) + 0.8).toFixed(2));
};

socialmap.methods._getMapHeight = function(mapWidth) {
	var defaultSize = this.get("geo.defaultSize");
	// we get width/height proportion for default size and calculate
	// the necessary height using it, according to the given width
	return Math.round(mapWidth / (defaultSize[0] / defaultSize[1]));
};

socialmap.methods._extractValidPins = function(data) {
	var app = this;
	// gather valid (with geo data) pins only
	return Echo.Utils.foldl([], data.entries || [], function(entry, acc) {
		var loc = app._extractGeoLocationInfo(entry);
		if (loc) {
			acc.push([loc, entry]);
		}
	});
};

socialmap.methods._actualizeHistoricalPins = function(pins) {
	var max = this.config.get("maxHistoricalPinsQueue");
	var historicalPins = this.get("pins").concat(pins);
	if (historicalPins.length > max) {
		historicalPins.splice(0, historicalPins.length - max);
	}
	this.set("pins", historicalPins);
};

socialmap.methods._repeatWithRandomInterval = function(func) {
	var app = this;
	var interval = this.config.get("presentation.pinsRenderingTimeoutRange");
	(function iterate() {
		var timeout = setTimeout(function() {
			clearTimeout(timeout);
			delete app.get("timeouts")[timeout];
			var stopIterating = func();
			if (!stopIterating) {
				iterate();
			}
		}, Echo.Utils.random.apply(null, interval) * 1000);

		// keep timeout reference to kill it at "destroy"
		app.get("timeouts")[timeout] = true;
	})();
};

socialmap.methods._carouselHistoricalPins = function(pins) {
	var app = this;
	app.set("pins", pins);
	this._repeatWithRandomInterval(function() {
		var pins = app.get("pins");
		if (pins.length && pins.length >= app.config.get("minHistoricalPinsQueue")) {
			var max = app.config.get("maxHistoricalPinsQueue");
			var id = Echo.Utils.random(1, Math.min(pins.length, max)) - 1;
			var pin = pins[id];
			if (pin && pin.length) {
				app._renderPin(pin[0], pin[1]);
			}
		}
	});
};

socialmap.methods._renderNewPins = function(pins) {
	if (!pins || !pins.length) return;

	var app = this, idx = 0;
	if (this.config.get("presentation.renderHistoricalData")) {
		this._actualizeHistoricalPins(pins);
	}
	this._repeatWithRandomInterval(function() {
		var pin = pins[idx];
		if (pin) {
			app._renderPin(pin[0], pin[1]);
		}
		idx++;
		return (idx === pins.length);
	});
};

socialmap.methods._renderPin = function(latlng, entry) {
	var app = this,
		map = app.get("map"),
		pinSize = app.get("zoom") * app.config.get("presentation.pinDefaultSize");

	var icon = L.divIcon({
		"className": "echo-apps-socialmap-pin",
		"iconSize": [pinSize, pinSize]
	});
	var marker = L.marker(latlng, {"icon": icon, "opacity": 1}).addTo(map);

	var pinCSS = $.extend({
		"opacity": "0.5",
		"border-radius": "50%", // make it a cirlce
		"background": app.config.get("presentation.pinBodyColor"),
		"border": "1px solid " + app.config.get("presentation.pinBorderColor"),
		"width": pinSize,
		"height": pinSize
	}, app._getPinTransitionCSS("scale(0)", "0"));
	var circle = $('

').css(pinCSS);
	$(marker._icon).append(circle);

	// timeout is used to init CSS transition
	setTimeout(function() {
		var duration = app.config.get("presentation.pinShowSpeed");
		circle.css(app._getPinTransitionCSS("scale(1)", duration));
	}, 50);

	// wait a given number of ms and fade out
	setTimeout(function() {
		var duration = app.config.get("presentation.pinFadeOutSpeed");
		circle.css(app._getPinTransitionCSS("scale(0)", duration));

		// remove marker as soon as an animation is over to avoid memory leaks
		setTimeout(function() {
			map.removeLayer(marker);
		}, parseInt(app.config.get("presentation.pinFadeOutSpeed")));

	}, parseInt(app.config.get("presentation.pinDisplayTime")));
};

socialmap.methods._renderMap = function() {
	var app = this,
		map,
		mapView = app.view.get("map"),
		timeout = app.config.get("viewportChangeTimeout");

	var _viewportResizeHandler = function() {
		var width = mapView.width();
		var zoom = app._getMapZoom(width);
		app.set("zoom", zoom);
		mapView.height(app._getMapHeight(width));
		if (map) {
			map.setView(app.get("geo.center"), zoom, {"reset": true});
		}
	};

	// store debounced version of the function
	this._viewportResizeHandler = Echo.Utils.debounce(_viewportResizeHandler, timeout);

	// trigger zoom/height calculations
	_viewportResizeHandler();

	$(window).bind("resize", this._viewportResizeHandler);

	// more information on the options below can be found
	// on Leaflet docs: http://leafletjs.com/reference.html
	map = new L.map(mapView.get(0), {
		"center": app.get("geo.center"),
		"zoom": app.get("zoom"),
		"minZoom": 0.1,
		"maxZoom": 5,
		"dragging": false,
		"touchZoom": false,
		"scrollWheelZoom": false,
		"doubleClickZoom": false,
		"boxZoom": false,
		"keyboard": false,
		"zoomControl": false,
		"trackResize": true,
		"attributionControl": false
	});
	L.geoJson(this.get("geo.json"), {
		"style": {
			"fillColor": app.config.get("presentation.mapColor"),
			"fillOpacity": 1,
			"stroke": true,
			"color": "#FFFFFF",
			"weight": 1,
			"opacity": 1
		}
	}).addTo(map);

	app.set("map", map);
};

socialmap.css = '.{class} .{class:map} { width: 100%; min-width: 300px; background: inherit; }';

Echo.App.create(socialmap);

})(Echo.jQuery);
<commit_msg>Moving rendering logic outside of the data retrieval machinery<commit_after>// SocialMap app uses LeafletJS for map visualization.
// More info on LeafletJS can be found here: http://leafletjs.com
(function($) {
"use strict";

var socialmap = Echo.App.manifest("Echo.Apps.SocialMap");

if (Echo.App.isDefined(socialmap)) return;

socialmap.vars = {
	// events list to maintain a number of last seen events to make
	// a map feel real-time in case of slow incoming data rate
	"pins": [],
	"timeouts": {}
};

socialmap.config = {
	"targetURL": undefined,
	"viewportChangeTimeout": 50,
	"minHistoricalPinsQueue": 5,    // min queue size to start historical pins
					// carousel (in case "renderHistoricalData" = true)
	"maxHistoricalPinsQueue": 50,
	"presentation": {
		"visualization": "us",
		"mapColor": "#D8D8D8",
		"pinBodyColor": "#3C3C3C",
		"pinBorderColor": "#3C3C3C",
		"pinShowSpeed": "100",
		"pinFadeOutSpeed": "2000",
		"pinDisplayTime": "5000",
		"pinDefaultSize": 8, // in px, pin size at 1x map scale
		"renderHistoricalData": true,
		"pinsRenderingTimeoutRange": [1, 3] // [min, max] in seconds
	},
	"dependencies": {
		"StreamServer": {
			"appkey": undefined,
			"apiBaseURL": "//api.echoenabled.com/v1/",
			"liveUpdates": {
				"transport": "websockets",
				"enabled": true,
				"websockets": {
					"URL": "//live.echoenabled.com/v1/"
				}
			}
		}
	}
};

socialmap.dependencies = [
	{"url": "{config:cdnBaseURL.sdk}/api.pack.js", "control": "Echo.StreamServer.API"},
	{"url": "{%= baseURLs.prod %}/third-party/leaflet.css"},
	{"url": "{%= baseURLs.prod %}/third-party/leaflet.js"}
];

socialmap.init = function() {
	var app = this;

	// check for "targetURL" field, without
	// this field we are unable to retrieve any data
	if (!this.config.get("targetURL")) {
		this.showMessage({
			"type": "error",
			"message": "Unable to retrieve data, target URL is not specified."
		});
		return;
	}

	var map = this.config.get("presentation.visualization");
	this._loadGeoJSON(map, function() {
		// save reference to GEO data object
		app.set("geo", Echo.Variables.GEO[app.config.get("presentation.visualization")]);
		app._requestData({
			"onData": function(data, options) {
				app.render();
				// we need to wait until the "render" function is executed
				// for elements to be appended into the DOM tree and be
				// laid out by the browser. Otherwise it wouldn't have a
				// width/height yet and Leaflet does NOT like that
				app._renderMap();
				if (app.config.get("presentation.renderHistoricalData")) {
					app._carouselHistoricalPins(app._extractValidPins(data));
				}
				app.ready();
			},
			"onUpdate": function(data) {
				app._renderNewPins(app._extractValidPins(data));
			},
			"onError": function(data, options) {
				var isCriticalError =
					typeof options.critical === "undefined" ||
					options.critical && options.requestType === "initial";

				if (isCriticalError) {
					app.showError(data, $.extend(options, {
						"request": app.request
					}));
				}
			}
		});
	});
};

socialmap.destroy = function() {
	// cleanup pin rendering timeouts
	var timeouts = this.get("timeouts");
	$.each(timeouts, function(id) {
		clearTimeout(id);
		delete timeouts[id];
	});

	var map = this.get("map");
	if (map) {
		map.remove();
	}
	$(window).off("resize", this._viewportResizeHandler);
};

socialmap.templates.main =
	'
' +
		'

' +
	'
';

socialmap.methods._assembleQuery = function() {
	var query = "childrenof:{config:targetURL} " +
		"itemsPerPage:{config:maxHistoricalPinsQueue} " +
		"markers:geo.marker children markers:geo.marker";
	return this.substitute({"template": query});
};

socialmap.methods._loadGeoJSON = function(visualization, callback) {
	var url = "{%= baseURLs.prod %}/third-party/geo." + visualization + ".js";
	Echo.Loader.download([{"url": url}], callback);
};

socialmap.methods._requestData = function(handlers) {
	var ssConfig = this.config.get("dependencies.StreamServer");
	// keep a reference to a request object in "this" to trigger its
	// automatic sweeping out on Echo.Control level at app destory time
	this.request = Echo.StreamServer.API.request({
		"endpoint": "search",
		"secure": this.config.get("useSecureAPI"),
		"apiBaseURL": ssConfig.apiBaseURL,
		"data": {
			"q": this._assembleQuery(),
			"appkey": ssConfig.appkey
		},
		"liveUpdates": $.extend(ssConfig.liveUpdates, {
			"onData": handlers.onUpdate
		}),
		"onError": handlers.onError,
		"onData": handlers.onData
	});
	this.request.send();
};

socialmap.methods._extractGeoLocationInfo = function(entry) {
	var latlng;
	// A geo-location marker looks like this,
	// using the standard rule in DataServer:
	//    geo.location:-117.57980013;33.46756302
	$.map(entry.object.markers || [], function(marker) {
		if (!/^geo.location:/.test(marker)) return;
		var parts = marker.split(":")[1].split(";");
		latlng = [parseFloat(parts[1]), parseFloat(parts[0])];
	});
	return latlng;
};

socialmap.methods._getPinTransitionCSS = function(transform, duration) {
	var prefixes = ["-o-", "-ms-", "-webkit-", "-moz-", ""];
	return Echo.Utils.foldl({}, prefixes, function(prefix, acc) {
		acc[prefix + "transition-timing-function"] = "ease-out";
		acc[prefix + "transition-duration"] = duration + "ms";
		acc[prefix + "transform"] = transform;
	});
};

socialmap.methods._log2 = function(value) {
	return Math.log2 ? Math.log2(value) : (Math.log(value) / Math.log(2));
};

socialmap.methods._getMapZoom = function(mapWidth) {
	var baseWidth = this.get("geo.defaultSize")[0];
	// note: adding 0.8 to start with 1x scale (vs 0x),
	//       adding 0.8, not 1 to fit map on a screen
	//       and center it (making zoom a bit smaller)
	return parseFloat((this._log2(mapWidth / baseWidth) + 0.8).toFixed(2));
};

socialmap.methods._getMapHeight = function(mapWidth) {
	var defaultSize = this.get("geo.defaultSize");
	// we get width/height proportion for default size and calculate
	// the necessary height using it, according to the given width
	return Math.round(mapWidth / (defaultSize[0] / defaultSize[1]));
};

socialmap.methods._extractValidPins = function(data) {
	var app = this;
	// gather valid (with geo data) pins only
	return Echo.Utils.foldl([], data.entries || [], function(entry, acc) {
		var loc = app._extractGeoLocationInfo(entry);
		if (loc) {
			acc.push([loc, entry]);
		}
	});
};

socialmap.methods._actualizeHistoricalPins = function(pins) {
	var max = this.config.get("maxHistoricalPinsQueue");
	var historicalPins = this.get("pins").concat(pins);
	if (historicalPins.length > max) {
		historicalPins.splice(0, historicalPins.length - max);
	}
	this.set("pins", historicalPins);
};

socialmap.methods._repeatWithRandomInterval = function(func) {
	var app = this;
	var interval = this.config.get("presentation.pinsRenderingTimeoutRange");
	(function iterate() {
		var timeout = setTimeout(function() {
			clearTimeout(timeout);
			delete app.get("timeouts")[timeout];
			var stopIterating = func();
			if (!stopIterating) {
				iterate();
			}
		}, Echo.Utils.random.apply(null, interval) * 1000);

		// keep timeout reference to kill it at "destroy"
		app.get("timeouts")[timeout] = true;
	})();
};

socialmap.methods._carouselHistoricalPins = function(pins) {
	var app = this;
	app.set("pins", pins);
	this._repeatWithRandomInterval(function() {
		var pins = app.get("pins");
		if (pins.length && pins.length >= app.config.get("minHistoricalPinsQueue")) {
			var max = app.config.get("maxHistoricalPinsQueue");
			var id = Echo.Utils.random(1, Math.min(pins.length, max)) - 1;
			var pin = pins[id];
			if (pin && pin.length) {
				app._renderPin(pin[0], pin[1]);
			}
		}
	});
};

socialmap.methods._renderNewPins = function(pins) {
	if (!pins || !pins.length) return;

	var app = this, idx = 0;
	if (this.config.get("presentation.renderHistoricalData")) {
		this._actualizeHistoricalPins(pins);
	}
	this._repeatWithRandomInterval(function() {
		var pin = pins[idx];
		if (pin) {
			app._renderPin(pin[0], pin[1]);
		}
		idx++;
		return (idx === pins.length);
	});
};

socialmap.methods._renderPin = function(latlng, entry) {
	var app = this,
		map = app.get("map"),
		pinSize = app.get("zoom") * app.config.get("presentation.pinDefaultSize");

	var icon = L.divIcon({
		"className": "echo-apps-socialmap-pin",
		"iconSize": [pinSize, pinSize]
	});
	var marker = L.marker(latlng, {"icon": icon, "opacity": 1}).addTo(map);

	var pinCSS = $.extend({
		"opacity": "0.5",
		"border-radius": "50%", // make it a cirlce
		"background": app.config.get("presentation.pinBodyColor"),
		"border": "1px solid " + app.config.get("presentation.pinBorderColor"),
		"width": pinSize,
		"height": pinSize
	}, app._getPinTransitionCSS("scale(0)", "0"));
	var circle = $('

').css(pinCSS);
	$(marker._icon).append(circle);

	// timeout is used to init CSS transition
	setTimeout(function() {
		var duration = app.config.get("presentation.pinShowSpeed");
		circle.css(app._getPinTransitionCSS("scale(1)", duration));
	}, 50);

	// wait a given number of ms and fade out
	setTimeout(function() {
		var duration = app.config.get("presentation.pinFadeOutSpeed");
		circle.css(app._getPinTransitionCSS("scale(0)", duration));

		// remove marker as soon as an animation is over to avoid memory leaks
		setTimeout(function() {
			map.removeLayer(marker);
		}, parseInt(app.config.get("presentation.pinFadeOutSpeed")));

	}, parseInt(app.config.get("presentation.pinDisplayTime")));
};

socialmap.methods._renderMap = function() {
	var app = this,
		map,
		mapView = app.view.get("map"),
		timeout = app.config.get("viewportChangeTimeout");

	var _viewportResizeHandler = function() {
		var width = mapView.width();
		var zoom = app._getMapZoom(width);
		app.set("zoom", zoom);
		mapView.height(app._getMapHeight(width));
		if (map) {
			map.setView(app.get("geo.center"), zoom, {"reset": true});
		}
	};

	// store debounced version of the function
	this._viewportResizeHandler = Echo.Utils.debounce(_viewportResizeHandler, timeout);

	// trigger zoom/height calculations
	_viewportResizeHandler();

	$(window).bind("resize", this._viewportResizeHandler);

	// more information on the options below can be found
	// on Leaflet docs: http://leafletjs.com/reference.html
	map = new L.map(mapView.get(0), {
		"center": app.get("geo.center"),
		"zoom": app.get("zoom"),
		"minZoom": 0.1,
		"maxZoom": 5,
		"dragging": false,
		"touchZoom": false,
		"scrollWheelZoom": false,
		"doubleClickZoom": false,
		"boxZoom": false,
		"keyboard": false,
		"zoomControl": false,
		"trackResize": true,
		"attributionControl": false
	});
	L.geoJson(this.get("geo.json"), {
		"style": {
			"fillColor": app.config.get("presentation.mapColor"),
			"fillOpacity": 1,
			"stroke": true,
			"color": "#FFFFFF",
			"weight": 1,
			"opacity": 1
		}
	}).addTo(map);

	app.set("map", map);
};

socialmap.css = '.{class} .{class:map} { width: 100%; min-width: 300px; background: inherit; }';

Echo.App.create(socialmap);

})(Echo.jQuery);
<|endoftext|>


<commit_before>//https://medium.com/@jeffandersen/building-a-node-js-rest-api-with-express-46b0901f29b6
var express = require('express');
var mysql = require("mysql");
var app = express();
var pool = mysql.createPool({
    connectionLimit : 100,
    host : 'localhost',
    user: 'root',
    password : '',
    database : 'delivery',
    debug : false
});

var appRouter = express.Router();

categoryRouter.get('/', function(req, res) {});
//categoryRouter.get('/:id', function(req, res) {});
//categoryRouter.post('/', function(req, res) {});

// Attach the routers for their respective paths
app.use('/categories', appRouter);

function categoryLookup(req, res, next) {
    pool.getConnection(function(err, connection) {
        if(err) {
            res.json({
                "code" : 100,
                "status" : "Error in connection database"
            });
            //return;
        }
        console.log("Connected as id " + connection.threadId);
        connection.query("SELECT * from categories", function(err, rows) {
            connection.release();
            if(!err) {
                res.json(rows);
            }
        });
        connection.on('error', function(err) {
            res.json({
                "code" : 100,
                "status" : "Error in connection database"
            });
        });
    });
}

module.exports = app;<commit_msg>server2<commit_after>//https://medium.com/@jeffandersen/building-a-node-js-rest-api-with-express-46b0901f29b6
var express = require('express');
var mysql = require("mysql");
var app = express();
var pool = mysql.createPool({
    connectionLimit : 100,
    host : 'localhost',
    user: 'root',
    password : '',
    database : 'delivery',
    debug : false
});

app.use(function(res, req, next) {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
    res.header('Access-Control-Allow-Headers', 'Content-Type');
    next();
});
var categoryRouter = express.Router();

categoryRouter.get('/', categoryList, function(req, res) {});
//categoryRouter.get('/:id', function(req, res) {});
//categoryRouter.post('/', function(req, res) {});
app.use('/categories', categoryRouter);

function categoryList(req, res, next) {
    pool.getConnection(function(err, connection) {
        if(err) {
            res.json({
                "code" : 100,
                "status" : "Error in connection database"
            });
            //return;
        }
        console.log("Connected as id " + connection.threadId);
        connection.query("SELECT * from categories", function(err, rows) {
            connection.release();
            if(!err) {
                res.json(rows);
            }
        });
        connection.on('error', function(err) {
            res.json({
                "code" : 100,
                "status" : "Error in connection database"
            });
        });
    });

}

module.exports = app;

app.listen(3000);<|endoftext|>


<commit_before>const jsonServer = require('json-server')
const clone = require('clone')
const data = require('../data.json')
const url = require('url');

const app = jsonServer.create()
const router = jsonServer.router(clone(data))

app.use((req, res, next) => {
  if (req.path === '/') return next()
  router.db.setState(clone(data))
  next()
})

app.use(jsonServer.defaults({
  logger: process.env.NODE_ENV !== 'production'
}))

app.use(router)

filterFields = (obj, fields) => {
  Object.keys(obj).forEach((prop) => {
    if (!fields.includes(prop)) {
      delete obj[prop]
    }
  })
  return obj
}

router.render = (req, res) => {
  const defaultLimit = 50
  const maxLimit = 250
  var data = res.locals.data
  var query = url.parse(req.originalUrl, true).query
  if (query.fields) {
    var fields = query.fields.split(',')
    if (data.constructor === Array) {
      data = data.map((obj) => filterFields(obj, fields))
    }
    if (data.constructor === Object) {
      data = filterFields(data, fields)
    }
  }
  if (query.limit) {
    if (data.constructor === Array) {
      data.length = Math.min(Math.min(query.limit, data.length), maxLimit)
    }
  } else {
    data.length = Math.min(defaultLimit, data.length)
  }
  res.jsonp(data)
}

module.exports = app
<commit_msg>Prevent from modifying data.length property in the event a single resource is requested to avoid mutating object property<commit_after>const jsonServer = require('json-server')
const clone = require('clone')
const data = require('../data.json')
const url = require('url');

const app = jsonServer.create()
const router = jsonServer.router(clone(data))

app.use((req, res, next) => {
  if (req.path === '/') return next()
  router.db.setState(clone(data))
  next()
})

app.use(jsonServer.defaults({
  logger: process.env.NODE_ENV !== 'production'
}))

app.use(router)

filterFields = (obj, fields) => {
  Object.keys(obj).forEach((prop) => {
    if (!fields.includes(prop)) {
      delete obj[prop]
    }
  })
  return obj
}

router.render = (req, res) => {
  const defaultLimit = 50
  const maxLimit = 250
  var data = res.locals.data
  var query = url.parse(req.originalUrl, true).query
  if (query.fields) {
    var fields = query.fields.split(',')
    if (data.constructor === Array) {
      data = data.map((obj) => filterFields(obj, fields))
    }
    if (data.constructor === Object) {
      data = filterFields(data, fields)
    }
  }
  if (query.limit) {
    if (data.constructor === Array) {
      data.length = Math.min(Math.min(query.limit, data.length), maxLimit)
    }
  }
  res.jsonp(data)
}

module.exports = app
<|endoftext|>


<commit_before>var http = require('http');
var fs = require('fs');

// Chargement du fichier index.html affiché au client
var server = http.createServer(function (req, res) {
	fs.readFile('./index.html', 'utf-8', function (error, content) {
		res.writeHead(200, {"Content-Type": "text/html"});
		res.end(content);
	});
});

// Chargement de socket.io
var io = require('socket.io').listen(server);

// Quand un client se connecte, on le note dans la console
io.sockets.on('connection', function (socket) {
	console.log('Un client est connecté !');

	socket.on('message', function (message) {
		console.log('message du client ' + message);
	});

	socket.on('new-game', function (newgame) {
		for(var champs in newgame){
			console.log(newgame[champs]);
		}
		socket.emit('after-create-game', "Tu peux jouer");
	})
});

// Quand un client se connecte, on le note dans la console

server.listen(8081);<commit_msg>feat(bdd-js) : Possibilité de trier les parties et d'en créer<commit_after>var http = require('http');
var fs = require('fs');

// Chargement du fichier index.html affiché au client
var server = http.createServer(function (req, res) {
	fs.readFile('./index.html', 'utf-8', function (error, content) {
		res.writeHead(200, {"Content-Type": "text/html"});
		res.end(content);
	});
});

// Chargement de socket.io
var io = require('socket.io').listen(server);

const Sequelize = require('sequelize');
const sequelize = new Sequelize('projectGame', 'adrien', 'Mecaplast062SQL', {
	host: 'localhost',
	dialect: 'mysql',
	define: {
		timestamps: false
	},
	pool: {
		max: 5,
		min: 0,
		idle: 10000
	},
});
const Op = Sequelize.Op;

const Parties = sequelize.define('parties', {
	jeu: {
		type: Sequelize.STRING
	},
	id_utilisateur1:{
		type: Sequelize.INTEGER
	},
	id_utilisateur2:{
		type: Sequelize.INTEGER
	},
	status: {
		type: Sequelize.STRING
	},
	date: {
		type: Sequelize.DATE,
		defaultValue: Sequelize.NOW
	},
	nom: {
		type: Sequelize.STRING
	}
});

io.sockets.on('connection', function (socket) {
	console.log('Un client est connecté !');

	socket.on('message', function (message) {
		console.log('message du client ' + message);
	});

	socket.on('new-game', function (newgame) {
		for(let champs in newgame){
			if(newgame[champs] === ""){
				socket.emit('error-empty-field', champs);
				return;
			}
			console.log(newgame[champs] + " " + champs);
		}

		// Table created
		Parties.create({
				nom: newgame["nom"],
				id_utilisateur1: newgame['id_utilisateur1'],
				jeu: newgame['jeu'],
				status: 'En attente',
				id_utilisateur2: null
		});

		//socket.emit('after-create-game');
	});

	socket.on('fetch-game', function (type) {
		if(typeof type === 'undefined'){
			type = 'Toutes;'
		}
		if(type === "Toutes"){
			Parties.findAll({
				where: {
					[Op.or] : [{Status: 'En attente'}, {Status: 'En cours'}]
				},
				raw: true,
			}).then(function(parties){socket.emit('a-game', parties);});
		}
		else{
			Parties.findAll({
				where: {
					Status: type
				},
				raw: true,
			}).then(function(parties){socket.emit('a-game', parties);});
		}
	});
});

sequelize
	.authenticate()
	.then(() => {
		console.log('Connection has been established successfully.');
	})
	.catch(err => {
		console.error('Unable to connect to the database:', err);
	});

server.listen(8081);

<|endoftext|>