<commit_before>//Copyright 2012 Telefonica Investigación y Desarrollo, S.A.U
//
//This file is part of RUSH.
//
//  RUSH is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
//  RUSH is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
//
//  You should have received a copy of the GNU Affero General Public License along with RUSH
//  . If not, seehttp://www.gnu.org/licenses/.
//
//For those usages not covered by the GNU Affero General Public License please contact with::dtc_support@tid.es

var http = require('http');
var https = require('https');

var MG = require('./myGlobals').C;
var url = require('url');
var configGlobal = require('./configBase.js');
var config = configGlobal.consumer;

var path = require('path');
var fs = require('fs');
var log = require('./logger');
var logger = log.newLogger();
logger.prefix = path.basename(module.filename, '.js');

var qs = require("querystring");

http.globalAgent.maxSockets = config.maxSockets;
https.globalAgent.maxSockets = config.maxSockets;

/*This reads the certificates directory to
add them to the list of trusted servers*/
var cas = [],
    casFiles,
    casPath;

if (config.casDir) {
  try {
    casPath = path.resolve(__dirname, config.casDir);
    casFiles = fs.readdirSync(casPath); // []
    for (var i = 0; i < casFiles.length; i++) {
      var certPath = path.resolve(casPath, casFiles[i]);
      if (getExtension(certPath) === 'crt') {
        cas.push(fs.readFileSync(certPath));
      }
    }
  } catch (err) {
    logger.warning(err,{ opType: 'GET CERTIFICATES'});
  }
}   else {
  logger.warning("CAs dir not set",{ opType: 'GET CERTIFICATES'});
}

function getExtension(filename) {
    var ext = path.extname(filename||'').split('.');
    return ext[ext.length - 1];
}
// END

function urlErrors(pUrl) {
  'use strict';
  var parsedUrl;
  if (pUrl) {
    parsedUrl = url.parse(pUrl);
    if (!parsedUrl.protocol) {
      return ('Protocol is not defined');
    } else if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {
      return ('Invalid protocol ' + parsedUrl.protocol);
    } else {
      if (! parsedUrl.hostname) {
        return ('Hostname expected. Empty host after protocol');
      }
    }
  }

  return null;
}

function validateHeaders(simpleRequest) {

  var errorsHeaders = [];

  //Check required headers
  if (! simpleRequest.headers[MG.HEAD_RELAYER_HOST]) {
    var error = {};
    error.type = MG.MISSING_PARAMETER;
    error.parameter = MG.HEAD_RELAYER_HOST;

    errorsHeaders.push(error);

  } else {

    //Check X-Relayer-Host format
    //Regex: host[:port]
    //host is an string that can contains letters, numbers, '.' and '-'
    //port is a number from 0 to 99999
    var REGEX_HOSTNAME_PORT = new RegExp('^[a-zA-Z0-9\.\-]+(:([0-9]{1,5}))?$');
    var result = REGEX_HOSTNAME_PORT.exec(simpleRequest.headers[MG.HEAD_RELAYER_HOST]);
    if (!result) {

      var error = {};
      error.type = MG.INVALID_PARAMETER;
      error.parameter = MG.HEAD_RELAYER_HOST;
      error.userMessage = 'Valid format: host[:port]';

      errorsHeaders.push(error);

    } else {

      var port = result[2];
      if (port > MG.MAX_PORT) {

        var error = {};
        error.type = MG.INVALID_PARAMETER;
        error.parameter = MG.HEAD_RELAYER_HOST;
        error.userMessage = 'Port should be a number between 0 and ' + MG.MAX_PORT;

        errorsHeaders.push(error);

      }
    }

    //Check URLs
    var urlHeaders = [ MG.HEAD_RELAYER_HTTPCALLBACK, MG.HEAD_RELAYER_HTTPCALLBACK_ERROR];

    for (var i = 0; i < urlHeaders.length; i++) {
      var urlToCheck = simpleRequest.headers[urlHeaders[i]];
      var urlError = urlErrors(urlToCheck);

      if (urlError) {

        var error = {};
        error.type = MG.INVALID_PARAMETER;
        error.parameter = urlHeaders[i];
        error.userMessage = urlError;

        errorsHeaders.push(error);
      }
    }

    //check Retry header
    var retryStr = simpleRequest.headers[MG.HEAD_RELAYER_RETRY];
    if (retryStr) {
      var retrySplit = retryStr.split(',');
      if (! retrySplit.every(function(num) {
        return isFinite(Number(num));
      })) {

        var error = {}
        error.type = MG.INVALID_PARAMETER;
        error.parameter = MG.HEAD_RELAYER_RETRY;
        error.userMessage = 'Invalid retry value: ' + retryStr;

        errorsHeaders.push(error);
      }
    }

    //check Persistence Header
    var persistence = simpleRequest.headers[MG.HEAD_RELAYER_PERSISTENCE];
    if (persistence !== undefined) {

      if (persistence === '') {
        delete simpleRequest.headers[MG.HEAD_RELAYER_PERSISTENCE];
      } else if (MG.ACCEPTED_PERSISTENCE.indexOf(persistence) === -1) {

        var error = {};
        error.type = MG.INVALID_PARAMETER_ACCEPTED_VALUES;
        error.parameter = MG.HEAD_RELAYER_PERSISTENCE;
        error.acceptedValues = MG.ACCEPTED_PERSISTENCE;

        errorsHeaders.push(error);
      }
    }

    //check protocol Header
    var protocol = simpleRequest.headers[MG.HEAD_RELAYER_PROTOCOL];
    if (protocol !== undefined) {

      if (protocol === '') {
        delete simpleRequest.headers[MG.HEAD_RELAYER_PROTOCOL];
      } else if (MG.ACCEPTED_PROTOCOLS.indexOf(protocol) === -1) {

        var error = {};
        error.type = MG.INVALID_PARAMETER_ACCEPTED_VALUES;
        error.parameter = MG.HEAD_RELAYER_PROTOCOL;
        error.acceptedValues = MG.ACCEPTED_PROTOCOLS;

        errorsHeaders.push(error);
      }
    }

  }

  return errorsHeaders;

}

function createTask(simpleRequest, callback) {
  'use strict';

  logger.debug('New Task', { op: 'CREATE TASK', userID: simpleRequest.user, correlator: simpleRequest.traceID,
    transid: simpleRequest.id, arguments: [ simpleRequest ] });

  // If there is a default proxy in config and no proxy was set in 'x-relayer-proxy'
  // use default proxy
  if(config.proxy && !simpleRequest.headers[MG.HEAD_RELAYER_PROXY] ) {
    simpleRequest.headers[MG.HEAD_RELAYER_PROXY] = config.proxy;
  }

  var errorsHeaders = validateHeaders(simpleRequest);

  if (errorsHeaders.length > 0) {
    callback(errorsHeaders, null);
  } else {
    callback(null, simpleRequest);
  }
}

function doJob(task, callback) {
  'use strict';

  var traceID = task.traceID;

  logger.debug('Do Job', { op: 'DO JOB', userID: task.user, correlator: traceID, transid: task.id,
    arguments: [ task ]});

  var httpModule;
  var targetHost = task.headers[MG.HEAD_RELAYER_HOST],
      proxyHost = task.headers[MG.HEAD_RELAYER_PROXY],
      extraHeaders =  task.headers[MG.HEAD_RELAYER_HEADER],
      req;

  if (!targetHost) {
    logger.warning('No target host', { op: 'DO JOB', userID: task.user, correlator: traceID,
      transid: task.id });
  } else {

    var protocol = task.headers[MG.HEAD_RELAYER_PROTOCOL] || task.protocol; // || 'http'
    var completeURL = protocol + '://' + targetHost + task.path;
    var options = url.parse(completeURL);
    task.headers.host = options.host;

    // if a proxy is specified ('X-Relayer-Proxy' or default by config)
    // options.host is changed to go to the proxy. Hopefully, headers host
    // remains as set from 'X-Relayer-Host'
    if(proxyHost) {
      var optionsProxy = proxyHost.split(':');
      options.host = proxyHost;
      options.port = optionsProxy[1];
      options.hostname = optionsProxy[0];
      options.path = completeURL;
    }

    if (options.protocol === 'https:') {
      httpModule = https;
      //This header takes precedence over config
      if (task.headers[MG.HEAD_RELAYER_SERVER_CERT]) {
        options.ca = new Buffer(task.headers[MG.HEAD_RELAYER_SERVER_CERT], 'base64');
        options.rejectUnathorized = true;
      } else {
        options.rejectUnauthorized = !config.trustAllServers;
        if (!config.trustAllServers) {
          options.ca = cas;
        }
      }
    } else { // assume plain http
      httpModule = http;
    }

    if (extraHeaders) {
      extraHeaders.split(",").forEach(function (h) {
        var value = ""+qs.unescapeBuffer(h,true)

        task.headers[value.split(":")[0].trim()] = value.split(":").slice(1).join(":").trim();

      })
    }

    options.headers = delXrelayerHeaders(task.headers);
    options.method = task.method;
    if (config.agent !== undefined) {
      options.agent = config.agent;
    }

    req = httpModule.request(options, function(rlyRes) {
      req.rush_connected = true;
      if (Math.floor(rlyRes.statusCode / 100) === 2) {
        //if no 5XX ERROR
        getResponse(rlyRes, task, function(task, respObj) {
          //PERSISTENCE
          if (callback) {
            callback(null, respObj);
          }
        });
      } else {
        getResponse(rlyRes, task, function(task, respObj) {
          var e = {
            id: task.id,
            traceID: task.traceID,
            exception: {
              exceptionId: 'SVC Relayed Host Error',
              exceptionText: 'Not relayed request ' + rlyRes.statusCode
            },
            statusCode: rlyRes.statusCode,
            headers: rlyRes.headers,
            body: respObj.body
          };
          logger.warning('Server Error (4xx, 5xx)', { op: 'DO JOB', userID: task.user, correlator: traceID,
            transid: task.id, error: e });
          doRetry(task, e, callback);
        });

      }
    });

    req.rush_connected = false;
    setTimeout(function(){
      if(!req.rush_connected) {
        logger.warning('config.responseTimeout reached. Task cancelled', { op: 'DO JOB', userID: task.user,
          correlator: traceID, transid : task.id });
        req.abort();
      }
    }, config.responseTimeout || MG.MAX_TIMEOUT);

    req.on('error', function(e) {
      req.rush_connected = true;
      e.resultOk = false;
      var errObj = {
        id: task.id,
        traceID: task.traceID,
        //error: e.code + '(' + e.syscall + ')'
        exception: {
          exceptionId: 'SVC Relayed Host Error',
          exceptionText: e.message
        }
      };

      logger.warning('Request Error', { op: 'DO JOB', userID: task.user, correlator: traceID,
        transid: task.id, error: e});
      doRetry(task, errObj, callback);
    });

    if (options.method === 'POST' || options.method === 'PUT') {
      //write body
      req.write(task.body);
    }
    req.end(); //?? sure HERE?
  }
}

