<commit_before>/* Copyright @ 2015 Atlassian Pty Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var transform = require('sdp-transform');
var transformUtils = require('./transform-utils');
var parseSsrcs = transformUtils.parseSsrcs;
var writeSsrcs = transformUtils.writeSsrcs;

//region Constants

var DEFAULT_NUM_OF_LAYERS = 3;

//endregion

//region Ctor

function Simulcast(options) {

    this.options = options ? options : {};

    if (!this.options.numOfLayers) {
        this.options.numOfLayers = DEFAULT_NUM_OF_LAYERS;
    }

    this.layers = [];
}

//endregion

//region Stateless private utility functions

/**
 * Returns a random integer between min (included) and max (excluded)
 * Using Math.round() gives a non-uniform distribution!
 * @returns {number}
 */
function generateSSRC() {
    var min = 0, max = 0xffffffff;
    return Math.floor(Math.random() * (max - min)) + min;
};

function processVideo(session, action) {
    if (session == null || !Array.isArray(session.media)) {
        return;
    }

    session.media.forEach(function (mLine) {
        if (mLine.type === 'video') {
            action(mLine);
        }
    });
}

function validateDescription(desc)
{
    return desc && desc != null
        && desc.type && desc.type != ''
        && desc.sdp && desc.sdp != '';
}

function explodeRemoteSimulcast(mLine) {

    if (!mLine || !Array.isArray(mLine.ssrcGroups)) {
        return;
    }

    var sources = parseSsrcs(mLine);
    var order = [];

    // Find the SIM group and explode its sources.
    var j = mLine.ssrcGroups.length;
    while (j--) {

        if (mLine.ssrcGroups[j].semantics !== 'SIM') {
            continue;
        }

        var simulcastSsrcs = mLine.ssrcGroups[j].ssrcs.split(' ');

        for (var i = 0; i < simulcastSsrcs.length; i++) {

            var ssrc = simulcastSsrcs[i];
            order.push(ssrc);

            var parts = sources[ssrc].msid.split(' ');
            sources[ssrc].msid = [parts[0], '/', i, ' ', parts[1], '/', i].join('');
            sources[ssrc].cname = [sources[ssrc].cname, '/', i].join('');

            // Remove all the groups that this SSRC participates in.
            mLine.ssrcGroups.forEach(function (relatedGroup) {
                if (relatedGroup.semantics === 'SIM') {
                    return;
                }

                var relatedSsrcs = relatedGroup.ssrcs.split(' ');
                if (relatedSsrcs.indexOf(ssrc) === -1) {
                    return;
                }

                // Nuke all the related SSRCs.
                relatedSsrcs.forEach(function (relatedSSRC) {
                    sources[relatedSSRC].msid = sources[ssrc].msid;
                    sources[relatedSSRC].cname = sources[ssrc].cname;
                    if (relatedSSRC !== ssrc) {
                        order.push(relatedSSRC);
                    }
                });

                // Schedule the related group for nuking.
            })
        }

        mLine.ssrcs = writeSsrcs(sources, order);
        mLine.ssrcGroups.splice(j, 1);
    };
}

function implodeRemoteSimulcast(mLine) {

    if (!mLine || !Array.isArray(mLine.ssrcGroups)) {
        console.info('Halt: There are no SSRC groups in the remote ' +
                'description.');
        return;
    }

    var sources = parseSsrcs(mLine);

    // Find the SIM group and nuke it.
    mLine.ssrcGroups.forEach(function (simulcastGroup) {
        if (simulcastGroup.semantics !== 'SIM') {
            return;
        }

        console.info("Imploding SIM group: " + simulcastGroup.ssrcs);
        // Schedule the SIM group for nuking.
        simulcastGroup.nuke = true;

        var simulcastSsrcs = simulcastGroup.ssrcs.split(' ');

        // Nuke all the higher layer SSRCs.
        for (var i = 1; i < simulcastSsrcs.length; i++) {

            var ssrc = simulcastSsrcs[i];
            delete sources[ssrc];

            // Remove all the groups that this SSRC participates in.
            mLine.ssrcGroups.forEach(function (relatedGroup) {
                if (relatedGroup.semantics === 'SIM') {
                    return;
                }

                var relatedSsrcs = relatedGroup.ssrcs.split(' ');
                if (relatedSsrcs.indexOf(ssrc) === -1) {
                    return;
                }

                // Nuke all the related SSRCs.
                relatedSsrcs.forEach(function (relatedSSRC) {
                    delete sources[relatedSSRC];
                });

                // Schedule the related group for nuking.
                relatedGroup.nuke = true;
            })
        }

        return;
    });

    mLine.ssrcs = writeSsrcs(sources);

    // Nuke all the scheduled groups.
    var i = mLine.ssrcGroups.length;
    while (i--) {
        if (mLine.ssrcGroups[i].nuke) {
            mLine.ssrcGroups.splice(i, 1);
        }
    }
}

function removeGoogConference(mLine) {
    if (!mLine || !Array.isArray(mLine.invalid)) {
        return;
    }

    var i = mLine.invalid.length;
    while (i--) {
        if (mLine.invalid[i].value == 'x-google-flag:conference') {
            mLine.invalid.splice(i, 1);
        }
    }
}

function assertGoogConference(mLine) {
    if (!mLine) {
        return;
    }

    if (!Array.isArray(mLine.invalid)) {
        mLine.invalid = [];
    }

    if (!mLine.invalid.some(
            function (i) { return i.value === 'x-google-flag:conference' })) {
        mLine.invalid.push({'value': 'x-google-flag:conference'});
    }
}

//endregion

//region "Private" functions

/**
 *
 * @param mLine
 * @private
 */
Simulcast.prototype._maybeInitializeLayers = function(mLine) {

    if (!mLine || mLine.type !== 'video') {
        return;
    }

    var sources = parseSsrcs(mLine);

    if (Object.keys(sources).length === 0) {

        // no sources, disable simulcast.
        if (this.layers.length !== 0) {
            this.layers = [];
        }

        return;
    }

    // find the base layer (we'll reuse its msid and cname).
    var baseLayerSSRC = Object.keys(sources)[0];
    var baseLayer = sources[baseLayerSSRC];

    // todo(gp) handle screen sharing.

    // check if base CNAME has changed and reinitialise layers.
    if (this.layers.length > 0
        && sources[baseLayerSSRC].cname !== this.layers[0].cname) {
        this.layers = [];
    }

    // (re)initialise layers
    if (this.layers.length < 1) {

        // first push the base layer.
        this.layers.push({
            ssrc: baseLayerSSRC,
            msid: baseLayer.msid,
            cname: baseLayer.cname
        });

        var rtx = false; // RFC 4588
        if (Array.isArray(mLine.rtp)) {
            rtx = mLine.rtp.some(
                function (rtpmap) { return rtpmap.codec === 'rtx'; });
        }

        if (rtx) {
            this.layers[0].rtx = generateSSRC();
        }

        // now push additional layers.
        for (var i = 1; i < Math.max(1, this.options.numOfLayers); i++) {

            var layer = { ssrc: generateSSRC() };
            if (rtx) {
                layer.rtx = generateSSRC();
            }

            this.layers.push(layer);
        }
    }
};

/**
 *
 * @param mLine
 * @private
 */
Simulcast.prototype._restoreSimulcastView = function(mLine) {
    if (mLine && mLine.type === 'video' && this.layers.length !== 0) {

        var sources = {};

        var msid = this.layers[0].msid;
        var cname = this.layers[0].cname;
        var simulcastSsrcs = [];
        var ssrcGroups = [];

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

            sources[layer.ssrc] = { msid: msid, cname: cname };
            simulcastSsrcs.push(layer.ssrc);

            if (layer.rtx) {

                sources[layer.rtx] = {
                    msid: msid,
                    cname: cname
                }

                ssrcGroups.push({
                    semantics: 'FID',
                    ssrcs: [layer.ssrc, layer.rtx].join(' ')
                });
            }
        }

        ssrcGroups.push({
            semantics: 'SIM',
            ssrcs: simulcastSsrcs.join(' ')
        });

        mLine.ssrcGroups = ssrcGroups;
        mLine.ssrcs = writeSsrcs(sources, simulcastSsrcs);
    }
}

//endregion

//region "Public" functions

Simulcast.prototype.isSupported = function () {
    return window.chrome;

    // TODO this needs improvements. For example I doubt that Chrome in Android
    // has simulcast support. Also, only recent versions of Chromium have native
    // simulcast support.
}

/**
 *
 * @param desc
 * @returns {RTCSessionDescription}
 */
Simulcast.prototype.mungeRemoteDescription = function (desc) {

    if (!validateDescription(desc)) {
        return desc;
    }

    var session = transform.parse(desc.sdp);

    var self = this;
    processVideo(session, function (mLine) {

        // Handle simulcast reception.
        if (self.options.explodeRemoteSimulcast) {
            explodeRemoteSimulcast(mLine);
        } else {
            implodeRemoteSimulcast(mLine);
        }

        // If native simulcast is enabled, we must append the x-goog-conference
        // attribute to the SDP.
        if (self.layers.length < 1) {
            removeGoogConference(mLine);
        } else {
            assertGoogConference(mLine);
        }
    });

    return new RTCSessionDescription({
        type: desc.type,
        sdp: transform.write(session)
    });
};

/**
 *
 * @param desc
 * @returns {RTCSessionDescription}
 */
Simulcast.prototype.mungeLocalDescription = function (desc) {

    if (!validateDescription(desc) || !this.isSupported()) {
        return desc;
    }

    var session = transform.parse(desc.sdp);

    var self = this;
    processVideo(session, function (mLine) {
        if (mLine.direction == 'recvonly' || mLine.direction == 'inactive')
        {
            return;
        }
        // Initialize native simulcast layers, if not already done.
        self._maybeInitializeLayers(mLine);

        // Update the SDP with the simulcast layers.
        self._restoreSimulcastView(mLine);
    });

    return new RTCSessionDescription({
        type: desc.type,
        sdp: transform.write(session)
    });
};

