<commit_before>var specialElHandlers = {
    /**
     * Needed for IE. Apparently IE doesn't think
     * that "selected" is an attribute when reading
     * over the attributes using selectEl.attributes
     */
    OPTION: function(fromEl, toEl) {
        if ((fromEl.selected = toEl.selected)) {
            fromEl.setAttribute('selected', '');
        } else {
            fromEl.removeAttribute('selected', '');
        }
    },
    /**
     * The "value" attribute is special for the <input> element
     * since it sets the initial value. Changing the "value"
     * attribute without changing the "value" property will have
     * no effect since it is only used to the set the initial value.
     * Similar for the "checked" attribute.
     */
    INPUT: function(fromEl, toEl) {
        fromEl.checked = toEl.checked;
        fromEl.value = toEl.value;

        if (!toEl.hasAttribute('checked')) {
            fromEl.removeAttribute('checked');
        }

        if (!toEl.hasAttribute('value')) {
            fromEl.removeAttribute('value');
        }
    }
};

function noop() {}

/**
 * Loop over all of the attributes on the target node and make sure the
 * original DOM node has the same attributes. If an attribute
 * found on the original node is not on the new node then remove it from
 * the original node
 * @param  {HTMLElement} fromNode
 * @param  {HTMLElement} toNode
 */
function morphAttrs(fromNode, toNode) {
    var attrs = toNode.attributes;
    var i;
    var attr;
    var attrName;
    var attrValue;
    var foundAttrs = {};

    for (i=attrs.length-1; i>=0; i--) {
        attr = attrs[i];
        if (attr.specified !== false) {
            attrName = attr.name;
            attrValue = attr.value;
            foundAttrs[attrName] = true;

            if (fromNode.getAttribute(attrName) !== attrValue) {
                fromNode.setAttribute(attrName, attrValue);
            }
        }
    }

    // Delete any extra attributes found on the original DOM element that weren't
    // found on the target element.
    attrs = fromNode.attributes;

    for (i=attrs.length-1; i>=0; i--) {
        attr = attrs[i];
        if (attr.specified !== false) {
            attrName = attr.name;
            if (!foundAttrs.hasOwnProperty(attrName)) {
                fromNode.removeAttribute(attrName);
            }
        }
    }
}

/**
 * Copies the children of one DOM element to another DOM element
 */
function moveChildren(from, to) {
    var curChild = from.firstChild;
    while(curChild) {
        var nextChild = curChild.nextSibling;
        to.appendChild(curChild);
        curChild = nextChild;
    }
    return to;
}

function morphdom(fromNode, toNode, options) {
    if (!options) {
        options = {};
    }

    if (typeof toNode === 'string') {
        var newBodyEl = document.createElement('body');
        newBodyEl.innerHTML = toNode;
        toNode = newBodyEl.childNodes[0];
    }

    var savedEls = {}; // Used to save off DOM elements with IDs
    var unmatchedEls = {};
    var onNodeDiscarded = options.onNodeDiscarded || noop;
    var onBeforeMorphEl = options.onBeforeMorphEl || noop;
    var onBeforeMorphElChildren = options.onBeforeMorphElChildren || noop;

    function removeNodeHelper(node, nestedInSavedEl) {
        var id = node.id;
        // If the node has an ID then save it off since we will want
        // to reuse it in case the target DOM tree has a DOM element
        // with the same ID
        if (id) {
            savedEls[id] = node;
        } else if (!nestedInSavedEl) {
            // If we are not nested in a saved element then we know that this node has been
            // completely discarded and will not exist in the final DOM.
            onNodeDiscarded(node);
        }

        if (node.nodeType === 1) {
            var curChild = node.firstChild;
            while(curChild) {
                removeNodeHelper(curChild, nestedInSavedEl || id);
                curChild = curChild.nextSibling;
            }
        }
    }

    function walkDiscardedChildNodes(node) {
        if (node.nodeType === 1) {
            var curChild = node.firstChild;
            while(curChild) {

                if (!curChild.id) {
                    // We only want to handle nodes that don't have an ID to avoid double
                    // walking the same saved element.

                    onNodeDiscarded(curChild);

                    // Walk recursively
                    walkDiscardedChildNodes(curChild);
                }

                curChild = curChild.nextSibling;
            }
        }
    }

    function removeNode(node, parentNode, alreadyVisited) {
        parentNode.removeChild(node);

        if (alreadyVisited) {
            if (!node.id) {
                onNodeDiscarded(node);
                walkDiscardedChildNodes(node);
            }
        } else {
            removeNodeHelper(node);
        }
    }

    function morphEl(fromNode, toNode, alreadyVisited) {
        if (toNode.id) {
            // If an element with an ID is being morphed then it is will be in the final
            // DOM so clear it out of the saved elements collection
            delete savedEls[toNode.id];
        }

        if (onBeforeMorphEl(fromNode, toNode) === false) {
            return;
        }

        morphAttrs(fromNode, toNode);

        if (onBeforeMorphElChildren(fromNode, toNode) === false) {
            return;
        }

        var curToNodeChild = toNode.firstChild;
        var curFromNodeChild = fromNode.firstChild;
        var curToNodeId;

        var fromNextSibling;
        var toNextSibling;
        var savedEl;
        var unmatchedEl;

        outer: while(curToNodeChild) {
            toNextSibling = curToNodeChild.nextSibling;
            curToNodeId = curToNodeChild.id;

            while(curFromNodeChild) {
                var curFromNodeId = curFromNodeChild.id;
                fromNextSibling = curFromNodeChild.nextSibling;

                if (!alreadyVisited) {
                    if (curFromNodeId && (unmatchedEl = unmatchedEls[curFromNodeId])) {
                        unmatchedEl.parentNode.replaceChild(curFromNodeChild, unmatchedEl);
                        morphEl(curFromNodeChild, unmatchedEl, alreadyVisited);
                        curFromNodeChild = fromNextSibling;
                        continue;
                    }
                }

                var curFromNodeType = curFromNodeChild.nodeType;

                if (curFromNodeType === curToNodeChild.nodeType) {
                    var isCompatible = false;

                    if (curFromNodeType === 1) { // Both nodes being compared are Element nodes
                        if (curFromNodeChild.tagName === curToNodeChild.tagName) {
                            // We have compatible DOM elements
                            if (curFromNodeId || curToNodeId) {
                                // If either DOM element has an ID then we handle
                                // those differently since we want to match up
                                // by ID
                                if (curToNodeId === curFromNodeId) {
                                    isCompatible = true;
                                }
                            } else {
                                isCompatible = true;
                            }
                        }

                        if (isCompatible) {
                            // We found compatible DOM elements so transform the current "from" node
                            // to match the current target DOM node.
                            morphEl(curFromNodeChild, curToNodeChild, alreadyVisited);
                        }
                    } else if (curFromNodeType === 3) { // Both nodes being compared are Text nodes
                        isCompatible = true;
                        // Simply update nodeValue on the original node to change the text value
                        curFromNodeChild.nodeValue = curToNodeChild.nodeValue;
                    }

                    if (isCompatible) {
                        curToNodeChild = toNextSibling;
                        curFromNodeChild = fromNextSibling;
                        continue outer;
                    }
                }

                // No compatible match so remove the old node from the DOM and continue trying
                // to find a match in the original DOM
                removeNode(curFromNodeChild, fromNode, alreadyVisited);
                curFromNodeChild = fromNextSibling;
            }

            if (curToNodeId) {
                if ((savedEl = savedEls[curToNodeId])) {
                    morphEl(savedEl, curToNodeChild, true);
                    curToNodeChild = savedEl; // We want to append the saved element instead
                } else {
                    // The current DOM element in the target tree has an ID
                    // but we did not find a match in any of the corresponding
                    // siblings. We just put the target element in the old DOM tree
                    // but if we later find an element in the old DOM tree that has
                    // a matching ID then we will replace the target element
                    // with the corresponding old element and morph the old element
                    unmatchedEls[curToNodeId] = curToNodeChild;
                }
            }

            // If we got this far then we did not find a candidate match for our "to node"
            // and we exhausted all of the children "from" nodes. Therefore, we will just
            // append the current "to node" to the end
            fromNode.appendChild(curToNodeChild);

            curToNodeChild = toNextSibling;
            curFromNodeChild = fromNextSibling;
        }

        // We have processed all of the "to nodes". If curFromNodeChild is non-null then
        // we still have some from nodes left over that need to be removed
        while(curFromNodeChild) {
            fromNextSibling = curFromNodeChild.nextSibling;
            removeNode(curFromNodeChild, fromNode, alreadyVisited);
            curFromNodeChild = fromNextSibling;
        }

        var specialElHandler = specialElHandlers[fromNode.tagName];
        if (specialElHandler) {
            specialElHandler(fromNode, toNode);
        }
    }

    var morphedNode = fromNode;
    var morphedNodeType = morphedNode.nodeType;
    var toNodeType = toNode.nodeType;

    // Handle the case where we are given two DOM nodes that are not
    // compatible (e.g. <div> --> <span> or <div> --> TEXT)
    if (morphedNodeType === 1) {
        if (toNodeType === 1) {
            if (morphedNode.tagName !== toNode.tagName) {
                onNodeDiscarded(fromNode);
                morphedNode = moveChildren(morphedNode, document.createElement(toNode.tagName));
            }
        } else {
            // Going from an element node to a text node
            return toNode;
        }
    } else if (morphedNodeType === 3) { // Text node
        if (toNodeType === 3) {
            morphedNode.nodeValue = toNode.nodeValue;
            return morphedNode;
        } else {
            onNodeDiscarded(fromNode);
            // Text node to something else
            return toNode;
        }
    }

    morphEl(morphedNode, toNode, false);

    // Fire the "onNodeDiscarded" event for any saved elements
    // that never found a new home in the morphed DOM
    for (var savedElId in savedEls) {
        if (savedEls.hasOwnProperty(savedElId)) {
            var savedEl = savedEls[savedElId];
            onNodeDiscarded(savedEl);
            walkDiscardedChildNodes(savedEl);
        }
    }

    if (morphedNode !== fromNode && fromNode.parentNode) {
        // If we had to swap out the from node with a new node because the old
        // node was not compatible with the target node then we need to
        // replace the old DOM node in the original DOM tree. This is only
        // possible if the original DOM node was part of a DOM tree which
        // we know is the case if it has a parent node.
        fromNode.parentNode.replaceChild(morphedNode, fromNode);
    }

    return morphedNode;
}

module.exports = morphdom;<commit_msg>Optimize string rendering with document range frags<commit_after>// Create a range object to efficently render strings to elements.
var range = document.createRange();
range.selectNode(document.body);

var specialElHandlers = {
    /**
     * Needed for IE. Apparently IE doesn't think
     * that "selected" is an attribute when reading
     * over the attributes using selectEl.attributes
     */
    OPTION: function(fromEl, toEl) {
        if ((fromEl.selected = toEl.selected)) {
            fromEl.setAttribute('selected', '');
        } else {
            fromEl.removeAttribute('selected', '');
        }
    },
    /**
     * The "value" attribute is special for the <input> element
     * since it sets the initial value. Changing the "value"
     * attribute without changing the "value" property will have
     * no effect since it is only used to the set the initial value.
     * Similar for the "checked" attribute.
     */
    INPUT: function(fromEl, toEl) {
        fromEl.checked = toEl.checked;
        fromEl.value = toEl.value;

        if (!toEl.hasAttribute('checked')) {
            fromEl.removeAttribute('checked');
        }

        if (!toEl.hasAttribute('value')) {
            fromEl.removeAttribute('value');
        }
    }
};