function getResponse(resp, task, callback) {
  'use strict';

  var data = [];
  var length = 0;

  resp.on('data', function(chunk) {
    data.push(chunk);
    length += chunk.length;
  });

  resp.on('end', function(chunk) {
    if (chunk) {
      data.push(chunk);
      length += chunk.length;
    } //avoid tail undefined

    var body_encoding = task.headers[MG.HEAD_RELAYER_ENCODING];

    if (! body_encoding || MG.ACEPTS_ENCODINGS.indexOf(body_encoding) === - 1) {
      body_encoding = 'utf8';
    }

    var buf = new Buffer(length);
    for (var i = 0, pos = 0; i < data.length; i++) {
      data[i].copy(buf, pos);
      pos += data[i].length;
    }

    var encodedBody = buf.toString(body_encoding);

    var respObj = {
      id: task.id,
      traceID: task.traceID,
      statusCode: resp.statusCode,
      encoding: body_encoding,
      headers: resp.headers,
      body: encodedBody
    };

    if (callback) {
      callback(task, respObj);
    }
  });
}

function doRetry(task, error, callback) {
  'use strict';

  var retryList = task.headers[MG.HEAD_RELAYER_RETRY];
  var time = - 1;
  if (retryList) {
    var retryA = retryList.split(',');
    if (retryA.length > 0) {
      time = parseInt(retryA.shift(), 10);
      if (retryA.length > 0) {
        // there is retry times still
        task.headers[MG.HEAD_RELAYER_RETRY] = retryA.join(',');
      } else {
        //Retry End with no success
        delete task.headers[MG.HEAD_RELAYER_RETRY];
      }
      if (time > 0) {
        setTimeout(function() {
          doJob(task, callback);
        }, time);
      }
    }
  } else {

    if (callback) {
      callback(error, null);
    }
  }
}

function delXrelayerHeaders(headers) {
  'use strict';

  var cleanHeaders = {};
  for (var h in headers) {
    if (headers.hasOwnProperty(h)) {
      if (h.toLowerCase().indexOf('x-relayer') !== 0) {
        cleanHeaders[h] = headers[h];
      }
    }
  }
  return cleanHeaders;
}
exports.doJob = doJob;
exports.createTask = createTask;

//require('./hookLogger.js').init(exports, logger);
<commit_msg>Allow extra headers to be 'x-relayer-'<commit_after>//Copyright 2012 Telefonica Investigación y Desarrollo, S.A.U
//
//This file is part of RUSH.
//
//  RUSH is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
//  RUSH is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
//
//  You should have received a copy of the GNU Affero General Public License along with RUSH
//  . If not, seehttp://www.gnu.org/licenses/.
//
//For those usages not covered by the GNU Affero General Public License please contact with::dtc_support@tid.es

var http = require('http');
var https = require('https');

var MG = require('./myGlobals').C;
var url = require('url');
var configGlobal = require('./configBase.js');
var config = configGlobal.consumer;

var path = require('path');
var fs = require('fs');
var log = require('./logger');
var logger = log.newLogger();
logger.prefix = path.basename(module.filename, '.js');

var qs = require("querystring");

http.globalAgent.maxSockets = config.maxSockets;
https.globalAgent.maxSockets = config.maxSockets;

/*This reads the certificates directory to
add them to the list of trusted servers*/
var cas = [],
    casFiles,
    casPath;

if (config.casDir) {
  try {
    casPath = path.resolve(__dirname, config.casDir);
    casFiles = fs.readdirSync(casPath); // []
    for (var i = 0; i < casFiles.length; i++) {
      var certPath = path.resolve(casPath, casFiles[i]);
      if (getExtension(certPath) === 'crt') {
        cas.push(fs.readFileSync(certPath));
      }
    }
  } catch (err) {
    logger.warning(err,{ opType: 'GET CERTIFICATES'});
  }
}   else {
  logger.warning("CAs dir not set",{ opType: 'GET CERTIFICATES'});
}

function getExtension(filename) {
    var ext = path.extname(filename||'').split('.');
    return ext[ext.length - 1];
}
// END

function urlErrors(pUrl) {
  'use strict';
  var parsedUrl;
  if (pUrl) {
    parsedUrl = url.parse(pUrl);
    if (!parsedUrl.protocol) {
      return ('Protocol is not defined');
    } else if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {
      return ('Invalid protocol ' + parsedUrl.protocol);
    } else {
      if (! parsedUrl.hostname) {
        return ('Hostname expected. Empty host after protocol');
      }
    }
  }

  return null;
}

function validateHeaders(simpleRequest) {

  var errorsHeaders = [];

  //Check required headers
  if (! simpleRequest.headers[MG.HEAD_RELAYER_HOST]) {
    var error = {};
    error.type = MG.MISSING_PARAMETER;
    error.parameter = MG.HEAD_RELAYER_HOST;

    errorsHeaders.push(error);

  } else {

    //Check X-Relayer-Host format
    //Regex: host[:port]
    //host is an string that can contains letters, numbers, '.' and '-'
    //port is a number from 0 to 99999
    var REGEX_HOSTNAME_PORT = new RegExp('^[a-zA-Z0-9\.\-]+(:([0-9]{1,5}))?$');
    var result = REGEX_HOSTNAME_PORT.exec(simpleRequest.headers[MG.HEAD_RELAYER_HOST]);
    if (!result) {

      var error = {};
      error.type = MG.INVALID_PARAMETER;
      error.parameter = MG.HEAD_RELAYER_HOST;
      error.userMessage = 'Valid format: host[:port]';

      errorsHeaders.push(error);

    } else {

      var port = result[2];
      if (port > MG.MAX_PORT) {

        var error = {};
        error.type = MG.INVALID_PARAMETER;
        error.parameter = MG.HEAD_RELAYER_HOST;
        error.userMessage = 'Port should be a number between 0 and ' + MG.MAX_PORT;

        errorsHeaders.push(error);

      }
    }

    //Check URLs
    var urlHeaders = [ MG.HEAD_RELAYER_HTTPCALLBACK, MG.HEAD_RELAYER_HTTPCALLBACK_ERROR];

    for (var i = 0; i < urlHeaders.length; i++) {
      var urlToCheck = simpleRequest.headers[urlHeaders[i]];
      var urlError = urlErrors(urlToCheck);

      if (urlError) {

        var error = {};
        error.type = MG.INVALID_PARAMETER;
        error.parameter = urlHeaders[i];
        error.userMessage = urlError;

        errorsHeaders.push(error);
      }
    }

    //check Retry header
    var retryStr = simpleRequest.headers[MG.HEAD_RELAYER_RETRY];
    if (retryStr) {
      var retrySplit = retryStr.split(',');
      if (! retrySplit.every(function(num) {
        return isFinite(Number(num));
      })) {

        var error = {}
        error.type = MG.INVALID_PARAMETER;
        error.parameter = MG.HEAD_RELAYER_RETRY;
        error.userMessage = 'Invalid retry value: ' + retryStr;

        errorsHeaders.push(error);
      }
    }

    //check Persistence Header
    var persistence = simpleRequest.headers[MG.HEAD_RELAYER_PERSISTENCE];
    if (persistence !== undefined) {

      if (persistence === '') {
        delete simpleRequest.headers[MG.HEAD_RELAYER_PERSISTENCE];
      } else if (MG.ACCEPTED_PERSISTENCE.indexOf(persistence) === -1) {

        var error = {};
        error.type = MG.INVALID_PARAMETER_ACCEPTED_VALUES;
        error.parameter = MG.HEAD_RELAYER_PERSISTENCE;
        error.acceptedValues = MG.ACCEPTED_PERSISTENCE;

        errorsHeaders.push(error);
      }
    }

    //check protocol Header
    var protocol = simpleRequest.headers[MG.HEAD_RELAYER_PROTOCOL];
    if (protocol !== undefined) {

      if (protocol === '') {
        delete simpleRequest.headers[MG.HEAD_RELAYER_PROTOCOL];
      } else if (MG.ACCEPTED_PROTOCOLS.indexOf(protocol) === -1) {

        var error = {};
        error.type = MG.INVALID_PARAMETER_ACCEPTED_VALUES;
        error.parameter = MG.HEAD_RELAYER_PROTOCOL;
        error.acceptedValues = MG.ACCEPTED_PROTOCOLS;

        errorsHeaders.push(error);
      }
    }

  }

  return errorsHeaders;

}

