<commit_before>/*
 * Copyright (c) 2014 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/* global _spaces, console */

define(function (require, exports, module) {
    "use strict";

    var EventEmitter = require("eventEmitter"),
        util = require("./util"),
        Promise = require("bluebird");

    /**
     * Promisified version of low-level os functions
     */
    var _os = Promise.promisifyAll(_spaces.os);

    /**
     * Promisified version of low-level keyboard focus functions
     */
    var _keyboardFocus = Promise.promisifyAll(_spaces.os.keyboardFocus);

    /**
     * Promisified version of low-level keyboard focus functions
     */
    var _clipboard = Promise.promisifyAll(_spaces.os.clipboard);

    /**
     * The OS object provides helper methods for dealing with operating
     * system by way of Photoshop.
     *
     * @extends EventEmitter
     * @constructor
     * @private
     */
    var OS = function () {
        EventEmitter.call(this);
    };
    util.inherits(OS, EventEmitter);

    /**
     * OS notifier kinds
     *
     * @const
     * @type{Object.<string, number>}
     */
    OS.prototype.notifierKind = _os.notifierKind;

    /**
     * OS event kinds
     *
     * @const
     * @type{Object.<string, number>}
     */
    OS.prototype.eventKind = _os.eventKind;

    /**
     * OS event modifiers
     *
     * @const
     * @type{Object.<string, number>}
     */
    OS.prototype.eventModifiers = _os.eventModifiers;

    /**
     * OS event keyCodes
     *
     * @const
     * @type{Object.<string, number>}
     */
    OS.prototype.eventKeyCode = _os.eventKeyCode;

    /**
     * Event handler for events from the native bridge.
     *
     * @private
     * @param {*=} err Error information
     * @param {String} event Name of the event
     * @param {*} payload
     */
    OS.prototype._eventHandler = function (err, event, payload) {
        if (err) {
            // TODO: emit an error event, as in adapter.js
            console.error("Failed to handle OS event: " + err);
            return;
        }

        this.emit("all", event, payload);
        this.emit(event, payload);
    };

    /**
     * Determine whether or not CEF currently has keyboard focus.
     *
     * @param {object=} options Options passed directly to the low-level call
     * @return {Promise} Resolves once the status of focus has been determined.
     */
    OS.prototype.hasKeyboardFocus = function (options) {
        options = options || {};

        return _keyboardFocus.isActiveAsync(options);
    };

    /**
     * Request that keyboard focus be transferred from Photoshop to CEF.
     *
     * @param {object=} options Options passed directly to the low-level aquire call
     * @return {Promise} Resolves once focus has been transferred.
     */
    OS.prototype.acquireKeyboardFocus = function (options) {
        options = options || {};

        return _keyboardFocus.acquireAsync(options);
    };

    /**
     * Request that keyboard focus be transferred from CEF to Photoshop.
     *
     * @param {object=} options Options passed directly to the low-level release call
     * @return {Promise} Resolves once focus has been transferred.
     */
    OS.prototype.releaseKeyboardFocus = function (options) {
        options = options || {};

        return _keyboardFocus.releaseAsync(options);
    };

    /**
     * @return {Promise}
     */
    OS.prototype.postEvent = function (eventInfo, options) {
        options = options || {};

        return _os.postEventAsync(eventInfo, options);
    };

    /**
     * @param {Array.<string>=} formats
     * @return {Promise.<{data: *, format: string}>}
     */
    OS.prototype.clipboardRead = function (formats) {
        var options = {
            formats: formats || ["string"]
        };

        return _clipboard.readAsync(options);
    };

    /**
     * @param {*} data
     * @param {string=} format
     * @return {Promise}
     */
    OS.prototype.clipboardWrite = function (data, format) {
        var options = {
            data: data,
            format: format || "string"
        };

        return _clipboard.writeAsync(options);
    };

    /**
     * Set the tooltip label, or invalidate the tooltip if the label is empty.
     *
     * @param {string} label
     * @return {Promise}
     */
    OS.prototype.setTooltip = function (label) {
        return _os.setTooltipAsync({
            label: label
        });
    };

    /**
     * Resets the mouse cursor, letting it catch up without a mouse move event
     *
     * @param {object} options Currently unused
     * @return {Promise}
     */
    OS.prototype.resetCursor = function (options) {
        options = options || {};
        return _os.resetCursorAsync(options);
    };

    /**
     * Gets a temporary file location, with a given name if provided
     *
     * @param {string?} name File name for the temporary file
     *
     * @return {Promise.<string>} Resolves to the temporary path
     */
    OS.prototype.getTempFilename = function (name) {
        var options = {
            name: name || ""
        };

        return _os.getTempFilenameAsync(options);
    };

    /**
     * The OS singleton
     * @type {OS}
     */
    var theOS = new OS();

    // bind native phtooshop event handler to our handler function
    _spaces.setNotifier(_spaces.notifierGroup.OS, {}, theOS._eventHandler.bind(theOS));

    module.exports = theOS;
});
<commit_msg>JSDoc fix.<commit_after>/*
 * Copyright (c) 2014 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/* global _spaces, console */

define(function (require, exports, module) {
    "use strict";

    var EventEmitter = require("eventEmitter"),
        util = require("./util"),
        Promise = require("bluebird");

    /**
     * Promisified version of low-level os functions
     */
    var _os = Promise.promisifyAll(_spaces.os);

    /**
     * Promisified version of low-level keyboard focus functions
     */
    var _keyboardFocus = Promise.promisifyAll(_spaces.os.keyboardFocus);

    /**
     * Promisified version of low-level keyboard focus functions
     */
    var _clipboard = Promise.promisifyAll(_spaces.os.clipboard);

    /**
     * The OS object provides helper methods for dealing with operating
     * system by way of Photoshop.
     *
     * @extends EventEmitter
     * @constructor
     * @private
     */
    var OS = function () {
        EventEmitter.call(this);
    };
    util.inherits(OS, EventEmitter);

    /**
     * OS notifier kinds
     *
     * @const
     * @type{Object.<string, number>}
     */
    OS.prototype.notifierKind = _os.notifierKind;

    /**
     * OS event kinds
     *
     * @const
     * @type{Object.<string, number>}
     */
    OS.prototype.eventKind = _os.eventKind;

    /**
     * OS event modifiers
     *
     * @const
     * @type{Object.<string, number>}
     */
    OS.prototype.eventModifiers = _os.eventModifiers;

    /**
     * OS event keyCodes
     *
     * @const
     * @type{Object.<string, number>}
     */
    OS.prototype.eventKeyCode = _os.eventKeyCode;

    /**
     * Event handler for events from the native bridge.
     *
     * @private
     * @param {*=} err Error information
     * @param {String} event Name of the event
     * @param {*} payload
     */
    OS.prototype._eventHandler = function (err, event, payload) {
        if (err) {
            // TODO: emit an error event, as in adapter.js
            console.error("Failed to handle OS event: " + err);
            return;
        }

        this.emit("all", event, payload);
        this.emit(event, payload);
    };

    /**
     * Determine whether or not CEF currently has keyboard focus.
     *
     * @param {object=} options Options passed directly to the low-level call
     * @return {Promise} Resolves once the status of focus has been determined.
     */
    OS.prototype.hasKeyboardFocus = function (options) {
        options = options || {};

        return _keyboardFocus.isActiveAsync(options);
    };

    /**
     * Request that keyboard focus be transferred from Photoshop to CEF.
     *
     * @param {object=} options Options passed directly to the low-level aquire call
     * @return {Promise} Resolves once focus has been transferred.
     */
    OS.prototype.acquireKeyboardFocus = function (options) {
        options = options || {};

        return _keyboardFocus.acquireAsync(options);
    };

    /**
     * Request that keyboard focus be transferred from CEF to Photoshop.
     *
     * @param {object=} options Options passed directly to the low-level release call
     * @return {Promise} Resolves once focus has been transferred.
     */
    OS.prototype.releaseKeyboardFocus = function (options) {
        options = options || {};

        return _keyboardFocus.releaseAsync(options);
    };

    /**
     * @return {Promise}
     */
    OS.prototype.postEvent = function (eventInfo, options) {
        options = options || {};

        return _os.postEventAsync(eventInfo, options);
    };

    /**
     * @param {Array.<string>=} formats
     * @return {Promise.<{data: *, format: string}>}
     */
    OS.prototype.clipboardRead = function (formats) {
        var options = {
            formats: formats || ["string"]
        };

        return _clipboard.readAsync(options);
    };

    /**
     * @param {*} data
     * @param {string=} format
     * @return {Promise}
     */
    OS.prototype.clipboardWrite = function (data, format) {
        var options = {
            data: data,
            format: format || "string"
        };

        return _clipboard.writeAsync(options);
    };

    /**
     * Set the tooltip label, or invalidate the tooltip if the label is empty.
     *
     * @param {string} label
     * @return {Promise}
     */
    OS.prototype.setTooltip = function (label) {
        return _os.setTooltipAsync({
            label: label
        });
    };

    /**
     * Resets the mouse cursor, letting it catch up without a mouse move event
     *
     * @param {object} options Currently unused
     * @return {Promise}
     */
    OS.prototype.resetCursor = function (options) {
        options = options || {};
        return _os.resetCursorAsync(options);
    };

    /**
     * Gets a temporary file location, with a given name if provided
     *
     * @param {string=} name File name for the temporary file
     *
     * @return {Promise.<string>} Resolves to the temporary path
     */
    OS.prototype.getTempFilename = function (name) {
        var options = {
            name: name || ""
        };

        return _os.getTempFilenameAsync(options);
    };

    /**
     * The OS singleton
     * @type {OS}
     */
    var theOS = new OS();

    // bind native phtooshop event handler to our handler function
    _spaces.setNotifier(_spaces.notifierGroup.OS, {}, theOS._eventHandler.bind(theOS));

    module.exports = theOS;
});
<|endoftext|>


<commit_before>const turf = require('@turf/turf');

class Cylinder {
  constructor(center, radius) {
    this.center = center;
    this.radius = radius;
  }

  checkEnter(c1, c2) {
    if (!this.isInside(c2) || this.isInside(c1))
      return;

    let intersection = turf.lineIntersect(turf.circle(this.center, this.radius / 1000), turf.lineString([c1, c2]));

    return intersection.features[0];
  }

  isInside(coordinate) {
    let distance = turf.distance(coordinate, this.center);
    return distance <= this.radius / 1000;
  }
}

class Line {
  constructor(center, length) {
    this.center = center;
    this.length = length;
  }

  update() {
    let p1 = turf.destination(this.center, this.length / 2000, this.bearing + 90);
    let p2 = turf.destination(this.center, this.length / 2000, this.bearing - 90);

    this.coordinates = [p1.geometry.coordinates, p2.geometry.coordinates];
  }

