<commit_before>/*
 * winston-papertrail.js:
 *          Transport for logging to Papertrail Service
 *          (Special thanks to Charlie Robbins)
 *          www.papertrailapp.com
 *
 * (C) 2012 Ken Perkins
 * MIT LICENCE
 *
 */

var os = require('os'),
    tls = require('tls'),
    util = require('util'),
    winston = require('winston');

//
// ### function Papertrail (options)
// Constructor for the Papertrail transport object.
//
var Papertrail = exports.Papertrail = function(options) {

    var self = this;
    options = options || {};

    self.name = 'Papertrail';
    self.level = options.level || 'info';

    // Papertrail Service Host
    self.host = options.host;

    // Papertrail Service Port
    self.port = options.port;

    // Hostname of the current app
    self.hostname = options.hostname || os.hostname();

    // Program is an affordance for Papertrail to name the source of log entries
    self.program = options.program || 'default';

    // Papertrail enables alerts on search matching. Prodiving a decorator for the level
    // makes these searches more specific
    self.levelDecorators = options.levelDecorators || ['[', ']'];

    // Number of attempts before decaying reconnection
    self.attemptsBeforeDecay = options.attemptsBeforeDecay || 5;

    // Maximum number of reconnection attempts before disabling buffer
    self.maximumAttempts = options.maximumAttempts || 25;

    // Delay between normal attempts
    self.connectionDelay = options.connectionDelay || 1000;

    // Maximum delay between attempts
    self.maxDelayBetweenReconnection =
        options.maxDelayBetweenReconnection || 60000;

    self.currentRetries = 0;
    self.totalRetries = 0;
    self.buffer = '';
    self.loggingEnabled = true;

    // Error out if we don't have a host or port
    if (!self.host || !self.port) {
        console.error('Missing required parameters');
        return;
    }

    // Open the connection
    try {
        connectStream();
    }
    catch (e) {
        // TODO figure out a better way of sending errors from connection
        console.error(getDate() +
            ' - [error]: Unable to connect Papertrail transport');
    }

    // Opens a connection to Papertrail
    function connectStream() {
        console.log(getDate() +
            ' - [info]: Attempting to connect to Papertrail');

        self.stream = tls.connect(self.port, self.host, {}, onConnected);

        self.stream.on('error', function(err) {
            console.error(getDate() +
                ' - [error]: Error with TLS connection ' + util.inspect(err));

            // Papertrail has a bug that periodically resets their logging
            // services thus disconnecting your transport. We use setTimeout
            // to throttle the reconnection attempts in case Papertrail is
            // truly offline

            setTimeout(function() {

                // Increment our retry counts
                self.currentRetries++;
                self.totalRetries++;

                // Decay the retry rate exponentially up to max between attempts
                if ((self.connectionDelay < self.maxDelayBetweenReconnection) &&
                    (self.currentRetries >= self.attemptsBeforeDecay)) {
                    self.connectionDelay = self.connectionDelay * 2;
                    self.currentRetries = 0;
                }

                connectStream();

                // Stop buffering messages after a fixed number of retries.
                // This is to keep the buffer from growing unbounded
                if (self.loggingEnabled &&
                    (self.totalRetries >= (self.maximumAttempts))) {
                    self.loggingEnabled = false;
                    console.log(getDate() +
                        ' - [error]: Max retries eclipsed, disabling buffering');
                }

            }, self.connectionDelay);
        });

        // If we have the stream end, simply reconnect
        self.stream.on('end', function() {
            console.log(getDate() + ' - [info]: Reconnecting logging...');
            connectStream();
        });
    }

    function onConnected() {
        console.log(getDate() +
            ' - [info]: Logging connected to ' +
            self.host + ':' + self.port + ' via TLS');

        // Reset our variables
        self.loggingEnabled = true;
        self.currentRetries = 0;
        self.totalRetries = 0;
        self.connectionDelay = 1000;

        // Did we get messages buffered
        if (self.buffer) {
            self.stream.write(self.buffer);
            self.buffer = '';
        }
    }
};

//
//
// Inherit from `winston.Transport` so you can take advantage
// of the base functionality and `.handleExceptions()`.
//
util.inherits(Papertrail, winston.Transport);

//
// Define a getter so that `winston.transports.Papertrail`
// is available and thus backwards compatible.
//
winston.transports.Papertrail = Papertrail;

//
// ### function log (level, msg, [meta], callback)
// #### @level {string} Level at which to log the message.
// #### @msg {string} Message to log
// #### @meta {Object} **Optional** Additional metadata to attach
// #### @callback {function} Continuation to respond to when complete.
// Core logging method exposed to Winston. Metadata is optional.
//
Papertrail.prototype.log = function(level, msg, meta, callback) {
    // If the logging buffer is disabled, drop the message on the floor
    if (!this.loggingEnabled) {
        return callback(null, true);
    }

    var decoratedLevel = this.levelDecorators[0] + level + this.levelDecorators[1],
        output = msg;

    if (meta) {
        if (typeof meta !== 'object') {
            output += ' ' + meta;
        }
        else {
            output += '\n' + util.inspect(meta);
        }
    }

    this.sendMessage(this.hostname, this.program, decoratedLevel, output);

    callback(null, true);
};

//
// ### function sendMessage (hostname, program, level, message)
// #### @hostname {string} Hostname of the source application.
// #### @program {string} Name of the source application
// #### @level {string} Log level to send
// #### @message {string} Actual log message
// Handles sending the message to the stream, or buffering if not
//
Papertrail.prototype.sendMessage = function(hostname, program, level, message) {

    var lines = message.split('\n');

    var msg = '';
    var gap = '';

    // If the incoming message has multiple lines, break them and format each
    // line as it's own message
    for (var i = 0; i < lines.length; i++) {

        // don't send extra message if our message ends with a newline
        if (lines[i].length == 0 && i == lines.length - 1) {
            break;
        }

        if (i == 1) {
            gap = '    ';
        }

        msg += "<25> " +
            getDate() + ' ' +
            hostname + ' ' +
            program + ' ' +
            level + ' ' +
            gap + lines[i] + '\r\n';
    }

    if (this.stream && this.stream.writable) {
        this.stream.write(msg);
    }
    else if (this.loggingEnabled) {
        this.buffer += msg;
    }
};

function getDate() {
    var d = new Date();
    return d.toLocaleString().split(' ')[1] + ' ' +
        pad2(d.getDate()) + ' ' +
        pad2(d.getHours()) + ':' +
        pad2(d.getMinutes()) + ':' +
        pad2(d.getSeconds());
}

function pad2(number) {
    return (number < 10 ? '0' : '') + number;
}<commit_msg>Comments console.log calls and improves error reporting.<commit_after>/*
 * winston-papertrail.js:
 *          Transport for logging to Papertrail Service
 *          (Special thanks to Charlie Robbins)
 *          www.papertrailapp.com
 *
 * (C) 2012 Ken Perkins
 * MIT LICENCE
 *
 */

var os = require('os'),
    tls = require('tls'),
    util = require('util'),
    winston = require('winston');

//
// ### function Papertrail (options)
// Constructor for the Papertrail transport object.
//
var Papertrail = exports.Papertrail = function(options) {

    var self = this;
    options = options || {};

    self.name = 'Papertrail';
    self.level = options.level || 'info';

    // Papertrail Service Host
    self.host = options.host;

    // Papertrail Service Port
    self.port = options.port;

    // Hostname of the current app
    self.hostname = options.hostname || os.hostname();

    // Program is an affordance for Papertrail to name the source of log entries
    self.program = options.program || 'default';

    // Papertrail enables alerts on search matching. Prodiving a decorator for the level
    // makes these searches more specific
    self.levelDecorators = options.levelDecorators || ['[', ']'];

    // Number of attempts before decaying reconnection
    self.attemptsBeforeDecay = options.attemptsBeforeDecay || 5;

    // Maximum number of reconnection attempts before disabling buffer
    self.maximumAttempts = options.maximumAttempts || 25;

    // Delay between normal attempts
    self.connectionDelay = options.connectionDelay || 1000;

    // Maximum delay between attempts
    self.maxDelayBetweenReconnection =
        options.maxDelayBetweenReconnection || 60000;

    self.currentRetries = 0;
    self.totalRetries = 0;
    self.buffer = '';
    self.loggingEnabled = true;

    // Error out if we don't have a host or port
    if (!self.host || !self.port) {
        throw new Error('Missing required parameters: host and port');
    }

    // Open the connection
    try {
        connectStream();
    }
    catch (e) {
        // TODO figure out a better way of sending errors from connection
        self.emit('error', e);
    }

    // Opens a connection to Papertrail
    function connectStream() {
        //console.log(getDate() +
        //    ' - [info]: Attempting to connect to Papertrail');

        self.stream = tls.connect(self.port, self.host, {}, onConnected);

        self.stream.on('error', function(err) {
            self.emit('error', err);

            // Papertrail has a bug that periodically resets their logging
            // services thus disconnecting your transport. We use setTimeout
            // to throttle the reconnection attempts in case Papertrail is
            // truly offline

            setTimeout(function() {

                // Increment our retry counts
                self.currentRetries++;
                self.totalRetries++;

                // Decay the retry rate exponentially up to max between attempts
                if ((self.connectionDelay < self.maxDelayBetweenReconnection) &&
                    (self.currentRetries >= self.attemptsBeforeDecay)) {
                    self.connectionDelay = self.connectionDelay * 2;
                    self.currentRetries = 0;
                }

                connectStream();

                // Stop buffering messages after a fixed number of retries.
                // This is to keep the buffer from growing unbounded
                if (self.loggingEnabled &&
                    (self.totalRetries >= (self.maximumAttempts))) {
                    self.loggingEnabled = false;
                    //console.log(getDate() +
                    //    ' - [error]: Max retries eclipsed, disabling buffering');
                }

            }, self.connectionDelay);
        });

        // If we have the stream end, simply reconnect
        self.stream.on('end', function() {
            //console.log(getDate() + ' - [info]: Reconnecting logging...');
            connectStream();
        });
    }

    function onConnected() {
        /*console.log(getDate() +
            ' - [info]: Logging connected to ' +
            self.host + ':' + self.port + ' via TLS');
        */
        // Reset our variables
        self.loggingEnabled = true;
        self.currentRetries = 0;
        self.totalRetries = 0;
        self.connectionDelay = 1000;

        // Did we get messages buffered
        if (self.buffer) {
            self.stream.write(self.buffer);
            self.buffer = '';
        }
    }
};