function createTask(simpleRequest, callback) {
  'use strict';

  logger.debug('New Task', { op: 'CREATE TASK', userID: simpleRequest.user, correlator: simpleRequest.traceID,
    transid: simpleRequest.id, arguments: [ simpleRequest ] });

  // If there is a default proxy in config and no proxy was set in 'x-relayer-proxy'
  // use default proxy
  if(config.proxy && !simpleRequest.headers[MG.HEAD_RELAYER_PROXY] ) {
    simpleRequest.headers[MG.HEAD_RELAYER_PROXY] = config.proxy;
  }

  var errorsHeaders = validateHeaders(simpleRequest);

  if (errorsHeaders.length > 0) {
    callback(errorsHeaders, null);
  } else {
    callback(null, simpleRequest);
  }
}

function doJob(task, callback) {
  'use strict';

  var traceID = task.traceID;

  logger.debug('Do Job', { op: 'DO JOB', userID: task.user, correlator: traceID, transid: task.id,
    arguments: [ task ]});

  var httpModule;
  var targetHost = task.headers[MG.HEAD_RELAYER_HOST],
      proxyHost = task.headers[MG.HEAD_RELAYER_PROXY],
      extraHeaders =  task.headers[MG.HEAD_RELAYER_HEADER],
      req;

  if (!targetHost) {
    logger.warning('No target host', { op: 'DO JOB', userID: task.user, correlator: traceID,
      transid: task.id });
  } else {

    var protocol = task.headers[MG.HEAD_RELAYER_PROTOCOL] || task.protocol; // || 'http'
    var completeURL = protocol + '://' + targetHost + task.path;
    var options = url.parse(completeURL);
    task.headers.host = options.host;

    // if a proxy is specified ('X-Relayer-Proxy' or default by config)
    // options.host is changed to go to the proxy. Hopefully, headers host
    // remains as set from 'X-Relayer-Host'
    if(proxyHost) {
      var optionsProxy = proxyHost.split(':');
      options.host = proxyHost;
      options.port = optionsProxy[1];
      options.hostname = optionsProxy[0];
      options.path = completeURL;
    }

    if (options.protocol === 'https:') {
      httpModule = https;
      //This header takes precedence over config
      if (task.headers[MG.HEAD_RELAYER_SERVER_CERT]) {
        options.ca = new Buffer(task.headers[MG.HEAD_RELAYER_SERVER_CERT], 'base64');
        options.rejectUnathorized = true;
      } else {
        options.rejectUnauthorized = !config.trustAllServers;
        if (!config.trustAllServers) {
          options.ca = cas;
        }
      }
    } else { // assume plain http
      httpModule = http;
    }

    options.headers = delXrelayerHeaders(task.headers);

    if (extraHeaders) {
      extraHeaders.split(",").forEach(function (h) {
        var value = ""+qs.unescapeBuffer(h,true)

        task.headers[value.split(":")[0].trim()] = value.split(":").slice(1).join(":").trim();

      })
    }

    options.method = task.method;
    if (config.agent !== undefined) {
      options.agent = config.agent;
    }

    req = httpModule.request(options, function(rlyRes) {
      req.rush_connected = true;
      if (Math.floor(rlyRes.statusCode / 100) === 2) {
        //if no 5XX ERROR
        getResponse(rlyRes, task, function(task, respObj) {
          //PERSISTENCE
          if (callback) {
            callback(null, respObj);
          }
        });
      } else {
        getResponse(rlyRes, task, function(task, respObj) {
          var e = {
            id: task.id,
            traceID: task.traceID,
            exception: {
              exceptionId: 'SVC Relayed Host Error',
              exceptionText: 'Not relayed request ' + rlyRes.statusCode
            },
            statusCode: rlyRes.statusCode,
            headers: rlyRes.headers,
            body: respObj.body
          };
          logger.warning('Server Error (4xx, 5xx)', { op: 'DO JOB', userID: task.user, correlator: traceID,
            transid: task.id, error: e });
          doRetry(task, e, callback);
        });

      }
    });

    req.rush_connected = false;
    setTimeout(function(){
      if(!req.rush_connected) {
        logger.warning('config.responseTimeout reached. Task cancelled', { op: 'DO JOB', userID: task.user,
          correlator: traceID, transid : task.id });
        req.abort();
      }
    }, config.responseTimeout || MG.MAX_TIMEOUT);

    req.on('error', function(e) {
      req.rush_connected = true;
      e.resultOk = false;
      var errObj = {
        id: task.id,
        traceID: task.traceID,
        //error: e.code + '(' + e.syscall + ')'
        exception: {
          exceptionId: 'SVC Relayed Host Error',
          exceptionText: e.message
        }
      };

      logger.warning('Request Error', { op: 'DO JOB', userID: task.user, correlator: traceID,
        transid: task.id, error: e});
      doRetry(task, errObj, callback);
    });

    if (options.method === 'POST' || options.method === 'PUT') {
      //write body
      req.write(task.body);
    }
    req.end(); //?? sure HERE?
  }
}

function getResponse(resp, task, callback) {
  'use strict';

  var data = [];
  var length = 0;

  resp.on('data', function(chunk) {
    data.push(chunk);
    length += chunk.length;
  });

  resp.on('end', function(chunk) {
    if (chunk) {
      data.push(chunk);
      length += chunk.length;
    } //avoid tail undefined

    var body_encoding = task.headers[MG.HEAD_RELAYER_ENCODING];

    if (! body_encoding || MG.ACEPTS_ENCODINGS.indexOf(body_encoding) === - 1) {
      body_encoding = 'utf8';
    }

    var buf = new Buffer(length);
    for (var i = 0, pos = 0; i < data.length; i++) {
      data[i].copy(buf, pos);
      pos += data[i].length;
    }

    var encodedBody = buf.toString(body_encoding);

    var respObj = {
      id: task.id,
      traceID: task.traceID,
      statusCode: resp.statusCode,
      encoding: body_encoding,
      headers: resp.headers,
      body: encodedBody
    };

    if (callback) {
      callback(task, respObj);
    }
  });
}

function doRetry(task, error, callback) {
  'use strict';

  var retryList = task.headers[MG.HEAD_RELAYER_RETRY];
  var time = - 1;
  if (retryList) {
    var retryA = retryList.split(',');
    if (retryA.length > 0) {
      time = parseInt(retryA.shift(), 10);
      if (retryA.length > 0) {
        // there is retry times still
        task.headers[MG.HEAD_RELAYER_RETRY] = retryA.join(',');
      } else {
        //Retry End with no success
        delete task.headers[MG.HEAD_RELAYER_RETRY];
      }
      if (time > 0) {
        setTimeout(function() {
          doJob(task, callback);
        }, time);
      }
    }
  } else {

    if (callback) {
      callback(error, null);
    }
  }
}

function delXrelayerHeaders(headers) {
  'use strict';

  var cleanHeaders = {};
  for (var h in headers) {
    if (headers.hasOwnProperty(h)) {
      if (h.toLowerCase().indexOf('x-relayer') !== 0) {
        cleanHeaders[h] = headers[h];
      }
    }
  }
  return cleanHeaders;
}
exports.doJob = doJob;
exports.createTask = createTask;

//require('./hookLogger.js').init(exports, logger);
<|endoftext|>


<commit_before>function RequiredValidator() {}

Object.assign(RequiredValidator.prototype, {
  validate(value) {
    return !!value === true && value != "false";
  },

  errorMessage() {
    return "Required";
  }
});

export default function() {
  return new RequiredValidator();
};
<commit_msg>Fix required validator<commit_after>function RequiredValidator() {}

Object.assign(RequiredValidator.prototype, {
  validate(value) {
    return !!value === true;
  },

  errorMessage() {
    return "Required";
  }
});

export default function() {
  return new RequiredValidator();
};
<|endoftext|>


