<commit_before>/**!
 * urllib - lib/urllib.js
 *
 * Copyright(c) 2011 - 2014 fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
 * MIT Licensed
 */

"use strict";

/**
 * Module dependencies.
 */

require('buffer-concat');
var http = require('http');
var https = require('https');
var urlutil = require('url');
var qs = require('querystring');
var path = require('path');
var fs = require('fs');
var debug = require('debug')('urllib');
var pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '../', 'package.json')));

var USER_AGENT = exports.USER_AGENT = 'node-urllib/' + pkg.version;
var TIME_OUT = exports.TIME_OUT = 60000; // 60 seconds

// change Agent.maxSockets to 1000
exports.agent = new http.Agent();
exports.agent.maxSockets = 1000;

exports.httpsAgent = new https.Agent();
exports.httpsAgent.maxSockets = 1000;

/**
 * The default request timeout(in milliseconds).
 * @type {Number}
 * @const
 */
exports.TIMEOUT = 5000;

var REQUEST_ID = 0;

/**
 * Handle all http request, both http and https support well.
 *
 * @example
 *
 * // GET http://httptest.cnodejs.net
 * urllib.request('http://httptest.cnodejs.net/test/get', function(err, data, res) {});
 * // POST http://httptest.cnodejs.net
 * var args = { type: 'post', data: { foo: 'bar' } };
 * urllib.request('http://httptest.cnodejs.net/test/post', args, function(err, data, res) {});
 *
 * @param {String|Object} url
 * @param {Object} [args], optional
 *   - {Object} [data]: request data, will auto be query stringify.
 *   - {String|Buffer} [content]: optional, if set content, `data` will ignore.
 *   - {ReadStream} [stream]: read stream to sent.
 *   - {WriteStream} [writeStream]: writable stream to save response data.
 *       If you use this, callback's data should be null.
 *       We will just `pipe(ws, {end: true})`.
 *   - {String} [method]: optional, could be GET | POST | DELETE | PUT, default is GET
 *   - {String} [dataType]: optional, `text` or `json`, default is text
 *   - {Object} [headers]: optional, request headers
 *   - {Number} [timeout]: request timeout(in milliseconds), default is `exports.TIMEOUT`
 *   - {Agent} [agent]: optional, http agent. Set `false` if you does not use agent.
 *   - {Agent} [httpsAgent]: optional, https agent. Set `false` if you does not use agent.
 *   - {String} [auth]: Basic authentication i.e. 'user:password' to compute an Authorization header.
 *   - {String|Buffer|Array} [ca]: An array of strings or Buffers of trusted certificates.
 *       If this is omitted several well known "root" CAs will be used, like VeriSign.
 *       These are used to authorize connections.
 *       Notes: This is necessary only if the server uses the self-signed certificate
 *   - {Boolean} [rejectUnauthorized]: If true, the server certificate is verified against the list of supplied CAs.
 *       An 'error' event is emitted if verification fails. Default: true.
 *   - {String|Buffer} [pfx]: A string or Buffer containing the private key,
 *       certificate and CA certs of the server in PFX or PKCS12 format.
 *   - {String|Buffer} [key]: A string or Buffer containing the private key of the client in PEM format.
 *       Notes: This is necessary only if using the client certificate authentication
 *   - {String|Buffer} [cert]: A string or Buffer containing the certificate key of the client in PEM format.
 *       Notes: This is necessary only if using the client certificate authentication
 *   - {String} [passphrase]: A string of passphrase for the private key or pfx.
 *   - {Boolean} [followRedirect]: Follow HTTP 3xx responses as redirects. defaults to false.
 *   - {Number} [maxRedirects]: The maximum number of redirects to follow, defaults to 10.
 *   - {Function(options)} [beforeRequest]: Before request hook, you can change every thing here.
 * @param {Function} callback, callback(error, data, res)
 * @return {HttpRequest} req object.
 * @api public
 */
exports.request = function (url, args, callback) {
  if (typeof args === 'function') {
    callback = args;
    args = null;
  }

  args = args || {};

  args.timeout = args.timeout || exports.TIMEOUT;
  args.maxRedirects = args.maxRedirects || 10;
  var parsedUrl = typeof url === 'string' ? urlutil.parse(url) : url;

  var method = (args.type || args.method || parsedUrl.method || 'GET').toUpperCase();
  var port = parsedUrl.port || 80;
  var httplib = http;
  var agent = args.agent || exports.agent;

  if (parsedUrl.protocol === 'https:') {
    httplib = https;
    agent = args.httpsAgent || exports.httpsAgent;
    if (args.httpsAgent === false) {
      agent = false;
    }
    if (!parsedUrl.port) {
      port = 443;
    }
  }

  if (args.agent === false) {
    agent = false;
  }

  var options = {
    host: parsedUrl.hostname || parsedUrl.host || 'localhost',
    path: parsedUrl.path || '/',
    method: method,
    port: port,
    agent: agent,
    headers: args.headers || {}
  };

  var sslNames = ['ca', 'pfx', 'key', 'cert', 'passphrase'];
  for (var i = 0; i < sslNames.length; i++) {
    var name = sslNames[i];
    if (args[name]) {
      options[name] = args[name];
    }
  }

  if (args.rejectUnauthorized !== undefined) {
    options.rejectUnauthorized = args.rejectUnauthorized;
  }

  var auth = args.auth || parsedUrl.auth;
  if (auth) {
    options.auth = auth;
  }

  var body = args.content || args.data;
  var isReadAction = method === 'GET' || method === 'HEAD';
  if (!args.content) {
    if (body && !(typeof body === 'string' || Buffer.isBuffer(body))) {
      if (isReadAction) {
        // read: GET, HEAD, use query string
        body = qs.stringify(body);
      } else {
        // auto add application/x-www-form-urlencoded when using urlencode form request
        if (!options.headers['Content-Type'] && !options.headers['content-type']) {
          options.headers['Content-Type'] = 'application/x-www-form-urlencoded';
        }

        var contentType = options.headers['Content-Type'] || options.headers['content-type'];
        if (contentType === 'application/json') {
          body = JSON.stringify(body);
        } else {
          // 'application/x-www-form-urlencoded'
          body = qs.stringify(body);
        }
      }
    }
  }

  // if it's a GET or HEAD request, data should be sent as query string
  if (isReadAction && body) {
    options.path += (parsedUrl.query ? '&' : '?') + body;
    body = null;
  }

  if (body) {
    var length = body.length;
    if (!Buffer.isBuffer(body)) {
      length = Buffer.byteLength(body);
    }
    options.headers['Content-Length'] = length;
  }

  if (args.dataType === 'json') {
    options.headers.Accept = 'application/json';
  }

  if (typeof args.beforeRequest === 'function') {
    // you can use this hook to change every thing.
    args.beforeRequest(options);
  }
  var timer = null;
  var __err = null;
  var connnected = false; // socket connected or not
  var done = function (err, data, res) {
    if (timer) {
      clearTimeout(timer);
      timer = null;
    }
    if (!callback) {
      console.warn('[urllib:warn] [%s] [worker:%s] %s %s callback twice!!!',
        Date(), process.pid, options.method, url);
      // TODO: why will be done twice?
      return;
    }
    var cb = callback;
    callback = null;
    if (err) {
      var statusCode = res && res.statusCode || -1;
      var headers = JSON.stringify(res && res.headers || {});
      err.message += ', ' + options.method + ' ' + url + ' ' + statusCode
        + '\nheaders: ' + headers;
      err.res = res;
      err.data = data;
    }
    cb(err, data, res);
  };

  var handleRedirect = function (res) {
    var err = null;
    if ((res.statusCode === 302 || res.statusCode === 301) && args.followRedirect) {  // handle redirect
      args._followRedirectCount = (args._followRedirectCount || 0) + 1;
      if (!res.headers.location) {
        err = new Error('Got statusCode ' + res.statusCode + ' but cannot resolve next location from headers');
        err.name = 'FollowRedirectError';
      } else if (args._followRedirectCount > args.maxRedirects) {
        err = new Error('Exceeded maxRedirects. Probably stuck in a redirect loop ' + url);
        err.name = 'MaxRedirectError';
      } else {
        var _url = urlutil.resolve(url, res.headers.location);
        debug('Request#%d %s: `redirected` from %s to %s', reqId, options.path, url, _url);
        // should pass done instead of callback
        exports.request(_url, args, done);
        return {
          redirect: true,
          error: null
        };
      }
    }
    return {
      redirect: false,
      error: err
    };
  };

  // set user-agent
  if (!options.headers['User-Agent'] || !options.headers['user-agent']) {
    options.headers['User-Agent'] = USER_AGENT;
  }

  var writeStream = args.writeStream;

  var reqId = ++REQUEST_ID;
  var req = httplib.request(options, function (res) {
    connnected = true;
    debug('Request#%d %s `req response` event emit: status %d, headers: %j',
      reqId, options.path, res.statusCode, res.headers);

    if (writeStream) {
      // If there's a writable stream to recieve the response data, just pipe the
      // response stream to that writable stream and call the callback when it has
      // finished writing.
      //
      // NOTE that when the response stream `res` emits an 'end' event it just
      // means that it has finished piping data to another stream. In the
      // meanwhile that writable stream may still writing data to the disk until
      // it emits a 'close' event.
      //
      // That means that we should not apply callback until the 'close' of the
      // writable stream is emited.
      //
      // See also:
      // - https://github.com/TBEDP/urllib/commit/959ac3365821e0e028c231a5e8efca6af410eabb
      // - http://nodejs.org/api/stream.html#stream_event_end
      // - http://nodejs.org/api/stream.html#stream_event_close_1
      var result = handleRedirect(res);
      if (result.redirect) {
        return;
      }
      if (result.error) {
        // end ths stream first
        writeStream.end();
        return done(result.error, null, res);
      }
      writeStream.on('close', done.bind(null, null, null, res));
      return res.pipe(writeStream);
    }

    // Otherwise, just concat those buffers.
    //
    // NOTE that the `chunk` is not a String but a Buffer. It means that if
    // you simply concat two chunk with `+` you're actually converting both
    // Buffers into Strings before concating them. It'll cause problems when
    // dealing with multi-byte characters.
    //
    // The solution is to store each chunk in an array and concat them with
    // 'buffer-concat' when all chunks is recieved.
    //
    // See also:
    // http://cnodejs.org/topic/4faf65852e8fb5bc65113403

    var chunks = [];
    var size = 0;

    res.on('data', function (chunk) {
      debug('Request#%d %s: `res data` event emit, size %d', reqId, options.path, chunk.length);
      size += chunk.length;
      chunks.push(chunk);
    });

    res.on('close', function () {
      debug('Request#%d %s: `res close` event emit, total size %d', reqId, options.path, size);
    });

    res.on('error', function () {
      debug('Request#%d %s: `res error` event emit, total size %d', reqId, options.path, size);
    });

    res.on('aborted', function () {
      res.aborted = true;
      debug('Request#%d %s: `res aborted` event emit, total size %d', reqId, options.path, size);
    });

    res.on('end', function () {
      var data = Buffer.concat(chunks, size);
      debug('Request#%d %s: `res end` event emit, total size %d, _dumped: %s', reqId, options.path, size, res._dumped);

      if (__err && connnected) {
        // req.abort() after `res data` event emit.
        return done(__err, data, res);
      }

      var result = handleRedirect(res);
      var err = result.error;
      if (result.redirect) {
        return;
      }

      if (args.dataType === 'json') {
        if (size === 0) {
          data = null;
        } else {
          try {
            data = JSON.parse(data);
          } catch (e) {
            if (e.name === 'SyntaxError') {
              e.name = 'JSONResponseFormatError';
            }
            err = e;
          }
        }
      }

      if (res.aborted) {
        err = new Error('Remote socket was terminated before `response.end()` was called');
        err.name = 'RemoteSocketClosedError';
      }

      done(err, data, res);
    });
  });

  var abortRequest = function () {
    // it wont case error event when req haven't been assigned a socket yet.
    if (!req.socket) {
      done(__err);
    }
    req.abort();
  };

  var timeout = args.timeout;

  timer = setTimeout(function () {
    timer = null;
    var msg = 'Request#' + reqId + ' timeout for ' + timeout + 'ms';
    __err = new Error(msg);
    __err.name = connnected ? 'ResponseTimeoutError' : 'ConnectionTimeoutError';
    debug('Request#%d %s %s: %s, connected: %s', reqId, options.path, __err.name, msg, connnected);
    abortRequest();
  }, timeout);

  req.once('close', function () {
    debug('Request#%d %s: `req close` event emit', reqId, options.path);
  });

  req.once('error', function (err) {
    if (!__err && err.name === 'Error') {
      err.name = 'RequestError';
    }
    err = __err || err;
    debug('Request#%d %s `req error` event emit, %s: %s', reqId, options.path, err.name, err.message);
    done(err);
  });

  if (writeStream) {
    writeStream.once('error', function (err) {
      __err = err;
      debug('Request#%d %s `writeStream error` event emit, %s: %s', reqId, options.path, err.name, err.message);
      abortRequest();
    });
  }

  if (args.stream) {
    args.stream.pipe(req);
    args.stream.once('error', function (err) {
      __err = err;
      debug('Request#%d %s `readStream error` event emit, %s: %s', reqId, options.path, err.name, err.message);
      abortRequest();
    });
  } else {
    req.end(body);
  }

  req.requestId = reqId;
  return req;
};
<commit_msg>remove buffer-concat<commit_after>/**!
 * urllib - lib/urllib.js
 *
 * Copyright(c) 2011 - 2014 fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
 * MIT Licensed
 */