  checkEnter(c1, c2) {
    let intersection = turf.lineIntersect(turf.lineString(this.coordinates), turf.lineString([c1, c2]));
    if (intersection.features.length === 0)
      return;

    let bearing = turf.bearing(c1, c2);
    let bearingDiff = turf.bearingToAngle(this.bearing - bearing);
    if (bearingDiff > 90 && bearingDiff < 270)
      return;

    return intersection.features[0];
  }
}

module.exports = { Cylinder, Line };
<commit_msg>Cylinder: Fix checkEnter() method<commit_after>const turf = require('@turf/turf');

class Cylinder {
  constructor(center, radius) {
    this.center = center;
    this.radius = radius;
  }

  checkEnter(c1, c2) {
    let intersection = turf.lineIntersect(turf.circle(this.center, this.radius / 1000), turf.lineString([c1, c2]));
    if (intersection.features.length === 0)
      return;

    if (this.isInside(c2))
      return intersection.features[0];
  }

  isInside(coordinate) {
    let distance = turf.distance(coordinate, this.center);
    return distance <= this.radius / 1000;
  }
}

class Line {
  constructor(center, length) {
    this.center = center;
    this.length = length;
  }

  update() {
    let p1 = turf.destination(this.center, this.length / 2000, this.bearing + 90);
    let p2 = turf.destination(this.center, this.length / 2000, this.bearing - 90);

    this.coordinates = [p1.geometry.coordinates, p2.geometry.coordinates];
  }

  checkEnter(c1, c2) {
    let intersection = turf.lineIntersect(turf.lineString(this.coordinates), turf.lineString([c1, c2]));
    if (intersection.features.length === 0)
      return;

    let bearing = turf.bearing(c1, c2);
    let bearingDiff = turf.bearingToAngle(this.bearing - bearing);
    if (bearingDiff > 90 && bearingDiff < 270)
      return;

    return intersection.features[0];
  }
}

module.exports = { Cylinder, Line };
<|endoftext|>


<commit_before>(function(){
  "use strict";

  const d = document;

  function createDiv(className) {
    const div = d.createElement('div');
    div.className = className;
    return div;
  }

  function appendDiv(node, className) {
    return node.appendChild(createDiv(className));
  }

  function createNode(text) {
    const node = createDiv('Node collapsed');
    appendDiv(node, 'Expander');
    const content = appendDiv(node, 'Content');
    content.contentEditable = 'true';
    if(text) content.textContent = text;
    appendDiv(node, 'Children');
    return node;
  }

  function expanderElement(node) { return node.firstChild; }
  function contentElement(node) { return node.children[1]; }
  function childrenElement(node) { return node.lastChild; }
  function parentNode(node) {
    node = node.parentElement;
    if(node && node.classList.contains('Children')) return node.parentElement;
    return null;
  }

  function isExpanded(node) { return node.classList.contains('expanded'); }
  function swapClass(node, oldClass, newClass) {
    node.classList.remove(oldClass);
    node.classList.add(newClass);
  }
  function expand(node) { swapClass(node, 'collapsed', 'expanded'); }
  function collapse(node) { swapClass(node, 'expanded', 'collapsed'); }
  function toggle(node) { (isExpanded(node)?collapse:expand)(node); }

  function focusContent(node) {
    if(node) contentElement(node).focus();
  }

  function lastVisibleLeaf(node) {
    while(node && isExpanded(node)) {
      var lastChild = childrenElement(node).lastChild;
      if(lastChild) node = lastChild;
    }
    return node;
  }

  function focusPrevContent(node) {
    var prev = node.previousSibling;
    if(prev) prev = lastVisibleLeaf(prev);
    else prev = parentNode(node);
    focusContent(prev);
  }

  function focusNextContent(node) {
    var next = isExpanded(node) ? childrenElement(node).firstChild : node.nextSibling;
    focusContent(next);
  }

  function appendSibling(node, sibling) {
    return node.insertAdjacentElement('afterEnd', sibling);
  }

  function prependSibling(node, sibling) {
    return node.insertAdjacentElement('beforeBegin', sibling);
  }

  function appendChild(node, child) {
    return childrenElement(node).appendChild(child);
  }

  function appendNewSibling(node) {
    focusContent(appendSibling(node, createNode()));
  }

  function indent(node) {
    const prev = node.previousSibling;
    if(prev) {
      expand(prev);
      appendChild(prev, node);
      focusContent(node);
    }
  }

  function outdent(node) {
    const parent = parentNode(node);
    if(parent) {
      appendSibling(parent, node);
      focusContent(node);
    }
  }

  function moveUp(node) {
    const prev = node.previousSibling || parentNode(node);
    if(prev) {
      prependSibling(prev, node);
      focusContent(node);
    }
  }

  function moveDown(node) {
    const next = node.nextSibling || parentNode(node);
    if(next) {
      appendSibling(next, node);
      focusContent(node);
    }
  }

  const outlinerElement = d.getElementById('outliner');
  focusContent(outlinerElement.appendChild(createNode('Test')));

  outlinerElement.addEventListener('click', e => {
    if(e.target.classList.contains('Expander')) {
      toggle(e.target.parentElement);
    }
  });

  const MOD_NONE = 0, MOD_CTRL = 1, MOD_ALT = 2, MOD_SHIFT = 4, MOD_META = 8;
  function getModKeys(e) {
    var mod = MOD_NONE;
    if(e.ctrlKey) mod += MOD_CTRL;
    if(e.altKey) mod += MOD_ALT;
    if(e.shiftKey) mod += MOD_SHIFT;
    if(e.metaKey) mod += MOD_META;
    return mod;
  }

  outlinerElement.addEventListener('keydown', e => {
    const node = e.target.parentElement;
    switch(e.key) {
      case 'Enter':
      if(getModKeys(e) == MOD_NONE) { appendNewSibling(node); e.preventDefault(); }
      break;

      case 'Tab':
      e.preventDefault();
      switch(getModKeys(e)) {
        case MOD_NONE: indent(node); break;
        case MOD_SHIFT: outdent(node); break;
      }
      break;

      case 'ArrowUp':
      switch(getModKeys(e)) {
        case MOD_ALT: moveUp(node); e.preventDefault(); break;
        case MOD_CTRL: focusPrevContent(node); e.preventDefault();break;
      }
      break;

      case 'ArrowDown':
      switch(getModKeys(e)) {
        case MOD_ALT: moveDown(node); e.preventDefault(); break;
        case MOD_CTRL: focusNextContent(node); e.preventDefault();break;
      }
      break;

      case 'ArrowLeft':
      if(getModKeys(e) == MOD_ALT) { outdent(node); e.preventDefault(); }
      break;

      case 'ArrowRight':
      if(getModKeys(e) == MOD_ALT) { indent(node); e.preventDefault(); }
      break;
    }
  });
}())<commit_msg>Bug fix<commit_after>(function(){
  "use strict";

  const d = document;

  function createDiv(className) {
    const div = d.createElement('div');
    div.className = className;
    return div;
  }

  function appendDiv(node, className) {
    return node.appendChild(createDiv(className));
  }

  function createNode(text) {
    const node = createDiv('Node collapsed');
    appendDiv(node, 'Expander');
    const content = appendDiv(node, 'Content');
    content.contentEditable = 'true';
    if(text) content.textContent = text;
    appendDiv(node, 'Children');
    return node;
  }

  function expanderElement(node) { return node.firstChild; }
  function contentElement(node) { return node.children[1]; }
  function childrenElement(node) { return node.lastChild; }
  function parentNode(node) {
    node = node.parentElement;
    if(node && node.classList.contains('Children')) return node.parentElement;
    return null;
  }

  function isExpanded(node) { return node.classList.contains('expanded'); }
  function swapClass(node, oldClass, newClass) {
    node.classList.remove(oldClass);
    node.classList.add(newClass);
  }
  function expand(node) { swapClass(node, 'collapsed', 'expanded'); }
  function collapse(node) { swapClass(node, 'expanded', 'collapsed'); }
  function toggle(node) { (isExpanded(node)?collapse:expand)(node); }

  function focusContent(node) {
    if(node) contentElement(node).focus();
  }

  function lastVisibleLeaf(node) {
    while(node && isExpanded(node)) {
      var lastChild = childrenElement(node).lastChild;
      if(lastChild) node = lastChild;
    }
    return node;
  }

  function focusPrevContent(node) {
    var prev = node.previousSibling;
    if(prev) prev = lastVisibleLeaf(prev);
    else prev = parentNode(node);
    focusContent(prev);
  }

  function focusNextContent(node) {
    var next = isExpanded(node) ? childrenElement(node).firstChild : node.nextSibling;
    if(!next) {
      const parent = parentNode(node);
      if(parent) next = parent.nextSibling;
    }
    focusContent(next);
  }

  function appendSibling(node, sibling) {
    return node.insertAdjacentElement('afterEnd', sibling);
  }

  function prependSibling(node, sibling) {
    return node.insertAdjacentElement('beforeBegin', sibling);
  }

  function appendChild(node, child) {
    return childrenElement(node).appendChild(child);
  }

  function appendNewSibling(node) {
    focusContent(appendSibling(node, createNode()));
  }

  function indent(node) {
    const prev = node.previousSibling;
    if(prev) {
      expand(prev);
      appendChild(prev, node);
      focusContent(node);
    }
  }

  function outdent(node) {
    const parent = parentNode(node);
    if(parent) {
      appendSibling(parent, node);
      focusContent(node);
    }
  }

  function moveUp(node) {
    const prev = node.previousSibling || parentNode(node);
    if(prev) {
      prependSibling(prev, node);
      focusContent(node);
    }
  }

  function moveDown(node) {
    const next = node.nextSibling || parentNode(node);
    if(next) {
      appendSibling(next, node);
      focusContent(node);
    }
  }

  const outlinerElement = d.getElementById('outliner');
  focusContent(outlinerElement.appendChild(createNode('Test')));

  outlinerElement.addEventListener('click', e => {
    if(e.target.classList.contains('Expander')) {
      toggle(e.target.parentElement);
    }
  });

  const MOD_NONE = 0, MOD_CTRL = 1, MOD_ALT = 2, MOD_SHIFT = 4, MOD_META = 8;
  function getModKeys(e) {
    var mod = MOD_NONE;
    if(e.ctrlKey) mod += MOD_CTRL;
    if(e.altKey) mod += MOD_ALT;
    if(e.shiftKey) mod += MOD_SHIFT;
    if(e.metaKey) mod += MOD_META;
    return mod;
  }

  outlinerElement.addEventListener('keydown', e => {
    const node = e.target.parentElement;
    switch(e.key) {
      case 'Enter':
      if(getModKeys(e) == MOD_NONE) { appendNewSibling(node); e.preventDefault(); }
      break;

      case 'Tab':
      e.preventDefault();
      switch(getModKeys(e)) {
        case MOD_NONE: indent(node); break;
        case MOD_SHIFT: outdent(node); break;
      }
      break;

      case 'ArrowUp':
      switch(getModKeys(e)) {
        case MOD_ALT: moveUp(node); e.preventDefault(); break;
        case MOD_CTRL: focusPrevContent(node); e.preventDefault();break;
      }
      break;

      case 'ArrowDown':
      switch(getModKeys(e)) {
        case MOD_ALT: moveDown(node); e.preventDefault(); break;
        case MOD_CTRL: focusNextContent(node); e.preventDefault();break;
      }
      break;

      case 'ArrowLeft':
      if(getModKeys(e) == MOD_ALT) { outdent(node); e.preventDefault(); }
      break;

      case 'ArrowRight':
      if(getModKeys(e) == MOD_ALT) { indent(node); e.preventDefault(); }
      break;
    }
  });
}())<|endoftext|>