function noop() {}

/**
 * Loop over all of the attributes on the target node and make sure the
 * original DOM node has the same attributes. If an attribute
 * found on the original node is not on the new node then remove it from
 * the original node
 * @param  {HTMLElement} fromNode
 * @param  {HTMLElement} toNode
 */
function morphAttrs(fromNode, toNode) {
    var attrs = toNode.attributes;
    var i;
    var attr;
    var attrName;
    var attrValue;
    var foundAttrs = {};

    for (i=attrs.length-1; i>=0; i--) {
        attr = attrs[i];
        if (attr.specified !== false) {
            attrName = attr.name;
            attrValue = attr.value;
            foundAttrs[attrName] = true;

            if (fromNode.getAttribute(attrName) !== attrValue) {
                fromNode.setAttribute(attrName, attrValue);
            }
        }
    }

    // Delete any extra attributes found on the original DOM element that weren't
    // found on the target element.
    attrs = fromNode.attributes;

    for (i=attrs.length-1; i>=0; i--) {
        attr = attrs[i];
        if (attr.specified !== false) {
            attrName = attr.name;
            if (!foundAttrs.hasOwnProperty(attrName)) {
                fromNode.removeAttribute(attrName);
            }
        }
    }
}

/**
 * Copies the children of one DOM element to another DOM element
 */
function moveChildren(from, to) {
    var curChild = from.firstChild;
    while(curChild) {
        var nextChild = curChild.nextSibling;
        to.appendChild(curChild);
        curChild = nextChild;
    }
    return to;
}

function morphdom(fromNode, toNode, options) {
    if (!options) {
        options = {};
    }

    if (typeof toNode === 'string') {
        var fragment = range.createContextualFragment(toNode);
        toNode = fragment.childNodes[0];
    }

    var savedEls = {}; // Used to save off DOM elements with IDs
    var unmatchedEls = {};
    var onNodeDiscarded = options.onNodeDiscarded || noop;
    var onBeforeMorphEl = options.onBeforeMorphEl || noop;
    var onBeforeMorphElChildren = options.onBeforeMorphElChildren || noop;

    function removeNodeHelper(node, nestedInSavedEl) {
        var id = node.id;
        // If the node has an ID then save it off since we will want
        // to reuse it in case the target DOM tree has a DOM element
        // with the same ID
        if (id) {
            savedEls[id] = node;
        } else if (!nestedInSavedEl) {
            // If we are not nested in a saved element then we know that this node has been
            // completely discarded and will not exist in the final DOM.
            onNodeDiscarded(node);
        }

        if (node.nodeType === 1) {
            var curChild = node.firstChild;
            while(curChild) {
                removeNodeHelper(curChild, nestedInSavedEl || id);
                curChild = curChild.nextSibling;
            }
        }
    }

    function walkDiscardedChildNodes(node) {
        if (node.nodeType === 1) {
            var curChild = node.firstChild;
            while(curChild) {

                if (!curChild.id) {
                    // We only want to handle nodes that don't have an ID to avoid double
                    // walking the same saved element.

                    onNodeDiscarded(curChild);

                    // Walk recursively
                    walkDiscardedChildNodes(curChild);
                }

                curChild = curChild.nextSibling;
            }
        }
    }

    function removeNode(node, parentNode, alreadyVisited) {
        parentNode.removeChild(node);

        if (alreadyVisited) {
            if (!node.id) {
                onNodeDiscarded(node);
                walkDiscardedChildNodes(node);
            }
        } else {
            removeNodeHelper(node);
        }
    }

    function morphEl(fromNode, toNode, alreadyVisited) {
        if (toNode.id) {
            // If an element with an ID is being morphed then it is will be in the final
            // DOM so clear it out of the saved elements collection
            delete savedEls[toNode.id];
        }

        if (onBeforeMorphEl(fromNode, toNode) === false) {
            return;
        }

        morphAttrs(fromNode, toNode);

        if (onBeforeMorphElChildren(fromNode, toNode) === false) {
            return;
        }

        var curToNodeChild = toNode.firstChild;
        var curFromNodeChild = fromNode.firstChild;
        var curToNodeId;

        var fromNextSibling;
        var toNextSibling;
        var savedEl;
        var unmatchedEl;

        outer: while(curToNodeChild) {
            toNextSibling = curToNodeChild.nextSibling;
            curToNodeId = curToNodeChild.id;

            while(curFromNodeChild) {
                var curFromNodeId = curFromNodeChild.id;
                fromNextSibling = curFromNodeChild.nextSibling;

                if (!alreadyVisited) {
                    if (curFromNodeId && (unmatchedEl = unmatchedEls[curFromNodeId])) {
                        unmatchedEl.parentNode.replaceChild(curFromNodeChild, unmatchedEl);
                        morphEl(curFromNodeChild, unmatchedEl, alreadyVisited);
                        curFromNodeChild = fromNextSibling;
                        continue;
                    }
                }

                var curFromNodeType = curFromNodeChild.nodeType;

                if (curFromNodeType === curToNodeChild.nodeType) {
                    var isCompatible = false;

                    if (curFromNodeType === 1) { // Both nodes being compared are Element nodes
                        if (curFromNodeChild.tagName === curToNodeChild.tagName) {
                            // We have compatible DOM elements
                            if (curFromNodeId || curToNodeId) {
                                // If either DOM element has an ID then we handle
                                // those differently since we want to match up
                                // by ID
                                if (curToNodeId === curFromNodeId) {
                                    isCompatible = true;
                                }
                            } else {
                                isCompatible = true;
                            }
                        }

                        if (isCompatible) {
                            // We found compatible DOM elements so transform the current "from" node
                            // to match the current target DOM node.
                            morphEl(curFromNodeChild, curToNodeChild, alreadyVisited);
                        }
                    } else if (curFromNodeType === 3) { // Both nodes being compared are Text nodes
                        isCompatible = true;
                        // Simply update nodeValue on the original node to change the text value
                        curFromNodeChild.nodeValue = curToNodeChild.nodeValue;
                    }

                    if (isCompatible) {
                        curToNodeChild = toNextSibling;
                        curFromNodeChild = fromNextSibling;
                        continue outer;
                    }
                }

                // No compatible match so remove the old node from the DOM and continue trying
                // to find a match in the original DOM
                removeNode(curFromNodeChild, fromNode, alreadyVisited);
                curFromNodeChild = fromNextSibling;
            }

            if (curToNodeId) {
                if ((savedEl = savedEls[curToNodeId])) {
                    morphEl(savedEl, curToNodeChild, true);
                    curToNodeChild = savedEl; // We want to append the saved element instead
                } else {
                    // The current DOM element in the target tree has an ID
                    // but we did not find a match in any of the corresponding
                    // siblings. We just put the target element in the old DOM tree
                    // but if we later find an element in the old DOM tree that has
                    // a matching ID then we will replace the target element
                    // with the corresponding old element and morph the old element
                    unmatchedEls[curToNodeId] = curToNodeChild;
                }
            }

            // If we got this far then we did not find a candidate match for our "to node"
            // and we exhausted all of the children "from" nodes. Therefore, we will just
            // append the current "to node" to the end
            fromNode.appendChild(curToNodeChild);

            curToNodeChild = toNextSibling;
            curFromNodeChild = fromNextSibling;
        }

        // We have processed all of the "to nodes". If curFromNodeChild is non-null then
        // we still have some from nodes left over that need to be removed
        while(curFromNodeChild) {
            fromNextSibling = curFromNodeChild.nextSibling;
            removeNode(curFromNodeChild, fromNode, alreadyVisited);
            curFromNodeChild = fromNextSibling;
        }

        var specialElHandler = specialElHandlers[fromNode.tagName];
        if (specialElHandler) {
            specialElHandler(fromNode, toNode);
        }
    }

    var morphedNode = fromNode;
    var morphedNodeType = morphedNode.nodeType;
    var toNodeType = toNode.nodeType;

    // Handle the case where we are given two DOM nodes that are not
    // compatible (e.g. <div> --> <span> or <div> --> TEXT)
    if (morphedNodeType === 1) {
        if (toNodeType === 1) {
            if (morphedNode.tagName !== toNode.tagName) {
                onNodeDiscarded(fromNode);
                morphedNode = moveChildren(morphedNode, document.createElement(toNode.tagName));
            }
        } else {
            // Going from an element node to a text node
            return toNode;
        }
    } else if (morphedNodeType === 3) { // Text node
        if (toNodeType === 3) {
            morphedNode.nodeValue = toNode.nodeValue;
            return morphedNode;
        } else {
            onNodeDiscarded(fromNode);
            // Text node to something else
            return toNode;
        }
    }

    morphEl(morphedNode, toNode, false);

    // Fire the "onNodeDiscarded" event for any saved elements
    // that never found a new home in the morphed DOM
    for (var savedElId in savedEls) {
        if (savedEls.hasOwnProperty(savedElId)) {
            var savedEl = savedEls[savedElId];
            onNodeDiscarded(savedEl);
            walkDiscardedChildNodes(savedEl);
        }
    }

    if (morphedNode !== fromNode && fromNode.parentNode) {
        // If we had to swap out the from node with a new node because the old
        // node was not compatible with the target node then we need to
        // replace the old DOM node in the original DOM tree. This is only
        // possible if the original DOM node was part of a DOM tree which
        // we know is the case if it has a parent node.
        fromNode.parentNode.replaceChild(morphedNode, fromNode);
    }

    return morphedNode;
}

module.exports = morphdom;<|endoftext|>


<commit_before>/**
 * Module dependencies.
 */
var passport = require('passport')
  , url = require('url')
  , util = require('util')
  , utils = require('./utils')
  , OAuth = require('oauth').OAuth
  , InternalOAuthError = require('../errors/internaloautherror');

/**
 * `OAuthStrategy` constructor.
 *
 * The OAuth authentication strategy authenticates requests using the OAuth
 * protocol.
 *
 * OAuth provides a facility for delegated authentication, whereby users can
 * authenticate using a third-party service such as Twitter.  Delegating in this
 * manner involves a sequence of events, including redirecting the user to the
 * third-party service for authorization.  Once authorization has been obtained,
 * the user is redirected back to the application and a token can be used to
 * obtain credentials.
 *
 * Applications must supply a `verify` callback which accepts a `token`,
 * `tokenSecret` and service-specific `profile`, and then calls the `done`
 * callback supplying a `user`, which should be set to `false` if the
 * credentials are not valid.  If an exception occured, `err` should be set.
 *
 * Options:
 *   - `requestTokenURL`       URL used to obtain an unauthorized request token
 *   - `accessTokenURL`        URL used to exchange a user-authorized request token for an access token
 *   - `userAuthorizationURL`  URL used to obtain user authorization
 *   - `consumerKey`           identifies client to service provider
 *   - `consumerSecret`        secret used to establish ownership of the consumer key
 *   - `callbackURL`           URL to which the service provider will redirect the user after obtaining authorization
 *   - `passReqToCallback`     when `true`, `req` is the first argument to the verify callback (default: `false`)
 *
 * Examples:
 *
 *     passport.use(new OAuthStrategy({
 *         requestTokenURL: 'https://www.example.com/oauth/request_token',
 *         accessTokenURL: 'https://www.example.com/oauth/access_token',
 *         userAuthorizationURL: 'https://www.example.com/oauth/authorize',
 *         consumerKey: '123-456-789',
 *         consumerSecret: 'shhh-its-a-secret'
 *         callbackURL: 'https://www.example.net/auth/example/callback'
 *       },
 *       function(token, tokenSecret, profile, done) {
 *         User.findOrCreate(..., function (err, user) {
 *           done(err, user);
 *         });
 *       }
 *     ));
 *
 * @param {Object} options
 * @param {Function} verify
 * @api public
 */