<commit_before>(function( window, document ) {
  'use strict';

  var head = document.head || document.getElementsByTagName('head')[0];

  //////////////////////////////////////////////////////////////////////////////
  // helpers

  function _nope() { return; }

  var _isString = function isString(obj) {
    return Object.prototype.toString.call(obj) === '[object String]';
  };

  var _isArray = Array.isArray || function isArray(obj) {
    return Object.prototype.toString.call(obj) === '[object Array]';
  };

  var _isFunction = function isFunction(obj) {
    return Object.prototype.toString.call(obj) === '[object Function]';
  };

  var _default = function (obj, src) {
    // extend obj with src properties if not exists;
    _each(src, function(val, key) {
      if (!obj[key]) { obj[key] = src[key]; }
    });
  };

  function _each(obj, iterator) {
    if (_isArray(obj)) {
      if (obj.forEach) {
        return obj.forEach(iterator);
      }
      for (var i = 0; i < obj.length; i++) {
        if (iterator(obj[i], i, obj) === false) {
          break;
        }
      }
    } else {
      for (var k in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, k)) {
          iterator(obj[k], k);
        }
      }
    }
  }

  function _asyncEach(arr, iterator, callback) {
    callback = callback || _nope;
    if (!arr.length) { return callback(); }

    var completed = 0;
    _each(arr, function (x) {
      iterator(x, function (err) {
        if (err) {
          callback(err);
          callback = _nope;
        } else {
          completed += 1;
          if (completed >= arr.length) {
            callback();
          }
        }
      });
    });
  }

  //////////////////////////////////////////////////////////////////////////////
  // Adapters for Store class

  var DomStorage = function (namespace) {
    this.ns = namespace + '__';
  };

  DomStorage.prototype.exists = function() {
    try {
      localStorage.setItem('__ls_test__','__ls_test__');
      localStorage.removeItem('__ls_test__');
      return true;
    } catch (e) {
      return false;
    }
  };

  DomStorage.prototype.init = function (callback) {
    callback();
  };

  DomStorage.prototype.remove = function (key, callback) {
    callback = callback || _nope;
    localStorage.removeItem(this.ns + key);
    callback();
  };

  DomStorage.prototype.set = function (key, value, expire, callback) {
    var obj = {};

    if (expire) {
      obj.expire = +(new Date()) + (expire * 1000);
    }
    obj.value = value;

    try {
      localStorage.setItem(this.ns + key, JSON.stringify(obj));
      return callback();
    } catch (e) {
      // FIXME: process possible errors & try cleanup + second attempt
      return callback(e);
    }
  };

  DomStorage.prototype.get = function (key, raw, callback) {
    if (_isFunction(raw)) {
      callback = raw;
      raw = false;
    }

    var obj = localStorage.getItem(this.ns + key);

    if (obj === null) {
      callback(new Error('key not found: ' + key));
      return;
    }

    try {
      var raw_data = JSON.parse(obj);
      return callback(null, raw ? raw_data : raw_data.value);
    } catch (e) {
      return callback(new Error('Can\'t unserialise data: ' + obj));
    }
  };

  DomStorage.prototype.clear = function (expiredOnly, callback) {
    var self = this;
    var now = +new Date();

    _each(localStorage, function(val, name) {
      var key = name.split(self.ns)[ 1 ];

      if (!key) { return; }

      if (!expiredOnly) {
        self.remove(key);
        return;
      }

      var raw;
      self.get(key, true, function(err, data) {
        raw = data; // can use this hack, because get is sync;
      });
      if (raw && (raw.expire > 0) && ((raw.expire - now) < 0)) {
        self.remove(key);
      }
    });

    callback();
  };

  /////////////////////////////////////////////////////////////////////////////
  // key/value storage with expiration

  var storeAdapters = {
    /*'indexeddb': IDB,
    'websql': WebSql,*/
    'localstorage': DomStorage
  };

  // namespace - db name or similar
  // storesList - array of allowed adapter names to use
  //
  var Storage = function (namespace, storesList) {
    var self = this;

    // States of db init singletone process
    // 'done' / 'progress' / 'failed' / undefined
    this.initState = undefined;
    this.initStack = [];

    _each(storesList, function(name) {
      // do storage names case insensitive
      name = name.toLowerCase();

      if (!storeAdapters[name]) {
        throw new Error('Wrong storage adapter name: ' + name, storesList);
      }

      if (storeAdapters[name].prototype.exists()) {
        self.db = new storeAdapters[name](namespace);
        return false; // terminate search on first success
      }
    });

    if (!self.db) {
      // If no adaprets - don't make error for correct fallback.
      // Just log that we continue work without storing results.
      console.log('None of requested storages available: ' + storesList);
    }
  };

  Storage.prototype.init = function (callback) {
    var self = this;

    if (!this.db) { return callback(new Error('No available db')); }

    if (this.initState === 'done') { return callback(); }

    if (this.initState === 'progress') {
      this.initStack.push(callback);
      return;
    }

    this.initStack.push(callback);
    this.initState = 'progress';

    this.db.init(function (err) {
      self.initState = !err ? 'done' : 'failed';
      _each(self.initStack, function (cb) {
        cb(err);
      });
      self.initStack = [];

      // Clear expired. A bit dirty without callback,
      // but we don't care until clear compleete
      if (!err) { self.clear(true); }
    });
  };

  Storage.prototype.set = function (key, value, expire, callback) {
    var self = this;
    if (_isFunction(expire)) {
      callback = expire;
      expire = undefined;
    }
    callback = callback || _nope;
    this.init(function(err) {
      if (err) { return callback(err); }
      self.db.set(key, value, expire, callback);
    });
  };

  Storage.prototype.get = function (key, callback) {
    var self = this;
    this.db.init(function(err) {
      if (err) { return callback(err); }
      self.db.get(key, callback);
    });
  };

  Storage.prototype.remove = function (key, callback) {
    var self = this;
    callback = callback || _nope;
    this.db.init(function(err) {
      if (err) { return callback(err); }
      self.db.remove(key, callback);
    });
  };

  Storage.prototype.clear = function (expiredOnly, callback) {
    var self = this;
    if (_isFunction(expiredOnly)) {
      callback = expiredOnly;
      expiredOnly = false;
    }
    callback = callback || _nope;

    this.db.init(function(err) {
      if (err) { return callback(err); }
      self.db.clear(expiredOnly, callback);
    });
  };

  //////////////////////////////////////////////////////////////////////////////
  // Bag class implementation

  function Bag(options) {

    var self = this;

    options = options || {};

    this.prefix       = options.namespace || 'bag';
    this.timeout      = options.timeout || 20;    // 20 seconds
    this.expire       = options.expire || 30*24;  // 30 days
    this.isValidItem  = options.isValidItem || null;

    this.stores = _isArray(options.stores) ? options.stores : [/*'indexeddb','websql',*/'localstorage'];

    var storage = null;

    function getUrl(url, callback) {
      var xhr = new XMLHttpRequest();
      xhr.open( 'GET', url );
      xhr.onreadystatechange = function() {
        if (xhr.readyState === 4) {
          if (xhr.status === 200) {
            callback(null, {
              content: xhr.responseText,
              type: xhr.getResponseHeader('content-type')
            });
            callback = _nope;
          } else {
            callback(new Error(xhr.statusText));
            callback = _nope;
          }
        }
      };

      setTimeout(function () {
        if (xhr.readyState < 4) {
          xhr.abort();
          callback(new Error('Timeout'));
          callback = _nope;
        }
      }, self.timeout * 1000);

      xhr.send();
    }

    function createCacheObj(obj, response) {
      var cacheObj = {};

      _each([ 'url', 'key', 'unique' ], function (key) {
        if (obj[key]) { cacheObj[key] = obj[key]; }
      });

      var now = +new Date();
      cacheObj.data = response.content;
      cacheObj.originalType = response.type;
      cacheObj.type = obj.type || response.type;
      cacheObj.stamp = now;

      return cacheObj;
    }

    function saveUrl(obj, callback) {
      getUrl(obj.url, function(err, result) {
        if (err) { return callback(err); }

        var delay = (obj.expire || self.expire) * 60*60; // in seconds

        var cached = createCacheObj(obj, result);

        self.set(obj.key, cached, delay, function() {
          // Don't check error - have to return data anyway
          _default(obj, cached);
          callback(null, obj);
        });
      });
    }

    function isCacheValid(cached, obj) {
      return !cached ||
        cached.expire - +new Date() < 0  ||
        obj.unique !== cached.unique ||
        (self.isValidItem && !self.isValidItem(cached, obj));
    }

    function fetch(obj, callback) {

      if (!obj.url) { return callback(); }
      obj.key = (obj.key || obj.url);

      self.get(obj.key, function(err_cache, cached) {

        // Check error only on forced fetch from cache
        if (err_cache && obj.cached) { return callback(err_cache); }

        // don't check errors here - if can't get object from store,
        // then just load it from web.
        obj.execute = (obj.execute !== false);
        var shouldFetch = !!err_cache || isCacheValid(cached, obj);

        // If don't have to load new date - return one from cache
        if (!obj.live && !shouldFetch) {
          obj.type = obj.type || cached.originalType;
          _default(obj, cached);
          callback(null, obj);
          return;
        }

        // start loading

        if (obj.unique) {
          // set parameter to prevent browser cache
          obj.url += ( ( obj.url.indexOf('?') > 0 ) ? '&' : '?' ) + 'bag-unique=' + obj.unique;
        }

        saveUrl(obj, function(err_load) {
          if (err_cache && err_load) { return callback(err_load); }

          if (err_load) {
            obj.type = obj.type || cached.originalType;
            _default(obj, cached);
            callback(null, obj);
            return;
          }

          callback(null, obj);
        });
      });
    }

    var handlers = {
      'application/javascript': function injectScript(obj) {
        var script = document.createElement('script');

        // add script name for dev tools
        var txt = obj.data + '\n//@ sourceURL=' + obj.url;

        // Have to use .text, since we support IE8,
        // which won't allow appending to a script
        script.text = txt;
        head.appendChild(script);
        return;
      },

      'text/css': function injectStyle(obj) {
        var style = document.createElement('style');

        // add style name for dev tools
        var txt = obj.data + '\n/*# sourceURL=' + obj.url + '<url> */';

        if (style.styleSheet) {
          style.styleSheet.cssText = txt; // IE method
        } else {
          style.appendChild(document.createTextNode(txt)); // others
        }

        head.appendChild(style);
        return;
      }
    };

    function execute(obj) {
      if (obj.type && handlers[obj.type]) {
        return handlers[obj.type](obj);
      }
    }

    //
    // Public methods
    //

    this.require = function(resourses, callback) {
      var res = _isArray(resourses) ? resourses : [resourses];

      // convert string urls to structures
      _each(res, function(r, i) {
        if (_isString(r)) { res[i] = { url: r }; }
      });

      if (!storage) { storage = new Storage(self.prefix, self.stores); }

      _asyncEach(res, fetch, function(err) {
        if (err) { return callback(err); }

        _each(res, function(obj) {
          if (obj.execute) {
            execute(obj);
          }
        });

        // return content only, if one need fuul info -
        // chaeck input object, that will be extended.
        var replies = [];
        _each(res, function(r) { replies.push(r.data); });

        callback(null, _isArray(resourses) ? replies : replies[0]);
      });
    };

    this.remove = function (key, callback) {
      if (!storage) { storage = new Storage(self.prefix, self.stores); }
      storage.remove(key, callback);
    };

    this.get = function (key, callback) {
      if (!storage) { storage = new Storage(self.prefix, self.stores); }
      storage.get(key, callback);
    };

    this.set = function (key, data, expire, callback) {
      if (!storage) { storage = new Storage(self.prefix, self.stores); }
      storage.set(key, data, expire, callback);
    };

    this.clear = function (expired, callback) {
      if (!storage) { storage = new Storage(self.prefix, self.stores); }
      storage.clear(expired, callback);
    };

    this.addHandler = function (types, handler) {
      types = _isArray(types) ? types : [types];
      _each(types, function (type) { handlers[type] = handler; });
    };

    this.removeHandler = function (types) {
      self.addHandler(types, undefined);
    };
  }

  window.Bag = Bag;

})(this, document);
<commit_msg>Added localStorage quota check<commit_after>(function( window, document ) {
  'use strict';

  var head = document.head || document.getElementsByTagName('head')[0];

  //////////////////////////////////////////////////////////////////////////////
  // helpers

  function _nope() { return; }

  var _isString = function isString(obj) {
    return Object.prototype.toString.call(obj) === '[object String]';
  };

  var _isArray = Array.isArray || function isArray(obj) {
    return Object.prototype.toString.call(obj) === '[object Array]';
  };

  var _isFunction = function isFunction(obj) {
    return Object.prototype.toString.call(obj) === '[object Function]';
  };

  var _default = function (obj, src) {
    // extend obj with src properties if not exists;
    _each(src, function(val, key) {
      if (!obj[key]) { obj[key] = src[key]; }
    });
  };

  function _each(obj, iterator) {
    if (_isArray(obj)) {
      if (obj.forEach) {
        return obj.forEach(iterator);
      }
      for (var i = 0; i < obj.length; i++) {
        if (iterator(obj[i], i, obj) === false) {
          break;
        }
      }
    } else {
      for (var k in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, k)) {
          iterator(obj[k], k);
        }
      }
    }
  }

  function _asyncEach(arr, iterator, callback) {
    callback = callback || _nope;
    if (!arr.length) { return callback(); }

    var completed = 0;
    _each(arr, function (x) {
      iterator(x, function (err) {
        if (err) {
          callback(err);
          callback = _nope;
        } else {
          completed += 1;
          if (completed >= arr.length) {
            callback();
          }
        }
      });
    });
  }

  //////////////////////////////////////////////////////////////////////////////
  // Adapters for Store class

  var DomStorage = function (namespace) {
    this.ns = namespace + '__';
  };

  DomStorage.prototype.exists = function() {
    try {
      localStorage.setItem('__ls_test__','__ls_test__');
      localStorage.removeItem('__ls_test__');
      return true;
    } catch (e) {
      return false;
    }
  };

  DomStorage.prototype.init = function (callback) {
    callback();
  };

  DomStorage.prototype.remove = function (key, callback) {
    callback = callback || _nope;
    localStorage.removeItem(this.ns + key);
    callback();
  };

  DomStorage.prototype.set = function (key, value, expire, callback) {
    var self = this;
    var obj = {};

    if (expire) {
      obj.expire = +(new Date()) + (expire * 1000);
    }
    obj.value = value;

    try {
      localStorage.setItem(self.ns + key, JSON.stringify(obj));
      return callback();
    } catch (e) {
      // On quota error try to reset storage & try again.
      // Just remove all keys, without conditions, no optimizations needed.
      if ( e.name.toUpperCase().indexOf('QUOTA') >= 0 ) {
        try {
          _each(localStorage, function(val, name) {
            var key = name.split(self.ns)[ 1 ];
            if (key) { self.remove(key); }
          });
          localStorage.setItem(self.ns + key, JSON.stringify(obj));
          return callback();
        } catch (e) {
          return callback(e);
        }
      }
      return callback(e);
    }
  };

  DomStorage.prototype.get = function (key, raw, callback) {
    if (_isFunction(raw)) {
      callback = raw;
      raw = false;
    }

    var obj = localStorage.getItem(this.ns + key);

    if (obj === null) {
      callback(new Error('key not found: ' + key));
      return;
    }

    try {
      var raw_data = JSON.parse(obj);
      return callback(null, raw ? raw_data : raw_data.value);
    } catch (e) {
      return callback(new Error('Can\'t unserialise data: ' + obj));
    }
  };

  DomStorage.prototype.clear = function (expiredOnly, callback) {
    var self = this;
    var now = +new Date();

    _each(localStorage, function(val, name) {
      var key = name.split(self.ns)[ 1 ];

      if (!key) { return; }

      if (!expiredOnly) {
        self.remove(key);
        return;
      }

      var raw;
      self.get(key, true, function(err, data) {
        raw = data; // can use this hack, because get is sync;
      });
      if (raw && (raw.expire > 0) && ((raw.expire - now) < 0)) {
        self.remove(key);
      }
    });

    callback();
  };

  /////////////////////////////////////////////////////////////////////////////
  // key/value storage with expiration

  var storeAdapters = {
    /*'indexeddb': IDB,
    'websql': WebSql,*/
    'localstorage': DomStorage
  };

  // namespace - db name or similar
  // storesList - array of allowed adapter names to use
  //
  var Storage = function (namespace, storesList) {
    var self = this;

    // States of db init singletone process
    // 'done' / 'progress' / 'failed' / undefined
    this.initState = undefined;
    this.initStack = [];

    _each(storesList, function(name) {
      // do storage names case insensitive
      name = name.toLowerCase();

      if (!storeAdapters[name]) {
        throw new Error('Wrong storage adapter name: ' + name, storesList);
      }

      if (storeAdapters[name].prototype.exists()) {
        self.db = new storeAdapters[name](namespace);
        return false; // terminate search on first success
      }
    });

    if (!self.db) {
      // If no adaprets - don't make error for correct fallback.
      // Just log that we continue work without storing results.
      console.log('None of requested storages available: ' + storesList);
    }
  };

  Storage.prototype.init = function (callback) {
    var self = this;

    if (!this.db) { return callback(new Error('No available db')); }

    if (this.initState === 'done') { return callback(); }

    if (this.initState === 'progress') {
      this.initStack.push(callback);
      return;
    }

    this.initStack.push(callback);
    this.initState = 'progress';

    this.db.init(function (err) {
      self.initState = !err ? 'done' : 'failed';
      _each(self.initStack, function (cb) {
        cb(err);
      });
      self.initStack = [];

      // Clear expired. A bit dirty without callback,
      // but we don't care until clear compleete
      if (!err) { self.clear(true); }
    });
  };

  Storage.prototype.set = function (key, value, expire, callback) {
    var self = this;
    if (_isFunction(expire)) {
      callback = expire;
      expire = undefined;
    }
    callback = callback || _nope;
    this.init(function(err) {
      if (err) { return callback(err); }
      self.db.set(key, value, expire, callback);
    });
  };

  Storage.prototype.get = function (key, callback) {
    var self = this;
    this.db.init(function(err) {
      if (err) { return callback(err); }
      self.db.get(key, callback);
    });
  };

  Storage.prototype.remove = function (key, callback) {
    var self = this;
    callback = callback || _nope;
    this.db.init(function(err) {
      if (err) { return callback(err); }
      self.db.remove(key, callback);
    });
  };

  Storage.prototype.clear = function (expiredOnly, callback) {
    var self = this;
    if (_isFunction(expiredOnly)) {
      callback = expiredOnly;
      expiredOnly = false;
    }
    callback = callback || _nope;

    this.db.init(function(err) {
      if (err) { return callback(err); }
      self.db.clear(expiredOnly, callback);
    });
  };

  //////////////////////////////////////////////////////////////////////////////
  // Bag class implementation

  function Bag(options) {

    var self = this;

    options = options || {};

    this.prefix       = options.namespace || 'bag';
    this.timeout      = options.timeout || 20;    // 20 seconds
    this.expire       = options.expire || 30*24;  // 30 days
    this.isValidItem  = options.isValidItem || null;

    this.stores = _isArray(options.stores) ? options.stores : [/*'indexeddb','websql',*/'localstorage'];

    var storage = null;

    function getUrl(url, callback) {
      var xhr = new XMLHttpRequest();
      xhr.open( 'GET', url );
      xhr.onreadystatechange = function() {
        if (xhr.readyState === 4) {
          if (xhr.status === 200) {
            callback(null, {
              content: xhr.responseText,
              type: xhr.getResponseHeader('content-type')
            });
            callback = _nope;
          } else {
            callback(new Error(xhr.statusText));
            callback = _nope;
          }
        }
      };

      setTimeout(function () {
        if (xhr.readyState < 4) {
          xhr.abort();
          callback(new Error('Timeout'));
          callback = _nope;
        }
      }, self.timeout * 1000);

      xhr.send();
    }

    function createCacheObj(obj, response) {
      var cacheObj = {};

      _each([ 'url', 'key', 'unique' ], function (key) {
        if (obj[key]) { cacheObj[key] = obj[key]; }
      });

      var now = +new Date();
      cacheObj.data = response.content;
      cacheObj.originalType = response.type;
      cacheObj.type = obj.type || response.type;
      cacheObj.stamp = now;

      return cacheObj;
    }

    function saveUrl(obj, callback) {
      getUrl(obj.url, function(err, result) {
        if (err) { return callback(err); }

        var delay = (obj.expire || self.expire) * 60*60; // in seconds

        var cached = createCacheObj(obj, result);

        self.set(obj.key, cached, delay, function() {
          // Don't check error - have to return data anyway
          _default(obj, cached);
          callback(null, obj);
        });
      });
    }

    function isCacheValid(cached, obj) {
      return !cached ||
        cached.expire - +new Date() < 0  ||
        obj.unique !== cached.unique ||
        (self.isValidItem && !self.isValidItem(cached, obj));
    }

    function fetch(obj, callback) {

      if (!obj.url) { return callback(); }
      obj.key = (obj.key || obj.url);

      self.get(obj.key, function(err_cache, cached) {

        // Check error only on forced fetch from cache
        if (err_cache && obj.cached) { return callback(err_cache); }

        // don't check errors here - if can't get object from store,
        // then just load it from web.
        obj.execute = (obj.execute !== false);
        var shouldFetch = !!err_cache || isCacheValid(cached, obj);

        // If don't have to load new date - return one from cache
        if (!obj.live && !shouldFetch) {
          obj.type = obj.type || cached.originalType;
          _default(obj, cached);
          callback(null, obj);
          return;
        }

        // start loading

        if (obj.unique) {
          // set parameter to prevent browser cache
          obj.url += ( ( obj.url.indexOf('?') > 0 ) ? '&' : '?' ) + 'bag-unique=' + obj.unique;
        }

        saveUrl(obj, function(err_load) {
          if (err_cache && err_load) { return callback(err_load); }

          if (err_load) {
            obj.type = obj.type || cached.originalType;
            _default(obj, cached);
            callback(null, obj);
            return;
          }

          callback(null, obj);
        });
      });
    }

    var handlers = {
      'application/javascript': function injectScript(obj) {
        var script = document.createElement('script');

        // add script name for dev tools
        var txt = obj.data + '\n//@ sourceURL=' + obj.url;

        // Have to use .text, since we support IE8,
        // which won't allow appending to a script
        script.text = txt;
        head.appendChild(script);
        return;
      },

      'text/css': function injectStyle(obj) {
        var style = document.createElement('style');

        // add style name for dev tools
        var txt = obj.data + '\n/*# sourceURL=' + obj.url + '<url> */';

        if (style.styleSheet) {
          style.styleSheet.cssText = txt; // IE method
        } else {
          style.appendChild(document.createTextNode(txt)); // others
        }

        head.appendChild(style);
        return;
      }
    };

    function execute(obj) {
      if (obj.type && handlers[obj.type]) {
        return handlers[obj.type](obj);
      }
    }

    //
    // Public methods
    //

    this.require = function(resourses, callback) {
      var res = _isArray(resourses) ? resourses : [resourses];

      // convert string urls to structures
      _each(res, function(r, i) {
        if (_isString(r)) { res[i] = { url: r }; }
      });

      if (!storage) { storage = new Storage(self.prefix, self.stores); }

      _asyncEach(res, fetch, function(err) {
        if (err) { return callback(err); }

        _each(res, function(obj) {
          if (obj.execute) {
            execute(obj);
          }
        });

        // return content only, if one need fuul info -
        // check input object, that will be extended.
        var replies = [];
        _each(res, function(r) { replies.push(r.data); });

        callback(null, _isArray(resourses) ? replies : replies[0]);
      });
    };

    this.remove = function (key, callback) {
      if (!storage) { storage = new Storage(self.prefix, self.stores); }
      storage.remove(key, callback);
    };

    this.get = function (key, callback) {
      if (!storage) { storage = new Storage(self.prefix, self.stores); }
      storage.get(key, callback);
    };

    this.set = function (key, data, expire, callback) {
      if (!storage) { storage = new Storage(self.prefix, self.stores); }
      storage.set(key, data, expire, callback);
    };

    this.clear = function (expired, callback) {
      if (!storage) { storage = new Storage(self.prefix, self.stores); }
      storage.clear(expired, callback);
    };

    this.addHandler = function (types, handler) {
      types = _isArray(types) ? types : [types];
      _each(types, function (type) { handlers[type] = handler; });
    };

    this.removeHandler = function (types) {
      self.addHandler(types, undefined);
    };
  }

  window.Bag = Bag;

})(this, document);
<|endoftext|>