<commit_before>var extend = require('xtend')
var getFormData = require('form-data-set/element')

var ENTER = 13

module.exports = SubmitSinkHandler

function SubmitSinkHandler(sink, data) {
    if (!(this instanceof SubmitSinkHandler)) {
        return new SubmitSinkHandler(sink, data)
    }

    this.sink = sink
    this.data = data
    this.id = sink.id
    this.type = 'submit'
}

SubmitSinkHandler.prototype.handleEvent = handleEvent

function handleEvent(ev) {
    var target = ev.target

    var isValid =
        (ev.type === 'click' && target.tagName === 'BUTTON') ||
        (
            (target.type === 'text' || target.tagName === 'TEXTAREA') &&
            (ev.keyCode === ENTER && !ev.shiftKey && ev.type === 'keyup') &&
            (target.value.trim() !== '')
        )

    if (!isValid) {
        return
    }

    var value = getFormData(ev.currentTarget)
    var data = extend(value, this.data)

    if (typeof this.sink === 'function') {
        this.sink(data)
    } else {
        this.sink.write(data)
    }
}
<commit_msg>remove gaurd against empty string<commit_after>var extend = require('xtend')
var getFormData = require('form-data-set/element')

var ENTER = 13

module.exports = SubmitSinkHandler

function SubmitSinkHandler(sink, data) {
    if (!(this instanceof SubmitSinkHandler)) {
        return new SubmitSinkHandler(sink, data)
    }

    this.sink = sink
    this.data = data
    this.id = sink.id
    this.type = 'submit'
}

SubmitSinkHandler.prototype.handleEvent = handleEvent

function handleEvent(ev) {
    var target = ev.target

    var isValid =
        (ev.type === 'click' && target.tagName === 'BUTTON') ||
        (
            (target.type === 'text' || target.tagName === 'TEXTAREA') &&
            (ev.keyCode === ENTER && !ev.shiftKey && ev.type === 'keyup')
        )

    if (!isValid) {
        return
    }

    var value = getFormData(ev.currentTarget)
    var data = extend(value, this.data)

    if (typeof this.sink === 'function') {
        this.sink(data)
    } else {
        this.sink.write(data)
    }
}
<|endoftext|>


<commit_before>/**
 * Created by Doron Sinai on 23/10/2014.
 */
var mongoose = require('mongoose');
var request = require('request');
var xml2js = require('xml2js');
var moment = require('moment');
var Agenda = require('agenda');
var async = require('async');
var fs = require('fs');
var _ = require('lodash');
var jsdom = require("jsdom").jsdom;
var config = require('config');
var Q = require('q');
var downloadFolder = config.get('downloadFolder');
var mkdirp = require("mkdirp");
var winston = require('winston');
require('./winston-config.js');
require("./db.js");
var mailer = require('./mailer.js');
var updater = require('./updater.js');
var debugLogger = winston.loggers.get('all');
var errorLogger = winston.loggers.get('terror');
var parser = xml2js.Parser({
    explicitArray: false,
    normalizeTags: true
});
var apiKey = config.get("apiKey"); //API key for the tv db
var User = mongoose.model('User');
var Show = mongoose.model('Show');
var Episode = mongoose.model('Episode');

function checkAndCreateFolder(folder){
    if(fs.existsSync(folder)){
        return true;
    }
    else{
        fs.mkdirSync(folder);
        if(fs.existsSync(folder)){
            return true;
        }
        return false;
    }
}

function downloadFile(url, folder, defer, fileName){
    var ret = [];
    var len = 0;
    var zlib = require("zlib");
    debugLogger.info('Going to download from the following URL: ' + url);
    debugLogger.info('and save it in this folder: ' + folder);
    request({
        url:url,
        gzip:true,
        headers: {
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
            "Referer":url
        }
    }).on('response', function(res){
        var path = res.request.uri.path.split('/');

        debugLogger.info('Giving it this filename: ' + fileName);

        res.on('data', function (data) {
            ret.push(data);
            len += data.length;
        });

        res.on('end', function(){
            var buffer = Buffer.concat(ret, len);
            var encoding = res.headers['content-encoding'];
            var folderName = downloadFolder + folder + '/';
            mkdirp(folderName,function(err){
                if(err){
                    errorLogger.error('Can not create folders');
                    defer.reject("Error creating folders");
                    return;
                }
                var wstream = fs.createWriteStream(folderName + fileName);
                if (encoding == 'gzip') {
                    zlib.gunzip(buffer, function(err, decoded) {
                        wstream.write(decoded);
                        wstream.end();
                    });
                } else if (encoding == 'deflate') {
                    zlib.inflate(buffer, function(err, decoded) {
                        wstream.write(decoded);
                        wstream.end();
                    })
                } else {
                    wstream.write(buffer.toString());
                    wstream.end();
                }
                defer.resolve({
                    status:"sucess",
                    filename:fileName
                });
            });

        });
    }).on('error', function(err) {
        errorLogger.error('Error while trying to save file ' + err);
        defer.reject("Error downloading file");
    });;
}

function requestSeriesPage(url){
    var debugMessagePrefix = '[' + url.name + ' S' + url.season + 'E' + url.number + '] ';
    var finishedRequest = Q.defer();
    var finishedRequestPromise = finishedRequest.promise;
    debugLogger.info(debugMessagePrefix + 'Going to request the page');

    request({
        url: url.url,
        gzip:true
    },  function(error, response, body) {
        debugLogger.info(debugMessagePrefix + 'Got a response back with status: ' + response.statusCode);
        if (!error && response.statusCode == 200) {
            debugLogger.info(debugMessagePrefix + 'About to parse the dom');
            jsdom.env(
                body,
                ["./jquery-2.1.1.min.js"],
                function (errors, window) {
                    if(errors) {
                        errorLogger.error(errors);
                        finishedRequest.reject("JSDOM error - see logs");
                        return;
                    }
                    if(url.number < 10){
                        url.number = '0' + url.number;
                    }
                    if(url.season < 10){
                        url.season = '0' + url.season;
                    }
                    var node = window.$(window.$.find('h3:contains("Season ' + url.season + '")')).next('div').find('span:contains("Episode ' + url.number + '")').parent().attr("onClick");
                    if(node && node.length > 1){
                        var episodeUrlTorrent = node.split('\'')[1];
                        if(episodeUrlTorrent){
                            debugLogger.info(debugMessagePrefix + 'Found the following episode number: ' + episodeUrlTorrent);
                            finishedRequest.resolve('' + episodeUrlTorrent);
                        }
                    }
                    else{
                        finishedRequest.reject("Did not find season in page");
                    }
                }
            );
        }
        else{
            errorLogger.error(error);
            finishedRequest.reject();
        }
    });

    //do it next
    finishedRequestPromise.then(function(episodeNumber){
        requestEpisodeSegment({episodeNumber: episodeNumber, debugMessagePrefix:debugMessagePrefix, name:url.name, deferred:url.deferred});
    }, function(reason){
        errorLogger.error(reason);
        url.deferred.reject(reason);
    }, function(){});  //.then(onFulfilled, onRejected, onProgress)
}

function requestEpisodeSegment(episode){
    var debugMessagePrefix = episode.debugMessagePrefix;
    var url = 'https://kickass.to/media/getepisode/' + episode.episodeNumber + '/';
    var finishedRequest = Q.defer();
    var finishedRequestPromise = finishedRequest.promise;

    debugLogger.info(debugMessagePrefix + 'Getting segment from: ' + url);

    request({
        url: url,
        gzip:true
    },  function(error, response, body) {
        if (!error && response.statusCode == 200) {
            jsdom.env(
                body,
                ["./jquery-2.1.1.min.js"],
                function (errors, window) {
                    if(errors) {
                        errorLogger.error("requestEpisodeSegment - jsdom - " + errors);
                        finishedRequest.reject("JSDOM error - see logs");
                        return;
                    }
                    debugLogger.info(debugMessagePrefix + "Look for x264 files in segment");
                    var node = window.$(window.$.find('tr:contains("x264")')).find(('a[title="Download torrent file"]'));
                    if (node.length < 1){
                        node = window.$.find('a[title="Download torrent file"]');
                        debugLogger.info(debugMessagePrefix + "No x264 torrents found in page");
                        if(node.length < 1){
                            debugLogger.info(debugMessagePrefix + "No torrents found - rejecting defer obj");
                            finishedRequest.reject("No torrents found");
                            return;
                        }
                    }
                    var nodeUrl = node[0].href;
                    finishedRequest.resolve(nodeUrl);
                }
            );
        }
        else{
            errorLogger.error("requestEpisodeSegment - request - " + error);
            finishedRequest.reject();
        }
    });

    //do it next
    finishedRequestPromise.then(function(nodeUrl){
        debugLogger.info(debugMessagePrefix + "Download file from: " + nodeUrl);
        getUelFromTorrentCache(nodeUrl, episode);
    }, function(reason){
        errorLogger.error(reason);
        episode.deferred.reject(reason);
    }, function(){});  //.then(onFulfilled, onRejected, onProgress)
}