function OAuthStrategy(options, verify) {
  options = options || {}
  passport.Strategy.call(this);
  this.name = 'oauth';
  this._verify = verify;

  if (!options.requestTokenURL) throw new Error('OAuthStrategy requires a requestTokenURL option');
  if (!options.accessTokenURL) throw new Error('OAuthStrategy requires a accessTokenURL option');
  if (!options.userAuthorizationURL) throw new Error('OAuthStrategy requires a userAuthorizationURL option');
  if (!options.consumerKey) throw new Error('OAuthStrategy requires a consumerKey option');
  if (options.consumerSecret === undefined) throw new Error('OAuthStrategy requires a consumerSecret option');
  if (!verify) throw new Error('OAuth authentication strategy requires a verify function');

  // NOTE: The _oauth property is considered "protected".  Subclasses are
  //       allowed to use it when making protected resource requests to retrieve
  //       the user profile.
  this._oauth = new OAuth(options.requestTokenURL, options.accessTokenURL,
                          options.consumerKey,  options.consumerSecret,
                          "1.0", options.callbackURL || null, options.signatureMethod || "HMAC-SHA1");

  this._userAuthorizationURL = options.userAuthorizationURL;
  this._passReqToCallback = options.passReqToCallback;
  this._skipUserProfile = (options.skipUserProfile === undefined) ? false : options.skipUserProfile;
  this._key = options.sessionKey || 'oauth';
}

/**
 * Inherit from `passport.Strategy`.
 */
util.inherits(OAuthStrategy, passport.Strategy);

/**
 * Authenticate request by delegating to a service provider using OAuth.
 *
 * @param {Object} req
 * @api protected
 */
OAuthStrategy.prototype.authenticate = function(req, options) {
  options = options || {};
  if (!req.session) { return this.error(new Error('OAuth authentication requires session support')); }

  var self = this;

  if (req.query && req.query['oauth_token']) {
    // The request being authenticated contains an oauth_token parameter in the
    // query portion of the URL.  This indicates that the service provider has
    // redirected the user back to the application, after authenticating the
    // user and obtaining their authorization.
    //
    // The value of the oauth_token parameter is the request token.  Together
    // with knowledge of the token secret (stored in the session), the request
    // token can be exchanged for an access token and token secret.
    //
    // This access token and token secret, along with the optional ability to
    // fetch profile information from the service provider, is sufficient to
    // establish the identity of the user.

    // Bail if the session does not contain the request token and corresponding
    // secret.  If this happens, it is most likely caused by initiating OAuth
    // from a different host than that of the callback endpoint (for example:
    // initiating from 127.0.0.1 but handling callbacks at localhost).
    if (!req.session[self._key]) { return self.error(new Error('failed to find request token in session')); }

    var oauthToken = req.query['oauth_token'];
    var oauthVerifier = req.query['oauth_verifier'] || null;
    var oauthTokenSecret = req.session[self._key]["oauth_token_secret"];

    // NOTE: The oauth_verifier parameter will be supplied in the query portion
    //       of the redirect URL, if the server supports OAuth 1.0a.

    this._oauth.getOAuthAccessToken(oauthToken, oauthTokenSecret, oauthVerifier, function(err, token, tokenSecret, params) {
      if (err) { return self.error(new InternalOAuthError('failed to obtain access token', err)); }

      // The request token has been exchanged for an access token.  Since the
      // request token is a single-use token, that data can be removed from the
      // session.
      delete req.session[self._key]['oauth_token'];
      delete req.session[self._key]['oauth_token_secret'];
      if (Object.keys(req.session[self._key]).length == 0) {
        delete req.session[self._key];
      }

      self._loadUserProfile(token, tokenSecret, params, function(err, profile) {
        if (err) { return self.error(err); };

        function verified(err, user, info) {
          if (err) { return self.error(err); }
          if (!user) { return self.fail(info); }
          self.success(user, info);
        }

        if (self._passReqToCallback) {
          var arity = self._verify.length;
          if (arity == 6) {
            self._verify(req, token, tokenSecret, params, profile, verified);
          } else { // arity == 5
            self._verify(req, token, tokenSecret, profile, verified);
          }
        } else {
          var arity = self._verify.length;
          if (arity == 5) {
            self._verify(token, tokenSecret, params, profile, verified);
          } else { // arity == 4
            self._verify(token, tokenSecret, profile, verified);
          }
        }
      });
    });
  } else {
    // In order to authenticate via OAuth, the application must obtain a request
    // token from the service provider and redirect the user to the service
    // provider to obtain their authorization.  After authorization has been
    // approved the user will be redirected back the application, at which point
    // the application can exchange the request token for an access token.
    //
    // In order to successfully exchange the request token, its corresponding
    // token secret needs to be known.  The token secret will be temporarily
    // stored in the session, so that it can be retrieved upon the user being
    // redirected back to the application.

    var params = this.requestTokenParams(options);
    this._oauth.getOAuthRequestToken(params, function(err, token, tokenSecret, params) {
      if (err) { return self.error(new InternalOAuthError('failed to obtain request token', err)); }

      // NOTE: params will contain an oauth_callback_confirmed property set to
      //       true, if the server supports OAuth 1.0a.
      //       { oauth_callback_confirmed: 'true' }

      if (!req.session[self._key]) { req.session[self._key] = {}; }
      req.session[self._key]['oauth_token'] = token;
      req.session[self._key]['oauth_token_secret'] = tokenSecret;

      var parsed = url.parse(self._userAuthorizationURL, true);
      parsed.query['oauth_token'] = token;
      utils.merge(parsed.query, self.userAuthorizationParams(options))
      delete parsed.search;
      var location = url.format(parsed);
      self.redirect(location);
    });
  }
}

/**
 * Retrieve user profile from service provider.
 *
 * OAuth-based authentication strategies can overrride this function in order to
 * load the user's profile from the service provider.  This assists applications
 * (and users of those applications) in the initial registration process by
 * automatically submitting required information.
 *
 * @param {String} accessToken
 * @param {Function} done
 * @api protected
 */
OAuthStrategy.prototype.userProfile = function(token, tokenSecret, params, done) {
  return done(null, {});
}

/**
 * Return extra parameters to be included in the request token request.
 *
 * Some OAuth providers require additional parameters to be included when
 * issuing a request token.  Since these parameters are not standardized by the
 * OAuth specification, OAuth-based authentication strategies can overrride this
 * function in order to populate these parameters as required by the provider.
 *
 * @param {Object} options
 * @return {Object}
 * @api protected
 */
OAuthStrategy.prototype.requestTokenParams = function(options) {
  return {};
}

/**
 * Return extra parameters to be included in the user authorization request.
 *
 * Some OAuth providers allow additional, non-standard parameters to be included
 * when requesting authorization.  Since these parameters are not standardized
 * by the OAuth specification, OAuth-based authentication strategies can
 * overrride this function in order to populate these parameters as required by
 * the provider.
 *
 * @param {Object} options
 * @return {Object}
 * @api protected
 */
OAuthStrategy.prototype.userAuthorizationParams = function(options) {
  return {};
}

/**
 * Load user profile, contingent upon options.
 *
 * @param {String} accessToken
 * @param {Function} done
 * @api private
 */
OAuthStrategy.prototype._loadUserProfile = function(token, tokenSecret, params, done) {
  var self = this;

  function loadIt() {
    return self.userProfile(token, tokenSecret, params, done);
  }
  function skipIt() {
    return done(null);
  }

  if (typeof this._skipUserProfile == 'function' && this._skipUserProfile.length > 1) {
    // async
    this._skipUserProfile(token, tokenSecret, function(err, skip) {
      if (err) { return done(err); }
      if (!skip) { return loadIt(); }
      return skipIt();
    });
  } else {
    var skip = (typeof this._skipUserProfile == 'function') ? this._skipUserProfile() : this._skipUserProfile;
    if (!skip) { return loadIt(); }
    return skipIt();
  }
}

/**
 * Expose `OAuthStrategy`.
 */
module.exports = OAuthStrategy;
<commit_msg>Add auto-generated callback url just like in OAuth2<commit_after>/**
 * Module dependencies.
 */
var passport = require('passport')
  , url = require('url')
  , util = require('util')
  , utils = require('./utils')
  , OAuth = require('oauth').OAuth
  , InternalOAuthError = require('../errors/internaloautherror');

/**
 * `OAuthStrategy` constructor.
 *
 * The OAuth authentication strategy authenticates requests using the OAuth
 * protocol.
 *
 * OAuth provides a facility for delegated authentication, whereby users can
 * authenticate using a third-party service such as Twitter.  Delegating in this
 * manner involves a sequence of events, including redirecting the user to the
 * third-party service for authorization.  Once authorization has been obtained,
 * the user is redirected back to the application and a token can be used to
 * obtain credentials.
 *
 * Applications must supply a `verify` callback which accepts a `token`,
 * `tokenSecret` and service-specific `profile`, and then calls the `done`
 * callback supplying a `user`, which should be set to `false` if the
 * credentials are not valid.  If an exception occured, `err` should be set.
 *
 * Options:
 *   - `requestTokenURL`       URL used to obtain an unauthorized request token
 *   - `accessTokenURL`        URL used to exchange a user-authorized request token for an access token
 *   - `userAuthorizationURL`  URL used to obtain user authorization
 *   - `consumerKey`           identifies client to service provider
 *   - `consumerSecret`        secret used to establish ownership of the consumer key
 *   - `callbackURL`           URL to which the service provider will redirect the user after obtaining authorization
 *   - `passReqToCallback`     when `true`, `req` is the first argument to the verify callback (default: `false`)
 *
 * Examples:
 *
 *     passport.use(new OAuthStrategy({
 *         requestTokenURL: 'https://www.example.com/oauth/request_token',
 *         accessTokenURL: 'https://www.example.com/oauth/access_token',
 *         userAuthorizationURL: 'https://www.example.com/oauth/authorize',
 *         consumerKey: '123-456-789',
 *         consumerSecret: 'shhh-its-a-secret'
 *         callbackURL: 'https://www.example.net/auth/example/callback'
 *       },
 *       function(token, tokenSecret, profile, done) {
 *         User.findOrCreate(..., function (err, user) {
 *           done(err, user);
 *         });
 *       }
 *     ));
 *
 * @param {Object} options
 * @param {Function} verify
 * @api public
 */
function OAuthStrategy(options, verify) {
  options = options || {}
  passport.Strategy.call(this);
  this.name = 'oauth';
  this._verify = verify;

  if (!options.requestTokenURL) throw new Error('OAuthStrategy requires a requestTokenURL option');
  if (!options.accessTokenURL) throw new Error('OAuthStrategy requires a accessTokenURL option');
  if (!options.userAuthorizationURL) throw new Error('OAuthStrategy requires a userAuthorizationURL option');
  if (!options.consumerKey) throw new Error('OAuthStrategy requires a consumerKey option');
  if (options.consumerSecret === undefined) throw new Error('OAuthStrategy requires a consumerSecret option');
  if (!verify) throw new Error('OAuth authentication strategy requires a verify function');

  // NOTE: The _oauth property is considered "protected".  Subclasses are
  //       allowed to use it when making protected resource requests to retrieve
  //       the user profile.
  this._oauth = new OAuth(options.requestTokenURL, options.accessTokenURL,
                          options.consumerKey,  options.consumerSecret,
                          "1.0", null, options.signatureMethod || "HMAC-SHA1");

  this._userAuthorizationURL = options.userAuthorizationURL;
  this._passReqToCallback = options.passReqToCallback;
  this._skipUserProfile = (options.skipUserProfile === undefined) ? false : options.skipUserProfile;
  this._key = options.sessionKey || 'oauth';
  this._callbackURL = options.callbackURL;
}

