<commit_before>var chai = require("chai");
var expect = chai.expect;
var Bucket = require("../lib/bucket_mem.js");
var utilities = require("../lib/bucket_utilities.js");
var isKeyInContents = utilities.isKeyInContents;

describe("bucket API for getting, putting and deleting objects in a bucket",function(){

	var bucket;

	before(function() {
	  bucket = new Bucket();
	});

	after(function(done) {
		bucket.DELETEBucket(function(){
			done();
		});
	});

  it("should create a bucket with a keyMap", function(done){
  	expect(bucket).to.be.an("object");
  	expect(bucket.keyMap).to.be.an("object");
  	done()
  });

  it("should be able to add an object to a bucket and get the object by key", function(done){
  	bucket.PUTObject("sampleKey", "sampleValue", function(){
  		bucket.GETObject("sampleKey", function(err, value, key){
  			expect(value).to.equal("sampleValue");
  			done();
  		})
  	});
  });

	it("should return an error in response to GETObject when no such key", function(done){
		bucket.GETObject("notThere", function(err, value, key){
			expect(err).to.be.true;
			expect(value).to.be.undefined;
			done();
		})
	});

	it("should be able to delete an object from a bucket", function(done){

		bucket.PUTObject("objectToDelete", "valueToDelete", function(){
			bucket.DELETEObject("objectToDelete", function(){
				bucket.GETObject("objectToDelete", function(err, value, key){
					expect(err).to.be.true;
					expect(value).to.be.undefined;
					done();
				});
			});
		});
	});

});

describe('bucket API for getting a subset of objects from a bucket', function() {
/*	Implementation of AWS GET Bucket (List Objects) functionality
	Rules:
		1) 	Return individual key if key does not contain the delimiter (even if key begins with specified prefix).
		2)	Return key under common prefix if key begins with prefix and contains delimiter.  All
				keys that contain the same substring starting with the prefix and ending with the first
				occurrence of the delimiter will be grouped together and appear once under common prefix.
				For instance, "key2/sample" and "key2/moreSample" will be
				grouped together under key2/ if prefix is "key" and delimiter is "/".
		3)	If do not specify prefix, return grouped keys under common prefix if they contain
				same substring starting at beginning of the key and ending before first occurrence of delimiter.
		4)	There will be no grouping if no delimiter specified as argument in GETBucketListObjects.
		5)	If marker specified, only return keys that occur alphabetically AFTER the marker.
		6)	If specify maxKeys, only return up to that max.  All keys grouped under common-prefix,
				will only count as one key to reach maxKeys.  If not all keys returned due to reaching maxKeys,
				is_truncated will be set to true and next_marker will specify the last key returned in
				this search so that it can serve as the marker in the next search.
				*/

	//DEFAULT_LIMIT used in most tests to have explicit limit
	var DEFAULT_LIMIT = 10;

	//SMALLLIMIT used to test that truncating and nextMarker is working
	var SMALL_LIMIT = 1;

	var DELIMITER = "/";

	var bucket;

	before(function() {
	  bucket = new Bucket();
	});

	after(function(done) {
		bucket.DELETEBucket(function(){
			done();
		});
	});

	it("should return individual key if key does not contain the delimiter even if key contains prefix", function(done){
		bucket.PUTObject("key1", "valueWithoutDelimiter", function(){
			bucket.PUTObject("noMatchKey", "non-matching key", function(){
				bucket.PUTObject("key1/", "valueWithDelimiter", function(){
					bucket.GETBucketListObjects("key", null, DELIMITER, DEFAULT_LIMIT, function(response){
						expect(isKeyInContents(response, "key1")).to.be.true;
						expect(response.CommonPrefixes.indexOf("key1")).to.equal(-1);
						expect(isKeyInContents(response, "key1/")).to.be.false;
						expect(response.CommonPrefixes.indexOf("key1/")).to.be.above(-1);
						expect(isKeyInContents(response, "noMatchKey")).to.be.false;
						expect(response.CommonPrefixes.indexOf("noMatchKey")).to.equal(-1);
						done();
					});
				});
			});
		});
	});

	it("should return grouped keys under common prefix if keys start with given prefix and contain given delimiter", function(done){
		bucket.PUTObject("key/one", "value1", function(){
			bucket.PUTObject("key/two", "value2", function(){
				bucket.PUTObject("key/three", "value2", function(){
					bucket.GETBucketListObjects("ke", null, DELIMITER, DEFAULT_LIMIT, function(response){
						expect(response.CommonPrefixes.indexOf("key/")).to.be.above(-1);
						expect(isKeyInContents(response, "key/")).to.be.false;
						done();
					});
				});
			});
		});
	});

	it("should return grouped keys if no prefix given and keys match before delimiter", function(done){
		bucket.PUTObject("noPrefix/one", "value1", function(){
			bucket.PUTObject("noPrefix/two", "value2", function(){
				bucket.GETBucketListObjects(null, null, DELIMITER, DEFAULT_LIMIT, function(response){
					expect(response.CommonPrefixes.indexOf("noPrefix/")).to.be.above(-1);
					expect(isKeyInContents(response, "noPrefix")).to.be.false;
					done();
				});
			});
		});
	});

	it("should return no grouped keys if no delimiter specified in GETBucketListObjects", function(done){
		bucket.GETBucketListObjects("key", null, null, DEFAULT_LIMIT, function(response){
			expect(response.CommonPrefixes.length).to.equal(0);
			done();
		});
	});

	it("should only return keys occurring alphabetically AFTER marker when no delimiter specified", function(done){
		 bucket.PUTObject("a", "shouldBeExcluded", function(){
			bucket.PUTObject("b", "shouldBeIncluded", function(){
				bucket.GETBucketListObjects(null, "a", null, DEFAULT_LIMIT, function(response){
					expect(isKeyInContents(response, "b")).to.be.true;
					expect(isKeyInContents(response, "a")).to.be.false;
					done();
				});
			});
		});
	});

	it("should only return keys occurring alphabetically AFTER marker when delimiter specified", function(done){
		bucket.GETBucketListObjects(null, "a", DELIMITER, DEFAULT_LIMIT, function(response){
			expect(isKeyInContents(response, "b")).to.be.true;
			expect(isKeyInContents(response, "a")).to.be.false;
			done();
		});
	});

	it("should only return keys occurring alphabetically AFTER marker when delimiter and prefix specified", function(done){
		bucket.GETBucketListObjects("b", "a", DELIMITER, DEFAULT_LIMIT, function(response){
			expect(isKeyInContents(response, "b")).to.be.true;
			expect(isKeyInContents(response, "a")).to.be.false;
			done();
		});
	});

	it("should return a NextMarker if maxKeys reached", function(done){
		 bucket.PUTObject("next/", "shouldBeListed", function(){
		 	bucket.PUTObject("next/rollUp", "shouldBeRolledUp", function(){
				bucket.PUTObject("next1/", "shouldBeNextMarker", function(){
					bucket.GETBucketListObjects("next", null, DELIMITER, SMALL_LIMIT, function(response){
						expect(response.CommonPrefixes.indexOf("next/")).to.be.above(-1);
						expect(response.CommonPrefixes.indexOf("next1/")).to.equal(-1);
						expect(response.NextMarker).to.equal("next1/");
						expect(response.IsTruncated).to.be.true;
						done();
					});
				});
			});
		});
	});

});

describe("stress test for bucket API", function(){

	this.timeout(200000);
	var makeid = require("./makeid.js");
	var shuffle = require("./shuffle.js");
	var timeDiff = require("./timeDiff.js");
	var async = require("async");
	//Test should be of at least 100,000 keys
	var NUM_KEYS = 100000;
	//We expect 1,000 puts per second
	var MAX_MILLISECONDS = NUM_KEYS;

	var prefixes = ["dogs","cats","tigers","elephants","monsters"];

	//TESTPREFIX is used to test alphabetical marker so must be string alphabetized before TESTMARKER
	var TESTPREFIX = prefixes[1];
	var TESTMARKER = "cz";

	//TESTLIMIT is used to test that setting a limit will result in a truncated result with a nextMarker set
	var TESTLIMIT = 1000;
	var DELIMITER = "/";
	var ODD_DELIMITER = "$";
	var bucket;

	before(function() {
	  bucket = new Bucket();
	});

	after(function(done) {
		bucket.DELETEBucket(function(){
			done();
		});
	});

	it("should put " + NUM_KEYS + " keys into bucket and retrieve bucket list in under " + MAX_MILLISECONDS + " milliseconds", function(done){
		var data = {};
		var keys = [];

		//Create dictionary entries based on prefixes array
		for(var i=0; i< prefixes.length; i++){
			data[prefixes[i]] = [];
		}

		//Populate dictionary with random key extensions
		for (var j=0; j<NUM_KEYS; j++){
			var prefix = prefixes[j % prefixes.length];
			data[prefix].push(makeid(10));
		};

		//Populate keys array with all keys including prefixes
		for(var key in data){
			for(var k=0; k< data[key].length; k++){
				keys.push(key + DELIMITER + data[key][k]);
			};
		};

		//Shuffle the keys array so the keys appear in random order
		shuffle(keys);

		//Start timing
		var startTime = process.hrtime();

		async.each(keys, function(item, next){
			bucket.PUTObject(item, "value", next);
		}, function(err){
			if(err){
				console.error("Error" + err);
				expect(err).to.be.undefined;
				done();
			} else {
				bucket.GETBucketListObjects(null, null, DELIMITER, null, function(response){
				//Stop timing and calculate millisecond time difference
				var diff = timeDiff(startTime);
				expect(diff).to.be.below(MAX_MILLISECONDS);

				prefixes.forEach(function(prefix, idx) {
					expect(response.CommonPrefixes.indexOf(prefix+DELIMITER)).to.be.above(-1);
				});

				done();
				});
			};
		});
	});

	it("should return all keys as Contents if delimiter does not match and specify NextMarker", function(done){
		bucket.GETBucketListObjects(null, null, ODD_DELIMITER, TESTLIMIT, function(response){
			expect(response.CommonPrefixes.length).to.equal(0);
			expect(response.Contents.length).to.equal(TESTLIMIT);
			expect(response.IsTruncated).to.be.true;
			expect(response.NextMarker).to.be.a.string;
			done();
		});
	});

	it("should return only keys occurring after specified marker", function(done){
		bucket.GETBucketListObjects(null, TESTMARKER, DELIMITER, null, function(response){
			expect(response.CommonPrefixes.length).to.equal(prefixes.length-1);
			expect(response.CommonPrefixes.indexOf(TESTPREFIX)).to.equal(-1);
			expect(response.Contents.length).to.equal(0);
			expect(response.IsTruncated).to.be.false;
			expect(response.NextMarker).to.be.undefined;
			done();
		});
	});

});

<commit_msg>Revise api tests to match new return values of GetBucketListObjects.<commit_after>var chai = require("chai");
var expect = chai.expect;
var Bucket = require("../lib/bucket_mem.js");
var utilities = require("../lib/bucket_utilities.js");
var isKeyInContents = utilities.isKeyInContents;