"use strict";

/**
 * Module dependencies.
 */

var http = require('http');
var https = require('https');
var urlutil = require('url');
var qs = require('querystring');
var path = require('path');
var fs = require('fs');
var debug = require('debug')('urllib');
var pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '../', 'package.json')));

var USER_AGENT = exports.USER_AGENT = 'node-urllib/' + pkg.version;
var TIME_OUT = exports.TIME_OUT = 60000; // 60 seconds

// change Agent.maxSockets to 1000
exports.agent = new http.Agent();
exports.agent.maxSockets = 1000;

exports.httpsAgent = new https.Agent();
exports.httpsAgent.maxSockets = 1000;

/**
 * The default request timeout(in milliseconds).
 * @type {Number}
 * @const
 */
exports.TIMEOUT = 5000;

var REQUEST_ID = 0;

/**
 * Handle all http request, both http and https support well.
 *
 * @example
 *
 * // GET http://httptest.cnodejs.net
 * urllib.request('http://httptest.cnodejs.net/test/get', function(err, data, res) {});
 * // POST http://httptest.cnodejs.net
 * var args = { type: 'post', data: { foo: 'bar' } };
 * urllib.request('http://httptest.cnodejs.net/test/post', args, function(err, data, res) {});
 *
 * @param {String|Object} url
 * @param {Object} [args], optional
 *   - {Object} [data]: request data, will auto be query stringify.
 *   - {String|Buffer} [content]: optional, if set content, `data` will ignore.
 *   - {ReadStream} [stream]: read stream to sent.
 *   - {WriteStream} [writeStream]: writable stream to save response data.
 *       If you use this, callback's data should be null.
 *       We will just `pipe(ws, {end: true})`.
 *   - {String} [method]: optional, could be GET | POST | DELETE | PUT, default is GET
 *   - {String} [dataType]: optional, `text` or `json`, default is text
 *   - {Object} [headers]: optional, request headers
 *   - {Number} [timeout]: request timeout(in milliseconds), default is `exports.TIMEOUT`
 *   - {Agent} [agent]: optional, http agent. Set `false` if you does not use agent.
 *   - {Agent} [httpsAgent]: optional, https agent. Set `false` if you does not use agent.
 *   - {String} [auth]: Basic authentication i.e. 'user:password' to compute an Authorization header.
 *   - {String|Buffer|Array} [ca]: An array of strings or Buffers of trusted certificates.
 *       If this is omitted several well known "root" CAs will be used, like VeriSign.
 *       These are used to authorize connections.
 *       Notes: This is necessary only if the server uses the self-signed certificate
 *   - {Boolean} [rejectUnauthorized]: If true, the server certificate is verified against the list of supplied CAs.
 *       An 'error' event is emitted if verification fails. Default: true.
 *   - {String|Buffer} [pfx]: A string or Buffer containing the private key,
 *       certificate and CA certs of the server in PFX or PKCS12 format.
 *   - {String|Buffer} [key]: A string or Buffer containing the private key of the client in PEM format.
 *       Notes: This is necessary only if using the client certificate authentication
 *   - {String|Buffer} [cert]: A string or Buffer containing the certificate key of the client in PEM format.
 *       Notes: This is necessary only if using the client certificate authentication
 *   - {String} [passphrase]: A string of passphrase for the private key or pfx.
 *   - {Boolean} [followRedirect]: Follow HTTP 3xx responses as redirects. defaults to false.
 *   - {Number} [maxRedirects]: The maximum number of redirects to follow, defaults to 10.
 *   - {Function(options)} [beforeRequest]: Before request hook, you can change every thing here.
 * @param {Function} callback, callback(error, data, res)
 * @return {HttpRequest} req object.
 * @api public
 */