//
//
// Inherit from `winston.Transport` so you can take advantage
// of the base functionality and `.handleExceptions()`.
//
util.inherits(Papertrail, winston.Transport);

//
// Define a getter so that `winston.transports.Papertrail`
// is available and thus backwards compatible.
//
winston.transports.Papertrail = Papertrail;

//
// ### function log (level, msg, [meta], callback)
// #### @level {string} Level at which to log the message.
// #### @msg {string} Message to log
// #### @meta {Object} **Optional** Additional metadata to attach
// #### @callback {function} Continuation to respond to when complete.
// Core logging method exposed to Winston. Metadata is optional.
//
Papertrail.prototype.log = function(level, msg, meta, callback) {
    // If the logging buffer is disabled, drop the message on the floor
    if (!this.loggingEnabled) {
        return callback(null, true);
    }

    var decoratedLevel = this.levelDecorators[0] + level + this.levelDecorators[1],
        output = msg;

    if (meta) {
        if (typeof meta !== 'object') {
            output += ' ' + meta;
        }
        else {
            output += '\n' + util.inspect(meta);
        }
    }

    this.sendMessage(this.hostname, this.program, decoratedLevel, output);

    callback(null, true);
};

//
// ### function sendMessage (hostname, program, level, message)
// #### @hostname {string} Hostname of the source application.
// #### @program {string} Name of the source application
// #### @level {string} Log level to send
// #### @message {string} Actual log message
// Handles sending the message to the stream, or buffering if not
//
Papertrail.prototype.sendMessage = function(hostname, program, level, message) {

    var lines = message.split('\n');

    var msg = '';
    var gap = '';

    // If the incoming message has multiple lines, break them and format each
    // line as it's own message
    for (var i = 0; i < lines.length; i++) {

        // don't send extra message if our message ends with a newline
        if (lines[i].length == 0 && i == lines.length - 1) {
            break;
        }

        if (i == 1) {
            gap = '    ';
        }

        msg += "<25> " +
            getDate() + ' ' +
            hostname + ' ' +
            program + ' ' +
            level + ' ' +
            gap + lines[i] + '\r\n';
    }

    if (this.stream && this.stream.writable) {
        this.stream.write(msg);
    }
    else if (this.loggingEnabled) {
        this.buffer += msg;
    }
};

function getDate() {
    var d = new Date();
    return d.toLocaleString().split(' ')[1] + ' ' +
        pad2(d.getDate()) + ' ' +
        pad2(d.getHours()) + ':' +
        pad2(d.getMinutes()) + ':' +
        pad2(d.getSeconds());
}

function pad2(number) {
    return (number < 10 ? '0' : '') + number;
}
<|endoftext|>


<commit_before>/*
 * Created by whincwu on 2017/10/28
 */
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import {isFunction} from '../utils'

export default class XConsoleView extends Component {
  static propTypes = {
    plugins: PropTypes.arrayOf(PropTypes.object),
    onClose: PropTypes.func
  }

  static defaultProps = {
    plugins: [],
    onClose: () => {}
  }

  constructor (props) {
    super(props)
    const plugins = props.plugins
    this.state = {
      // actived plugin id
      value: plugins.length > 0 ? plugins[0].id : undefined
    }
    this.cachedPluginViews = {}

    // bind methods used in JSX
    this.changeTab = this.changeTab.bind(this)
  }

  componentDidMount () {
    const plugins = this.props.plugins
    this.changeTab(plugins[0] ? plugins[0].id : '')
  }

  changeTab (newValue, oldValue) {
    const plugins = this.props.plugins
    // trigger 'hide' event of disactived plugin before rerender
    const disactivedPlugin = plugins.find(plugin => plugin.id === oldValue)
    if (disactivedPlugin && isFunction(disactivedPlugin.onHide)) {
      disactivedPlugin.onHide()
    }
    // trigger 'show' event of actived plugin before rerender
    const activedPlugin = plugins.find(plugin => plugin.id === newValue)
    if (activedPlugin && isFunction(activedPlugin.onShow)) {
      activedPlugin.onShow()
    }

    this.setState({ value: newValue })
  }

  render () {
    const {plugins, onClose} = this.props
    const {value} = this.state
    return (

                {plugins.map(plugin => {
                  const isLast = plugins[plugins.length - 1].id === plugin.id
                  return (
 plugin.id !== value && this.changeTab(plugin.id, value)}
                  >
                    {plugin.name}

                  )
                })}

              {plugins.map(plugin => {
                let hidden = plugin.id !== value ? 'xc-tab-panel--hidden' : ''
                return (
                  <div key={plugin.id} className={`xc-tab-panel ${hidden}`}>
                    {isFunction(plugin.render) && plugin.render()}

                )
              })}

    )
  }
}
<commit_msg>refactor(XConsoleView): remove unused code<commit_after>/*
 * Created by whincwu on 2017/10/28
 */
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import {isFunction} from '../utils'

export default class XConsoleView extends Component {
  static propTypes = {
    plugins: PropTypes.arrayOf(PropTypes.object),
    onClose: PropTypes.func
  }

  static defaultProps = {
    plugins: [],
    onClose: () => {}
  }

  constructor (props) {
    super(props)
    const plugins = props.plugins
    this.state = {
      // actived plugin id
      value: plugins.length > 0 ? plugins[0].id : undefined
    }

    // bind methods used in JSX
    this.changeTab = this.changeTab.bind(this)
  }

  componentDidMount () {
    const plugins = this.props.plugins
    this.changeTab(plugins[0] ? plugins[0].id : '')
  }

  changeTab (newValue, oldValue) {
    const plugins = this.props.plugins
    // trigger 'hide' event of disactived plugin before rerender
    const disactivedPlugin = plugins.find(plugin => plugin.id === oldValue)
    if (disactivedPlugin && isFunction(disactivedPlugin.onHide)) {
      disactivedPlugin.onHide()
    }
    // trigger 'show' event of actived plugin before rerender
    const activedPlugin = plugins.find(plugin => plugin.id === newValue)
    if (activedPlugin && isFunction(activedPlugin.onShow)) {
      activedPlugin.onShow()
    }

    this.setState({ value: newValue })
  }

  render () {
    const {plugins, onClose} = this.props
    const {value} = this.state
    return (

                {plugins.map(plugin => {
                  const isLast = plugins[plugins.length - 1].id === plugin.id
                  return (
 plugin.id !== value && this.changeTab(plugin.id, value)}
                  >
                    {plugin.name}

                  )
                })}

              {plugins.map(plugin => {
                let hidden = plugin.id !== value ? 'xc-tab-panel--hidden' : ''
                return (
                  <div key={plugin.id} className={`xc-tab-panel ${hidden}`}>
                    {isFunction(plugin.render) && plugin.render()}

                )
              })}

    )
  }
}
<|endoftext|>


<commit_before>'use strict';

var rangy = require('rangy');
var highlighterMod = require('../../node_modules/rangy/lib/rangy-highlighter.js');
var classapplierMod = require('../../node_modules/rangy/lib/rangy-classapplier.js');
var StorageArea = chrome.storage.local;

console.log('\'Allo \'Allo! Content script test');

rangy.init();

var highlighter = rangy.createHighlighter();

highlighter.addClassApplier(rangy.createClassApplier("highlight", {
    ignoreWhiteSpace: true,
    tagNames: ["span", "a"]
}));

document.addEventListener("click", highlightIfNeeded, false);

function highlightIfNeeded() {
    StorageArea.get("highlight", function(items) {
        console.log('local storage: ' + items.highlight);
        if (items.highlight) {
            highlightSelectedText();
        }
    });

}

function highlightSelectedText() {
    highlighter.highlightSelection("highlight");
    var elements = document.getElementsByClassName("highlight");

    for (var i=0; i < elements.length; ++i) {
        elements[i].style.backgroundColor = "yellow";
    }

    console.log("clicked and highlighted");
}

<commit_msg>add highlight persistent functionality and refactor<commit_after>'use strict';
//////////////////// CONSTANTS ///////////////////
var STORAGE_SERIALIZED_STRING = "serializedString";
var STORAGE_HIGHLIGHT_MODE = "highlightMode";
var HIGHLIGHT_CLASS = "chronoteHighlight";
var HIGHLIGHT_COLOR = "yellow";
/////////////////////////////////////////////////

var rangy = require('rangy');
var highlighterMod = require('../../node_modules/rangy/lib/rangy-highlighter.js');
var classapplierMod = require('../../node_modules/rangy/lib/rangy-classapplier.js');
var StorageArea = chrome.storage.local;
var highlighter;