describe("bucket API for getting, putting and deleting objects in a bucket",function(){

	var bucket;

	before(function() {
	  bucket = new Bucket();
	});

	after(function(done) {
		bucket.DELETEBucket(function(){
			done();
		});
	});

  it("should create a bucket with a keyMap", function(done){
  	expect(bucket).to.be.an("object");
  	expect(bucket.keyMap).to.be.an("object");
  	done()
  });

  it("should be able to add an object to a bucket and get the object by key", function(done){
  	bucket.PUTObject("sampleKey", "sampleValue", function(){
  		bucket.GETObject("sampleKey", function(err, value, key){
  			expect(value).to.equal("sampleValue");
  			done();
  		})
  	});
  });

	it("should return an error in response to GETObject when no such key", function(done){
		bucket.GETObject("notThere", function(err, value, key){
			expect(err).to.be.true;
			expect(value).to.be.undefined;
			done();
		})
	});

	it("should be able to delete an object from a bucket", function(done){

		bucket.PUTObject("objectToDelete", "valueToDelete", function(){
			bucket.DELETEObject("objectToDelete", function(){
				bucket.GETObject("objectToDelete", function(err, value, key){
					expect(err).to.be.true;
					expect(value).to.be.undefined;
					done();
				});
			});
		});
	});

});

describe('bucket API for getting a subset of objects from a bucket', function() {
/*	Implementation of AWS GET Bucket (List Objects) functionality
	Rules:
		1) 	Return individual key if key does not contain the delimiter (even if key begins with specified prefix).
		2)	Return key under common prefix if key begins with prefix and contains delimiter.  All
				keys that contain the same substring starting with the prefix and ending with the first
				occurrence of the delimiter will be grouped together and appear once under common prefix.
				For instance, "key2/sample" and "key2/moreSample" will be
				grouped together under key2/ if prefix is "key" and delimiter is "/".
		3)	If do not specify prefix, return grouped keys under common prefix if they contain
				same substring starting at beginning of the key and ending before first occurrence of delimiter.
		4)	There will be no grouping if no delimiter specified as argument in GETBucketListObjects.
		5)	If marker specified, only return keys that occur alphabetically AFTER the marker.
		6)	If specify maxKeys, only return up to that max.  All keys grouped under common-prefix,
				will only count as one key to reach maxKeys.  If not all keys returned due to reaching maxKeys,
				is_truncated will be set to true and next_marker will specify the last key returned in
				this search so that it can serve as the marker in the next search.
				*/

	//DEFAULT_LIMIT used in most tests to have explicit limit
	var DEFAULT_LIMIT = 10;

	//SMALLLIMIT used to test that truncating and nextMarker is working
	var SMALL_LIMIT = 1;

	var DELIMITER = "/";

	var bucket;

	before(function() {
	  bucket = new Bucket();
	});

	after(function(done) {
		bucket.DELETEBucket(function(){
			done();
		});
	});

	it("should return individual key if key does not contain the delimiter even if key contains prefix", function(done){
		bucket.PUTObject("key1", "valueWithoutDelimiter", function(){
			bucket.PUTObject("noMatchKey", "non-matching key", function(){
				bucket.PUTObject("key1/", "valueWithDelimiter", function(){
					bucket.GETBucketListObjects("key", null, DELIMITER, DEFAULT_LIMIT, function(err, response){
						expect(isKeyInContents(response, "key1")).to.be.true;
						expect(response.CommonPrefixes.indexOf("key1")).to.equal(-1);
						expect(isKeyInContents(response, "key1/")).to.be.false;
						expect(response.CommonPrefixes.indexOf("key1/")).to.be.above(-1);
						expect(isKeyInContents(response, "noMatchKey")).to.be.false;
						expect(response.CommonPrefixes.indexOf("noMatchKey")).to.equal(-1);
						done();
					});
				});
			});
		});
	});

	it("should return grouped keys under common prefix if keys start with given prefix and contain given delimiter", function(done){
		bucket.PUTObject("key/one", "value1", function(){
			bucket.PUTObject("key/two", "value2", function(){
				bucket.PUTObject("key/three", "value2", function(){
					bucket.GETBucketListObjects("ke", null, DELIMITER, DEFAULT_LIMIT, function(err, response){
						expect(response.CommonPrefixes.indexOf("key/")).to.be.above(-1);
						expect(isKeyInContents(response, "key/")).to.be.false;
						done();
					});
				});
			});
		});
	});

	it("should return grouped keys if no prefix given and keys match before delimiter", function(done){
		bucket.PUTObject("noPrefix/one", "value1", function(){
			bucket.PUTObject("noPrefix/two", "value2", function(){
				bucket.GETBucketListObjects(null, null, DELIMITER, DEFAULT_LIMIT, function(err, response){
					expect(response.CommonPrefixes.indexOf("noPrefix/")).to.be.above(-1);
					expect(isKeyInContents(response, "noPrefix")).to.be.false;
					done();
				});
			});
		});
	});

	it("should return no grouped keys if no delimiter specified in GETBucketListObjects", function(done){
		bucket.GETBucketListObjects("key", null, null, DEFAULT_LIMIT, function(err, response){
			expect(response.CommonPrefixes.length).to.equal(0);
			done();
		});
	});

	it("should only return keys occurring alphabetically AFTER marker when no delimiter specified", function(done){
		 bucket.PUTObject("a", "shouldBeExcluded", function(){
			bucket.PUTObject("b", "shouldBeIncluded", function(){
				bucket.GETBucketListObjects(null, "a", null, DEFAULT_LIMIT, function(err, response){
					expect(isKeyInContents(response, "b")).to.be.true;
					expect(isKeyInContents(response, "a")).to.be.false;
					done();
				});
			});
		});
	});

	it("should only return keys occurring alphabetically AFTER marker when delimiter specified", function(done){
		bucket.GETBucketListObjects(null, "a", DELIMITER, DEFAULT_LIMIT, function(err, response){
			expect(isKeyInContents(response, "b")).to.be.true;
			expect(isKeyInContents(response, "a")).to.be.false;
			done();
		});
	});

	it("should only return keys occurring alphabetically AFTER marker when delimiter and prefix specified", function(done){
		bucket.GETBucketListObjects("b", "a", DELIMITER, DEFAULT_LIMIT, function(err, response){
			expect(isKeyInContents(response, "b")).to.be.true;
			expect(isKeyInContents(response, "a")).to.be.false;
			done();
		});
	});

	it("should return a NextMarker if maxKeys reached", function(done){
		 bucket.PUTObject("next/", "shouldBeListed", function(){
		 	bucket.PUTObject("next/rollUp", "shouldBeRolledUp", function(){
				bucket.PUTObject("next1/", "shouldBeNextMarker", function(){
					bucket.GETBucketListObjects("next", null, DELIMITER, SMALL_LIMIT, function(err, response){
						expect(response.CommonPrefixes.indexOf("next/")).to.be.above(-1);
						expect(response.CommonPrefixes.indexOf("next1/")).to.equal(-1);
						expect(response.NextMarker).to.equal("next1/");
						expect(response.IsTruncated).to.be.true;
						done();
					});
				});
			});
		});
	});

});

describe("stress test for bucket API", function(){

	this.timeout(200000);
	var makeid = require("./makeid.js");
	var shuffle = require("./shuffle.js");
	var timeDiff = require("./timeDiff.js");
	var async = require("async");
	//Test should be of at least 100,000 keys
	var NUM_KEYS = 100000;
	//We expect 1,000 puts per second
	var MAX_MILLISECONDS = NUM_KEYS;

	var prefixes = ["dogs","cats","tigers","elephants","monsters"];

	//TESTPREFIX is used to test alphabetical marker so must be string alphabetized before TESTMARKER
	var TESTPREFIX = prefixes[1];
	var TESTMARKER = "cz";

	//TESTLIMIT is used to test that setting a limit will result in a truncated result with a nextMarker set
	var TESTLIMIT = 1000;
	var DELIMITER = "/";
	var ODD_DELIMITER = "$";
	var bucket;

	before(function() {
	  bucket = new Bucket();
	});

	after(function(done) {
		bucket.DELETEBucket(function(){
			done();
		});
	});

	it("should put " + NUM_KEYS + " keys into bucket and retrieve bucket list in under " + MAX_MILLISECONDS + " milliseconds", function(done){
		var data = {};
		var keys = [];

		//Create dictionary entries based on prefixes array
		for(var i=0; i< prefixes.length; i++){
			data[prefixes[i]] = [];
		}

		//Populate dictionary with random key extensions
		for (var j=0; j<NUM_KEYS; j++){
			var prefix = prefixes[j % prefixes.length];
			data[prefix].push(makeid(10));
		};

		//Populate keys array with all keys including prefixes
		for(var key in data){
			for(var k=0; k< data[key].length; k++){
				keys.push(key + DELIMITER + data[key][k]);
			};
		};

		//Shuffle the keys array so the keys appear in random order
		shuffle(keys);

		//Start timing
		var startTime = process.hrtime();

		async.each(keys, function(item, next){
			bucket.PUTObject(item, "value", next);
		}, function(err){
			if(err){
				console.error("Error" + err);
				expect(err).to.be.undefined;
				done();
			} else {
				bucket.GETBucketListObjects(null, null, DELIMITER, null, function(err, response){
				//Stop timing and calculate millisecond time difference
				var diff = timeDiff(startTime);
				expect(diff).to.be.below(MAX_MILLISECONDS);

				prefixes.forEach(function(prefix, idx) {
					expect(response.CommonPrefixes.indexOf(prefix+DELIMITER)).to.be.above(-1);
				});

				done();
				});
			};
		});
	});

	it("should return all keys as Contents if delimiter does not match and specify NextMarker", function(done){
		bucket.GETBucketListObjects(null, null, ODD_DELIMITER, TESTLIMIT, function(err, response){
			expect(response.CommonPrefixes.length).to.equal(0);
			expect(response.Contents.length).to.equal(TESTLIMIT);
			expect(response.IsTruncated).to.be.true;
			expect(response.NextMarker).to.be.a.string;
			done();
		});
	});

	it("should return only keys occurring after specified marker", function(done){
		bucket.GETBucketListObjects(null, TESTMARKER, DELIMITER, null, function(err, response){
			expect(response.CommonPrefixes.length).to.equal(prefixes.length-1);
			expect(response.CommonPrefixes.indexOf(TESTPREFIX)).to.equal(-1);
			expect(response.Contents.length).to.equal(0);
			expect(response.IsTruncated).to.be.false;
			expect(response.NextMarker).to.be.undefined;
			done();
		});
	});

});

<|endoftext|>


<commit_before>'use strict';

var express = require('express');
var controller = require('./document.controller');
var auth = require('../../auth/auth.service');

var router = express.Router();

router.get('/', auth.isAuthenticated(), controller.index);
router.get('/:id', auth.isAuthenticated(), controller.show);
router.post('/', controller.create);
router.put('/:id', auth.isAuthenticated(), controller.update);
router.patch('/:id', auth.isAuthenticated(), controller.update);
router.delete('/:id', auth.isAuthenticated(), controller.destroy);
router.get('/preview/:id', controller.getPreview);