exports.request = function (url, args, callback) {
  if (typeof args === 'function') {
    callback = args;
    args = null;
  }

  args = args || {};

  args.timeout = args.timeout || exports.TIMEOUT;
  args.maxRedirects = args.maxRedirects || 10;
  var parsedUrl = typeof url === 'string' ? urlutil.parse(url) : url;

  var method = (args.type || args.method || parsedUrl.method || 'GET').toUpperCase();
  var port = parsedUrl.port || 80;
  var httplib = http;
  var agent = args.agent || exports.agent;

  if (parsedUrl.protocol === 'https:') {
    httplib = https;
    agent = args.httpsAgent || exports.httpsAgent;
    if (args.httpsAgent === false) {
      agent = false;
    }
    if (!parsedUrl.port) {
      port = 443;
    }
  }

  if (args.agent === false) {
    agent = false;
  }

  var options = {
    host: parsedUrl.hostname || parsedUrl.host || 'localhost',
    path: parsedUrl.path || '/',
    method: method,
    port: port,
    agent: agent,
    headers: args.headers || {}
  };

  var sslNames = ['ca', 'pfx', 'key', 'cert', 'passphrase'];
  for (var i = 0; i < sslNames.length; i++) {
    var name = sslNames[i];
    if (args[name]) {
      options[name] = args[name];
    }
  }

  if (args.rejectUnauthorized !== undefined) {
    options.rejectUnauthorized = args.rejectUnauthorized;
  }

  var auth = args.auth || parsedUrl.auth;
  if (auth) {
    options.auth = auth;
  }

  var body = args.content || args.data;
  var isReadAction = method === 'GET' || method === 'HEAD';
  if (!args.content) {
    if (body && !(typeof body === 'string' || Buffer.isBuffer(body))) {
      if (isReadAction) {
        // read: GET, HEAD, use query string
        body = qs.stringify(body);
      } else {
        // auto add application/x-www-form-urlencoded when using urlencode form request
        if (!options.headers['Content-Type'] && !options.headers['content-type']) {
          options.headers['Content-Type'] = 'application/x-www-form-urlencoded';
        }

        var contentType = options.headers['Content-Type'] || options.headers['content-type'];
        if (contentType === 'application/json') {
          body = JSON.stringify(body);
        } else {
          // 'application/x-www-form-urlencoded'
          body = qs.stringify(body);
        }
      }
    }
  }

  // if it's a GET or HEAD request, data should be sent as query string
  if (isReadAction && body) {
    options.path += (parsedUrl.query ? '&' : '?') + body;
    body = null;
  }

  if (body) {
    var length = body.length;
    if (!Buffer.isBuffer(body)) {
      length = Buffer.byteLength(body);
    }
    options.headers['Content-Length'] = length;
  }

  if (args.dataType === 'json') {
    options.headers.Accept = 'application/json';
  }

  if (typeof args.beforeRequest === 'function') {
    // you can use this hook to change every thing.
    args.beforeRequest(options);
  }
  var timer = null;
  var __err = null;
  var connnected = false; // socket connected or not
  var done = function (err, data, res) {
    if (timer) {
      clearTimeout(timer);
      timer = null;
    }
    if (!callback) {
      console.warn('[urllib:warn] [%s] [worker:%s] %s %s callback twice!!!',
        Date(), process.pid, options.method, url);
      // TODO: why will be done twice?
      return;
    }
    var cb = callback;
    callback = null;
    if (err) {
      var statusCode = res && res.statusCode || -1;
      var headers = JSON.stringify(res && res.headers || {});
      err.message += ', ' + options.method + ' ' + url + ' ' + statusCode
        + '\nheaders: ' + headers;
      err.res = res;
      err.data = data;
    }
    cb(err, data, res);
  };

  var handleRedirect = function (res) {
    var err = null;
    if ((res.statusCode === 302 || res.statusCode === 301) && args.followRedirect) {  // handle redirect
      args._followRedirectCount = (args._followRedirectCount || 0) + 1;
      if (!res.headers.location) {
        err = new Error('Got statusCode ' + res.statusCode + ' but cannot resolve next location from headers');
        err.name = 'FollowRedirectError';
      } else if (args._followRedirectCount > args.maxRedirects) {
        err = new Error('Exceeded maxRedirects. Probably stuck in a redirect loop ' + url);
        err.name = 'MaxRedirectError';
      } else {
        var _url = urlutil.resolve(url, res.headers.location);
        debug('Request#%d %s: `redirected` from %s to %s', reqId, options.path, url, _url);
        // should pass done instead of callback
        exports.request(_url, args, done);
        return {
          redirect: true,
          error: null
        };
      }
    }
    return {
      redirect: false,
      error: err
    };
  };

  // set user-agent
  if (!options.headers['User-Agent'] || !options.headers['user-agent']) {
    options.headers['User-Agent'] = USER_AGENT;
  }

  var writeStream = args.writeStream;

  var reqId = ++REQUEST_ID;
  var req = httplib.request(options, function (res) {
    connnected = true;
    debug('Request#%d %s `req response` event emit: status %d, headers: %j',
      reqId, options.path, res.statusCode, res.headers);

    if (writeStream) {
      // If there's a writable stream to recieve the response data, just pipe the
      // response stream to that writable stream and call the callback when it has
      // finished writing.
      //
      // NOTE that when the response stream `res` emits an 'end' event it just
      // means that it has finished piping data to another stream. In the
      // meanwhile that writable stream may still writing data to the disk until
      // it emits a 'close' event.
      //
      // That means that we should not apply callback until the 'close' of the
      // writable stream is emited.
      //
      // See also:
      // - https://github.com/TBEDP/urllib/commit/959ac3365821e0e028c231a5e8efca6af410eabb
      // - http://nodejs.org/api/stream.html#stream_event_end
      // - http://nodejs.org/api/stream.html#stream_event_close_1
      var result = handleRedirect(res);
      if (result.redirect) {
        return;
      }
      if (result.error) {
        // end ths stream first
        writeStream.end();
        return done(result.error, null, res);
      }
      writeStream.on('close', done.bind(null, null, null, res));
      return res.pipe(writeStream);
    }

    // Otherwise, just concat those buffers.
    //
    // NOTE that the `chunk` is not a String but a Buffer. It means that if
    // you simply concat two chunk with `+` you're actually converting both
    // Buffers into Strings before concating them. It'll cause problems when
    // dealing with multi-byte characters.
    //
    // The solution is to store each chunk in an array and concat them with
    // 'buffer-concat' when all chunks is recieved.
    //
    // See also:
    // http://cnodejs.org/topic/4faf65852e8fb5bc65113403

    var chunks = [];
    var size = 0;

    res.on('data', function (chunk) {
      debug('Request#%d %s: `res data` event emit, size %d', reqId, options.path, chunk.length);
      size += chunk.length;
      chunks.push(chunk);
    });

    res.on('close', function () {
      debug('Request#%d %s: `res close` event emit, total size %d', reqId, options.path, size);
    });

    res.on('error', function () {
      debug('Request#%d %s: `res error` event emit, total size %d', reqId, options.path, size);
    });

    res.on('aborted', function () {
      res.aborted = true;
      debug('Request#%d %s: `res aborted` event emit, total size %d', reqId, options.path, size);
    });

    res.on('end', function () {
      var data = Buffer.concat(chunks, size);
      debug('Request#%d %s: `res end` event emit, total size %d, _dumped: %s', reqId, options.path, size, res._dumped);

      if (__err && connnected) {
        // req.abort() after `res data` event emit.
        return done(__err, data, res);
      }

      var result = handleRedirect(res);
      var err = result.error;
      if (result.redirect) {
        return;
      }

      if (args.dataType === 'json') {
        if (size === 0) {
          data = null;
        } else {
          try {
            data = JSON.parse(data);
          } catch (e) {
            if (e.name === 'SyntaxError') {
              e.name = 'JSONResponseFormatError';
            }
            err = e;
          }
        }
      }

      if (res.aborted) {
        err = new Error('Remote socket was terminated before `response.end()` was called');
        err.name = 'RemoteSocketClosedError';
      }

      done(err, data, res);
    });
  });

  var abortRequest = function () {
    // it wont case error event when req haven't been assigned a socket yet.
    if (!req.socket) {
      done(__err);
    }
    req.abort();
  };

  var timeout = args.timeout;

  timer = setTimeout(function () {
    timer = null;
    var msg = 'Request#' + reqId + ' timeout for ' + timeout + 'ms';
    __err = new Error(msg);
    __err.name = connnected ? 'ResponseTimeoutError' : 'ConnectionTimeoutError';
    debug('Request#%d %s %s: %s, connected: %s', reqId, options.path, __err.name, msg, connnected);
    abortRequest();
  }, timeout);

  req.once('close', function () {
    debug('Request#%d %s: `req close` event emit', reqId, options.path);
  });

  req.once('error', function (err) {
    if (!__err && err.name === 'Error') {
      err.name = 'RequestError';
    }
    err = __err || err;
    debug('Request#%d %s `req error` event emit, %s: %s', reqId, options.path, err.name, err.message);
    done(err);
  });

  if (writeStream) {
    writeStream.once('error', function (err) {
      __err = err;
      debug('Request#%d %s `writeStream error` event emit, %s: %s', reqId, options.path, err.name, err.message);
      abortRequest();
    });
  }

  if (args.stream) {
    args.stream.pipe(req);
    args.stream.once('error', function (err) {
      __err = err;
      debug('Request#%d %s `readStream error` event emit, %s: %s', reqId, options.path, err.name, err.message);
      abortRequest();
    });
  } else {
    req.end(body);
  }

  req.requestId = reqId;
  return req;
};
<|endoftext|>


<commit_before>'use strict';

import fs from 'fs';
import jpeg from 'jpeg-js';
import DevtoolsTimelineModel from 'devtools-timeline-model';

function getPixel(x, y, channel, width, buff) {
	return buff[(x + y * width) * 4 + channel];
}

function convertPixelsToHistogram(img) {
	const createHistogramArray = function () {
		const ret = new Array(256);
		for (let i = 0; i < ret.length; i++) {
			ret[i] = 0;
		}
		return ret;
	};

	const width = img.width;
	const height = img.height;

	const histograms = [
		createHistogramArray(),
		createHistogramArray(),
		createHistogramArray()
	];

	for (let channel = 0; channel < histograms.length; channel++) {
		for (let i = 0; i < width; i++) {
			for (let j = 0; j < height; j++) {
				const pixelValue = getPixel(i, j, channel, width, img.data);

				// Erase pixels considered as white
				if (getPixel(i, j, 0, width, img.data) < 249 &&
						getPixel(i, j, 1, width, img.data) < 249 &&
						getPixel(i, j, 2, width, img.data) < 249) {
					histograms[channel][pixelValue]++;
				}
			}
		}
	}

	return histograms;
}

function extractFramesFromTimeline(timeline) {
	let model;
	if (timeline.frameModel) {
		model = timeline;
	} else {
		const trace = typeof timeline === 'string' ? fs.readFileSync(timeline, 'utf-8') : timeline;
		model = new DevtoolsTimelineModel(trace);
	}
	const rawFrames = model.filmStripModel().frames();

	const timelineModel = model.timelineModel();
	const start = timelineModel.minimumRecordTime();
	const end = timelineModel.maximumRecordTime();

	return Promise.all(rawFrames.map(f => f.imageDataPromise())).then(ret => {
		return Promise.all(ret.map(function (img, index) {
			const imgBuff = new Buffer(img, 'base64');
			return frame(imgBuff, rawFrames[index].timestamp);
		}))
		.then(function (frames) {
			const firstFrame = frame(frames[0].getImage(), start);
			const lastFrame = frame(frames[frames.length - 1].getImage(), end);

			return [firstFrame, ...frames, lastFrame];
		});
	});
}

function frame(imgBuff, ts) {
	let _histogram = null;
	let _progress = null;
	let _perceptualProgress = null;

	return {
		getHistogram: function () {
			if (_histogram) {
				return _histogram;
			}

			const pixels = jpeg.decode(imgBuff);
			_histogram = convertPixelsToHistogram(pixels);
			return _histogram;
		},

		getTimeStamp: function () {
			return ts;
		},

		setProgress: function (progress) {
			_progress = progress;
		},

		setPerceptualProgress: function (progress) {
			_perceptualProgress = progress;
		},

		getImage: function () {
			return imgBuff;
		},

		getParsedImage: function () {
			return jpeg.decode(imgBuff);
		},

		getProgress: function () {
			return _progress;
		},

		getPerceptualProgress: function () {
			return _perceptualProgress;
		}
	};
}

module.exports = {
	extractFramesFromTimeline,
	create: frame
};
<commit_msg>instanceof test is classier, you know.<commit_after>'use strict';

import fs from 'fs';
import jpeg from 'jpeg-js';
import DevtoolsTimelineModel from 'devtools-timeline-model';

function getPixel(x, y, channel, width, buff) {
	return buff[(x + y * width) * 4 + channel];
}

function convertPixelsToHistogram(img) {
	const createHistogramArray = function () {
		const ret = new Array(256);
		for (let i = 0; i < ret.length; i++) {
			ret[i] = 0;
		}
		return ret;
	};

	const width = img.width;
	const height = img.height;

	const histograms = [
		createHistogramArray(),
		createHistogramArray(),
		createHistogramArray()
	];

	for (let channel = 0; channel < histograms.length; channel++) {
		for (let i = 0; i < width; i++) {
			for (let j = 0; j < height; j++) {
				const pixelValue = getPixel(i, j, channel, width, img.data);

				// Erase pixels considered as white
				if (getPixel(i, j, 0, width, img.data) < 249 &&
						getPixel(i, j, 1, width, img.data) < 249 &&
						getPixel(i, j, 2, width, img.data) < 249) {
					histograms[channel][pixelValue]++;
				}
			}
		}
	}

	return histograms;
}