/**
 * Inherit from `passport.Strategy`.
 */
util.inherits(OAuthStrategy, passport.Strategy);

/**
 * Authenticate request by delegating to a service provider using OAuth.
 *
 * @param {Object} req
 * @api protected
 */
OAuthStrategy.prototype.authenticate = function(req, options) {
  options = options || {};
  if (!req.session) { return this.error(new Error('OAuth authentication requires session support')); }

  var self = this;

  if (req.query && req.query['oauth_token']) {
    // The request being authenticated contains an oauth_token parameter in the
    // query portion of the URL.  This indicates that the service provider has
    // redirected the user back to the application, after authenticating the
    // user and obtaining their authorization.
    //
    // The value of the oauth_token parameter is the request token.  Together
    // with knowledge of the token secret (stored in the session), the request
    // token can be exchanged for an access token and token secret.
    //
    // This access token and token secret, along with the optional ability to
    // fetch profile information from the service provider, is sufficient to
    // establish the identity of the user.

    // Bail if the session does not contain the request token and corresponding
    // secret.  If this happens, it is most likely caused by initiating OAuth
    // from a different host than that of the callback endpoint (for example:
    // initiating from 127.0.0.1 but handling callbacks at localhost).
    if (!req.session[self._key]) { return self.error(new Error('failed to find request token in session')); }

    var oauthToken = req.query['oauth_token'];
    var oauthVerifier = req.query['oauth_verifier'] || null;
    var oauthTokenSecret = req.session[self._key]["oauth_token_secret"];

    // NOTE: The oauth_verifier parameter will be supplied in the query portion
    //       of the redirect URL, if the server supports OAuth 1.0a.

    this._oauth.getOAuthAccessToken(oauthToken, oauthTokenSecret, oauthVerifier, function(err, token, tokenSecret, params) {
      if (err) { return self.error(new InternalOAuthError('failed to obtain access token', err)); }

      // The request token has been exchanged for an access token.  Since the
      // request token is a single-use token, that data can be removed from the
      // session.
      delete req.session[self._key]['oauth_token'];
      delete req.session[self._key]['oauth_token_secret'];
      if (Object.keys(req.session[self._key]).length == 0) {
        delete req.session[self._key];
      }

      self._loadUserProfile(token, tokenSecret, params, function(err, profile) {
        if (err) { return self.error(err); };

        function verified(err, user, info) {
          if (err) { return self.error(err); }
          if (!user) { return self.fail(info); }
          self.success(user, info);
        }

        if (self._passReqToCallback) {
          var arity = self._verify.length;
          if (arity == 6) {
            self._verify(req, token, tokenSecret, params, profile, verified);
          } else { // arity == 5
            self._verify(req, token, tokenSecret, profile, verified);
          }
        } else {
          var arity = self._verify.length;
          if (arity == 5) {
            self._verify(token, tokenSecret, params, profile, verified);
          } else { // arity == 4
            self._verify(token, tokenSecret, profile, verified);
          }
        }
      });
    });
  } else {
    // In order to authenticate via OAuth, the application must obtain a request
    // token from the service provider and redirect the user to the service
    // provider to obtain their authorization.  After authorization has been
    // approved the user will be redirected back the application, at which point
    // the application can exchange the request token for an access token.
    //
    // In order to successfully exchange the request token, its corresponding
    // token secret needs to be known.  The token secret will be temporarily
    // stored in the session, so that it can be retrieved upon the user being
    // redirected back to the application.

    var params = this.requestTokenParams(options);
    var callbackURL = options.callbackURL || this._callbackURL;
    if (callbackURL) {
      var parsed = url.parse(callbackURL);
      if (!parsed.protocol) {
        // The callback URL is relative, resolve a fully qualified URL from the
        // URL of the originating request.
        callbackURL = url.resolve(utils.originalURL(req), callbackURL);
      }
    }
    params['oauth_callback'] = callbackURL;
    this._oauth.getOAuthRequestToken(params, function(err, token, tokenSecret, params) {
      if (err) { return self.error(new InternalOAuthError('failed to obtain request token', err)); }

      // NOTE: params will contain an oauth_callback_confirmed property set to
      //       true, if the server supports OAuth 1.0a.
      //       { oauth_callback_confirmed: 'true' }

      if (!req.session[self._key]) { req.session[self._key] = {}; }
      req.session[self._key]['oauth_token'] = token;
      req.session[self._key]['oauth_token_secret'] = tokenSecret;

      var parsed = url.parse(self._userAuthorizationURL, true);
      parsed.query['oauth_token'] = token;
      utils.merge(parsed.query, self.userAuthorizationParams(options))
      delete parsed.search;
      var location = url.format(parsed);
      self.redirect(location);
    });
  }
}

/**
 * Retrieve user profile from service provider.
 *
 * OAuth-based authentication strategies can overrride this function in order to
 * load the user's profile from the service provider.  This assists applications
 * (and users of those applications) in the initial registration process by
 * automatically submitting required information.
 *
 * @param {String} accessToken
 * @param {Function} done
 * @api protected
 */
OAuthStrategy.prototype.userProfile = function(token, tokenSecret, params, done) {
  return done(null, {});
}

/**
 * Return extra parameters to be included in the request token request.
 *
 * Some OAuth providers require additional parameters to be included when
 * issuing a request token.  Since these parameters are not standardized by the
 * OAuth specification, OAuth-based authentication strategies can overrride this
 * function in order to populate these parameters as required by the provider.
 *
 * @param {Object} options
 * @return {Object}
 * @api protected
 */
OAuthStrategy.prototype.requestTokenParams = function(options) {
  return {};
}

/**
 * Return extra parameters to be included in the user authorization request.
 *
 * Some OAuth providers allow additional, non-standard parameters to be included
 * when requesting authorization.  Since these parameters are not standardized
 * by the OAuth specification, OAuth-based authentication strategies can
 * overrride this function in order to populate these parameters as required by
 * the provider.
 *
 * @param {Object} options
 * @return {Object}
 * @api protected
 */
OAuthStrategy.prototype.userAuthorizationParams = function(options) {
  return {};
}

/**
 * Load user profile, contingent upon options.
 *
 * @param {String} accessToken
 * @param {Function} done
 * @api private
 */
OAuthStrategy.prototype._loadUserProfile = function(token, tokenSecret, params, done) {
  var self = this;

  function loadIt() {
    return self.userProfile(token, tokenSecret, params, done);
  }
  function skipIt() {
    return done(null);
  }

  if (typeof this._skipUserProfile == 'function' && this._skipUserProfile.length > 1) {
    // async
    this._skipUserProfile(token, tokenSecret, function(err, skip) {
      if (err) { return done(err); }
      if (!skip) { return loadIt(); }
      return skipIt();
    });
  } else {
    var skip = (typeof this._skipUserProfile == 'function') ? this._skipUserProfile() : this._skipUserProfile;
    if (!skip) { return loadIt(); }
    return skipIt();
  }
}

/**
 * Expose `OAuthStrategy`.
 */
module.exports = OAuthStrategy;
<|endoftext|>


<commit_before>'use strict';
require('core-js/shim');
var Promise = require('prfun');

var program = require('commander');
var json = require('../package.json');

var fs = require('fs');
var Pusher = require('pusher-client');
var url = require('url');

var p = require('./p');
var request = require('./retry-request');

var CHILD_IGNORE = { childOptions: { stdio: 'ignore' } };
var readFile = Promise.promisify(fs.readFile, false, fs);

program
	.version(json.version)
	.usage('[options] [github user/project]')
	.option(
		'--travis-api <url>',
		'Travis API endpoint [https://api.travis-ci.org]',
		'https://api.travis-ci.org'
	).option(
		'--remote <name>',
		'Use the specified git remote for push [github]',
		'github'
	).option(
		'-j, --job <n>',
		'Only follow the specified job (job numbers start at 1)'
	);

program.parse(process.argv);

if (program.args.length > 1) {
	console.error('Too many arguments.');
	return 1;
}

var getUserAgent = function(reponame) {
	return json.name + '/' + json.version + ' (' +
		'node ' + process.version + ' ' + process.platform + ' ' + process.arch +
		')' + (reponame ? ' ' + reponame : '');
};

var gitRemoteRegex =
	/Push\s+URL:\s+(?:https?:\/\/github.com\/|git@github.com:)([^\/]+)\/([^\/]+?)(?:\.git)?\s/;

var getRepoName = function() {
	return Promise.resolve(program.args[0]).then(function(reponame) {
		if (reponame) { return reponame; }
		// Look in .git/config for 'github' remote.
		return p.execFile('git', ['remote','show',program.remote]).then(function(out) {
			var m = gitRemoteRegex.exec(out.stdout);
			if (m) {
				return m[1] + '/' + m[2];
			}
		});
	}).catch(function(e) { /* Ignore. */ }).then(function(reponame) {
		if (reponame) { return reponame; }
		// Look in .git/config for 'origin' remote.
		return p.execFile('git', ['remote','show','origin']).then(function(out) {
			var m = gitRemoteRegex.exec(out.stdout);
			if (m) {
				return m[1] + '/' + m[2];
			}
		});
	}).catch(function(e) { /* Ignore. */ }).then(function(reponame) {
		if (reponame) { return reponame; }
		// Look in ./package.json
		return readFile(
			'./package.json', { encoding: 'utf8' }
		).then(function(data) {
			var json = JSON.parse(data);
			if (json.repository && json.repository.type === 'git') {
				var m =
					/^(https?:\/\/github.com\/)?([^\/]+)\/([^\/]+?)(\.git)?$/.
					exec(json.repository.url || '');
				if (m) {
					return m[2] + '/' + m[3];
				}
			}
		});
	}).catch(function(e) { /* Ignore */ }).then(function(reponame) {
		if (reponame) { return reponame; }
		throw new Error("Can't determine github repo name.");
	});
};

// Ensure that there is a github remote, named 'github'.
var ensureRemote = function(repo) {
	return p.spawn('git', ['remote', 'show', program.remote], CHILD_IGNORE).then(function() {
		/* OK, github already exists. */
	}, function(e) {
		/* Doesn't exist, create it! */
		return p.spawn('git', ['remote','add',program.remote,'git@github.com:' + repo]);
	});
};

// Get the hash of HEAD.
var getGitHead = function() {
	return p.execFile('git', ['rev-parse', 'HEAD']).then(function(out) {
		return out.stdout.trim();
	});
};

// Create the new branch name.
var getBranchName = function(hash) {
	// Use a slash in the branch name so that gerrit can use
	// its access controls to ensure that the npm-travis user can only
	// push to npm-travis/* branches (not branches in general).
	return json.name + '/' + hash.slice(0, 8);
};

// Push to github.
var ensureBranch = function(repo, branchname) {
	return ensureRemote(repo).then(function() {
		return p.spawn('git', ['push', program.remote, 'HEAD:refs/heads/' + branchname], CHILD_IGNORE);
	});
};