module.exports = router;
<commit_msg>admin role api permissions<commit_after>'use strict';

var express = require('express');
var controller = require('./document.controller');
var auth = require('../../auth/auth.service');

var router = express.Router();

router.get('/', auth.isAuthenticated(), controller.index);
router.get('/:id', auth.isAuthenticated(), controller.show);
router.post('/', auth.hasRole('admin'), controller.create);
router.put('/:id', auth.isAuthenticated(), controller.update);
router.patch('/:id', auth.isAuthenticated(), controller.update);
router.delete('/:id', auth.hasRole('admin'), controller.destroy);
router.get('/preview/:id', controller.getPreview);

module.exports = router;
<|endoftext|>


<commit_before>// DNS list module
var dns = require('dns');
var net = require('net');
var is_rfc1918 = require('./net_utils').is_rfc1918;

exports.enable_stats = false;
exports.disable_allowed = false;
exports.redis_host = '127.0.0.1:6379';
var redis_client;

exports.lookup = function (lookup, zone, cb) {
    var self = this;

    if (!lookup || !zone) {
        process.nextTick(function () {
            return cb(new Error("missing data"));
        });
    }

    if (this.enable_stats && !redis_client) {
        var redis = require('redis');
        var host_port = this.redis_host.split(':');
        if (!host_port[0]) host_port[0] = '127.0.0.1';
        if (!host_port[1]) {
            host_port[1] = 6379;
        }
        else {
            host_port[1] = parseInt(host_port[1], 10);
        }
        redis_client = redis.createClient(host_port[1], host_port[0]);
        redis_client.on('error', function (err) {
            self.logerror("Redis error: " + err);
            redis_client.quit();
            redis_client = null; // should force a reconnect - not sure if that's the right thing but better than nothing...
        });
    }

    // Reverse lookup if IPv4 address
    if (net.isIPv4(lookup)) {
        lookup = lookup.split('.').reverse().join('.');
    }
    // TODO: IPv6 not supported
    else if (net.isIPv6(lookup)) {
        process.nextTick(function () {
            return cb(new Error("IPv6 not supported"));
        });
    }

    if (this.enable_stats) {
        var start = new Date().getTime();
    }

    // Build the query, adding the root dot if missing
    var query = [lookup, zone].join('.');
    if (query[query.length-1] !== '.') {
        query += '.';
    }
    this.logdebug('looking up: ' + query);
    dns.resolve(query, 'A', function (err, a) {
        // Statistics
        if (self.enable_stats) {
            var elapsed = new Date().getTime() - start;
            redis_client.hincrby('dns-list-stat:' + zone, 'TOTAL', 1);
            (err)
                ? redis_client.hincrby('dns-list-stat:' + zone, err.code, 1)
                : redis_client.hincrby('dns-list-stat:' + zone, 'LISTED', 1);
            redis_client.hget('dns-list-stat:' + zone, 'AVG_RT', function (err, rt) {
                if (!err)
                    redis_client.hset('dns-list-stat:' + zone, 'AVG_RT',
                        (parseInt(rt) ? (parseInt(elapsed) + parseInt(rt))/2 : parseInt(elapsed)));
            });
        }
        // Check for a return of 127.0.0.1 or outside 127/8
        // This should *never* happen on a proper DNS list
        if (a && (a[0] === '127.0.0.1' || (a[0].split('.'))[0] !== '127')) {
            self.disable_zone(zone, a);
            // Return a null A record instead
            return cb(err, null);
        }
        // Disable list if it starts timing out
        if (err && err.code === 'ETIMEOUT') self.disable_zone(zone, err.code);
        if (err && err.code === 'ENOTFOUND') {
            return cb(null, a);  // Not an error for a DNSBL
        }
        return cb(err, a);
    });
};

exports.multi = function (lookup, zones, cb) {
    if (!lookup || !zones) return cb();
    if (typeof zones === 'string') zones = [ '' + zones ];
    var self = this;
    var listed = [];
    var pending = 0;
    zones.forEach(function (zone) {
        pending++;
        self.lookup(lookup, zone, function (err, a) {
            pending--;
            if (a) listed.push(zone);
            cb(err, zone, a, pending);
            // All queries completed?
            if (pending === 0) {
                // Statistics: check hit overlap
                if (self.enable_stats && listed.length > 0) {
                    listed.forEach(function (zone) {
                        for (var i=0; i < listed.length; i++) {
                            (listed[i] === zone)
                                ? redis_client.hincrby('dns-list-overlap:' + zone, 'TOTAL', 1)
                                : redis_client.hincrby('dns-list-overlap:' + zone, listed[i], 1);
                        }
                    });
                }
            }
        });
    });
};

// Return first positive or last result.
exports.first = function (lookup, zones, cb) {
    if (!lookup || !zones) return cb();
    if (typeof zones === 'string') zones = [ '' + zones ];
    var run_cb = 0;
    this.multi(lookup, zones, function (err, zone, a, pending) {
        if (!run_cb && ((!err && a) || pending === 0)) {
            run_cb++;
            return cb(err, zone, a);
        }
    });
};

exports.check_zones = function (interval) {
    var self = this;
    this.disable_allowed = true;
    if (interval) interval = parseInt(interval);
    if ((this.zones && this.zones.length) || (this.disabled_zones && this.disabled_zones.length)) {
        var zones = [];
        if (this.zones && this.zones.length) zones = zones.concat(this.zones);
        if (this.disabled_zones && this.disabled_zones.length) zones = zones.concat(this.disabled_zones);

        // A DNS list should never return positive or an error for this lookup
        // If it does we remove it from the zone list and do no further lookups
        this.multi('127.0.0.1', zones, function (err, zone, a, pending) {
            if (a || (err && err.code === 'ETIMEOUT')) {
                return self.disable_zone(zone, ((a) ? a : err.code));
            }
            // Try the test point
            self.lookup('127.0.0.2', zone, function (err, a) {
                if (!a) {
                    self.logwarn('zone \'' + zone + '\' did not respond to test point (' + err + ')');
                    return self.disable_zone(zone, a);
                }
                // Was this zone previously disabled?
                if (self.zones.indexOf(zone) === -1) {
                    self.loginfo('re-enabling zone ' + zone);
                    self.zones.push(zone);
                }
            });
        });
    }
    // Set-up a timer to re-test
    if (interval && interval >= 5 && !this._interval) {
        this.logdebug('will re-test list zones every ' + interval + ' minutes');
        this._interval = setInterval(function () {
            self.check_zones();
        }, (interval * 60) * 1000);
    }
};

exports.disable_zone = function (zone, result) {
    if (zone && this.zones && this.zones.length && this.disable_allowed) {
        var idx = this.zones.indexOf(zone);
        if (idx !== -1) {
            this.zones.splice(idx, 1);
            if (!(this.disabled_zones && this.disabled_zones.length)) this.disabled_zones = [];
            if (this.disabled_zones.indexOf(zone) === -1) this.disabled_zones.push(zone);
            this.logwarn('disabling zone \'' + zone + '\'' + (result ? ': ' + result : ''));
        }
    }
};
<commit_msg>dns_list_base: moved init_redis into new method<commit_after>// DNS list module
var dns = require('dns');
var net = require('net');
var is_rfc1918 = require('./net_utils').is_rfc1918;

exports.enable_stats = false;
exports.disable_allowed = false;
exports.redis_host = '127.0.0.1:6379';
var redis_client;

exports.lookup = function (lookup, zone, cb) {
    var self = this;

    if (!lookup || !zone) {
        process.nextTick(function () {
            return cb(new Error("missing data"));
        });
    }

    if (this.enable_stats) { init_redis(); }

    // Reverse lookup if IPv4 address
    if (net.isIPv4(lookup)) {
        lookup = lookup.split('.').reverse().join('.');
    }
    else if (net.isIPv6(lookup)) {
        // TODO: IPv6 not supported
        process.nextTick(function () {
            return cb(new Error("IPv6 not supported"));
        });
    }

    if (this.enable_stats) {
        var start = new Date().getTime();
    }

    // Build the query, adding the root dot if missing
    var query = [lookup, zone].join('.');
    if (query[query.length-1] !== '.') {
        query += '.';
    }
    this.logdebug('looking up: ' + query);
    dns.resolve(query, 'A', function (err, a) {
        // Statistics
        if (self.enable_stats) {
            var elapsed = new Date().getTime() - start;
            redis_client.hincrby('dns-list-stat:' + zone, 'TOTAL', 1);
            (err)
                ? redis_client.hincrby('dns-list-stat:' + zone, err.code, 1)
                : redis_client.hincrby('dns-list-stat:' + zone, 'LISTED', 1);
            redis_client.hget('dns-list-stat:' + zone, 'AVG_RT', function (err, rt) {
                if (!err)
                    redis_client.hset('dns-list-stat:' + zone, 'AVG_RT',
                        (parseInt(rt) ? (parseInt(elapsed) + parseInt(rt))/2 : parseInt(elapsed)));
            });
        }
        // Check for a return of 127.0.0.1 or outside 127/8
        // This should *never* happen on a proper DNS list
        if (a && (a[0] === '127.0.0.1' || (a[0].split('.'))[0] !== '127')) {
            self.disable_zone(zone, a);
            // Return a null A record instead
            return cb(err, null);
        }
        // Disable list if it starts timing out
        if (err && err.code === 'ETIMEOUT') self.disable_zone(zone, err.code);
        if (err && err.code === 'ENOTFOUND') {
            return cb(null, a);  // Not an error for a DNSBL
        }
        return cb(err, a);
    });
};

exports.init_redis = function () {
    if (redis_client) { return; }

    var redis = require('redis');
    var host_port = this.redis_host.split(':');
    if (!host_port[0]) host_port[0] = '127.0.0.1';
    if (!host_port[1]) {
        host_port[1] = 6379;
    }
    else {
        host_port[1] = parseInt(host_port[1], 10);
    }

    redis_client = redis.createClient(host_port[1], host_port[0]);
    redis_client.on('error', function (err) {
        self.logerror("Redis error: " + err);
        redis_client.quit();
        redis_client = null; // should force a reconnect - not sure if that's the right thing but better than nothing...
    });
};

exports.multi = function (lookup, zones, cb) {
    if (!lookup || !zones) return cb();
    if (typeof zones === 'string') zones = [ '' + zones ];
    var self = this;
    var listed = [];
    var pending = 0;
    zones.forEach(function (zone) {
        pending++;
        self.lookup(lookup, zone, function (err, a) {
            pending--;
            if (a) listed.push(zone);
            cb(err, zone, a, pending);
            // All queries completed?
            if (pending === 0) {
                // Statistics: check hit overlap
                if (self.enable_stats && listed.length > 0) {
                    listed.forEach(function (zone) {
                        for (var i=0; i < listed.length; i++) {
                            (listed[i] === zone)
                                ? redis_client.hincrby('dns-list-overlap:' + zone, 'TOTAL', 1)
                                : redis_client.hincrby('dns-list-overlap:' + zone, listed[i], 1);
                        }
                    });
                }
            }
        });
    });
};

