<commit_before>var http = require('http');
var https = require('https');
var querystring = require('querystring');

var log = require('./log');

const varPrefix = ':';
const varPrefixRegex = new RegExp(varPrefix, 'g');
const varRegex = new RegExp(varPrefix + '[a-z|_|\-|.]+', 'g', 'i');

/** -----
 * Translate URL according to Gitlab request body
 * @param {string} url - URL to translate
 * @param {Object} gitlabBody - Request body from Gitlab
 * @returns {string} - Translated URL, empty on error
 */
function translateUrl(url, gitlabBody) {
    var matches = url.match(varRegex);

    if (matches !== null) {
        var i = 0;
        var error = false;
        for (; i < matches.length; i++) {
            var value = 'undefined';
            try {
                value = gitlabBody[matches[i].substring(1)];
            } catch (e) {
                log.error('Problem while evaluating variable "' + matches[i] + '":', e.message);
                error = true;
            }

            url = url.replace(matches[i], value);
        }
    }

    return error ? '' : url;
}

/** -----
 * Translate body according to Gitlab request body
 * @param {Object} body - Body to translate
 * @param {Object} gitlabBody - Request body from Gitlab
 * @returns {string} - Translated JSON formatted body, empty on error
 */
function translateBody(body, gitlabBody) {
    body = JSON.stringify(body);
    var matches = body.match(varRegex);

    if (matches !== null) {
        var i = 0;
        var error = false;
        for (; i < matches.length; i++) {
            var value = 'undefined';
            try {
                value = gitlabBody[matches[i].substring(1)];
            } catch (e) {
                log.error('Problem while evaluating variable "' + matches[i] + '":', e.message);
                error = true;
            }

            if (typeof value === 'object') {
                value = JSON.stringify(value);
                body = body.replace('"' + matches[i] + '"', value);
            } else {
                body = body.replace(matches[i], value);
            }
        }
    }

    //todo: :__original-data__ keyword

    return error ? '' : JSON.parse(body);
}

/** -----
 * Forward an incoming web hook to another service endpoint
 * @param {Object} target - Target object from translation
 * @param {string} path - An already translated target's path
 * @param {Object} body - An already translated body
 * @param {forwardResult} done - Callback
 */
function forwardWebhook(target, path, body, done) {
    // Set default variables if necessary
    var proto = (typeof target.protocol !== 'undefined') ? target.protocol.toLowerCase() : 'http';
    var port = proto === 'http' ? 80 : 443;
    port = (typeof target.port !== 'undefined') ? target.port : port;
    var method = ((typeof target.method !== 'undefined')) ? target.method.toUpperCase() : 'GET';
    var ctype = (typeof target.content_type !== 'undefined') ? target.content_type : 'application/json';
    body = (ctype === 'xform') ? querystring.stringify(body) : JSON.stringify(body);
    ctype = (ctype === 'xform') ? 'application/x-www-form-urlencoded' : 'application/json';

    // Prepare request
    var options = {
        hostname: target.host,
        port: port,
        path: path,
        method: method,
        headers: {
            'Content-Type': ctype,
            'Content-Length': Buffer.byteLength(body)
        }
    };

    // Send request
    proto = (proto === 'http') ? http : https;
    var req = proto.request(options, function (res) {
        if (res.statusCode >= 200 && res.statusCode < 400) {
            done(true);
        } else {
            log.warn('Outgoing request failed (' + res.statusCode + ' - ' + res.statusMessage + ')');

            var str = '';
            res.on('data', function (chunk) {
                str += chunk;
            });
            res.on('end', function () {
                log.debug('Request body |', str);
            });

            done(false);
        }

    });

    // catch error
    req.on('error', function (e) {
        log.error('Outgoing request failed', e.message);
        done(false);
    });

    // write data to request body
    req.write(body);
    req.end();
}
/**
 * @callback forwardResult
 * @param {boolean} res - True on success, false otherwise
 */

/** -----
 * Check if required attributes are there
 * @param {Object} translation - Translation object from config
 * @returns {boolean} - True if checks pass, false otherwise
 */
function checkTranslation(translation) {
    // Target
    if (typeof translation.target === 'undefined') {
        log.error('Object "target" is required!');
        return false;
    }

    var res = true;

    // Path
    if (typeof translation.target.path === 'undefined') {
        log.error('Attribute "target.path" is required!');
        res = false;
    }

    // Host
    if (typeof translation.target.host === 'undefined') {
        log.error('Attribute "target.host" is required!');
        res = false;
    }

    return res;
}

/**
 * Get translation name
 * @param {Object} translation - Translation object from configuration file
 * @return {string} - Translation name if defined
 */
function getName(translation) {
    var name = (typeof translation.name === 'undefined') ? 'no_name' : translation.name;
    return (typeof translation.token !== 'undefined' && name === 'no_name') ? translation.token : name;
}

module.exports = {
    /**
     * Execute the translation if possible
     * @param {Object} translation - Translation object from configuration file
     * @param {Object} gitlabBody - Request body from Gitlab
     * @param {string} gitlabToken - Token from Gitlab to identify trigger
     * @param {processResult} done - Callback (only executed if translation has to be processed)
     */
    process: function (translation, gitlabBody, gitlabToken, done) {
        if (!checkTranslation(translation))
            return done(false, translation);

        var controlPassed = true;

        // Check token if provided
        if (typeof translation.token !== 'undefined') {
            if (translation.token !== gitlabToken) {
                controlPassed = false;
            }
        }

        // Check condition if provided
        try {
            if (typeof translation.condition !== 'undefined') {
                var condition = translation.condition.replace(varPrefixRegex, 'gitlabBody.');
                if (!eval(condition)) {
                    controlPassed = false;
                    log.info('Condition was not met for translation "' + getName(translation) + '"');
                }
            }
        } catch (e) {
            log.error('Problem while evaluating condition: ' + e.message);
            controlPassed = false;
        }

        if (controlPassed) {
            var url = translateUrl(translation.target.path, gitlabBody);

            var body = {};
            if (typeof translation.body !== 'undefined')
                body = translateBody(translation.body, gitlabBody);

            // Check translations success
            if (url === '' || body === '')
                return done(false, translation);

            // Forward web hook request
            forwardWebhook(translation.target, url, body, function (res) {
                return done(res, translation);
            });
        }
    },
    /**
     * @callback processResult
     * @param {boolean} status - True if translation was executed, false otherwise
     * @param {Object} translation - Translation object
     */

    translateUrl: translateUrl,
    translateBody: translateBody,
    forwardWebhook: forwardWebhook,
    checkTranslation: checkTranslation,
    getName: getName
};
<commit_msg>Remove eval usage for retrieveing properties<commit_after>var http = require('http');
var https = require('https');
var querystring = require('querystring');

var log = require('./log');

const varPrefix = ':';
const varPrefixRegex = new RegExp(varPrefix, 'g');
const varRegex = new RegExp(varPrefix + '[a-z|_|\-|.]+', 'g', 'i');

/** -----
 * Retrieves nested property from object by dot notated string
 * @param {string} propertyName - Dot notated property string
 * @param {Object} object - object to retrieve data from
 */
function getProperty(propertyName, object) {
    var parts = propertyName.split("."),
        length = parts.length,
        i,
        property = object || this;

    for (i = 0; i < length; i++) {
        property = property[parts[i]];
    }

    return property;
}

/** -----
 * Translate URL according to Gitlab request body
 * @param {string} url - URL to translate
 * @param {Object} gitlabBody - Request body from Gitlab
 * @returns {string} - Translated URL, empty on error
 */
function translateUrl(url, gitlabBody) {
    var matches = url.match(varRegex);

    if (matches !== null) {
        var i = 0;
        var error = false;
        for (; i < matches.length; i++) {
            var value = 'undefined';
            try {
                value = getProperty(matches[i].substring(1), gitlabBody);
            } catch (e) {
                log.error('Problem while evaluating variable "' + matches[i] + '":', e.message);
                error = true;
            }

            url = url.replace(matches[i], value);
        }
    }

    return error ? '' : url;
}

/** -----
 * Translate body according to Gitlab request body
 * @param {Object} body - Body to translate
 * @param {Object} gitlabBody - Request body from Gitlab
 * @returns {string} - Translated JSON formatted body, empty on error
 */
function translateBody(body, gitlabBody) {
    body = JSON.stringify(body);
    var matches = body.match(varRegex);

    if (matches !== null) {
        var i = 0;
        var error = false;
        for (; i < matches.length; i++) {
            var value = 'undefined';
            try {
                value = getProperty(matches[i].substring(1), gitlabBody);
            } catch (e) {
                log.error('Problem while evaluating variable "' + matches[i] + '":', e.message);
                error = true;
            }

            if (typeof value === 'object') {
                value = JSON.stringify(value);
                body = body.replace('"' + matches[i] + '"', value);
            } else {
                body = body.replace(matches[i], value);
            }
        }
    }

    //todo: :__original-data__ keyword

    return error ? '' : JSON.parse(body);
}

/** -----
 * Forward an incoming web hook to another service endpoint
 * @param {Object} target - Target object from translation
 * @param {string} path - An already translated target's path
 * @param {Object} body - An already translated body
 * @param {forwardResult} done - Callback
 */
function forwardWebhook(target, path, body, done) {
    // Set default variables if necessary
    var proto = (typeof target.protocol !== 'undefined') ? target.protocol.toLowerCase() : 'http';
    var port = proto === 'http' ? 80 : 443;
    port = (typeof target.port !== 'undefined') ? target.port : port;
    var method = ((typeof target.method !== 'undefined')) ? target.method.toUpperCase() : 'GET';
    var ctype = (typeof target.content_type !== 'undefined') ? target.content_type : 'application/json';
    body = (ctype === 'xform') ? querystring.stringify(body) : JSON.stringify(body);
    ctype = (ctype === 'xform') ? 'application/x-www-form-urlencoded' : 'application/json';

    // Prepare request
    var options = {
        hostname: target.host,
        port: port,
        path: path,
        method: method,
        headers: {
            'Content-Type': ctype,
            'Content-Length': Buffer.byteLength(body)
        }
    };

    // Send request
    proto = (proto === 'http') ? http : https;
    var req = proto.request(options, function (res) {
        if (res.statusCode >= 200 && res.statusCode < 400) {
            done(true);
        } else {
            log.warn('Outgoing request failed (' + res.statusCode + ' - ' + res.statusMessage + ')');

            var str = '';
            res.on('data', function (chunk) {
                str += chunk;
            });
            res.on('end', function () {
                log.debug('Request body |', str);
            });

            done(false);
        }

    });

    // catch error
    req.on('error', function (e) {
        log.error('Outgoing request failed', e.message);
        done(false);
    });

    // write data to request body
    req.write(body);
    req.end();
}
/**
 * @callback forwardResult
 * @param {boolean} res - True on success, false otherwise
 */