function getUelFromTorrentCache(url,episode){
    var debugMessagePrefix = "Get torrent file from torrentCache ";
    var finishedRequest = Q.defer();
    var fileName;
    var finishedRequestPromise = finishedRequest.promise;
    request({
        url: url,
        gzip:true
    },  function(error, response, body) {
        if (!error && response.statusCode == 200) {
            jsdom.env(
                body,
                ["./jquery-2.1.1.min.js"],
                function (errors, window) {
                    if(errors) {
                        errorLogger.error("requestEpisodeSegment - jsdom - " + errors);
                        finishedRequest.reject("JSDOM error - see logs");
                        return;
                    }
                    debugLogger.info(debugMessagePrefix + "Look for torrent phrase");
                    var node = window.$.find('a:contains("torrent file")');
                    var fileUrl = null;
                    node.length > 0 ? fileUrl = window.$.find('a:contains("torrent file")')[0].href : fileUrl = null;
                    if(fileUrl === null){
                        finishedRequest.reject("Did not find url for file");
                        return;
                    }
                    var splittedUrl = fileUrl.split("/");
                    fileName = splittedUrl.pop();
                    fileUrl = splittedUrl.join("/");
                    finishedRequest.resolve(fileUrl+".torrent");
                }
            );
        }
        else{
            errorLogger.error("requestEpisodeSegment - request - " + error);
            finishedRequest.reject("response.statusCode: " + response.statusCode);
        }
    });

    //do it next
    finishedRequestPromise.then(function(nodeUrl){
        debugLogger.info(debugMessagePrefix + "Download file from: " + nodeUrl);
        downloadFile(nodeUrl, episode.name, episode.deferred, fileName);
    }, function(reason){
        errorLogger.error(reason);
        episode.deferred.reject(reason);
    }, function(){});  //.then(onFulfilled, onRejected, onProgress)
}
/********************Set some tasks for later *******************/
var agenda = new Agenda({db: { address: 'localhost:27017/agendaJobs'}});

agenda.define('today shows',function(job,done){
    var downloadDays = config.get('downloadDays');
    if(!downloadDays || isNaN(parseInt(downloadDays))){
        downloadDays = 1;
    }
    debugLogger.info('Number of days back to look for: ' +downloadDays);
    async.waterfall([
        // find relevant episode in the db
        function(callback){
            var start = moment().subtract(downloadDays, 'days').hour(0).minute(0).toDate();
            var end = moment().hour(0).minute(0).toDate();
            debugLogger.info('Look for episode in range: ' + start + ' - ' + end);
            Episode.find({firstAired:{$gte:start, $lt:end}},function(err, results){
                _.each(results, function(r){
                    debugLogger.info('Found the following: ', r.episodeId + ':' + r.episodeName);
                })

                if(err) callback(err);
                if(results.length < 1) {
                    debugLogger.info('No episode found for that date range');
                    callback(111);
                }
                callback(null,results);
            });
        },
        //extract the urls of each show to kickass torrents
        function(episodes, callback){
            var urls = [];
            var showsNumbers = {}
            Show.find({},function(err,shows){
                if(err) callback(err);

                for(var i=0; i <shows.length; i++){
                    var o = {}
                    o.url = shows[i].url;
                    o.name = shows[i].name;
                    showsNumbers[shows[i]._id] = o;
                }

                _.each(episodes,function(episode){
                    var u = {};
                    u.season = episode.season;
                    u.number = episode.episodeNumber;
                    u.url = showsNumbers[episode.showId]["url"];
                    u.name = showsNumbers[episode.showId]["name"];
                    if(u.url && (u.url !== "")){
                        urls.push(u);
                    }

                });
                if(urls.length ===0) callback(222);
                callback(null, urls)
            })
        },
        //go get the torrent files urls
        function(urls, callback){
            var promises = [];
            _.each(urls,function(url){
                console.log(url);
                var deferred = Q.defer();
                promises.push(deferred.promise);
                url.deferred = deferred;
                requestSeriesPage(url);
            });

            Q.allSettled(promises).spread(function(){
                var args = arguments;
                var sucessFiles = [];
                var failoureFiles = [];
                var mailSubject = "";
                var mailBody = "";
                var successString = "Success: \n";
                var failureString = "Failure: \n";
                for(var i=0; i<args.length; i++){
                    var promise = args[i];
                    if(promise.state === "fulfilled"){
                        sucessFiles.push(promise.value.filename);
                        successString += promise.value.filename + "\n"
                    }
                    else{
                        failoureFiles.push(promise.value);
                        failureString += promise.value + "\n";
                    }
                }
                if(failoureFiles.length === 0){
                    mailSubject = sucessFiles.length + " New episodes were downloaded successfully";
                }
                else{
                    mailSubject = sucessFiles.length + " New episodes were downloaded successfully and " + failoureFiles.length + " files had errors";
                }
                mailBody = successString + failureString;
                debugLogger.info('Going to send email: ', mailSubject ,mailBody)
                agenda.now('send mail',{
                    mailBody: mailBody,
                    mailSubject:mailSubject
                });

            }).done();

            callback(null);
        }
    ], function (err, result) {
        if(err){
            if(err === 111){
                debugLogger.info('No episodes were found for today');
            }
            else if(err === 222){
                debugLogger.info('No links found in db');
            }
            else{
                errorLogger.error(err)
            }
        }
        done();
    });
})
mailer(agenda);
updater(agenda);
agenda.start();
agenda.every('24 hours', 'today shows');
agenda.every('24 hours', 'update db');

/********************Set some tasks for later*******************/<commit_msg>fix file name<commit_after>/**
 * Created by Doron Sinai on 23/10/2014.
 */
var mongoose = require('mongoose');
var request = require('request');
var xml2js = require('xml2js');
var moment = require('moment');
var Agenda = require('agenda');
var async = require('async');
var fs = require('fs');
var _ = require('lodash');
var jsdom = require("jsdom").jsdom;
var config = require('config');
var Q = require('q');
var downloadFolder = config.get('downloadFolder');
var mkdirp = require("mkdirp");
var winston = require('winston');
require('./winston-config.js');
require("./db.js");
var mailer = require('./mailer.js');
var updater = require('./updater.js');
var debugLogger = winston.loggers.get('all');
var errorLogger = winston.loggers.get('terror');
var parser = xml2js.Parser({
    explicitArray: false,
    normalizeTags: true
});
var apiKey = config.get("apiKey"); //API key for the tv db
var User = mongoose.model('User');
var Show = mongoose.model('Show');
var Episode = mongoose.model('Episode');

function checkAndCreateFolder(folder){
    if(fs.existsSync(folder)){
        return true;
    }
    else{
        fs.mkdirSync(folder);
        if(fs.existsSync(folder)){
            return true;
        }
        return false;
    }
}

function downloadFile(url, folder, defer, fileName){
    var ret = [];
    var len = 0;
    var zlib = require("zlib");
    debugLogger.info('Going to download from the following URL: ' + url);
    debugLogger.info('and save it in this folder: ' + folder);
    request({
        url:url,
        gzip:true,
        headers: {
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
            "Referer":url
        }
    }).on('response', function(res){
        var path = res.request.uri.path.split('/');

        debugLogger.info('Giving it this filename: ' + fileName);

        res.on('data', function (data) {
            ret.push(data);
            len += data.length;
        });

        res.on('end', function(){
            var buffer = Buffer.concat(ret, len);
            var encoding = res.headers['content-encoding'];
            var folderName = downloadFolder + folder + '/';
            mkdirp(folderName,function(err){
                if(err){
                    errorLogger.error('Can not create folders');
                    defer.reject("Error creating folders");
                    return;
                }
                var wstream = fs.createWriteStream(folderName + fileName);
                if (encoding == 'gzip') {
                    zlib.gunzip(buffer, function(err, decoded) {
                        wstream.write(decoded);
                        wstream.end();
                    });
                } else if (encoding == 'deflate') {
                    zlib.inflate(buffer, function(err, decoded) {
                        wstream.write(decoded);
                        wstream.end();
                    })
                } else {
                    wstream.write(buffer.toString());
                    wstream.end();
                }
                defer.resolve({
                    status:"sucess",
                    filename:fileName
                });
            });

        });
    }).on('error', function(err) {
        errorLogger.error('Error while trying to save file ' + err);
        defer.reject("Error downloading file");
    });;
}

function requestSeriesPage(url){
    var debugMessagePrefix = '[' + url.name + ' S' + url.season + 'E' + url.number + '] ';
    var finishedRequest = Q.defer();
    var finishedRequestPromise = finishedRequest.promise;
    debugLogger.info(debugMessagePrefix + 'Going to request the page');

    request({
        url: url.url,
        gzip:true
    },  function(error, response, body) {
        debugLogger.info(debugMessagePrefix + 'Got a response back with status: ' + response.statusCode);
        if (!error && response.statusCode == 200) {
            debugLogger.info(debugMessagePrefix + 'About to parse the dom');
            jsdom.env(
                body,
                ["./jquery-2.1.1.min.js"],
                function (errors, window) {
                    if(errors) {
                        errorLogger.error(errors);
                        finishedRequest.reject("JSDOM error - see logs");
                        return;
                    }
                    if(url.number < 10){
                        url.number = '0' + url.number;
                    }
                    if(url.season < 10){
                        url.season = '0' + url.season;
                    }
                    var node = window.$(window.$.find('h3:contains("Season ' + url.season + '")')).next('div').find('span:contains("Episode ' + url.number + '")').parent().attr("onClick");
                    if(node && node.length > 1){
                        var episodeUrlTorrent = node.split('\'')[1];
                        if(episodeUrlTorrent){
                            debugLogger.info(debugMessagePrefix + 'Found the following episode number: ' + episodeUrlTorrent);
                            finishedRequest.resolve('' + episodeUrlTorrent);
                        }
                    }
                    else{
                        finishedRequest.reject("Did not find season in page");
                    }
                }
            );
        }
        else{
            errorLogger.error(error);
            finishedRequest.reject();
        }
    });

    //do it next
    finishedRequestPromise.then(function(episodeNumber){
        requestEpisodeSegment({episodeNumber: episodeNumber, debugMessagePrefix:debugMessagePrefix, name:url.name, deferred:url.deferred});
    }, function(reason){
        errorLogger.error(reason);
        url.deferred.reject(reason);
    }, function(){});  //.then(onFulfilled, onRejected, onProgress)
}

function requestEpisodeSegment(episode){
    var debugMessagePrefix = episode.debugMessagePrefix;
    var url = 'https://kickass.to/media/getepisode/' + episode.episodeNumber + '/';
    var finishedRequest = Q.defer();
    var finishedRequestPromise = finishedRequest.promise;

    debugLogger.info(debugMessagePrefix + 'Getting segment from: ' + url);

    request({
        url: url,
        gzip:true
    },  function(error, response, body) {
        if (!error && response.statusCode == 200) {
            jsdom.env(
                body,
                ["./jquery-2.1.1.min.js"],
                function (errors, window) {
                    if(errors) {
                        errorLogger.error("requestEpisodeSegment - jsdom - " + errors);
                        finishedRequest.reject("JSDOM error - see logs");
                        return;
                    }
                    debugLogger.info(debugMessagePrefix + "Look for x264 files in segment");
                    var node = window.$(window.$.find('tr:contains("x264")')).find(('a[title="Download torrent file"]'));
                    if (node.length < 1){
                        node = window.$.find('a[title="Download torrent file"]');
                        debugLogger.info(debugMessagePrefix + "No x264 torrents found in page");
                        if(node.length < 1){
                            debugLogger.info(debugMessagePrefix + "No torrents found - rejecting defer obj");
                            finishedRequest.reject("No torrents found");
                            return;
                        }
                    }
                    var nodeUrl = node[0].href;
                    finishedRequest.resolve(nodeUrl);
                }
            );
        }
        else{
            errorLogger.error("requestEpisodeSegment - request - " + error);
            finishedRequest.reject();
        }
    });

    //do it next
    finishedRequestPromise.then(function(nodeUrl){
        debugLogger.info(debugMessagePrefix + "Download file from: " + nodeUrl);
        getUelFromTorrentCache(nodeUrl, episode);
    }, function(reason){
        errorLogger.error(reason);
        episode.deferred.reject(reason);
    }, function(){});  //.then(onFulfilled, onRejected, onProgress)
}