// Return first positive or last result.
exports.first = function (lookup, zones, cb) {
    if (!lookup || !zones) return cb();
    if (typeof zones === 'string') zones = [ '' + zones ];
    var run_cb = 0;
    this.multi(lookup, zones, function (err, zone, a, pending) {
        if (!run_cb && ((!err && a) || pending === 0)) {
            run_cb++;
            return cb(err, zone, a);
        }
    });
};

exports.check_zones = function (interval) {
    var self = this;
    this.disable_allowed = true;
    if (interval) interval = parseInt(interval);
    if ((this.zones && this.zones.length) || (this.disabled_zones && this.disabled_zones.length)) {
        var zones = [];
        if (this.zones && this.zones.length) zones = zones.concat(this.zones);
        if (this.disabled_zones && this.disabled_zones.length) zones = zones.concat(this.disabled_zones);

        // A DNS list should never return positive or an error for this lookup
        // If it does we remove it from the zone list and do no further lookups
        this.multi('127.0.0.1', zones, function (err, zone, a, pending) {
            if (a || (err && err.code === 'ETIMEOUT')) {
                return self.disable_zone(zone, ((a) ? a : err.code));
            }
            // Try the test point
            self.lookup('127.0.0.2', zone, function (err, a) {
                if (!a) {
                    self.logwarn('zone \'' + zone + '\' did not respond to test point (' + err + ')');
                    return self.disable_zone(zone, a);
                }
                // Was this zone previously disabled?
                if (self.zones.indexOf(zone) === -1) {
                    self.loginfo('re-enabling zone ' + zone);
                    self.zones.push(zone);
                }
            });
        });
    }
    // Set-up a timer to re-test
    if (interval && interval >= 5 && !this._interval) {
        this.logdebug('will re-test list zones every ' + interval + ' minutes');
        this._interval = setInterval(function () {
            self.check_zones();
        }, (interval * 60) * 1000);
    }
};

exports.disable_zone = function (zone, result) {
    if (zone && this.zones && this.zones.length && this.disable_allowed) {
        var idx = this.zones.indexOf(zone);
        if (idx !== -1) {
            this.zones.splice(idx, 1);
            if (!(this.disabled_zones && this.disabled_zones.length)) this.disabled_zones = [];
            if (this.disabled_zones.indexOf(zone) === -1) this.disabled_zones.push(zone);
            this.logwarn('disabling zone \'' + zone + '\'' + (result ? ': ' + result : ''));
        }
    }
};
<|endoftext|>


<commit_before>var path = require('path')

var HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    debug: true,
    devtool: 'cheap-source-map',
    resolve: {
        extensions: [
            '',
            '.styl',
            '.ts',
            '.tsx',
            '.js',
            '.json',
            '.jade',
            '.html',
            '.less',
            '.css',
            '.json'
        ]
    },

    context: path.join(__dirname, 'src'),

    plugins: [new HtmlWebpackPlugin({
        template: './html/layout.html',
        inject: false
    })],

    entry: {
        'js/1-vendor':
        [
            require.resolve('jquery'),
            './js/libraries/resampler.js',
            require.resolve('indexeddbshim'),
            require.resolve('sugar-date'),
            './js/libraries/jquery.oembed.js'
        ],
        'js/app': './js/app',
        'js/config': '../config.js' // TODO: Add server version info to config.js ~ F
    },

    output: {
        path: path.join(__dirname, 'public'),
        filename: '[name].js',
        chunkFilename: '[name].js'
    },

    stats: {
        colors: true,
        reasons: true
    },

    module: {
        loaders: [
            {
                test: require.resolve('jquery'),
                loader: "expose?$!expose?jQuery"
            },
            {
                test: /react\.js$/,
                loader: "expose?React"
            },
            {
              test: /resampler\.js$/,
              loader: 'expose?Resample!imports?this=>window!exports?Resample'
            },
            {
              test: /jquery\.oembed\.js$/,
              loader: 'imports?jQuery=jquery'
            },
            {
                test: /\.html$/,
                loader: 'html-loader?attrs=img:src link:href'
            },
            {
                test: /\.png$/,
                loader: "file?name=images/[hash].[ext]"
            },
            {
                test: /\.styl$/,
                loader: 'file?name=css/[hash].css!stylus'
            },
            {
                test: /\.ts(x?)$/,
                exclude: /(node_modules|bower_components|libraries)/,
                loader: 'babel!ts-loader'
            },
            {
                test: /\.js(x?)$/,
                exclude: /(node_modules|bower_components|libraries)/,
                loader: 'babel'
            },
            {
                test: /\.jade$/,
                loader: 'jade-loader'
            },
            {
                test: /\.json$/,
                loader: 'json-loader'
            }
        ]
    },
    node: {
        Buffer: true,
        console: true,
        global: true,
        fs: "empty"
    },
    externals: {
        jquery: 'jQuery'
    }
};
<commit_msg>webpack: remove duplicated json extension<commit_after>var path = require('path')

var HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    debug: true,
    devtool: 'cheap-source-map',
    resolve: {
        extensions: [
            '',
            '.styl',
            '.ts',
            '.tsx',
            '.js',
            '.jade',
            '.html',
            '.less',
            '.css',
            '.json'
        ]
    },

    context: path.join(__dirname, 'src'),

    plugins: [new HtmlWebpackPlugin({
        template: './html/layout.html',
        inject: false
    })],

    entry: {
        'js/1-vendor':
        [
            require.resolve('jquery'),
            './js/libraries/resampler.js',
            require.resolve('indexeddbshim'),
            require.resolve('sugar-date'),
            './js/libraries/jquery.oembed.js'
        ],
        'js/app': './js/app',
        'js/config': '../config.js' // TODO: Add server version info to config.js ~ F
    },

    output: {
        path: path.join(__dirname, 'public'),
        filename: '[name].js',
        chunkFilename: '[name].js'
    },

    stats: {
        colors: true,
        reasons: true
    },

    module: {
        loaders: [
            {
                test: require.resolve('jquery'),
                loader: "expose?$!expose?jQuery"
            },
            {
                test: /react\.js$/,
                loader: "expose?React"
            },
            {
              test: /resampler\.js$/,
              loader: 'expose?Resample!imports?this=>window!exports?Resample'
            },
            {
              test: /jquery\.oembed\.js$/,
              loader: 'imports?jQuery=jquery'
            },
            {
                test: /\.html$/,
                loader: 'html-loader?attrs=img:src link:href'
            },
            {
                test: /\.png$/,
                loader: "file?name=images/[hash].[ext]"
            },
            {
                test: /\.styl$/,
                loader: 'file?name=css/[hash].css!stylus'
            },
            {
                test: /\.ts(x?)$/,
                exclude: /(node_modules|bower_components|libraries)/,
                loader: 'babel!ts-loader'
            },
            {
                test: /\.js(x?)$/,
                exclude: /(node_modules|bower_components|libraries)/,
                loader: 'babel'
            },
            {
                test: /\.jade$/,
                loader: 'jade-loader'
            },
            {
                test: /\.json$/,
                loader: 'json-loader'
            }
        ]
    },
    node: {
        Buffer: true,
        console: true,
        global: true,
        fs: "empty"
    },
    externals: {
        jquery: 'jQuery'
    }
};
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013, David Farrell <shokku.ra@gmail.com>
 *  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * - Neither the name of iBot nor the names of its contributors may be used to
 *   endorse or promote products derived from this software without specific
 *   prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

exports.mod = function(context)
{
	this.segmentDuration = 10000;

	this._load = function(data)
	{

	}

	this._save = function()
	{

	}

	this._suspend = function()
	{

	}

	this._resume = function(data)
	{

	}

	this.core$cmd = function(server, prefix, target, cmd, params)
	{
		if(cmd === 'scheduler')
		{
			switch(params[0])
			{
				case '+':
					server.do('scheduler$fire', server, [parseInt(params[1]), parseInt(params[2])], undefined, 'test', server, target);
					break;
			}
		}
	}

	this._fire = function()
	{
		var args = Array.prototype.slice.call(arguments, 0);
		var server = args[0];
		var timespan = args[1];
		var name = args[2];

		if(server === undefined) throw new Error('Server undefined');

		var ms = 0;

		if(typeof timespan === 'object')
		{
			if(typeof timespan[0] === 'number' && !isNaN(timespan[0])) ms += timespan[0];
			if(typeof timespan[1] === 'number' && !isNaN(timespan[1])) ms += timespan[1] * this.segmentDuration;
		}
		else if(typeof timespan === 'number')
		{
			ms = timespan;
		}

		var a = [ms, name].concat(args.slice(3));
		server.fireTimed.apply(server, a);
	}

	this.scheduler$test = function(server, target)
	{
		server.send('PRIVMSG ' + target + ' :fired');
	}
}
<commit_msg>Added method stubs in scheduler for _loaded and _unloaded.<commit_after>/*
 * Copyright (c) 2013, David Farrell <shokku.ra@gmail.com>
 *  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * - Neither the name of iBot nor the names of its contributors may be used to
 *   endorse or promote products derived from this software without specific
 *   prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

exports.mod = function(context)
{
	this.segmentDuration = 10000;

	this._load = function(data)
	{

	}

	this._save = function()
	{

	}

	this._suspend = function()
	{

	}

	this._resume = function(data)
	{

	}

	this._loaded = function(server)
	{

	}

	this._unloaded = function(server)
	{

	}

	this.core$cmd = function(server, prefix, target, cmd, params)
	{
		if(cmd === 'scheduler')
		{
			switch(params[0])
			{
				case '+':
					server.do('scheduler$fire', server, [parseInt(params[1]), parseInt(params[2])], undefined, 'test', server, target);
					break;
			}
		}
	}

	this._fire = function()
	{
		var args = Array.prototype.slice.call(arguments, 0);
		var server = args[0];
		var timespan = args[1];
		var name = args[2];

		if(server === undefined) throw new Error('Server undefined');

		var ms = 0;

		if(typeof timespan === 'object')
		{
			if(typeof timespan[0] === 'number' && !isNaN(timespan[0])) ms += timespan[0];
			if(typeof timespan[1] === 'number' && !isNaN(timespan[1])) ms += timespan[1] * this.segmentDuration;
		}
		else if(typeof timespan === 'number')
		{
			ms = timespan;
		}

		var a = [ms, name].concat(args.slice(3));
		server.fireTimed.apply(server, a);
	}

	this.scheduler$test = function(server, target)
	{
		server.send('PRIVMSG ' + target + ' :fired');
	}
}
<|endoftext|>


<commit_before>// Copyright 2013-2018, University of Colorado Boulder

/**
 * Convenience type for creating a vertical list of AquaRadioButtons.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var AquaRadioButton = require( 'SUN/AquaRadioButton' );
  var HStrut = require( 'SCENERY/nodes/HStrut' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var sun = require( 'SUN/sun' );
  var Tandem = require( 'TANDEM/Tandem' );
  var VBox = require( 'SCENERY/nodes/VBox' );

  // a11y - An id for each instance of VerticalAquaRadioButtonGroup, passed to individual buttons in the group.
  // Each button in a radio button group must have the same "name" attribute to be considered in a group, otherwise
  // arrow keys will navigate all radio type inputs in the document.
  var instanceCount = 0;

  /**
   * Main constructor.
   *
   * @param {Object[]} items - Each item describes a radio button, and is an object with these properties:
   *    node: Node, // label for the button
   *    value: *, // value associated with the button
   *    property: Property.<*>, // Property associated with the button
   *    [tandemName: Tandem], // optional tandem for PhET-iO
   *    [labelContent: string] // optional label for a11y
   * @param {Object} [options]
   * @constructor
   */
  function VerticalAquaRadioButtonGroup( items, options ) {

    instanceCount++;

    options = _.extend( {

      // dilation of pointer areas, y dimension is computed
      touchAreaXDilation: 0,
      mouseAreaXDilation: 0,

      // uniform radius of all buttons
      radius: AquaRadioButton.DEFAULT_RADIUS,

      // options passed to constructor of the AquaRadioButtons
      radioButtonOptions: {},

      //TODO #344 this is the total of left and right margins, replace with xMargin?
      padding: 8,

      // supertype options
      spacing: 3, // vertical space between each button
      tandem: Tandem.required,

      // supertype a11y options
      tagName: 'ul',
      groupFocusHighlight: true

    }, options );

    // Verify that the client hasn't set options that we will be overwriting.
    assert && assert( options.radioButtonOptions.radius === undefined,
      'VerticalAquaRadioButtonGroup sets radioButtonOptions.radius' );
    assert && assert( !options.children, 'VerticalAquaRadioButtonGroup sets children' );

    // Determine the max item width
    var maxWidth = 0;
    for ( var i = 0; i < items.length; i++ ) {
      maxWidth = Math.max( maxWidth, items[ i ].node.width );
    }

    // Uniform button width
    var buttonWidth = maxWidth + options.padding;

    // Create a radio button for each item
    options.children = [];
    for ( i = 0; i < items.length; i++ ) {

      var item = items[ i ];

      // Content for the radio button. Add an invisible strut, so that buttons have uniform width.
      var content = new Node( {
        children: [ new HStrut( buttonWidth + options.padding ), item.node ]
      } );

      var radioButton = new AquaRadioButton( item.property, item.value, content,
        _.extend( {}, options.radioButtonOptions, {
          radius: options.radius,
          tandem: item.tandemName ? options.tandem.createTandem( item.tandemName ) : Tandem.required,
          labelContent: item.labelContent || null,
          a11yNameAttribute: instanceCount
        } ) );

      // set pointer areas, y dimensions are computed
      var yDilation = options.spacing / 2;
      radioButton.mouseArea = radioButton.localBounds.dilatedXY( options.mouseAreaXDilation, yDilation );
      radioButton.touchArea = radioButton.localBounds.dilatedXY( options.touchAreaXDilation, yDilation );

      options.children.push( radioButton );
    }

    VBox.call( this, options );
  }

  sun.register( 'VerticalAquaRadioButtonGroup', VerticalAquaRadioButtonGroup );

  return inherit( VBox, VerticalAquaRadioButtonGroup );
} );
<commit_msg>move radius option to radioButtonOptions for VerticalAquaRadioButtonGroup, https://github.com/phetsims/sun/issues/344<commit_after>// Copyright 2013-2018, University of Colorado Boulder