function extractFramesFromTimeline(timeline) {
	let model;
	if (timeline instanceof DevtoolsTimelineModel) {
		model = timeline;
	} else {
		const trace = typeof timeline === 'string' ? fs.readFileSync(timeline, 'utf-8') : timeline;
		model = new DevtoolsTimelineModel(trace);
	}
	const rawFrames = model.filmStripModel().frames();

	const timelineModel = model.timelineModel();
	const start = timelineModel.minimumRecordTime();
	const end = timelineModel.maximumRecordTime();

	return Promise.all(rawFrames.map(f => f.imageDataPromise())).then(ret => {
		return Promise.all(ret.map(function (img, index) {
			const imgBuff = new Buffer(img, 'base64');
			return frame(imgBuff, rawFrames[index].timestamp);
		}))
		.then(function (frames) {
			const firstFrame = frame(frames[0].getImage(), start);
			const lastFrame = frame(frames[frames.length - 1].getImage(), end);

			return [firstFrame, ...frames, lastFrame];
		});
	});
}

function frame(imgBuff, ts) {
	let _histogram = null;
	let _progress = null;
	let _perceptualProgress = null;

	return {
		getHistogram: function () {
			if (_histogram) {
				return _histogram;
			}

			const pixels = jpeg.decode(imgBuff);
			_histogram = convertPixelsToHistogram(pixels);
			return _histogram;
		},

		getTimeStamp: function () {
			return ts;
		},

		setProgress: function (progress) {
			_progress = progress;
		},

		setPerceptualProgress: function (progress) {
			_perceptualProgress = progress;
		},

		getImage: function () {
			return imgBuff;
		},

		getParsedImage: function () {
			return jpeg.decode(imgBuff);
		},

		getProgress: function () {
			return _progress;
		},

		getPerceptualProgress: function () {
			return _perceptualProgress;
		}
	};
}

module.exports = {
	extractFramesFromTimeline,
	create: frame
};
<|endoftext|>


<commit_before>//
// NOTE: This is a prototype, and not yet ready for real use.
//

import * as attributes from './attributes.js';
import deepContains from './deepContains.js';
import Symbol from '../mixins/Symbol.js';
import symbols from '../mixins/symbols.js';

const appendedToDocumentKey = Symbol('appendedToDocument');
const forceAppendToBodyKey = Symbol('forceAppendToBody');
const placeholderKey = Symbol('placeholder');
const previousFocusedElementKey = Symbol('previousFocusedElement');
const previousZIndexKey = Symbol('previousZIndex');

export default function OverlayMixin(Base) {

  // The class prototype added by the mixin.
  class Overlay extends Base {

    constructor() {
      // @ts-ignore
      super();
      this.addEventListener('blur', () => {
        // The focus was taken from us, perhaps because the focus was set
        // elsewhere, so we don't want to try to restore focus when closing.
        this[previousFocusedElementKey] = null;
      });
    }

    [symbols.afterEffect](effect) {
      if (super[symbols.afterEffect]) { super[symbols.afterEffect](effect); }
      switch (effect) {
        case 'closing':
          // Hide the element.
          makeVisible(this, false);

          // Restore z-index.
          this.style.zIndex = this[previousZIndexKey] === '' ?
            null :
            this[previousZIndexKey];
          this[previousZIndexKey] = null;

          if (this[appendedToDocumentKey]) {
            // The overlay wasn't in the document when opened, so we added it.
            // Remove it now.
            this.parentNode.removeChild(this);
            this[appendedToDocumentKey] = false;
          } else if (this[placeholderKey]) {
            // The overlay was moved; return it to its original location.
            this[placeholderKey].parentNode.replaceChild(this, this[placeholderKey]);
            this[placeholderKey] = null;
          }

          break;
      }
    }

    [symbols.beforeEffect](effect) {
      if (super[symbols.beforeEffect]) { super[symbols.beforeEffect](effect); }
      switch (effect) {

        case 'closing':
          // Restore previously focused element before closing.
          if (this[previousFocusedElementKey]) {
            this[previousFocusedElementKey].focus();
            this[previousFocusedElementKey] = null;
          }
          break;

        case 'opening':
          // Remember which element had the focus before we opened.
          this[previousFocusedElementKey] = document.activeElement;

          // Add the element to the document if it's not present yet.
          /** @type {any} */
          const element = this;
          const isElementInBody = deepContains(document.body, element);
          if (isElementInBody) {
            if (this.forceAppendToBody) {
              // Swap a placeholder for the overlay and move the overlay to the
              // top level of the document body.
              this[placeholderKey] = createPlaceholder(this);
              this.parentNode.replaceChild(this[placeholderKey], this);
              document.body.appendChild(element);
            }
          } else {
            // Overlay isn't in document yet.
            this[appendedToDocumentKey] = true;
            document.body.appendChild(element);
          }

          // Remember the element's current z-index.
          this[previousZIndexKey] = this.style.zIndex;
          // It seems like it should be possible to rely on inspecting zIndex
          // via getComputedStyle. However, unit testing reveals at least one
          // case where an inline zIndex style change made immediately before
          // opening the overlay was not reflected by getComputedStyle. Hence,
          // we also check the inline style value.
          // Also note that Safari returns a default zIndex of "0" for elements
          // with position: fixed, while Blink returns "auto".
          const style = getComputedStyle(element)
          const computedZIndex = style.zIndex;
          if (element.style.zIndex === ''
              && (computedZIndex === 'auto' ||
                (style.position === 'fixed' && computedZIndex === '0'))) {
            // Assign default z-index.
            this.style.zIndex = maxZIndexInUse() + 1;
          }

          // Finally make it visible and give it focus.
          makeVisible(this, true);
          this.focus();
          break;

      }
    }

    connectedCallback() {
      if (super.connectedCallback) { super.connectedCallback(); }
      /** @type {any} */
      const element = this;
      attributes.writePendingAttributes(element);
      this.setAttribute('tabindex', '0');
    }

    /**
     * @type {boolean}
     * @default false
     */
    get forceAppendToBody() {
      return this[forceAppendToBodyKey];
    }
    set forceAppendToBody(forceAppendToBody) {
      const parsed = String(forceAppendToBody) === 'true';
      this[forceAppendToBodyKey] = parsed;
      if ('forceAppendToBody' in Base.prototype) { super.opened = parsed; }
    }
  }

  return Overlay;

}

/*
 * Return a placeholder element used to hold an overlay's position in the DOM if
 * it is using forceAppendToBody, so that we can return the overlay to its
 * original location when it's closed.
 */
function createPlaceholder(element) {
  const placeholder = new Comment();
  placeholder.textContent = ` Placeholder for the open ${element.localName}, which will return here when closed. `;
  return placeholder;
}

function makeVisible(element, visible) {
  attributes.setClass(element, 'visible', visible);
  console.log(`  ${visible ? 'added' : 'removed'} visible => ${element.classList}`);
}

function maxZIndexInUse() {
  const elements = document.body.querySelectorAll('*');
  const zIndices = Array.prototype.map.call(elements, element => {
    const style = getComputedStyle(element);
    let zIndex = 0;
    if (style.position !== 'static' && style.zIndex !== 'auto') {
      const parsed = style.zIndex ? parseInt(style.zIndex) : 0;
      zIndex = !isNaN(parsed) ? parsed : 0;
    }
    return zIndex;
  });
  return Math.max(...zIndices);
}
<commit_msg>Rename forceAppendToBody -> teleportToBodyOnOpen.<commit_after>//
// NOTE: This is a prototype, and not yet ready for real use.
//

import * as attributes from './attributes.js';
import deepContains from './deepContains.js';
import Symbol from '../mixins/Symbol.js';
import symbols from '../mixins/symbols.js';

const appendedToDocumentKey = Symbol('appendedToDocument');
const forceAppendToBodyKey = Symbol('forceAppendToBody');
const placeholderKey = Symbol('placeholder');
const previousFocusedElementKey = Symbol('previousFocusedElement');
const previousZIndexKey = Symbol('previousZIndex');

export default function OverlayMixin(Base) {

  // The class prototype added by the mixin.
  class Overlay extends Base {

    constructor() {
      // @ts-ignore
      super();
      this.addEventListener('blur', () => {
        // The focus was taken from us, perhaps because the focus was set
        // elsewhere, so we don't want to try to restore focus when closing.
        this[previousFocusedElementKey] = null;
      });
    }

    [symbols.afterEffect](effect) {
      if (super[symbols.afterEffect]) { super[symbols.afterEffect](effect); }
      switch (effect) {
        case 'closing':
          // Hide the element.
          makeVisible(this, false);

          // Restore z-index.
          this.style.zIndex = this[previousZIndexKey] === '' ?
            null :
            this[previousZIndexKey];
          this[previousZIndexKey] = null;

          if (this[appendedToDocumentKey]) {
            // The overlay wasn't in the document when opened, so we added it.
            // Remove it now.
            this.parentNode.removeChild(this);
            this[appendedToDocumentKey] = false;
          } else if (this[placeholderKey]) {
            // The overlay was moved; return it to its original location.
            this[placeholderKey].parentNode.replaceChild(this, this[placeholderKey]);
            this[placeholderKey] = null;
          }

          break;
      }
    }

    [symbols.beforeEffect](effect) {
      if (super[symbols.beforeEffect]) { super[symbols.beforeEffect](effect); }
      switch (effect) {

        case 'closing':
          // Restore previously focused element before closing.
          if (this[previousFocusedElementKey]) {
            this[previousFocusedElementKey].focus();
            this[previousFocusedElementKey] = null;
          }
          break;

        case 'opening':
          // Remember which element had the focus before we opened.
          this[previousFocusedElementKey] = document.activeElement;

          // Add the element to the document if it's not present yet.
          /** @type {any} */
          const element = this;
          const isElementInBody = deepContains(document.body, element);
          if (isElementInBody) {
            if (this.teleportToBodyOnOpen) {
              // Swap a placeholder for the overlay and move the overlay to the
              // top level of the document body.
              this[placeholderKey] = createPlaceholder(this);
              this.parentNode.replaceChild(this[placeholderKey], this);
              document.body.appendChild(element);
            }
          } else {
            // Overlay isn't in document yet.
            this[appendedToDocumentKey] = true;
            document.body.appendChild(element);
          }

          // Remember the element's current z-index.
          this[previousZIndexKey] = this.style.zIndex;
          // It seems like it should be possible to rely on inspecting zIndex
          // via getComputedStyle. However, unit testing reveals at least one
          // case where an inline zIndex style change made immediately before
          // opening the overlay was not reflected by getComputedStyle. Hence,
          // we also check the inline style value.
          // Also note that Safari returns a default zIndex of "0" for elements
          // with position: fixed, while Blink returns "auto".
          const style = getComputedStyle(element)
          const computedZIndex = style.zIndex;
          if (element.style.zIndex === ''
              && (computedZIndex === 'auto' ||
                (style.position === 'fixed' && computedZIndex === '0'))) {
            // Assign default z-index.
            this.style.zIndex = maxZIndexInUse() + 1;
          }

          // Finally make it visible and give it focus.
          makeVisible(this, true);
          this.focus();
          break;

      }
    }

    connectedCallback() {
      if (super.connectedCallback) { super.connectedCallback(); }
      /** @type {any} */
      const element = this;
      attributes.writePendingAttributes(element);
      this.setAttribute('tabindex', '0');
    }

    /**
     * @type {boolean}
     * @default false
     */
    get teleportToBodyOnOpen() {
      return this[forceAppendToBodyKey];
    }
    set teleportToBodyOnOpen(teleportToBodyOnOpen) {
      const parsed = String(teleportToBodyOnOpen) === 'true';
      this[forceAppendToBodyKey] = parsed;
      if ('teleportToBodyOnOpen' in Base.prototype) { super.opened = parsed; }
    }
  }

  return Overlay;

}