/** -----
 * Check if required attributes are there
 * @param {Object} translation - Translation object from config
 * @returns {boolean} - True if checks pass, false otherwise
 */
function checkTranslation(translation) {
    // Target
    if (typeof translation.target === 'undefined') {
        log.error('Object "target" is required!');
        return false;
    }

    var res = true;

    // Path
    if (typeof translation.target.path === 'undefined') {
        log.error('Attribute "target.path" is required!');
        res = false;
    }

    // Host
    if (typeof translation.target.host === 'undefined') {
        log.error('Attribute "target.host" is required!');
        res = false;
    }

    return res;
}

/**
 * Get translation name
 * @param {Object} translation - Translation object from configuration file
 * @return {string} - Translation name if defined
 */
function getName(translation) {
    var name = (typeof translation.name === 'undefined') ? 'no_name' : translation.name;
    return (typeof translation.token !== 'undefined' && name === 'no_name') ? translation.token : name;
}

module.exports = {
    /**
     * Execute the translation if possible
     * @param {Object} translation - Translation object from configuration file
     * @param {Object} gitlabBody - Request body from Gitlab
     * @param {string} gitlabToken - Token from Gitlab to identify trigger
     * @param {processResult} done - Callback (only executed if translation has to be processed)
     */
    process: function (translation, gitlabBody, gitlabToken, done) {
        if (!checkTranslation(translation))
            return done(false, translation);

        var controlPassed = true;

        // Check token if provided
        if (typeof translation.token !== 'undefined') {
            if (translation.token !== gitlabToken) {
                controlPassed = false;
            }
        }

        // Check condition if provided
        try {
            if (typeof translation.condition !== 'undefined') {
                var condition = translation.condition.replace(varPrefixRegex, 'gitlabBody.');
                if (!eval(condition)) {
                    controlPassed = false;
                    log.info('Condition was not met for translation "' + getName(translation) + '"');
                }
            }
        } catch (e) {
            log.error('Problem while evaluating condition: ' + e.message);
            controlPassed = false;
        }

        if (controlPassed) {
            var url = translateUrl(translation.target.path, gitlabBody);

            var body = {};
            if (typeof translation.body !== 'undefined')
                body = translateBody(translation.body, gitlabBody);

            // Check translations success
            if (url === '' || body === '')
                return done(false, translation);

            // Forward web hook request
            forwardWebhook(translation.target, url, body, function (res) {
                return done(res, translation);
            });
        }
    },
    /**
     * @callback processResult
     * @param {boolean} status - True if translation was executed, false otherwise
     * @param {Object} translation - Translation object
     */

    translateUrl: translateUrl,
    translateBody: translateBody,
    forwardWebhook: forwardWebhook,
    checkTranslation: checkTranslation,
    getName: getName
};
<|endoftext|>


<commit_before>import Ember from 'ember';
import faker from 'faker';

var Phil = {
  private: {
    parseValue: function(str) {
      str = "" + str;
      if (str.match(/^\d+\.\.\d+$/)) {
        return Phil.private.parseRange(str);
      } else if (str.match(/^.+,.+$/)) {
        return Phil.private.parseArray(str);
      }
      var arr = [];
      arr.push(str);
      return arr;
    },
    parseRange: function(str) {
      var endpoints = str.split('..'),
          arr = [];
      for (var i=+endpoints[0]; i <= +endpoints[1]; i++) {
        arr.push(i);
      }
      return arr;
    },
    parseArray: function(str) {
      return str.split(',');
    },
    loopAmount: function(num) {
      return Phil.private.parseRange("1.." + Phil.pick(num));
    },
    wrap: function(tag, content) {
      return "<" + tag + ">" + content + "</" + tag + ">";
    },
    generateTag: function(tag) {
      switch (tag) {
        case 'p':
          return Phil.paragraphs(1);
        case 'blockquote':
          var paragraphs = Phil.paragraphs(Phil.pick("1..3"));
          return (Phil.private.wrap('blockquote', paragraphs));
        default:
          return (Phil.private.wrap(tag, Phil.words("3..15")));
      }
    },
    getFakerMethod: function(name) {
      var methods = name.split('.');
      return faker[methods[0]][methods[1]] || false;
    },
    parseArguments: function(args) {
      var output = [];
      for (var i=1; i < args.length-1; i++) {
        output.push(Phil.pick(args[i]));
      }
      return output;
    }
  },
  pick: function(str) {
    var arr = Phil.private.parseValue(str);
    return arr[Math.floor(Math.random() * arr.length)];
  },
  words: function(num) {
    return faker.lorem.words(Phil.pick(num)).join(' ');
  },
  paragraphs: function(num) {
    var output = [];
    Phil.private.loopAmount(num).forEach(function() {
      output.push(Phil.private.wrap('p', faker.lorem.paragraph()));
    });
    return output.join('');
  },
  tag: function(tag, words) {
    return Phil.private.wrap(tag, Phil.words(words || "5..15"));
  },
  markup: function(tags) {
    return tags.split(' ').map(function(tag) {
      return Phil.private.generateTag(tag);
    }).join(' ');
  }
};

export default function(property, arg) {
  switch(property) {
    case 'pick':
      return Phil.pick(arg);
    case 'words':
      return Phil.words(arg);
    case 'paragraphs':
      return new Ember.Handlebars.SafeString(Phil.paragraphs(arg));
    case 'tag':
      return new Ember.Handlebars.SafeString(Phil.tag(arguments[1], arguments[2]));
    case 'markup':
      return new Ember.Handlebars.SafeString(Phil.markup(arg))
    default:
      var fakerMethod = Phil.private.getFakerMethod(property);
      if (fakerMethod) {
        return fakerMethod.apply(this, Phil.private.parseArguments(arguments));
      }
      return;
  }
}
<commit_msg>Reorganize utility methods<commit_after>import Ember from 'ember';
import faker from 'faker';

var PhilUtil = {
  parseValue: function(str) {
    str = "" + str;
    if (str.match(/^\d+\.\.\d+$/)) {
      return PhilUtil.parseRange(str);
    } else if (str.match(/^.+,.+$/)) {
      return PhilUtil.parseArray(str);
    }
    var arr = [];
    arr.push(str);
    return arr;
  },
  parseRange: function(str) {
    var endpoints = str.split('..'),
        arr = [];
    for (var i=+endpoints[0]; i <= +endpoints[1]; i++) {
      arr.push(i);
    }
    return arr;
  },
  parseArray: function(str) {
    return str.split(',');
  },
  loopAmount: function(num) {
    return PhilUtil.parseRange("1.." + Phil.pick(num));
  },
  wrap: function(tag, content) {
    return "<" + tag + ">" + content + "</" + tag + ">";
  },
  generateTag: function(tag) {
    switch (tag) {
      case 'p':
        return Phil.paragraphs(1);
      case 'blockquote':
        var paragraphs = Phil.paragraphs(Phil.pick("1..3"));
        return (PhilUtil.wrap('blockquote', paragraphs));
      default:
        return (PhilUtil.wrap(tag, Phil.words("3..15")));
    }
  },
  getFakerMethod: function(name) {
    var methods = name.split('.');
    return faker[methods[0]][methods[1]] || false;
  },
  parseArguments: function(args) {
    var output = [];
    for (var i=1; i < args.length-1; i++) {
      output.push(Phil.pick(args[i]));
    }
    return output;
  }
};

var Phil = {
  pick: function(str) {
    var arr = PhilUtil.parseValue(str);
    return arr[Math.floor(Math.random() * arr.length)];
  },
  words: function(num) {
    return faker.lorem.words(Phil.pick(num)).join(' ');
  },
  paragraphs: function(num) {
    return PhilUtil.loopAmount(num).map(function() {
      return PhilUtil.wrap('p', faker.lorem.paragraph());
    }).join('');
  },
  tag: function(tag, words) {
    return PhilUtil.wrap(tag, Phil.words(words || "5..15"));
  },
  markup: function(tags) {
    return tags.split(' ').map(function(tag) {
      return PhilUtil.generateTag(tag);
    }).join(' ');
  }
};

export default function(property, arg) {
  switch(property) {
    case 'pick':
      return Phil.pick(arg);
    case 'words':
      return Phil.words(arg);
    case 'paragraphs':
      return new Ember.Handlebars.SafeString(Phil.paragraphs(arg));
    case 'tag':
      return new Ember.Handlebars.SafeString(Phil.tag(arguments[1], arguments[2]));
    case 'markup':
      return new Ember.Handlebars.SafeString(Phil.markup(arg));
    default:
      var fakerMethod = PhilUtil.getFakerMethod(property);
      if (fakerMethod) {
        return fakerMethod.apply(this, PhilUtil.parseArguments(arguments));
      }
      return;
  }
}
<|endoftext|>


<commit_before>"use strict"