/**
 * Convenience type for creating a vertical list of AquaRadioButtons.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var AquaRadioButton = require( 'SUN/AquaRadioButton' );
  var HStrut = require( 'SCENERY/nodes/HStrut' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var sun = require( 'SUN/sun' );
  var Tandem = require( 'TANDEM/Tandem' );
  var VBox = require( 'SCENERY/nodes/VBox' );

  // a11y - An id for each instance of VerticalAquaRadioButtonGroup, passed to individual buttons in the group.
  // Each button in a radio button group must have the same "name" attribute to be considered in a group, otherwise
  // arrow keys will navigate all radio type inputs in the document.
  var instanceCount = 0;

  /**
   * Main constructor.
   *
   * @param {Object[]} items - Each item describes a radio button, and is an object with these properties:
   *    node: Node, // label for the button
   *    value: *, // value associated with the button
   *    property: Property.<*>, // Property associated with the button
   *    [tandemName: Tandem], // optional tandem for PhET-iO
   *    [labelContent: string] // optional label for a11y
   * @param {Object} [options]
   * @constructor
   */
  function VerticalAquaRadioButtonGroup( items, options ) {

    instanceCount++;

    options = _.extend( {

      // options passed to constructor of the AquaRadioButtons
      radioButtonOptions: {},

      // dilation of pointer areas for each radio button, y dimension is computed
      touchAreaXDilation: 0,
      mouseAreaXDilation: 0,

      //TODO #344 this is the total of left and right margins, replace with xMargin?
      padding: 8,

      // supertype options
      spacing: 3, // vertical space between each button
      tandem: Tandem.required,

      // supertype a11y options
      tagName: 'ul',
      groupFocusHighlight: true

    }, options );

    // Verify that the client hasn't set options that we will be overwriting.
    assert && assert( !options.children, 'VerticalAquaRadioButtonGroup sets children' );

    // Determine the max item width
    var maxWidth = 0;
    for ( var i = 0; i < items.length; i++ ) {
      maxWidth = Math.max( maxWidth, items[ i ].node.width );
    }

    // Uniform button width
    var buttonWidth = maxWidth + options.padding;

    // Create a radio button for each item
    options.children = [];
    for ( i = 0; i < items.length; i++ ) {

      var item = items[ i ];

      // Content for the radio button. Add an invisible strut, so that buttons have uniform width.
      var content = new Node( {
        children: [ new HStrut( buttonWidth + options.padding ), item.node ]
      } );

      var radioButton = new AquaRadioButton( item.property, item.value, content,
        _.extend( {}, options.radioButtonOptions, {
          tandem: item.tandemName ? options.tandem.createTandem( item.tandemName ) : Tandem.required,
          labelContent: item.labelContent || null,
          a11yNameAttribute: instanceCount
        } ) );

      // set pointer areas, y dimensions are computed
      var yDilation = options.spacing / 2;
      radioButton.mouseArea = radioButton.localBounds.dilatedXY( options.mouseAreaXDilation, yDilation );
      radioButton.touchArea = radioButton.localBounds.dilatedXY( options.touchAreaXDilation, yDilation );

      options.children.push( radioButton );
    }

    VBox.call( this, options );
  }

  sun.register( 'VerticalAquaRadioButtonGroup', VerticalAquaRadioButtonGroup );

  return inherit( VBox, VerticalAquaRadioButtonGroup );
} );
<|endoftext|>


<commit_before>/*jslint continue: true, devel: true, evil: true, indent: 2, nomen: true, plusplus: true, regexp: true, rhino: true, sloppy: true, sub: true, unparam: true, vars: true, white: true */
/*global _, HostAdapter, hostAdapter */
function handleResponse(responseText, response) {
  'use strict';

  console.log('Got response');

  if (response.statusCode !== 200) {
    console.error('Got bad status code ' + response.statusCode);
    return null;
  }

  var resultObject = JSON.parse(responseText);

  console.log('Got ' + resultObject.hits.length + ' hits');

  return _(resultObject.hits).chain().filter(function (hit) {
    return !!hit.url;
  }).map(function (hit) {
    console.log('got hit url = ' + hit.url);
    return {
      label : hit.title || '',
      iconUrl: 'https://news.ycombinator.com/favicon.ico',
      uri : hit.url,
      // embeddable: false, let solveforall guess
      summaryHtml: _(hit.story_text || '').escapeHTML(),
      relevance: 0.4 * (1.0 - Math.pow(2.0, -Math.max((hit.points || 0), 1) * 0.01))
    };
  }).value();
}

function generateResults(recognitionResults, q, context) {
  'use strict';

  if (context.isSuggestionQuery) {
    return [];
  }

  var url = 'https://hn.algolia.com/api/v1/search';

  var request = hostAdapter.makeWebRequest(url, {
    data: {
      query: q
    }
  });

  request.send('handleResponse');

  return HostAdapter.SUSPEND;
}
<commit_msg>Output a single content block of Hacker News results<commit_after>/*jslint continue: true, devel: true, evil: true, indent: 2, nomen: true, plusplus: true, regexp: true, rhino: true, sloppy: true, sub: true, unparam: true, vars: true, white: true */
/*global _, HostAdapter, hostAdapter */

var ICON_URL = 'https://news.ycombinator.com/favicon.ico';
var BASE_RELEVANCE = 0.4;

function makeResponseHandler(q, context) {
  'use strict';

  return function (responseText, response) {
    'use strict';

    console.log('Got response');

    if (response.statusCode !== 200) {
      console.error('Got bad status code ' + response.statusCode);
      return null;
    }

    var resultObject = JSON.parse(responseText);
    var hits = resultObject.hits;
    console.log('Got ' + hits.length + ' hits');

    if (hits.length === 0) {
      return [];
    }

    var outputLinks = false;

    if (!context.isSuggestionQuery || (context.settings.outputLinks !== 'true')) {
      var xml = <s><![CDATA[
<!doctype html>

  <title>Hacker News Search Results</title>

Hacker News search results for "<%= q %>":

      <% _(hits).each(function (hit) {
          if (hit.url) { %>

            <a href="<%= hit.url %>" target="_top"><%= hit.title || '(No title)' %>
            <% if (hit.story_text) { %>
              : <%= _(hit.story_text).prune(100) %>
            <% } %>

      <%  } }); %>

      Search results provided by Algolia.

]]></s>;

      var template = xml.toString();
      var model = { q: q, hits: hits };
      var content = ejs.render(template, model);

      return [{
        label: 'Hacker News Search',
        uri: 'https://hn.algolia.com/#!/story/forever/0/' + encodeURIComponent(q),
        tooltip: 'Hacker News Search results for "' + _(q).escapeHTML() + '"',
        iconUrl: ICON_URL,
        relevance: BASE_RELEVANCE,
        content: content,
        serverSideSanitized: true,
        categories: [{value: 'programming', weight: 1.0}, {value: 'business', weight: 0.5}]
      }];

    } else {
      return _(hits).chain().filter(function (hit) {
        return !!hit.url;
      }).map(function (hit) {
        console.log('got hit url = ' + hit.url);
        return {
          label : hit.title || '',
          iconUrl: ICON_URL,
          uri : hit.url,
          // embeddable: false, let solveforall guess
          summaryHtml: _(hit.story_text || '').escapeHTML(),
          relevance: BASE_RELEVANCE * (1.0 - Math.pow(2.0, -Math.max((hit.points || 0), 1) * 0.01))
        };
      }).value();
    }
  };
}

function generateResults(recognitionResults, q, context) {
  'use strict';

  if (context.isSuggestionQuery) {
    return [];
  }

  var url = 'https://hn.algolia.com/api/v1/search';

  var request = hostAdapter.makeWebRequest(url, {
    data: {
      query: q
    }
  });

  request.send('makeResponseHandler(' + JSON.stringify(q) +
    ',' + JSON.stringify(context) + ')');

  return HostAdapter.SUSPEND;
}
<|endoftext|>