function getUelFromTorrentCache(url,episode){
    var debugMessagePrefix = "Get torrent file from torrentCache ";
    var finishedRequest = Q.defer();
    var fileName;
    var finishedRequestPromise = finishedRequest.promise;
    request({
        url: url,
        gzip:true
    },  function(error, response, body) {
        if (!error && response.statusCode == 200) {
            jsdom.env(
                body,
                ["./jquery-2.1.1.min.js"],
                function (errors, window) {
                    if(errors) {
                        errorLogger.error("requestEpisodeSegment - jsdom - " + errors);
                        finishedRequest.reject("JSDOM error - see logs");
                        return;
                    }
                    debugLogger.info(debugMessagePrefix + "Look for torrent phrase");
                    var node = window.$.find('a:contains("torrent file")');
                    var fileUrl = null;
                    node.length > 0 ? fileUrl = window.$.find('a:contains("torrent file")')[0].href : fileUrl = null;
                    if(fileUrl === null){
                        finishedRequest.reject("Did not find url for file");
                        return;
                    }
                    var splittedUrl = fileUrl.split("/");
                    fileName = splittedUrl.pop();
                    fileName = fileName.replace('%5B','[').replace('%5D',']');
                    fileUrl = splittedUrl.join("/");
                    finishedRequest.resolve(fileUrl+".torrent");
                }
            );
        }
        else{
            errorLogger.error("requestEpisodeSegment - request - " + error);
            finishedRequest.reject("response.statusCode: " + response.statusCode);
        }
    });

    //do it next
    finishedRequestPromise.then(function(nodeUrl){
        debugLogger.info(debugMessagePrefix + "Download file from: " + nodeUrl);
        downloadFile(nodeUrl, episode.name, episode.deferred, fileName);
    }, function(reason){
        errorLogger.error(reason);
        episode.deferred.reject(reason);
    }, function(){});  //.then(onFulfilled, onRejected, onProgress)
}
/********************Set some tasks for later *******************/
var agenda = new Agenda({db: { address: 'localhost:27017/agendaJobs'}});

agenda.define('today shows',function(job,done){
    var downloadDays = config.get('downloadDays');
    if(!downloadDays || isNaN(parseInt(downloadDays))){
        downloadDays = 1;
    }
    debugLogger.info('Number of days back to look for: ' +downloadDays);
    async.waterfall([
        // find relevant episode in the db
        function(callback){
            var start = moment().subtract(downloadDays, 'days').hour(0).minute(0).toDate();
            var end = moment().hour(0).minute(0).toDate();
            debugLogger.info('Look for episode in range: ' + start + ' - ' + end);
            Episode.find({firstAired:{$gte:start, $lt:end}},function(err, results){
                _.each(results, function(r){
                    debugLogger.info('Found the following: ', r.episodeId + ':' + r.episodeName);
                })

                if(err) callback(err);
                if(results.length < 1) {
                    debugLogger.info('No episode found for that date range');
                    callback(111);
                }
                callback(null,results);
            });
        },
        //extract the urls of each show to kickass torrents
        function(episodes, callback){
            var urls = [];
            var showsNumbers = {}
            Show.find({},function(err,shows){
                if(err) callback(err);

                for(var i=0; i <shows.length; i++){
                    var o = {}
                    o.url = shows[i].url;
                    o.name = shows[i].name;
                    showsNumbers[shows[i]._id] = o;
                }

                _.each(episodes,function(episode){
                    var u = {};
                    u.season = episode.season;
                    u.number = episode.episodeNumber;
                    u.url = showsNumbers[episode.showId]["url"];
                    u.name = showsNumbers[episode.showId]["name"];
                    if(u.url && (u.url !== "")){
                        urls.push(u);
                    }

                });
                if(urls.length ===0) callback(222);
                callback(null, urls)
            })
        },
        //go get the torrent files urls
        function(urls, callback){
            var promises = [];
            _.each(urls,function(url){
                console.log(url);
                var deferred = Q.defer();
                promises.push(deferred.promise);
                url.deferred = deferred;
                requestSeriesPage(url);
            });

            Q.allSettled(promises).spread(function(){
                var args = arguments;
                var sucessFiles = [];
                var failoureFiles = [];
                var mailSubject = "";
                var mailBody = "";
                var successString = "Success: \n";
                var failureString = "Failure: \n";
                for(var i=0; i<args.length; i++){
                    var promise = args[i];
                    if(promise.state === "fulfilled"){
                        sucessFiles.push(promise.value.filename);
                        successString += promise.value.filename + "\n"
                    }
                    else{
                        failoureFiles.push(promise.value);
                        failureString += promise.value + "\n";
                    }
                }
                if(failoureFiles.length === 0){
                    mailSubject = sucessFiles.length + " New episodes were downloaded successfully";
                }
                else{
                    mailSubject = sucessFiles.length + " New episodes were downloaded successfully and " + failoureFiles.length + " files had errors";
                }
                mailBody = successString + failureString;
                debugLogger.info('Going to send email: ', mailSubject ,mailBody)
                agenda.now('send mail',{
                    mailBody: mailBody,
                    mailSubject:mailSubject
                });

            }).done();

            callback(null);
        }
    ], function (err, result) {
        if(err){
            if(err === 111){
                debugLogger.info('No episodes were found for today');
            }
            else if(err === 222){
                debugLogger.info('No links found in db');
            }
            else{
                errorLogger.error(err)
            }
        }
        done();
    });
})
mailer(agenda);
updater(agenda);
agenda.start();
agenda.every('24 hours', 'today shows');
agenda.every('24 hours', 'update db');

/********************Set some tasks for later*******************/<|endoftext|>


<commit_before>
var net = require('net');
var util = require('util');
var events = require('events');
var async = require('async');

var mdns = require('mdns');
var isme = require('isme');
var gmid = require('gmid');

var IS_HEX_STRING = /^[0123456789ABCDEF]+$/;

function compareHex(hexA, hexB) {
  for (var i = 0, l = hexA.length; i < l; i++) {
    // if hex digits are the same nothing can be concluded
    if (hexA[i] === hexB[i]) continue;

    // At this point the hex digits arn't the same, compare the size
    // and return true if hexA was greater than hexB
    return (parseInt(hexA[i], 16) > parseInt(hexB[i], 16));
  }

  // If they where totally equal, something went wrong and
  // it should be ignored, thats a `return true`.
  return true;
}

function isObject(value) {
  return (typeof value === 'object' && value !== null);
}

function Service(settings, connectionHandler) {
  if (!(this instanceof Service)) {
    return new Service(settings, connectionHandler);
  }

  var self = this;

  // Check that settings is of correct type
  if (typeof settings !== 'string' && !isObject(settings)) {
    throw new TypeError('first argument must be a string or an object');
  }

  // Create settings object
  if (typeof settings === 'string') {
    settings = {
      name: settings
    };
  }
  if (settings.hasOwnProperty('uuid') === false) {
    settings.uuid = gmid();
  }

  // Check that the service name property is of correct type
  if (typeof settings.name !== 'string') {
    throw new TypeError('service name must be a string type');
  }

  // Check that the service uuid property is of correct type
  if (typeof settings.uuid !== 'string') {
    throw new TypeError('service uuid must be a string type');
  }
  settings.uuid = settings.uuid.toUpperCase();

  // Check that service uuid is a hex string
  if (IS_HEX_STRING.test(settings.uuid) === false) {
    throw new TypeError('service uuid must be a hex string');
  }

  // Collection of online sockets
  this.connections = [];

  // Add connection handler if given
  if (connectionHandler) this.on('connection', connectionHandler);

  // Contains unique ID and a service key (given by service name)
  this._uuid = settings.uuid;
  this._key = mdns.tcp(settings.name);

  // Collection of services
  this._services = [];
  this._serviceBuffer = [];

  // Keeps addresses and port
  this._address = {
    addresses: null,
    port: null
  };

  this._relayError = function (err) {
    self.emit('error', err);
  };

  // Keeps main objects, unfortunately the announce object
  // can't be created before the service server is online
  this._server = net.createServer();
  this._server.on('error', this._relayError);

  this._discover = mdns.createBrowser(this._key);
  this._discover.on('error', this._relayError);

  this._announce = null;
}
util.inherits(Service, events.EventEmitter);
module.exports = Service;

Service.prototype.listen = function () {
  var self = this;
  var args = Array.prototype.slice.call(arguments);

  var port, address;

  // Extract callback
  if (typeof args[args.length - 1] === 'function') {
    this.once('listening', args.pop());
  }

  // Extract address
  if (args.length === 2) {
    address = args.pop() || "0.0.0.0";
  } else {
    address = "0.0.0.0";
  }
  if (typeof address !== 'string') {
    throw new TypeError('address must be a string');
  }

  // Extract port
  port = args.pop() || 0;
  if (typeof port !== 'number') {
    throw new TypeError('port must be a number');
  }

  // Start service server
  this._server.listen(port, address);

  // Server is online
  this._server.once('listening', function () {
    self._address.port = self._server.address().port;

    // Start announceing and discovering
    self._createAnnouncer();
    self._announce.start();
    self._discover.start();

    // Listening event will be emited once an
    // self announcement has been made, see
    // serviceUp handler below
  });

  // Got connection start handshake
  this._server.on('connection', function (socket) {
    self._perfromHandshake(socket);
  });

  // Used when this service is online but an announcement was made
  function offline(service) {

    // Use self announcement to catch broadcasted public ip
    if (self._selfAnnouncement(service)) {
      self._address.addresses = service.addresses;

      // Switch to online mode
      self._discover.removeListener('serviceUp', offline);
      self._discover.on('serviceUp', online);

      // Emit listening
      self.emit('listening');

      // Connect to buffered services
      self._serviceBuffer.forEach(self._addService.bind(self));
      self._serviceBuffer = [];

      return;
    }

    // When offline, then store service in buffer
    self._serviceBuffer.push(service);
  }
  this._discover.on('serviceUp', offline);

  // Used when this service is online
  function online(service) {

    // Skip self announcement
    if (self._selfAnnouncement(service)) return;

    self._addService(service);
  }
};