describe("offline enabler library", function()
{
	var async = new AsyncSpec(this);

    async.it("validate map", function(done)
    {
        expect(g_map).toEqual(jasmine.any(Object));
        expect(g_map.id).toEqual("map");
        done();
    });

    async.it("validate tiled layer", function(done)
    {
        expect(g_basemapLayer).toEqual(jasmine.any(Object));
        expect(g_basemapLayer.tileInfo).toEqual(jasmine.any(Object));
        done();
    });

	async.it("extends the tiled layer object", function(done)
	{
		expect(g_basemapLayer.goOffline).toBeUndefined();
		g_offlineTilesEnabler.extend(g_basemapLayer,function(success)
		{
			expect(success).toEqual(true);
			expect(g_basemapLayer.goOffline).toEqual(jasmine.any(Function));
			expect(g_basemapLayer.goOnline).toEqual(jasmine.any(Function));
			expect(g_basemapLayer.getTileUrl).toEqual(jasmine.any(Function));
			expect(g_basemapLayer._getTileUrl).toEqual(jasmine.any(Function));
			expect(g_basemapLayer.prepareForOffline).toEqual(jasmine.any(Function));
			expect(g_basemapLayer._storeTile).toEqual(jasmine.any(Function));
			expect(g_basemapLayer.deleteAllTiles).toEqual(jasmine.any(Function));
			expect(g_basemapLayer.offline).toEqual(jasmine.any(Object));
			expect(g_basemapLayer.offline.store).toEqual(jasmine.any(Object));

			g_basemapLayer.offline.proxyPath = "../lib/resource-proxy/proxy.php";
	        done();
		});
	});

	async.it("can go offline", function(done)
	{
		expect(g_basemapLayer.goOffline).toEqual(jasmine.any(Function));
		expect(g_basemapLayer.offline.online).toEqual(true);
		g_basemapLayer.goOffline();
		expect(g_basemapLayer.offline.online).toEqual(false);
        done();
	});

	async.it("can go online", function(done)
	{
		expect(g_basemapLayer.goOffline).toEqual(jasmine.any(Function));
		expect(g_basemapLayer.offline.online).toEqual(false);
		g_basemapLayer.goOnline();
		expect(g_basemapLayer.offline.online).toEqual(true);
        done();
	})

	async.it("delete all tiles", function(done)
	{
		g_basemapLayer.deleteAllTiles(function(success)
		{
			expect(success).toEqual(true);
			setTimeout(function()
			{
				g_basemapLayer.getOfflineUsage(function(usage)
				{
					expect(usage.tileCount).toEqual(0);
			        done();
				});
			},1);
		});
	});

	async.it("stores one tile", function(done)
	{
		g_basemapLayer.getOfflineUsage(function(usage)
		{
			expect(usage.tileCount).toEqual(0);
			g_basemapLayer._storeTile(14,6177,8023, function(success)
			{
				expect(success).toEqual(true);
				g_basemapLayer.getOfflineUsage(function(usage)
				{
					expect(usage.tileCount).toEqual(1);
			        done();
				});
			});
		});
	});

	async.it("gets level estimation", function(done)
	{
		require(["esri/geometry/Extent"],function(Extent)
		{
            var extent = new Extent({"xmin":-822542.2830377579,"ymin":4580841.761960262,"xmax":94702.05638410954,"ymax":5131188.365613382,"spatialReference":{"wkid":102100}});
			g_basemapLayer.estimateTileSize(function(tileSize){
                var estimation = g_basemapLayer.getLevelEstimation(extent,10,tileSize);
                expect(estimation.tileCount).toEqual(375);
                expect(estimation.sizeBytes).toEqual(estimation.tileCount * tileSize);

                var estimation = g_basemapLayer.getLevelEstimation(extent,8,tileSize);
                expect(estimation.tileCount).toEqual(28);
                expect(estimation.sizeBytes).toEqual(estimation.tileCount * tileSize);
                var estimation = g_basemapLayer.getLevelEstimation(extent,2,tileSize);
                expect(estimation.tileCount).toEqual(2);
                expect(estimation.sizeBytes).toEqual(estimation.tileCount * tileSize);
                done();
            }.bind(this));
		});
	});

	async.it("prepares the layer for offline usage", function(done)
	{
		require(["esri/geometry/Extent"], function(Extent)
		{
			g_basemapLayer.deleteAllTiles(function(success)
			{
				expect(success).toEqual(true);
				var extent = new Extent({"xmin":-822542.2830377579,"ymin":4580841.761960262,"xmax":94702.05638410954,"ymax":5131188.365613382,"spatialReference":{"wkid":102100}});
				var callCount = 0;
				var reportProgress = function(progress)
				{
					callCount += 1;
					expect(progress.error).not.toBeDefined();

					if( progress.finishedDownloading )
					{
						g_basemapLayer.getOfflineUsage(function(usage)
						{
							expect(usage.tileCount).toEqual(28);
							expect(callCount).toEqual(29);
					        done();
						});
					}

					return false; // cancelRequested = false;
				}

				g_basemapLayer.prepareForOffline(8,8,extent,reportProgress);
			});
		});
	});

	async.it("returns placeholder urls when offline", function(done)
	{
		require(["dojo/dom"], function(dom)
		{
			var fakeTile = dom.byId('fakeTile');

			g_basemapLayer.goOnline();
			var onlineUrl = g_basemapLayer.getTileUrl(14,6178,8023);

            //NOTE: We are getting new attributes at ArcGIS JS API v3.8 : blankTile=false&_ts=1393031666639 <last part is a random number>
            // http://services.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer/tile/14/6178/8023?blankTile=false&_ts=1393031666639"
            var tempUrl = onlineUrl.slice( 0, onlineUrl.indexOf('?'))
			expect(tempUrl).toEqual('http://services.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer/tile/14/6178/8023');

			g_basemapLayer.goOffline();
			var offlineUrl = fakeTile.src = g_basemapLayer.getTileUrl(14,6178,8023);
			expect(offlineUrl).toEqual('void:/14/6178/8023');
			done();
		})
	});

});<commit_msg>updated spec to include two new helper methods<commit_after>"use strict"

describe("offline enabler library", function()
{
	var async = new AsyncSpec(this);

    async.it("validate map", function(done)
    {
        expect(g_map).toEqual(jasmine.any(Object));
        expect(g_map.id).toEqual("map");
        done();
    });

    async.it("validate tiled layer", function(done)
    {
        expect(g_basemapLayer).toEqual(jasmine.any(Object));
        expect(g_basemapLayer.tileInfo).toEqual(jasmine.any(Object));
        done();
    });

	async.it("extends the tiled layer object", function(done)
	{
		expect(g_basemapLayer.goOffline).toBeUndefined();
		g_offlineTilesEnabler.extend(g_basemapLayer,function(success)
		{
			expect(success).toEqual(true);
			expect(g_basemapLayer.goOffline).toEqual(jasmine.any(Function));
			expect(g_basemapLayer.goOnline).toEqual(jasmine.any(Function));
			expect(g_basemapLayer.getTileUrl).toEqual(jasmine.any(Function));
			expect(g_basemapLayer._getTileUrl).toEqual(jasmine.any(Function));
			expect(g_basemapLayer.prepareForOffline).toEqual(jasmine.any(Function));
			expect(g_basemapLayer._storeTile).toEqual(jasmine.any(Function));
			expect(g_basemapLayer.deleteAllTiles).toEqual(jasmine.any(Function));
			expect(g_basemapLayer.offline).toEqual(jasmine.any(Object));
			expect(g_basemapLayer.offline.store).toEqual(jasmine.any(Object));

			g_basemapLayer.offline.proxyPath = "../lib/resource-proxy/proxy.php";
	        done();
		});
	});

	async.it("can go offline", function(done)
	{
		expect(g_basemapLayer.goOffline).toEqual(jasmine.any(Function));
		expect(g_basemapLayer.offline.online).toEqual(true);
		g_basemapLayer.goOffline();
		expect(g_basemapLayer.offline.online).toEqual(false);
        done();
	});

	async.it("can go online", function(done)
	{
		expect(g_basemapLayer.goOffline).toEqual(jasmine.any(Function));
		expect(g_basemapLayer.offline.online).toEqual(false);
		g_basemapLayer.goOnline();
		expect(g_basemapLayer.offline.online).toEqual(true);
        done();
	})

	async.it("delete all tiles", function(done)
	{
		g_basemapLayer.deleteAllTiles(function(success)
		{
			expect(success).toEqual(true);
			setTimeout(function()
			{
				g_basemapLayer.getOfflineUsage(function(usage)
				{
					expect(usage.tileCount).toEqual(0);
			        done();
				});
			},1);
		});
	});

	async.it("stores one tile", function(done)
	{
		g_basemapLayer.getOfflineUsage(function(usage)
		{
			expect(usage.tileCount).toEqual(0);
			g_basemapLayer._storeTile(14,6177,8023, function(success)
			{
				expect(success).toEqual(true);
				g_basemapLayer.getOfflineUsage(function(usage)
				{
					expect(usage.tileCount).toEqual(1);
			        done();
				});
			});
		});
	});

	async.it("gets level estimation", function(done)
	{
		require(["esri/geometry/Extent"],function(Extent)
		{
            var extent = new Extent({"xmin":-822542.2830377579,"ymin":4580841.761960262,"xmax":94702.05638410954,"ymax":5131188.365613382,"spatialReference":{"wkid":102100}});
			g_basemapLayer.estimateTileSize(function(tileSize){
                var estimation = g_basemapLayer.getLevelEstimation(extent,10,tileSize);
                expect(estimation.tileCount).toEqual(375);
                expect(estimation.sizeBytes).toEqual(estimation.tileCount * tileSize);

                var estimation = g_basemapLayer.getLevelEstimation(extent,8,tileSize);
                expect(estimation.tileCount).toEqual(28);
                expect(estimation.sizeBytes).toEqual(estimation.tileCount * tileSize);
                var estimation = g_basemapLayer.getLevelEstimation(extent,2,tileSize);
                expect(estimation.tileCount).toEqual(2);
                expect(estimation.sizeBytes).toEqual(estimation.tileCount * tileSize);
                done();
            }.bind(this));
		});
	});

	async.it("prepares the layer for offline usage", function(done)
	{
		require(["esri/geometry/Extent"], function(Extent)
		{
			g_basemapLayer.deleteAllTiles(function(success)
			{
				expect(success).toEqual(true);
				var extent = new Extent({"xmin":-822542.2830377579,"ymin":4580841.761960262,"xmax":94702.05638410954,"ymax":5131188.365613382,"spatialReference":{"wkid":102100}});
				var callCount = 0;
				var reportProgress = function(progress)
				{
					callCount += 1;
					expect(progress.error).not.toBeDefined();

					if( progress.finishedDownloading )
					{
						g_basemapLayer.getOfflineUsage(function(usage)
						{
							expect(usage.tileCount).toEqual(28);
							expect(callCount).toEqual(29);
					        done();
						});
					}

					return false; // cancelRequested = false;
				}

				g_basemapLayer.prepareForOffline(8,8,extent,reportProgress);
			});
		});
	});

    async.it("get tile urls",function(done)
    {
        require(["esri/geometry/Extent"],function(Extent){
            var extent = new Extent({"xmin":-822542.2830377579,"ymin":4580841.761960262,"xmax":94702.05638410954,"ymax":5131188.365613382,"spatialReference":{"wkid":102100}});
            var cells = g_basemapLayer.getTileUrlsByExtent(extent,3);
            expect(cells.length).toBeGreaterThan(0);
            done();
        });
    });

    async.it("get extent buffer",function(done)
    {
        require(["esri/geometry/Extent"],function(Extent){
            var extent = new Extent({"xmin":-822542.2830377579,"ymin":4580841.761960262,"xmax":94702.05638410954,"ymax":5131188.365613382,"spatialReference":{"wkid":102100}});
            var newExtent = g_basemapLayer.getExtentBuffer(1000,extent);
            expect(newExtent.xmin).toBe(-823542.2830377579);
            done();
        });
    });

	async.it("returns placeholder urls when offline", function(done)
	{
		require(["dojo/dom"], function(dom)
		{
			var fakeTile = dom.byId('fakeTile');

			g_basemapLayer.goOnline();
			var onlineUrl = g_basemapLayer.getTileUrl(14,6178,8023);

            //NOTE: We are getting new attributes at ArcGIS JS API v3.8 : blankTile=false&_ts=1393031666639 <last part is a random number>
            // http://services.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer/tile/14/6178/8023?blankTile=false&_ts=1393031666639"
            var tempUrl = onlineUrl.slice( 0, onlineUrl.indexOf('?'))
			expect(tempUrl).toEqual('http://services.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer/tile/14/6178/8023');

			g_basemapLayer.goOffline();
			var offlineUrl = fakeTile.src = g_basemapLayer.getTileUrl(14,6178,8023);
			expect(offlineUrl).toEqual('void:/14/6178/8023');
			done();
		})
	});

});<|endoftext|>