<commit_before>/*

@param menuTrigger: One or more elements that have menus attached. Typically
this will be .va-btn-sidebarnav-trigger
*/

class SideBarMenu {
   constructor(menuTrigger) {
    this.menuTrigger = Array.from(menuTrigger);
    this.init = this.init.bind(this);
    this.getMenu = this.getMenu.bind(this);
    this.openMenu = this.openMenu.bind(this);
    this.closeMenu = this.closeMenu.bind(this);
    this.menu = null;
    this.init();
  }

  init() {
    this.menuTrigger.map((mt) => {
      mt.addEventListener('click', (domEvent) => {
        this.openMenu(domEvent.currentTarget);
      });
    });
  }

  getMenu(element) {
    const el = document.querySelector(`#${element.getAttribute('aria-controls')}`);
    this.menu = el;
    return this.menu;
  }

  openMenu(trigger) {
    this.getMenu(trigger).classList.add('va-sidebarnav--opened');
    document.body.classList.add('va-pos-fixed');
    this.closeMenu(trigger);
  }

  closeMenu(trigger) {
    const close = this.menu.querySelector('.va-sidebarnav-close');
    close.addEventListener('click', (domEvent) => {
        this.menu.classList.remove('va-sidebarnav--opened');
        document.body.classList.remove('va-pos-fixed');
    })
  }
}