<commit_before>// Licensed to Cloudera, Inc. under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  Cloudera, Inc. licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

var AssistStorageEntry = (function () {

  var PAGE_SIZE = 100;

  var TYPE_SPECIFICS = {
    'adls': {
      apiHelperFetchFunction: 'fetchAdlsPath',
      dblClickPubSubId: 'assist.dblClickAdlsItem',
      goHomePubSubId: 'assist.adls.go.home',
      selectEntryPubSubId: 'assist.selectAdlsEntry'
    },
    'hdfs': {
      apiHelperFetchFunction: 'fetchHdfsPath',
      dblClickPubSubId: 'assist.dblClickHdfsItem',
      goHomePubSubId: 'assist.hdfs.go.home',
      selectEntryPubSubId: 'assist.selectHdfsEntry'
    },
    's3': {
      apiHelperFetchFunction: 'fetchS3Path',
      dblClickPubSubId: 'assist.dblClickS3Item',
      goHomePubSubId: 'assist.s3.go.home',
      selectEntryPubSubId: 'assist.selectS3Entry'
    }
  };

  /**
   * @param {object} options
   * @param {object} options.definition
   * @param {string} options.definition.name
   * @param {string} options.definition.type (file, dir)
   * @param {string} options.type - The storage type ('adls', 'hdfs', 's3')
   * @param {AssistStorageEntry} options.parent
   * @param {ApiHelper} options.apiHelper
   * @constructor
   */
  function AssistStorageEntry (options) {
    var self = this;
    self.type = options.type;
    self.definition = options.definition;
    self.apiHelper = options.apiHelper;
    self.parent = options.parent;
    self.path = '';
    if (self.parent !== null) {
      self.path = self.parent.path;
      if (self.parent.path !== '/') {
        self.path += '/'
      }
    }
    self.path += self.definition.name;
    self.currentPage = 1;
    self.hasMorePages = true;

    self.filter = ko.observable('').extend({ rateLimit: 400 });

    self.filter.subscribe(function () {
      self.loadEntries();
    });

    self.entries = ko.observableArray([]);

    self.loaded = false;
    self.loading = ko.observable(false);
    self.loadingMore = ko.observable(false);
    self.hasErrors = ko.observable(false);
    self.open = ko.observable(false);

    self.open.subscribe(function(newValue) {
      if (newValue && self.entries().length == 0) {
        self.loadEntries();
      }
    });

    self.hasEntries = ko.computed(function() {
      return self.entries().length > 0;
    });
  }

  AssistStorageEntry.prototype.dblClick = function () {
    var self = this;
    huePubSub.publish(TYPE_SPECIFICS[self.type].dblClickPubSubId, self);
  };

  AssistStorageEntry.prototype.loadEntries = function(callback) {
    var self = this;
    if (self.loading()) {
      return;
    }
    self.loading(true);
    self.hasErrors(false);

    var successCallback = function(data) {
      self.hasMorePages = data.page.next_page_number > self.currentPage;
      var filteredFiles = $.grep(data.files, function (file) {
        return file.name !== '.' && file.name !== '..';
      });
      self.entries($.map(filteredFiles, function (file) {
        return new AssistStorageEntry({
          type: self.type,
          definition: file,
          parent: self,
          apiHelper: self.apiHelper
        })
      }));
      self.loaded = true;
      self.loading(false);
      if (callback) {
        callback();
      }
    };

    var errorCallback = function () {
      self.hasErrors(true);
      self.loading(false);
      if (callback) {
        callback();
      }
    };

    self.apiHelper[TYPE_SPECIFICS[self.type].apiHelperFetchFunction]({
      pageSize: PAGE_SIZE,
      page: self.currentPage,
      filter: self.filter().trim() ? self.filter() : undefined,
      pathParts: self.getHierarchy(),
      successCallback: successCallback,
      errorCallback: errorCallback
    })
  };

  AssistStorageEntry.prototype.goHome = function () {
    var self = this;
    huePubSub.publish(TYPE_SPECIFICS[self.type].goHomePubSubId);
  };

  AssistStorageEntry.prototype.loadDeep = function(folders, callback) {
    var self = this;

    if (folders.length === 0) {
      callback(self);
      return;
    }

    var nextName = folders.shift();
    var loadedPages = 0;
    var findNextAndLoadDeep = function () {

      var foundEntry = $.grep(self.entries(), function (entry) {
        return entry.definition.name === nextName && entry.definition.type === 'dir';
      });
      var passedAlphabetically = self.entries().length > 0 && self.entries()[self.entries().length - 1].definition.name.localeCompare(nextName) > 0;

      if (foundEntry.length === 1) {
        foundEntry[0].loadDeep(folders, callback);
      } else if (!passedAlphabetically && self.hasMorePages && loadedPages < 50) {
        loadedPages++;
        self.fetchMore(function () {
          findNextAndLoadDeep();
        }, function () {
          callback(self);
        });
      } else {
        callback(self);
      }
    };

    if (! self.loaded) {
      self.loadEntries(findNextAndLoadDeep);
    } else {
      findNextAndLoadDeep();
    }
  };

  AssistStorageEntry.prototype.getHierarchy = function () {
    var self = this;
    var parts = [];
    var entry = self;
    while (entry != null) {
      parts.push(entry.definition.name);
      entry = entry.parent;
    }
    parts.reverse();
    return parts;
  };

  AssistStorageEntry.prototype.toggleOpen = function (data, event) {
    var self = this;
    if (self.definition.type === 'file') {
      if (IS_HUE_4) {
        if (event.ctrlKey || event.metaKey || event.which === 2) {
          window.open('/hue' + self.definition.url, '_blank');
        } else {
          huePubSub.publish('open.link', self.definition.url);
        }
      } else {
        window.open(self.definition.url, '_blank');
      }
      return;
    }
    self.open(!self.open());
    if (self.definition.name === '..') {
      if (self.parent.parent) {
        huePubSub.publish(TYPE_SPECIFICS[self.type].selectEntryPubSubId, self.parent.parent);
      }
    } else {
      huePubSub.publish(TYPE_SPECIFICS[self.type].selectEntryPubSubId, self);
    }
  };

  AssistStorageEntry.prototype.fetchMore = function (successCallback, errorCallback) {
    var self = this;
    if (!self.hasMorePages || self.loadingMore()) {
      return;
    }
    self.currentPage++;
    self.loadingMore(true);
    self.hasErrors(false);
    self.apiHelper.fetchHdfsPath({
      pageSize: PAGE_SIZE,
      page: self.currentPage,
      filter: self.filter().trim() ? self.filter() : undefined,
      pathParts: self.getHierarchy(),
      successCallback: function (data) {
        self.hasMorePages = data.page.next_page_number > self.currentPage;
        var filteredFiles = $.grep(data.files, function (file) {
          return file.name !== '.' && file.name !== '..';
        });
        self.entries(self.entries().concat($.map(filteredFiles, function (file) {
          return new AssistStorageEntry({
            type: self.type,
            definition: file,
            parent: self,
            apiHelper: self.apiHelper
          })
        })));
        self.loadingMore(false);
        if (successCallback) {
          successCallback();
        }
      },
      errorCallback: function () {
        self.hasErrors(true);
        if (errorCallback) {
          errorCallback();
        }
      }
    });
  };

  AssistStorageEntry.prototype.openInImporter = function () {
    huePubSub.publish('open.in.importer', this.definition.path);
  };

  return AssistStorageEntry;
})();
<commit_msg>HUE-7578 [assist] Fetch more entries when scrolling to the end in the S3 and ADLS panels<commit_after>// Licensed to Cloudera, Inc. under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  Cloudera, Inc. licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