<commit_before>/* eslint-env jest */
/* eslint global-require:0 no-invalid-this:0 */
import each from 'lodash/each';
import map from 'lodash/map';
import assign from 'lodash/assign';
import keyBy from 'lodash/keyBy';

const blank = require.requireActual('../../__test__/blank');

describe('index', () => {
  const validations = {
    html: jest.genMockFn(),
    css: jest.genMockFn(),
    javascript: jest.genMockFn(),
  };
  jest.setMock('../../validations', validations);

  const Actions = require.requireActual('../index');
  const LocalPersistor = require('../../persistors/LocalPersistor').default;
  let dispatch, getState;

  function dispatchThunkAction(action) {
    action(dispatch, getState);
  }

  function expectActions(...actionTypes) {
    const resolveFns = {};
    dispatch.promised = {};

    each(actionTypes, (actionType) => {
      dispatch.promised[actionType] = new Promise((resolve) => {
        resolveFns[actionType] = resolve;
      });
    });

    dispatch.mockImplementation((action) => {
      if (resolveFns[action.type]) {
        resolveFns[action.type](action);
      }
    });
  }

  beforeEach(() => {
    jasmine.DEFAULT_TIMEOUT_INTERVAL = 50;
    dispatch = jest.genMockFunction();
    getState = jest.genMockFunction();
  });

  afterEach(() => {
    LocalPersistor.setCurrentProjectKey.mockClear();
    LocalPersistor.save.mockClear();
  });

  describe('createProject', () => {
    beforeEach(function() {
      getState.mockReturnValue(blank.state);

      dispatchThunkAction(Actions.createProject());
      this.dispatchedAction = dispatch.mock.calls[0][0];
    });

    it('should dispatch one action', () => {
      expect(dispatch.mock.calls.length).toBe(1);
    });

    it('should dispatch PROJECT_CREATED action', function() {
      expect(this.dispatchedAction.type).toBe('PROJECT_CREATED');
    });

    it('should add a project key', function() {
      expect(this.dispatchedAction.payload.projectKey).toBeDefined();
    });

    it('should save current project key', () => {
      expect(LocalPersistor.setCurrentProjectKey.mock.calls).
        toEqual([['12345']]);
    });

    it('should save new project', () => {
      expect(LocalPersistor.save.mock.calls).
        toEqual([
          [blank.project],
        ]);
    });
  });

  describe('loadCurrentProjectFromStorage with project', () => {
    beforeEach(() => {
      LocalPersistor.getCurrentProjectKey.mockReturnValue(
        Promise.resolve(blank.project.projectKey)
      );
      LocalPersistor.load.mockReturnValue(Promise.resolve(blank.project));

      getState.mockReturnValue(blank.state);
      each(validations, (validation) => {
        validation.mockImplementation(() => Promise.resolve([]));
      });

      expectActions(
        'CURRENT_PROJECT_LOADED_FROM_STORAGE',
        'VALIDATING_SOURCE',
        'VALIDATED_SOURCE'
      );

      dispatchThunkAction(Actions.loadCurrentProjectFromStorage());
    });

    pit(
      'should dispatch CURRENT_PROJECT_LOADED_FROM_STORAGE',
      () => dispatch.promised.CURRENT_PROJECT_LOADED_FROM_STORAGE.
        then((action) => {
          expect(action).toBeDefined();
        })
    );

    pit(
      'should pass project in payload',
      () => dispatch.promised.CURRENT_PROJECT_LOADED_FROM_STORAGE.
        then((action) => {
          expect(action.payload.project).toBe(blank.project);
        })
    );

    pit(
      'should dispatch VALIDATING_SOURCE',
      () => dispatch.promised.VALIDATING_SOURCE.then((action) => {
        expect(action).toBeDefined();
      })
    );

    pit(
      'should dispatch VALIDATED_SOURCE',
      () => dispatch.promised.VALIDATED_SOURCE.then((action) => {
        expect(action).toBeDefined();
      })
    );
  });

  describe('loadCurrentProjectFromStorage with no project', () => {
    let actionsDispatched;

    beforeEach(() => {
      getState.mockReturnValue(blank.state);
      LocalPersistor.getCurrentProjectKey.
        mockReturnValue(Promise.resolve(undefined));
      dispatchThunkAction(Actions.loadCurrentProjectFromStorage());

      actionsDispatched = new Promise(dispatch.mockImplementation).
        then(() => dispatch.mock.calls[0][0]);
    });

    pit(
      'should dispatch PROJECT_CREATED action',
      () => actionsDispatched.then((action) => {
        expect(action.type).toBe('PROJECT_CREATED');
      })
    );
  });

  describe('updateProjectSource', () => {
    let actionsDispatched, error, promisedValidatedSourceAction;

    beforeEach(() => {
      getState.mockReturnValue(blank.state);

      promisedValidatedSourceAction = new Promise((resolve) => {
        dispatch.mockImplementation((action) => {
          if (action.type === 'VALIDATED_SOURCE') {
            resolve(action);
          }
        });
      });

      error = {
        row: 1, column: 1,
        raw: 'bad css',
        text: 'bad css',
        type: 'error',
      };

      validations.css.mockReturnValue(Promise.resolve([error]));

      dispatchThunkAction(Actions.updateProjectSource(
        '12345',
        'css',
        'p { display: none; }'
      ));

      actionsDispatched = keyBy(map(dispatch.mock.calls, 0), 'type');
    });

    it('should dispatch PROJECT_SOURCE_EDITED', () => {
      expect(actionsDispatched.PROJECT_SOURCE_EDITED).toBeDefined();
    });

    it('should include projectKey in payload', () => {
      expect(actionsDispatched.PROJECT_SOURCE_EDITED.payload.projectKey).
        toBe('12345');
    });

    it('should include language in payload', () => {
      expect(actionsDispatched.PROJECT_SOURCE_EDITED.payload.language).
        toBe('css');
    });

    it('should include newValue in payload', () => {
      expect(actionsDispatched.PROJECT_SOURCE_EDITED.payload.newValue).
        toBe('p { display: none; }');
    });

    it('should dispatch validation starting action', () => {
      expect(actionsDispatched.VALIDATING_SOURCE).toBeDefined();
    });

    it('should include language in validation starting action', () => {
      expect(actionsDispatched.VALIDATING_SOURCE.payload.language).
        toBe('css');
    });

    pit(
      'should dispatch validation complete action',
      () => promisedValidatedSourceAction.then((action) => {
        expect(action).toBeDefined();
      })
    );

    pit(
      'should send errors in validation complete action',
      () => promisedValidatedSourceAction.then((action) => {
        expect(action.payload.errors).toEqual([error]);
      })
    );

    pit(
      'should send errors in validation complete action',
      () => promisedValidatedSourceAction.then((action) => {
        expect(action.payload.language).toBe('css');
      })
    );

    it('should save project', () => {
      expect(LocalPersistor.save.mock.calls).toEqual([
        [blank.project],
      ]);
    });
  });

  describe('changeCurrentProject', () => {
    beforeEach(() => {
      expectActions(
        'CURRENT_PROJECT_CHANGED',
        'VALIDATING_SOURCE',
        'VALIDATED_SOURCE'
      );

      getState.mockReturnValue(blank.state);

      each(validations, (validation) => {
        validation.mockImplementation(() => Promise.resolve([]));
      });

      dispatchThunkAction(Actions.changeCurrentProject('12345'));
    });

    pit(
      'should dispatch CURRENT_PROJECT_CHANGED',
      () => dispatch.promised.CURRENT_PROJECT_CHANGED.then((action) => {
        expect(action).toBeDefined();
      })
    );

    pit(
      'should dispatch project key in the payload',
      () => dispatch.promised.CURRENT_PROJECT_CHANGED.then((action) => {
        expect(action.payload.projectKey).toBe('12345');
      })
    );

    pit(
      'should update current project key in storage',
      () => dispatch.promised.CURRENT_PROJECT_CHANGED.then(() => {
        expect(LocalPersistor.setCurrentProjectKey.mock.calls).toEqual([
          ['12345'],
        ]);
      })
    );

    pit(
      'should validate sources',
      () => dispatch.promised.VALIDATING_SOURCE.then((action) => {
        expect(action).toBeDefined();
      })
    );

    pit(
      'should finish validating sources',
      () => dispatch.promised.VALIDATED_SOURCE.then((action) => {
        expect(action).toBeDefined();
      })
    );
  });

  describe('toggleLibrary', () => {
    beforeEach(() => {
      expectActions(
        'PROJECT_LIBRARY_TOGGLED',
        'VALIDATING_SOURCE',
        'VALIDATED_SOURCE'
      );

      getState.mockReturnValue(blank.state);
      each(validations, (validation) => {
        validation.mockImplementation(() => Promise.resolve([]));
      });

      dispatchThunkAction(Actions.toggleLibrary('12345', 'jquery'));
    });

    pit(
      'should dispatch PROJECT_LIBRARY_TOGGLED with library',
      () => dispatch.promised.PROJECT_LIBRARY_TOGGLED.then((action) => {
        expect(action.payload.libraryKey).toBe('jquery');
      })
    );

    pit(
      'should dispatch PROJECT_LIBRARY_TOGGLED with project',
      () => dispatch.promised.PROJECT_LIBRARY_TOGGLED.then((action) => {
        expect(action.payload.projectKey).toBe('12345');
      })
    );

    pit(
      'should dispatch VALIDATING_SOURCE',
      () => dispatch.promised.VALIDATING_SOURCE
    );

    pit(
      'should dispatch VALIDATED_SOURCE',
      () => dispatch.promised.VALIDATED_SOURCE
    );
  });

  describe('loadAllProjects', () => {
    beforeEach(() => {
      expectActions('PROJECT_LOADED_FROM_STORAGE');

      getState.mockReturnValue(blank.state);

      LocalPersistor.all.mockReturnValue(Promise.resolve([
        assign({}, blank.project, {projectKey: '23456'}),
      ]));

      dispatchThunkAction(Actions.loadAllProjects());
    });

    pit(
      'should dispatch PROJECT_LOADED_FROM_STORAGE',
      () => dispatch.promised.PROJECT_LOADED_FROM_STORAGE.
        then((action) => {
          expect(action.payload.project.projectKey).toBe('23456');
        })
    );
  });
});
<commit_msg>Add missing global annotation<commit_after>/* eslint-env jest */
/* eslint global-require:0 no-invalid-this:0 */
/* global jasmine */
import each from 'lodash/each';
import map from 'lodash/map';
import assign from 'lodash/assign';
import keyBy from 'lodash/keyBy';

const blank = require.requireActual('../../__test__/blank');