document.addEventListener('DOMContentLoaded', () => {
  new SideBarMenu(document.querySelectorAll('.va-btn-sidebarnav-trigger'));
});
<commit_msg>Fix for indent level<commit_after>/*

@param menuTrigger: One or more elements that have menus attached. Typically
this will be .va-btn-sidebarnav-trigger
*/

class SideBarMenu {
   constructor(menuTrigger) {
    this.menuTrigger = Array.from(menuTrigger);
    this.init = this.init.bind(this);
    this.getMenu = this.getMenu.bind(this);
    this.openMenu = this.openMenu.bind(this);
    this.closeMenu = this.closeMenu.bind(this);
    this.menu = null;
    this.init();
  }

  init() {
    this.menuTrigger.map((mt) => {
      mt.addEventListener('click', (domEvent) => {
        this.openMenu(domEvent.currentTarget);
      });
    });
  }

  getMenu(element) {
    const el = document.querySelector(`#${element.getAttribute('aria-controls')}`);
    this.menu = el;
    return this.menu;
  }

  openMenu(trigger) {
    this.getMenu(trigger).classList.add('va-sidebarnav--opened');
    document.body.classList.add('va-pos-fixed');
    this.closeMenu(trigger);
  }

  closeMenu(trigger) {
    const close = this.menu.querySelector('.va-sidebarnav-close');
    close.addEventListener('click', (domEvent) => {
      this.menu.classList.remove('va-sidebarnav--opened');
      document.body.classList.remove('va-pos-fixed');
    })
  }
}

document.addEventListener('DOMContentLoaded', () => {
  new SideBarMenu(document.querySelectorAll('.va-btn-sidebarnav-trigger'));
});
<|endoftext|>