console.log('\'Allo \'Allo! Content script test');

rangy.init();
initHighlighter();
restoreHighlights();
document.addEventListener("click", highlightIfNeeded, false);

function initHighlighter() {
    highlighter = rangy.createHighlighter();
    highlighter.addClassApplier(rangy.createClassApplier(HIGHLIGHT_CLASS, {
        ignoreWhiteSpace: true,
        tagNames: ["span", "a"]
    }));
}

function restoreHighlights() {
    StorageArea.get(STORAGE_SERIALIZED_STRING, function(items) {
        if (items[STORAGE_SERIALIZED_STRING]) {
            highlighter.deserialize(items[STORAGE_SERIALIZED_STRING]);
            styleHighlightedText();
        }
    });
}

function highlightIfNeeded() {
    StorageArea.get(STORAGE_HIGHLIGHT_MODE, function(items) {
        if (items[STORAGE_HIGHLIGHT_MODE]) {
            highlighter.highlightSelection(HIGHLIGHT_CLASS);
            styleHighlightedText();
            storeHighlights(highlighter.serialize());
        }
    });
}

function storeHighlights(serializedString) {
    var highlights = {};
    highlights[STORAGE_SERIALIZED_STRING] = serializedString;
    StorageArea.set(highlights);
}

function styleHighlightedText() {
    var elements = document.getElementsByClassName(HIGHLIGHT_CLASS);

    for (var i=0; i < elements.length; ++i) {
        elements[i].style.backgroundColor = HIGHLIGHT_COLOR;
    }
}

<|endoftext|>


<commit_before>/*
  Copyright (C) 2012 Yusuke Suzuki <utatane.tea@gmail.com>

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/*jslint bitwise:true */
/*global esmangle:true, exports:true, define:true*/

(function (factory, global) {
    'use strict';

    function namespace(str, obj) {
        var i, iz, names, name;
        names = str.split('.');
        for (i = 0, iz = names.length; i < iz; ++i) {
            name = names[i];
            if (obj.hasOwnProperty(name)) {
                obj = obj[name];
            } else {
                obj = (obj[name] = {});
            }
        }
        return obj;
    }

    // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js,
    // and plain browser loading,
    if (typeof define === 'function' && define.amd) {
        define('esmangle', ['exports', 'esmangle/common', 'escope'], function(exports, common, escope) {
            factory(exports, common, escope);
        });
    } else if (typeof exports !== 'undefined') {
        exports.common = require('./lib/common');
        factory(exports, exports.common, require('escope'));
    } else {
        factory(namespace('esmangle', global), namespace('esmangle.common', global), namespace('escope', global));
    }
}(function (exports, common, escope) {
    'use strict';

    var VERSION,
        Syntax;

    // Sync with package.json.
    VERSION = '0.0.6-dev';

    Syntax = common.Syntax;

    // simple visitor implementation

    function passAsUnique(scope, name) {
        var i, iz;
        if (common.isKeyword(name) || common.isRestrictedWord(name)) {
            return false;
        }
        if (common.hasOwnProperty(scope.taints, name)) {
            return false;
        }
        for (i = 0, iz = scope.through.length; i < iz; ++i) {
            if (scope.through[i].identifier.name === name) {
                return false;
            }
        }
        return true;
    }

    function generateName(scope, tip) {
        do {
            tip = common.generateNextName(tip);
        } while (!passAsUnique(scope, tip));
        return tip;
    }

    function run(scope) {
        var i, iz, j, jz, variable, name, def, ref;

        if (scope.isStatic()) {
            name = '9';

            scope.variables.sort(function (a, b) {
                if (a.tainted) {
                    return 1;
                }
                if (b.tainted) {
                    return -1;
                }
                return (b.identifiers.length + b.references.length) - (a.identifiers.length + a.references.length);
            });

            for (i = 0, iz = scope.variables.length; i < iz; ++i) {
                variable = scope.variables[i];

                if (variable.tainted) {
                    continue;
                }

                // Because `arguments` definition is nothing.
                // But if `var arguments` is defined, identifiers.length !== 0
                // and this doesn't indicate arguments.
                if (variable.identifiers.length === 0) {
                    // do not change names because this is special name
                    continue;
                }

                name = generateName(scope, name);

                for (j = 0, jz = variable.identifiers.length; j < jz; ++j) {
                    def = variable.identifiers[j];
                    // change definition's name
                    def.name = name;
                }

                for (j = 0, jz = variable.references.length; j < jz; ++j) {
                    ref = variable.references[j];
                    // change reference's name
                    ref.identifier.name = name;
                }
            }
        }
    }

    function mangle(tree, options) {
        var result, manager, i, iz;

        if (options == null) {
            options = { destructive: false };
        }

        result = (options.destructive) ? tree : common.deepCopy(tree);
        manager = escope.analyze(result);

        // mangling names
        for (i = 0, iz = manager.scopes.length; i < iz; ++i) {
            run(manager.scopes[i]);
        }

        return result;
    }

    // recover some broken AST

    function recover(tree) {
        function trailingIf(node) {
            while (true) {
                switch (node.type) {
                case Syntax.IfStatement:
                    if (!node.alternate) {
                        return true;
                    }
                    node = node.alternate;
                    continue;

                case Syntax.LabeledStatement:
                case Syntax.ForStatement:
                case Syntax.ForInStatement:
                case Syntax.WhileStatement:
                case Syntax.WithStatement:
                    node = node.body;
                    continue;
                }
                return false;
            }
        }

        common.traverse(tree, {
            leave: function leave(node) {
                if (node.type === Syntax.IfStatement && node.alternate) {
                    // force wrap up or not
                    if (node.consequent.type !== Syntax.BlockStatement) {
                        if (trailingIf(node.consequent)) {
                            node.consequent = {
                                type: Syntax.BlockStatement,
                                body: [ node.consequent ]
                            };
                        }
                    }
                }
            }
        });

        return tree;
    }

    function iteration(tree, p, options) {
        var i, iz, pass, res, changed, statuses, passes, result;

        function addPass(pass) {
            var name;
            if (typeof pass !== 'function') {
                // automatic lookup pass (esmangle pass format)
                name = Object.keys(pass)[0];
                pass = pass[name];
            }
            if (pass.hasOwnProperty('passName')) {
                name = pass.passName;
            } else {
                name = pass.name;
            }
            passes.push(pass);
            statuses.push(true);
        }

        function fillStatuses(bool) {
            var i, iz;
            for (i = 0, iz = statuses.length; i < iz; ++i) {
                statuses[i] = bool;
            }
        }

        if (options == null) {
            options = { destructive: false };
        }

        result = (options.destructive) ? tree : common.deepCopy(tree);

        statuses = [];
        passes = [];

        for (i = 0, iz = p.length; i < iz; ++i) {
            addPass(p[i]);
        }

        do {
            changed = false;
            for (i = 0, iz = passes.length; i < iz; ++i) {
                pass = passes[i];
                if (statuses[i]) {
                    res = pass(result, { destructive: true });
                    if (res.modified) {
                        changed = true;
                        fillStatuses(true);
                    } else {
                        statuses[i] = false;
                    }
                    result = res.result;
                }
            }
        } while (changed);

        return result;
    }

    function optimize(tree, pipeline, options) {
        var i, iz, j, jz, section, pass;

        if (null == pipeline) {
            pipeline = [
                [
                    'lib/pass/hoist-variable-to-arguments',
                    'lib/pass/transform-dynamic-to-static-property-access',
                    'lib/pass/transform-dynamic-to-static-property-definition',
                    'lib/pass/reordering-function-declarations',
                    'lib/pass/remove-unused-label',
                    'lib/pass/remove-empty-statement',
                    'lib/pass/remove-wasted-blocks',
                    'lib/pass/transform-to-compound-assignment',
                    'lib/pass/transform-to-sequence-expression',
                    'lib/pass/transform-branch-to-expression',
                    'lib/pass/transform-typeof-undefined',
                    'lib/pass/reduce-sequence-expression',
                    'lib/pass/reduce-branch-jump',
                    'lib/pass/reduce-multiple-if-statements',
                    'lib/pass/dead-code-elimination',
                    'lib/pass/remove-side-effect-free-expressions',
                    'lib/pass/tree-based-constant-folding'
                ].map(exports.require),
                [
                    'lib/post/transform-static-to-dynamic-property-access',
                    'lib/post/rewrite-boolean',
                    'lib/post/rewrite-conditional-expression'
                ].map(exports.require)
            ];
        }

        for (i = 0, iz = pipeline.length; i < iz; ++i) {
            section = pipeline[i];
            // simple iterative pass
            if (common.isArray(section)) {
                tree = iteration(tree, section, options);
            } else if (section.once) {
                pass = section.pass;
                for (j = 0, jz = pass.length; j < jz; ++j) {
                    tree = pass[j](tree, options).result;
                }
            }
        }

        return recover(tree);
    }

    exports.version = VERSION;
    exports.mangle = mangle;
    exports.optimize = optimize;

    if (typeof exports.pass === 'undefined') {
        exports.pass = {};
    }

    if (typeof process !== 'undefined') {
        // for node.js environment
        exports.require = (function () {
            var fs = require('fs'),
                path = require('path'),
                root = path.join(path.dirname(fs.realpathSync(__filename)));

            return function() {
                var args = Array.prototype.slice.call(arguments);
                args[0] = path.join(root, args[0]);
                return require.apply(this, args);
            };
        }());
    }
}, this));
/* vim: set sw=4 ts=4 et tw=80 : */
<commit_msg>Insert exports.require guard<commit_after>/*
  Copyright (C) 2012 Yusuke Suzuki <utatane.tea@gmail.com>

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/*jslint bitwise:true */
/*global esmangle:true, exports:true, define:true*/