describe('index', () => {
  const validations = {
    html: jest.genMockFn(),
    css: jest.genMockFn(),
    javascript: jest.genMockFn(),
  };
  jest.setMock('../../validations', validations);

  const Actions = require.requireActual('../index');
  const LocalPersistor = require('../../persistors/LocalPersistor').default;
  let dispatch, getState;

  function dispatchThunkAction(action) {
    action(dispatch, getState);
  }

  function expectActions(...actionTypes) {
    const resolveFns = {};
    dispatch.promised = {};

    each(actionTypes, (actionType) => {
      dispatch.promised[actionType] = new Promise((resolve) => {
        resolveFns[actionType] = resolve;
      });
    });

    dispatch.mockImplementation((action) => {
      if (resolveFns[action.type]) {
        resolveFns[action.type](action);
      }
    });
  }

  beforeEach(() => {
    jasmine.DEFAULT_TIMEOUT_INTERVAL = 50;
    dispatch = jest.genMockFunction();
    getState = jest.genMockFunction();
  });

  afterEach(() => {
    LocalPersistor.setCurrentProjectKey.mockClear();
    LocalPersistor.save.mockClear();
  });

  describe('createProject', () => {
    beforeEach(function() {
      getState.mockReturnValue(blank.state);

      dispatchThunkAction(Actions.createProject());
      this.dispatchedAction = dispatch.mock.calls[0][0];
    });

    it('should dispatch one action', () => {
      expect(dispatch.mock.calls.length).toBe(1);
    });

    it('should dispatch PROJECT_CREATED action', function() {
      expect(this.dispatchedAction.type).toBe('PROJECT_CREATED');
    });

    it('should add a project key', function() {
      expect(this.dispatchedAction.payload.projectKey).toBeDefined();
    });

    it('should save current project key', () => {
      expect(LocalPersistor.setCurrentProjectKey.mock.calls).
        toEqual([['12345']]);
    });

    it('should save new project', () => {
      expect(LocalPersistor.save.mock.calls).
        toEqual([
          [blank.project],
        ]);
    });
  });

  describe('loadCurrentProjectFromStorage with project', () => {
    beforeEach(() => {
      LocalPersistor.getCurrentProjectKey.mockReturnValue(
        Promise.resolve(blank.project.projectKey)
      );
      LocalPersistor.load.mockReturnValue(Promise.resolve(blank.project));

      getState.mockReturnValue(blank.state);
      each(validations, (validation) => {
        validation.mockImplementation(() => Promise.resolve([]));
      });

      expectActions(
        'CURRENT_PROJECT_LOADED_FROM_STORAGE',
        'VALIDATING_SOURCE',
        'VALIDATED_SOURCE'
      );

      dispatchThunkAction(Actions.loadCurrentProjectFromStorage());
    });

    pit(
      'should dispatch CURRENT_PROJECT_LOADED_FROM_STORAGE',
      () => dispatch.promised.CURRENT_PROJECT_LOADED_FROM_STORAGE.
        then((action) => {
          expect(action).toBeDefined();
        })
    );

    pit(
      'should pass project in payload',
      () => dispatch.promised.CURRENT_PROJECT_LOADED_FROM_STORAGE.
        then((action) => {
          expect(action.payload.project).toBe(blank.project);
        })
    );

    pit(
      'should dispatch VALIDATING_SOURCE',
      () => dispatch.promised.VALIDATING_SOURCE.then((action) => {
        expect(action).toBeDefined();
      })
    );

    pit(
      'should dispatch VALIDATED_SOURCE',
      () => dispatch.promised.VALIDATED_SOURCE.then((action) => {
        expect(action).toBeDefined();
      })
    );
  });

  describe('loadCurrentProjectFromStorage with no project', () => {
    let actionsDispatched;

    beforeEach(() => {
      getState.mockReturnValue(blank.state);
      LocalPersistor.getCurrentProjectKey.
        mockReturnValue(Promise.resolve(undefined));
      dispatchThunkAction(Actions.loadCurrentProjectFromStorage());

      actionsDispatched = new Promise(dispatch.mockImplementation).
        then(() => dispatch.mock.calls[0][0]);
    });

    pit(
      'should dispatch PROJECT_CREATED action',
      () => actionsDispatched.then((action) => {
        expect(action.type).toBe('PROJECT_CREATED');
      })
    );
  });

  describe('updateProjectSource', () => {
    let actionsDispatched, error, promisedValidatedSourceAction;

    beforeEach(() => {
      getState.mockReturnValue(blank.state);

      promisedValidatedSourceAction = new Promise((resolve) => {
        dispatch.mockImplementation((action) => {
          if (action.type === 'VALIDATED_SOURCE') {
            resolve(action);
          }
        });
      });

      error = {
        row: 1, column: 1,
        raw: 'bad css',
        text: 'bad css',
        type: 'error',
      };

      validations.css.mockReturnValue(Promise.resolve([error]));

      dispatchThunkAction(Actions.updateProjectSource(
        '12345',
        'css',
        'p { display: none; }'
      ));

      actionsDispatched = keyBy(map(dispatch.mock.calls, 0), 'type');
    });

    it('should dispatch PROJECT_SOURCE_EDITED', () => {
      expect(actionsDispatched.PROJECT_SOURCE_EDITED).toBeDefined();
    });

    it('should include projectKey in payload', () => {
      expect(actionsDispatched.PROJECT_SOURCE_EDITED.payload.projectKey).
        toBe('12345');
    });

    it('should include language in payload', () => {
      expect(actionsDispatched.PROJECT_SOURCE_EDITED.payload.language).
        toBe('css');
    });

    it('should include newValue in payload', () => {
      expect(actionsDispatched.PROJECT_SOURCE_EDITED.payload.newValue).
        toBe('p { display: none; }');
    });

    it('should dispatch validation starting action', () => {
      expect(actionsDispatched.VALIDATING_SOURCE).toBeDefined();
    });

    it('should include language in validation starting action', () => {
      expect(actionsDispatched.VALIDATING_SOURCE.payload.language).
        toBe('css');
    });

    pit(
      'should dispatch validation complete action',
      () => promisedValidatedSourceAction.then((action) => {
        expect(action).toBeDefined();
      })
    );

    pit(
      'should send errors in validation complete action',
      () => promisedValidatedSourceAction.then((action) => {
        expect(action.payload.errors).toEqual([error]);
      })
    );

    pit(
      'should send errors in validation complete action',
      () => promisedValidatedSourceAction.then((action) => {
        expect(action.payload.language).toBe('css');
      })
    );

    it('should save project', () => {
      expect(LocalPersistor.save.mock.calls).toEqual([
        [blank.project],
      ]);
    });
  });

  describe('changeCurrentProject', () => {
    beforeEach(() => {
      expectActions(
        'CURRENT_PROJECT_CHANGED',
        'VALIDATING_SOURCE',
        'VALIDATED_SOURCE'
      );

      getState.mockReturnValue(blank.state);

      each(validations, (validation) => {
        validation.mockImplementation(() => Promise.resolve([]));
      });

      dispatchThunkAction(Actions.changeCurrentProject('12345'));
    });

    pit(
      'should dispatch CURRENT_PROJECT_CHANGED',
      () => dispatch.promised.CURRENT_PROJECT_CHANGED.then((action) => {
        expect(action).toBeDefined();
      })
    );

    pit(
      'should dispatch project key in the payload',
      () => dispatch.promised.CURRENT_PROJECT_CHANGED.then((action) => {
        expect(action.payload.projectKey).toBe('12345');
      })
    );

    pit(
      'should update current project key in storage',
      () => dispatch.promised.CURRENT_PROJECT_CHANGED.then(() => {
        expect(LocalPersistor.setCurrentProjectKey.mock.calls).toEqual([
          ['12345'],
        ]);
      })
    );

    pit(
      'should validate sources',
      () => dispatch.promised.VALIDATING_SOURCE.then((action) => {
        expect(action).toBeDefined();
      })
    );

    pit(
      'should finish validating sources',
      () => dispatch.promised.VALIDATED_SOURCE.then((action) => {
        expect(action).toBeDefined();
      })
    );
  });

  describe('toggleLibrary', () => {
    beforeEach(() => {
      expectActions(
        'PROJECT_LIBRARY_TOGGLED',
        'VALIDATING_SOURCE',
        'VALIDATED_SOURCE'
      );

      getState.mockReturnValue(blank.state);
      each(validations, (validation) => {
        validation.mockImplementation(() => Promise.resolve([]));
      });

      dispatchThunkAction(Actions.toggleLibrary('12345', 'jquery'));
    });

    pit(
      'should dispatch PROJECT_LIBRARY_TOGGLED with library',
      () => dispatch.promised.PROJECT_LIBRARY_TOGGLED.then((action) => {
        expect(action.payload.libraryKey).toBe('jquery');
      })
    );

    pit(
      'should dispatch PROJECT_LIBRARY_TOGGLED with project',
      () => dispatch.promised.PROJECT_LIBRARY_TOGGLED.then((action) => {
        expect(action.payload.projectKey).toBe('12345');
      })
    );

    pit(
      'should dispatch VALIDATING_SOURCE',
      () => dispatch.promised.VALIDATING_SOURCE
    );

    pit(
      'should dispatch VALIDATED_SOURCE',
      () => dispatch.promised.VALIDATED_SOURCE
    );
  });

  describe('loadAllProjects', () => {
    beforeEach(() => {
      expectActions('PROJECT_LOADED_FROM_STORAGE');

      getState.mockReturnValue(blank.state);

      LocalPersistor.all.mockReturnValue(Promise.resolve([
        assign({}, blank.project, {projectKey: '23456'}),
      ]));

      dispatchThunkAction(Actions.loadAllProjects());
    });

    pit(
      'should dispatch PROJECT_LOADED_FROM_STORAGE',
      () => dispatch.promised.PROJECT_LOADED_FROM_STORAGE.
        then((action) => {
          expect(action.payload.project.projectKey).toBe('23456');
        })
    );
  });
});
<|endoftext|>


<commit_before>var stylus = require('stylus'),
    Visitor = stylus.Visitor
    nodes = stylus.nodes;

var Scanner = module.exports = function Scanner(root, imagesSeen) {
  Visitor.call(this, root);
  this.imagesSeen = imagesSeen;
  this.imageGroups = {};
  this.tree = [];
};

Scanner.prototype.__proto__ = Visitor.prototype;

Scanner.prototype.combine = function() {
  this.visit(this.root);
};

Scanner.prototype.generateSelector = function(tree) {
  // Originally implemented in stylus compiler.js
  function groupSelectors(group) {
    var curr = [];

    // Construct an array of arrays
    // representing the selector hierarchy
    group.nodes.forEach(function(node){
      curr.push(node.parent
          ? node
          : node.val);
    });
    return curr;
  }

  // Reverse recurse the
  // hierarchy array to build
  // up the selector combinations.
  // When we reach root, we have our
  // selector string built
  var selectors = []
    , buf = [];
  function join(arr, i) {
    if (i) {
      groupSelectors(arr[i]).forEach(function(str){
        buf.unshift(str);
        join(arr, i - 1);
        buf.shift();
      });
    } else {
      groupSelectors(arr[0]).forEach(function(selector){
        var str = selector.trim();
        if (buf.length) {
          for (var i = 0, len = buf.length; i < len; ++i) {
            if (~buf[i].indexOf('&')) {
              str = buf[i].replace(/&/g, str).trim();
            } else {
              str += ' ' + buf[i].trim();
            }
          }
        }
        selectors.push(str.trimRight());
      });
    }
  }

  join(tree, tree.length - 1);
  return new nodes.Selector([ new nodes.Literal(selectors.join(','))]);
};