<commit_before>// exceptional.js
// standard javascript functions which throw errors instead of return codes
/* global window */
/* jshint maxparams: 5 */

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

	var debug = require('debug')('exceptional');

	function _methodNamesOf (object) {
		var keys = [];
		Object.keys(object).forEach(function (type) {
			if (type.match(/^[a-z]/i) && 'function' === typeof object[type]) {
				keys.push(type);
			}
		});
		return keys.sort();
	}

	function _objectNamesOf (object) {
		var keys = [];
		Object.keys(object).forEach(function (type) {
			if (type.match(/^[a-z]/i) && 'object' === typeof object[type]) {
				keys.push(type);
			}
		});
		return keys.sort();
	}

	function _makeInstanceMethod (handler) {
		var method = function () {
			var args = Array.prototype.slice.call(arguments);
			args.unshift(this);
			return handler.apply(null, args);
		};
		return method;
	}

	function _makeStaticMethod (handler) {
		var method = function () {
			var args = Array.prototype.slice.call(arguments);
			return handler.apply(null, args);
		};
		return method;
	}

	function _targetObjectOf (name) {
		var maker = {
			makeMethod: _makeStaticMethod
		};
		if (name === 'global') {
			maker.target = global;
		}
		else if (name === 'Math') {
			maker.target = Math;
		}
		else {
			maker.target = global[name].prototype;
			maker.makeMethod = _makeInstanceMethod;
		}
		return maker;
	}

	var self = exports.exceptional = {

		'augment': function (typeName) {
			/* jshint maxcomplexity: 5 */
			if (!typeName) {
				_objectNamesOf(exports.exceptional).forEach(function (type) {
					exports.exceptional.augment(type);
				});
				return;
			}

			if (typeName === 'window') {
				typeName = 'global';
			}

			var maker, methods = exports.exceptional[typeName];
			if ('object' !== typeof methods) {
				throw new TypeError('cannot augment ' + typeName +
					'.prototype with non-object: ' + typeof methods);
			}

			maker = _targetObjectOf(typeName);
			_methodNamesOf(methods).forEach(function (method) {
				var _super = method + 'Ex';

				debug('attach ', typeName, _super, _super in maker.target);
				if (maker.target.hasOwnProperty(_super)) {
					throw new TypeError('cannot augment ' + typeName +
							'.prototype with method ' + _super +
							' as that would overwrite an existing method');
				}
				maker.target[_super] = maker.makeMethod(methods[method]);
			});
			return maker.target;
		},

		'unaugment': function (typeName) {
			/* jshint maxcomplexity: 5 */
			if (!typeName) {
				_objectNamesOf(exports.exceptional).forEach(function (type) {
					exports.exceptional.unaugment(type);
				});
				return;
			}

			if (typeName === 'window') {
				typeName = 'global';
			}

			var maker, methods = exports.exceptional[typeName];
			if ('object' !== typeof methods) {
				throw new TypeError('cannot unaugment ' + typeName +
					'.prototype');
			}

			maker = _targetObjectOf(typeName);
			_methodNamesOf(methods).forEach(function (method) {
				var _super = method + 'Ex';

				debug('delete ', typeName, _super, _super in maker.target);
				if (_super in maker.target) {
					delete maker.target[_super];
				}
			});
			return maker.target;
		},

		'TypeError': {
			throw: function (value, type, param, method) {
				method = method? (method + '() ') : '';
				param = param? (param + ' ') : '';
				throw new TypeError(method + param + 'argument <' + value +
					'> must be ' + type);
			},
		},

		'RangeError': {
			throw: function (value, type, param, method) {
				method = method? (method + '() ') : '';
				param = param? (param + ' ') : '';
				throw new RangeError(method + param + 'argument <' + value +
						'> must be ' + type);
			},

			throwIfInvalid: function (value, param, method) {
				/* jshint maxcomplexity: 7 */
				var message, Type;
				if (typeof value !== 'number') {
					Type = TypeError;
					message = '> must be a finite number';
				}
				else if (typeof value !== 'number' || !isFinite(value)) {
					Type = RangeError;
					message = '> must be a finite number';
				}
				if (Type) {
					method = method? (method + '() ') : '';
					param = param? (param + ' ') : '';
					throw new Type(method + param +
						'argument <' + value + message);
				}
				return value;
			},

			throwIfOutOfRange: function (value, min, max, param, method) {
				/* jshint maxcomplexity: 10 */
				var message;

				self.RangeError.throwIfInvalid(value, param, method);
				if ((min === void 0 || min === null) && value > max) {
					message = '> must be at most ' + max;
				}
				else if ((max === void 0 || max === null) && value < min) {
					message = '> must be at least ' + min;
				}
				else if (value < min || value > max) {
					message = '> must be between ' + min + ' and ' + max;
				}
				if (message) {
					method = method? (method + '() ') : '';
					param = param? (param + ' ') : '';
					throw new RangeError(method + param +
						'argument <' + value + message);
				}
				return value;
			}
		},

		global: {
			parseFloat: function (string) {
				var value = parseFloat(string);
				if (isNaN(value)) {
					self.TypeError.throw(string, 'a string representing a ' +
							'floating point number', 'string', 'parseFloatEx');
				}
				if (!isFinite(value)) {
					self.RangeError.throw(value, 'a string representing a ' +
							'finite floating point number', 'string', 'parseFloatEx');
				}
				return value;
			},

			parseInt: function (string, radix) {
				var value = parseInt(string, radix);
				self.RangeError.throwIfOutOfRange(radix, 2, 36, 'radix', 'parseIntEx');
				if (isNaN(value)) {
					self.TypeError.throw(string, 'a string representing an ' +
							'integer number', 'string', 'parseIntEx');
				}
				return value;
			},

			isNaN: function (value) {
				if (isNaN(value)) {
					throw new RangeError('Not a Number (NaN)');
				}
				return value;
			},

			isFinite: function (value) {
				if (!isFinite(value)) {
					throw new RangeError('Not a finite number <' + value + '>');
				}
				return value;
			},

			'-': '-'
		},

		'String': {
			charAt: function (str, index) {
				self.RangeError.throwIfOutOfRange(index, 0,
					Math.max(0, str.length - 1), 'index', 'charAtEx');
				return str.charAt(index);
			},

			charCodeAt: function (str, index) {
				self.RangeError.throwIfOutOfRange(index, 0,
					Math.max(0, str.length - 1), 'index', 'charCodeAtEx');
				return str.charCodeAt(index);
			},

			indexOf: function (str, find) {
				var where = str.indexOf(find);
				if (-1 === where) {
					throw new ReferenceError('indexOf() find argument <' +
						find + '> not found within target string');
				}
				return where;
			},

			lastIndexOf: function (str, find) {
				var where = str.lastIndexOf(find);
				if (-1 === where) {
					throw new ReferenceError('lastIndexOf() find argument <' +
						find + '> not found within target string');
				}
				return where;
			},

			match: function (str, regex) {
				var matches = str.match(regex);
				if (!matches) {
					throw new ReferenceError('matchEx() regex argument <' +
						regex + '> not found within target string');
				}
				return matches;
			},

			replace: function (str, regex, replace, flags) {
				var matched = false,
					fnReplace = typeof replace === 'function' ?
						function () {
							matched = true;
							return replace.apply(this, arguments);
						} :
						function () {
							matched = true;
							return replace;
						},
					replacement = str.replace(regex, fnReplace, flags);

				if (!matched) {
					throw new ReferenceError('replaceEx() regex argument <' +
						regex + '> not found within target string');
				}
				return replacement;
			},

			search: function (str, regex) {
				var where = str.search(regex);
				if (-1 === where) {
					throw new ReferenceError('searchEx() regex argument <' +
						regex + '> not found within target string');
				}
				return where;
			},

			slice: function (str, beginSlice, endSlice) {
				/* jshint maxcomplexity: 6 */
				var max = Math.max(0, str.length - 1);
				self.RangeError.throwIfOutOfRange(beginSlice, -str.length,
					max, 'beginSlice', 'sliceEx');
				if (endSlice !== void 0 && endSlice !== null) {
					self.RangeError.throwIfOutOfRange(endSlice, -str.length,
						max, 'endSlice', 'sliceEx');
				}

				var slice = str.slice(beginSlice, endSlice);
				if (slice === '') {
					throw new ReferenceError('sliceEx() endpoints return nothing ' +
						'from the string');
				}
				return slice;
			},

			split: function (str, separator, limit) {
				var split = str.split(separator, limit);
				if (split.length < 1 || split.length === 1 && limit !== 1) {
					throw new ReferenceError('splitEx() separator argument <'
						+ separator + '> not found in string');
				}
				return split;
			},

			substr: function (str, start, length) {
				/* jshint maxcomplexity: 6 */
				var max = Math.max(0, str.length - 1);
				self.RangeError.throwIfOutOfRange(start, -str.length,
					max, 'start', 'substrEx');
				max = Math.max(1, str.length);
				if (length !== void 0 && length !== null) {
					self.RangeError.throwIfOutOfRange(length, 1,
							max, 'length', 'substrEx');
				}

				var substr = str.substr(start, length);
				if (substr === '') {
					throw new ReferenceError('substrEx() length argument ' +
						'references nothing from the string: ' + length);
				}
				if (length && substr.length !== length) {
					throw new ReferenceError('substrEx() start and length ' +
						'argument combination lies outside the string: ' +
							start + ', ' + length);
				}
				return substr;
			},

			substring: function (str, beginIndex, endIndex) {
				var max = Math.max(0, str.length - 1);
				self.RangeError.throwIfOutOfRange(beginIndex, 0,
					max, 'beginIndex', 'substringEx');
				max += 1;
				if (endIndex === void 0 || endIndex === null) {
					endIndex = max;
				}
				self.RangeError.throwIfOutOfRange(endIndex, 0,
					max, 'endIndex', 'substringEx');

				var substring = str.substring(beginIndex, endIndex);
				if (substring === '') {
					throw new ReferenceError('substringEx() endIndex argument ' +
						'references nothing from the string ' + endIndex);
				}
				return substring;
			},

			'-': '-'
		},

		Math: {
			acos: function (value) {
				var result = Math.acos(value);
				if (!isFinite(result)) {
					self.RangeError.throwIfOutOfRange(value, -1, 1, 'value', 'acosEx');
				}
				return result;
			},

			'-': '-'
		},

		'-': '-'
	};
})(typeof module === 'object' && typeof module.exports === 'object' ?
		module.exports : window, typeof global === 'object' ? global: window);
<commit_msg>Turn off debug in augment/unaugment<commit_after>// exceptional.js
// standard javascript functions which throw errors instead of return codes
/* global window */
/* jshint maxparams: 5 */

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