//endregion

module.exports = Simulcast;
<commit_msg>Reinitialize simulcast layers also on base layer SSRC change<commit_after>/* Copyright @ 2015 Atlassian Pty Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var transform = require('sdp-transform');
var transformUtils = require('./transform-utils');
var parseSsrcs = transformUtils.parseSsrcs;
var writeSsrcs = transformUtils.writeSsrcs;

//region Constants

var DEFAULT_NUM_OF_LAYERS = 3;

//endregion

//region Ctor

function Simulcast(options) {

    this.options = options ? options : {};

    if (!this.options.numOfLayers) {
        this.options.numOfLayers = DEFAULT_NUM_OF_LAYERS;
    }

    this.layers = [];
}

//endregion

//region Stateless private utility functions

/**
 * Returns a random integer between min (included) and max (excluded)
 * Using Math.round() gives a non-uniform distribution!
 * @returns {number}
 */
function generateSSRC() {
    var min = 0, max = 0xffffffff;
    return Math.floor(Math.random() * (max - min)) + min;
};

function processVideo(session, action) {
    if (session == null || !Array.isArray(session.media)) {
        return;
    }

    session.media.forEach(function (mLine) {
        if (mLine.type === 'video') {
            action(mLine);
        }
    });
}

function validateDescription(desc)
{
    return desc && desc != null
        && desc.type && desc.type != ''
        && desc.sdp && desc.sdp != '';
}

function explodeRemoteSimulcast(mLine) {

    if (!mLine || !Array.isArray(mLine.ssrcGroups)) {
        return;
    }

    var sources = parseSsrcs(mLine);
    var order = [];

    // Find the SIM group and explode its sources.
    var j = mLine.ssrcGroups.length;
    while (j--) {

        if (mLine.ssrcGroups[j].semantics !== 'SIM') {
            continue;
        }

        var simulcastSsrcs = mLine.ssrcGroups[j].ssrcs.split(' ');

        for (var i = 0; i < simulcastSsrcs.length; i++) {

            var ssrc = simulcastSsrcs[i];
            order.push(ssrc);

            var parts = sources[ssrc].msid.split(' ');
            sources[ssrc].msid = [parts[0], '/', i, ' ', parts[1], '/', i].join('');
            sources[ssrc].cname = [sources[ssrc].cname, '/', i].join('');

            // Remove all the groups that this SSRC participates in.
            mLine.ssrcGroups.forEach(function (relatedGroup) {
                if (relatedGroup.semantics === 'SIM') {
                    return;
                }

                var relatedSsrcs = relatedGroup.ssrcs.split(' ');
                if (relatedSsrcs.indexOf(ssrc) === -1) {
                    return;
                }

                // Nuke all the related SSRCs.
                relatedSsrcs.forEach(function (relatedSSRC) {
                    sources[relatedSSRC].msid = sources[ssrc].msid;
                    sources[relatedSSRC].cname = sources[ssrc].cname;
                    if (relatedSSRC !== ssrc) {
                        order.push(relatedSSRC);
                    }
                });

                // Schedule the related group for nuking.
            })
        }

        mLine.ssrcs = writeSsrcs(sources, order);
        mLine.ssrcGroups.splice(j, 1);
    };
}

function implodeRemoteSimulcast(mLine) {

    if (!mLine || !Array.isArray(mLine.ssrcGroups)) {
        console.info('Halt: There are no SSRC groups in the remote ' +
                'description.');
        return;
    }

    var sources = parseSsrcs(mLine);

    // Find the SIM group and nuke it.
    mLine.ssrcGroups.forEach(function (simulcastGroup) {
        if (simulcastGroup.semantics !== 'SIM') {
            return;
        }

        console.info("Imploding SIM group: " + simulcastGroup.ssrcs);
        // Schedule the SIM group for nuking.
        simulcastGroup.nuke = true;

        var simulcastSsrcs = simulcastGroup.ssrcs.split(' ');

        // Nuke all the higher layer SSRCs.
        for (var i = 1; i < simulcastSsrcs.length; i++) {

            var ssrc = simulcastSsrcs[i];
            delete sources[ssrc];

            // Remove all the groups that this SSRC participates in.
            mLine.ssrcGroups.forEach(function (relatedGroup) {
                if (relatedGroup.semantics === 'SIM') {
                    return;
                }

                var relatedSsrcs = relatedGroup.ssrcs.split(' ');
                if (relatedSsrcs.indexOf(ssrc) === -1) {
                    return;
                }

                // Nuke all the related SSRCs.
                relatedSsrcs.forEach(function (relatedSSRC) {
                    delete sources[relatedSSRC];
                });

                // Schedule the related group for nuking.
                relatedGroup.nuke = true;
            })
        }

        return;
    });

    mLine.ssrcs = writeSsrcs(sources);

    // Nuke all the scheduled groups.
    var i = mLine.ssrcGroups.length;
    while (i--) {
        if (mLine.ssrcGroups[i].nuke) {
            mLine.ssrcGroups.splice(i, 1);
        }
    }
}

function removeGoogConference(mLine) {
    if (!mLine || !Array.isArray(mLine.invalid)) {
        return;
    }

    var i = mLine.invalid.length;
    while (i--) {
        if (mLine.invalid[i].value == 'x-google-flag:conference') {
            mLine.invalid.splice(i, 1);
        }
    }
}

function assertGoogConference(mLine) {
    if (!mLine) {
        return;
    }

    if (!Array.isArray(mLine.invalid)) {
        mLine.invalid = [];
    }

    if (!mLine.invalid.some(
            function (i) { return i.value === 'x-google-flag:conference' })) {
        mLine.invalid.push({'value': 'x-google-flag:conference'});
    }
}

//endregion

//region "Private" functions

/**
 *
 * @param mLine
 * @private
 */
Simulcast.prototype._maybeInitializeLayers = function(mLine) {

    if (!mLine || mLine.type !== 'video') {
        return;
    }

    var sources = parseSsrcs(mLine);

    if (Object.keys(sources).length === 0) {

        // no sources, disable simulcast.
        if (this.layers.length !== 0) {
            this.layers = [];
        }

        return;
    }

    // find the base layer (we'll reuse its msid and cname).
    var baseLayerSSRC;
    // If we have stored layers the first one is the base layer, but pick it
    // only if it's SSRC still exists in local description. If not then
    // the layers will be reinitialized(baseline SSRCs will not match).
    if (this.layers.length > 0 && sources[this.layers[0].ssrc]) {
        baseLayerSSRC = this.layers[0].ssrc;
    } else {
        // FIXME Picking first key is ok only if there is 1 SSRC, otherwise
        // Object keys will be sorted in ascending order. Usually this is
        // the initialization case when there are no layers.
        baseLayerSSRC = Object.keys(sources)[0];
    }
    var baseLayer = sources[baseLayerSSRC];

    // todo(gp) handle screen sharing.

    // check if base CNAME or SSRC has changed and reinitialise layers.
    if (this.layers.length > 0
        && (baseLayer.cname !== this.layers[0].cname ||
            baseLayerSSRC !== this.layers[0].ssrc)) {
        this.layers = [];
    }

    // (re)initialise layers
    if (this.layers.length < 1) {

        // first push the base layer.
        this.layers.push({
            ssrc: baseLayerSSRC,
            msid: baseLayer.msid,
            cname: baseLayer.cname
        });

        var rtx = false; // RFC 4588
        if (Array.isArray(mLine.rtp)) {
            rtx = mLine.rtp.some(
                function (rtpmap) { return rtpmap.codec === 'rtx'; });
        }

        if (rtx) {
            this.layers[0].rtx = generateSSRC();
        }

        // now push additional layers.
        for (var i = 1; i < Math.max(1, this.options.numOfLayers); i++) {

            var layer = { ssrc: generateSSRC() };
            if (rtx) {
                layer.rtx = generateSSRC();
            }

            this.layers.push(layer);
        }
    }
};

/**
 *
 * @param mLine
 * @private
 */
Simulcast.prototype._restoreSimulcastView = function(mLine) {
    if (mLine && mLine.type === 'video' && this.layers.length !== 0) {

        var sources = {};

        var msid = this.layers[0].msid;
        var cname = this.layers[0].cname;
        var simulcastSsrcs = [];
        var ssrcGroups = [];

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

            sources[layer.ssrc] = { msid: msid, cname: cname };
            simulcastSsrcs.push(layer.ssrc);

            if (layer.rtx) {

                sources[layer.rtx] = {
                    msid: msid,
                    cname: cname
                }

                ssrcGroups.push({
                    semantics: 'FID',
                    ssrcs: [layer.ssrc, layer.rtx].join(' ')
                });
            }
        }

        ssrcGroups.push({
            semantics: 'SIM',
            ssrcs: simulcastSsrcs.join(' ')
        });

        mLine.ssrcGroups = ssrcGroups;
        mLine.ssrcs = writeSsrcs(sources, simulcastSsrcs);
    }
}

//endregion

//region "Public" functions

Simulcast.prototype.isSupported = function () {
    return window.chrome;

    // TODO this needs improvements. For example I doubt that Chrome in Android
    // has simulcast support. Also, only recent versions of Chromium have native
    // simulcast support.
}

/**
 *
 * @param desc
 * @returns {RTCSessionDescription}
 */
Simulcast.prototype.mungeRemoteDescription = function (desc) {

    if (!validateDescription(desc)) {
        return desc;
    }

    var session = transform.parse(desc.sdp);

    var self = this;
    processVideo(session, function (mLine) {

        // Handle simulcast reception.
        if (self.options.explodeRemoteSimulcast) {
            explodeRemoteSimulcast(mLine);
        } else {
            implodeRemoteSimulcast(mLine);
        }

        // If native simulcast is enabled, we must append the x-goog-conference
        // attribute to the SDP.
        if (self.layers.length < 1) {
            removeGoogConference(mLine);
        } else {
            assertGoogConference(mLine);
        }
    });

    return new RTCSessionDescription({
        type: desc.type,
        sdp: transform.write(session)
    });
};