(function (factory, global) {
    'use strict';

    function namespace(str, obj) {
        var i, iz, names, name;
        names = str.split('.');
        for (i = 0, iz = names.length; i < iz; ++i) {
            name = names[i];
            if (obj.hasOwnProperty(name)) {
                obj = obj[name];
            } else {
                obj = (obj[name] = {});
            }
        }
        return obj;
    }

    // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js,
    // and plain browser loading,
    if (typeof define === 'function' && define.amd) {
        define('esmangle', ['exports', 'esmangle/common', 'escope'], function(exports, common, escope) {
            factory(exports, common, escope);
        });
    } else if (typeof exports !== 'undefined') {
        exports.common = require('./lib/common');
        factory(exports, exports.common, require('escope'));
    } else {
        factory(namespace('esmangle', global), namespace('esmangle.common', global), namespace('escope', global));
    }
}(function (exports, common, escope) {
    'use strict';

    var VERSION,
        Syntax;

    // Sync with package.json.
    VERSION = '0.0.6-dev';

    Syntax = common.Syntax;

    // simple visitor implementation

    function passAsUnique(scope, name) {
        var i, iz;
        if (common.isKeyword(name) || common.isRestrictedWord(name)) {
            return false;
        }
        if (common.hasOwnProperty(scope.taints, name)) {
            return false;
        }
        for (i = 0, iz = scope.through.length; i < iz; ++i) {
            if (scope.through[i].identifier.name === name) {
                return false;
            }
        }
        return true;
    }

    function generateName(scope, tip) {
        do {
            tip = common.generateNextName(tip);
        } while (!passAsUnique(scope, tip));
        return tip;
    }

    function run(scope) {
        var i, iz, j, jz, variable, name, def, ref;

        if (scope.isStatic()) {
            name = '9';

            scope.variables.sort(function (a, b) {
                if (a.tainted) {
                    return 1;
                }
                if (b.tainted) {
                    return -1;
                }
                return (b.identifiers.length + b.references.length) - (a.identifiers.length + a.references.length);
            });

            for (i = 0, iz = scope.variables.length; i < iz; ++i) {
                variable = scope.variables[i];

                if (variable.tainted) {
                    continue;
                }

                // Because `arguments` definition is nothing.
                // But if `var arguments` is defined, identifiers.length !== 0
                // and this doesn't indicate arguments.
                if (variable.identifiers.length === 0) {
                    // do not change names because this is special name
                    continue;
                }

                name = generateName(scope, name);

                for (j = 0, jz = variable.identifiers.length; j < jz; ++j) {
                    def = variable.identifiers[j];
                    // change definition's name
                    def.name = name;
                }

                for (j = 0, jz = variable.references.length; j < jz; ++j) {
                    ref = variable.references[j];
                    // change reference's name
                    ref.identifier.name = name;
                }
            }
        }
    }

    function mangle(tree, options) {
        var result, manager, i, iz;

        if (options == null) {
            options = { destructive: false };
        }

        result = (options.destructive) ? tree : common.deepCopy(tree);
        manager = escope.analyze(result);

        // mangling names
        for (i = 0, iz = manager.scopes.length; i < iz; ++i) {
            run(manager.scopes[i]);
        }

        return result;
    }

    // recover some broken AST

    function recover(tree) {
        function trailingIf(node) {
            while (true) {
                switch (node.type) {
                case Syntax.IfStatement:
                    if (!node.alternate) {
                        return true;
                    }
                    node = node.alternate;
                    continue;

                case Syntax.LabeledStatement:
                case Syntax.ForStatement:
                case Syntax.ForInStatement:
                case Syntax.WhileStatement:
                case Syntax.WithStatement:
                    node = node.body;
                    continue;
                }
                return false;
            }
        }

        common.traverse(tree, {
            leave: function leave(node) {
                if (node.type === Syntax.IfStatement && node.alternate) {
                    // force wrap up or not
                    if (node.consequent.type !== Syntax.BlockStatement) {
                        if (trailingIf(node.consequent)) {
                            node.consequent = {
                                type: Syntax.BlockStatement,
                                body: [ node.consequent ]
                            };
                        }
                    }
                }
            }
        });

        return tree;
    }

    function iteration(tree, p, options) {
        var i, iz, pass, res, changed, statuses, passes, result;

        function addPass(pass) {
            var name;
            if (typeof pass !== 'function') {
                // automatic lookup pass (esmangle pass format)
                name = Object.keys(pass)[0];
                pass = pass[name];
            }
            if (pass.hasOwnProperty('passName')) {
                name = pass.passName;
            } else {
                name = pass.name;
            }
            passes.push(pass);
            statuses.push(true);
        }

        function fillStatuses(bool) {
            var i, iz;
            for (i = 0, iz = statuses.length; i < iz; ++i) {
                statuses[i] = bool;
            }
        }

        if (options == null) {
            options = { destructive: false };
        }

        result = (options.destructive) ? tree : common.deepCopy(tree);

        statuses = [];
        passes = [];

        for (i = 0, iz = p.length; i < iz; ++i) {
            addPass(p[i]);
        }

        do {
            changed = false;
            for (i = 0, iz = passes.length; i < iz; ++i) {
                pass = passes[i];
                if (statuses[i]) {
                    res = pass(result, { destructive: true });
                    if (res.modified) {
                        changed = true;
                        fillStatuses(true);
                    } else {
                        statuses[i] = false;
                    }
                    result = res.result;
                }
            }
        } while (changed);

        return result;
    }

    function optimize(tree, pipeline, options) {
        var i, iz, j, jz, section, pass;

        if (null == pipeline) {
            if (!exports.require) {
                return tree;
            }
            pipeline = [
                [
                    'lib/pass/hoist-variable-to-arguments',
                    'lib/pass/transform-dynamic-to-static-property-access',
                    'lib/pass/transform-dynamic-to-static-property-definition',
                    'lib/pass/reordering-function-declarations',
                    'lib/pass/remove-unused-label',
                    'lib/pass/remove-empty-statement',
                    'lib/pass/remove-wasted-blocks',
                    'lib/pass/transform-to-compound-assignment',
                    'lib/pass/transform-to-sequence-expression',
                    'lib/pass/transform-branch-to-expression',
                    'lib/pass/transform-typeof-undefined',
                    'lib/pass/reduce-sequence-expression',
                    'lib/pass/reduce-branch-jump',
                    'lib/pass/reduce-multiple-if-statements',
                    'lib/pass/dead-code-elimination',
                    'lib/pass/remove-side-effect-free-expressions',
                    'lib/pass/tree-based-constant-folding'
                ].map(exports.require),
                [
                    'lib/post/transform-static-to-dynamic-property-access',
                    'lib/post/rewrite-boolean',
                    'lib/post/rewrite-conditional-expression'
                ].map(exports.require)
            ];
        }

        for (i = 0, iz = pipeline.length; i < iz; ++i) {
            section = pipeline[i];
            // simple iterative pass
            if (common.isArray(section)) {
                tree = iteration(tree, section, options);
            } else if (section.once) {
                pass = section.pass;
                for (j = 0, jz = pass.length; j < jz; ++j) {
                    tree = pass[j](tree, options).result;
                }
            }
        }

        return recover(tree);
    }

    exports.version = VERSION;
    exports.mangle = mangle;
    exports.optimize = optimize;

    if (typeof exports.pass === 'undefined') {
        exports.pass = {};
    }

    if (typeof process !== 'undefined') {
        // for node.js environment
        exports.require = (function () {
            var fs = require('fs'),
                path = require('path'),
                root = path.join(path.dirname(fs.realpathSync(__filename)));

            return function() {
                var args = Array.prototype.slice.call(arguments);
                args[0] = path.join(root, args[0]);
                return require.apply(this, args);
            };
        }());
    }
}, this));
/* vim: set sw=4 ts=4 et tw=80 : */
<|endoftext|>


<commit_before>'use strict';

/**
 * Defines the PublishManager which handles all the process in
 * package publication. Package is copied, extracted, interpreted
 * and video is sent to the specified video provider.
 *
 * @example
 *     var openVeoAPI = require("@openveo/api");
 *     var PublishManager = process.requirePublish("app/server/PublishManager.js");
 *     var db = openVeoAPI.applicationStorage.getDatabase();
 *
 *     var publishManager = new PublishManager(db);
 *
 *     // Listen to errors dispatched by the publish manager
 *     publishManager.on("error", function(error){
 *       // Do something
 *     });
 *
 *     // Listen to complete publications dispatched by the publish manager
 *     publishManager.on("complete", function(mediaPackage){
 *       // Do something
 *     });
 *
 *     publishManager.publish({
 *       "type" : "vimeo", // The video platform to use
 *       "originalPackagePath" : "/tmp/video-package.tar" // Path of package to publish
 *     });
 *
 * @module publish-manager
 */

var util = require('util');
var events = require('events');
var path = require('path');
var shortid = require('shortid');
var openVeoAPI = require('@openveo/api');
var Package = process.requirePublish('app/server/packages/Package.js');
var VideoModel = process.requirePublish('app/server/models/VideoModel.js');
var configDir = openVeoAPI.fileSystem.getConfDir();
var publishConf = require(path.join(configDir, 'publish/publishConf.json'));
var errors = process.requirePublish('app/server/packages/errors.js');
var maxConcurrentPublish = publishConf.maxConcurrentPublish || 3;
var acceptedPackagesExtensions = ['tar', 'mp4'];