/*
 * Return a placeholder element used to hold an overlay's position in the DOM if
 * it is using forceAppendToBody, so that we can return the overlay to its
 * original location when it's closed.
 */
function createPlaceholder(element) {
  const placeholder = new Comment();
  placeholder.textContent = ` Placeholder for the open ${element.localName}, which will return here when closed. `;
  return placeholder;
}

function makeVisible(element, visible) {
  attributes.setClass(element, 'visible', visible);
  console.log(`  ${visible ? 'added' : 'removed'} visible => ${element.classList}`);
}

function maxZIndexInUse() {
  const elements = document.body.querySelectorAll('*');
  const zIndices = Array.prototype.map.call(elements, element => {
    const style = getComputedStyle(element);
    let zIndex = 0;
    if (style.position !== 'static' && style.zIndex !== 'auto') {
      const parsed = style.zIndex ? parseInt(style.zIndex) : 0;
      zIndex = !isNaN(parsed) ? parsed : 0;
    }
    return zIndex;
  });
  return Math.max(...zIndices);
}
<|endoftext|>


<commit_before>/*
 * @package     etd-framework/js
 *
 * @version     1.0
 * @copyright   Copyright (C) 2015 ETD Solutions, SARL Etudoo. Tous droits réservés.
 * @license     Apache License Version 2.0 (https://raw.githubusercontent.com/etd-framework/js/master/LICENSE)
 * @author      ETD Solutions http://etd-solutions.com
 */

define(["jquery", "etdsolutions/app", "etdsolutions/text"], function($, app, text) {

    /*
     * Méthodes privées
     */

    function sendTask(self, $this, $icon, options, newValue, handler) {

        // On crée l'objet de données.
        var data = {};

        $.each(options.props, function(i, str) {
            data[str] =  $this.data(i);
        });

        data[options.valueName] = newValue;
        data[options.token] = '1';

        if (!$icon.hasClass('fa-spin')) {
            $icon.removeClass('fa-warning ' + options.icons).addClass('fa-spin fa-refresh');

            self.task(options.uri, data, function(data) {

                if (options.successCallback) {
                    options.successCallback(data, $this, newValue);
                }

                $this.data('current', newValue);

                var btnParams = handler(newValue, options.states || {});

                $this.tooltip('destroy');
                $this.attr('title', text._(btnParams.txt));
                $this.tooltip({
                    container: 'body',
                    html: true
                });
                $icon.removeClass('fa-spin fa-refresh').addClass(btnParams.icon);

            }, function() {

                $icon.removeClass('fa-spin fa-refresh').addClass('fa-warning');

            });
        }

    }

    function btnCall(self, type, btnSelector, options, handler) {

        $(btnSelector).on('click', function(e) {

            e.preventDefault();

            var $this = $(this),
                $icon = $this.find('.fa');

            // On change la valeur.
            var newValue;

            if (type == 'toggle') {

                newValue = 1 - $this.data('current');

                sendTask(self, $this, $icon, options, newValue, handler);

            } else if (type == 'choice') {

                if (!$this.data('init')) {

                    var html = '';
                    $.each(options.states, function(val) {
                        if (val != 'default') {
                            html += '<input type="radio" name="' + options.valueName + '" value="' + val + '"> ' + this.txt + '';
                        }
                    });

                    $this.popover({
                            html: true,
                            content: html,
                            placement: 'bottom',
                            template: '

'
                    }).on('shown.bs.popover', function() {
                        $this.data('bs.popover').$tip.find('input').on('change', function() {

                            $this.popover('hide');
                            newValue = $(this).val();
                            sendTask(self, $this, $icon, options, newValue, handler);

                        });
                    });

                    $this.data('init', true);
                    $this.popover('show');
                }

            }

        });

    }

    /*
     * Objet public
     */

    return {

        createChoiceBtn: function(btnSelector, options) {

            btnCall(this, 'choice', btnSelector, options, function(newValue, states) {

                var btnParams = {
                    txt: states.default.txt,
                    icon: states.default.icon
                };

                if (states[newValue]) {
                    btnParams.txt = states[newValue].txt;
                    btnParams.icon = states[newValue].icon;
                }

                return btnParams;

            });

            return this;

        },

        createToggleBtn: function(btnSelector, options) {

            options['icons'] = 'fa-check fa-times';

            btnCall(this, 'toggle', btnSelector, options, function(newValue, states) {

                var btnParams = {
                    txt: '',
                    icon: ''
                };

                if (newValue == 1) {
                    btnParams.txt = 'APP_GLOBAL_YES';
                    btnParams.icon = 'fa-check';
                } else {
                    btnParams.txt = 'APP_GLOBAL_NO';
                    btnParams.icon = 'fa-times';
                }

                return btnParams;

            });

            return this;

        },

        /**
         * Méthode pour appeler un controller
         *
         * @param uri
         * @param data
         * @param successCallback
         * @param failCallback
         * @returns jqXHR
         */
        task: function(uri, data, successCallback, failCallback) {

            return $.ajax(uri, {
                dataType: 'json',
                type: 'POST',
                data: data
            }).done(function(data) {
                if (data.error) {
                    app.raiseError(data.message);
                    if (failCallback) {
                        failCallback(data);
                    }
                } else if(successCallback) {
                    successCallback(data);
                }
            }).fail(function(obj) {
                if (obj.responseJSON && obj.responseJSON.error) {
                    app.raiseError(obj.responseJSON.message);
                    if (failCallback) {
                        failCallback(obj.responseJSON);
                    }
                } else {
                    if (failCallback) {
                        failCallback();
                    }
                }
            });

        }

    };

});<commit_msg>Ajout classe popover pour style css<commit_after>/*
 * @package     etd-framework/js
 *
 * @version     1.0
 * @copyright   Copyright (C) 2015 ETD Solutions, SARL Etudoo. Tous droits réservés.
 * @license     Apache License Version 2.0 (https://raw.githubusercontent.com/etd-framework/js/master/LICENSE)
 * @author      ETD Solutions http://etd-solutions.com
 */

define(["jquery", "etdsolutions/app", "etdsolutions/text"], function($, app, text) {

    /*
     * Méthodes privées
     */

    function sendTask(self, $this, $icon, options, newValue, handler) {

        // On crée l'objet de données.
        var data = {};

        $.each(options.props, function(i, str) {
            data[str] =  $this.data(i);
        });

        data[options.valueName] = newValue;
        data[options.token] = '1';

        if (!$icon.hasClass('fa-spin')) {
            $icon.removeClass('fa-warning ' + options.icons).addClass('fa-spin fa-refresh');

            self.task(options.uri, data, function(data) {

                if (options.successCallback) {
                    options.successCallback(data, $this, newValue);
                }

                $this.data('current', newValue);

                var btnParams = handler(newValue, options.states || {});

                $this.tooltip('destroy');
                $this.attr('title', text._(btnParams.txt));
                $this.tooltip({
                    container: 'body',
                    html: true
                });
                $icon.removeClass('fa-spin fa-refresh').addClass(btnParams.icon);

            }, function() {

                $icon.removeClass('fa-spin fa-refresh').addClass('fa-warning');

            });
        }

    }

    function btnCall(self, type, btnSelector, options, handler) {

        $(btnSelector).on('click', function(e) {

            e.preventDefault();

            var $this = $(this),
                $icon = $this.find('.fa');

            // On change la valeur.
            var newValue;

            if (type == 'toggle') {

                newValue = 1 - $this.data('current');

                sendTask(self, $this, $icon, options, newValue, handler);

            } else if (type == 'choice') {

                if (!$this.data('init')) {

                    var html = '';
                    $.each(options.states, function(val) {
                        if (val != 'default') {
                            html += '<input type="radio" name="' + options.valueName + '" value="' + val + '"> ' + this.txt + '';
                        }
                    });

                    $this.popover({
                            html: true,
                            content: html,
                            placement: 'bottom',
                            template: '

'
                    }).on('shown.bs.popover', function() {
                        $this.data('bs.popover').$tip.find('input').on('change', function() {

                            $this.popover('hide');
                            newValue = $(this).val();
                            sendTask(self, $this, $icon, options, newValue, handler);

                        });
                    });

                    $this.data('init', true);
                    $this.popover('show');
                }

            }

        });

    }

    /*
     * Objet public
     */

    return {

        createChoiceBtn: function(btnSelector, options) {

            btnCall(this, 'choice', btnSelector, options, function(newValue, states) {

                var btnParams = {
                    txt: states.default.txt,
                    icon: states.default.icon
                };

                if (states[newValue]) {
                    btnParams.txt = states[newValue].txt;
                    btnParams.icon = states[newValue].icon;
                }

                return btnParams;

            });

            return this;

        },

        createToggleBtn: function(btnSelector, options) {

            options['icons'] = 'fa-check fa-times';

            btnCall(this, 'toggle', btnSelector, options, function(newValue, states) {

                var btnParams = {
                    txt: '',
                    icon: ''
                };

                if (newValue == 1) {
                    btnParams.txt = 'APP_GLOBAL_YES';
                    btnParams.icon = 'fa-check';
                } else {
                    btnParams.txt = 'APP_GLOBAL_NO';
                    btnParams.icon = 'fa-times';
                }

                return btnParams;

            });

            return this;

        },

        /**
         * Méthode pour appeler un controller
         *
         * @param uri
         * @param data
         * @param successCallback
         * @param failCallback
         * @returns jqXHR
         */
        task: function(uri, data, successCallback, failCallback) {

            return $.ajax(uri, {
                dataType: 'json',
                type: 'POST',
                data: data
            }).done(function(data) {
                if (data.error) {
                    app.raiseError(data.message);
                    if (failCallback) {
                        failCallback(data);
                    }
                } else if(successCallback) {
                    successCallback(data);
                }
            }).fail(function(obj) {
                if (obj.responseJSON && obj.responseJSON.error) {
                    app.raiseError(obj.responseJSON.message);
                    if (failCallback) {
                        failCallback(obj.responseJSON);
                    }
                } else {
                    if (failCallback) {
                        failCallback();
                    }
                }
            });

        }

    };

});<|endoftext|>