var AssistStorageEntry = (function () {

  var PAGE_SIZE = 100;

  var TYPE_SPECIFICS = {
    'adls': {
      apiHelperFetchFunction: 'fetchAdlsPath',
      dblClickPubSubId: 'assist.dblClickAdlsItem',
      goHomePubSubId: 'assist.adls.go.home',
      selectEntryPubSubId: 'assist.selectAdlsEntry'
    },
    'hdfs': {
      apiHelperFetchFunction: 'fetchHdfsPath',
      dblClickPubSubId: 'assist.dblClickHdfsItem',
      goHomePubSubId: 'assist.hdfs.go.home',
      selectEntryPubSubId: 'assist.selectHdfsEntry'
    },
    's3': {
      apiHelperFetchFunction: 'fetchS3Path',
      dblClickPubSubId: 'assist.dblClickS3Item',
      goHomePubSubId: 'assist.s3.go.home',
      selectEntryPubSubId: 'assist.selectS3Entry'
    }
  };

  /**
   * @param {object} options
   * @param {object} options.definition
   * @param {string} options.definition.name
   * @param {string} options.definition.type (file, dir)
   * @param {string} options.type - The storage type ('adls', 'hdfs', 's3')
   * @param {AssistStorageEntry} options.parent
   * @param {ApiHelper} options.apiHelper
   * @constructor
   */
  function AssistStorageEntry (options) {
    var self = this;
    self.type = options.type;
    self.definition = options.definition;
    self.apiHelper = options.apiHelper;
    self.parent = options.parent;
    self.path = '';
    if (self.parent !== null) {
      self.path = self.parent.path;
      if (self.parent.path !== '/') {
        self.path += '/'
      }
    }
    self.path += self.definition.name;
    self.currentPage = 1;
    self.hasMorePages = true;

    self.filter = ko.observable('').extend({ rateLimit: 400 });

    self.filter.subscribe(function () {
      self.loadEntries();
    });

    self.entries = ko.observableArray([]);

    self.loaded = false;
    self.loading = ko.observable(false);
    self.loadingMore = ko.observable(false);
    self.hasErrors = ko.observable(false);
    self.open = ko.observable(false);

    self.open.subscribe(function(newValue) {
      if (newValue && self.entries().length == 0) {
        self.loadEntries();
      }
    });

    self.hasEntries = ko.computed(function() {
      return self.entries().length > 0;
    });
  }

  AssistStorageEntry.prototype.dblClick = function () {
    var self = this;
    huePubSub.publish(TYPE_SPECIFICS[self.type].dblClickPubSubId, self);
  };

  AssistStorageEntry.prototype.loadEntries = function(callback) {
    var self = this;
    if (self.loading()) {
      return;
    }
    self.loading(true);
    self.hasErrors(false);

    var successCallback = function(data) {
      self.hasMorePages = data.page.next_page_number > self.currentPage;
      var filteredFiles = $.grep(data.files, function (file) {
        return file.name !== '.' && file.name !== '..';
      });
      self.entries($.map(filteredFiles, function (file) {
        return new AssistStorageEntry({
          type: self.type,
          definition: file,
          parent: self,
          apiHelper: self.apiHelper
        })
      }));
      self.loaded = true;
      self.loading(false);
      if (callback) {
        callback();
      }
    };

    var errorCallback = function () {
      self.hasErrors(true);
      self.loading(false);
      if (callback) {
        callback();
      }
    };

    self.apiHelper[TYPE_SPECIFICS[self.type].apiHelperFetchFunction]({
      pageSize: PAGE_SIZE,
      page: self.currentPage,
      filter: self.filter().trim() ? self.filter() : undefined,
      pathParts: self.getHierarchy(),
      successCallback: successCallback,
      errorCallback: errorCallback
    })
  };

  AssistStorageEntry.prototype.goHome = function () {
    var self = this;
    huePubSub.publish(TYPE_SPECIFICS[self.type].goHomePubSubId);
  };

  AssistStorageEntry.prototype.loadDeep = function(folders, callback) {
    var self = this;

    if (folders.length === 0) {
      callback(self);
      return;
    }

    var nextName = folders.shift();
    var loadedPages = 0;
    var findNextAndLoadDeep = function () {

      var foundEntry = $.grep(self.entries(), function (entry) {
        return entry.definition.name === nextName && entry.definition.type === 'dir';
      });
      var passedAlphabetically = self.entries().length > 0 && self.entries()[self.entries().length - 1].definition.name.localeCompare(nextName) > 0;

      if (foundEntry.length === 1) {
        foundEntry[0].loadDeep(folders, callback);
      } else if (!passedAlphabetically && self.hasMorePages && loadedPages < 50) {
        loadedPages++;
        self.fetchMore(function () {
          findNextAndLoadDeep();
        }, function () {
          callback(self);
        });
      } else {
        callback(self);
      }
    };

    if (! self.loaded) {
      self.loadEntries(findNextAndLoadDeep);
    } else {
      findNextAndLoadDeep();
    }
  };

  AssistStorageEntry.prototype.getHierarchy = function () {
    var self = this;
    var parts = [];
    var entry = self;
    while (entry != null) {
      parts.push(entry.definition.name);
      entry = entry.parent;
    }
    parts.reverse();
    return parts;
  };

  AssistStorageEntry.prototype.toggleOpen = function (data, event) {
    var self = this;
    if (self.definition.type === 'file') {
      if (IS_HUE_4) {
        if (event.ctrlKey || event.metaKey || event.which === 2) {
          window.open('/hue' + self.definition.url, '_blank');
        } else {
          huePubSub.publish('open.link', self.definition.url);
        }
      } else {
        window.open(self.definition.url, '_blank');
      }
      return;
    }
    self.open(!self.open());
    if (self.definition.name === '..') {
      if (self.parent.parent) {
        huePubSub.publish(TYPE_SPECIFICS[self.type].selectEntryPubSubId, self.parent.parent);
      }
    } else {
      huePubSub.publish(TYPE_SPECIFICS[self.type].selectEntryPubSubId, self);
    }
  };

  AssistStorageEntry.prototype.fetchMore = function (successCallback, errorCallback) {
    var self = this;
    if (!self.hasMorePages || self.loadingMore()) {
      return;
    }
    self.currentPage++;
    self.loadingMore(true);
    self.hasErrors(false);

    self.apiHelper[TYPE_SPECIFICS[self.type].apiHelperFetchFunction]({
      pageSize: PAGE_SIZE,
      page: self.currentPage,
      filter: self.filter().trim() ? self.filter() : undefined,
      pathParts: self.getHierarchy(),
      successCallback: function (data) {
        self.hasMorePages = data.page.next_page_number > self.currentPage;
        var filteredFiles = $.grep(data.files, function (file) {
          return file.name !== '.' && file.name !== '..';
        });
        self.entries(self.entries().concat($.map(filteredFiles, function (file) {
          return new AssistStorageEntry({
            type: self.type,
            definition: file,
            parent: self,
            apiHelper: self.apiHelper
          })
        })));
        self.loadingMore(false);
        if (successCallback) {
          successCallback();
        }
      },
      errorCallback: function () {
        self.hasErrors(true);
        if (errorCallback) {
          errorCallback();
        }
      }
    });
  };

  AssistStorageEntry.prototype.openInImporter = function () {
    huePubSub.publish('open.in.importer', this.definition.path);
  };

  return AssistStorageEntry;
})();
<|endoftext|>


<commit_before>#!/usr/bin/env node
'use strict';

var fs = require('fs');

var getValue = function(config, name) {
    var value = config.match(new RegExp('<' + name + '>(.*?)</' + name + '>', "i"))
    if(value && value[1]) {
        return value[1]
    } else {
        return null
    }
}

function fileExists(path) {
  try  {
    return fs.statSync(path).isFile();
  }
  catch (e) {
    return false;
  }
}

var config = fs.readFileSync("config.xml").toString()
var name = getValue(config, "name")

if(fileExists("GoogleService-Info.plist")){
  try {
  	var contents = fs.readFileSync("GoogleService-Info.plist").toString();
      fs.writeFileSync("platforms/ios/" + name + "/Resources/GoogleService-Info.plist", contents)
  } catch(err) {
    process.stdout.write(err);
  }
}

try {
	var contents = fs.readFileSync("google-services.json").toString();
    fs.writeFileSync("platforms/android/google-services.json", contents);

    var json = JSON.parse(contents);
    var strings = fs.readFileSync("platforms/android/res/values/strings.xml").toString();

    // strip non-default value
    strings = strings.replace(new RegExp('<string name="google_app_id">([^\@<]+?)</string>', "i"), '')

    // strip non-default value
    strings = strings.replace(new RegExp('<string name="google_api_key">([^\@<]+?)</string>', "i"), '')

    // strip empty lines
    strings = strings.replace(new RegExp('(\r\n|\n|\r)[ \t]*(\r\n|\n|\r)', "gm"), '$1')

    // replace the default value
    strings = strings.replace(new RegExp('<string name="google_app_id">([^<]+?)</string>', "i"), '<string name="google_app_id">' + json.client[0].client_info.mobilesdk_app_id + '</string>')

    // replace the default value
    strings = strings.replace(new RegExp('<string name="google_api_key">([^<]+?)</string>', "i"), '<string name="google_api_key">' + json.client[0].api_key[0].current_key + '</string>')

    fs.writeFileSync("platforms/android/res/values/strings.xml", strings);
} catch(err) {
    process.stdout.write(err);
}
<commit_msg>Add a test to ignore the Android platform.<commit_after>#!/usr/bin/env node
'use strict';

var fs = require('fs');

var getValue = function(config, name) {
    var value = config.match(new RegExp('<' + name + '>(.*?)</' + name + '>', "i"))
    if(value && value[1]) {
        return value[1]
    } else {
        return null
    }
}

function fileExists(path) {
  try  {
    return fs.statSync(path).isFile();
  }
  catch (e) {
    return false;
  }
}

var config = fs.readFileSync("config.xml").toString()
var name = getValue(config, "name")

if(fileExists("GoogleService-Info.plist")){
  try {
  	var contents = fs.readFileSync("GoogleService-Info.plist").toString();
      fs.writeFileSync("platforms/ios/" + name + "/Resources/GoogleService-Info.plist", contents)
  } catch(err) {
    process.stdout.write(err);
  }
}

if(fileExists("google-services.json")){
	try {
		var contents = fs.readFileSync("google-services.json").toString();
	    fs.writeFileSync("platforms/android/google-services.json", contents);

	    var json = JSON.parse(contents);
	    var strings = fs.readFileSync("platforms/android/res/values/strings.xml").toString();

	    // strip non-default value
	    strings = strings.replace(new RegExp('<string name="google_app_id">([^\@<]+?)</string>', "i"), '')

	    // strip non-default value
	    strings = strings.replace(new RegExp('<string name="google_api_key">([^\@<]+?)</string>', "i"), '')

	    // strip empty lines
	    strings = strings.replace(new RegExp('(\r\n|\n|\r)[ \t]*(\r\n|\n|\r)', "gm"), '$1')

	    // replace the default value
	    strings = strings.replace(new RegExp('<string name="google_app_id">([^<]+?)</string>', "i"), '<string name="google_app_id">' + json.client[0].client_info.mobilesdk_app_id + '</string>')

	    // replace the default value
	    strings = strings.replace(new RegExp('<string name="google_api_key">([^<]+?)</string>', "i"), '<string name="google_api_key">' + json.client[0].api_key[0].current_key + '</string>')

	    fs.writeFileSync("platforms/android/res/values/strings.xml", strings);
	} catch(err) {
	    process.stdout.write(err);
	}
}
<|endoftext|>


<commit_before>var arr = [
    {name: '小米1', value: 1, type: 2, date: '2017-06-07T08:00:01.589Z' },
    { name: '锤子T1', value: 1, type: 2, date: '2017-06-07T08:10:01.589Z' },
    { name: '小米2', value: 1, type: 4, date: '2017-06-07T08:00:01.589Z' },
    { name: '小米2', value: 4, type: 4, date: '2017-06-07T08:10:21.189Z' },
    { name: '小米4', value: 1, type: 4, date: '2017-06-07T08:00:01.560Z' },
    { name: '小米4', value: 2, type: 4, date: '2017-06-07T08:10:31.584Z' },
    { name: '小米6', value: 1, type: 3, date: '2017-06-07T08:00:01.589Z' },
    { name: '小米5s', value: 1, type: 4, date: '2017-06-07T08:00:01.589Z' },
    { name: '锤子T2', value: 1, type: 4, date: '2017-06-07T08:00:01.589Z' },
    { name: '锤子T1', value: 4, type: 4, date: '2017-06-07T08:06:01.589Z' },
    { name: '魅蓝note5', value: 1, type: 4, date: '2017-06-07T08:00:01.589Z' },
    { name: '魅蓝note2', value: 5, type: 4, date: '2017-06-02T08:07:01.589Z' },
    { name: '魅蓝note2', value: 6, type: 4, date: '2017-06-07T08:00:01.589Z' },
    { name: '魅蓝note3', value: 1, type: 4, date: '2017-06-05T08:00:01.589Z' },
    { name: '魅蓝note', value: 1, type: 4, date: '2017-06-07T08:00:01.589Z' },
    { name: 'oppor9', value: 7, type: 4, date: '2017-06-04T08:04:01.588Z' },
    { name: '华为p9', value: 1, type: 4, date: '2017-06-02T08:00:01.577Z' },
    { name: '华为p9', value: 2, type: 4, date: '2017-06-07T08:00:01.110Z' },
    { name: '华为p10', value: 1, type: 1, date: '2017-06-07T08:00:01.534Z' }
];

/**
* type为4的数据过滤出来，
* 按相同的 name + date（按天）合并value（value累加），
* 最后按 value 降序(从大到小)排序.
* 然后每行按照 "小米2,4,2017年06月07日" 的格式, 打印出来.
*/
function printArray(arr) {
    function isType4(obj) {
        return obj.type === 4;
    }
    function formatDate(gmt) {
        var date = new Date(gmt);
        return date.getFullYear() + '年' +
            (date.getMonth() + 1) + '月' +
            date.getDate() + '日';
    }
    var resultArr = [];
    if (Array.isArray(arr)) {
        var arr = arr.filter(isType4);
        var resultArr = [];
        var tempMap = {};
        for (var i = 0; i < arr.length; i++) {
            var key = arr[i].name;
            if (!tempMap[key]) {
                tempMap[key] = {
                    name: key,
                    value: arr[i].value,
                    date: arr[i].date
                };
                resultArr.push(tempMap[key])
            } else {
                tempMap[key].value += arr[i].value;
            }
        }
        resultArr
            .sort(function(a, b) {
                return b.value - a.value;
            })
            .map(x => x.name + ',' + x.value + ',' + formatDate(x.date))
            .forEach(x => console.log(x));
    }
}