Scanner.prototype.visitLiteral = function(lit) {
  if (lit.url && this.imagesSeen[lit.url.href] > 1) {
    this.dupeUrl = lit.url;
  }
};

Scanner.prototype.visitProperty = function(prop) {
  this.visit(prop.expr);
};

Scanner.prototype.visitRoot =
Scanner.prototype.visitBlock = function(block) {
  var nodes = block.nodes;
  for (var i = 0; i < nodes.length; i++) {
    this.visit(nodes[i]);
    if (this.dupeUrl) {
      var name = this.dupeUrl.href + ' ' + nodes[i],
          group = this.imageGroups[name] = this.imageGroups[name] || [];
      group.push({ tree: this.tree.slice(), ref: nodes[i] });
    }
    this.dupeUrl = undefined;
  }
};
Scanner.prototype.visitArguments =
Scanner.prototype.visitExpression = function(expr) {
  expr.nodes.forEach(function(node) {
    this.visit(node);
  }, this);
};

Scanner.prototype.visitKeyframes = function(node) {
  node.frames.forEach(function(frame) {
    this.visit(frame.block);
  }, this);
};

Scanner.prototype.visitGroup = function(group) {
  this.tree.push(group);
  this.visit(group.block);
  this.tree.pop();
};

Scanner.prototype.visitFontFace =
Scanner.prototype.visitMedia =
Scanner.prototype.visitPage = function(page) {
  this.visit(page.block);
};

Scanner.prototype.visitCall = function(call) {
  call.args.nodes.forEach(function(arg) {
    this.visit(arg);
  }, this);
};

Scanner.prototype.visitImport =
Scanner.prototype.visitJSLiteral =
Scanner.prototype.visitComment =
Scanner.prototype.visitFunction =
Scanner.prototype.visitVariable =
Scanner.prototype.visitCharset =
Scanner.prototype.visitBoolean =
Scanner.prototype.visitRGBA =
Scanner.prototype.visitHSLA =
Scanner.prototype.visitUnit =
Scanner.prototype.visitIdent =
Scanner.prototype.visitString =
Scanner.prototype.visitNull = function() {};
<commit_msg>Add data uri merge to scanner<commit_after>var stylus = require('stylus'),
    Visitor = stylus.Visitor
    nodes = stylus.nodes;

var Scanner = module.exports = function Scanner(root, imagesSeen) {
  Visitor.call(this, root);
  this.imagesSeen = imagesSeen;
  this.imageGroups = {};
  this.tree = [];
};

Scanner.prototype.__proto__ = Visitor.prototype;

Scanner.prototype.combine = function() {
  this.visit(this.root);

  // Replace duplicated data URIs when possible
  for (var name in this.imageGroups) {
    var imageGroup = this.imageGroups[name],
        group;
    if (imageGroup.length >= 2) {
      for (var i = 0; i < imageGroup.length; i++) {
        var imageRef = imageGroup[i];
        if (!group) {
          group = new nodes.Group();
          block = new nodes.Block();
          block.push(imageRef.ref);
        }

        var originalNodes = imageRef.tree[imageRef.tree.length-1].block.nodes
        for (var n = 0; n < originalNodes.length; n++) {
          if (originalNodes[n] == imageRef.ref) {
            originalNodes.splice(n, 1);
            break;
          }
        }

        group.push(this.generateSelector(imageRef.tree));
      }

      if (group) {
        // Finalize the group and insert
        group.block = block;
        this.root.nodes.push(group);
      }
    }
  }
};

Scanner.prototype.generateSelector = function(tree) {
  // Originally implemented in stylus compiler.js
  function groupSelectors(group) {
    var curr = [];

    // Construct an array of arrays
    // representing the selector hierarchy
    group.nodes.forEach(function(node){
      curr.push(node.parent
          ? node
          : node.val);
    });
    return curr;
  }

  // Reverse recurse the
  // hierarchy array to build
  // up the selector combinations.
  // When we reach root, we have our
  // selector string built
  var selectors = []
    , buf = [];
  function join(arr, i) {
    if (i) {
      groupSelectors(arr[i]).forEach(function(str){
        buf.unshift(str);
        join(arr, i - 1);
        buf.shift();
      });
    } else {
      groupSelectors(arr[0]).forEach(function(selector){
        var str = selector.trim();
        if (buf.length) {
          for (var i = 0, len = buf.length; i < len; ++i) {
            if (~buf[i].indexOf('&')) {
              str = buf[i].replace(/&/g, str).trim();
            } else {
              str += ' ' + buf[i].trim();
            }
          }
        }
        selectors.push(str.trimRight());
      });
    }
  }

  join(tree, tree.length - 1);
  return new nodes.Selector([ new nodes.Literal(selectors.join(','))]);
};

Scanner.prototype.visitLiteral = function(lit) {
  if (lit.url && this.imagesSeen[lit.url.href] > 1) {
    this.dupeUrl = lit.url;
  }
};

Scanner.prototype.visitProperty = function(prop) {
  this.visit(prop.expr);
};

Scanner.prototype.visitRoot =
Scanner.prototype.visitBlock = function(block) {
  var nodes = block.nodes;
  for (var i = 0; i < nodes.length; i++) {
    this.visit(nodes[i]);
    if (this.dupeUrl) {
      var name = this.dupeUrl.href + ' ' + nodes[i],
          group = this.imageGroups[name] = this.imageGroups[name] || [];
      group.push({ tree: this.tree.slice(), ref: nodes[i] });
    }
    this.dupeUrl = undefined;
  }
};
Scanner.prototype.visitArguments =
Scanner.prototype.visitExpression = function(expr) {
  expr.nodes.forEach(function(node) {
    this.visit(node);
  }, this);
};

Scanner.prototype.visitKeyframes = function(node) {
  node.frames.forEach(function(frame) {
    this.visit(frame.block);
  }, this);
};

Scanner.prototype.visitGroup = function(group) {
  this.tree.push(group);
  this.visit(group.block);
  this.tree.pop();
};

Scanner.prototype.visitFontFace =
Scanner.prototype.visitMedia =
Scanner.prototype.visitPage = function(page) {
  this.visit(page.block);
};

Scanner.prototype.visitCall = function(call) {
  call.args.nodes.forEach(function(arg) {
    this.visit(arg);
  }, this);
};

Scanner.prototype.visitImport =
Scanner.prototype.visitJSLiteral =
Scanner.prototype.visitComment =
Scanner.prototype.visitFunction =
Scanner.prototype.visitVariable =
Scanner.prototype.visitCharset =
Scanner.prototype.visitBoolean =
Scanner.prototype.visitRGBA =
Scanner.prototype.visitHSLA =
Scanner.prototype.visitUnit =
Scanner.prototype.visitIdent =
Scanner.prototype.visitString =
Scanner.prototype.visitNull = function() {};
<|endoftext|>


<commit_before>'use strict';

const assetDefinitions = require('../asset-definitions');
const co = require('co');
const R = require('ramda');
const PathUtil = require('./PathUtil');
const File = require('./File');
const PromiseUtil = require('./PromiseUtil');
const Log = require('./Log');

function extensionsForAsset(type, playground) {
  const getExtensions = R.pipe(
    R.path([type, 'preprocessor']),
    R.defaultTo(type),
    R.prop(R.__, assetDefinitions[type].preProcessors),
    R.prop('extensions')
  );

  return getExtensions(playground);
}

const createAssetFileIfNotExist = co.wrap(function *(opts, playground, type, getTmpl) {
  const getFullPath = PathUtil.joinTwo(opts.baseDir);
  const extensions = extensionsForAsset(type, playground);
  const tryReadFile = R.pipe(R.concat(`${type}.`), getFullPath, File.readFile);

  try {
    yield PromiseUtil.tryInOrder(tryReadFile, extensions);
    return false;
  } catch (err) {
    const filePath = getFullPath(`${type}.${extensions[0]}`);
    Log.info(`Did not find ${filePath}, creating one...`);
    const fileContent = yield getTmpl(extensions[0]);
    yield File.writeFile(filePath, fileContent);
    Log.logCreateFile(filePath);
    return true;
  }
});

const createHtmlFileIfNotExist = co.wrap(function *(opts, playground) {
  return yield createAssetFileIfNotExist(opts, playground, 'html', co.wrap(function (ext) {
    return '
Hello
';
  }));
});

const createJsFileIfNotExist = co.wrap(function *(opts, playground) {
  return yield createAssetFileIfNotExist(opts, playground, 'js', co.wrap(function (ext) {
    return 'console.log(\'Hello\')';
  }));
});

const createCssFileIfNotExist = co.wrap(function *(opts, playground) {
  return yield createAssetFileIfNotExist(opts, playground, 'css', co.wrap(function (ext) {
    return 'body { color: red; }';
  }));
});

module.exports = {
  createHtmlFileIfNotExist,
  createJsFileIfNotExist,
  createCssFileIfNotExist,
};
<commit_msg>Update<commit_after>'use strict';

const assetDefinitions = require('../asset-definitions');
const co = require('co');
const CONST = require('../CONST');
const File = require('./File');
const Log = require('./Log');
const PathUtil = require('./PathUtil');
const PromiseUtil = require('./PromiseUtil');
const R = require('ramda');

function extensionsForAsset(type, playground) {
  const getExtensions = R.pipe(
    R.path([type, 'preprocessor']),
    R.defaultTo(type),
    R.prop(R.__, assetDefinitions[type].preProcessors),
    R.prop('extensions')
  );

  return getExtensions(playground);
}

const createAssetFileIfNotExist = co.wrap(function *(opts, playground, type, getTmpl) {
  const getFullPath = PathUtil.joinTwo(opts.baseDir);
  const extensions = extensionsForAsset(type, playground);
  const tryReadFile = R.pipe(R.concat(`${type}.`), getFullPath, File.readFile);

  try {
    yield PromiseUtil.tryInOrder(tryReadFile, extensions);
    return false;
  } catch (err) {
    const filePath = getFullPath(`${type}.${extensions[0]}`);
    Log.info(`Did not find ${filePath}, creating one...`);
    const fileContent = yield getTmpl(extensions[0]);
    yield File.writeFile(filePath, fileContent);
    Log.logCreateFile(filePath);
    return true;
  }
});