var deleteBranch = function(repo, branchname) {
	return ensureRemote(repo).then(function() {
		return p.spawn('git', ['push', program.remote, ':' + branchname], {
			timeout: 60 * 1000,
			childOptions: CHILD_IGNORE.childOptions,
		});
	});
};

var apiRequest = function(path, opts) {
	opts = opts || {};
	if (Array.isArray(path)) {
		path = '/' + path.map(encodeURIComponent).join('/');
	}
	var apiURL = url.resolve(program.travisApi, path);
	var mimeType = 'application/vnd.travis-ci.2+json';
	if (opts.chunked) {
		mimeType += '; chunked=true';
	}
	return request({
		url: apiURL,
		encoding: 'utf8',
		pool: false,
		headers: {
			'User-Agent': getUserAgent(opts.reponame),
			Accept: mimeType,
		},
		retries: opts.retries,
		delay: opts.delay,
		timeout: opts.timeout,
	}).then(function(resp) {
		if (resp.response.statusCode !== 200) {
			throw new Error('Bad status: ' + resp.response.statusCode);
		}
		return opts.raw ? resp.body : JSON.parse(resp.body);
	});
};

var getTravisConfig = function() {
	return apiRequest('/config').get('config');
};

var getTravisRepo = function(reponame) {
	return apiRequest('/repos/' + reponame, { reponame: reponame }).get('repo');
};

var getTravisBuild = function(reponame, repo_id, branch, retry) {
	return apiRequest(['repos', repo_id, 'branches', branch], {
		reponame: reponame,
		timeout: 30 * 1000,
		delay:   5 * 1000, /* Check every 5 seconds... */
		retries: retry ? 3 * 12 : 0, /* ...for up to 3 minutes. */
	}).get('branch');
};

var getTravisJob = function(reponame, job_id) {
	return apiRequest(['jobs', job_id], { reponame: reponame }).get('job');
};

var getTravisLog = function(reponame, log_id, chunked) {
	return apiRequest(['logs', log_id], {
		reponame: reponame,
		chunked: chunked,
	}).get('log');
};

var Log = function() {
	this.chunks = [];
	this.next = 1;
	this._last = null;
};
Log.prototype.has = function(n) {
	return this.chunks[n] !== undefined;
};
Log.prototype.add = function(n, data) {
	this.chunks[n] = data;
	return this.advance();
};
Log.prototype.done = function() {
	return this._last !== null && this.next > this._last;
};
Log.prototype.advance = function() {
	while (this.chunks[this.next] !== undefined) {
		process.stdout.write(this.chunks[this.next++]);
	}
	return this.done();
};
Log.prototype.last = function(n) {
	this._last = n;
	return this.done();
};

var doit = function() {
	var state = {}, pusher;
	var initState = function() {
		return Promise.all([
			getTravisConfig().then(function(config) {
				state.config = config;
				pusher = new Pusher(state.config.pusher.key);
				if (false) { /* Debugging. */
					pusher.connection.on('message', function(message) {
						console.log('GOT PUSHER MESSAGE', message);
					});
				}
			}),
			getGitHead().then(function(hash) {
				state.githash = hash;
				state.branch = getBranchName(hash);
			}),
			getRepoName().then(function(reponame) {
				state.reponame = reponame;
				return getTravisRepo(reponame).then(function(repo) {
					state.repo = repo;
				});
			}),
		]).then(function() {
			// Check to see if builds on this branch already exist.
			return getTravisBuild(state.reponame, state.repo.id, state.branch, false).catch(function(e) {
				// Doesn't yet exist; push to github and then retry query until
				// we see the build show up in travis.
				return ensureBranch(state.reponame, state.branch).then(function() {
					// Clean up this branch on Ctrl-C.
					state.SIGINT = function() {
						console.log('\nCleaning up', state.branch);
						deleteBranch(state.reponame, state.branch).catch(function(e) {
							/* Ignore failure; we're dying here. */
						}).then(function() { process.exit(1); });
					};
					process.once('SIGINT', state.SIGINT);
					// Ok, get the build on this branch.
					return getTravisBuild(state.reponame, state.repo.id, state.branch, true);
				});
			});
		}).then(function(build) {
			// Ok, we've got a build on this branch!
			state.build = build;
			console.log(
				'==> Travis build',
				state.build.number,
				'(',
				'https://' + state.config.host + '/' + state.reponame + '/builds/' + state.build.id,
				')'
			);
			return state;
		});
	};
	var watchJob = function(job_num) {
		return Promise.resolve().then(function() {
			state.job_num = job_num;
			state.job_id = state.build.job_ids[state.job_num - 1];
			console.log(
				'==> Travis job',
				state.build.number + '.' + state.job_num,
				'(',
				'https://' + state.config.host + '/' + state.reponame + '/jobs/' + state.job_id,
				')'
			);
			// Check job #
			if (!state.job_id) {
				throw new Error(
					'Bad job number: ' + state.job_num +
						' (' + state.build.job_ids.length + ' jobs)'
				);
			}
			return getTravisJob(state.reponame, state.job_id);
		}).then(function(job) {
			state.job = job;
			var log = new Log();
			if (/^(passed|failed)$/.test(state.job.state || '')) {
				// Fetch all the logs, easy-peasy.
				return getTravisLog(state.reponame, state.job.log_id, false/*Not chunked*/).then(function(resp_log) {
					log.last(1);
					log.add(1, resp_log.body);
				});
			}
			// Hm, we need to stream the logs.
			var channelName = 'job-' + state.job_id;
			var channel = pusher.subscribe(channelName);
			var streamResolve;
			var handleChunk = function(data) {
				var content = data._log !== undefined ? data._log : data.content;
				if (data.final) {
					// Weird: *Usually* there's one more chunk coming.
					// Wait for it, but no more than 10s.
					log.last(data.number + 1);
					setTimeout(function() {
						if (!log.has(data.number + 1)) {
							log.add(data.number + 1, '\n----\n');
							streamResolve();
						}
					}, 10 * 1000);
				}
				var last = log.add(data.number, content);
				if (last) { streamResolve(); }
			};
			return Promise.all([
				// Fetch chunked log to fill in gaps.
				new Promise(function(resolve, reject) {
					channel.bind('pusher:subscription_succeeded', function() {
						getTravisLog(state.reponame, state.job.log_id, true /* Chunked */).then(function(resp_log) {
							resp_log.parts.forEach(handleChunk);
						}).then(resolve, reject);
					});
				}),
				// Tail log for this job.
				new Promise(function(resolve, reject) {
					streamResolve = resolve;
					channel.bind('job:log', handleChunk);
					channel.bind('job:finished', streamResolve);
				}).then(function() {
					pusher.unsubscribe(channelName);
				}),
			]);
		}).then(function() {
			return getTravisJob(state.reponame, state.job_id);
		}).then(function(job) {
			state.job = job;
			return state.job.state;
		});
	};
	var cleanup = function() {
		return Promise.resolve().then(function() {
			pusher.disconnect();
			return getTravisBuild(state.reponame, state.repo.id, state.branch, false);
		}).then(function(build) {
			state.build = build;
			// If build is complete (all jobs), we can clean up the branch.
			if (state.SIGINT) {
				process.removeListener('SIGINT', state.SIGINT);
			}
			if (/^(passed|failed)$/.test(state.build.state || '')) {
				return deleteBranch(state.reponame, state.branch).catch(function(e) {
					/* Ignore failure, maybe already deleted. */
				});
			}
		});
	};

	// Ok, put all the pieces together.
	return initState().then(function() {
		if (program.job !== undefined) {
			// Watch one particular job.
			return watchJob((+program.job));
		}
		// Watch all jobs.
		return Promise.reduce(state.build.job_ids, function(_,jid,index) {
			return watchJob(index + 1);
		}, null);
	}).then(function() {
		return cleanup();
	}).then(function() {
		if (program.job !== undefined) {
			// Return our job status.
			return state.job.state;
		}
		// Return our build status.
		return state.build.state;
	});
};

doit().then(function(status) {
	console.log('==> ' + json.name + ': ' + status);
	if (status !== 'passed') {
		process.exit(1);
	} else {
		process.exit(0);
	}
}).done();
<commit_msg>Workaround Travis' delay in updating the build status at end of job.<commit_after>'use strict';
require('core-js/shim');
var Promise = require('prfun');

var program = require('commander');
var json = require('../package.json');

var fs = require('fs');
var Pusher = require('pusher-client');
var url = require('url');

var p = require('./p');
var request = require('./retry-request');

var CHILD_IGNORE = { childOptions: { stdio: 'ignore' } };
var readFile = Promise.promisify(fs.readFile, false, fs);

program
	.version(json.version)
	.usage('[options] [github user/project]')
	.option(
		'--travis-api <url>',
		'Travis API endpoint [https://api.travis-ci.org]',
		'https://api.travis-ci.org'
	).option(
		'--remote <name>',
		'Use the specified git remote for push [github]',
		'github'
	).option(
		'-j, --job <n>',
		'Only follow the specified job (job numbers start at 1)'
	);

program.parse(process.argv);

if (program.args.length > 1) {
	console.error('Too many arguments.');
	return 1;
}

var getUserAgent = function(reponame) {
	return json.name + '/' + json.version + ' (' +
		'node ' + process.version + ' ' + process.platform + ' ' + process.arch +
		')' + (reponame ? ' ' + reponame : '');
};

var gitRemoteRegex =
	/Push\s+URL:\s+(?:https?:\/\/github.com\/|git@github.com:)([^\/]+)\/([^\/]+?)(?:\.git)?\s/;

var getRepoName = function() {
	return Promise.resolve(program.args[0]).then(function(reponame) {
		if (reponame) { return reponame; }
		// Look in .git/config for 'github' remote.
		return p.execFile('git', ['remote','show',program.remote]).then(function(out) {
			var m = gitRemoteRegex.exec(out.stdout);
			if (m) {
				return m[1] + '/' + m[2];
			}
		});
	}).catch(function(e) { /* Ignore. */ }).then(function(reponame) {
		if (reponame) { return reponame; }
		// Look in .git/config for 'origin' remote.
		return p.execFile('git', ['remote','show','origin']).then(function(out) {
			var m = gitRemoteRegex.exec(out.stdout);
			if (m) {
				return m[1] + '/' + m[2];
			}
		});
	}).catch(function(e) { /* Ignore. */ }).then(function(reponame) {
		if (reponame) { return reponame; }
		// Look in ./package.json
		return readFile(
			'./package.json', { encoding: 'utf8' }
		).then(function(data) {
			var json = JSON.parse(data);
			if (json.repository && json.repository.type === 'git') {
				var m =
					/^(https?:\/\/github.com\/)?([^\/]+)\/([^\/]+?)(\.git)?$/.
					exec(json.repository.url || '');
				if (m) {
					return m[2] + '/' + m[3];
				}
			}
		});
	}).catch(function(e) { /* Ignore */ }).then(function(reponame) {
		if (reponame) { return reponame; }
		throw new Error("Can't determine github repo name.");
	});
};

// Ensure that there is a github remote, named 'github'.
var ensureRemote = function(repo) {
	return p.spawn('git', ['remote', 'show', program.remote], CHILD_IGNORE).then(function() {
		/* OK, github already exists. */
	}, function(e) {
		/* Doesn't exist, create it! */
		return p.spawn('git', ['remote','add',program.remote,'git@github.com:' + repo]);
	});
};

// Get the hash of HEAD.
var getGitHead = function() {
	return p.execFile('git', ['rev-parse', 'HEAD']).then(function(out) {
		return out.stdout.trim();
	});
};