/**
 *
 * @param desc
 * @returns {RTCSessionDescription}
 */
Simulcast.prototype.mungeLocalDescription = function (desc) {

    if (!validateDescription(desc) || !this.isSupported()) {
        return desc;
    }

    var session = transform.parse(desc.sdp);

    var self = this;
    processVideo(session, function (mLine) {
        if (mLine.direction == 'recvonly' || mLine.direction == 'inactive')
        {
            return;
        }
        // Initialize native simulcast layers, if not already done.
        self._maybeInitializeLayers(mLine);

        // Update the SDP with the simulcast layers.
        self._restoreSimulcastView(mLine);
    });

    return new RTCSessionDescription({
        type: desc.type,
        sdp: transform.write(session)
    });
};

//endregion

module.exports = Simulcast;
<|endoftext|>


<commit_before>var xtend = require('xtend');
var jwt = require('jwt-simple');

var defaultOptions = {
  algorithm: 'HS256',
  succeedWithoutToken: false
};

function authenticate(options, verify) {
  var _this = this;
  this.options = xtend(defaultOptions, options);
  if (!this.options.secret) {
    throw new TypeError('SocketioJwtAuth requires a secret');
  }

  this.verify = verify;
  if (!this.verify) {
    throw new TypeError('SocketioJwtAuth requires a verify callback');
  }

  this.success = function(next) {
    next();
  }

  this.fail = function(err, next) {
    var error;
    // Legacy support for users who handle the errors as strings
    // Should be removed on next release.
    if (typeof(err) === 'string') {
      error = new Error(err);
    } else {
      // Socket IO parser only parses object if there is a data field
      // This allows for better error handling by checking name/type
      error = err;
      error.data = { name: err.name, message: err.message };
    }
    next(error);
  }

  return function(socket, next) {
    var token = socket.handshake.headers['x-auth-token'] || socket.handshake.query.auth_token;
    var verified = function(err, user, message) {
      if (err) {
        return _this.fail(err, next);
      } else if (!user) {
        if (!_this.options.succeedWithoutToken) return _this.fail(message, next);
        socket.request.user = {logged_in: false};
        return _this.success(next)
      } else {
        user.logged_in = true;
        socket.request.user = user;
        return _this.success(next);
      }
    };
    try {
      var payload = {};
      if (!token) {
        if (!_this.options.succeedWithoutToken) {
          return _this.fail('No auth token', next);
        }
      } else {
        payload = jwt.decode(token, _this.options.secret, false, _this.options.algorithm);
      }
      _this.verify(payload, verified);
    } catch (ex) {
      _this.fail(ex, next);
    }
  }
}

exports.authenticate = authenticate;
<commit_msg>Added optional socket.handshake.auth.auth_token token path for socket.io v3<commit_after>var xtend = require('xtend');
var jwt = require('jwt-simple');

var defaultOptions = {
  algorithm: 'HS256',
  succeedWithoutToken: false
};

function authenticate(options, verify) {
  var _this = this;
  this.options = xtend(defaultOptions, options);
  if (!this.options.secret) {
    throw new TypeError('SocketioJwtAuth requires a secret');
  }

  this.verify = verify;
  if (!this.verify) {
    throw new TypeError('SocketioJwtAuth requires a verify callback');
  }

  this.success = function(next) {
    next();
  }

  this.fail = function(err, next) {
    var error;
    // Legacy support for users who handle the errors as strings
    // Should be removed on next release.
    if (typeof(err) === 'string') {
      error = new Error(err);
    } else {
      // Socket IO parser only parses object if there is a data field
      // This allows for better error handling by checking name/type
      error = err;
      error.data = { name: err.name, message: err.message };
    }
    next(error);
  }

  return function(socket, next) {
    var token = socket.handshake.headers['x-auth-token'] || socket.handshake.query.auth_token || socket.handshake.auth.auth_token;
    var verified = function(err, user, message) {
      if (err) {
        return _this.fail(err, next);
      } else if (!user) {
        if (!_this.options.succeedWithoutToken) return _this.fail(message, next);
        socket.request.user = {logged_in: false};
        return _this.success(next)
      } else {
        user.logged_in = true;
        socket.request.user = user;
        return _this.success(next);
      }
    };
    try {
      var payload = {};
      if (!token) {
        if (!_this.options.succeedWithoutToken) {
          return _this.fail('No auth token', next);
        }
      } else {
        payload = jwt.decode(token, _this.options.secret, false, _this.options.algorithm);
      }
      _this.verify(payload, verified);
    } catch (ex) {
      _this.fail(ex, next);
    }
  }
}

exports.authenticate = authenticate;
<|endoftext|>


<commit_before>/*jslint node : true, nomen: true, plusplus: true, vars: true, eqeq: true,*/
"use strict";

var director = require('director'),
    union = require('union'),
    url = require('url'),
    ecstatic = require('ecstatic'),
    local = require('./local-fixtures'),
    proxy = require('./proxy-fixtures');

module.exports = function (options) {

    var opts = options || { fixtures: { path: './fixtures'}, 'static': {}};

    var resources = ecstatic({
        root:  opts['static'].path  || './src',
        defaultExt: 'html'
    });

    var router = new director.http.Router();

    if (!Array.isArray(opts.fixtures)) {
        opts.fixtures = [opts.fixtures];
    }

    var handlers =  opts.fixtures.map(function (config) {
        return config.path ? local(config) : proxy(config);
    });

    handlers.push(resources);
    handlers.push(function dispatch(req, res) {
        var found = router.dispatch(req, res);
        if (!found) {
            res.emit('next');
        }
    });

    var server = union.createServer({
        before: handlers
    });

    var self = {
        routes : function (fn) {
            fn(router);
            return self;
        },
        listen : server.listen.bind(server)
    };

    return self;
};
<commit_msg>Permet la modification du paramètre baseDir<commit_after>/*jslint node : true, nomen: true, plusplus: true, vars: true, eqeq: true,*/
"use strict";

var director = require('director'),
    union = require('union'),
    url = require('url'),
    ecstatic = require('ecstatic'),
    local = require('./local-fixtures'),
    proxy = require('./proxy-fixtures');

module.exports = function (options) {

    var opts = options || { fixtures: { path: './fixtures'}, 'static': {}};

    var resources = ecstatic({
        root:  opts['static'].path  || './src',
        baseDir: opts['static'].baseDir || '/',
        defaultExt: 'html'
    });

    var router = new director.http.Router();

    if (!Array.isArray(opts.fixtures)) {
        opts.fixtures = [opts.fixtures];
    }

    var handlers =  opts.fixtures.map(function (config) {
        return config.path ? local(config) : proxy(config);
    });

    handlers.push(resources);
    handlers.push(function dispatch(req, res) {
        var found = router.dispatch(req, res);
        if (!found) {
            res.emit('next');
        }
    });

    var server = union.createServer({
        before: handlers
    });

    var self = {
        routes : function (fn) {
            fn(router);
            return self;
        },
        listen : server.listen.bind(server)
    };

    return self;
};
<|endoftext|>


<commit_before>'use strict';

var BinWrapper = require('bin-wrapper');
var path = require('path');
var pkg = require('../package.json');

/**
 * Variables
 */

var BIN_VERSION = '1.19';
var BASE_URL = 'https://raw.github.com/imagemin/advpng-bin/v' + pkg.version + '/vendor/';

/**
 * Initialize a new BinWrapper
 */

var bin = new BinWrapper()
	.src(BASE_URL + 'osx/advpng', 'darwin')
	.src(BASE_URL + 'linux/advpng', 'linux')
	.src(BASE_URL + 'win32/advpng', 'win32')
	.dest(path.join(__dirname, '../vendor'))
	.use('advpng');

/**
 * Module exports
 */

module.exports = bin;
module.exports.v = BIN_VERSION;
<commit_msg>Set correct Windows binary<commit_after>'use strict';

var BinWrapper = require('bin-wrapper');
var path = require('path');
var pkg = require('../package.json');

/**
 * Variables
 */

var BIN_VERSION = '1.19';
var BASE_URL = 'https://raw.github.com/imagemin/advpng-bin/v' + pkg.version + '/vendor/';

/**
 * Initialize a new BinWrapper
 */

var bin = new BinWrapper()
	.src(BASE_URL + 'osx/advpng', 'darwin')
	.src(BASE_URL + 'linux/advpng', 'linux')
	.src(BASE_URL + 'win32/advpng.exe', 'win32')
	.dest(path.join(__dirname, '../vendor'))
	.use(process.platform === 'win32' ? 'advpng.exe' : 'advpng');

/**
 * Module exports
 */

module.exports = bin;
module.exports.v = BIN_VERSION;
<|endoftext|>