const createHtmlFileIfNotExist = co.wrap(function *(opts, playground) {
  return yield createAssetFileIfNotExist(opts, playground, 'html', co.wrap(function *(ext) {
    return ext === 'html' ? yield File.readFile(CONST.HTML_EXAMPLE) : '';
  }));
});

const createJsFileIfNotExist = co.wrap(function *(opts, playground) {
  return yield createAssetFileIfNotExist(opts, playground, 'js', co.wrap(function (ext) {
    return '';
  }));
});

const createCssFileIfNotExist = co.wrap(function *(opts, playground) {
  return yield createAssetFileIfNotExist(opts, playground, 'css', co.wrap(function (ext) {
    return '';
  }));
});

module.exports = {
  createHtmlFileIfNotExist,
  createJsFileIfNotExist,
  createCssFileIfNotExist,
};
<|endoftext|>


<commit_before>/* global Promise */

var express = require("express");
var bodyParser = require("body-parser");
var fs = require("fs");
var spawn = require("child_process").spawn;

var app = express();

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

app.get("/is_crawling", function isCrawling(req, res) {
  fs.stat("crawling.lock", function statResult(err) {
    if (err) {
      res.json({
        crawling: false
      });
    } else {
      res.json({
        crawling: true
      });
    }
  });
});

/**
 * withCrawlingLockAsync
 *
 * Execute the function @crawl with a lock, to ensure that @crawl cannot
 * be executed more than one simultaneously. This functions checks for a
 * crawling.lock file, then creates it and continues.
 *
 * Obviously, this isn't race condition proof, but should be good enough for
 * now.
 *
 * @param crawl {function} - The function to call to start crawling
 * @param error {function} - An error handler function
 * @param done {function} - The function to call when we're done with the lock
 * @returns {undefined}
 */
function withCrawlingLockAsync(crawl, error, done) {
  fs.stat("crawling.lock", function statResult(errStat) {
    /* We want this */
    if (errStat) {
      fs.writeFile("crawling.lock", function onErrorWrite(errWrite) {
        if (errWrite) {
          error(errWrite);
          return;
        }

        crawl(function onDoneCrawling() {
          fs.unlink("crawling.lock", function onErrorUnlink(errUnlink) {
            if (errUnlink) {
              error(errUnlink);
              return;
            }

            done.apply(arguments);
          });
        });
      });
    } else {
      error("Lock file already exists! Cannot crawl");
    }
  });
}

app.post("/start_crawling", function startCrawling(req, res) {
  withCrawlingLockAsync(function (done) {
    res.status(200);
    res.json({
      status: "started"
    });

    new Promise(function downloadDocuments(resolve, reject) {
      // Create child python process
      var loadProc = spawn("./python-virtualenv/bin/python", ["./python-virtualenv/bin/download-pubmed-articles"], {
        stdio: ["pipe", "inherit", "inherit"]
      });
      loadProc.on("exit", function onExit(code, signal) {
        if (code !== 0 || signal) {
          reject("Crawling process failed with " + code + " " + signal);
        } else {
          resolve();
        }
      });
    }).then(function () {
      return new Promise(function parseXML(resolve, reject) {
        // Create child python process
        var loadProc = spawn("./python-virtualenv/bin/python", ["./python-virtualenv/bin/parse-pubmed-files", "Retractions"], {
          stdio: ["pipe", "pipe", "pipe"]
        });
        loadProc.on("exit", function onExit(code, signal) {
          if (code !== 0 || signal) {
            reject("Crawling process failed with " + code + " " + signal);
          } else {
            console.log(stdout.join(""));
            resolve(JSON.parse(stdout.join("")));
          }
        });
        var stdout = [];
        loadProc.stdout.on("data", function onData(data) {
          stdout.push(String(data));
        });
      });
    }).then(function (json) {
      return new Promise(function loadDB(resolve, reject) {
        // Create child python process
        var loadProc = spawn("./python-virtualenv/bin/python", ["./python-virtualenv/bin/load-pubmed-files"], {
          stdio: ["pipe", "inherit", "inherit"]
        });
        // Crawling is completed now, resolve or reject promise
        loadProc.on("exit", function onExit(code, signal) {
          if (code !== 0 || signal) {
            reject("Crawling process failed with " + code + " " + signal);
          } else {
            resolve();
          }
        });
        loadProc.stdin.write(JSON.stringify(json));
        loadProc.stdin.end();
      });
    }).then(function onDone() {
      done();
    }).catch(function onError(error) {
      console.error("Crawing process failed with " + error + " " + error.stack);
      done();
    });
  }, function onErrorAcquiringLock(error) {
    res.send(500);
    res.json({
      status: "failure",
      message: String(error)
    });
  }, function onDoneWithLock() {
  });

  res.json({
    status: "started"
  });
});

app.set("port", 6001);

var server = app.listen(app.get("port"), function onPort() {
  console.log("Scraper service server listening on port " + server.address().port);
});
<commit_msg>scraper: Add names to steps<commit_after>/* global Promise */

var express = require("express");
var bodyParser = require("body-parser");
var fs = require("fs");
var spawn = require("child_process").spawn;

var app = express();

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

app.get("/is_crawling", function isCrawling(req, res) {
  fs.stat("crawling.lock", function statResult(err) {
    if (err) {
      res.json({
        crawling: false
      });
    } else {
      res.json({
        crawling: true
      });
    }
  });
});

/**
 * withCrawlingLockAsync
 *
 * Execute the function @crawl with a lock, to ensure that @crawl cannot
 * be executed more than one simultaneously. This functions checks for a
 * crawling.lock file, then creates it and continues.
 *
 * Obviously, this isn't race condition proof, but should be good enough for
 * now.
 *
 * @param crawl {function} - The function to call to start crawling
 * @param error {function} - An error handler function
 * @param done {function} - The function to call when we're done with the lock
 * @returns {undefined}
 */
function withCrawlingLockAsync(crawl, error, done) {
  fs.stat("crawling.lock", function statResult(errStat) {
    /* We want this */
    if (errStat) {
      fs.writeFile("crawling.lock", function onErrorWrite(errWrite) {
        if (errWrite) {
          error(errWrite);
          return;
        }

        crawl(function onDoneCrawling() {
          fs.unlink("crawling.lock", function onErrorUnlink(errUnlink) {
            if (errUnlink) {
              error(errUnlink);
              return;
            }

            done.apply(arguments);
          });
        });
      });
    } else {
      error("Lock file already exists! Cannot crawl");
    }
  });
}

app.post("/start_crawling", function startCrawling(req, res) {
  withCrawlingLockAsync(function onAcquiredLock(done) {
    res.status(200);
    res.json({
      status: "started"
    });

    new Promise(function downloadDocuments(resolve, reject) {
      // Create child python process
      var loadProc = spawn("./python-virtualenv/bin/python", ["./python-virtualenv/bin/download-pubmed-articles"], {
        stdio: ["pipe", "inherit", "inherit"]
      });
      loadProc.on("exit", function onExit(code, signal) {
        if (code !== 0 || signal) {
          reject("Crawling process failed with " + code + " " + signal);
        } else {
          resolve();
        }
      });
    }).then(function parseDocuments() {
      return new Promise(function parseXML(resolve, reject) {
        // Create child python process
        var loadProc = spawn("./python-virtualenv/bin/python", ["./python-virtualenv/bin/parse-pubmed-files", "Retractions"], {
          stdio: ["pipe", "pipe", "pipe"]
        });
        loadProc.on("exit", function onExit(code, signal) {
          if (code !== 0 || signal) {
            reject("Crawling process failed with " + code + " " + signal);
          } else {
            console.log(stdout.join(""));
            resolve(JSON.parse(stdout.join("")));
          }
        });
        var stdout = [];
        loadProc.stdout.on("data", function onData(data) {
          stdout.push(String(data));
        });
      });
    }).then(function loadDatabase(json) {
      return new Promise(function loadDB(resolve, reject) {
        // Create child python process
        var loadProc = spawn("./python-virtualenv/bin/python", ["./python-virtualenv/bin/load-pubmed-files"], {
          stdio: ["pipe", "inherit", "inherit"]
        });
        // Crawling is completed now, resolve or reject promise
        loadProc.on("exit", function onExit(code, signal) {
          if (code !== 0 || signal) {
            reject("Crawling process failed with " + code + " " + signal);
          } else {
            resolve();
          }
        });
        loadProc.stdin.write(JSON.stringify(json));
        loadProc.stdin.end();
      });
    }).then(function onDone() {
      done();
    }).catch(function onError(error) {
      console.error("Crawing process failed with " + error + " " + error.stack);
      done();
    });
  }, function onErrorAcquiringLock(error) {
    res.send(500);
    res.json({
      status: "failure",
      message: String(error)
    });
  }, function onDoneWithLock() {
  });

  res.json({
    status: "started"
  });
});

app.set("port", 6001);

var server = app.listen(app.get("port"), function onPort() {
  console.log("Scraper service server listening on port " + server.address().port);
});
<|endoftext|>


<commit_before>"use strict";

/**
* Handle ratelimiting something
* @prop {Number} tokens How many tokens the bucket has consumed in this interval
* @prop {Number} lastReset Timestamp of last token clearing
* @prop {Number} lastSend Timestamp of last token consumption
* @prop {Number} tokenLimit The max number tokens the bucket can consume per interval
* @prop {Number} interval How long (in ms) to wait between clearing used tokens
*/
class Bucket {
    /**
    * Construct a Bucket
    * @arg {Number} tokenLimit The max number of tokens the bucket can consume per interval
    * @arg {Number} interval How long (in ms) to wait between clearing used tokens
    * @arg {Object} [options] Optional parameters
    * @arg {Number} options.reservedTokens How many tokens to reserve for priority operations
    * @arg {Object} options.latencyRef A latency reference object
    * @arg {Number} options.latencyRef.latency Interval between consuming tokens
    */
    constructor(tokenLimit, interval, options = {}) {
        this.tokenLimit = tokenLimit;
        this.interval = interval;
        this.latencyRef = options.latencyRef || {latency: 0};
        this.lastReset = this.tokens = this.lastSend = 0;
        this.reservedTokens = options.reservedTokens || 0;
        this._queue = [];
    }

    /**
    * Queue something in the Bucket
    * @arg {Function} func A callback to call when a token can be consumed
    * @arg {Boolean} priority Whether or not the callback should use reserved tokens
    */
    queue(func, priority=false) {
        if(priority) {
            this._queue.unshift({func, priority});
        } else {
            this._queue.push({func, priority});
        }
        this.check();
    }