Service.prototype._createAnnouncer = function () {

  this._announce = mdns.createAdvertisement(
    this._key, this._address.port, {
      // workaround for some wird bug
      // ref: https://github.com/agnat/node_mdns/issues/51
      name: this._uuid
    }
  );

  this._announce.on('error', this._relayError);
};

Service.prototype._addService = function (service) {
  var self = this;

  // Get IPv4 addresses
  // TODO: look intro IPv6 support
  var addresses = service.addresses.filter(net.isIPv4.bind(net));

  // Create remote connection object
  var remote = {
    port: service.port,
    addresses: service.addresses
  };

  // Prevent double connection
  this._services.push(remote);

  // The service worker with the highest number gets the pleasure of
  // initiating the TPC connection.
  if (compareHex(this._uuid, service.name)) return;

  // Don't allow multiply connection to same service worker
  if (this._existConnection(remote)) return;

  // Connect to remote and start handshake
  var socket = net.connect({ port: service.port, host: addresses[0] });

  // Relay errors to the service object, when handshake is done the
  // error handler is removed.
  socket.on('error', this._relayError);

  socket.once('connect', function () {

    // Handshake:
    // Inform remote about the service it just connected to,
    // the handshake ends with a line break (can't exist in unparsed JSON),
    // indicating that the message ends.
    socket.write(JSON.stringify(self._address) + "\n", function () {

      // Done, not ready to claim this a connection
      self._addSocket(socket, remote);
    });
  });
};

Service.prototype._perfromHandshake = function (socket) {
  var self = this;

  var buffer = "";
  var length = 0;

  // Relay errors to the service object, when handshake is done the
  // error handler is removed.
  socket.on('error', this._relayError);

  // The first data chunk should be a JSON string, containing information
  // about the remote service.
  socket.once('data', function removeMe(chunk) {
    // backup the old length and update buffer
    length = buffer.length;
    buffer += chunk.toString();

    // handshake message not ended
    var index = buffer.indexOf('\n');
    if (index === -1) return;

    // parse handshake object
    var remote = JSON.parse(buffer.slice(0, index));

    // slice chunk buffer so the handshake object isn't a part of it
    chunk = chunk.slice((index - length) + 1).toString();

    // Done handshake
    self._addSocket(socket, remote);

    // emit remaining data chunk
    if (chunk.length !== 0) {
      socket.emit('data', chunk);
    }
  });
};

// Remove remote object from services list
Service.prototype._removeService = function (remote) {
    var index = this._services.indexOf(remote);
    if (index === -1) return;

    this._services.splice(index, 1);
};

Service.prototype._addSocket = function (socket, remote) {
  var self = this;

  // Add remote info to socket object
  socket.remote = remote;

  // Add socket to connections list now
  this.connections.push(socket);

  // Remove socket from connection list once closed
  socket.once('close', function () {
    self._removeService(remote);
    self._removeSocket(socket);
  });

  // Remove handshake error handler, its up to the user to
  // handle errors now.
  socket.removeListener('error', this._relayError);

  // Done, emit connection event
  this.emit('connection', socket);
};

// Remove socket from connections list
Service.prototype._removeSocket = function (socket) {
    var index = this.connections.indexOf(socket);
    if (index === -1) return;

    this.connections.splice(index, 1);
};

Service.prototype._selfAnnouncement = function (service) {
  return (service.addresses.some(isme) &&
          service.port == this._address.port &&
          service.name === this._uuid);
};

Service.prototype._existConnection = function (remote) {
  var connections = this.connections;
  var i = connections.length;

  // If the remote object was found in connections list, then it exist
  while (i--) {
    var serviceInfo = this._services[i];

    if (matchArray(serviceInfo.addresses, remote.addresses) &&
        serviceInfo.port == remote.port) {
      return true;
    }
  }

  // No matching remote object was found
  return false;
};

function matchArray(arr1, arr2) {
  if (arr1.length !== arr2.length) return false;

  var i = arr1.length;
  while (i--) {
    if (arr1[i] !== arr2[i]) return false;
  }
}

Service.prototype.address = function () {
  // addresses is the last property there will be set
  if (this._address.addresses === null) {
    return null;
  }

  return this._address;
};

Service.prototype.close = function (callback) {
  var self = this;

  // reset address info
  this._address = {
    port: null,
    addresses: null
  };

  // Callback is just a close event handler
  if (callback) this.once('close', callback),

  // Stop announceing and discovering
  this._announce.stop();
  this._discover.stop();

  async.parallel([

    // Close all sockets
    function (done) {
      async.forEach(self.connections.slice(0), function(socket, eachDone) {
        socket.end();
        socket.once('close', function () { eachDone(null); });
      }, done);
    },

    // Close connection server, note the server.close callback
    // won't be executed before all connected sockets are closed.
    function (done) {
      self._server.close(done);
    }
  ], function () {
    self.emit('close');
  });
};
<commit_msg>[fix] bugs related to double connection prevention<commit_after>
var net = require('net');
var util = require('util');
var events = require('events');
var async = require('async');

var mdns = require('mdns');
var isme = require('isme');
var gmid = require('gmid');

var IS_HEX_STRING = /^[0123456789ABCDEF]+$/;

function compareHex(hexA, hexB) {
  for (var i = 0, l = hexA.length; i < l; i++) {
    // if hex digits are the same nothing can be concluded
    if (hexA[i] === hexB[i]) continue;

    // At this point the hex digits arn't the same, compare the size
    // and return true if hexA was greater than hexB
    return (parseInt(hexA[i], 16) > parseInt(hexB[i], 16));
  }

  // If they where totally equal, something went wrong and
  // it should be ignored, thats a `return true`.
  return true;
}

function isObject(value) {
  return (typeof value === 'object' && value !== null);
}

function Service(settings, connectionHandler) {
  if (!(this instanceof Service)) {
    return new Service(settings, connectionHandler);
  }

  var self = this;

  // Check that settings is of correct type
  if (typeof settings !== 'string' && !isObject(settings)) {
    throw new TypeError('first argument must be a string or an object');
  }

  // Create settings object
  if (typeof settings === 'string') {
    settings = {
      name: settings
    };
  }
  if (settings.hasOwnProperty('uuid') === false) {
    settings.uuid = gmid();
  }

  // Check that the service name property is of correct type
  if (typeof settings.name !== 'string') {
    throw new TypeError('service name must be a string type');
  }

  // Check that the service uuid property is of correct type
  if (typeof settings.uuid !== 'string') {
    throw new TypeError('service uuid must be a string type');
  }
  settings.uuid = settings.uuid.toUpperCase();

  // Check that service uuid is a hex string
  if (IS_HEX_STRING.test(settings.uuid) === false) {
    throw new TypeError('service uuid must be a hex string');
  }

  // Collection of online sockets
  this.connections = [];

  // Add connection handler if given
  if (connectionHandler) this.on('connection', connectionHandler);

  // Contains unique ID and a service key (given by service name)
  this._uuid = settings.uuid;
  this._key = mdns.tcp(settings.name);

  // Collection of services
  this._services = [];
  this._serviceBuffer = [];

  // Keeps addresses and port
  this._address = {
    addresses: null,
    port: null
  };

  this._relayError = function (err) {
    self.emit('error', err);
  };

  // Keeps main objects, unfortunately the announce object
  // can't be created before the service server is online
  this._server = net.createServer();
  this._server.on('error', this._relayError);

  this._discover = mdns.createBrowser(this._key);
  this._discover.on('error', this._relayError);

  this._announce = null;
}
util.inherits(Service, events.EventEmitter);
module.exports = Service;

Service.prototype.listen = function () {
  var self = this;
  var args = Array.prototype.slice.call(arguments);

  var port, address;

  // Extract callback
  if (typeof args[args.length - 1] === 'function') {
    this.once('listening', args.pop());
  }

  // Extract address
  if (args.length === 2) {
    address = args.pop() || "0.0.0.0";
  } else {
    address = "0.0.0.0";
  }
  if (typeof address !== 'string') {
    throw new TypeError('address must be a string');
  }

  // Extract port
  port = args.pop() || 0;
  if (typeof port !== 'number') {
    throw new TypeError('port must be a number');
  }

  // Start service server
  this._server.listen(port, address);

  // Server is online
  this._server.once('listening', function () {
    self._address.port = self._server.address().port;

    // Start announceing and discovering
    self._createAnnouncer();
    self._announce.start();
    self._discover.start();

    // Listening event will be emited once an
    // self announcement has been made, see
    // serviceUp handler below
  });

  // Got connection start handshake
  this._server.on('connection', function (socket) {
    self._perfromHandshake(socket);
  });

  // Used when this service is online but an announcement was made
  function offline(service) {

    // Use self announcement to catch broadcasted public ip
    if (self._selfAnnouncement(service)) {
      self._address.addresses = service.addresses;

      // Switch to online mode
      self._discover.removeListener('serviceUp', offline);
      self._discover.on('serviceUp', online);

      // Emit listening
      self.emit('listening');

      // Connect to buffered services
      self._serviceBuffer.forEach(self._addService.bind(self));
      self._serviceBuffer = [];

      return;
    }

    // When offline, then store service in buffer
    self._serviceBuffer.push(service);
  }
  this._discover.on('serviceUp', offline);

  // Used when this service is online
  function online(service) {

    // Skip self announcement
    if (self._selfAnnouncement(service)) return;

    self._addService(service);
  }
};

Service.prototype._createAnnouncer = function () {

  this._announce = mdns.createAdvertisement(
    this._key, this._address.port, {
      // workaround for some wird bug
      // ref: https://github.com/agnat/node_mdns/issues/51
      name: this._uuid
    }
  );

  this._announce.on('error', this._relayError);
};

Service.prototype._addService = function (service) {
  var self = this;

  // Create remote connection object
  var remote = {
    port: service.port,
    addresses: service.addresses.sort()
  };

  // The service worker with the highest number gets the pleasure of
  // initiating the TPC connection.
  if (compareHex(this._uuid, service.name)) return;

  // Don't allow multiply connection to same service worker
  if (this._existConnection(remote)) return;
  this._services.push(remote);

  // Connect to remote and start handshake
  var addresses = service.addresses.filter(net.isIPv4.bind(net));
  var socket = net.connect({ port: service.port, host: addresses[0] });

  // Relay errors to the service object, when handshake is done the
  // error handler is removed.
  socket.on('error', this._relayError);

  socket.once('connect', function () {

    // Handshake:
    // Inform remote about the service it just connected to,
    // the handshake ends with a line break (can't exist in unparsed JSON),
    // indicating that the message ends.
    socket.write(JSON.stringify(self._address) + "\n", function () {

      // Done, not ready to claim this a connection
      self._addSocket(socket, remote);
    });
  });
};