/**
 * Defines a custom error with an error code.
 *
 * @class PublishError
 * @constructor
 * @extends Error
 * @param {String} message The error message
 * @param {String} code The error code
 */
function PublishError(message, code) {
  this.name = 'PublishError';
  this.message = message || '';
  this.code = code;
}

util.inherits(PublishError, Error);

/**
 * Creates a PublishManager to retrieve, validate and publish
 * a package.
 *
 * @class PublishManager
 * @constructor
 * @param {Database} database A database to store information about
 * the package
 * PublishManager emits the following events :
 *  - Event *error* An error occured
 *    - **Error** The error
 *  - Event *complete* A package was successfully published
 *    - **Object** The published package
 *  - Event *retry* A package in error restarts
 *    - **Object** The package
 *  - Event *upload* A package stuck in "waiting for upload" state starts upload process
 *    - **Object** The package
 */
function PublishManager(database) {
  openVeoAPI.applicationStorage.setDatabase(database);

  /**
   * Packages waiting to be processed.
   *
   * @property queue
   * @type Array
   */
  this.queue = [];

  /**
   * Packages being processed.
   *
   * @property pendingPackages
   * @type Array
   */
  this.pendingPackages = [];

  /**
   * Video model.
   *
   * @property videoModel
   * @type VideoModel
   */
  this.videoModel = new VideoModel();
}

util.inherits(PublishManager, events.EventEmitter);
module.exports = PublishManager;

/**
 * Removes a package from pending packages.
 *
 * @method removeFromPending
 * @private
 * @param {Number} packageId The package id to remove
 */
function removeFromPending(packageId) {
  for (var i = 0; i < this.pendingPackages.length; i++) {
    if (this.pendingPackages[i]['id'] === packageId)
      this.pendingPackages.splice(i, 1);
  }
}

/**
 * Handles Package error event.
 *
 * @method onError
 * @private
 * @param {Error} error The dispatched errors
 * @param {Object} mediaPackage The package on error
 */
function onError(error, mediaPackage) {

  // Remove video from pending videos
  removeFromPending.call(this, mediaPackage.id);

  // Publish pending package from FIFO queue
  if (this.queue.length)
    this.publish(this.queue.shift(0));

  // Add package id to the error message
  if (error)
    error.message += ' (' + mediaPackage.id + ')';

  this.emit('error', error, error.code);
}

/**
 * Handles Package complete event.
 *
 * @method onComplete
 * @private
 * @param {Object} mediaPackage The package on error
 */
function onComplete(mediaPackage) {

  // Remove package from pending packages
  removeFromPending.call(this, mediaPackage.id);

  // Publish pending package from FIFO queue
  if (this.queue.length)
    this.publish(this.queue.shift(0));

  this.emit('complete', mediaPackage);
}

/**
 * Creates a media package manager corresponding to the package type.
 *
 * @method createMediaPackageManager
 * @private
 * @param {Object} mediaPackage The media package to manage
 * @return {Package} A Package manager
 */
function createMediaPackageManager(mediaPackage) {
  var self = this;
  var mediaPackageManager = Package.getPackage(mediaPackage.packageType, mediaPackage);

  // Handle errors from package manager
  mediaPackageManager.on('error', function(error) {
    onError.call(self, error, mediaPackage);
  });

  // Handle complete events from package manager
  mediaPackageManager.on('complete', function(completePackage) {
    onComplete.call(self, completePackage);
  });

  return mediaPackageManager;
}

/**
 * Adds packages to the list of pending packages.
 *
 * @method addPackage
 * @private
 * @param {Object} mediaPackage The package to add to pending packages
 * @return {Boolean} true if the package is successfully to pending packages
 * false if it has been added to queue
 */
function addPackage(mediaPackage) {
  process.logger.debug('Actually ' + this.pendingPackages.length + ' pending packages');
  var idAllreadyPending = this.pendingPackages.filter(function(pendingPackage) {
    return mediaPackage.id === pendingPackage.id;
  });

  // Too much pending packages
  if (this.pendingPackages.length >= maxConcurrentPublish || idAllreadyPending) {

    // Add package to queue
    this.queue.push(mediaPackage);
    process.logger.debug('Add package ' + mediaPackage.originalPackagePath + '(' + mediaPackage.id + ') to queue');
    return false;
  } else {

    // Process can deal with the package
    process.logger.debug('Add package ' + mediaPackage.originalPackagePath +
                      '(' + mediaPackage.id + ') to pending packages');

    // Add package to the list of pending packages
    this.pendingPackages.push(mediaPackage);
    return true;
  }
}

/**
 * Tests if package is a valid one depending on the given type.
 *
 * @method isValidPackageType
 * @private
 * @param {String} type The package type
 * @return {Boolean} true if the package is valid false otherwise
 */
function isValidPackageType(type) {
  return acceptedPackagesExtensions.indexOf(type) >= 0;
}

/**
 * Publishes the given package.
 *
 * Package must be one of the supported type.
 *
 * @example
 *     // video package object example
 *     {
 *       "type": "vimeo", // Platform type
 *       "originalPackagePath": "/tmp/2015-03-09_16-53-10_rich-media.tar" // Package file
 *     }
 *
 * @method publish
 * @param {Object} mediaPackage Package to publish
 */
PublishManager.prototype.publish = function(mediaPackage) {
  var self = this;

  if (mediaPackage && (typeof mediaPackage === 'object')) {
    var pathDescriptor = path.parse(mediaPackage.originalPackagePath);

    // Generate a package id
    mediaPackage.id = shortid.generate();

    // Find out package type depending on package extension
    mediaPackage.packageType = pathDescriptor.ext.slice(1);

    // Use file name as media title
    mediaPackage.title = pathDescriptor.name;

    // Validate extension
    if (!isValidPackageType(mediaPackage.packageType))
      return this.emit('error', new PublishError('Package type is not valid (' + mediaPackage.packageType + ')',
                                                 errors.INVALID_PACKAGE_TYPE_ERROR));

    this.videoModel.get({originalPackagePath: mediaPackage.originalPackagePath}, function(error, videos) {
      if (error) {
        self.emit('error', new PublishError('Getting medias with original package path "' +
                                            mediaPackage.originalPackagePath + '" failed with message : ' +
                                            error.message,
                                                 errors.UNKNOWN_ERROR));
      } else if (!videos || !videos.length) {

        // Package does not exist
        // Publish it
        var mediaPackageManager = createMediaPackageManager.call(self, mediaPackage);
        mediaPackageManager.init(Package.PACKAGE_SUBMITTED_STATE, Package.INIT_TRANSITION);

        // Package can be added to pending packages
        if (addPackage.call(self, mediaPackage))
          mediaPackageManager.executeTransition(Package.INIT_TRANSITION);

      }

    });

  } else
    this.emit('error', new PublishError('mediaPackage argument must be an Object', errors.UNKNOWN_ERROR));
};

/**
 * Retries publishing a package which is on error.
 *
 * @method retry
 * @param {String} packageId The id of the package on error
 * @param {Boolean} forceRetry Force retrying a package no matter its state
 */
PublishManager.prototype.retry = function(packageId, forceRetry) {
  if (packageId) {
    var self = this;

    // Retrieve package information
    this.videoModel.getOne(packageId, null, function(error, mediaPackage) {
      if (error) {
        self.emit('error', new PublishError('Getting package ' + packageId + ' failed with message : ' + error.message,
                                    errors.UNKNOWN_ERROR));
      } else if (!mediaPackage) {

        // Package does not exist
        self.emit('error', new PublishError('Cannot retry package ' + packageId + ' (not found)',
                                            errors.PACKAGE_NOT_FOUND_ERROR));

      } else if (mediaPackage.state === VideoModel.ERROR_STATE || forceRetry) {

        // Got package information
        // Package is indeed in error
        self.videoModel.updateState(mediaPackage.id, VideoModel.PENDING_STATE, function() {

          // Retry officially started
          self.emit('retry', mediaPackage);

        });

        var mediaPackageManager = createMediaPackageManager.call(self, mediaPackage);
        process.logger.info('Retry package ' + mediaPackage.id);
        mediaPackageManager.init(mediaPackage.lastState, mediaPackage.lastTransition);

        // Package can be added to pending packages
        if (addPackage.call(self, mediaPackage))
          mediaPackageManager.executeTransition(mediaPackage.lastTransition);
      }

    });
  }
};

/**
 * Retries publishing all packages in a non stable state.
 *
 * Stable states are :
 * - VideoModel.ERROR_STATE
 * - VideoModel.WAITING_FOR_UPLOAD_STATE
 * - VideoModel.READY_STATE
 * - VideoModel.PUBLISHED_STATE
 *
 * @method retryAll
 */
PublishManager.prototype.retryAll = function() {
  var self = this;

  // Retrieve all packages in a non stable state
  this.videoModel.get({
    state: {
      $nin: [
        VideoModel.ERROR_STATE,
        VideoModel.WAITING_FOR_UPLOAD_STATE,
        VideoModel.READY_STATE,
        VideoModel.PUBLISHED_STATE
      ]
    }
  }, function(error, mediaPackages) {
    if (error)
      return self.emit('error', new PublishError('Getting packages in non stable state failed with message : ' +
                                                 error.message,
                                            errors.UNKNOWN_ERROR));

    mediaPackages.forEach(function(mediaPackage) {
      self.retry(mediaPackage.id, true);
    });

  });

};