<commit_before>var
	cheerio = require('cheerio'),
	request = require('./request'),

	baseHost = '.craigslist.org',
	defaultRequestOptions = {
		hostname : '',
		path : '',
	    secure : true
	},
	searchMaxAsk = '&maxAsk=',
	searchMinAsk = '&minAsk=';
	// searchPath = '/search/sss?sort=rel&query=';

    var initialize = (function (self) {
	'use strict';

	self = self || {};
	self.options = {};
	/*
		Accepts string of HTML and parses that string to find all pertinent listings.
	*/
	function getRssListings (options, html) {
		var
			$ = cheerio.load(html),
			listing = {},
			listings = [];

        var category = $('channel').find('title').text()

		$('item').each(function (i, element) {
			var children = $(element).children()

            var url = $(element).attr('rdf:about')
			var idx = url.lastIndexOf('/')
			var idx1 = url.indexOf('.', idx)
			var listing = { category : category, url: url, pid: url.substring(idx + 1, idx1) }

			for (var i=0; i<children.length; i++) {
				var t, child = children[i]
				var name = child.name

				if (name == 'title') {
					t = child.children[0].data.trim()
					if (t.indexOf('[CDATA[') != -1)
						t = t.substring(7, t.length - 2).trim().replace(/^\&\#x0024\;/g, '').replace(/&\#x0024\;/g, '$')

					var priceIdx = t.indexOf('$')
					if (priceIdx != -1)  {
						var sidx = t.indexOf(' ', priceIdx)
						listing.price = sidx == -1 ? t.substring(priceIdx) : t.substring(priceIdx, sidx)
					}
					var idx = t.lastIndexOf('(')
				    if (idx == -1)
				    	listing.title = t
				    else {
				    	var idx1 = t.indexOf(')', idx)
				    	listing.location = t.substring(idx + 1, idx1).trim()
				    	listing.title = t.substring(0, idx).trim()
				    }

				}
				else if (name == 'description') {
					t = child.children[0].data.trim()
					if (t.indexOf('[CDATA[') != -1)
						t = t.substring(7, t.length - 2)
					listing.description = t.replace(/&\#x0024\;/g, '$').trim()
				}
				else if (name == 'dc:date')
					listing.date = $(child).text().trim()
				else if (name == 'enc:enclosure') {
					listing.hasPic = true
					listing.img = $(child).attr('resource')
				}
			}

					// coordinates : {
					// 	lat : $(element).attr('data-latitude'),
					// 	lon : $(element).attr('data-longitude')
					// },

				// make sure lat / lon is valid
				// if (typeof listing.coordinates.lat === 'undefined' ||
				// 	typeof listing.coordinates.lon === 'undefined') {
				// 	delete listing.coordinates;
				// }
			listings.push(listing);
		});

		return listings;
	}

	/*
		Accepts string of HTML and parses that string to find all pertinent listings.
	*/
	function getListings (options, html) {
		var
			$ = cheerio.load(html),
			listing = {},
			listings = [];

		$('div.content').find('p.row').each(function (i, element) {
			listing = {
				category : $(element)
					.find('span.l2 a.gc')
					.text(),
				coordinates : {
					lat : $(element).attr('data-latitude'),
					lon : $(element).attr('data-longitude')
				},
				date : $(element)
					.find('span.date')
					.text(),
				hasPic : ($(element)
					.find('span.l2 span.p')
					.text()
					.trim()) !== '',
				location : $(element)
					.find('span.pnr small')
					.text()
					.replace(/[\(,\)]/g, '').trim(), // santize
				pid : $(element)
					.attr('data-pid'),
				price : $(element)
					.find('span.l2 span.price')
					.text()
					.replace(/^\&\#x0024\;/g, ''), // sanitize
				title : $(element)
					.find('span.pl a')
					.text().trim()
				// url :
				// 	(options.secure ? 'https://' : 'http://') +
				// 	options.hostname + $(element).find('span.pl a').attr('href')
			}
			var rurl = $(element).find('span.pl a').attr('href');
			if (rurl.indexOf('http') == -1)
			  rurl = (options.secure ? 'https://' : 'http://') + options.hostname + rurl
			listing.url = rurl;

			if (listing.hasPic) {
				var dId = $(element).find('a.i').attr('data-id')
				if (dId)
				  listing.pic = 'http://images.craigslist.org/' + dId.substring(2) + '_300x300.jpg'
			}
			var d = $(element).find('span.date')
			if (!d  ||  d.text().length == 0) {
				d = $(element).find('time').attr('datetime')
				listing.date = d;
			}

			// make sure lat / lon is valid
			if (typeof listing.coordinates.lat === 'undefined' ||
				typeof listing.coordinates.lon === 'undefined') {
				delete listing.coordinates;
			}

			listings.push(listing);
		});

		return listings;
	}

	function getCities(options, html) {
		var
			$ = cheerio.load(html),
			listing = {},
			listings = [];

		$('div.colmask').find('a').each(function (i, element) {
           var href = $(element).attr('href')
           var idx = href.indexOf('//'), idx1 = href.indexOf('.');

           listing = { city: href.substring(idx + 2, idx1),
           			   href: href,
                       cityName: $(element).text().replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()})
                     }

           listings.push(listing)
		})
		return listings
	}

	/*
		Accepts options, iterates through the known acceptable keys from defaultOptions
		and if found in input options, uses that. If not found in input options to method,
		falls back to the options specified when the module was initialized. If not found
		in initialization options, uses the default options setting. All keys provided in
		the input options variable are retained.
	*/
	function getRequestOptions (options, query) {
		var requestOptions = JSON.parse(JSON.stringify(defaultRequestOptions));

		// ensure default options are set, even if omitted from input options
		requestOptions.hostname = options.allCities? 'www' + baseHost : (options.city || self.options.city || '') + baseHost;

		// preserve any extraneous input option keys (may have addition instructions for underlying request object)
		Object.keys(options).forEach(function (key) {
			if (
				// key !== 'maxAsk'   &&
				// key !== 'minAsk'   &&
				// key !== 'category' &&
				// key !== 'hasPic'   &&
				// key !== 's'        &&
				typeof requestOptions[key] === 'undefined' &&
				typeof defaultRequestOptions[key] === 'undefined') {
				requestOptions[key] = options[key];
			}
		});

		// set path
		if (options.allCities)
		  requestOptions.path = '/about/sites'
		else
		  requestOptions.path = '/search/' + (options.category ? options.category : 'sss') + '?sort=rel' +
  		                                     (options.s ? '&s=' + options.s : '') +
		                                     (options.hasPic && (options.hasPic == '1' || options.hasPic == 'true') ? '&hasPic=1' : '') +
		                                     (options.rss ? '&format=rss' : '') +
		                                     (options.query ? '&query=' + encodeURIComponent(options.query) : '');

		// add min and max asking price
		if (typeof options.minAsk !== 'undefined') {
			requestOptions.path += searchMinAsk + options.minAsk;
		}

		if (typeof options.maxAsk !== 'undefined') {
			requestOptions.path += searchMaxAsk + options.maxAsk;
		}

		return requestOptions;
	}

	/*
		options = {
			city : '',
			maxAsk : '',
			minAsk : '',
		}
	*/
	self.search = function (options, query, callback) {
		if (typeof query === 'function' && typeof callback === 'undefined') {
			callback = query;
			query = options;
			options = {};
		}

		options = getRequestOptions(options, query);

		self.request.get(options, function (err, data) {
			if (err) {
				return callback(err);
			}

			return callback(null, options.allCities ? getCities(options, data) : (options.rss ? getRssListings(options, data) : getListings(options, data)))
		});
	};

	/*
		the module
	*/
	return function (options) {
		options = options || {};

		self.options = options;
		self.request = request.initialize();
		return self;
	};
}({}));

exports = module.exports = initialize;
exports.initialize = initialize;
<commit_msg>fixes for all cities search<commit_after>var
	cheerio = require('cheerio'),
	request = require('./request'),

	baseHost = '.craigslist.org',
	defaultRequestOptions = {
		hostname : '',
		path : '',
	    secure : true
	},
	searchMaxAsk = '&maxAsk=',
	searchMinAsk = '&minAsk=';
	// searchPath = '/search/sss?sort=rel&query=';

    var initialize = (function (self) {
	'use strict';

	self = self || {};
	self.options = {};
	/*
		Accepts string of HTML and parses that string to find all pertinent listings.
	*/
	function getRssListings (options, html) {
		var
			$ = cheerio.load(html),
			listing = {},
			listings = [];

        var category = $('channel').find('title').text()

		$('item').each(function (i, element) {
			var children = $(element).children()

            var url = $(element).attr('rdf:about')
			var idx = url.lastIndexOf('/')
			var idx1 = url.indexOf('.', idx)
			var listing = { category : category, url: url, pid: url.substring(idx + 1, idx1) }

			for (var i=0; i<children.length; i++) {
				var t, child = children[i]
				var name = child.name

				if (name == 'title') {
					t = child.children[0].data.trim()
					if (t.indexOf('[CDATA[') != -1)
						t = t.substring(7, t.length - 2).trim().replace(/^\&\#x0024\;/g, '').replace(/&\#x0024\;/g, '$')

					var priceIdx = t.indexOf('$')
					if (priceIdx != -1)  {
						var sidx = t.indexOf(' ', priceIdx)
						listing.price = sidx == -1 ? t.substring(priceIdx) : t.substring(priceIdx, sidx)
					}
					var idx = t.lastIndexOf('(')
				    if (idx == -1)
				    	listing.title = t
				    else {
				    	var idx1 = t.indexOf(')', idx)
				    	listing.location = t.substring(idx + 1, idx1).trim()
				    	listing.title = t.substring(0, idx).trim()
				    }

				}
				else if (name == 'description') {
					t = child.children[0].data.trim()
					if (t.indexOf('[CDATA[') != -1)
						t = t.substring(7, t.length - 2)
					listing.description = t.replace(/&\#x0024\;/g, '$').trim()
				}
				else if (name == 'dc:date')
					listing.date = $(child).text().trim()
				else if (name == 'enc:enclosure') {
					listing.hasPic = true
					listing.img = $(child).attr('resource')
				}
			}

					// coordinates : {
					// 	lat : $(element).attr('data-latitude'),
					// 	lon : $(element).attr('data-longitude')
					// },

				// make sure lat / lon is valid
				// if (typeof listing.coordinates.lat === 'undefined' ||
				// 	typeof listing.coordinates.lon === 'undefined') {
				// 	delete listing.coordinates;
				// }
			listings.push(listing);
		});

		return listings;
	}

	/*
		Accepts string of HTML and parses that string to find all pertinent listings.
	*/
	function getListings (options, html) {
		var
			$ = cheerio.load(html),
			listing = {},
			listings = [];

		$('div.content').find('p.row').each(function (i, element) {
			listing = {
				category : $(element)
					.find('span.l2 a.gc')
					.text(),
				coordinates : {
					lat : $(element).attr('data-latitude'),
					lon : $(element).attr('data-longitude')
				},
				date : $(element)
					.find('span.date')
					.text(),
				hasPic : ($(element)
					.find('span.l2 span.p')
					.text()
					.trim()) !== '',
				location : $(element)
					.find('span.pnr small')
					.text()
					.replace(/[\(,\)]/g, '').trim(), // santize
				pid : $(element)
					.attr('data-pid'),
				price : $(element)
					.find('span.l2 span.price')
					.text()
					.replace(/^\&\#x0024\;/g, ''), // sanitize
				title : $(element)
					.find('span.pl a')
					.text().trim()
				// url :
				// 	(options.secure ? 'https://' : 'http://') +
				// 	options.hostname + $(element).find('span.pl a').attr('href')
			}
			var rurl = $(element).find('span.pl a').attr('href');
			if (rurl) {
			  if (rurl.indexOf('http') == -1)
			    rurl = (options.secure ? 'https://' : 'http://') + options.hostname + rurl
			  listing.url = rurl;
		    }

			if (listing.hasPic) {
				var dId = $(element).find('a.i').attr('data-id')
				if (dId)
				  listing.pic = 'http://images.craigslist.org/' + dId.substring(2) + '_300x300.jpg'
			}
			var d = $(element).find('span.date')
			if (!d  ||  d.text().length == 0) {
				d = $(element).find('time').attr('datetime')
				listing.date = d;
			}

			// make sure lat / lon is valid
			if (typeof listing.coordinates.lat === 'undefined' ||
				typeof listing.coordinates.lon === 'undefined') {
				delete listing.coordinates;
			}

			listings.push(listing);
		});

		return listings;
	}

	function getCities(options, html) {
		var
			$ = cheerio.load(html),
			listing = {},
			listings = [];

		$('div.colmask').find('a').each(function (i, element) {
           var href = $(element).attr('href')
           var idx = href.indexOf('//'), idx1 = href.indexOf('.');

           listing = { city: href.substring(idx + 2, idx1),
           			   href: href,
                       cityName: $(element).text().replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()})
                     }

           listings.push(listing)
		})
		return listings
	}

	/*
		Accepts options, iterates through the known acceptable keys from defaultOptions
		and if found in input options, uses that. If not found in input options to method,
		falls back to the options specified when the module was initialized. If not found
		in initialization options, uses the default options setting. All keys provided in
		the input options variable are retained.
	*/
	function getRequestOptions (options, query) {
		var requestOptions = JSON.parse(JSON.stringify(defaultRequestOptions));

		// ensure default options are set, even if omitted from input options
		requestOptions.hostname = options.allCities? 'www' + baseHost : (options.city || self.options.city || '') + baseHost;

		// preserve any extraneous input option keys (may have addition instructions for underlying request object)
		Object.keys(options).forEach(function (key) {
			if (
				// key !== 'maxAsk'   &&
				// key !== 'minAsk'   &&
				// key !== 'category' &&
				// key !== 'hasPic'   &&
				// key !== 's'        &&
				typeof requestOptions[key] === 'undefined' &&
				typeof defaultRequestOptions[key] === 'undefined') {
				requestOptions[key] = options[key];
			}
		});

		// set path
		if (options.allCities)
		  requestOptions.path = '/about/sites'
		else
		  requestOptions.path = '/search/' + (options.category ? options.category : 'sss') + '?sort=rel' +
  		                                     (options.s ? '&s=' + options.s : '') +
		                                     (options.hasPic && (options.hasPic == '1' || options.hasPic == 'true') ? '&hasPic=1' : '') +
		                                     (options.rss ? '&format=rss' : '') +
		                                     (options.query ? '&query=' + encodeURIComponent(options.query) : '');

		// add min and max asking price
		if (typeof options.minAsk !== 'undefined') {
			requestOptions.path += searchMinAsk + options.minAsk;
		}

		if (typeof options.maxAsk !== 'undefined') {
			requestOptions.path += searchMaxAsk + options.maxAsk;
		}

		return requestOptions;
	}

	/*
		options = {
			city : '',
			maxAsk : '',
			minAsk : '',
		}
	*/
	self.search = function (options, query, callback) {
		if (typeof query === 'function' && typeof callback === 'undefined') {
			callback = query;
			query = options;
			options = {};
		}

		options = getRequestOptions(options, query);

		self.request.get(options, function (err, data) {
			if (err) {
				return callback(err);
			}

			return callback(null, options.allCities ? getCities(options, data) : (options.rss ? getRssListings(options, data) : getListings(options, data)))
		});
	};

	/*
		the module
	*/
	return function (options) {
		options = options || {};

		self.options = options;
		self.request = request.initialize();
		return self;
	};
}({}));

exports = module.exports = initialize;
exports.initialize = initialize;
<|endoftext|>


<commit_before>var $ = require("jQuery");
const World = require('./world.js');

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

var world = new World();
makeBricks();

$(".startBtn").on('click', function() {
  setInterval(draw, 10);
});

// $(".resetBtn").on('click', function() {
//
// });

$(".level1Btn").on('click', function() {
  world.bricks.rowCount = 1;
  makeBricks();
});

$(".level2Btn").on('click', function() {
  world.bricks.rowCount = 2;
  makeBricks();
});

$(".level3Btn").on('click', function() {
  world.bricks.rowCount = 3;
  makeBricks();
});

var pumpkin = new Image();
pumpkin.src = ('../images/pumpkin-icon.svg');

function makeCircle() {
  ctx.fillStyle = "purple";
  ctx.drawImage(pumpkin, world.circle.x, world.circle.y, world.circle.width, world.circle.height);
}

function makePaddle() {
  ctx.fillStyle = "white";
  ctx.fillRect(world.paddle.x, world.paddle.y, world.paddle.width, world.paddle.height);
}

function makeBricks() {
  world.bricks.makeBricksColumnAndRows();
  for(var column = 0; column < world.bricks.columnCount; column++) {
    for(var row = 0; row < world.bricks.rowCount; row++) {
      world.bricks.brickPosition(column, row);
    }
  }
}

function drawBricks() {
  for(var column = 0; column < world.bricks.columnCount; column++) {
    for(var row = 0; row < world.bricks.rowCount; row++) {
      if(world.bricks.bricks[column][row].status === 1){
      ctx.fillStyle = "orange";
      ctx.fillRect(world.bricks.bricks[column][row].x, world.bricks.bricks[column][row].y, world.bricks.width, world.bricks.height);
      }
    }
  }
}

function drawScore(){
  ctx.font = "16px Arial";
  ctx.fillStyle = 'orange';
  ctx.fillText("Score: "+world.score, 8, 395);
}

function draw() {
  ctx.clearRect(world.x, world.y, world.width, world.height);
  makeCircle();
  makePaddle();
  drawBricks();
  drawScore();
  world.collision();
  world.collisionBricks();
  world.circle.moveRight();
  world.circle.moveUp();
}

// setInterval(draw, 10);

document.addEventListener('keydown', function() {
  if(event.keyCode === 39){
    world.paddle.rightArrowWasPressed();
  }
  if (event.keyCode === 37) {
    world.paddle.leftArrowWasPressed();
  }
});
<commit_msg>Space button to start<commit_after>var $ = require("jQuery");
const World = require('./world.js');

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

var world = new World();
makeBricks();

$(".startBtn").on('click', function() {
  draw();
  // setInterval(draw, 10);

});

// $(".resetBtn").on('click', function() {
//
// });

$(".level1Btn").on('click', function() {
  world.bricks.rowCount = 1;
  makeBricks();
});

$(".level2Btn").on('click', function() {
  world.bricks.rowCount = 2;
  makeBricks();
});

$(".level3Btn").on('click', function() {
  world.bricks.rowCount = 3;
  makeBricks();
});

var pumpkin = new Image();
pumpkin.src = ('../images/pumpkin-icon.svg');

function makeCircle() {
  ctx.fillStyle = "purple";
  ctx.drawImage(pumpkin, world.circle.x, world.circle.y, world.circle.width, world.circle.height);
}

function makePaddle() {
  ctx.fillStyle = "white";
  ctx.fillRect(world.paddle.x, world.paddle.y, world.paddle.width, world.paddle.height);
}

function makeBricks() {
  world.bricks.makeBricksColumnAndRows();
  for(var column = 0; column < world.bricks.columnCount; column++) {
    for(var row = 0; row < world.bricks.rowCount; row++) {
      world.bricks.brickPosition(column, row);
    }
  }
}

function drawBricks() {
  for(var column = 0; column < world.bricks.columnCount; column++) {
    for(var row = 0; row < world.bricks.rowCount; row++) {
      if(world.bricks.bricks[column][row].status === 1){
      ctx.fillStyle = "orange";
      ctx.fillRect(world.bricks.bricks[column][row].x, world.bricks.bricks[column][row].y, world.bricks.width, world.bricks.height);
      }
    }
  }
}

function drawScore(){
  ctx.font = "16px Arial";
  ctx.fillStyle = 'orange';
  ctx.fillText("Score: "+world.score, 8, 395);
}

function draw() {
  ctx.clearRect(world.x, world.y, world.width, world.height);
  makeCircle();
  makePaddle();
  drawBricks();
  drawScore();
  world.collision();
  world.collisionBricks();
  world.circle.moveRight();
  world.circle.moveUp();
}

// setInterval(draw, 10);

document.addEventListener('keydown', function() {
  if(event.keyCode === 39){
    world.paddle.rightArrowWasPressed();
  }
  if (event.keyCode === 37) {
    world.paddle.leftArrowWasPressed();
  }
});

document.addEventListener('keydown', function(){
  if(event.keyCode === 32){
    setInterval(draw, 10);
  }
});
<|endoftext|>


<commit_before>/** @babel */
import {TextEditor, CompositeDisposable} from 'atom';
import PluginStateStore from './plugin-state-store';

import PanelUI from './views/panel/panel-ui.jsx';

module.exports = {
	config: {
		specSearchPaths: {type: 'array', default: ['spec', 'fast_spec']},
		specDefaultPath: {type: 'string', default: 'spec'},
		rspecAnalyzerScript: {type: 'string', default: undefined},
		rubyPathCommand: {type: 'string', default: 'ruby'},
		rspecPathCommand: {type: 'string', default: 'rspec'},
		changeToSpecFileOnClick: {type: 'boolean', default: true},
		showRSpecWarningMessages: {type: 'boolean', default: true},
		sizeOfRSpecMessageStrings: {type: 'integer', default: 500}
	},
	rspecTreeRunnerView: null,
	initialized: false,
	subscriptions: null,
	activate() {
		this.panel = new PanelUI();
		window.addEventListener('resize', this.panel.resizeSubContainer, false);

		this.currentState = new PluginStateStore();
		this.currentState.on('stateUpdated', state => this.panel.updateState(state));

		this.subscriptions = new CompositeDisposable();
		this.subscriptions.add(
			atom.commands.add('atom-text-editor', 'buffer:saved', () => {
				const editor = atom.workspace.getActiveTextEditor();
				this.mainView.handleEditorEvents(editor);
			}),
			atom.commands.add('atom-workspace', 'rspec-tree-runner:toggle', () => atom.workspace.toggle(this.panel)),
			atom.commands.add('atom-workspace', 'rspec-tree-runner:toggle-spec-file', () => this.mainView.toggleSpecFile(this.currentState.currentCorrespondingFilePath)),
			atom.commands.add('atom-workspace', 'rspec-tree-runner:run-tests', () => this.currentState.runTests()),
			atom.commands.add('atom-workspace', 'rspec-tree-runner:run-single-test', () => this.currentState.runSingleTest())
		);
		atom.workspace.observeActivePaneItem(item => this.wireEventsForEditor(item));
	},
	wireEventsForEditor(item) {
		if (item instanceof TextEditor) {
			const currentBuffer = item.getBuffer();
			currentBuffer.onDidSave(() => this.currentState.set(item));
			this.currentState.set(item);
		} else {
			this.currentState.setNullState();
		}
	},
	deactivate() {
		this.modalPanel.destroy();
		this.subscriptions.dispose();
		this.rspecTreeRunnerView.destroy();
		window.removeEventListener('resize', this.resizeSubContainer, false);
	},
	serialize() {
	}
};
<commit_msg>onDidChangeActivePaneItem to track only elements that matter<commit_after>/** @babel */
import {TextEditor, CompositeDisposable} from 'atom';
import PluginStateStore from './plugin-state-store';

import PanelUI from './views/panel/panel-ui.jsx';

module.exports = {
	config: {
		specSearchPaths: {type: 'array', default: ['spec', 'fast_spec']},
		specDefaultPath: {type: 'string', default: 'spec'},
		rspecAnalyzerScript: {type: 'string', default: undefined},
		rubyPathCommand: {type: 'string', default: 'ruby'},
		rspecPathCommand: {type: 'string', default: 'rspec'},
		changeToSpecFileOnClick: {type: 'boolean', default: true},
		showRSpecWarningMessages: {type: 'boolean', default: true},
		sizeOfRSpecMessageStrings: {type: 'integer', default: 500}
	},
	rspecTreeRunnerView: null,
	initialized: false,
	subscriptions: null,
	activate() {
		this.panel = new PanelUI();
		window.addEventListener('resize', this.panel.resizeSubContainer, false);

		this.currentState = new PluginStateStore();
		this.currentState.on('stateUpdated', state => this.panel.updateState(state));

		this.subscriptions = new CompositeDisposable();
		this.subscriptions.add(
			atom.commands.add('atom-text-editor', 'buffer:saved', () => {
				const editor = atom.workspace.getActiveTextEditor();
				this.mainView.handleEditorEvents(editor);
			}),
			atom.commands.add('atom-workspace', 'rspec-tree-runner:toggle', () => atom.workspace.toggle(this.panel)),
			atom.commands.add('atom-workspace', 'rspec-tree-runner:toggle-spec-file', () => this.mainView.toggleSpecFile(this.currentState.currentCorrespondingFilePath)),
			atom.commands.add('atom-workspace', 'rspec-tree-runner:run-tests', () => this.currentState.runTests()),
			atom.commands.add('atom-workspace', 'rspec-tree-runner:run-single-test', () => this.currentState.runSingleTest())
		);
		atom.workspace.onDidChangeActivePaneItem(item => this.wireEventsForEditor(item));
	},
	wireEventsForEditor(item) {
		if (item instanceof TextEditor) {
			const currentBuffer = item.getBuffer();
			currentBuffer.onDidSave(() => this.currentState.set(item));
			this.currentState.set(item);
		} else {
			this.currentState.setNullState();
		}
	},
	deactivate() {
		this.modalPanel.destroy();
		this.subscriptions.dispose();
		this.rspecTreeRunnerView.destroy();
		window.removeEventListener('resize', this.resizeSubContainer, false);
	},
	serialize() {
	}
};
<|endoftext|>


<commit_before>'use strict';

const {EventEmitter}     = require ('events');
const {Client}           = require ('_debugger');
const {spawn}            = require ('child_process');
const watt               = require ('watt');
const escapeStringRegexp = require ('escape-string-regexp');
const path               = require ('path');
const fs                 = require ('fs');

const linesExtractor = require ('./lines-extractor.js');

class Wannabe extends EventEmitter {
  /**
   * Construct the Wannabe object.
   *
   * @param {string} script - Test script.
   * @param {string|RegExp} regtest - Tests cases to grep.
   */
  constructor (script, regtest) {
    super ();

    this._frames  = {};
    this._script  = script;
    this._client  = new Client ();
    this._regtest = regtest;
    this._mocha   = {
      kill: () => {}
    };

    watt.wrapAll (this);
  }

  /**
   * Spawn mocha with the node debugger.
   */
  _spawn (next) {
    const regex = this._regtest instanceof RegExp ?
                  this._regtest.toString ().replace (/^\/(.*)\/.?$/, '$1') :
                  escapeStringRegexp (this._regtest);

    this._mocha = spawn ('node', [
      '--debug',
      '--debug-brk',
      path.join (__dirname, './mocha.js'),
      this._script,
      regex
    ], {
      detached: false,
      stdio: ['ignore', 'ignore', 'pipe']
    });

    this._mocha.unref ();

    this._mocha.stderr.once ('data', () => next ());

    this._mocha.on ('close', () => {});
    this._mocha.on ('error', (err) => {
      console.error (err);
    });
  }

  /**
   * Inspect the frame on the current breakpoint.
   *
   * It populates this._frames with the local variables.
   * The key of this._frames is the line number.
   *
   * NOTE: setBreakpoint is slow!
   */
  * _inspect (next) {
    const {frames} = yield this._client.reqBacktrace (next);

    this._frames[`${frames[0].line}`] = [];
    frames[0].locals.forEach ((local) => {
      this._frames[`${frames[0].line}`].push ({
        name:  local.name,
        value: local.value.value
      });
    });
  }

  /**
   * Initialize the client connection.
   *
   * It waits on the first breakpoint because the child is executed with
   * the --debug-brk argument.
   */
  * init (next) {
    yield this._spawn (next);

    let first = true;
    this._client.on ('break', () => {
      if (first) {
        this.emit ('ready');
        first = false;
        return;
      }

      this._inspect (() => this._client.reqContinue (() => {}));
    });

    this._client.once ('connect', next.parallel ());
    this._client.connect (5858);
    yield next.sync ();

    yield this.once ('ready', next); // wait for the first breakpoint (--debug-brk)
  }

  /**
   * Set one or more breakpoints according to the current script.
   *
   * @param {number[]} lines - Lines where put the breakpoints.
   */
  * setBreakpoints (lines, next) {
    for (const line of lines) {
      this._client.setBreakpoint ({
        type:   'script',
        target: this._script,
        line:   line
      }, next.parallel ());
    }

    yield next.sync ();
  }

  /**
   * Run Wannabe.
   *
   * When a new breakpoint is reached, the continue request is automatically
   * sent until that the child is terminated.
   *
   * @returns all frames.
   */
  * run (next) {
    const promise = new Promise ((resolve) => {
      this._client.once ('error', (err) => {
        resolve (this._frames);
      });

      this._client.once ('close', () => {
        resolve (this._frames);
      });
    });

    yield this._client.reqContinue (next);
    return yield promise;
  }

  dispose () {
    this._mocha.kill ('SIGINT');
  }
}

module.exports = watt (function * (script, data, func, pattern, next) {
  let file = null;
  if (data) {
    /* We must create a new script in the same location that {script}.
     * The reason is that we can not pass a buffer to mocha; only files
     * can be passed to the runner.
     */
    file = path.join (script, `../.wannabe-${path.basename (script)}`);
    yield fs.writeFile (file, data, next);
  } else {
    file = script;
  }

  let _exThrown = false;
  let frames = null;
  const wannabe = new Wannabe (file, pattern);

  try {
    yield wannabe.init ();

    const lines = data ?
      linesExtractor.byData (data, func, pattern) :
      linesExtractor.byFile (file, func, pattern);

    yield wannabe.setBreakpoints (lines);
    frames = yield wannabe.run ();
  } catch (ex) {
    _exThrown = true;
    throw ex;
  } finally {
    wannabe.dispose ();
    if (data) {
      yield fs.unlink (file, next);
    }
    // FIXME: see watt bug: https://github.com/mappum/watt/issues/16
    if (!_exThrown) {
      return frames;
    }
  }
});
<commit_msg>Add 'use babel' declaration<commit_after>'use babel';
'use strict';

const {EventEmitter}     = require ('events');
const {Client}           = require ('_debugger');
const {spawn}            = require ('child_process');
const watt               = require ('watt');
const escapeStringRegexp = require ('escape-string-regexp');
const path               = require ('path');
const fs                 = require ('fs');

const linesExtractor = require ('./lines-extractor.js');

class Wannabe extends EventEmitter {
  /**
   * Construct the Wannabe object.
   *
   * @param {string} script - Test script.
   * @param {string|RegExp} regtest - Tests cases to grep.
   */
  constructor (script, regtest) {
    super ();

    this._frames  = {};
    this._script  = script;
    this._client  = new Client ();
    this._regtest = regtest;
    this._mocha   = {
      kill: () => {}
    };

    watt.wrapAll (this);
  }

  /**
   * Spawn mocha with the node debugger.
   */
  _spawn (next) {
    const regex = this._regtest instanceof RegExp ?
                  this._regtest.toString ().replace (/^\/(.*)\/.?$/, '$1') :
                  escapeStringRegexp (this._regtest);

    this._mocha = spawn ('node', [
      '--debug',
      '--debug-brk',
      path.join (__dirname, './mocha.js'),
      this._script,
      regex
    ], {
      detached: false,
      stdio: ['ignore', 'ignore', 'pipe']
    });

    this._mocha.unref ();

    this._mocha.stderr.once ('data', () => next ());

    this._mocha.on ('close', () => {});
    this._mocha.on ('error', (err) => {
      console.error (err);
    });
  }

  /**
   * Inspect the frame on the current breakpoint.
   *
   * It populates this._frames with the local variables.
   * The key of this._frames is the line number.
   *
   * NOTE: setBreakpoint is slow!
   */
  * _inspect (next) {
    const {frames} = yield this._client.reqBacktrace (next);

    this._frames[`${frames[0].line}`] = [];
    frames[0].locals.forEach ((local) => {
      this._frames[`${frames[0].line}`].push ({
        name:  local.name,
        value: local.value.value
      });
    });
  }

  /**
   * Initialize the client connection.
   *
   * It waits on the first breakpoint because the child is executed with
   * the --debug-brk argument.
   */
  * init (next) {
    yield this._spawn (next);

    let first = true;
    this._client.on ('break', () => {
      if (first) {
        this.emit ('ready');
        first = false;
        return;
      }

      this._inspect (() => this._client.reqContinue (() => {}));
    });

    this._client.once ('connect', next.parallel ());
    this._client.connect (5858);
    yield next.sync ();

    yield this.once ('ready', next); // wait for the first breakpoint (--debug-brk)
  }

  /**
   * Set one or more breakpoints according to the current script.
   *
   * @param {number[]} lines - Lines where put the breakpoints.
   */
  * setBreakpoints (lines, next) {
    for (const line of lines) {
      this._client.setBreakpoint ({
        type:   'script',
        target: this._script,
        line:   line
      }, next.parallel ());
    }

    yield next.sync ();
  }

  /**
   * Run Wannabe.
   *
   * When a new breakpoint is reached, the continue request is automatically
   * sent until that the child is terminated.
   *
   * @returns all frames.
   */
  * run (next) {
    const promise = new Promise ((resolve) => {
      this._client.once ('error', (err) => {
        resolve (this._frames);
      });

      this._client.once ('close', () => {
        resolve (this._frames);
      });
    });

    yield this._client.reqContinue (next);
    return yield promise;
  }

  dispose () {
    this._mocha.kill ('SIGINT');
  }
}

module.exports = watt (function * (script, data, func, pattern, next) {
  let file = null;
  if (data) {
    /* We must create a new script in the same location that {script}.
     * The reason is that we can not pass a buffer to mocha; only files
     * can be passed to the runner.
     */
    file = path.join (script, `../.wannabe-${path.basename (script)}`);
    yield fs.writeFile (file, data, next);
  } else {
    file = script;
  }

  let _exThrown = false;
  let frames = null;
  const wannabe = new Wannabe (file, pattern);

  try {
    yield wannabe.init ();

    const lines = data ?
      linesExtractor.byData (data, func, pattern) :
      linesExtractor.byFile (file, func, pattern);

    yield wannabe.setBreakpoints (lines);
    frames = yield wannabe.run ();
  } catch (ex) {
    _exThrown = true;
    throw ex;
  } finally {
    wannabe.dispose ();
    if (data) {
      yield fs.unlink (file, next);
    }
    // FIXME: see watt bug: https://github.com/mappum/watt/issues/16
    if (!_exThrown) {
      return frames;
    }
  }
});
<|endoftext|>


<commit_before>/**
 * phantomas CommonJS module
 */
'use strict';

var debug = require('debug')('phantomas'),
	emitter = require('events').EventEmitter,
	spawn = require('child_process').spawn,
	path = require('path'),
	phantomjs = require('phantomjs'),
	Q = require('q'),
	VERSION = require('./../package').version;

function phantomas(url, options, callback) {
	var engineArgs = [],
		deferred = Q.defer(),
		events = new emitter(),
		proc,
		engine,
		spawnOptions = {
			env: process.env
		},
		data = '';

	// options can be omitted
	if (typeof options === 'function') {
		callback = options;
		options = {};
	}

	debug('nodejs %s', process.version);
	debug('PhantomJS v%s installed in %s', phantomjs.version, phantomjs.path);
	debug('phantomas v%s installed in %s', VERSION, phantomas.path);
	debug('URL: <%s>', url);
	debug('Options: %s', JSON.stringify(options));

	// options handling
	options = options || {};
	options.url = options.url || url || false;

	options.debug = options.debug || 'false'; //--debug can be either 'true' or 'false'

	// TODO: select the engine
	engine = phantomjs;

	// build args
	Object.keys(options).forEach(function(key) {
		var val = options[key],
			nativeOptions = [
				'cookies-file',
				'debug',
				'ignore-ssl-errors',
				'proxy',
				'proxy-auth',
				'proxy-type'
			];

		if (val === false) {
			return;
		}

		// handle native PhantomJS options (#163)
		// @see http://phantomjs.org/api/command-line.html
		if (nativeOptions.indexOf(key) > -1) {
			engineArgs.push('--' + key + '=' + val);

			delete options[key];
		}
	});

	// add a script to run
	var scriptFile = phantomas.path + '/scripts/phantomas.js';
	engineArgs.push(scriptFile);

	// pass JSON encoded options
	engineArgs.push(JSON.stringify(options));

	debug('Running %s (using %s)', scriptFile, engine.path);
	debug('Passing phantomas options: %j', options);

	// spawn the process
	proc = spawn(engine.path, engineArgs, spawnOptions);

	debug('Spawned with pid #%d', proc.pid);

	proc.on('error', function(err) {
		debug('Error: %s', err);
	});

	// put together JSON from stdout
	proc.stdout.on('data', function(buf) {
		data += buf;
	});

	// set up IPC
	var ipc = new (require('./ipc'))(proc.stderr);
	ipc.setEventEmitter(events);

	// pipe log messages to error stream
	var errStream = new (require('stream').Readable)();
	errStream.on('error', function() {});

	ipc.on('log', function(msg) {
		errStream.push(msg + "\n");
	});

	// handle loading progress
	var progressDebug = require('debug')('phantomas:progress');

	ipc.on('progress', function(progress, inc) {
		progressDebug('%d% (+%d%)', progress, inc);
		deferred.notify(progress / 100);
	});

	// handle page loading milestones (#240)
	var milestoneDebug = require('debug')('phantomas:milestone');

	ipc.on('metric', function(metric, value) {
		switch(metric) {
			case 'timeToFirstByte':
			case 'timeToLastByte':
			case 'onDOMReadyTime':
			case 'windowOnLoadTime':
				milestoneDebug('%s: %d ms', metric, value);
				events.emit('milestone', metric, value);
				break;
		}
	});

	// failures handling
	ipc.on('exit', function(code, msg) {
		if ((code > 0) && (typeof msg === 'string')) {
			errStream.push('phantomas: (' + code + ') ' + msg + "\n");
		}
	});

	// process results
	proc.on('close', function(code) {
		var debug = require('debug')('phantomas:results'),
			json = false,
			results = false;

		debug('Process returned code #%d', code);
		debug('JSON: %s', data);

		// (try to) parse to JSON
		try {
			json = JSON.parse(data);
		}
		catch(ex) {
			debug('Error when parsing JSON (%s)', ex);
			errStream.push(data);
		}

		if (json !== false) {
			events.emit('data', json);

			// wrap JSON data into results object
			results = new (require('../core/results'))(json);
			events.emit('results', results);
		}

		if (code > 0) {
			if (events.listeners('error').length > 0) {
				events.emit('error', code);
			}
		}

		if (typeof callback === 'function') {
			callback(code === 0 ? null : code, json, results);
		}

		// either reject or resolve the promise
		if (code > 250) {
			deferred.reject(code);
		}
		else {
			deferred.resolve({
				code: code,
				json: json,
				results: results
			});
		}
	});

	var promise = deferred.promise;

	promise.pid = proc.pid;
	promise.stdout = proc.stdout;
	promise.stderr = errStream;
	promise.on = events.on.bind(events);

	return promise;
}

phantomas.metadata = require(__dirname + '/metadata/metadata.json');
phantomas.path = path.normalize(__dirname + '/..');
phantomas.version = VERSION;

module.exports = phantomas;
<commit_msg>Show SlimerJS version and path<commit_after>/**
 * phantomas CommonJS module
 */
'use strict';

var debug = require('debug')('phantomas'),
	emitter = require('events').EventEmitter,
	spawn = require('child_process').spawn,
	path = require('path'),
	phantomjs = require('phantomjs'),
	slimerjs = require('slimerjs'),
	Q = require('q'),
	VERSION = require('./../package').version;

function phantomas(url, options, callback) {
	var engineArgs = [],
		deferred = Q.defer(),
		events = new emitter(),
		proc,
		engine,
		spawnOptions = {
			env: process.env
		},
		data = '';

	// options can be omitted
	if (typeof options === 'function') {
		callback = options;
		options = {};
	}

	debug('nodejs %s', process.version);
	debug('PhantomJS v%s installed in %s', phantomjs.version, phantomjs.path);
	debug('SlimerJS v%s installed in %s', slimerjs.version, slimerjs.path);
	debug('phantomas v%s installed in %s', VERSION, phantomas.path);
	debug('URL: <%s>', url);
	debug('Options: %s', JSON.stringify(options));

	// options handling
	options = options || {};
	options.url = options.url || url || false;

	options.debug = options.debug || 'false'; //--debug can be either 'true' or 'false'

	// TODO: select the engine
	engine = phantomjs;

	// build args
	Object.keys(options).forEach(function(key) {
		var val = options[key],
			nativeOptions = [
				'cookies-file',
				'debug',
				'ignore-ssl-errors',
				'proxy',
				'proxy-auth',
				'proxy-type'
			];

		if (val === false) {
			return;
		}

		// handle native PhantomJS options (#163)
		// @see http://phantomjs.org/api/command-line.html
		if (nativeOptions.indexOf(key) > -1) {
			engineArgs.push('--' + key + '=' + val);

			delete options[key];
		}
	});

	// add a script to run
	var scriptFile = phantomas.path + '/scripts/phantomas.js';
	engineArgs.push(scriptFile);

	// pass JSON encoded options
	engineArgs.push(JSON.stringify(options));

	debug('Running %s (using %s)', scriptFile, engine.path);
	debug('Passing phantomas options: %j', options);

	// spawn the process
	proc = spawn(engine.path, engineArgs, spawnOptions);

	debug('Spawned with pid #%d', proc.pid);

	proc.on('error', function(err) {
		debug('Error: %s', err);
	});

	// put together JSON from stdout
	proc.stdout.on('data', function(buf) {
		data += buf;
	});

	// set up IPC
	var ipc = new (require('./ipc'))(proc.stderr);
	ipc.setEventEmitter(events);

	// pipe log messages to error stream
	var errStream = new (require('stream').Readable)();
	errStream.on('error', function() {});

	ipc.on('log', function(msg) {
		errStream.push(msg + "\n");
	});

	// handle loading progress
	var progressDebug = require('debug')('phantomas:progress');

	ipc.on('progress', function(progress, inc) {
		progressDebug('%d% (+%d%)', progress, inc);
		deferred.notify(progress / 100);
	});

	// handle page loading milestones (#240)
	var milestoneDebug = require('debug')('phantomas:milestone');

	ipc.on('metric', function(metric, value) {
		switch(metric) {
			case 'timeToFirstByte':
			case 'timeToLastByte':
			case 'onDOMReadyTime':
			case 'windowOnLoadTime':
				milestoneDebug('%s: %d ms', metric, value);
				events.emit('milestone', metric, value);
				break;
		}
	});

	// failures handling
	ipc.on('exit', function(code, msg) {
		if ((code > 0) && (typeof msg === 'string')) {
			errStream.push('phantomas: (' + code + ') ' + msg + "\n");
		}
	});

	// process results
	proc.on('close', function(code) {
		var debug = require('debug')('phantomas:results'),
			json = false,
			results = false;

		debug('Process returned code #%d', code);
		debug('JSON: %s', data);

		// (try to) parse to JSON
		try {
			json = JSON.parse(data);
		}
		catch(ex) {
			debug('Error when parsing JSON (%s)', ex);
			errStream.push(data);
		}

		if (json !== false) {
			events.emit('data', json);

			// wrap JSON data into results object
			results = new (require('../core/results'))(json);
			events.emit('results', results);
		}

		if (code > 0) {
			if (events.listeners('error').length > 0) {
				events.emit('error', code);
			}
		}

		if (typeof callback === 'function') {
			callback(code === 0 ? null : code, json, results);
		}

		// either reject or resolve the promise
		if (code > 250) {
			deferred.reject(code);
		}
		else {
			deferred.resolve({
				code: code,
				json: json,
				results: results
			});
		}
	});

	var promise = deferred.promise;

	promise.pid = proc.pid;
	promise.stdout = proc.stdout;
	promise.stderr = errStream;
	promise.on = events.on.bind(events);

	return promise;
}

phantomas.metadata = require(__dirname + '/metadata/metadata.json');
phantomas.path = path.normalize(__dirname + '/..');
phantomas.version = VERSION;

module.exports = phantomas;
<|endoftext|>


<commit_before>"use strict";

var _ = require('lodash');
var async = require('async');

var asyncRun = function (func, callback, obj) {
	var async = 0;
	var done = 0;
	var result;
	var err;

	var _callback = function () {
		done++ || callback.apply(this, arguments);
		callback = null;
	};

	var asyncCbk = function () {
		return async++ ? null : _callback;
	};

	try {
		if (obj == null) {
			result = func(asyncCbk);
		} else {
			obj = Object.create(obj);
			obj.async = asyncCbk;
			result = func.call(obj);
		}
	} catch (e) {
		err = e;
	}

	!async && _callback.call(this, err, result);
};

var task = function (grunt, files, options, gruntDone) {
	options = _.extend({
		log: true,

		read: function (fSrc, fDest, fOptions) {
			return grunt.file.read(fSrc);
		},

		process: function (fSrc, fDest, content, fOptions) {
			return content;
		},

		save: function (fSrc, fDest, content, fOptions) {
			var fileObject = {};
			fileObject[fDest] = content;
			return fileObject;
		}
	}, options);

	var fileObjects = [];
	files.forEach(function (f) {
		f.src.filter(function (filepath) {
			if (!grunt.file.exists(filepath)) {
				grunt.log.warn('Source file "' + filepath + '" not found.');
				return false;
			}

			return true;
		}).forEach(function (srcFilePath) {
			fileObjects.push({
				src: srcFilePath,
				dest: f.dest,
				object: f
			});
		});
	});

	async.each(fileObjects, function (fileObject, fileDone) {
		async.nextTick(function () {
			async.series([
				function (done) {
					asyncRun(function () {
						return options.read.call(this, fileObject.src, fileObject.dest, fileObject.object);
					}, function (err, result) {
						fileObject.content = result;
						done(err);
					}, options);
				},

				function (done) {
					asyncRun(function () {
						return options.process.call(this, fileObject.src, fileObject.dest, fileObject.content, fileObject.object);
					}, function (err, result) {
						fileObject.content = result;
						done(err);
					}, options);
				},

				function (done) {
					asyncRun(function () {
						return options.save.call(this, fileObject.src, fileObject.dest, fileObject.content, fileObject.object);
					}, function (err, filesHash) {
						if (err) {
							done(err);
							return;
						}

						_.each(filesHash, function (fileContent, filePath) {
							grunt.file.write(filePath, fileContent);

							if (options.log) {
								grunt.log.ok('File "' + filePath.replace(process.cwd(), '') + '" ' + (filePath === fileObject.src ? 'processed' : 'created'));
							}
						});

						done();
					}, options);
				}
			], fileDone);
		});
	}, gruntDone);
};

module.exports = task;<commit_msg>change code format<commit_after>'use strict';

var _ = require('lodash');
var async = require('async');

var asyncRun = function (func, callback, obj) {
	var async = 0;
	var done = 0;
	var result;
	var err;

	var _callback = function () {
		if (!done++) {
			callback.apply(this, arguments);
		}

		callback = null;
	};

	var asyncCbk = function () {
		return async++ ? null : _callback;
	};

	try {
		if (obj == null) {
			result = func(asyncCbk);
		} else {
			obj = Object.create(obj);
			obj.async = asyncCbk;
			result = func.call(obj);
		}
	} catch (e) {
		err = e;
	}

	if (!async) {
		_callback.call(this, err, result);
	}
};

var task = function (grunt, files, options, gruntDone) {
	options = _.extend({
		log: true,

		read: function (fSrc, fDest, fOptions) {
			return grunt.file.read(fSrc);
		},

		process: function (fSrc, fDest, content, fOptions) {
			return content;
		},

		save: function (fSrc, fDest, content, fOptions) {
			var fileObject = {};

			fileObject[fDest] = content;

			return fileObject;
		}
	}, options);

	var fileObjects = [];

	files.forEach(function (f) {
		f.src.filter(function (filepath) {
			if (!grunt.file.exists(filepath)) {
				grunt.log.warn('Source file "' + filepath + '" not found.');

				return false;
			}

			return true;
		}).forEach(function (srcFilePath) {
			fileObjects.push({
				src: srcFilePath,
				dest: f.dest,
				object: f
			});
		});
	});

	async.each(fileObjects, function (fileObject, fileDone) {
		async.nextTick(function () {
			async.series([
				function (done) {
					asyncRun(function () {
						return options.read.call(this, fileObject.src, fileObject.dest, fileObject.object);
					}, function (err, result) {
						fileObject.content = result;
						done(err);
					}, options);
				},

				function (done) {
					asyncRun(function () {
						return options.process.call(this, fileObject.src, fileObject.dest, fileObject.content, fileObject.object);
					}, function (err, result) {
						fileObject.content = result;
						done(err);
					}, options);
				},

				function (done) {
					asyncRun(function () {
						return options.save.call(this, fileObject.src, fileObject.dest, fileObject.content, fileObject.object);
					}, function (err, filesHash) {
						if (err) {
							done(err);

							return;
						}

						_.each(filesHash, function (fileContent, filePath) {
							grunt.file.write(filePath, fileContent);

							if (options.log) {
								grunt.log.ok('File "' + filePath.replace(process.cwd(), '') + '" ' + (filePath === fileObject.src ? 'processed' : 'created'));
							}
						});

						done();
					}, options);
				}
			], fileDone);
		});
	}, gruntDone);
};

module.exports = task;
<|endoftext|>