printArray(arr);
<commit_msg>[+]second mehod<commit_after>var arr = [
    { name: '小米1', value: 1, type: 2, date: '2017-06-07T08:00:01.589Z' },
    { name: '锤子T1', value: 1, type: 2, date: '2017-06-07T08:10:01.589Z' },
    { name: '小米2', value: 1, type: 4, date: '2017-06-07T08:00:01.589Z' },
    { name: '小米2', value: 4, type: 4, date: '2017-06-07T08:10:21.189Z' },
    { name: '小米4', value: 1, type: 4, date: '2017-06-07T08:00:01.560Z' },
    { name: '小米4', value: 2, type: 4, date: '2017-06-07T08:10:31.584Z' },
    { name: '小米6', value: 1, type: 3, date: '2017-06-07T08:00:01.589Z' },
    { name: '小米5s', value: 1, type: 4, date: '2017-06-07T08:00:01.589Z' },
    { name: '锤子T2', value: 1, type: 4, date: '2017-06-07T08:00:01.589Z' },
    { name: '锤子T1', value: 4, type: 4, date: '2017-06-07T08:06:01.589Z' },
    { name: '魅蓝note5', value: 1, type: 4, date: '2017-06-07T08:00:01.589Z' },
    { name: '魅蓝note2', value: 5, type: 4, date: '2017-06-02T08:07:01.589Z' },
    { name: '魅蓝note2', value: 6, type: 4, date: '2017-06-07T08:00:01.589Z' },
    { name: '魅蓝note3', value: 1, type: 4, date: '2017-06-05T08:00:01.589Z' },
    { name: '魅蓝note', value: 1, type: 4, date: '2017-06-07T08:00:01.589Z' },
    { name: 'oppor9', value: 7, type: 4, date: '2017-06-04T08:04:01.588Z' },
    { name: '华为p9', value: 1, type: 4, date: '2017-06-02T08:00:01.577Z' },
    { name: '华为p9', value: 2, type: 4, date: '2017-06-07T08:00:01.110Z' },
    { name: '华为p10', value: 1, type: 1, date: '2017-06-07T08:00:01.534Z' }
];

/**
* type为4的数据过滤出来，
* 按相同的 name + date（按天）合并value（value累加），
* 最后按 value 降序(从大到小)排序.
* 然后每行按照 "小米2,4,2017年06月07日" 的格式, 打印出来.
*/
function formatDate(gmt) {
    var date = new Date(gmt);
    return date.getFullYear() + '年' +
        (date.getMonth() + 1) + '月' +
        date.getDate() + '日';
}

function printArray2(arr) {
    var obj = {
        resultArr: []
    }
    Object.defineProperties(obj, {
        'item': {
            set: function (item) {
                if (item.type !== 4) {
                    return;
                }
                this.__tempMap = this.__tempMap || {};
                var key = item.name,
                    date = formatDate(item.date);
                if (this.__tempMap[key] && this.__tempMap[key].date === date) {
                    this.__tempMap[key].value += item.value;
                } else {
                    this.__tempMap[key] = {
                        name: key,
                        value: item.value,
                        date: date
                    };
                    this.resultArr.push(this.__tempMap[key]);
                }
            },
            get: function () {
                return this.str;
            }
        },
        "str": {
            set: function(newVal) {
                return this.str = newVal;
            },
            get: function() {
                return this.resultArr
                    .sort(function (a, b) {
                        return b.value - a.value;
                    })
            }
        }
    })

    arr.forEach(function (element) {
        obj.item = element;
    }, this);
    obj.item.forEach(element =>
        console.log(element.name + ',' + element.value + ',' + element.date)
    )
}

function printArray1(arr) {
    var resultArr = [];
    if (Array.isArray(arr)) {
        var resultArr = [];
        var tempMap = {};
        for (var i = 0; i < arr.length; i++) {
            if (arr[i].type == 4) {
                var key = arr[i].name,
                    date = formatDate(arr[i].date);
                if (tempMap[key] && tempMap[key].date === date) {
                    tempMap[key].value += arr[i].value;
                } else {
                    tempMap[key] = {
                        name: key,
                        value: arr[i].value,
                        date: date
                    };
                    resultArr.push(tempMap[key]);
                }
            }
        }
        resultArr
            .sort(function (a, b) {
                return b.value - a.value;
            })
            .forEach(element => console.log(element.name + ',' + element.value + ',' + element.date));
    }
}
printArray1(arr);
console.log('________')
printArray2(arr);<|endoftext|>


<commit_before>const core = require('@actions/core');
const path = require("path");
const fs = require("fs");
const github = require('@actions/github');
const glob = require('glob');

function sleep(milliseconds) {
  return new Promise(resolve => setTimeout(resolve, milliseconds))
}

async function runOnce() {
  // Load all our inputs and env vars. Note that `getInput` reads from `INPUT_*`
  const files = core.getInput('files');
  const name = core.getInput('name');
  const token = core.getInput('token');
  const slug = process.env.GITHUB_REPOSITORY;
  const owner = slug.split('/')[0];
  const repo = slug.split('/')[1];
  const sha = process.env.GITHUB_SHA;

  core.info(`files: ${files}`);
  core.info(`name: ${name}`);
  core.info(`token: ${token}`);

  const octokit = new github.GitHub(token);

  // Delete the previous release since we can't overwrite one. This may happen
  // due to retrying an upload or it may happen because we're doing the dev
  // release.
  const releases = await octokit.paginate("GET /repos/:owner/:repo/releases", { owner, repo });
  for (const release of releases) {
    if (release.tag_name !== name) {
      continue;
    }
    const release_id = release.id;
    core.info(`deleting release ${release_id}`);
    await octokit.repos.deleteRelease({ owner, repo, release_id });
  }

  // We also need to update the `dev` tag while we're at it on the `dev` branch.
  if (name == 'nightly') {
    try {
      core.info(`updating nightly tag`);
      await octokit.git.updateRef({
          owner,
          repo,
          ref: 'tags/nightly',
          sha,
          force: true,
      });
    } catch (e) {
      console.log("ERROR: ", JSON.stringify(e, null, 2));
      core.info(`creating nightly tag`);
      await octokit.git.createTag({
        owner,
        repo,
        tag: 'nightly',
        message: 'nightly release',
        object: sha,
        type: 'commit',
      });
    }
  }

  // Creates an official GitHub release for this `tag`, and if this is `dev`
  // then we know that from the previous block this should be a fresh release.
  core.info(`creating a release`);
  const release = await octokit.repos.createRelease({
    owner,
    repo,
    tag_name: name,
    prerelease: name === 'nightly',
  });

  // Upload all the relevant assets for this release as just general blobs.
  for (const file of glob.sync(files)) {
    const size = fs.statSync(file).size;
    core.info(`upload ${file}`);
    await octokit.repos.uploadReleaseAsset({
      data: fs.createReadStream(file),
      headers: { 'content-length': size, 'content-type': 'application/octet-stream' },
      name: path.basename(file),
      url: release.data.upload_url,
    });
  }
}

async function run() {
  const retries = 10;
  for (let i = 0; i < retries; i++) {
    try {
      await runOnce();
      break;
    } catch (e) {
      if (i === retries - 1)
        throw e;
      logError(e);
      console.log("RETRYING after 10s");
      await sleep(10000)
    }
  }
}

function logError(e) {
  console.log("ERROR: ", e.message);
  try {
    console.log(JSON.stringify(e, null, 2));
  } catch (e) {
    // ignore json errors for now
  }
  console.log(e.stack);
}

run().catch(err => {
  logError(err);
  core.setFailed(err.message);
});
<commit_msg>Set release name<commit_after>const core = require('@actions/core');
const path = require("path");
const fs = require("fs");
const github = require('@actions/github');
const glob = require('glob');

function sleep(milliseconds) {
  return new Promise(resolve => setTimeout(resolve, milliseconds))
}

async function runOnce() {
  // Load all our inputs and env vars. Note that `getInput` reads from `INPUT_*`
  const files = core.getInput('files');
  const name = core.getInput('name');
  const token = core.getInput('token');
  const slug = process.env.GITHUB_REPOSITORY;
  const owner = slug.split('/')[0];
  const repo = slug.split('/')[1];
  const sha = process.env.GITHUB_SHA;

  core.info(`files: ${files}`);
  core.info(`name: ${name}`);
  core.info(`token: ${token}`);

  const octokit = new github.GitHub(token);

  // Delete the previous release since we can't overwrite one. This may happen
  // due to retrying an upload or it may happen because we're doing the dev
  // release.
  const releases = await octokit.paginate("GET /repos/:owner/:repo/releases", { owner, repo });
  for (const release of releases) {
    if (release.tag_name !== name) {
      continue;
    }
    const release_id = release.id;
    core.info(`deleting release ${release_id}`);
    await octokit.repos.deleteRelease({ owner, repo, release_id });
  }

  // We also need to update the `dev` tag while we're at it on the `dev` branch.
  if (name == 'nightly') {
    try {
      core.info(`updating nightly tag`);
      await octokit.git.updateRef({
          owner,
          repo,
          ref: 'tags/nightly',
          sha,
          force: true,
      });
    } catch (e) {
      console.log("ERROR: ", JSON.stringify(e, null, 2));
      core.info(`creating nightly tag`);
      await octokit.git.createTag({
        owner,
        repo,
        tag: 'nightly',
        message: 'nightly release',
        object: sha,
        type: 'commit',
      });
    }
  }

  // Creates an official GitHub release for this `tag`, and if this is `dev`
  // then we know that from the previous block this should be a fresh release.
  core.info(`creating a release`);
  const release = await octokit.repos.createRelease({
    owner,
    repo,
    name,
    tag_name: name,
    prerelease: name === 'nightly',
  });

  // Upload all the relevant assets for this release as just general blobs.
  for (const file of glob.sync(files)) {
    const size = fs.statSync(file).size;
    core.info(`upload ${file}`);
    await octokit.repos.uploadReleaseAsset({
      data: fs.createReadStream(file),
      headers: { 'content-length': size, 'content-type': 'application/octet-stream' },
      name: path.basename(file),
      url: release.data.upload_url,
    });
  }
}

async function run() {
  const retries = 10;
  for (let i = 0; i < retries; i++) {
    try {
      await runOnce();
      break;
    } catch (e) {
      if (i === retries - 1)
        throw e;
      logError(e);
      console.log("RETRYING after 10s");
      await sleep(10000)
    }
  }
}

function logError(e) {
  console.log("ERROR: ", e.message);
  try {
    console.log(JSON.stringify(e, null, 2));
  } catch (e) {
    // ignore json errors for now
  }
  console.log(e.stack);
}

run().catch(err => {
  logError(err);
  core.setFailed(err.message);
});
<|endoftext|>