/**
 * Uploads a media blocked in waiting to upload state.
 *
 * @method upload
 * @param {String} packageId The id of the package waiting to be uploaded
 * @param {String} platform The type of the video platform to upload to
 */
PublishManager.prototype.upload = function(packageId, platform) {
  if (packageId && platform) {
    var self = this;

    // Retrieve package information
    this.videoModel.getOne(packageId, null, function(error, mediaPackage) {
      if (error) {
        self.emit('error', new PublishError('Getting package ' + packageId + ' failed with message : ' + error.message,
                                            errors.UNKNOWN_ERROR));
      } else if (!mediaPackage) {

        // Package does not exist
        self.emit('error', new PublishError('Cannot upload package ' + packageId + ' (not found)',
                                            errors.PACKAGE_NOT_FOUND_ERROR));

      } else if (mediaPackage.state === VideoModel.WAITING_FOR_UPLOAD_STATE) {

        // Package is indeed waiting for upload
        self.videoModel.updateState(mediaPackage.id, VideoModel.PENDING_STATE, function() {

          // Upload officially started
          self.emit('upload', mediaPackage);

        });
        self.videoModel.updateType(mediaPackage.id, platform);

        var mediaPackageManager = createMediaPackageManager.call(self, mediaPackage);
        process.logger.info('Force upload package ' + mediaPackage.id);
        mediaPackage.type = platform;
        mediaPackageManager.init(mediaPackage.lastState, mediaPackage.lastTransition);

        // Package can be added to pending packages
        if (addPackage.call(self, mediaPackage))
          mediaPackageManager.executeTransition(mediaPackage.lastTransition);

      }

    });
  }
};
<commit_msg>Delete pending package by its OriginalFileName too<commit_after>'use strict';

/**
 * Defines the PublishManager which handles all the process in
 * package publication. Package is copied, extracted, interpreted
 * and video is sent to the specified video provider.
 *
 * @example
 *     var openVeoAPI = require("@openveo/api");
 *     var PublishManager = process.requirePublish("app/server/PublishManager.js");
 *     var db = openVeoAPI.applicationStorage.getDatabase();
 *
 *     var publishManager = new PublishManager(db);
 *
 *     // Listen to errors dispatched by the publish manager
 *     publishManager.on("error", function(error){
 *       // Do something
 *     });
 *
 *     // Listen to complete publications dispatched by the publish manager
 *     publishManager.on("complete", function(mediaPackage){
 *       // Do something
 *     });
 *
 *     publishManager.publish({
 *       "type" : "vimeo", // The video platform to use
 *       "originalPackagePath" : "/tmp/video-package.tar" // Path of package to publish
 *     });
 *
 * @module publish-manager
 */

var util = require('util');
var events = require('events');
var path = require('path');
var shortid = require('shortid');
var openVeoAPI = require('@openveo/api');
var Package = process.requirePublish('app/server/packages/Package.js');
var VideoModel = process.requirePublish('app/server/models/VideoModel.js');
var configDir = openVeoAPI.fileSystem.getConfDir();
var publishConf = require(path.join(configDir, 'publish/publishConf.json'));
var errors = process.requirePublish('app/server/packages/errors.js');
var maxConcurrentPublish = publishConf.maxConcurrentPublish || 3;
var acceptedPackagesExtensions = ['tar', 'mp4'];

/**
 * Defines a custom error with an error code.
 *
 * @class PublishError
 * @constructor
 * @extends Error
 * @param {String} message The error message
 * @param {String} code The error code
 */
function PublishError(message, code) {
  this.name = 'PublishError';
  this.message = message || '';
  this.code = code;
}

util.inherits(PublishError, Error);

/**
 * Creates a PublishManager to retrieve, validate and publish
 * a package.
 *
 * @class PublishManager
 * @constructor
 * @param {Database} database A database to store information about
 * the package
 * PublishManager emits the following events :
 *  - Event *error* An error occured
 *    - **Error** The error
 *  - Event *complete* A package was successfully published
 *    - **Object** The published package
 *  - Event *retry* A package in error restarts
 *    - **Object** The package
 *  - Event *upload* A package stuck in "waiting for upload" state starts upload process
 *    - **Object** The package
 */
function PublishManager(database) {
  openVeoAPI.applicationStorage.setDatabase(database);

  /**
   * Packages waiting to be processed.
   *
   * @property queue
   * @type Array
   */
  this.queue = [];

  /**
   * Packages being processed.
   *
   * @property pendingPackages
   * @type Array
   */
  this.pendingPackages = [];

  /**
   * Video model.
   *
   * @property videoModel
   * @type VideoModel
   */
  this.videoModel = new VideoModel();
}

util.inherits(PublishManager, events.EventEmitter);
module.exports = PublishManager;

/**
 * Removes a package from pending packages.
 *
 * @method removeFromPending
 * @private
 * @param {Object} mediaPackage The package to remove
 */
function removeFromPending(mediaPackage) {

  for (var i = 0; i < this.pendingPackages.length; i++) {
    if (this.pendingPackages[i]['id'] === mediaPackage.id) {
      this.pendingPackages.splice(i, 1);
      break;
    }
  }
  for (var j = 0; j < this.pendingPackages.length; j++) {
    if (this.pendingPackages[j]['originalFileName'] === mediaPackage.originalFileName) {
      this.pendingPackages.splice(j, 1);
      break;
    }
  }
  process.logger.debug('Package ' + mediaPackage.id + ' from ' +
   mediaPackage.originalFileName + ' is removed from pendingPackages');
}

/**
 * Handles Package error event.
 *
 * @method onError
 * @private
 * @param {Error} error The dispatched errors
 * @param {Object} mediaPackage The package on error
 */
function onError(error, mediaPackage) {

  // Remove video from pending videos
  removeFromPending.call(this, mediaPackage);

  // Publish pending package from FIFO queue
  if (this.queue.length)
    this.publish(this.queue.shift(0));

  // Add package id to the error message
  if (error)
    error.message += ' (' + mediaPackage.id + ')';

  this.emit('error', error, error.code);
}

/**
 * Handles Package complete event.
 *
 * @method onComplete
 * @private
 * @param {Object} mediaPackage The package on error
 */
function onComplete(mediaPackage) {

  // Remove package from pending packages
  removeFromPending.call(this, mediaPackage);

  // Publish pending package from FIFO queue
  if (this.queue.length)
    this.publish(this.queue.shift(0));

  this.emit('complete', mediaPackage);
}

/**
 * Creates a media package manager corresponding to the package type.
 *
 * @method createMediaPackageManager
 * @private
 * @param {Object} mediaPackage The media package to manage
 * @return {Package} A Package manager
 */
function createMediaPackageManager(mediaPackage) {
  var self = this;
  var mediaPackageManager = Package.getPackage(mediaPackage.packageType, mediaPackage);

  // Handle errors from package manager
  mediaPackageManager.on('error', function(error) {
    onError.call(self, error, mediaPackage);
  });

  // Handle complete events from package manager
  mediaPackageManager.on('complete', function(completePackage) {
    onComplete.call(self, completePackage);
  });

  return mediaPackageManager;
}

/**
 * Adds packages to the list of pending packages.
 *
 * @method addPackage
 * @private
 * @param {Object} mediaPackage The package to add to pending packages
 * @return {Boolean} true if the package is successfully to pending packages
 * false if it has been added to queue
 */
function addPackage(mediaPackage) {
  process.logger.debug('Actually ' + this.pendingPackages.length + ' pending packages');
  var idAllreadyPending = this.pendingPackages.filter(function(pendingPackage) {
    return mediaPackage.originalFileName === pendingPackage.originalFileName;
  });

  // Too much pending packages
  if (this.pendingPackages.length >= maxConcurrentPublish || idAllreadyPending.length) {

    // Add package to queue
    this.queue.push(mediaPackage);
    process.logger.debug('Add package ' + mediaPackage.originalPackagePath + '(' + mediaPackage.id + ') to queue');
    return false;
  } else {

    // Process can deal with the package
    process.logger.debug('Add package ' + mediaPackage.originalPackagePath +
                      '(' + mediaPackage.id + ') to pending packages');

    // Add package to the list of pending packages
    this.pendingPackages.push(mediaPackage);
    return true;
  }
}

/**
 * Tests if package is a valid one depending on the given type.
 *
 * @method isValidPackageType
 * @private
 * @param {String} type The package type
 * @return {Boolean} true if the package is valid false otherwise
 */
function isValidPackageType(type) {
  return acceptedPackagesExtensions.indexOf(type) >= 0;
}

/**
 * Publishes the given package.
 *
 * Package must be one of the supported type.
 *
 * @example
 *     // video package object example
 *     {
 *       "type": "vimeo", // Platform type
 *       "originalPackagePath": "/tmp/2015-03-09_16-53-10_rich-media.tar" // Package file
 *     }
 *
 * @method publish
 * @param {Object} mediaPackage Package to publish
 */