    check() {
        if(this.timeout || this._queue.length === 0) {
            return;
        }
        if(this.lastReset + this.interval + this.tokenLimit * this.latencyRef.latency < Date.now()) {
            this.lastReset = Date.now();
            this.tokens = Math.max(0, this.tokens - this.tokenLimit);
        }

        let val;
        const tokensAvailable = this.tokens < this.tokenLimit;
        const unreservedTokensAvailable = this.tokens < (this.tokenLimit - this.reservedTokens);
        while(this._queue.length > 0 && (unreservedTokensAvailable || (tokensAvailable && this._queue[0].priority))) {
            this.tokens++;
            const item = this._queue.shift();
            val = this.latencyRef.latency - Date.now() + this.lastSend;
            if(this.latencyRef.latency === 0 || val <= 0) {
                item.func();
                this.lastSend = Date.now();
            } else {
                setTimeout(() => {
                    item.func();
                }, val);
                this.lastSend = Date.now() + val;
            }
        }

        if(this._queue.length > 0 && !this.timeout) {
            this.timeout = setTimeout(() => {
                this.timeout = null;
                this.check();
            }, this.tokens < this.tokenLimit ? this.latencyRef.latency : Math.max(0, this.lastReset + this.interval + this.tokenLimit * this.latencyRef.latency - Date.now()));
        }
    }
}

module.exports = Bucket;
<commit_msg>Fix Bucket ratelimiting (#965)<commit_after>"use strict";

/**
* Handle ratelimiting something
* @prop {Number} tokens How many tokens the bucket has consumed in this interval
* @prop {Number} lastReset Timestamp of last token clearing
* @prop {Number} lastSend Timestamp of last token consumption
* @prop {Number} tokenLimit The max number tokens the bucket can consume per interval
* @prop {Number} interval How long (in ms) to wait between clearing used tokens
*/
class Bucket {
    /**
    * Construct a Bucket
    * @arg {Number} tokenLimit The max number of tokens the bucket can consume per interval
    * @arg {Number} interval How long (in ms) to wait between clearing used tokens
    * @arg {Object} [options] Optional parameters
    * @arg {Number} options.reservedTokens How many tokens to reserve for priority operations
    * @arg {Object} options.latencyRef A latency reference object
    * @arg {Number} options.latencyRef.latency Interval between consuming tokens
    */
    constructor(tokenLimit, interval, options = {}) {
        this.tokenLimit = tokenLimit;
        this.interval = interval;
        this.latencyRef = options.latencyRef || {latency: 0};
        this.lastReset = this.tokens = this.lastSend = 0;
        this.reservedTokens = options.reservedTokens || 0;
        this._queue = [];
    }

    /**
    * Queue something in the Bucket
    * @arg {Function} func A callback to call when a token can be consumed
    * @arg {Boolean} priority Whether or not the callback should use reserved tokens
    */
    queue(func, priority=false) {
        if(priority) {
            this._queue.unshift({func, priority});
        } else {
            this._queue.push({func, priority});
        }
        this.check();
    }

    check() {
        if(this.timeout || this._queue.length === 0) {
            return;
        }
        if(this.lastReset + this.interval + this.tokenLimit * this.latencyRef.latency < Date.now()) {
            this.lastReset = Date.now();
            this.tokens = Math.max(0, this.tokens - this.tokenLimit);
        }

        let val;
        let tokensAvailable = this.tokens < this.tokenLimit;
        let unreservedTokensAvailable = this.tokens < (this.tokenLimit - this.reservedTokens);
        while(this._queue.length > 0 && (unreservedTokensAvailable || (tokensAvailable && this._queue[0].priority))) {
            this.tokens++;
            tokensAvailable = this.tokens < this.tokenLimit;
            unreservedTokensAvailable = this.tokens < (this.tokenLimit - this.reservedTokens);
            const item = this._queue.shift();
            val = this.latencyRef.latency - Date.now() + this.lastSend;
            if(this.latencyRef.latency === 0 || val <= 0) {
                item.func();
                this.lastSend = Date.now();
            } else {
                setTimeout(() => {
                    item.func();
                }, val);
                this.lastSend = Date.now() + val;
            }
        }

        if(this._queue.length > 0 && !this.timeout) {
            this.timeout = setTimeout(() => {
                this.timeout = null;
                this.check();
            }, this.tokens < this.tokenLimit ? this.latencyRef.latency : Math.max(0, this.lastReset + this.interval + this.tokenLimit * this.latencyRef.latency - Date.now()));
        }
    }
}

module.exports = Bucket;
<|endoftext|>


<commit_before>const _ = require('underscore');
const GitCommander = require('./gitCommander');

/**
 * @module Blamer
 *
 * Blamer is a Class that should be instantiated with an atom 'Git' object
 * for the root repository in the project.
 *
 * @param {Git} repo - an instance of 'Git' class from atom workspace. See
 *   https://atom.io/docs/api/v0.92.0/api/ for more info.
 */
var Blamer = function(repo) {
  if (!repo) {
    throw new Error('Cannot create a Blamer without a repository.');
  }

  this.repo = repo;
  this.initialize();
}

/**
 * Initializes this Blamer instance, by creating git-tools repos for the root
 * repository and submodules.
 */
Blamer.prototype.initialize = function() {
  this.tools = {};
  this.tools.root = new GitCommander(this.repo.getWorkingDirectory());

  var submodules = this.repo.submodules;
  if (submodules) {
    for (var submodulePath in submodules) {
      this.tools[submodulePath] = new GitCommander(this.repo.getWorkingDirectory() + '/' + submodulePath);
    }
  }
}

/**
 * Blames the given filePath and calls callback with blame lines or error.
 *
 * @param {string} filePath - filePath to blame
 * @param {function} callback - callback to call back with blame data
 */
Blamer.prototype.blame = function(filePath, callback) {
  // Ensure file path is relative to root repo
  filePath = this.repo.relativize(filePath);
  var repoUtil = this.repoUtilForPath(filePath);

  // Ensure that if this file is in a submodule, we remove the submodule dir
  // from the path
  filePath = this.removeSubmodulePrefix(filePath);

  // relativize the path again, just in case we now are using a submodule repo
  console.log('filePath: ' + filePath);
  console.log('repoPath: ' + repoUtil.workingDirectory);

  if (!_.isFunction(callback)) {
    throw new Error('Must be called with a callback function');
  }

  // Make the async blame call on the git repo
  repoUtil.blame(filePath, function(err, blame) {
    callback(err, blame);
  });
}

/**
 * Utility to get the GitCommander repository for the given filePath. Takes into
 * account whether the file is part of a submodule and returns that repository
 * if necessary.
 *
 * @param {string} filePath - the path to the file in question.
 */
Blamer.prototype.repoUtilForPath = function(filePath) {
  var submodules = this.repo.submodules;

  // By default, we return the root GitCommander repository.
  var repoUtil = this.tools.root;

  // if we have submodules, loop through them and see if the given file path
  // belongs inside one of the repositories. If so, we return the GitCommander repo
  // for that submodule.
  if (submodules) {
    for (var submodulePath in submodules) {
      var submoduleRegex = new RegExp('^' + submodulePath);
      if (submoduleRegex.test(filePath)) {
        repoUtil = this.tools[submodulePath];
      }
    }
  }

  return repoUtil;
}

/**
 * If the file path given is inside a submodule, removes the submodule
 * directory prefix.
 *
 * @param {string} filePath - path to file to relativize
 * @param {Repo} toolsRepo - git-tools Repo
 */
Blamer.prototype.removeSubmodulePrefix = function(filePath) {
  var submodules = this.repo.submodules;
  if (submodules) {
    for (var submodulePath in submodules) {
      var submoduleRegex = new RegExp('^' + submodulePath);
      if (submoduleRegex.test(filePath)) {
        filePath = filePath.replace(submoduleRegex, '');
      }
    }
  }

  // remove leading '/' if there is one before returning
  filePath = filePath.replace(/^\//, '');
  return filePath;
}

module.exports = Blamer;
<commit_msg>remove some logs<commit_after>const _ = require('underscore');
const GitCommander = require('./gitCommander');

/**
 * @module Blamer
 *
 * Blamer is a Class that should be instantiated with an atom 'Git' object
 * for the root repository in the project.
 *
 * @param {Git} repo - an instance of 'Git' class from atom workspace. See
 *   https://atom.io/docs/api/v0.92.0/api/ for more info.
 */
var Blamer = function(repo) {
  if (!repo) {
    throw new Error('Cannot create a Blamer without a repository.');
  }

  this.repo = repo;
  this.initialize();
}

/**
 * Initializes this Blamer instance, by creating git-tools repos for the root
 * repository and submodules.
 */
Blamer.prototype.initialize = function() {
  this.tools = {};
  this.tools.root = new GitCommander(this.repo.getWorkingDirectory());

  var submodules = this.repo.submodules;
  if (submodules) {
    for (var submodulePath in submodules) {
      this.tools[submodulePath] = new GitCommander(this.repo.getWorkingDirectory() + '/' + submodulePath);
    }
  }
}

/**
 * Blames the given filePath and calls callback with blame lines or error.
 *
 * @param {string} filePath - filePath to blame
 * @param {function} callback - callback to call back with blame data
 */
Blamer.prototype.blame = function(filePath, callback) {
  // Ensure file path is relative to root repo
  filePath = this.repo.relativize(filePath);
  var repoUtil = this.repoUtilForPath(filePath);

  // Ensure that if this file is in a submodule, we remove the submodule dir
  // from the path
  filePath = this.removeSubmodulePrefix(filePath);

  if (!_.isFunction(callback)) {
    throw new Error('Must be called with a callback function');
  }

  // Make the async blame call on the git repo
  repoUtil.blame(filePath, function(err, blame) {
    callback(err, blame);
  });
}

/**
 * Utility to get the GitCommander repository for the given filePath. Takes into
 * account whether the file is part of a submodule and returns that repository
 * if necessary.
 *
 * @param {string} filePath - the path to the file in question.
 */
Blamer.prototype.repoUtilForPath = function(filePath) {
  var submodules = this.repo.submodules;

  // By default, we return the root GitCommander repository.
  var repoUtil = this.tools.root;

  // if we have submodules, loop through them and see if the given file path
  // belongs inside one of the repositories. If so, we return the GitCommander repo
  // for that submodule.
  if (submodules) {
    for (var submodulePath in submodules) {
      var submoduleRegex = new RegExp('^' + submodulePath);
      if (submoduleRegex.test(filePath)) {
        repoUtil = this.tools[submodulePath];
      }
    }
  }

  return repoUtil;
}

/**
 * If the file path given is inside a submodule, removes the submodule
 * directory prefix.
 *
 * @param {string} filePath - path to file to relativize
 * @param {Repo} toolsRepo - git-tools Repo
 */
Blamer.prototype.removeSubmodulePrefix = function(filePath) {
  var submodules = this.repo.submodules;
  if (submodules) {
    for (var submodulePath in submodules) {
      var submoduleRegex = new RegExp('^' + submodulePath);
      if (submoduleRegex.test(filePath)) {
        filePath = filePath.replace(submoduleRegex, '');
      }
    }
  }

  // remove leading '/' if there is one before returning
  filePath = filePath.replace(/^\//, '');
  return filePath;
}

module.exports = Blamer;
<|endoftext|>