<commit_before>import React from 'react'
import { renderToString } from 'react-dom/server'
import { StaticRouter as Router } from 'react-router-dom'
import { Provider } from 'react-redux'
import { compose, createStore } from 'redux'

import 'utils/polyfills'
import Pages from 'pages'
import renderSite from 'renderers/renderSite'
import rootReducer from 'reducers'
import { updatePageMeta } from 'reducers/pageMeta'

module.exports = (req, res) => {
	const context = {}
	const store = compose()(createStore)(rootReducer)

	const renderedContent = renderToString(
		<Provider store={store}>
			<Router
				location={req.url}
				context={context}
			>
				<Pages />
			</Router>
		</Provider>
	)

	store.dispatch(updatePageMeta(req.url))
	const renderedPage = renderSite(renderedContent, store.getState())

	if (context.url) {
		res.writeHead(302, { Location: context.url })
		res.end()

	} else {
		res.send(renderedPage).end()
	}
}
<commit_msg>Fixed server-side rendering glitch with rootReducer and createStore<commit_after>import React from 'react'
import { renderToString } from 'react-dom/server'
import { StaticRouter as Router } from 'react-router-dom'
import { Provider } from 'react-redux'
import { compose, createStore } from 'redux'

import 'utils/polyfills'

import Pages from 'pages'
import renderSite from 'renderers/renderSite'
import { rootReducer } from 'reducers'
import { updatePageMeta } from 'reducers/pageMeta'

module.exports = (req, res) => {
	const context = {}
	const store = compose()(createStore)(rootReducer)

	const renderedContent = renderToString(
		<Provider store={store}>
			<Router
				location={req.url}
				context={context}
			>
				<Pages />
			</Router>
		</Provider>
	)

	store.dispatch(
		updatePageMeta(req.url)
	)

	const renderedPage = renderSite(renderedContent, store.getState())

	if (context.url) {
		res
		.writeHead(302, { Location: context.url })
		.end()

	} else {
		res
		.send(renderedPage)
		.end()
	}
}
<|endoftext|>


<commit_before>/*
 * Utilex
 * Copyright (c) 2013 Fatih Cetinkaya (http://github.com/cmfatih/utilex)
 * For the full copyright and license information, please view the LICENSE.txt file.
 */

/* jslint node: true */
'use strict';

var fs     = require('fs'),
    path   = require('path'),
    http   = require('http'),
    crypto = require('crypto');

// Init the module
exports = module.exports = (function() {

  // Returns tidy arguments
  var args = function args() {

    if(!process.argv.length) {
      return {};
    }

    var argv  = process.argv,
        argsF = {},
        currentArg,
        currentParam,
        paramFlag,
        posBegin,
        posEnd;

    for(var i = 0, len = argv.length; i < len; i++) {
      currentArg = (''+argv[i]).trim();

      if(currentArg) {
        if(currentArg.indexOf('-') === 0) {
          paramFlag = false;
          posBegin  = (currentArg.indexOf('--') === 0) ? 2 : 1;
          posEnd    = currentArg.length;

          if(posEnd > posBegin) {
            currentParam = currentArg.substring(posBegin, posEnd);
            argsF[currentParam] = '';
            paramFlag = true;
          }
        } else {
          if(paramFlag === true) {
            argsF[currentParam] = currentArg;
            currentParam = null;
            paramFlag = false;
          } else {
            argsF[currentArg] = '';
          }
        }
      }
    }

    return argsF;
  };

  // Returns date time
  var datetime = function datetime(date) {
    var pDate = (typeof date === 'object' && date.toISOString) ? date : new Date();
    return new Date(pDate.toISOString().replace('Z', '+0' + (pDate.getTimezoneOffset()/60) + ':00')).toISOString().replace('T', ' ').substr(0, 19);
  };

  // Clears console
  var consoleClear = function consoleClear() {
    if(typeof console !== 'undefined') console.log('\u001B[2J\u001B[0;0f');
  };

  // Returns the length of the given string
  var strLen = function strLen(str, isByteLen) {
    if(typeof str !== undefined) {
      if(isByteLen === true) {
        return ~-encodeURI(str).split(/%..|./).length;
      } else {
        return str.length;
      }
    }

    return 0;
  };

  // Checks whether the given directory is writable or not
  var dirIsWritable = function dirIsWritable(dirPath) {

    var fp = dirPath + path.sep + 'file.tmp' ;

    if(!fs.existsSync(dirPath)) return false; // Check dir

    try {
      fs.writeFileSync(fp); // Write to the dir
      try {
        fs.unlinkSync(fp); // Remove the temp file
        return true;
      } catch(e) {
        return false;
      }
    } catch(e) {
      return false;
    }

    return false;
  };

  // Gets a file
  // TODO: Just rewrite this
  var httpGetFile = function httpGet(getUrl, filePath, callback) {
    if(!getUrl || !filePath)
      return false;
    if(typeof callback !== 'function')
      callback = function callback(err, result) { return err || result; };

    // Get and save
    http.get(getUrl, function(res) {
      if(res.statusCode === 200) {
        var ws = res.pipe(fs.createWriteStream(filePath));

        ws.on('finish', function() {
          return callback(null, filePath);
        });
        ws.on('error', function(err) {
          return callback(err);
        });
      } else {
        res.destroy();
        return callback('Bad response (' + res.statusCode + ')');
      }
    }).on('error', function(err) {
      return callback(err);
    });
  };

  // Loads a JSON file
  var jsonLoad = function jsonLoad(filePath) {
    if(typeof filePath === 'string' && fs.existsSync(filePath)) {
      try {
        return JSON.parse(fs.readFileSync(filePath));
      } catch(e) {
        return false;
      }
    }

    return false;
  };

  // Simulates an asynchronous function
  var asyncFunc = function asyncFunc(input, options, callback) {
    if(typeof options === 'function')
      callback = options;
    if(typeof callback !== 'function')
      callback = function callback(err) { return err; };
    if(!options || typeof options !== 'object')
      options = {};
    if(!options.delay || isNaN(options.delay))
      options.delay = Math.floor((Math.random() * 200));

    setTimeout(function() {
      return callback({input: input, execTime: options.delay});
    }, options.delay);
  };

  // Returns a tasker
  var tasker = function() {
    var tasks = [];

    // Adds a task
    var add = function add(anything) {
      tasks.push(anything);
    };

    // Runs tasks
    var run = function run(cbEach, cbDone) {
      var next = function next() {
        var task = tasks.shift();
        if(!task) { return cbDone(); }
        return cbEach(task, next);
      };
      next();
    };

    // Return
    return {
      add: add,
      run: run
    };
  };

  // Returns a unique id
  var uid = function() {
    return crypto.randomBytes(20).toString('hex');
  }

  // Return
  return {
    args:          args,
    asyncFunc:     asyncFunc,
    consoleClear:  consoleClear,
    datetime:      datetime,
    dirIsWritable: dirIsWritable,
    httpGetFile:   httpGetFile,
    jsonLoad:      jsonLoad,
    strLen:        strLen,
    tasker:        tasker,
    uid:           uid
  };
})();<commit_msg>Add semicolon<commit_after>/*
 * Utilex
 * Copyright (c) 2013 Fatih Cetinkaya (http://github.com/cmfatih/utilex)
 * For the full copyright and license information, please view the LICENSE.txt file.
 */

/* jslint node: true */
'use strict';

var fs     = require('fs'),
    path   = require('path'),
    http   = require('http'),
    crypto = require('crypto');