PublishManager.prototype.publish = function(mediaPackage) {
  var self = this;

  if (mediaPackage && (typeof mediaPackage === 'object')) {
    var pathDescriptor = path.parse(mediaPackage.originalPackagePath);

    // Generate a package id
    mediaPackage.id = shortid.generate();

    // Find out package type depending on package extension
    mediaPackage.packageType = pathDescriptor.ext.slice(1);

    // Use file name as media title
    mediaPackage.title = pathDescriptor.name;

    // Validate extension
    if (!isValidPackageType(mediaPackage.packageType))
      return this.emit('error', new PublishError('Package type is not valid (' + mediaPackage.packageType + ')',
                                                 errors.INVALID_PACKAGE_TYPE_ERROR));

    this.videoModel.get({originalPackagePath: mediaPackage.originalPackagePath}, function(error, videos) {
      if (error) {
        self.emit('error', new PublishError('Getting medias with original package path "' +
                                            mediaPackage.originalPackagePath + '" failed with message : ' +
                                            error.message,
                                                 errors.UNKNOWN_ERROR));
      } else if (!videos || !videos.length) {

        // Package does not exist
        // Publish it
        var mediaPackageManager = createMediaPackageManager.call(self, mediaPackage);
        mediaPackageManager.init(Package.PACKAGE_SUBMITTED_STATE, Package.INIT_TRANSITION);

        // Package can be added to pending packages
        if (addPackage.call(self, mediaPackage))
          mediaPackageManager.executeTransition(Package.INIT_TRANSITION);

      }

    });

  } else
    this.emit('error', new PublishError('mediaPackage argument must be an Object', errors.UNKNOWN_ERROR));
};

/**
 * Retries publishing a package which is on error.
 *
 * @method retry
 * @param {String} packageId The id of the package on error
 * @param {Boolean} forceRetry Force retrying a package no matter its state
 */
PublishManager.prototype.retry = function(packageId, forceRetry) {
  if (packageId) {
    var self = this;

    // Retrieve package information
    this.videoModel.getOne(packageId, null, function(error, mediaPackage) {
      if (error) {
        self.emit('error', new PublishError('Getting package ' + packageId + ' failed with message : ' + error.message,
                                    errors.UNKNOWN_ERROR));
      } else if (!mediaPackage) {

        // Package does not exist
        self.emit('error', new PublishError('Cannot retry package ' + packageId + ' (not found)',
                                            errors.PACKAGE_NOT_FOUND_ERROR));

      } else if (mediaPackage.state === VideoModel.ERROR_STATE || forceRetry) {

        // Got package information
        // Package is indeed in error
        self.videoModel.updateState(mediaPackage.id, VideoModel.PENDING_STATE, function() {

          // Retry officially started
          self.emit('retry', mediaPackage);

        });

        var mediaPackageManager = createMediaPackageManager.call(self, mediaPackage);
        process.logger.info('Retry package ' + mediaPackage.id);
        mediaPackageManager.init(mediaPackage.lastState, mediaPackage.lastTransition);

        // Package can be added to pending packages
        if (addPackage.call(self, mediaPackage))
          mediaPackageManager.executeTransition(mediaPackage.lastTransition);
      }

    });
  }
};

/**
 * Retries publishing all packages in a non stable state.
 *
 * Stable states are :
 * - VideoModel.ERROR_STATE
 * - VideoModel.WAITING_FOR_UPLOAD_STATE
 * - VideoModel.READY_STATE
 * - VideoModel.PUBLISHED_STATE
 *
 * @method retryAll
 */
PublishManager.prototype.retryAll = function() {
  var self = this;

  // Retrieve all packages in a non stable state
  this.videoModel.get({
    state: {
      $nin: [
        VideoModel.ERROR_STATE,
        VideoModel.WAITING_FOR_UPLOAD_STATE,
        VideoModel.READY_STATE,
        VideoModel.PUBLISHED_STATE
      ]
    }
  }, function(error, mediaPackages) {
    if (error)
      return self.emit('error', new PublishError('Getting packages in non stable state failed with message : ' +
                                                 error.message,
                                            errors.UNKNOWN_ERROR));

    mediaPackages.forEach(function(mediaPackage) {
      self.retry(mediaPackage.id, true);
    });

  });

};

/**
 * Uploads a media blocked in waiting to upload state.
 *
 * @method upload
 * @param {String} packageId The id of the package waiting to be uploaded
 * @param {String} platform The type of the video platform to upload to
 */
PublishManager.prototype.upload = function(packageId, platform) {
  if (packageId && platform) {
    var self = this;

    // Retrieve package information
    this.videoModel.getOne(packageId, null, function(error, mediaPackage) {
      if (error) {
        self.emit('error', new PublishError('Getting package ' + packageId + ' failed with message : ' + error.message,
                                            errors.UNKNOWN_ERROR));
      } else if (!mediaPackage) {

        // Package does not exist
        self.emit('error', new PublishError('Cannot upload package ' + packageId + ' (not found)',
                                            errors.PACKAGE_NOT_FOUND_ERROR));

      } else if (mediaPackage.state === VideoModel.WAITING_FOR_UPLOAD_STATE) {

        // Package is indeed waiting for upload
        self.videoModel.updateState(mediaPackage.id, VideoModel.PENDING_STATE, function() {

          // Upload officially started
          self.emit('upload', mediaPackage);

        });
        self.videoModel.updateType(mediaPackage.id, platform);

        var mediaPackageManager = createMediaPackageManager.call(self, mediaPackage);
        process.logger.info('Force upload package ' + mediaPackage.id);
        mediaPackage.type = platform;
        mediaPackageManager.init(mediaPackage.lastState, mediaPackage.lastTransition);

        // Package can be added to pending packages
        if (addPackage.call(self, mediaPackage))
          mediaPackageManager.executeTransition(mediaPackage.lastTransition);

      }

    });
  }
};
<|endoftext|>


<commit_before>angular.module(PKG.name + '.services')
  .factory('myFileUploader', function($rootScope, $q, $window, cfpLoadingBar) {
    function upload(fileObj){
      var deferred = $q.defer();

      if (!$rootScope.currentUser || !$rootScope.currentUser.token) {
        deferred.reject(400);
      }

      var xhr = new $window.XMLHttpRequest();
      xhr.upload.addEventListener('progress', function (e) {
        if (e.type === 'progress') {
          console.log('App Upload in progress!');
        }
      });
      var path = fileObj.path;
      xhr.open('POST', path, true);
      xhr.setRequestHeader('Content-type', 'application/octet-stream');
      xhr.setRequestHeader('X-Archive-Name', fileObj.file.name);
      xhr.setRequestHeader('X-ApiKey', '');
      xhr.setRequestHeader('Authorization', 'Bearer ' + $rootScope.currentUser.token);
      xhr.send(fileObj.file);
      cfpLoadingBar.start();
      xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
          if (xhr.status > 399){
            deferred.reject(xhr.response);
          } else {
            deferred.resolve();
          }
          cfpLoadingBar.complete();
        }
      };
      return deferred.promise;
    }
    return {
      upload: upload
    };
  });
<commit_msg>handle local case<commit_after>angular.module(PKG.name + '.services')
  .factory('myFileUploader', function($rootScope, $q, $window, $alert, cfpLoadingBar) {
    function upload(fileObj){
      var deferred = $q.defer();
      console.log($rootScope.currentUser);
      if (!$rootScope.currentUser) {
        deferred.reject(400);
        $alert({
          title: 'Must specify user: ',
          content: 'Could not find user.',
          type: 'danger'
        });
      } else {
        var xhr = new $window.XMLHttpRequest();
        xhr.upload.addEventListener('progress', function (e) {
          if (e.type === 'progress') {
            console.log('App Upload in progress!');
          }
        });
        var path = fileObj.path;
        xhr.open('POST', path, true);
        xhr.setRequestHeader('Content-type', 'application/octet-stream');
        xhr.setRequestHeader('X-Archive-Name', fileObj.file.name);
        xhr.setRequestHeader('X-ApiKey', '');
        xhr.setRequestHeader('Authorization', 'Bearer ' + $rootScope.currentUser.token);
        xhr.send(fileObj.file);
        cfpLoadingBar.start();
        xhr.onreadystatechange = function () {
          if (xhr.readyState === 4) {
            if (xhr.status > 399){
              deferred.reject(xhr.response);
            } else {
              deferred.resolve();
            }
            cfpLoadingBar.complete();
          }
        };
      }
      return deferred.promise;
    }
    return {
      upload: upload
    };
  });
<|endoftext|>


<commit_before>/* eslint-disable no-use-before-define */
import React from 'react-native';
import Camera from 'react-native-camera';
import RNFS from 'react-native-fs';
import { connect } from 'react-redux';
import { postQuilt } from '../actions/index';

const {
  Component,
  Dimensions,
  StyleSheet,
  Text,
  View,
} = React;

class ShowCamera extends Component {
  constructor(props) {
    super(props);
    this.cameraRef = this.cameraRef.bind(this);
    this._onStartCapture = this._onStartCapture.bind(this);
    this._onStopCapture = this._onStopCapture.bind(this);
    this.onCapturePress = this.onCapturePress.bind(this);

    // refactor into redux?
    this.state = {
      isCapturing: false,
    }
  }

  // testing video posting, should be moved into action creator in future
  // also, add spinners,
  // add catches

  _onStartCapture() {
    console.log('start capturing');
    this.setState({
      isCapturing: true,
    });
    this.camera.capture().then((file) => {
      console.log('reading file')
      return RNFS.readFile(file, 'base64');
    }).then((data) => {
      console.log('sending data');

      let options = {
        username: 'tasiov',
        friends: ['josh', 'griffin'],
        quilt: {
          filename: 'quilt43',
          title: 'quiltTitle',
          theme: 'quiltTheme',
          video: data,
        },
      }

      this.props.postQuilt(options);
    });
  }