// Create the new branch name.
var getBranchName = function(hash) {
	// Use a slash in the branch name so that gerrit can use
	// its access controls to ensure that the npm-travis user can only
	// push to npm-travis/* branches (not branches in general).
	return json.name + '/' + hash.slice(0, 8);
};

// Push to github.
var ensureBranch = function(repo, branchname) {
	return ensureRemote(repo).then(function() {
		return p.spawn('git', ['push', program.remote, 'HEAD:refs/heads/' + branchname], CHILD_IGNORE);
	});
};

var deleteBranch = function(repo, branchname) {
	return ensureRemote(repo).then(function() {
		return p.spawn('git', ['push', program.remote, ':' + branchname], {
			timeout: 60 * 1000,
			childOptions: CHILD_IGNORE.childOptions,
		});
	});
};

var apiRequest = function(path, opts) {
	opts = opts || {};
	if (Array.isArray(path)) {
		path = '/' + path.map(encodeURIComponent).join('/');
	}
	var apiURL = url.resolve(program.travisApi, path);
	var mimeType = 'application/vnd.travis-ci.2+json';
	if (opts.chunked) {
		mimeType += '; chunked=true';
	}
	return request({
		url: apiURL,
		encoding: 'utf8',
		pool: false,
		headers: {
			'User-Agent': getUserAgent(opts.reponame),
			Accept: mimeType,
		},
		retries: opts.retries,
		delay: opts.delay,
		timeout: opts.timeout,
	}).then(function(resp) {
		if (resp.response.statusCode !== 200) {
			throw new Error('Bad status: ' + resp.response.statusCode);
		}
		return opts.raw ? resp.body : JSON.parse(resp.body);
	});
};

var getTravisConfig = function() {
	return apiRequest('/config').get('config');
};

var getTravisRepo = function(reponame) {
	return apiRequest('/repos/' + reponame, { reponame: reponame }).get('repo');
};

var getTravisBuild = function(reponame, repo_id, branch, retry) {
	return apiRequest(['repos', repo_id, 'branches', branch], {
		reponame: reponame,
		timeout: 30 * 1000,
		delay:   5 * 1000, /* Check every 5 seconds... */
		retries: retry ? 3 * 12 : 0, /* ...for up to 3 minutes. */
	}).get('branch');
};

var getTravisJob = function(reponame, job_id) {
	return apiRequest(['jobs', job_id], { reponame: reponame }).get('job');
};

var getTravisLog = function(reponame, log_id, chunked) {
	return apiRequest(['logs', log_id], {
		reponame: reponame,
		chunked: chunked,
	}).get('log');
};

var Log = function() {
	this.chunks = [];
	this.next = 1;
	this._last = null;
};
Log.prototype.has = function(n) {
	return this.chunks[n] !== undefined;
};
Log.prototype.add = function(n, data) {
	this.chunks[n] = data;
	return this.advance();
};
Log.prototype.done = function() {
	return this._last !== null && this.next > this._last;
};
Log.prototype.advance = function() {
	while (this.chunks[this.next] !== undefined) {
		process.stdout.write(this.chunks[this.next++]);
	}
	return this.done();
};
Log.prototype.last = function(n) {
	this._last = n;
	return this.done();
};

var doit = function() {
	var state = {}, pusher;
	var initState = function() {
		return Promise.all([
			getTravisConfig().then(function(config) {
				state.config = config;
				pusher = new Pusher(state.config.pusher.key);
				if (false) { /* Debugging. */
					pusher.connection.on('message', function(message) {
						console.log('GOT PUSHER MESSAGE', message);
					});
				}
			}),
			getGitHead().then(function(hash) {
				state.githash = hash;
				state.branch = getBranchName(hash);
			}),
			getRepoName().then(function(reponame) {
				state.reponame = reponame;
				return getTravisRepo(reponame).then(function(repo) {
					state.repo = repo;
				});
			}),
		]).then(function() {
			// Check to see if builds on this branch already exist.
			return getTravisBuild(state.reponame, state.repo.id, state.branch, false).catch(function(e) {
				// Doesn't yet exist; push to github and then retry query until
				// we see the build show up in travis.
				return ensureBranch(state.reponame, state.branch).then(function() {
					// Clean up this branch on Ctrl-C.
					state.SIGINT = function() {
						console.log('\nCleaning up', state.branch);
						deleteBranch(state.reponame, state.branch).catch(function(e) {
							/* Ignore failure; we're dying here. */
						}).then(function() { process.exit(1); });
					};
					process.once('SIGINT', state.SIGINT);
					// Ok, get the build on this branch.
					return getTravisBuild(state.reponame, state.repo.id, state.branch, true);
				});
			});
		}).then(function(build) {
			// Ok, we've got a build on this branch!
			state.build = build;
			console.log(
				'==> Travis build',
				state.build.number,
				'(',
				'https://' + state.config.host + '/' + state.reponame + '/builds/' + state.build.id,
				')'
			);
			return state;
		});
	};
	var watchJob = function(job_num) {
		return Promise.resolve().then(function() {
			state.job_num = job_num;
			state.job_id = state.build.job_ids[state.job_num - 1];
			console.log(
				'==> Travis job',
				state.build.number + '.' + state.job_num,
				'(',
				'https://' + state.config.host + '/' + state.reponame + '/jobs/' + state.job_id,
				')'
			);
			// Check job #
			if (!state.job_id) {
				throw new Error(
					'Bad job number: ' + state.job_num +
						' (' + state.build.job_ids.length + ' jobs)'
				);
			}
			return getTravisJob(state.reponame, state.job_id);
		}).then(function(job) {
			state.job = job;
			var log = new Log();
			if (/^(passed|failed)$/.test(state.job.state || '')) {
				// Fetch all the logs, easy-peasy.
				return getTravisLog(state.reponame, state.job.log_id, false/*Not chunked*/).then(function(resp_log) {
					log.last(1);
					log.add(1, resp_log.body);
				});
			}
			// Hm, we need to stream the logs.
			var channelName = 'job-' + state.job_id;
			var channel = pusher.subscribe(channelName);
			var streamResolve;
			var handleChunk = function(data) {
				var content = data._log !== undefined ? data._log : data.content;
				if (data.final) {
					// Weird: *Usually* there's one more chunk coming.
					// Wait for it, but no more than 10s.
					log.last(data.number + 1);
					setTimeout(function() {
						if (!log.has(data.number + 1)) {
							log.add(data.number + 1, '\n----\n');
							streamResolve();
						}
					}, 10 * 1000);
				}
				var last = log.add(data.number, content);
				if (last) { streamResolve(); }
			};
			return Promise.all([
				// Fetch chunked log to fill in gaps.
				new Promise(function(resolve, reject) {
					channel.bind('pusher:subscription_succeeded', function() {
						getTravisLog(state.reponame, state.job.log_id, true /* Chunked */).then(function(resp_log) {
							resp_log.parts.forEach(handleChunk);
						}).then(resolve, reject);
					});
				}),
				// Tail log for this job.
				new Promise(function(resolve, reject) {
					streamResolve = resolve;
					channel.bind('job:log', handleChunk);
					channel.bind('job:finished', streamResolve);
				}).then(function() {
					pusher.unsubscribe(channelName);
				}),
			]);
		}).then(function() {
			return getTravisJob(state.reponame, state.job_id);
		}).then(function(job) {
			state.job = job;
			return state.job.state;
		});
	};
	var getBuildStatus = function(retries) {
		return getTravisBuild(
			state.reponame, state.repo.id, state.branch, false
		).then(function(build) {
			if (build.state !== 'started' || retries === 0) {
				return build;
			}
			// There's sometimes a race here: if the build state isn't
			// "passed" or "failed" yet, wait a little bit and try again.
			// In separate jobs mode this isn't unusual, but we still want
			// to be sure that the last one out cleans up the branch.
			console.log('==> ' + json.name + ': ' + build.state + ' (retrying)');
			return Promise.delay(5 * 1000).then(function() {
				return getBuildStatus(retries - 1);
			});
		});
	};
	var cleanup = function() {
		return Promise.resolve().then(function() {
			pusher.disconnect();
			return getBuildStatus(6/*30 seconds*/);
		}).then(function(build) {
			state.build = build;
			// If build is complete (all jobs), we can clean up the branch.
			if (state.SIGINT) {
				process.removeListener('SIGINT', state.SIGINT);
			}
			if (/^(passed|failed)$/.test(state.build.state || '')) {
				return deleteBranch(state.reponame, state.branch).catch(function(e) {
					/* Ignore failure, maybe already deleted. */
				});
			}
		});
	};

	// Ok, put all the pieces together.
	return initState().then(function() {
		if (program.job !== undefined) {
			// Watch one particular job.
			return watchJob((+program.job));
		}
		// Watch all jobs.
		return Promise.reduce(state.build.job_ids, function(_,jid,index) {
			return watchJob(index + 1);
		}, null);
	}).then(function() {
		return cleanup();
	}).then(function() {
		if (program.job !== undefined) {
			// Return our job status.
			return state.job.state;
		}
		// Return our build status.
		return state.build.state;
	});
};

doit().then(function(status) {
	console.log('==> ' + json.name + ': ' + status);
	if (status !== 'passed') {
		process.exit(1);
	} else {
		process.exit(0);
	}
}).done();
<|endoftext|>


<commit_before>const request = require('request-promise');
const co = require('co');
const csv = require('csv');
const messages = require('elasticio-node').messages;
const client = require('elasticio-rest-node')();

let stringifier;
let signedUrl;
let timeout;
let rowCount = 0;

exports.init = init;
exports.process = processAction;

function init() {
    return co(function* gen() {

        stringifier = csv.stringify({
            header: true
        });

        signedUrl = yield client.resources.storage.createSignedUrl();

        console.log(signedUrl);

        let putUrl = signedUrl.put_url;
        console.log('CSV file to be uploaded file to uri=%s', putUrl);

        stringifier.pipe(request.put(putUrl));
    });
}

function processAction(msg) {
    const self = this;

    if (timeout) {
        clearTimeout(timeout);
    }

    timeout = setTimeout(()=> {
        console.log('Closing the stream due to inactivity');
        co(function* gen() {
            console.log('The resulting CSV file contains %s rows', rowCount);
            stringifier.end();

            const messageToEmit = messages.newMessageWithBody({
                rowCount
            });
            const fileName = messageToEmit.id + '.csv';
            messageToEmit.attachments[fileName] = {
                "content-type": "text/csv",
                url: signedUrl.get_url
            };
            console.log('%j', messageToEmit);
            signedUrl = null;
            rowCount = 0;

            console.log('Emitting message');
            self.emit('data', messageToEmit);

            yield init();
        });
    }, 5000);

    console.log('Writing to stream', msg.body);

    stringifier.write(msg.body);
    rowCount++;
}

/**

co(function* proc() {
    yield init();

    for (let i = 0; i <= 100; i++) {

        processAction({
            body: {
                run: i,
                timestamp: Date.now()
            }
        });
    }

    setTimeout(()=> {

        console.log('Next round');

        for (let i = 100; i <= 200; i++) {

            processAction({
                body: {
                    run: i,
                    timestamp: Date.now()
                }
            });
        }
    }, 10000);
});
 **/
<commit_msg>Increased timeout to 10 seconds<commit_after>const request = require('request-promise');
const co = require('co');
const csv = require('csv');
const messages = require('elasticio-node').messages;
const client = require('elasticio-rest-node')();