Service.prototype._perfromHandshake = function (socket) {
  var self = this;

  var buffer = "";
  var length = 0;

  // Relay errors to the service object, when handshake is done the
  // error handler is removed.
  socket.on('error', this._relayError);

  // The first data chunk should be a JSON string, containing information
  // about the remote service.
  socket.once('data', function removeMe(chunk) {
    // backup the old length and update buffer
    length = buffer.length;
    buffer += chunk.toString();

    // handshake message not ended
    var index = buffer.indexOf('\n');
    if (index === -1) return;

    // parse handshake object
    var remote = JSON.parse(buffer.slice(0, index));

    // slice chunk buffer so the handshake object isn't a part of it
    chunk = chunk.slice((index - length) + 1).toString();

    // Done handshake
    self._addSocket(socket, remote);

    // emit remaining data chunk
    if (chunk.length !== 0) {
      socket.emit('data', chunk);
    }
  });
};

// Remove remote object from services list
Service.prototype._removeService = function (remote) {
    var index = this._services.indexOf(remote);
    if (index === -1) return;

    this._services.splice(index, 1);
};

Service.prototype._addSocket = function (socket, remote) {
  var self = this;

  // Add remote info to socket object
  socket.remote = remote;

  // Add socket to connections list now
  this.connections.push(socket);

  // Remove socket from connection list once closed
  socket.once('close', function () {
    self._removeService(remote);
    self._removeSocket(socket);
  });

  // Remove handshake error handler, its up to the user to
  // handle errors now.
  socket.removeListener('error', this._relayError);

  // Done, emit connection event
  this.emit('connection', socket);
};

// Remove socket from connections list
Service.prototype._removeSocket = function (socket) {
    var index = this.connections.indexOf(socket);
    if (index === -1) return;

    this.connections.splice(index, 1);
};

Service.prototype._selfAnnouncement = function (service) {
  return (service.addresses.some(isme) &&
          service.port == this._address.port &&
          service.name === this._uuid);
};

Service.prototype._existConnection = function (remote) {
  var services = this._services;
  var i = services.length;

  // If the remote object was found in connections list, then it exist
  while (i--) {
    var serviceInfo = this._services[i];

    if (matchArray(serviceInfo.addresses, remote.addresses) &&
        serviceInfo.port == remote.port) {
      return true;
    }
  }

  // No matching remote object was found
  return false;
};

function matchArray(arr1, arr2) {
  if (arr1.length !== arr2.length) return false;

  var i = arr1.length;
  while (i--) {
    if (arr1[i] !== arr2[i]) return false;
  }

  return true;
}

Service.prototype.address = function () {
  // addresses is the last property there will be set
  if (this._address.addresses === null) {
    return null;
  }

  return this._address;
};

Service.prototype.close = function (callback) {
  var self = this;

  // reset address info
  this._address = {
    port: null,
    addresses: null
  };

  // Callback is just a close event handler
  if (callback) this.once('close', callback),

  // Stop announceing and discovering
  this._announce.stop();
  this._discover.stop();

  async.parallel([

    // Close all sockets
    function (done) {
      async.forEach(self.connections.slice(0), function(socket, eachDone) {
        socket.end();
        socket.once('close', function () { eachDone(null); });
      }, done);
    },

    // Close connection server, note the server.close callback
    // won't be executed before all connected sockets are closed.
    function (done) {
      self._server.close(done);
    }
  ], function () {
    self.emit('close');
  });
};
<|endoftext|>


<commit_before>(function (window, Worker, Blob, URL) {
    'use strict';
    var workerFactory = function (job) {

        var singleThreadWorker = function (jobFunc) {
            var worker = function () {
                this.result = null;
                this.error = null;
                this.postMessage = function (data) {
                    try {
                        this.result = jobFunc(data);
                    } catch (e) {
                        this.error = e;
                    }
                };
                this.close = function() {
                    this.result = null;
                    this.error = null;
                };
            }
            return new worker(job);
        };

        var multiThreadWorker = function (jobFunc) {
            var createBlob = function (response) {
                var blob, BlobBuilder;
                try {
                    blob = new Blob([response], { type: 'text/javascript' });
                } catch (e) { /* Backwards-compatibility */
                    BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder;
                    blob = new BlobBuilder();
                    blob.append(response);
                    blob = blob.getBlob();
                }

                return blob;
            },
            jobCode = 'onmessage = function (msg) { var job = ' + jobFunc.toString() + '; var result = job(msg.data); this.postMessage(result); };',
            blob = createBlob(jobCode),
            url = URL.createObjectURL(blob),
            workerInstance = new Worker(url);

            workerInstance.url = url;
            workerInstance.close = function () {
                this.terminate();
                if (blob && url) { /* Release the blob URL when closing Worker */
                    URL.revokeObjectURL(url);
                };
            }
            return workerInstance;
        };

        var create = function (parallel) {
            var workerInstance;
            try {
                workerInstance = (parallel === false) || !Worker ? singleThreadWorker(job) : multiThreadWorker(job);
            } catch (e) {     /* If Worker is not supported by the browser or causes issues, fallback to 'Single Thread Worker' mode */
                workerInstance = singleThreadWorker(job);
            }
            return workerInstance;
        }

        return { create: create };
    };

    var Thread = function (opt) {

        var self = this, options = opt || {}, workerInstance, thenPromise, failPromise;

        this.then = function (callback) {
            if (workerInstance.result) { /* If job is passed as a promise */
                callback.bind(this)(workerInstance.result);
            }
            thenPromise = callback;
            return this;
        };

        this.fail = function (callback) {
            if (workerInstance.error) { /* If job is passed as a promise */
                callback.bind(this)(workerInstance.error);
            }
            failPromise = callback;
            return this;
        };

        this.start = function (data, job) {
            /* Initialize worker instance */
            workerInstance = workerFactory(job).create(options.parallel);

            workerInstance.onmessage = function (msg) {
                /* After executing the job, call 'then' promise */
                if (typeof thenPromise === 'function') {
                    thenPromise.bind(self)(msg.data, self.getId());
                }
            };

            workerInstance.onerror = function (msg) {
                /* If error occurs while executing code call 'fail' promise */
                if (typeof failPromise === 'function') {
                    failPromise.bind(self)(msg);
                }
            };

            /* Start worker */
            workerInstance.postMessage(data);
            return this;
        };

        this.close = function () {
            workerInstance.close();
        };

        this.reStart = function (data) {
            /* Re-execute job with new parameters */
            if (workerInstance.result) {
                workerInstance.result = null;
            }
            if (workerInstance.error) {
                workerInstance.error = null;
            }
            workerInstance.postMessage(data);
            return this;
        };

        this.getId = function () {
            /* Return unique identifier of the Thread */
            return workerInstance.url.split('/')[1];
        };
    };

    if (!window.Thread) {
        /* Initialize global Thread object */
        window.Thread = Thread;
    }

    return Thread;

}(window, window.Worker, Blob, window.URL || window.webkitURL));<commit_msg>Update worker comments<commit_after>(function (window, Worker, Blob, URL) {
    'use strict';
    var workerFactory = function (job) {

        var singleThreadWorker = function (jobFunc) {
            /* Single Thread Worker to support fallback in unsupported browsers */
            var worker = function () {
                this.result = null;
                this.error = null;
                this.postMessage = function (data) {
                    try {
                        this.result = jobFunc(data);
                    } catch (e) {
                        this.error = e;
                    }
                };
                this.close = function () {
                    this.result = null;
                    this.error = null;
                };
            }
            return new worker(job);
        };

        var multiThreadWorker = function (jobFunc) {
            /* Multi Thread Worker that uses 'Worker' object */
            var createBlob = function (response) {
                var blob, BlobBuilder;
                try {
                    blob = new Blob([response], { type: 'text/javascript' });
                } catch (e) { /* Backwards-compatibility */
                    BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder;
                    blob = new BlobBuilder();
                    blob.append(response);
                    blob = blob.getBlob();
                }

                return blob;
            },
            jobCode = 'onmessage = function (msg) { var job = ' + jobFunc.toString() + '; var result = job(msg.data); this.postMessage(result); };',
            blob = createBlob(jobCode),
            url = URL.createObjectURL(blob),
            workerInstance = new Worker(url);

            workerInstance.url = url;
            workerInstance.close = function () {
                this.terminate();
                if (blob && url) { /* Release the blob URL when closing Worker */
                    URL.revokeObjectURL(url);
                };
            }
            return workerInstance;
        };

        var create = function (parallel) {
            var workerInstance;
            try {
                workerInstance = (parallel === false) || !Worker ? singleThreadWorker(job) : multiThreadWorker(job);
            } catch (e) {     /* If Worker is not supported by the browser or causes issues, fallback to 'Single Thread Worker' mode */
                workerInstance = singleThreadWorker(job);
            }
            return workerInstance;
        }
        return { create: create };
    };

    var Thread = function (opt) {

        var self = this, options = opt || {}, workerInstance, thenPromise, failPromise;

        this.then = function (callback) {
            if (workerInstance.result) { /* Initialize then promise */
                callback.bind(this)(workerInstance.result);
            }
            thenPromise = callback;
            return this;
        };

        this.fail = function (callback) {
            if (workerInstance.error) { /* Initialize fail promise */
                callback.bind(this)(workerInstance.error);
            }
            failPromise = callback;
            return this;
        };

        this.start = function (data, job) {
            /* Initialize worker instance */
            workerInstance = workerFactory(job).create(options.parallel);

            workerInstance.onmessage = function (msg) {
                /* After executing the job, call 'then' promise */
                if (typeof thenPromise === 'function') {
                    thenPromise.bind(self)(msg.data, self.getId());
                }
            };

            workerInstance.onerror = function (msg) {
                /* If error occurs while executing code call 'fail' promise */
                if (typeof failPromise === 'function') {
                    failPromise.bind(self)(msg);
                }
            };

            /* Start worker */
            workerInstance.postMessage(data);
            return this;
        };

        this.close = function () {
            workerInstance.close();
        };

        this.reStart = function (data) {
            /* Re-execute job with new parameters */
            if (workerInstance.result) {
                workerInstance.result = null;
            }
            if (workerInstance.error) {
                workerInstance.error = null;
            }
            workerInstance.postMessage(data);
            return this;
        };

        this.getId = function () {
            /* Return unique identifier of the Thread */
            return workerInstance.url.split('/')[1];
        };
    };

    if (!window.Thread) {
        /* Initialize global Thread object */
        window.Thread = Thread;
    }

    return Thread;

}(window, window.Worker, Blob, window.URL || window.webkitURL));<|endoftext|>