// Init the module
exports = module.exports = (function() {

  // Returns tidy arguments
  var args = function args() {

    if(!process.argv.length) {
      return {};
    }

    var argv  = process.argv,
        argsF = {},
        currentArg,
        currentParam,
        paramFlag,
        posBegin,
        posEnd;

    for(var i = 0, len = argv.length; i < len; i++) {
      currentArg = (''+argv[i]).trim();

      if(currentArg) {
        if(currentArg.indexOf('-') === 0) {
          paramFlag = false;
          posBegin  = (currentArg.indexOf('--') === 0) ? 2 : 1;
          posEnd    = currentArg.length;

          if(posEnd > posBegin) {
            currentParam = currentArg.substring(posBegin, posEnd);
            argsF[currentParam] = '';
            paramFlag = true;
          }
        } else {
          if(paramFlag === true) {
            argsF[currentParam] = currentArg;
            currentParam = null;
            paramFlag = false;
          } else {
            argsF[currentArg] = '';
          }
        }
      }
    }

    return argsF;
  };

  // Returns date time
  var datetime = function datetime(date) {
    var pDate = (typeof date === 'object' && date.toISOString) ? date : new Date();
    return new Date(pDate.toISOString().replace('Z', '+0' + (pDate.getTimezoneOffset()/60) + ':00')).toISOString().replace('T', ' ').substr(0, 19);
  };

  // Clears console
  var consoleClear = function consoleClear() {
    if(typeof console !== 'undefined') console.log('\u001B[2J\u001B[0;0f');
  };

  // Returns the length of the given string
  var strLen = function strLen(str, isByteLen) {
    if(typeof str !== undefined) {
      if(isByteLen === true) {
        return ~-encodeURI(str).split(/%..|./).length;
      } else {
        return str.length;
      }
    }

    return 0;
  };

  // Checks whether the given directory is writable or not
  var dirIsWritable = function dirIsWritable(dirPath) {

    var fp = dirPath + path.sep + 'file.tmp' ;

    if(!fs.existsSync(dirPath)) return false; // Check dir

    try {
      fs.writeFileSync(fp); // Write to the dir
      try {
        fs.unlinkSync(fp); // Remove the temp file
        return true;
      } catch(e) {
        return false;
      }
    } catch(e) {
      return false;
    }

    return false;
  };

  // Gets a file
  // TODO: Just rewrite this
  var httpGetFile = function httpGet(getUrl, filePath, callback) {
    if(!getUrl || !filePath)
      return false;
    if(typeof callback !== 'function')
      callback = function callback(err, result) { return err || result; };

    // Get and save
    http.get(getUrl, function(res) {
      if(res.statusCode === 200) {
        var ws = res.pipe(fs.createWriteStream(filePath));

        ws.on('finish', function() {
          return callback(null, filePath);
        });
        ws.on('error', function(err) {
          return callback(err);
        });
      } else {
        res.destroy();
        return callback('Bad response (' + res.statusCode + ')');
      }
    }).on('error', function(err) {
      return callback(err);
    });
  };

  // Loads a JSON file
  var jsonLoad = function jsonLoad(filePath) {
    if(typeof filePath === 'string' && fs.existsSync(filePath)) {
      try {
        return JSON.parse(fs.readFileSync(filePath));
      } catch(e) {
        return false;
      }
    }

    return false;
  };

  // Simulates an asynchronous function
  var asyncFunc = function asyncFunc(input, options, callback) {
    if(typeof options === 'function')
      callback = options;
    if(typeof callback !== 'function')
      callback = function callback(err) { return err; };
    if(!options || typeof options !== 'object')
      options = {};
    if(!options.delay || isNaN(options.delay))
      options.delay = Math.floor((Math.random() * 200));

    setTimeout(function() {
      return callback({input: input, execTime: options.delay});
    }, options.delay);
  };

  // Returns a tasker
  var tasker = function() {
    var tasks = [];

    // Adds a task
    var add = function add(anything) {
      tasks.push(anything);
    };

    // Runs tasks
    var run = function run(cbEach, cbDone) {
      var next = function next() {
        var task = tasks.shift();
        if(!task) { return cbDone(); }
        return cbEach(task, next);
      };
      next();
    };

    // Return
    return {
      add: add,
      run: run
    };
  };

  // Returns a unique id
  var uid = function() {
    return crypto.randomBytes(20).toString('hex');
  };

  // Return
  return {
    args:          args,
    asyncFunc:     asyncFunc,
    consoleClear:  consoleClear,
    datetime:      datetime,
    dirIsWritable: dirIsWritable,
    httpGetFile:   httpGetFile,
    jsonLoad:      jsonLoad,
    strLen:        strLen,
    tasker:        tasker,
    uid:           uid
  };
})();<|endoftext|>


<commit_before>/* global Nauper, wrapText */
Nauper.Frame = function Frame(engine, args) {
  this.engine = engine;
  this.render = this.engine.render;
  this.size = this.engine.size;
  this.canvas = this.engine.canvas;
  this.characters = args.characters;
  this.displayOrder = args.displayOrder;
  this.background = args.background;
  this.text = args.textbox;
  this.type = 'frame';

  this.draw = function draw() {
    if (this.check()) {
      this.render.clearRect(0, 0, this.size.width, this.size.height);
      this.setBackground();
      this.displayCharacters();
    } else {
      this.engine.nextElement();
    }
  }.bind(this);
};

Nauper.Frame.prototype.setBackground = function setBackground() {
  if (this.background) {
    this.canvas.style.backgroundImage = `url(./data/images/backgrounds/${this.background})`;
  }
};

Nauper.Frame.prototype.check = function check() {
  return true;
};

Nauper.Frame.prototype.setText = function setText() {
  if (this.text !== undefined) {
    const x = this.size.width * 0.025;
    const y = this.size.height * 0.80;
    const height = this.size.height * 0.18;
    const width = this.size.width * 0.95;
    const radius = this.size.height * 0.05;
    const textwidth = this.size.width * 0.80;
    const textx = this.size.width * 0.10;
    const texty = (this.size.height * 0.83) + 27;
    let texts = [];
    this.render.fillStyle = this.text.base;
    if (this.text.edges === 'default') {
      this.render.fillRect(x, y, width, height);
    } else if (this.text.edges === 'rounded') {
      this.render.beginPath();
      this.render.moveTo(x, y + radius);
      this.render.lineTo(x, (y + height) - radius);
      this.render.quadraticCurveTo(x, y + height, x + radius, y + height);
      this.render.lineTo((x + width) - radius, y + height);
      this.render.quadraticCurveTo(x + width, y + height, x + width, (y + height) - radius);
      this.render.lineTo(x + width, y + radius);
      this.render.quadraticCurveTo(x + width, y, (x + width) - radius, y);
      this.render.lineTo(x + radius, y);
      this.render.quadraticCurveTo(x, y, x, y + radius);
      this.render.fill();
    }
    this.render.fillStyle = this.text.namecolor;
    this.render.fillText(this.text.name, this.size.width * 0.10, y + 27);
    this.render.fillStyle = this.text.textcolor;
    texts = wrapText(this.engine, this.text.text, this.render.font, textwidth);
    for (let j = 0; j < texts.result.length; j += 1) {
      let i = texts.result[j];
      this.render.fillText(i, textx, texty + (texts.height * j));
    }
  }
};

Nauper.Frame.prototype.displayCharacters = function displayCharacters() {
  if (this.displayOrder !== undefined && this.displayOrder.length !== 0) {
    let loaded = this.displayOrder.length;
    this.displayOrder.forEach((i, index) => {
      if (i !== false && i !== undefined) {
        let img = new Image();
        img.addEventListener('load', () => {
          let ratio = (this.size.height * 1.20) / img.height;
          let offsetY = this.size.height * 0.10;
          let offsetX = this.size.width * (0.225 * index);
          this.render.drawImage(img, offsetX, offsetY, img.width * ratio, img.height * ratio);
          loaded -= 1;
          if (loaded === 0) {
            this.setText();
          }
        });
        img.src = this.characters[i];
      } else {
        loaded -= 1;
      }
    });
  } else {
    this.setText();
  }
};
<commit_msg>Little Frame changes<commit_after>/* global Nauper, wrapText */
Nauper.Frame = function Frame(engine, args) {
  this.engine = engine;
  this.render = this.engine.render;
  this.size = this.engine.size;
  this.canvas = this.engine.canvas;
  this.characters = args.characters;
  this.displayOrder = args.displayOrder;
  this.background = args.background;
  this.text = args.textbox;
  this.type = 'frame';

  this.draw = function draw() {
    if (this.check()) {
      this.render.clearRect(0, 0, this.size.width, this.size.height);
      this.setBackground();
      this.displayCharacters();
    } else {
      this.engine.nextElement();
    }
  }.bind(this);
};

Nauper.Frame.prototype.setBackground = function setBackground() {
  if (this.background) {
    this.canvas.style.backgroundImage = `url(./data/images/backgrounds/${this.background})`;
  }
};

Nauper.Frame.prototype.check = function check() {
  return true;
};

Nauper.Frame.prototype.setText = function setText() {
  if (this.text !== undefined) {
    const x = this.size.width * 0.025;
    const y = this.size.height * 0.80;
    const height = this.size.height * 0.18;
    const width = this.size.width * 0.95;
    const radius = this.size.height * 0.05;
    const textwidth = this.size.width * 0.80;
    const textx = this.size.width * 0.10;
    const texty = (this.size.height * 0.83) + 27;
    let texts = [];
    this.render.fillStyle = this.text.base;
    if (this.text.edges === 'default') {
      this.render.fillRect(x, y, width, height);
    } else if (this.text.edges === 'rounded') {
      this.render.beginPath();
      this.render.moveTo(x, y + radius);
      this.render.lineTo(x, (y + height) - radius);
      this.render.quadraticCurveTo(x, y + height, x + radius, y + height);
      this.render.lineTo((x + width) - radius, y + height);
      this.render.quadraticCurveTo(x + width, y + height, x + width, (y + height) - radius);
      this.render.lineTo(x + width, y + radius);
      this.render.quadraticCurveTo(x + width, y, (x + width) - radius, y);
      this.render.lineTo(x + radius, y);
      this.render.quadraticCurveTo(x, y, x, y + radius);
      this.render.fill();
    }
    this.render.fillStyle = this.text.namecolor;
    this.render.fillText(this.text.name, this.size.width * 0.10, y + 27);
    this.render.fillStyle = this.text.textcolor;
    texts = wrapText(this.engine, this.text.text, this.render.font, textwidth);
    for (let j = 0; j < texts.result.length; j += 1) {
      let i = texts.result[j];
      this.render.fillText(i, textx, texty + (texts.height * j));
    }
  }
};

Nauper.Frame.prototype.displayCharacters = function displayCharacters() {
  if (this.displayOrder !== undefined && this.displayOrder.length !== 0) {
    let loaded = this.displayOrder.length;
    this.displayOrder.forEach((i, index) => {
      if (i !== false && i !== undefined) {
        let img = new Image();
        img.addEventListener('load', () => {
          const ratio = (this.size.height * 1.20) / img.height;
          const offsetY = this.size.height * 0.10;
          const offsetX = this.size.width * (0.225 * index);
          this.render.drawImage(img, offsetX, offsetY, img.width * ratio, img.height * ratio);
          loaded -= 1;
          if (loaded === 0) {
            this.setText();
          }
        });
        img.src = this.characters[i];
      } else {
        loaded -= 1;
      }
    });
  } else {
    this.setText();
  }
};
<|endoftext|>