let stringifier;
let signedUrl;
let timeout;
let rowCount = 0;

exports.init = init;
exports.process = processAction;

function init() {
    return co(function* gen() {

        stringifier = csv.stringify({
            header: true
        });

        signedUrl = yield client.resources.storage.createSignedUrl();

        let putUrl = signedUrl.put_url;
        console.log('CSV file to be uploaded file to uri=%s', putUrl);

        stringifier.pipe(request.put(putUrl));
    });
}

function processAction(msg) {
    const self = this;

    if (timeout) {
        clearTimeout(timeout);
    }

    timeout = setTimeout(()=> {
        console.log('Closing the stream due to inactivity');
        co(function* gen() {
            console.log('The resulting CSV file contains %s rows', rowCount);
            stringifier.end();

            const messageToEmit = messages.newMessageWithBody({
                rowCount
            });
            const fileName = messageToEmit.id + '.csv';
            messageToEmit.attachments[fileName] = {
                "content-type": "text/csv",
                url: signedUrl.get_url
            };
            signedUrl = null;
            rowCount = 0;

            console.log('Emitting message %j', messageToEmit);
            self.emit('data', messageToEmit);

            yield init();
        });
    }, 10000);

    console.log('Writing to stream', msg.body);

    stringifier.write(msg.body);
    rowCount++;
}

/**

co(function* proc() {
    yield init();

    for (let i = 0; i <= 100; i++) {

        processAction({
            body: {
                run: i,
                timestamp: Date.now()
            }
        });
    }

    setTimeout(()=> {

        console.log('Next round');

        for (let i = 100; i <= 200; i++) {

            processAction({
                body: {
                    run: i,
                    timestamp: Date.now()
                }
            });
        }
    }, 10000);
});
 **/
<|endoftext|>


<commit_before>$(document).ready(function() {

  // Helpers

  $.fn.elementText = function() {
    var str = '';
    $(this).contents().each(function() {
      if ($(this).nodeType == 3) {
        str += $(this).textContent || $(this).innerText || '';
      }
    });
    return str;
  };

  // Sidebar expanding

  var self = $(this);
  self.sidebarExpanded = false;
  self.originalSidebarStyle = {
    'width': '',
    'position': '',
    'box-shadow': '',
  };
  self.originalSidebarLeftMargin = parseInt($('#p-sidebar').css(
    'margin-left'));
  self.sidebarLeftMarginAnimationWidth = 10;
  self.sidebarExpandedStyle = {
    'width': '80%',
    'right': '20px',
    'position': 'absolute',
    'box-shadow': '0 0 0 9999px rgba(0, 0, 0, 0.3)',
  };
  self.sidebarWidthToggleOriginalText =
    $('#p-sidebar-width-toggle').text();
  self.sidebarWidthToggleExpandedText = 'Narrow sidebar';

  self.toggleSidebarWidth = function() {
    if (self.sidebarExpanded) {
      self.sidebarExpanded = false;
      $('#p-sidebar-width-toggle')
        .text(self.sidebarWidthToggleOriginalText);
      $('#p-sidebar').css(self.originalSidebarStyle);
    } else {
      self.sidebarExpanded = true;
      $('#p-sidebar-width-toggle')
        .text(self.sidebarWidthToggleExpandedText);
      $('#p-sidebar').css(self.sidebarExpandedStyle);
    }
  };

  $('#p-sidebar-width-toggle').click(function(e) {
    e.preventDefault();
    self.toggleSidebarWidth();
  });

  // Collapse the sidebar when clicking on a link while the sidebar is
  // expanded. So when hitting the back button the sidebar will be
  // collapsed. This is moreintuitive.
  $('#toc a').click(function() {
    if (self.sidebarExpanded) {
      console.log('LEAVE');  // DEBUG
      self.toggleSidebarWidth();
    }
  });

  // Keybinding
  $('body').append(
    '\

\

Close
\

Key bindings
\

\

?: Toggle key bindings help
\

w: Toggle sidebar width
\

s: Select sidebar search box
\

Esc: Unselect sidebar search box
\

\
'
  );
  self.helpBox = $('#help-box');
  self.helpBox.hide();
  self.helpBox.css({
    'position': 'fixed',
    'background-color': '#FAFAFA',
    'color': '#686868',
    'top': '50%',
    'left': '50%',
    'width': '500px',
    'height': '400px',
    'margin-left': '-200px',
    'margin-top': '-300px',
    'z-index': '9999',
    'padding': '10px',
    'box-shadow': '0 0 0 9999px rgba(0, 0, 0, 0.2)',
    'border': '1px solid #ACACAC',
  });
  $('#p-sidebar-search-box').parent().find('a:first').parent().prepend('\
Key bindings\
');
  $('.help-box-toggle').click(function(e) {
    e.preventDefault();
    self.helpBox.toggle();
  })
  $(document).keypress(function(e) {
    // 119: w   - Toggle sidebar width
    // 115: s   - Select sidebar search box
    // 0:   Esc - Unselect sidebar search box
    // 63:  ?   - Toggle key bindings help
    if (e.target.nodeName != 'INPUT' && e.target.nodeName != 'TEXTAREA') {
      if (e.which == 119) {
        self.toggleSidebarWidth();
      } else if (e.which == 115) {
        $('#p-sidebar-search-box').select();
      } else if (e.which == 63) {
        $('#help-box').toggle();
      }
    } else if (e.target.id == 'sidebar-search-box') {
      if (e.which == 0) {
        $('#p-sidebar-search-box').blur();
      }
    }
  });

  // Search

  $('#p-sidebar-search-box').keyup(function() {
    var filter = $(this).val().toLowerCase();

    $('#p-sidebar li').each(function() {
      var text = $(this).text().toLowerCase();

      if (text.match(filter)) {
        $(this).show()
      } else {
        $(this).hide();
      }
    });
  });

});
<commit_msg>Skip `Key bindings' and `widen sidebar' links when hitting TAB<commit_after>$(document).ready(function() {

  // Helpers

  $.fn.elementText = function() {
    var str = '';
    $(this).contents().each(function() {
      if ($(this).nodeType == 3) {
        str += $(this).textContent || $(this).innerText || '';
      }
    });
    return str;
  };

  // Sidebar expanding

  var self = $(this);
  self.sidebarExpanded = false;
  self.originalSidebarStyle = {
    'width': '',
    'position': '',
    'box-shadow': '',
  };
  self.originalSidebarLeftMargin = parseInt($('#p-sidebar').css(
    'margin-left'));
  self.sidebarLeftMarginAnimationWidth = 10;
  self.sidebarExpandedStyle = {
    'width': '80%',
    'right': '20px',
    'position': 'absolute',
    'box-shadow': '0 0 0 9999px rgba(0, 0, 0, 0.3)',
  };
  self.sidebarWidthToggleOriginalText =
    $('#p-sidebar-width-toggle').text();
  self.sidebarWidthToggleExpandedText = 'Narrow sidebar';

  self.toggleSidebarWidth = function() {
    if (self.sidebarExpanded) {
      self.sidebarExpanded = false;
      $('#p-sidebar-width-toggle')
        .text(self.sidebarWidthToggleOriginalText);
      $('#p-sidebar').css(self.originalSidebarStyle);
    } else {
      self.sidebarExpanded = true;
      $('#p-sidebar-width-toggle')
        .text(self.sidebarWidthToggleExpandedText);
      $('#p-sidebar').css(self.sidebarExpandedStyle);
    }
  };

  $('#p-sidebar-width-toggle').click(function(e) {
    e.preventDefault();
    self.toggleSidebarWidth();
  });

  // Collapse the sidebar when clicking on a link while the sidebar is
  // expanded. So when hitting the back button the sidebar will be
  // collapsed. This is moreintuitive.
  $('#toc a').click(function() {
    if (self.sidebarExpanded) {
      self.toggleSidebarWidth();
    }
  });

  // Keybinding
  $('body').append(
    '\

\

Close
\

Key bindings
\

\

?: Toggle key bindings help
\

w: Toggle sidebar width
\

s: Select sidebar search box
\

Esc: Unselect sidebar search box
\

\
'
  );
  self.helpBox = $('#help-box');
  self.helpBox.hide();
  self.helpBox.css({
    'position': 'fixed',
    'background-color': '#FAFAFA',
    'color': '#686868',
    'top': '50%',
    'left': '50%',
    'width': '500px',
    'height': '400px',
    'margin-left': '-200px',
    'margin-top': '-300px',
    'z-index': '9999',
    'padding': '10px',
    'box-shadow': '0 0 0 9999px rgba(0, 0, 0, 0.2)',
    'border': '1px solid #ACACAC',
  });
  $('#p-sidebar-search-box').parent().find('a:first').parent().prepend('\
Key bindings\
');
  $('.help-box-toggle').click(function(e) {
    e.preventDefault();
    self.helpBox.toggle();
  })
  $(document).keypress(function(e) {
    // 119: w   - Toggle sidebar width
    // 115: s   - Select sidebar search box
    // 0:   Esc - Unselect sidebar search box
    // 63:  ?   - Toggle key bindings help
    if (e.target.nodeName != 'INPUT' && e.target.nodeName != 'TEXTAREA') {
      if (e.which == 119) {
        self.toggleSidebarWidth();
      } else if (e.which == 115) {
        $('#p-sidebar-search-box').select();
      } else if (e.which == 63) {
        $('#help-box').toggle();
      }
    } else if (e.target.id == 'sidebar-search-box') {
      if (e.which == 0) {
        $('#p-sidebar-search-box').blur();
      }
    }
  });

  // Skip `Key bindings' and `Widen sidebar' links when navigation using
  // TAB.
  $('#p-sidebar-search-box').parent().find('a').slice(0,2)
    .each(function(index) {
      $(this).attr('tabindex', '9999');
      // Add tooltips
      var title = '';
      if (index == 0) {
        title = 'Shortcut: "?"';
      } else if (index == 1) {
        title = 'Shortcut: "w"';
      }
      $(this).attr('title', title);
      $(this).attr('data-toggle', 'tooltip');
      $(this).attr('data-placement', 'top');
    });

  // Search

  $('#p-sidebar-search-box').keyup(function() {
    var filter = $(this).val().toLowerCase();

    $('#p-sidebar li').each(function() {
      var text = $(this).text().toLowerCase();

      if (text.match(filter)) {
        $(this).show()
      } else {
        $(this).hide();
      }
    });
  });

});
<|endoftext|>


<commit_before>// Constructor
var AnsiParser = module.exports = {};

/**
 * parse
 * Parses the string containing ANSI styles.
 *
 * @name parse
 * @function
 * @param {String} input The input string.
 * @return {Array} An array of object like below:
 *
 *  ```js
 *  [
 *    {
 *        start: "\u00\u001b[1m\u001b[38;5;231",
 *        end: "\u001b[0m\u001b[22m",
 *        content: "H"
 *    },
 *    {
 *       start: "\u00\u001b[1m\u001b[38;5;231",
 *       content: "e"
 *       end: "\u001b[0m\u001b[22m",
 *    },
 *    ...
 *  ]
 *  ```
 */