  _onStopCapture() {
    console.log('stop capturing');
    this.setState({
      isCapturing: false,
    });
    this.camera.stopCapture()
  }

  onCapturePress() {
    if (!this.state.isCapturing) {
      this._onStartCapture();
    } else {
      this._onStopCapture();
    }
  }

  cameraRef(cam) {
    // not sure the reason for this function yet
    this.camera = cam;
  }

  render() {
    return (
      <View style={styles.container}>
        <Camera
          ref={this.cameraRef}
          style={styles.preview}
          aspect={'fill'}
          captureTarget={Camera.constants.CaptureTarget.temp}
          captureMode={Camera.constants.CaptureMode.video}
        >
          <Text style={styles.capture} onPress={this.onCapturePress}>
            [CAPTURE]
          </Text>
        </Camera>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  preview: {
    flex: 1,
    justifyContent: 'flex-end',
    alignItems: 'center',
    height: Dimensions.get('window').height,
    width: Dimensions.get('window').width,
  },
  capture: {
    flex: 0,
    backgroundColor: '#fff',
    borderRadius: 5,
    color: '#000',
    padding: 10,
    margin: 40,
  },
});

// get the state of the current quilt
// which will be passed with the video
// to action creator to post data
function mapStateToProps (state) {
  const currentQuilt = state.get('currentQuilt');
  return {
    currentQuilt: currentQuilt.toObject(),
  };
}

// dispatch postQuilt with previous current quilt state plus video data
function mapDispatchToProps (dispatch) {
  return {
    postQuilt: (data) => {
      dispatch(postQuilt(data));
    },
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(ShowCamera);
<commit_msg>update post quilt to remove dev testing<commit_after>/* eslint-disable no-use-before-define */
import React from 'react-native';
import Camera from 'react-native-camera';
import RNFS from 'react-native-fs';
import { connect } from 'react-redux';
import { postQuilt } from '../actions/index';

const {
  Component,
  Dimensions,
  StyleSheet,
  Text,
  View,
} = React;

class ShowCamera extends Component {
  constructor(props) {
    super(props);
    this.cameraRef = this.cameraRef.bind(this);
    this._onStartCapture = this._onStartCapture.bind(this);
    this._onStopCapture = this._onStopCapture.bind(this);
    this.onCapturePress = this.onCapturePress.bind(this);

    // refactor into redux?
    this.state = {
      isCapturing: false,
    }
  }

  // testing video posting, should be moved into action creator in future
  // also, add spinners,
  // add catches

  _onStartCapture() {
    console.log('start capturing');
    this.setState({
      isCapturing: true,
    });
    this.camera.capture().then((file) => {
      console.log('reading file')
      return RNFS.readFile(file, 'base64');
    }).then((data) => {
      console.log('sending data');

      this.props.postQuilt(Object.assign(this.props.currentQuilt, {
        vid: data,
      }));

      // let options = {
      //   username: 'tasiov',
      //   friends: ['josh', 'griffin'],
      //   quilt: {
      //     filename: 'quilt43',
      //     title: 'quiltTitle',
      //     theme: 'quiltTheme',
      //     video: data,
      //   },
      // }
      //
      // this.props.postQuilt(options);
    });
  }

  _onStopCapture() {
    console.log('stop capturing');
    this.setState({
      isCapturing: false,
    });
    this.camera.stopCapture()
  }

  onCapturePress() {
    if (!this.state.isCapturing) {
      this._onStartCapture();
    } else {
      this._onStopCapture();
    }
  }

  cameraRef(cam) {
    // not sure the reason for this function yet
    this.camera = cam;
  }

  render() {
    return (
      <View style={styles.container}>
        <Camera
          ref={this.cameraRef}
          style={styles.preview}
          aspect={'fill'}
          captureTarget={Camera.constants.CaptureTarget.temp}
          captureMode={Camera.constants.CaptureMode.video}
        >
          <Text style={styles.capture} onPress={this.onCapturePress}>
            [CAPTURE]
          </Text>
        </Camera>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  preview: {
    flex: 1,
    justifyContent: 'flex-end',
    alignItems: 'center',
    height: Dimensions.get('window').height,
    width: Dimensions.get('window').width,
  },
  capture: {
    flex: 0,
    backgroundColor: '#fff',
    borderRadius: 5,
    color: '#000',
    padding: 10,
    margin: 40,
  },
});

// get the state of the current quilt
// which will be passed with the video
// to action creator to post data
function mapStateToProps (state) {
  const currentQuilt = state.get('currentQuilt');
  return {
    currentQuilt: currentQuilt.toObject(),
  };
}

// dispatch postQuilt with previous current quilt state plus video data
function mapDispatchToProps (dispatch) {
  return {
    postQuilt: (data) => {
      dispatch(postQuilt(data));
    },
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(ShowCamera);
<|endoftext|>


<commit_before>/*
 * PAGES: Downloads rendered pages from a Bolt server
 */
module.exports = function (grunt) {
    grunt.registerTask('pages', 'Downloads rendered pages from a Bolt server', function () {
        var request = require('request'),
            done = this.async(),
            outpath,
            outfile,
            baseurl,
            pages,
            options,
            queue = [];

        // Require config variables.
        grunt.config.requires(
            'path.tmp',
            'pages.baseurl',
            'pages.requests'
        );

        // Create empty output directory inside tmp folder.
        outpath = grunt.config('path.tmp') + '/pages';
        if (grunt.file.isDir(outpath)) {
            grunt.file.delete(outpath);
        }
        grunt.file.mkdir(outpath);

        // Request all required pages.
        pages = grunt.config('pages.requests');
        for (var dest in pages) {
            // Ignore requests with a destination that starts with "#".
            if (dest.substr(0, 1) !== '#') {
                // Set request options.
                if (typeof pages[dest] === 'object') {
                    options = pages[dest];
                } else {
                    options = {
                        url: pages[dest] !== '' ? pages[dest] : dest
                    };
                }
                options.baseUrl = options.baseUrl || grunt.config('pages.baseurl');
                options.followAllRedirects = true; // "followRedirect" doesn't seem to work with 302.
                options.jar = true;

                // Path, where to put the file. Make it always end with ".html"
                outfile = outpath + '/' + dest.replace(/^(.+)\.html$/, '$1') + '.html';

                // Build a request queue.
                queue.push({
                    opt: options,
                    out: outfile
                });
            }
        }

        getNextPage();

        function getNextPage() {
            var next = queue.shift();

            if (next) {
                grunt.log.writeln('Get page "' + next.out + '"');
                grunt.verbose.writeln(require('util').inspect(next.opt, false, 2, true));

                request(
                    next.opt,
                    function (error, response, body) {
                        if (!error && (response.statusCode < 200 || response.statusCode >= 300)) {
                            error = 'Status code: ' + response.statusCode;
                        }
                        if (error) {
                            grunt.fail.warn(error);
                            return done(false);
                        }
                        // Write response body to file.
                        grunt.file.write(next.out, body);
                        // Go on with next request.
                        getNextPage();
                    }
                );
            } else {
                done();
            }
        }
    });
};
<commit_msg>Check for output directory<commit_after>/*
 * PAGES: Downloads rendered pages from a Bolt server
 */
module.exports = function (grunt) {
    grunt.registerTask('pages', 'Downloads rendered pages from a Bolt server', function () {
        var request = require('request'),
            done = this.async(),
            outpath,
            outfile,
            baseurl,
            pages,
            options,
            queue = [];

        // Require config variables.
        grunt.config.requires(
            'path.tmp',
            'pages.baseurl',
            'pages.requests'
        );

        // Create empty output directory inside tmp folder.
        outpath = grunt.config('path.tmp') + '/pages';
        if (grunt.file.isDir(outpath)) {
            grunt.file.delete(outpath);
        }
        grunt.file.mkdir(outpath);
        if (!grunt.file.isDir(outpath)) {
            grunt.log.error('Output directory "' + outpath + '" missing!');
            return done(false);
        }

        // Request all required pages.
        pages = grunt.config('pages.requests');
        for (var dest in pages) {
            // Ignore requests with a destination that starts with "#".
            if (dest.substr(0, 1) !== '#') {
                // Set request options.
                if (typeof pages[dest] === 'object') {
                    options = pages[dest];
                } else {
                    options = {
                        url: pages[dest] !== '' ? pages[dest] : dest
                    };
                }
                options.baseUrl = options.baseUrl || grunt.config('pages.baseurl');
                options.followAllRedirects = true; // "followRedirect" doesn't seem to work with 302.
                options.jar = true;

                // Path, where to put the file. Make it always end with ".html"
                outfile = outpath + '/' + dest.replace(/^(.+)\.html$/, '$1') + '.html';

                // Build a request queue.
                queue.push({
                    opt: options,
                    out: outfile
                });
            }
        }

        getNextPage();

        function getNextPage() {
            var next = queue.shift();

            if (next) {
                grunt.log.writeln('Get page "' + next.out + '"');
                grunt.verbose.writeln(require('util').inspect(next.opt, false, 2, true));

                request(
                    next.opt,
                    function (error, response, body) {
                        if (!error && (response.statusCode < 200 || response.statusCode >= 300)) {
                            error = 'Status code: ' + response.statusCode;
                        }
                        if (error) {
                            grunt.fail.warn(error);
                            return done(false);
                        }
                        // Write response body to file.
                        grunt.file.write(next.out, body);
                        // Go on with next request.
                        getNextPage();
                    }
                );
            } else {
                done();
            }
        }
    });
};
<|endoftext|>