//	var debug = require('debug')('exceptional');

	function _methodNamesOf (object) {
		var keys = [];
		Object.keys(object).forEach(function (type) {
			if (type.match(/^[a-z]/i) && 'function' === typeof object[type]) {
				keys.push(type);
			}
		});
		return keys.sort();
	}

	function _objectNamesOf (object) {
		var keys = [];
		Object.keys(object).forEach(function (type) {
			if (type.match(/^[a-z]/i) && 'object' === typeof object[type]) {
				keys.push(type);
			}
		});
		return keys.sort();
	}

	function _makeInstanceMethod (handler) {
		var method = function () {
			var args = Array.prototype.slice.call(arguments);
			args.unshift(this);
			return handler.apply(null, args);
		};
		return method;
	}

	function _makeStaticMethod (handler) {
		var method = function () {
			var args = Array.prototype.slice.call(arguments);
			return handler.apply(null, args);
		};
		return method;
	}

	function _targetObjectOf (name) {
		var maker = {
			makeMethod: _makeStaticMethod
		};
		if (name === 'global') {
			maker.target = global;
		}
		else if (name === 'Math') {
			maker.target = Math;
		}
		else {
			maker.target = global[name].prototype;
			maker.makeMethod = _makeInstanceMethod;
		}
		return maker;
	}

	var self = exports.exceptional = {

		'augment': function (typeName) {
			/* jshint maxcomplexity: 5 */
			if (!typeName) {
				_objectNamesOf(exports.exceptional).forEach(function (type) {
					exports.exceptional.augment(type);
				});
				return;
			}

			if (typeName === 'window') {
				typeName = 'global';
			}

			var maker, methods = exports.exceptional[typeName];
			if ('object' !== typeof methods) {
				throw new TypeError('cannot augment ' + typeName +
					'.prototype with non-object: ' + typeof methods);
			}

			maker = _targetObjectOf(typeName);
			_methodNamesOf(methods).forEach(function (method) {
				var _super = method + 'Ex';

				//debug('attach ', typeName, _super, _super in maker.target);
				if (maker.target.hasOwnProperty(_super)) {
					throw new TypeError('cannot augment ' + typeName +
							'.prototype with method ' + _super +
							' as that would overwrite an existing method');
				}
				maker.target[_super] = maker.makeMethod(methods[method]);
			});
			return maker.target;
		},

		'unaugment': function (typeName) {
			/* jshint maxcomplexity: 5 */
			if (!typeName) {
				_objectNamesOf(exports.exceptional).forEach(function (type) {
					exports.exceptional.unaugment(type);
				});
				return;
			}

			if (typeName === 'window') {
				typeName = 'global';
			}

			var maker, methods = exports.exceptional[typeName];
			if ('object' !== typeof methods) {
				throw new TypeError('cannot unaugment ' + typeName +
					'.prototype');
			}

			maker = _targetObjectOf(typeName);
			_methodNamesOf(methods).forEach(function (method) {
				var _super = method + 'Ex';

				//debug('delete ', typeName, _super, _super in maker.target);
				if (_super in maker.target) {
					delete maker.target[_super];
				}
			});
			return maker.target;
		},

		'TypeError': {
			throw: function (value, type, param, method) {
				method = method? (method + '() ') : '';
				param = param? (param + ' ') : '';
				throw new TypeError(method + param + 'argument <' + value +
					'> must be ' + type);
			},
		},

		'RangeError': {
			throw: function (value, type, param, method) {
				method = method? (method + '() ') : '';
				param = param? (param + ' ') : '';
				throw new RangeError(method + param + 'argument <' + value +
						'> must be ' + type);
			},

			throwIfInvalid: function (value, param, method) {
				/* jshint maxcomplexity: 7 */
				var message, Type;
				if (typeof value !== 'number') {
					Type = TypeError;
					message = '> must be a finite number';
				}
				else if (typeof value !== 'number' || !isFinite(value)) {
					Type = RangeError;
					message = '> must be a finite number';
				}
				if (Type) {
					method = method? (method + '() ') : '';
					param = param? (param + ' ') : '';
					throw new Type(method + param +
						'argument <' + value + message);
				}
				return value;
			},

			throwIfOutOfRange: function (value, min, max, param, method) {
				/* jshint maxcomplexity: 10 */
				var message;

				self.RangeError.throwIfInvalid(value, param, method);
				if ((min === void 0 || min === null) && value > max) {
					message = '> must be at most ' + max;
				}
				else if ((max === void 0 || max === null) && value < min) {
					message = '> must be at least ' + min;
				}
				else if (value < min || value > max) {
					message = '> must be between ' + min + ' and ' + max;
				}
				if (message) {
					method = method? (method + '() ') : '';
					param = param? (param + ' ') : '';
					throw new RangeError(method + param +
						'argument <' + value + message);
				}
				return value;
			}
		},

		global: {
			parseFloat: function (string) {
				var value = parseFloat(string);
				if (isNaN(value)) {
					self.TypeError.throw(string, 'a string representing a ' +
							'floating point number', 'string', 'parseFloatEx');
				}
				if (!isFinite(value)) {
					self.RangeError.throw(value, 'a string representing a ' +
							'finite floating point number', 'string', 'parseFloatEx');
				}
				return value;
			},

			parseInt: function (string, radix) {
				var value = parseInt(string, radix);
				self.RangeError.throwIfOutOfRange(radix, 2, 36, 'radix', 'parseIntEx');
				if (isNaN(value)) {
					self.TypeError.throw(string, 'a string representing an ' +
							'integer number', 'string', 'parseIntEx');
				}
				return value;
			},

			isNaN: function (value) {
				if (isNaN(value)) {
					throw new RangeError('Not a Number (NaN)');
				}
				return value;
			},

			isFinite: function (value) {
				if (!isFinite(value)) {
					throw new RangeError('Not a finite number <' + value + '>');
				}
				return value;
			},

			'-': '-'
		},

		'String': {
			charAt: function (str, index) {
				self.RangeError.throwIfOutOfRange(index, 0,
					Math.max(0, str.length - 1), 'index', 'charAtEx');
				return str.charAt(index);
			},

			charCodeAt: function (str, index) {
				self.RangeError.throwIfOutOfRange(index, 0,
					Math.max(0, str.length - 1), 'index', 'charCodeAtEx');
				return str.charCodeAt(index);
			},

			indexOf: function (str, find) {
				var where = str.indexOf(find);
				if (-1 === where) {
					throw new ReferenceError('indexOf() find argument <' +
						find + '> not found within target string');
				}
				return where;
			},

			lastIndexOf: function (str, find) {
				var where = str.lastIndexOf(find);
				if (-1 === where) {
					throw new ReferenceError('lastIndexOf() find argument <' +
						find + '> not found within target string');
				}
				return where;
			},

			match: function (str, regex) {
				var matches = str.match(regex);
				if (!matches) {
					throw new ReferenceError('matchEx() regex argument <' +
						regex + '> not found within target string');
				}
				return matches;
			},

			replace: function (str, regex, replace, flags) {
				var matched = false,
					fnReplace = typeof replace === 'function' ?
						function () {
							matched = true;
							return replace.apply(this, arguments);
						} :
						function () {
							matched = true;
							return replace;
						},
					replacement = str.replace(regex, fnReplace, flags);

				if (!matched) {
					throw new ReferenceError('replaceEx() regex argument <' +
						regex + '> not found within target string');
				}
				return replacement;
			},

			search: function (str, regex) {
				var where = str.search(regex);
				if (-1 === where) {
					throw new ReferenceError('searchEx() regex argument <' +
						regex + '> not found within target string');
				}
				return where;
			},

			slice: function (str, beginSlice, endSlice) {
				/* jshint maxcomplexity: 6 */
				var max = Math.max(0, str.length - 1);
				self.RangeError.throwIfOutOfRange(beginSlice, -str.length,
					max, 'beginSlice', 'sliceEx');
				if (endSlice !== void 0 && endSlice !== null) {
					self.RangeError.throwIfOutOfRange(endSlice, -str.length,
						max, 'endSlice', 'sliceEx');
				}

				var slice = str.slice(beginSlice, endSlice);
				if (slice === '') {
					throw new ReferenceError('sliceEx() endpoints return nothing ' +
						'from the string');
				}
				return slice;
			},

			split: function (str, separator, limit) {
				var split = str.split(separator, limit);
				if (split.length < 1 || split.length === 1 && limit !== 1) {
					throw new ReferenceError('splitEx() separator argument <'
						+ separator + '> not found in string');
				}
				return split;
			},

			substr: function (str, start, length) {
				/* jshint maxcomplexity: 6 */
				var max = Math.max(0, str.length - 1);
				self.RangeError.throwIfOutOfRange(start, -str.length,
					max, 'start', 'substrEx');
				max = Math.max(1, str.length);
				if (length !== void 0 && length !== null) {
					self.RangeError.throwIfOutOfRange(length, 1,
							max, 'length', 'substrEx');
				}

				var substr = str.substr(start, length);
				if (substr === '') {
					throw new ReferenceError('substrEx() length argument ' +
						'references nothing from the string: ' + length);
				}
				if (length && substr.length !== length) {
					throw new ReferenceError('substrEx() start and length ' +
						'argument combination lies outside the string: ' +
							start + ', ' + length);
				}
				return substr;
			},

			substring: function (str, beginIndex, endIndex) {
				var max = Math.max(0, str.length - 1);
				self.RangeError.throwIfOutOfRange(beginIndex, 0,
					max, 'beginIndex', 'substringEx');
				max += 1;
				if (endIndex === void 0 || endIndex === null) {
					endIndex = max;
				}
				self.RangeError.throwIfOutOfRange(endIndex, 0,
					max, 'endIndex', 'substringEx');

				var substring = str.substring(beginIndex, endIndex);
				if (substring === '') {
					throw new ReferenceError('substringEx() endIndex argument ' +
						'references nothing from the string ' + endIndex);
				}
				return substring;
			},

			'-': '-'
		},

		Math: {
			acos: function (value) {
				var result = Math.acos(value);
				if (!isFinite(result)) {
					self.RangeError.throwIfOutOfRange(value, -1, 1, 'value', 'acosEx');
				}
				return result;
			},

			'-': '-'
		},

		'-': '-'
	};
})(typeof module === 'object' && typeof module.exports === 'object' ?
		module.exports : window, typeof global === 'object' ? global: window);
<|endoftext|>