AnsiParser.parse = function (input) {

    if (Array.isArray(input)) {
        var arr = [];
        for (var i = 0; i < input.length; ++i) {
            arr.push(AnsiParser.parse(input[i]));
        }
        return arr;
    }

    if (typeof input !== "string") {
        throw new Error("Input is not a string.");
    }

    if (input === AnsiParser.removeAnsi(input)) {
        return input.split("").map(function (c) {
            return {
                start: ""
              , content: c
              , end: ""
            };
        });
    }

    var str = input
      , re = /((?:\u001b\[.*?m)+)([^])([^]|)(?=.*?((?:\u001b\[.*?m)+)|)/
      , cur
      , result = []
      ;

    while (null != (cur = str.match(re))) {
        if ("\u001b" === cur[2]) {
            str = str.replace(re, "$2$3");
        }
        else {
            result.push({
                start: cur[1]
              , content: cur[2]
              , end: cur[4] || ""
            })
            str = str.replace(re, "$1$3")
        }
    }

    return result;
};

/**
 * removeAnsi
 * Removes ANSI styles from the input string.
 *
 * @name removeAnsi
 * @function
 * @param {String} input The input string.
 * @return {String} The string without ANSI styles.
 */
AnsiParser.removeAnsi = function (input) {
    return input.replace(/\u001b\[.*?m/g, "");
};

/**
 * stringify
 * Stringifies an array of objects in the format defined by `AnsiParser`.
 *
 * @name stringify
 * @function
 * @param {Array} arr The input array.
 * @return {String} The stringified input array.
 */
AnsiParser.stringify = function (arr) {
    var str = ""
      , cArr = null
      ;
    for (var i = 0; i < arr.length; ++i) {
        cArr = arr[i];
        str += cArr.start + cArr.content + cArr.end;
    }
    return str;
};

/**
 * addChar
 * Adds a new char into array.
 *
 * @name addChar
 * @function
 * @param {Array} arr The input array.
 * @param {String} c The char to add.
 * @param {String} s ANSI start style.
 * @param {String} e ANSI end style.
 * @return {undefined}
 */
AnsiParser.addChar = function (arr, c, s, e) {
    arr.push({
        start: s || ""
      , content: c
      , start: e || ""
    });
};
<commit_msg>Add empty space at the end<commit_after>// Constructor
var AnsiParser = module.exports = {};

/**
 * parse
 * Parses the string containing ANSI styles.
 *
 * @name parse
 * @function
 * @param {String} input The input string.
 * @return {Array} An array of object like below:
 *
 *  ```js
 *  [
 *    {
 *        start: "\u00\u001b[1m\u001b[38;5;231",
 *        end: "\u001b[0m\u001b[22m",
 *        content: "H"
 *    },
 *    {
 *       start: "\u00\u001b[1m\u001b[38;5;231",
 *       content: "e"
 *       end: "\u001b[0m\u001b[22m",
 *    },
 *    ...
 *  ]
 *  ```
 */
AnsiParser.parse = function (input) {

    if (Array.isArray(input)) {
        var arr = [];
        for (var i = 0; i < input.length; ++i) {
            arr.push(AnsiParser.parse(input[i]));
        }
        return arr;
    }

    if (typeof input !== "string") {
        throw new Error("Input is not a string.");
    }

    // TODO This is a hack to prevent strange outputs
    if (input.substr(-1) !== " ") {
        input = input + " ";
    }

    if (input === AnsiParser.removeAnsi(input)) {
        return input.split("").map(function (c) {
            return {
                start: ""
              , content: c
              , end: ""
            };
        });
    }

    var str = input
      , re = /((?:\u001b\[.*?m)+)([^])([^]|)(?=.*?((?:\u001b\[.*?m)+)|)/
      , cur
      , result = []
      ;

    while (null != (cur = str.match(re))) {
        if ("\u001b" === cur[2]) {
            str = str.replace(re, "$2$3");
        }
        else {
            result.push({
                start: cur[1]
              , content: cur[2]
              , end: cur[4] || ""
            })
            str = str.replace(re, "$1$3")
        }
    }

    return result;
};

/**
 * removeAnsi
 * Removes ANSI styles from the input string.
 *
 * @name removeAnsi
 * @function
 * @param {String} input The input string.
 * @return {String} The string without ANSI styles.
 */
AnsiParser.removeAnsi = function (input) {
    return input.replace(/\u001b\[.*?m/g, "");
};

/**
 * stringify
 * Stringifies an array of objects in the format defined by `AnsiParser`.
 *
 * @name stringify
 * @function
 * @param {Array} arr The input array.
 * @return {String} The stringified input array.
 */
AnsiParser.stringify = function (arr) {
    var str = ""
      , cArr = null
      ;
    for (var i = 0; i < arr.length; ++i) {
        cArr = arr[i];
        str += cArr.start + cArr.content + cArr.end;
    }
    return str;
};

/**
 * addChar
 * Adds a new char into array.
 *
 * @name addChar
 * @function
 * @param {Array} arr The input array.
 * @param {String} c The char to add.
 * @param {String} s ANSI start style.
 * @param {String} e ANSI end style.
 * @return {undefined}
 */
AnsiParser.addChar = function (arr, c, s, e) {
    arr.push({
        start: s || ""
      , content: c
      , start: e || ""
    });
};
<|endoftext|>


<commit_before>var docxReader = require("./docx/docx-reader");
var docxStyleMap = require("./docx/style-map");
var DocumentConverter = require("./document-to-html").DocumentConverter;
var readStyle = require("./style-reader").readStyle;
var readOptions = require("./options-reader").readOptions;
var unzip = require("./unzip");
var Result = require("./results").Result;

exports.convertToHtml = convertToHtml;
exports.convertToMarkdown = convertToMarkdown;
exports.convert = convert;
exports.extractRawText = extractRawText;
exports.images = require("./images");
exports.transforms = require("./transforms");
exports.underline = require("./underline");
exports.embedStyleMap = embedStyleMap;
exports.readEmbeddedStyleMap = readEmbeddedStyleMap;

function convertToHtml(input, options) {
    return convert(input, options);
}

function convertToMarkdown(input, options) {
    var markdownOptions = Object.create(options || {});
    markdownOptions.outputFormat = "markdown";
    return convert(input, markdownOptions);
}

function convert(input, options) {
    options = Object.create(options || {});

    return unzip.openZip(input)
        .tap(function(docxFile) {
            if (!options.styleMap) {
                return docxStyleMap.readStyleMap(docxFile).then(function(styleMap) {
                    options.styleMap = styleMap || "";
                });
            }
        })
        .then(function(docxFile) {
            var fullOptions = readOptions(options);
            return docxReader.read(docxFile, input)
                .then(function(documentResult) {
                    return documentResult.map(fullOptions.transformDocument);
                })
                .then(function(documentResult) {
                    return convertDocumentToHtml(documentResult, fullOptions);
                });
        });
}

function readEmbeddedStyleMap(input) {
    return unzip.openZip(input)
        .then(docxStyleMap.readStyleMap);
}

function convertDocumentToHtml(documentResult, options) {
    var parsedOptions = Object.create(options);
    var styleMapResult = parseStyleMap(options.styleMap);
    parsedOptions.styleMap = styleMapResult.value;
    var documentConverter = new DocumentConverter(parsedOptions);

    return documentResult.flatMapThen(function(document) {
        return styleMapResult.flatMapThen(function(styleMap) {
            return documentConverter.convertToHtml(document);
        });
    });
}

function parseStyleMap(styleMap) {
    return Result.combine((styleMap || []).map(readStyle))
        .map(function(styleMap) {
            return styleMap.filter(function(styleMapping) {
                return !!styleMapping;
            });
        });
}

function extractRawText(input) {
    return unzip.openZip(input)
        .then(docxReader.read)
        .then(function(documentResult) {
            return documentResult.map(convertElementToRawText);
        });
}

function convertElementToRawText(element) {
    if (element.type === "text") {
        return element.value;
    } else {
        var tail = element.type === "paragraph" ? "\n\n" : "";
        return (element.children || []).map(convertElementToRawText).join("") + tail;
    }
}

function embedStyleMap(input, styleMap) {
    return unzip.openZip(input)
        .then(function(docxFile) {
            docxStyleMap.writeStyleMap(docxFile, styleMap);

            return {
                toBuffer: docxFile.toBuffer
            };
        });
}

exports.styleMapping = function() {
    throw new Error('Use a raw string instead of mammoth.styleMapping e.g. "p[style-name=\'Title\'] => h1" instead of mammoth.styleMapping("p[style-name=\'Title\'] => h1")');
};
<commit_msg>Fix race condition<commit_after>var docxReader = require("./docx/docx-reader");
var docxStyleMap = require("./docx/style-map");
var DocumentConverter = require("./document-to-html").DocumentConverter;
var readStyle = require("./style-reader").readStyle;
var readOptions = require("./options-reader").readOptions;
var unzip = require("./unzip");
var Result = require("./results").Result;

exports.convertToHtml = convertToHtml;
exports.convertToMarkdown = convertToMarkdown;
exports.convert = convert;
exports.extractRawText = extractRawText;
exports.images = require("./images");
exports.transforms = require("./transforms");
exports.underline = require("./underline");
exports.embedStyleMap = embedStyleMap;
exports.readEmbeddedStyleMap = readEmbeddedStyleMap;

function convertToHtml(input, options) {
    return convert(input, options);
}

function convertToMarkdown(input, options) {
    var markdownOptions = Object.create(options || {});
    markdownOptions.outputFormat = "markdown";
    return convert(input, markdownOptions);
}

function convert(input, options) {
    options = Object.create(options || {});

    return unzip.openZip(input)
        .tap(function(docxFile) {
            if (!options.styleMap) {
                return docxStyleMap.readStyleMap(docxFile).then(function(styleMap) {
                    options.styleMap = styleMap || "";
                });
            }
        })
        .then(function(docxFile) {
            var fullOptions = readOptions(options);
            return docxReader.read(docxFile, input)
                .then(function(documentResult) {
                    return documentResult.map(fullOptions.transformDocument);
                })
                .then(function(documentResult) {
                    return convertDocumentToHtml(documentResult, fullOptions);
                });
        });
}

function readEmbeddedStyleMap(input) {
    return unzip.openZip(input)
        .then(docxStyleMap.readStyleMap);
}

function convertDocumentToHtml(documentResult, options) {
    var parsedOptions = Object.create(options);
    var styleMapResult = parseStyleMap(options.styleMap);
    parsedOptions.styleMap = styleMapResult.value;
    var documentConverter = new DocumentConverter(parsedOptions);

    return documentResult.flatMapThen(function(document) {
        return styleMapResult.flatMapThen(function(styleMap) {
            return documentConverter.convertToHtml(document);
        });
    });
}

function parseStyleMap(styleMap) {
    return Result.combine((styleMap || []).map(readStyle))
        .map(function(styleMap) {
            return styleMap.filter(function(styleMapping) {
                return !!styleMapping;
            });
        });
}

function extractRawText(input) {
    return unzip.openZip(input)
        .then(docxReader.read)
        .then(function(documentResult) {
            return documentResult.map(convertElementToRawText);
        });
}

function convertElementToRawText(element) {
    if (element.type === "text") {
        return element.value;
    } else {
        var tail = element.type === "paragraph" ? "\n\n" : "";
        return (element.children || []).map(convertElementToRawText).join("") + tail;
    }
}

function embedStyleMap(input, styleMap) {
    return unzip.openZip(input)
        .tap(function(docxFile) {
            return docxStyleMap.writeStyleMap(docxFile, styleMap);
        })
        .then(function(docxFile) {
            return {
                toBuffer: docxFile.toBuffer
            };
        });
}

exports.styleMapping = function() {
    throw new Error('Use a raw string instead of mammoth.styleMapping e.g. "p[style-name=\'Title\'] => h1" instead of mammoth.styleMapping("p[style-name=\'Title\'] => h1")');
};
<|endoftext|>