<commit_before>/**
 *	ACCENT.JS: multi language code highlighting library
 *
 *	Description:
 *		implemented in javascript, this project aims to be a simple multi language code highlighting library,
 *		taking stylistic influence from various popular text editors
 **/

// TODO generalize language object to parse regexp patterns with generic function
;( function() {

	'use strict';

	// allows for a more functionally pure iterations on Dom node collections
	NodeList.prototype.forEach =
		HTMLCollection.prototype.forEach =
			Array.prototype.forEach;

	//+ _compose :: array(function) -> function -> any
	var _compose = function() {
		var fns = Array.prototype.slice.call(arguments);

		return function(arg) {
			return fns.reduce(function(arg, fn) {
				return fn(arg);
			}, arg);
		};
	},

	/**
	 *	supported languages namespaces
	 *	returns expressions and syntax wrappers for each specified language feature
	 **/
	_language = {};

	// helper methods for parsers of language features
	_language['util'] = {

		// surrounds a string in a span with the class property specified
		highlight: function(syntaxClass) {
			return function(match) {
				return '', match, '');
			};
		},

		// remove syntax node that do not match the specified class
		// i.e. ignoreNot('js-operation') removes all nodes of a string that do not match 'js-operation '
		ignoreNot: function(syntaxClass) {

			var self = this;

			return function(match) {
				var escaped = match.replace(/\(.*?)<\/span>/g, '$1');
				return self.highlight(syntaxClass)(escaped);
			};
		}
	};

	// the javascript language parser
	_language['javascript'] = [

		// common language operators such as conditionals and loops
		[
			/\b(if|else|continue|switch|case|default|break|return|for|try|catch|throw)(?=[^\w])/g,
			_language.util.highlight('js-operation')
		],

		// variable assignment keywords
		[
			/(\bfunction|var|const|in|new|this|prototype)(?=[^\w])/g,
			_language.util.highlight('js-declaration')
		],

		// frequently used methods
		[
			/(\.\bgetElementById|getElementsBy(ClassName|TagName|Name)|(type|instance)of|hasOwnProperty)/g,
			_language.util.highlight('js-special')
		],

		// common dom methods
		[
			/(\.\binnerHTML|createElement|parentNode|innerHTML|(append|replace)Child)(?=[^\w])/g,
			_language.util.highlight('js-dom')
		],

		// globals [window]
		[
			/\b(window|console|document)/g,
			_language.util.highlight('js-global')
		],

		// basic types and special type checking keywords
		[
			/\b(Array|String|Function|Object|Number|Date|Boolean|Error|RegExp|Math|null|undefined|true|false)(?=[^\w])/g,
			_language.util.highlight('js-type')
		],

		// numeric values (including hexadecimal)
		[
			/(-{0,1}\d+\.{0,1}\d+|-{0,1}0x\w+)(?=[^\w])/g,
			_language.util.highlight('js-numeric')
		],

		// double/single qouted string
		[
			/(".*?"|'.*?')/g,
			_language.util.ignoreNot('js-string')
		],

		// comments
		[
			/(\/{2}.*?\n+|\/\*(.|[\r\n])*\*\/)/g,
			_language.util.ignoreNot('js-comment')
		],

		// regexp literals
		[
			/[^<](\/.+?\/(\s|,|\]|;|\/|g|i|m|y))/g,
			_language.util.ignoreNot('js-regexp')
		],
	];

	// formats the code content with a default theme
	var _format = function(theme) {
		return function(node) {
			// wrap with pre formatter tag
			var parent = node.parentNode,
			pre = document.createElement('pre');

			parent.replaceChild(pre, node);
			pre.appendChild(node);

			// prefix theme with the acc flag
			node.parentNode.className = 'acc-'.concat(theme);

			return node;
		};
	},

	// parses element text body according to the selected language property rules (see language)
	_parse = function(lang) {
		return function(node) {
			node.innerHTML = lang.reduce(function(textBody, grammer) {
				return textBody.replace(grammer[0], grammer[1]);
			}, node.innerHTML);

			return node;
		};
	},

	// outward facing reference
	// clean up closure functionality, implment a less general condition evaluation function
	accent = (function() {

		// reliable way to check type of accent function arguments
		//+ _is :: any,str -> boolean
		var _is = function(entity, type) {
			return Object.prototype.toString.call(entity).slice(8, -1) === type;
		},

		//+ _areStr :: array -> boolean
		_areStr = function() {
			var args = Array.prototype.slice.call(arguments);

			return args.reduce(function(status, maybeString) {
				return _is(maybeString, 'String')
			}, true);
		},

		//+ _highlight :: string,string -> domNode -> undefined
		_highlight = function(lang, theme) {
			return function(elem) {
				_compose(_format(theme), _parse(lang))(elem);
			};
		};

		return function(identifier, lang, theme) {

			if(_areStr(identifier, lang, theme)) {
				var elems =  document.getElementsByClassName(identifier),
				begin = _highlight(_language[lang], theme);
				elems.forEach(begin);

			} else {
				throw new TypeError('identifier, language & theme parameters must be strings');
			}
		};

	})();

	/**
	 *	usage => accent(_className, _language, _theme)
	 *		_className	:= the class property used to identify the node(s) for highlighting
	 *		_language	:= the language that the class node(s) describe
	 *		_theme		:=	the theme (default or custom) accent will use to apply properties to the node(s)
	 **/
	window.accent = accent;

} )();<commit_msg>small update to _areStr function<commit_after>/**
 *	ACCENT.JS: multi language code highlighting library
 *
 *	Description:
 *		implemented in javascript, this project aims to be a simple multi language code highlighting library,
 *		taking stylistic influence from various popular text editors
 **/

// TODO generalize language object to parse regexp patterns with generic function
;( function() {

	'use strict';

	// allows for a more functionally pure iterations on Dom node collections
	NodeList.prototype.forEach =
		HTMLCollection.prototype.forEach =
			Array.prototype.forEach;

	//+ _compose :: array(function) -> function -> any
	var _compose = function() {
		var fns = Array.prototype.slice.call(arguments);

		return function(arg) {
			return fns.reduce(function(arg, fn) {
				return fn(arg);
			}, arg);
		};
	},

	/**
	 *	supported languages namespaces
	 *	returns expressions and syntax wrappers for each specified language feature
	 **/
	_language = {};

	// helper methods for parsers of language features
	_language['util'] = {

		// surrounds a string in a span with the class property specified
		highlight: function(syntaxClass) {
			return function(match) {
				return '', match, '');
			};
		},

		// remove syntax node that do not match the specified class
		// i.e. ignoreNot('js-operation') removes all nodes of a string that do not match 'js-operation '
		ignoreNot: function(syntaxClass) {

			var self = this;

			return function(match) {
				var escaped = match.replace(/\(.*?)<\/span>/g, '$1');
				return self.highlight(syntaxClass)(escaped);
			};
		}
	};

	// the javascript language parser
	_language['javascript'] = [

		// common language operators such as conditionals and loops
		[
			/\b(if|else|continue|switch|case|default|break|return|for|try|catch|throw)(?=[^\w])/g,
			_language.util.highlight('js-operation')
		],

		// variable assignment keywords
		[
			/(\bfunction|var|const|in|new|this|prototype)(?=[^\w])/g,
			_language.util.highlight('js-declaration')
		],

		// frequently used methods
		[
			/(\.\bgetElementById|getElementsBy(ClassName|TagName|Name)|(type|instance)of|hasOwnProperty)/g,
			_language.util.highlight('js-special')
		],

		// common dom methods
		[
			/(\.\binnerHTML|createElement|parentNode|innerHTML|(append|replace)Child)(?=[^\w])/g,
			_language.util.highlight('js-dom')
		],

		// globals [window]
		[
			/\b(window|console|document)/g,
			_language.util.highlight('js-global')
		],

		// basic types and special type checking keywords
		[
			/\b(Array|String|Function|Object|Number|Date|Boolean|Error|RegExp|Math|null|undefined|true|false)(?=[^\w])/g,
			_language.util.highlight('js-type')
		],

		// numeric values (including hexadecimal)
		[
			/(-{0,1}\d+\.{0,1}\d+|-{0,1}0x\w+)(?=[^\w])/g,
			_language.util.highlight('js-numeric')
		],

		// double/single qouted string
		[
			/(".*?"|'.*?')/g,
			_language.util.ignoreNot('js-string')
		],

		// comments
		[
			/(\/{2}.*?\n+|\/\*(.|[\r\n])*\*\/)/g,
			_language.util.ignoreNot('js-comment')
		],

		// regexp literals
		[
			/[^<](\/.+?\/(\s|,|\]|;|\/|g|i|m|y))/g,
			_language.util.ignoreNot('js-regexp')
		],
	];

	// formats the code content with a default theme
	var _format = function(theme) {
		return function(node) {
			// wrap with pre formatter tag
			var parent = node.parentNode,
			pre = document.createElement('pre');

			parent.replaceChild(pre, node);
			pre.appendChild(node);

			// prefix theme with the acc flag
			node.parentNode.className = 'acc-'.concat(theme);

			return node;
		};
	},

	// parses element text body according to the selected language property rules (see language)
	_parse = function(lang) {
		return function(node) {
			node.innerHTML = lang.reduce(function(textBody, grammer) {
				return textBody.replace(grammer[0], grammer[1]);
			}, node.innerHTML);

			return node;
		};
	},

	// outward facing reference
	// clean up closure functionality, implment a less general condition evaluation function
	accent = (function() {

		// reliable way to check type of accent function arguments
		//+ _is :: any,str -> boolean
		var _is = function(entity, type) {
			return Object.prototype.toString.call(entity).slice(8, -1) === type;
		},

		//+ _areStr :: array -> boolean
		_areStr = function() {
			var args = Array.prototype.slice.call(arguments);

			return args.reduce(function(_, maybeString) {
				return _is(maybeString, 'String')
			}, false);
		},

		//+ _highlight :: string,string -> domNode -> undefined
		_highlight = function(lang, theme) {
			return function(elem) {
				_compose(_format(theme), _parse(lang))(elem);
			};
		};

		return function(identifier, lang, theme) {

			if(_areStr(identifier, lang, theme)) {
				var elems =  document.getElementsByClassName(identifier),
				begin = _highlight(_language[lang], theme);
				elems.forEach(begin);

			} else {
				throw new TypeError('identifier, language & theme parameters must be strings');
			}
		};

	})();

	/**
	 *	usage => accent(_className, _language, _theme)
	 *		_className	:= the class property used to identify the node(s) for highlighting
	 *		_language	:= the language that the class node(s) describe
	 *		_theme		:=	the theme (default or custom) accent will use to apply properties to the node(s)
	 **/
	window.accent = accent;

} )();<|endoftext|>