<commit_before>(function rocketbeltProgressIndicators(rb, document) {
  function onClassMutation(mutations) {
    var mutationsLen = mutations && mutations.length ? mutations.length : 0;

    for (var k = 0; k < mutationsLen; k++) {
      var mutation = mutations[k];
      var el = mutation.target;

      if (mutation.target.classList.contains('is-busy')) {
        // If "is-busy" was added, do the decoratin'
		if (mutation.target.getElementsByClassName('is-busy_overlay').length === 0) {
		// Only add overlay if one doesn't already exist
			var dot = '

';
			var a11yAttrs = 'aria-label="Loading." role="alert" aria-live="polite" aria-busy="true"';
			var elType = el.nodeName === 'UL' || el.nodeName === 'OL' ? 'li' : 'div';
			var fragment = '<' + elType + ' class="is-busy_overlay is-busy_overlay-opaque" ' + a11yAttrs + '>' + dot + dot + dot + '</' + elType + '>';

			el.innerHTML = el.innerHTML + fragment;
		}
      } else {
        // If "is-busy" was removed
        var overlay = el.querySelectorAll('.is-busy_overlay')[0];

        if (overlay && !overlay.parentNode.classList.contains('is-busy')) {
		overlay.parentNode.removeChild(overlay);
	}
      }
    }
  }

  function observeComponents() {
    var busyable = document.querySelectorAll('.is-busyable');

    if (busyable.length === 0) {
      busyable = document.querySelectorAll('body');
    }

    var busyableLen = busyable.length;

    for (var i = 0; i < busyableLen; i++) {
      var busyableEl = busyable[i];

      // Set an observer to listen for .invalid.
      var observer = new MutationObserver(function (mutations) { onClassMutation(mutations); });
      observer.observe(busyableEl, { subtree: true, attributes: true, attributeOldValue: true, attributeFilter: ['class'] });
    }
  }

  rb.onDocumentReady(observeComponents);
  rb.progressIndicators = rb.progressIndicators || {};
  rb.progressIndicators.observeComponents = observeComponents;
})(window.rb, document);
<commit_msg>verifies classList exists<commit_after>(function rocketbeltProgressIndicators(rb, document) {
  function onClassMutation(mutations) {
    var mutationsLen = mutations && mutations.length ? mutations.length : 0;

    for (var k = 0; k < mutationsLen; k++) {
      var mutation = mutations[k];
      var el = mutation.target;

      if (mutation.target.classList && mutation.target.classList.contains('is-busy')) {
        // If "is-busy" was added, do the decoratin'
		if (mutation.target.getElementsByClassName('is-busy_overlay').length === 0) {
		// Only add overlay if one doesn't already exist
			var dot = '

';
			var a11yAttrs = 'aria-label="Loading." role="alert" aria-live="polite" aria-busy="true"';
			var elType = el.nodeName === 'UL' || el.nodeName === 'OL' ? 'li' : 'div';
			var fragment = '<' + elType + ' class="is-busy_overlay is-busy_overlay-opaque" ' + a11yAttrs + '>' + dot + dot + dot + '</' + elType + '>';

			el.innerHTML = el.innerHTML + fragment;
		}
      } else {
        // If "is-busy" was removed
        var overlay = el.querySelectorAll('.is-busy_overlay')[0];

        if (overlay && !overlay.parentNode.classList.contains('is-busy')) {
		overlay.parentNode.removeChild(overlay);
	}
      }
    }
  }

  function observeComponents() {
    var busyable = document.querySelectorAll('.is-busyable');

    if (busyable.length === 0) {
      busyable = document.querySelectorAll('body');
    }

    var busyableLen = busyable.length;

    for (var i = 0; i < busyableLen; i++) {
      var busyableEl = busyable[i];

      // Set an observer to listen for .invalid.
      var observer = new MutationObserver(function (mutations) { onClassMutation(mutations); });
      observer.observe(busyableEl, { subtree: true, attributes: true, attributeOldValue: true, attributeFilter: ['class'] });
    }
  }

  rb.onDocumentReady(observeComponents);
  rb.progressIndicators = rb.progressIndicators || {};
  rb.progressIndicators.observeComponents = observeComponents;
})(window.rb, document);
<|endoftext|>


<commit_before>// Copyright (c) 2015 Florian Hartmann, https://github.com/florian https://github.com/florian/cookie.js
!function(a,b){var c=function(){return c.get.apply(c,arguments)},d=c.utils={isArray:Array.isArray||function(a){return"[object Array]"===Object.prototype.toString.call(a)},isPlainObject:function(a){return!!a&&"[object Object]"===Object.prototype.toString.call(a)},toArray:function(a){return Array.prototype.slice.call(a)},getKeys:Object.keys||function(a){var b=[],c="";for(c in a)a.hasOwnProperty(c)&&b.push(c);return b},encode:function(a){return String(a).replace(/[,;"\\=\s%]/g,function(a){return encodeURIComponent(a)})},decode:function(a){return decodeURIComponent(a)},retrieve:function(a,b){return null==a?b:a}};c.defaults={},c.expiresMultiplier=86400,c.set=function(c,e,f){if(d.isPlainObject(c))for(var g in c)c.hasOwnProperty(g)&&this.set(g,c[g],e);else{f=d.isPlainObject(f)?f:{expires:f};var h=f.expires!==b?f.expires:this.defaults.expires||"",i=typeof h;"string"===i&&""!==h?h=new Date(h):"number"===i&&(h=new Date(+new Date+1e3*this.expiresMultiplier*h)),""!==h&&"toUTCString"in h&&(h=";expires="+h.toUTCString());var j=f.path||this.defaults.path;j=j?";path="+j:"";var k=f.domain||this.defaults.domain;k=k?";domain="+k:"";var l=f.secure||this.defaults.secure?";secure":"";!1===f.secure&&(l=""),a.cookie=d.encode(c)+"="+d.encode(e)+h+j+k+l}return this},c.setDefault=function(a,e,f){if(d.isPlainObject(a)){for(var g in a)this.get(g)===b&&this.set(g,a[g],e);return c}if(this.get(a)===b)return this.set.apply(this,arguments)},c.remove=function(a){a=d.isArray(a)?a:d.toArray(arguments);for(var b=0,c=a.length;b<c;b++)this.set(a[b],"",-1);return this},c.removeSpecific=function(a,b){if(!b)return this.remove(a);a=d.isArray(a)?a:[a],b.expires=-1;for(var c=0,e=a.length;c<e;c++)this.set(a[c],"",b);return this},c.empty=function(){return this.remove(d.getKeys(this.all()))},c.get=function(a,b){var c=this.all();if(d.isArray(a)){for(var e={},f=0,g=a.length;f<g;f++){var h=a[f];e[h]=d.retrieve(c[h],b)}return e}return d.retrieve(c[a],b)},c.all=function(){if(""===a.cookie)return{};for(var b=a.cookie.split("; "),c={},e=0,f=b.length;e<f;e++){var g=b[e].split("="),h=d.decode(g.shift()),i=d.decode(g.join("="));c[h]=i}return c},c.enabled=function(){if(navigator.cookieEnabled)return!0;var a="_"===c.set("_","_").get("_");return c.remove("_"),a},"function"==typeof define&&define.amd?define(function(){return{cookie:c}}):"undefined"!=typeof exports?exports.cookie=c:window.cookie=c}("undefined"==typeof document?null:document);<commit_msg>Remove cookie.min.js<commit_after><|endoftext|>


<commit_before>var http = require('http');
var connect = require('connect');
var url = require('url');
var request = require('request');
var citeproc = require('./citeproc');
var doi = require('./doi');
var settings = require('./settings');

console.log(settings);

function init() {
	console.log("creating server...");
	var server = connect().use(connect.logger()).use(connect.static('./html'))
			.use(dispatcher);
	http.createServer(server).listen(settings.port);
	console.log("server listening on port " + settings.port + ".");
}

function dispatcher(req, res) {
	var path = url.parse(req.url).pathname;
	switch (path) {
	case "/format":
		formatHandler(req, res);
		break;
	case "/styles":
		listHandler(req, res, citeproc.getStyles());
		break;
	case "/locales":
		listHandler(req, res, citeproc.getLocales());
		break;
	default:
		sendResponse(res, 404, "url not found");
	}
}

function listHandler(req, res, array) {
	var json = JSON.stringify(array);
	sendResponse(res, 200, json, {
		"Content-Type" : "application/json"
	});
}

function formatHandler(req, res) {
	var query = getQuery(req.url);
	var doi = query.doi;
	if (doi == undefined)
		sendResponse(res, 400, "doi param required");
	else {
		require("./doi").resolve(
				doi,
				function(data) {
					try {
						item = JSON.parse(data);
						citeproc.format(item, query.style, query.lang,
								function(text) {
									sendResponse(res, 200, text);
								}, function(msg) {
									sendResponse(res, 400, msg);
								});
					} catch (err) {
						sendResponse(res, 500, "error while formatting: "
								+ err.message);
					}
				}, function(code, msg) {
					sendResponse(res, code, msg);
				}, "application/citeproc+json");
	}
}

function getQuery(url) {
	return require('url').parse(url, true).query;
}

function sendResponse(res, code, body, options) {
	if (options == null)
		options = {};
	if (options["Content-Type"] == undefined)
		options["Content-Type"] = "text/plain";
	res.writeHead(code, options);
	res.write(body);
	res.end();
}

init();<commit_msg>use connect.query<commit_after>var http = require('http');
var connect = require('connect');
var url = require('url');
var request = require('request');
var citeproc = require('./citeproc');
var doi = require('./doi');
var settings = require('./settings');

console.log(settings);

function init() {
	console.log("creating server...");

	var server = connect();
	server.use(connect.query());
	server.use(connect.logger());
	server.use(connect.static('./html'));
	server.use(dispatcher);

	http.createServer(server).listen(settings.port);
	console.log("server listening on port " + settings.port + ".");
}

function dispatcher(req, res) {
	var path = url.parse(req.url).pathname;
	switch (path) {
	case "/format":
		formatHandler(req, res);
		break;
	case "/styles":
		listHandler(req, res, citeproc.getStyles());
		break;
	case "/locales":
		listHandler(req, res, citeproc.getLocales());
		break;
	default:
		sendResponse(res, 404, "url not found");
	}
}

function listHandler(req, res, array) {
	var json = JSON.stringify(array);
	sendResponse(res, 200, json, {
		"Content-Type" : "application/json"
	});
}

function formatHandler(req, res) {
	var query = req.query
	var doi = query.doi;
	if (doi == undefined)
		sendResponse(res, 400, "doi param required");
	else {
		require("./doi").resolve(
				doi,
				function(data) {
					try {
						item = JSON.parse(data);
						citeproc.format(item, query.style, query.lang,
								function(text) {
									sendResponse(res, 200, text);
								}, function(msg) {
									sendResponse(res, 400, msg);
								});
					} catch (err) {
						sendResponse(res, 500, "error while formatting: "
								+ err.message);
					}
				}, function(code, msg) {
					sendResponse(res, code, msg);
				}, "application/citeproc+json");
	}
}

function sendResponse(res, code, body, options) {
	if (options == null)
		options = {};
	if (options["Content-Type"] == undefined)
		options["Content-Type"] = "text/plain";
	res.writeHead(code, options);
	res.write(body);
	res.end();
}

init();<|endoftext|>