<commit_before>/* This module uses a give ip to visit the whole urls in the bucket assigned to it
*/
module.exports = function(bucketExplorer , database) {
	//
    var ObjectId = require('mongodb').ObjectId;
	var Spooky = require('spooky');
	var bucket;
    var ipQueue = [];
    var visited = 0;

    var exitFlag = false;

    //
    var Buckets = database.model('Buckets');

    //
    var setBucket =  function(bucketObj){
    	 console.log(bucketObj);
    	 bucket = bucketObj;
         if(bucket.urls.length>0){
            for(var i=0; i<bucket.urls.length; i++){
                bucket.urls[i].visited = 0;
                bucket.urls[i].statusText = 'No status yet';
            };
         }

    	 console.log(bucket);
    	 console.log('bucket set in visitor');
         startUpdateDaemon();
    };

    var getBucket = function(){
    	return bucket;
    };

    var updateBucket = function(bucketObj , meta){
    	console.log(meta);
    	if(bucket){
            if(bucketObj._id+'' == bucket._id){
	           if(meta.action == 0){
	           	   console.log('Deleting url in this bucket');
                   bucket.urls.splice(meta.index , 1);
	           }
	           else if(meta.action == 1) {
                   console.log('Updating url in this bucket');
                   bucket = bucketObj;
	           }
	    	}
	    	else{
	    	   console.log('Just passing by');
	    	}
    	}
    	else{
    		 console.log('Bucket empty here');
    	}
    }

    //updateBucket after every 100 secs of activity
    function startUpdateDaemon(){
        console.log('Starting cron daemon');
        setInterval(function(){
            bucket.lastActive = Date.now()+'';
            Buckets.update(
                {_id : ObjectId(bucket._id)},
                bucket,
                function(err , result){
                    if(err){
                        console.log(err);
                        res.status(500).send('Database error during cron update in visitor');
                    }
                    else {
                        console.log('bucket updated in cron job');
                    }
                }
          );
        } , 100000);
    }

    //
    var exitWhenDone = function(){
         exitFlag = true;
    }

   //
    var visitWith = function(ip){
        if(ipQueue.indexOf(ip)<0){
            ipQueue.push(ip);
            runGhostProxy (ip , bucket.urls , 0 , function(){
                if(exitFlag && visited >= ipQueue.length){
                    console.log('All ips have been visited exiting process...');
                    process.exit(0);
                }
            });
        }
        else{
            console.log('ip alredy used or currently in use');
        }
    }

    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

	function runGhostProxy (ip , urls , index , callback){
		console.log('starting ghost');
		console.log('process starting '+ip+' '+urls[index].urlName+' '+urls[index].selector);

	    var spooky = new Spooky({
			child: {
				transport: 'http',
				proxy: ip
			},
			casper: {
				logLevel: 'debug',
				verbose: true,
				options: {
				   clientScripts: ['http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js'],
				   pageSettings: {
				        Referer: 'https://www.google.com.ng'
				    }
				}
			}
		 },
		 function(err){
		   if (err) {
				e = new Error('Failed to initialize SpookyJS');
				e.details = err;
				throw e;
			}

			//
			spooky.start(urls[index].urlName);
			spooky.then([{url:urls[index].urlName , selector:urls[index].selector} , function(){

                 this.viewport(1024, 768, function() {
					  console.log('Viewport size changed');

			          //
                      if(selector=='none'){
							 this.then(function(){
							 	  this.wait(30000 , function(){
								    this.emit('done', 'Hello, from ' + this.getCurrentUrl());
								    phantom.clearCookies();
							 	  	this.clear();
							 	  });

							 });
						}

						//General case
						else{
						   this.then(function(){
						   	   this.waitForSelector(selector , function(){
						   	   	  this.thenClick(selector , function() {
										this.wait(5000 , function(){
										    this.emit('done', 'Hello, from ' + this.getCurrentUrl());
										    phantom.clearCookies();
									 	  	this.clear();
									 	});
								    });

						   	   } , function(){
						   	   	     this.emit('done', 'The selector was not found');
								     phantom.clearCookies();
							 	  	 this.clear();
						   	   } , 20000);
						   });
						}

				    });

			}]);

            //
			spooky.run();
		});

		//logs and listeners
		spooky.on('error', function (e, stack) {
			console.log('here');//
			console.error(e);
			Greeting = e;

			if (stack) {
				console.log(stack);
				Greeting = stack;
			}

            //
            if(index < urls.length - 1){
               index++;
               spooky.destroy();
               return runGhostProxy(ip , urls , index , callback);
			}
            else{
                console.log('This round done visiting with '+ip);
                spooky.destroy();
                visited ++;
                return callback();
            }
		});

		//
		spooky.on('console', function (line) {
			console.log(line);
		});

		////
		spooky.on('done', function (greeting) {
			console.log(greeting);
			bucket.urls[index].statusText = greeting;
            bucket.urls[index].visited++;

			if(index < urls.length - 1){
               index++;
               spooky.destroy();
               return runGhostProxy(ip , urls , index , callback);
			}
            else{
                console.log('This round done visiting with '+ip);
                visited++;
                spooky.destroy();
                return callback();
            }

		});
	};

    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	return {
	    visitWith:visitWith,
	    getBucket:getBucket,
	    setBucket:setBucket,
	    updateBucket:updateBucket,
	    exitWhenDone:exitWhenDone
	}

};<commit_msg>updated<commit_after>/* This module uses a give ip to visit the whole urls in the bucket assigned to it
*/
module.exports = function(bucketExplorer , database) {
	//
    var ObjectId = require('mongodb').ObjectId;
	var Spooky = require('spooky');
	var bucket;
    var ipQueue = [];
    var visited = 0;

    var exitFlag = false;

    //
    var Buckets = database.model('Buckets');

    //
    var setBucket =  function(bucketObj){
    	 bucket = bucketObj;
         if(bucket.urls.length>0){
            for(var i=0; i<bucket.urls.length; i++){
                bucket.urls[i].visited = 0;
                bucket.urls[i].statusText = 'No status yet';
            };
         }

    	 console.log(bucket);
    	 console.log('bucket set in visitor');
         startUpdateDaemon();
    };

    var getBucket = function(){
    	return bucket;
    };

    var updateBucket = function(bucketObj , meta){
    	console.log(meta);
    	if(bucket){
            if(bucketObj._id+'' == bucket._id){
	           if(meta.action == 0){
	           	   console.log('Deleting url in this bucket');
                   bucket.urls.splice(meta.index , 1);
	           }
	           else if(meta.action == 1) {
                   console.log('Updating url in this bucket');
                   bucket = bucketObj;
	           }
	    	}
	    	else{
	    	   console.log('Just passing by');
	    	}
    	}
    	else{
    		 console.log('Bucket empty here');
    	}
    }

    //updateBucket after every 100 secs of activity
    function startUpdateDaemon(){
        console.log('Starting cron daemon');
        setInterval(function(){
            bucket.lastActive = Date.now()+'';
            Buckets.update(
                {_id : ObjectId(bucket._id)},
                bucket,
                function(err , result){
                    if(err){
                        console.log(err);
                        res.status(500).send('Database error during cron update in visitor');
                    }
                    else {
                        console.log('bucket updated in cron job');
                    }
                }
          );
        } , 100000);
    }

    //
    var exitWhenDone = function(){
         exitFlag = true;
    }

   //
    var visitWith = function(ip){
        if(ipQueue.indexOf(ip)<0){
            ipQueue.push(ip);
            runGhostProxy (ip , bucket.urls , 0 , function(){
                if(exitFlag && visited >= ipQueue.length){
                    console.log('All ips have been visited exiting process...');
                    process.exit(0);
                }
            });
        }
        else{
            console.log('ip alredy used or currently in use');
        }
    }

    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

	function runGhostProxy (ip , urls , index , callback){
		console.log('starting ghost');
		console.log('process starting '+ip+' '+urls[index].urlName+' '+urls[index].selector);

	    var spooky = new Spooky({
			child: {
				transport: 'http',
				proxy: ip
			},
			casper: {
				logLevel: 'debug',
				verbose: true,
				options: {
				   clientScripts: ['http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js'],
				   pageSettings: {
				        Referer: 'https://www.google.com.ng'
				    }
				}
			}
		 },
		 function(err){
		   if (err) {
				e = new Error('Failed to initialize SpookyJS');
				e.details = err;
				throw e;
			}

			//
			spooky.start(urls[index].urlName);
			spooky.then([{url:urls[index].urlName , selector:urls[index].selector} , function(){

                 this.viewport(1024, 768, function() {
					  console.log('Viewport size changed');

			          //
                      if(selector=='none'){
							 this.then(function(){
							 	  this.wait(30000 , function(){
								    this.emit('done', 'Hello, from ' + this.getCurrentUrl());
								    phantom.clearCookies();
							 	  	this.clear();
							 	  });

							 });
						}

						//General case
						else{
						   this.then(function(){
						   	   this.waitForSelector(selector , function(){
						   	   	  this.thenClick(selector , function() {
										this.wait(5000 , function(){
										    this.emit('done', 'Hello, from ' + this.getCurrentUrl());
										    phantom.clearCookies();
									 	  	this.clear();
									 	});
								    });

						   	   } , function(){
						   	   	     this.emit('done', 'The selector was not found');
								     phantom.clearCookies();
							 	  	 this.clear();
						   	   } , 20000);
						   });
						}

				    });

			}]);

            //
			spooky.run();
		});

		//logs and listeners
		spooky.on('error', function (e, stack) {
			console.log('here');//
			console.error(e);
			Greeting = e;

			if (stack) {
				console.log(stack);
				Greeting = stack;
			}

            //
            if(index < urls.length - 1){
               index++;
               spooky.destroy();
               return runGhostProxy(ip , urls , index , callback);
			}
            else{
                console.log('This round done visiting with '+ip);
                spooky.destroy();
                visited ++;
                return callback();
            }
		});

		//
		spooky.on('console', function (line) {
			console.log(line);
		});

		////
		spooky.on('done', function (greeting) {
			console.log(greeting);
			bucket.urls[index].statusText = greeting;
            bucket.urls[index].visited++;

			if(index < urls.length - 1){
               index++;
               spooky.destroy();
               return runGhostProxy(ip , urls , index , callback);
			}
            else{
                console.log('This round done visiting with '+ip);
                visited++;
                spooky.destroy();
                return callback();
            }

		});
	};

    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	return {
	    visitWith:visitWith,
	    getBucket:getBucket,
	    setBucket:setBucket,
	    updateBucket:updateBucket,
	    exitWhenDone:exitWhenDone
	}

};<|endoftext|>


<commit_before>/**
 * Vow
 *
 * Copyright (c) 2012-2013 Filatov Dmitry (dfilatov@yandex-team.ru)
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 *
 * @version 0.3.0
 */(function(e){var t=function(e){this._res=e,this._isFulfilled=!!arguments.length,this._isRejected=!1,this._fulfilledCallbacks=[],this._rejectedCallbacks=[],this._progressCallbacks=[]};t.prototype={valueOf:function(){return this._res},isFulfilled:function(){return this._isFulfilled},isRejected:function(){return this._isRejected},isResolved:function(){return this._isFulfilled||this._isRejected},fulfill:function(e){if(this.isResolved())return;this._isFulfilled=!0,this._res=e,this._callCallbacks(this._fulfilledCallbacks,e),this._fulfilledCallbacks=this._rejectedCallbacks=this._progressCallbacks=r},reject:function(e){if(this.isResolved())return;this._isRejected=!0,this._res=e,this._callCallbacks(this._rejectedCallbacks,e),this._fulfilledCallbacks=this._rejectedCallbacks=this._progressCallbacks=r},notify:function(e){if(this.isResolved())return;this._callCallbacks(this._progressCallbacks,e)},then:function(e,n,r){var i=new t,s;return this._isRejected||(s={promise:i,fn:e},this._isFulfilled?this._callCallbacks([s],this._res):this._fulfilledCallbacks.push(s)),this._isFulfilled||(s={promise:i,fn:n},this._isRejected?this._callCallbacks([s],this._res):this._rejectedCallbacks.push(s)),this.isResolved()||this._progressCallbacks.push({promise:i,fn:r}),i},fail:function(e){return this.then(r,e)},always:function(e){var t=this,n=function(){return e(t)};return this.then(n,n)},progress:function(e){return this.then(r,r,e)},spread:function(e,t){return this.then(function(t){return e.apply(this,t)},t)},done:function(){this.fail(s)},delay:function(e){return this.then(function(n){var r=new t;return setTimeout(function(){r.fulfill(n)},e),r})},timeout:function(e){var n=new t,r=setTimeout(function(){n.reject(Error("timed out"))},e);return n.sync(this),n.always(function(){clearTimeout(r)}),n},sync:function(e){var t=this;e.then(function(e){t.fulfill(e)},function(e){t.reject(e)})},_callCallbacks:function(e,t){var r=e.length;if(!r)return;var s=this.isResolved(),u=this.isFulfilled();i(function(){var i=0,a,f,l;while(i<r){a=e[i++],f=a.promise,l=a.fn;if(o(l)){var c;try{c=l(t)}catch(h){f.reject(h);continue}s?n.isPromise(c)?function(e){c.then(function(t){e.fulfill(t)},function(t){e.reject(t)})}(f):f.fulfill(c):f.notify(c)}else s?u?f.fulfill(t):f.reject(t):f.notify(t)}})}};var n={promise:function(e){return arguments.length?this.isPromise(e)?e:new t(e):new t},when:function(e,t,n,r){return this.promise(e).then(t,n,r)},fail:function(e,t){return this.when(e,r,t)},always:function(e,t){return this.promise(e).always(t)},progress:function(e,t){return this.promise(e).progress(t)},spread:function(e,t,n){return this.promise(e).spread(t,n)},done:function(e){this.isPromise(e)&&e.done()},isPromise:function(e){return e&&o(e.then)},valueOf:function(e){return this.isPromise(e)?e.valueOf():e},isFulfilled:function(e){return this.isPromise(e)?e.isFulfilled():!0},isRejected:function(e){return this.isPromise(e)?e.isRejected():!1},isResolved:function(e){return this.isPromise(e)?e.isResolved():!0},fulfill:function(e){return this.when(e,r,function(e){return e})},reject:function(e){return this.when(e,function(e){var n=new t;return n.reject(e),n})},resolve:function(e){return this.isPromise(e)?e:this.when(e)},invoke:function(e){try{return this.promise(e.apply(null,u.call(arguments,1)))}catch(t){return this.reject(t)}},forEach:function(e,t,n,r){var i=r?r.length:e.length,s=0;while(s<i)this.when(e[r?r[s]:s],t,n),++s},all:function(e){var r=new t,i=f(e),s=i?l(e):c(e),o=s.length,u=i?[]:{};if(!o)return r.fulfill(u),r;var a=o,h=function(){if(!--a){var t=0;while(t<o)u[s[t]]=n.valueOf(e[s[t++]]);r.fulfill(u)}},p=function(e){r.reject(e)};return this.forEach(e,h,p,s),r},allResolved:function(e){var n=new t,r=f(e),i=r?l(e):c(e),s=i.length,o=r?[]:{};if(!s)return n.fulfill(o),n;var u=function(){--s||n.fulfill(e)};return this.forEach(e,u,u,i),n},any:function(e){var n=new t,r=e.length;if(!r)return n.reject(Error()),n;var i=0,s,o=function(e){n.fulfill(e)},u=function(e){i||(s=e),++i===r&&n.reject(s)};return this.forEach(e,o,u),n},delay:function(e,t){return this.promise(e).delay(t)},timeout:function(e,t){return this.promise(e).timeout(t)}},r,i=function(){if(typeof process=="object")return process.nextTick;if(e.setImmediate)return e.setImmediate;var t=[],n=function(){var e=t,n=0,r=t.length;t=[];while(n<r)e[n++]()};if(e.postMessage){var r=!0;if(e.attachEvent){var i=function(){r=!1};e.attachEvent("onmessage",i),e.postMessage("__checkAsync","*"),e.detachEvent("onmessage",i)}if(r){var s="__promise"+ +(new Date),o=function(e){e.data===s&&(e.stopPropagation&&e.stopPropagation(),n())};return e.addEventListener?e.addEventListener("message",o,!0):e.attachEvent("onmessage",o),function(n){t.push(n)===1&&e.postMessage(s,"*")}}}var u=e.document;if("onreadystatechange"in u.createElement("script")){var a=function(){var e=u.createElement("script");e.onreadystatechange=function(){e.parentNode.removeChild(e),e=e.onreadystatechange=null,n()},(u.documentElement||u.body).appendChild(e)};return function(e){t.push(e)===1&&a()}}return function(e){setTimeout(e,0)}}(),s=function(e){i(function(){throw e})},o=function(e){return typeof e=="function"},u=Array.prototype.slice,a=Object.prototype.toString,f=Array.isArray||function(e){return a.call(e)==="[object Array]"},l=function(e){var t=[],n=0,r=e.length;while(n<r)t.push(n++);return t},c=Object.keys||function(e){var t=[];for(var n in e)e.hasOwnProperty(n)&&t.push(n);return t};typeof exports=="object"?module.exports=n:typeof define=="function"?define(function(e,t,r){r.exports=n}):e.Vow=n})(this);<commit_msg>add minified version<commit_after>/**
 * Vow
 *
 * Copyright (c) 2012-2013 Filatov Dmitry (dfilatov@yandex-team.ru)
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 *
 * @version 0.3.1
 */(function(e){var t=function(e){this._res=e,this._isFulfilled=!!arguments.length,this._isRejected=!1,this._fulfilledCallbacks=[],this._rejectedCallbacks=[],this._progressCallbacks=[]};t.prototype={valueOf:function(){return this._res},isFulfilled:function(){return this._isFulfilled},isRejected:function(){return this._isRejected},isResolved:function(){return this._isFulfilled||this._isRejected},fulfill:function(e){if(this.isResolved())return;this._isFulfilled=!0,this._res=e,this._callCallbacks(this._fulfilledCallbacks,e),this._fulfilledCallbacks=this._rejectedCallbacks=this._progressCallbacks=r},reject:function(e){if(this.isResolved())return;this._isRejected=!0,this._res=e,this._callCallbacks(this._rejectedCallbacks,e),this._fulfilledCallbacks=this._rejectedCallbacks=this._progressCallbacks=r},notify:function(e){if(this.isResolved())return;this._callCallbacks(this._progressCallbacks,e)},then:function(e,n,r){var i=new t,s;return this._isRejected||(s={promise:i,fn:e},this._isFulfilled?this._callCallbacks([s],this._res):this._fulfilledCallbacks.push(s)),this._isFulfilled||(s={promise:i,fn:n},this._isRejected?this._callCallbacks([s],this._res):this._rejectedCallbacks.push(s)),this.isResolved()||this._progressCallbacks.push({promise:i,fn:r}),i},fail:function(e){return this.then(r,e)},always:function(e){var t=this,n=function(){return e(t)};return this.then(n,n)},progress:function(e){return this.then(r,r,e)},spread:function(e,t){return this.then(function(t){return e.apply(this,t)},t)},done:function(){this.fail(s)},delay:function(e){return this.then(function(n){var r=new t;return setTimeout(function(){r.fulfill(n)},e),r})},timeout:function(e){var n=new t,r=setTimeout(function(){n.reject(Error("timed out"))},e);return n.sync(this),n.always(function(){clearTimeout(r)}),n},sync:function(e){var t=this;e.then(function(e){t.fulfill(e)},function(e){t.reject(e)})},_callCallbacks:function(e,t){var r=e.length;if(!r)return;var s=this.isResolved(),u=this.isFulfilled();i(function(){var i=0,a,f,l;while(i<r){a=e[i++],f=a.promise,l=a.fn;if(o(l)){var c;try{c=l(t)}catch(h){f.reject(h);continue}s?n.isPromise(c)?function(e){c.then(function(t){e.fulfill(t)},function(t){e.reject(t)})}(f):f.fulfill(c):f.notify(c)}else s?u?f.fulfill(t):f.reject(t):f.notify(t)}})}};var n={promise:function(e){return arguments.length?this.isPromise(e)?e:new t(e):new t},when:function(e,t,n,r){return this.promise(e).then(t,n,r)},fail:function(e,t){return this.when(e,r,t)},always:function(e,t){return this.promise(e).always(t)},progress:function(e,t){return this.promise(e).progress(t)},spread:function(e,t,n){return this.promise(e).spread(t,n)},done:function(e){this.isPromise(e)&&e.done()},isPromise:function(e){return e&&o(e.then)},valueOf:function(e){return this.isPromise(e)?e.valueOf():e},isFulfilled:function(e){return this.isPromise(e)?e.isFulfilled():!0},isRejected:function(e){return this.isPromise(e)?e.isRejected():!1},isResolved:function(e){return this.isPromise(e)?e.isResolved():!0},fulfill:function(e){return this.when(e,r,function(e){return e})},reject:function(e){return this.when(e,function(e){var n=new t;return n.reject(e),n})},resolve:function(e){return this.isPromise(e)?e:this.when(e)},invoke:function(e){try{return this.promise(e.apply(null,u.call(arguments,1)))}catch(t){return this.reject(t)}},forEach:function(e,t,n,r){var i=r?r.length:e.length,s=0;while(s<i)this.when(e[r?r[s]:s],t,n),++s},all:function(e){var r=new t,i=f(e),s=i?l(e):c(e),o=s.length,u=i?[]:{};if(!o)return r.fulfill(u),r;var a=o,h=function(){if(!--a){var t=0;while(t<o)u[s[t]]=n.valueOf(e[s[t++]]);r.fulfill(u)}},p=function(e){r.reject(e)};return this.forEach(e,h,p,s),r},allResolved:function(e){var n=new t,r=f(e),i=r?l(e):c(e),s=i.length,o=r?[]:{};if(!s)return n.fulfill(o),n;var u=function(){--s||n.fulfill(e)};return this.forEach(e,u,u,i),n},any:function(e){var n=new t,r=e.length;if(!r)return n.reject(Error()),n;var i=0,s,o=function(e){n.fulfill(e)},u=function(e){i||(s=e),++i===r&&n.reject(s)};return this.forEach(e,o,u),n},delay:function(e,t){return this.promise(e).delay(t)},timeout:function(e,t){return this.promise(e).timeout(t)}},r,i=function(){if(typeof process=="object")return process.nextTick;if(e.setImmediate)return e.setImmediate;var t=[],n=function(){var e=t,n=0,r=t.length;t=[];while(n<r)e[n++]()};if(e.postMessage){var r=!0;if(e.attachEvent){var i=function(){r=!1};e.attachEvent("onmessage",i),e.postMessage("__checkAsync","*"),e.detachEvent("onmessage",i)}if(r){var s="__promise"+ +(new Date),o=function(e){e.data===s&&(e.stopPropagation&&e.stopPropagation(),n())};return e.addEventListener?e.addEventListener("message",o,!0):e.attachEvent("onmessage",o),function(n){t.push(n)===1&&e.postMessage(s,"*")}}}var u=e.document;if("onreadystatechange"in u.createElement("script")){var a=function(){var e=u.createElement("script");e.onreadystatechange=function(){e.parentNode.removeChild(e),e=e.onreadystatechange=null,n()},(u.documentElement||u.body).appendChild(e)};return function(e){t.push(e)===1&&a()}}return function(e){setTimeout(e,0)}}(),s=function(e){i(function(){throw e})},o=function(e){return typeof e=="function"},u=Array.prototype.slice,a=Object.prototype.toString,f=Array.isArray||function(e){return a.call(e)==="[object Array]"},l=function(e){var t=[],n=0,r=e.length;while(n<r)t.push(n++);return t},c=Object.keys||function(e){var t=[];for(var n in e)e.hasOwnProperty(n)&&t.push(n);return t};typeof exports=="object"?module.exports=n:typeof modules=="object"?modules.define("vow",function(e){e(n)}):typeof define=="function"?define(function(e,t,r){r.exports=n}):e.Vow=n})(this);<|endoftext|>


<commit_before>var fs = require('fs');
var path = require('path');
var argv = require('optimist').argv;

var configLoader = function(api, next){

  api.configLoader = {
    _start: function(api, callback){
      api.log('environment: ' + api.env);
      callback();
    }
  }

  api.watchedFiles = [];

  api.watchFileAndAct = function(file, callback){
    if(api.config.general.developmentMode === true && api.watchedFiles.indexOf(file) < 0){
      api.watchedFiles.push(file);
      fs.watchFile(file, {interval: 1000}, function(curr, prev){
        if(curr.mtime > prev.mtime){
          process.nextTick(function(){
            var cleanPath = file;
            if(process.platform === 'win32'){
              cleanPath = file.replace(/\//g, '\\');
            }
            delete require.cache[require.resolve(cleanPath)];
            callback();
          });
        }
      });
    }
  };

  api.unWatchAllFiles = function(){
    for(var i in api.watchedFiles){
      fs.unwatchFile(api.watchedFiles[i]);
    }
    api.watchedFiles = [];
  };

  if(api._startingParams.api){
    api.utils.hashMerge(api, api._startingParams.api);
  }

  api.env = 'development'

  if(argv.NODE_ENV){
    api.env = argv.NODE_ENV;
  } else if(process.env.NODE_ENV){
    api.env = process.env.NODE_ENV;
  }

  var configPath = path.resolve(api.projectRoot, 'config');

  if(argv.config){
    if(argv.config.charAt(0) === '/'){ configPath = argv.config }
    else { configPath = path.resolve(api.projectRoot, argv.config) }
  } else if(process.env.ACTIONHERO_CONFIG) {
    if(process.env.ACTIONHERO_CONFIG.charAt(0) === '/'){ configPath = process.env.ACTIONHERO_CONFIG }
    else { configPath = path.resolve(api.projectRoot, process.env.ACTIONHERO_CONFIG) }
  } else if(!fs.existsSync(configPath)){
    throw new Error(configPath + 'No config directory found in this project, specified with --config, or found in process.env.ACTIONHERO_CONFIG');
  }

  api.loadConfigDirectory = function(configPath, watch){
    var configFiles = api.utils.recursiveDirectoryGlob(configPath);

    var loadRetries = 0;
    var loadErrors = {};
    for(var i = 0, limit = configFiles.length; (i < limit); i++){
      var f = configFiles[i];
      try{
        // attempt configuration file load
        var localConfig = require(f);
        if(localConfig.default){  api.config = api.utils.hashMerge(api.config, localConfig.default, api); }
        if(localConfig[api.env]){ api.config = api.utils.hashMerge(api.config, localConfig[api.env], api); }
        // configuration file load success: clear retries and
        // errors since progress has been made
        loadRetries = 0;
        loadErrors = {};
      } catch(error){
        // error loading configuration, abort if all remaining
        // configuration files have been tried and failed
        // indicating inability to progress
        loadErrors[f] = error.toString();
        if(++loadRetries === limit-i){
            throw new Error('Unable to load configurations, errors: '+JSON.stringify(loadErrors));
        }
        // adjust configuration files list: remove and push
        // failed configuration to the end of the list and
        // continue with next file at same index
        configFiles.push(configFiles.splice(i--, 1)[0]);
        continue;
      }

      if(watch !== false){
        // configuration file loaded: set watch
        api.watchFileAndAct(f, function(){
          api.log('\r\n\r\n*** rebooting due to config change ***\r\n\r\n', 'info');
          delete require.cache[require.resolve(f)];
          api.commands.restart.call(api._self);
        });
      }
    }

    // We load the config twice. Utilize configuration files load order that succeeded on the first pass.
    // This is to allow 'literal' values to be loaded whenever possible, and then for refrences to be resolved
    configFiles.forEach(function(f){
      var localConfig = require(f);
      if(localConfig.default){  api.config = api.utils.hashMerge(api.config, localConfig.default, api); }
      if(localConfig[api.env]){ api.config = api.utils.hashMerge(api.config, localConfig[api.env], api); }
    });

  }

  api.config = {};

  //load the default config of actionhero
  api.loadConfigDirectory(__dirname + '/../config', false);

  //load the project specific config
  api.loadConfigDirectory(configPath);

  var pluginActions      = [];
  var pluginTasks        = [];
  var pluginServers      = [];
  var pluginInitializers = [];
  var pluginPublics      = [];

  //loop over it's plugins
  api.config.general.paths.plugin.forEach(function(p){
    api.config.general.plugins.forEach(function(plugin){
      var pluginPackageBase = path.normalize(p + '/' + plugin);
      if(api.projectRoot !== pluginPackageBase){
        if(fs.existsSync(pluginPackageBase + '/config')){
          //and merge the plugin config
          api.loadConfigDirectory( pluginPackageBase + '/config', false);
          //collect all paths that could have multiple target folders
          pluginActions      = pluginActions.concat(api.config.general.paths.action);
          pluginTasks        = pluginTasks.concat(api.config.general.paths.task);
          pluginServers      = pluginServers.concat(api.config.general.paths.server);
          pluginInitializers = pluginInitializers.concat(api.config.general.paths.initializer);
          pluginPublics      = pluginPublics.concat(api.config.general.paths.public);
        }
        //additionally add the following paths if they exists
        if(fs.existsSync(pluginPackageBase + '/actions')){      pluginActions.unshift(      pluginPackageBase + '/actions'      );}
        if(fs.existsSync(pluginPackageBase + '/tasks')){        pluginTasks.unshift(        pluginPackageBase + '/tasks'        );}
        if(fs.existsSync(pluginPackageBase + '/servers')){      pluginServers.unshift(      pluginPackageBase + '/servers'      );}
        if(fs.existsSync(pluginPackageBase + '/initializers')){ pluginInitializers.unshift( pluginPackageBase + '/initializers' );}
        if(fs.existsSync(pluginPackageBase + '/public')){       pluginPublics.unshift(      pluginPackageBase + '/public'       );}
      }
    });
  });

  //now load the project config again to overrule plugin configs
  api.loadConfigDirectory(configPath);

  //apply plugin paths for actions, tasks, servers and initializers
  api.config.general.paths.action      = pluginActions.concat(api.config.general.paths.action);
  api.config.general.paths.task        = pluginTasks.concat(api.config.general.paths.task);
  api.config.general.paths.server      = pluginServers.concat(api.config.general.paths.server);
  api.config.general.paths.initializer = pluginInitializers.concat(api.config.general.paths.initializer);
  api.config.general.paths.public      = pluginPublics.concat(api.config.general.paths.public);

  // the first plugin path shoud alawys be the local project
  api.config.general.paths.public.reverse();

  //finally merge starting params into the config
  if(api._startingParams.configChanges){
    api.config = api.utils.hashMerge(api.config, api._startingParams.configChanges);
  }

  // cleanup
  api.config.general.paths.action      = api.utils.arrayUniqueify( api.config.general.paths.action.map(path.normalize) );
  api.config.general.paths.task        = api.utils.arrayUniqueify( api.config.general.paths.task.map(path.normalize) );
  api.config.general.paths.server      = api.utils.arrayUniqueify( api.config.general.paths.server.map(path.normalize) );
  api.config.general.paths.initializer = api.utils.arrayUniqueify( api.config.general.paths.initializer.map(path.normalize) );
  api.config.general.paths.public      = api.utils.arrayUniqueify( api.config.general.paths.public.map(path.normalize) );
  api.config.general.paths.pid         = api.utils.arrayUniqueify( api.config.general.paths.pid.map(path.normalize) );
  api.config.general.paths.log         = api.utils.arrayUniqueify( api.config.general.paths.log.map(path.normalize) );
  api.config.general.paths.plugin      = api.utils.arrayUniqueify( api.config.general.paths.plugin.map(path.normalize) );

  next();
}

/////////////////////////////////////////////////////////////////////
// exports
exports.configLoader = configLoader;<commit_msg>config<commit_after>var fs = require('fs');
var path = require('path');
var argv = require('optimist').argv;

var configLoader = function(api, next){

  api.configLoader = {
    _start: function(api, callback){
      api.log('environment: ' + api.env);
      callback();
    }
  }

  api.watchedFiles = [];

  api.watchFileAndAct = function(file, callback){
    if(api.config.general.developmentMode === true && api.watchedFiles.indexOf(file) < 0){
      api.watchedFiles.push(file);
      fs.watchFile(file, {interval: 1000}, function(curr, prev){
        if(curr.mtime > prev.mtime){
          process.nextTick(function(){
            var cleanPath = file;
            if(process.platform === 'win32'){
              cleanPath = file.replace(/\//g, '\\');
            }
            delete require.cache[require.resolve(cleanPath)];
            callback(file);
          });
        }
      });
    }
  };

  api.unWatchAllFiles = function(){
    for(var i in api.watchedFiles){
      fs.unwatchFile(api.watchedFiles[i]);
    }
    api.watchedFiles = [];
  };

  if(api._startingParams.api){
    api.utils.hashMerge(api, api._startingParams.api);
  }

  api.env = 'development'

  if(argv.NODE_ENV){
    api.env = argv.NODE_ENV;
  } else if(process.env.NODE_ENV){
    api.env = process.env.NODE_ENV;
  }

  var configPath = path.resolve(api.projectRoot, 'config');

  if(argv.config){
    if(argv.config.charAt(0) === '/'){ configPath = argv.config }
    else { configPath = path.resolve(api.projectRoot, argv.config) }
  } else if(process.env.ACTIONHERO_CONFIG) {
    if(process.env.ACTIONHERO_CONFIG.charAt(0) === '/'){ configPath = process.env.ACTIONHERO_CONFIG }
    else { configPath = path.resolve(api.projectRoot, process.env.ACTIONHERO_CONFIG) }
  } else if(!fs.existsSync(configPath)){
    throw new Error(configPath + 'No config directory found in this project, specified with --config, or found in process.env.ACTIONHERO_CONFIG');
  }

  var rebootCallback = function(file){
    api.log('\r\n\r\n*** rebooting due to config change (' + file + ') ***\r\n\r\n', 'info');
    delete require.cache[require.resolve(file)];
    api.commands.restart.call(api._self);
  }

  api.loadConfigDirectory = function(configPath, watch){
    var configFiles = api.utils.recursiveDirectoryGlob(configPath);

    var loadRetries = 0;
    var loadErrors = {};
    for(var i = 0, limit = configFiles.length; (i < limit); i++){
      var f = configFiles[i];
      try{
        // attempt configuration file load
        var localConfig = require(f);
        if(localConfig.default){  api.config = api.utils.hashMerge(api.config, localConfig.default, api); }
        if(localConfig[api.env]){ api.config = api.utils.hashMerge(api.config, localConfig[api.env], api); }
        // configuration file load success: clear retries and
        // errors since progress has been made
        loadRetries = 0;
        loadErrors = {};
      } catch(error){
        // error loading configuration, abort if all remaining
        // configuration files have been tried and failed
        // indicating inability to progress
        loadErrors[f] = error.toString();
        if(++loadRetries === limit-i){
            throw new Error('Unable to load configurations, errors: '+JSON.stringify(loadErrors));
        }
        // adjust configuration files list: remove and push
        // failed configuration to the end of the list and
        // continue with next file at same index
        configFiles.push(configFiles.splice(i--, 1)[0]);
        continue;
      }

      if(watch !== false){
        // configuration file loaded: set watch
        api.watchFileAndAct(f, rebootCallback);
      }
    }

    // We load the config twice. Utilize configuration files load order that succeeded on the first pass.
    // This is to allow 'literal' values to be loaded whenever possible, and then for refrences to be resolved
    configFiles.forEach(function(f){
      var localConfig = require(f);
      if(localConfig.default){  api.config = api.utils.hashMerge(api.config, localConfig.default, api); }
      if(localConfig[api.env]){ api.config = api.utils.hashMerge(api.config, localConfig[api.env], api); }
    });

  }

  api.config = {};

  //load the default config of actionhero
  api.loadConfigDirectory(__dirname + '/../config', false);

  //load the project specific config
  api.loadConfigDirectory(configPath);

  var pluginActions      = [];
  var pluginTasks        = [];
  var pluginServers      = [];
  var pluginInitializers = [];
  var pluginPublics      = [];

  //loop over it's plugins
  api.config.general.paths.plugin.forEach(function(p){
    api.config.general.plugins.forEach(function(plugin){
      var pluginPackageBase = path.normalize(p + '/' + plugin);
      if(api.projectRoot !== pluginPackageBase){
        if(fs.existsSync(pluginPackageBase + '/config')){
          //and merge the plugin config
          api.loadConfigDirectory( pluginPackageBase + '/config', false);
          //collect all paths that could have multiple target folders
          pluginActions      = pluginActions.concat(api.config.general.paths.action);
          pluginTasks        = pluginTasks.concat(api.config.general.paths.task);
          pluginServers      = pluginServers.concat(api.config.general.paths.server);
          pluginInitializers = pluginInitializers.concat(api.config.general.paths.initializer);
          pluginPublics      = pluginPublics.concat(api.config.general.paths.public);
        }
        //additionally add the following paths if they exists
        if(fs.existsSync(pluginPackageBase + '/actions')){      pluginActions.unshift(      pluginPackageBase + '/actions'      );}
        if(fs.existsSync(pluginPackageBase + '/tasks')){        pluginTasks.unshift(        pluginPackageBase + '/tasks'        );}
        if(fs.existsSync(pluginPackageBase + '/servers')){      pluginServers.unshift(      pluginPackageBase + '/servers'      );}
        if(fs.existsSync(pluginPackageBase + '/initializers')){ pluginInitializers.unshift( pluginPackageBase + '/initializers' );}
        if(fs.existsSync(pluginPackageBase + '/public')){       pluginPublics.unshift(      pluginPackageBase + '/public'       );}
      }
    });
  });

  //now load the project config again to overrule plugin configs
  api.loadConfigDirectory(configPath);

  //apply plugin paths for actions, tasks, servers and initializers
  api.config.general.paths.action      = pluginActions.concat(api.config.general.paths.action);
  api.config.general.paths.task        = pluginTasks.concat(api.config.general.paths.task);
  api.config.general.paths.server      = pluginServers.concat(api.config.general.paths.server);
  api.config.general.paths.initializer = pluginInitializers.concat(api.config.general.paths.initializer);
  api.config.general.paths.public      = pluginPublics.concat(api.config.general.paths.public);

  // the first plugin path shoud alawys be the local project
  api.config.general.paths.public.reverse();

  //finally merge starting params into the config
  if(api._startingParams.configChanges){
    api.config = api.utils.hashMerge(api.config, api._startingParams.configChanges);
  }

  // cleanup
  api.config.general.paths.action      = api.utils.arrayUniqueify( api.config.general.paths.action.map(path.normalize) );
  api.config.general.paths.task        = api.utils.arrayUniqueify( api.config.general.paths.task.map(path.normalize) );
  api.config.general.paths.server      = api.utils.arrayUniqueify( api.config.general.paths.server.map(path.normalize) );
  api.config.general.paths.initializer = api.utils.arrayUniqueify( api.config.general.paths.initializer.map(path.normalize) );
  api.config.general.paths.public      = api.utils.arrayUniqueify( api.config.general.paths.public.map(path.normalize) );
  api.config.general.paths.pid         = api.utils.arrayUniqueify( api.config.general.paths.pid.map(path.normalize) );
  api.config.general.paths.log         = api.utils.arrayUniqueify( api.config.general.paths.log.map(path.normalize) );
  api.config.general.paths.plugin      = api.utils.arrayUniqueify( api.config.general.paths.plugin.map(path.normalize) );

  next();
}

/////////////////////////////////////////////////////////////////////
// exports
exports.configLoader = configLoader;<|endoftext|>


<commit_before>"use babel";

const fs = require("fs");
const path = require("path");
const EventEmitter = require("events");
const child_process = require("child_process");
const promisify = require('util.promisify');
const chokidar = require("chokidar");

const RunTargetsView = require("./RunTargetsView.js");

const execFile = promisify(child_process.execFile);

let bazelsRunning = {};

class BazelBuildProvider extends EventEmitter {
	constructor(cwd) {
		super();
		this.cwd = cwd;
		this.bazelExec = `E:\\Programs\\bazel-0.6.0-windows-x86_64\\bazel.exe`;
		this.bazelTargets = [];

		if(!bazelsRunning[this.cwd]) {
			bazelsRunning[this.cwd] = 0;
		}
	}

	_watch() {
		if(this.watcher) {
			return;
		}

		this.watcher = chokidar.watch([
			"./WORKSPACE",
			"./BUILD",
			"./**/BUILD",
		], {
			cwd: this.cwd,
			followSymlinks: false,
			awaitWriteFinish: true,
		});

		this.watcher.on('change', (path) => {
			this._refresh();
		});

		// These get triggered everytime, cannot refresh on add/unlink
		// this.watcher.on('add', () => this._refresh());
		// this.watcher.on('unlink', () => this._refresh());
	}

	_refresh() {
		clearTimeout(this._refreshTimeout);

		if(bazelsRunning[this.cwd] > 0) {
			return;
		}

		this._refreshTimeout = setTimeout(() => {
			console.log("refreshing targets...");
			this.emit("refresh");
		}, 10);
	}

	async execBazel(...args) {
		let result;
		bazelsRunning[this.cwd] += 1;

		const options = {
			cwd: this.cwd
		};

		try {
			result = await execFile(this.bazelExec, args, options);
		} catch(err) {
			bazelsRunning[this.cwd] -= 1;
			throw err;
		}

		bazelsRunning[this.cwd] -= 1;

		return result;
	}

	destructor() {
		if(this.watcher) {
			this.watcher.close();
		}
	}

	getNiceName() {
		return "Build Bazel";
	}

	isEligible() {
		const workspaceFilePath = path.resolve(this.cwd, "WORKSPACE");
		let stats = null;

		try {
			stats = fs.statSync(workspaceFilePath);
		} catch(err) {
			if(err.code !== 'ENOENT') {
				throw err;
			}
		}

		return stats && stats.isFile();
	}

	async settings() {

		const execBazel = this.execBazel.bind(this);

		const targetsRaw = await this.execBazel(
			'query',
			'--noimplicit_deps',
			'--nohost_deps',
			`kind('rule', deps(//...))`
		);

		this._watch();

		const targetNames = targetsRaw.split("\n");

		this.bazelTargets = [
			{
				exec: this.bazelExec,
				args: ["clean"],
				name: "clean",
				cwd: this.cwd,
			},
			{
				exec: this.bazelExec,
				name: "run",
				preBuild: async function() {
					console.log(this);
					let runTargetsView = new RunTargetsView();

					let result = await execBazel(
						"query",
						"kind('_binary', deps(//...))"
					);

					let runnableTargetNames = result.split("\n");
					let runnableTargets = [];

					for(let runTargetName of runnableTargetNames) {
						runTargetName = runTargetName.trim();

						if(!runTargetName || !runTargetName.startsWith("//")) {
							continue;
						}

						runnableTargets.push(runTargetName);
					}

					runTargetsView.setItems(runnableTargets);

					try {
						let confirmedTarget = await runTargetsView.awaitItemConfirmed();

						this.args = [
							"run",
							confirmedTarget
						];
					} catch(err) {
						this.exec = "echo";
						this.args = [err];
					}
				}
			},
			{
				exec: this.bazelExec,
				args: ["build", "//...:*"],
				name: "//...:*",
				cwd: this.cwd,
			}
		];

		for(let targetName of targetNames) {
			targetName = targetName.trim();

			if(!targetName || !targetName.startsWith("//")) {
				continue;
			}

			this.bazelTargets.push({
				exec: this.bazelExec,
				args: ["build", targetName],
				name: targetName,
				cwd: this.cwd,
			});
		}

		return this.bazelTargets;
	}
};

exports.BazelBuildProvider = BazelBuildProvider;
<commit_msg>Removed hard coded bazel path<commit_after>"use babel";

const fs = require("fs");
const path = require("path");
const EventEmitter = require("events");
const child_process = require("child_process");
const promisify = require('util.promisify');
const chokidar = require("chokidar");

const RunTargetsView = require("./RunTargetsView.js");

const execFile = promisify(child_process.execFile);

let bazelsRunning = {};

class BazelBuildProvider extends EventEmitter {
	constructor(cwd) {
		super();
		this.cwd = cwd;
		// @TODO: Make bazel executable option
		this.bazelExec = `bazel`;
		this.bazelTargets = [];

		if(!bazelsRunning[this.cwd]) {
			bazelsRunning[this.cwd] = 0;
		}
	}

	_watch() {
		if(this.watcher) {
			return;
		}

		this.watcher = chokidar.watch([
			"./WORKSPACE",
			"./BUILD",
			"./**/BUILD",
		], {
			cwd: this.cwd,
			followSymlinks: false,
			awaitWriteFinish: true,
		});

		this.watcher.on('change', (path) => {
			this._refresh();
		});

		// These get triggered everytime, cannot refresh on add/unlink
		// this.watcher.on('add', () => this._refresh());
		// this.watcher.on('unlink', () => this._refresh());
	}

	_refresh() {
		clearTimeout(this._refreshTimeout);

		if(bazelsRunning[this.cwd] > 0) {
			return;
		}

		this._refreshTimeout = setTimeout(() => {
			console.log("refreshing targets...");
			this.emit("refresh");
		}, 10);
	}

	async execBazel(...args) {
		let result;
		bazelsRunning[this.cwd] += 1;

		const options = {
			cwd: this.cwd
		};

		try {
			result = await execFile(this.bazelExec, args, options);
		} catch(err) {
			bazelsRunning[this.cwd] -= 1;
			throw err;
		}

		bazelsRunning[this.cwd] -= 1;

		return result;
	}

	destructor() {
		if(this.watcher) {
			this.watcher.close();
		}
	}

	getNiceName() {
		return "Build Bazel";
	}

	isEligible() {
		const workspaceFilePath = path.resolve(this.cwd, "WORKSPACE");
		let stats = null;

		try {
			stats = fs.statSync(workspaceFilePath);
		} catch(err) {
			if(err.code !== 'ENOENT') {
				throw err;
			}
		}

		return stats && stats.isFile();
	}

	async settings() {

		const execBazel = this.execBazel.bind(this);

		const targetsRaw = await this.execBazel(
			'query',
			'--noimplicit_deps',
			'--nohost_deps',
			`kind('rule', deps(//...))`
		);

		this._watch();

		const targetNames = targetsRaw.split("\n");

		this.bazelTargets = [
			{
				exec: this.bazelExec,
				args: ["clean"],
				name: "clean",
				cwd: this.cwd,
			},
			{
				exec: this.bazelExec,
				name: "run",
				preBuild: async function() {
					console.log(this);
					let runTargetsView = new RunTargetsView();

					let result = await execBazel(
						"query",
						"kind('_binary', deps(//...))"
					);

					let runnableTargetNames = result.split("\n");
					let runnableTargets = [];

					for(let runTargetName of runnableTargetNames) {
						runTargetName = runTargetName.trim();

						if(!runTargetName || !runTargetName.startsWith("//")) {
							continue;
						}

						runnableTargets.push(runTargetName);
					}

					runTargetsView.setItems(runnableTargets);

					try {
						let confirmedTarget = await runTargetsView.awaitItemConfirmed();

						this.args = [
							"run",
							confirmedTarget
						];
					} catch(err) {
						this.exec = "echo";
						this.args = [err];
					}
				}
			},
			{
				exec: this.bazelExec,
				args: ["build", "//...:*"],
				name: "//...:*",
				cwd: this.cwd,
			}
		];

		for(let targetName of targetNames) {
			targetName = targetName.trim();

			if(!targetName || !targetName.startsWith("//")) {
				continue;
			}

			this.bazelTargets.push({
				exec: this.bazelExec,
				args: ["build", targetName],
				name: targetName,
				cwd: this.cwd,
			});
		}

		return this.bazelTargets;
	}
};

exports.BazelBuildProvider = BazelBuildProvider;
<|endoftext|>


<commit_before>(function (JGS, $, undefined) {
  "use strict";
  /**
   * This class simulates a backend time series data service.  It makes it possible to provide a live demo without
   * having any backend service dependencies.  The server-side downsampling can be done an infinite number of ways
   * and will depend on nature of project, data purpose, size, etc.  Relational databases can be used, but often not
   * ideal.  For those looking for time series specific databases,  I would recommend checking out OpenTSDB, KairoDB,
   * or my newest favorite, tempo-db.com.
   *
   * This class simulates a randomized loading delay, default from 100 - 1000ms.
   *
   *
   @class ServerDataSimulator
   @constructor
   */
  JGS.ServerDataSimulator = function (seriesName, minDelay, maxDelay) {
    this.seriesName = seriesName;
    this.minDelay = minDelay === undefined ? 100 : minDelay;
    this.maxDelay = maxDelay === undefined ? 1000 : maxDelay;

    this.onServerDataLoadCallbacks = $.Callbacks();
  };

  JGS.ServerDataSimulator.prototype.loadData = function (dataLoadReq) {
    //console.log("loadData", dataLoadReq);

    //Generate fake raw data set on first call
    if (!this.serverData) {
      this._generateServerData();
    }

    //Do down sampling per the dataLoadReq
    var dataPoints = [];

    var timePerInterval = (dataLoadReq.endDateTm.getTime() - dataLoadReq.startDateTm.getTime()) / dataLoadReq.numIntervals;
    var currTime = dataLoadReq.startDateTm.getTime();

    //Find start of load request in the raw dataset
    var currIdx = 0;
    for (var i = 0; i < this.serverData.length; i++) {

      if (this.serverData[currIdx].x < (currTime - timePerInterval))
        currIdx++;
      else
        break;
    }

    // Calculate average/min/max while downsampling
    while (currIdx < this.serverData.length && currTime < dataLoadReq.endDateTm.getTime()) {
      var numPoints = 0;
      var sum = 0;
      var min = 9007199254740992;
      var max = -9007199254740992;

      while (currIdx < this.serverData.length && this.serverData[currIdx].x < currTime) {
        sum += this.serverData[currIdx].y;
        min = Math.min(min, this.serverData[currIdx].y);
        max = Math.max(max, this.serverData[currIdx].y);
        currIdx++;
        numPoints++;
      }

      var avg = sum / numPoints

      if (numPoints == 0) {
        if (dataLoadReq.includeMinMax) {
          dataPoints.push({
            x: currTime,
            avg: null,
            min: null,
            max: null
          });
        }
        else {
          dataPoints.push({x: currTime, avg: null});
        }
      }
      else {
        if (dataLoadReq.includeMinMax) {
          dataPoints.push({
            x: currTime,
            avg: Math.round(avg),
            min: Math.round(min),
            max: Math.round(max)
          });
        }
        else {
          dataPoints.push({x: currTime, avg: avg});
        }
      }

      currTime += timePerInterval;
      currTime = Math.round(currTime);
    }

    var delay = (Math.random() * (this.maxDelay - this.minDelay)) + this.minDelay;

    //console.log("dataPoints", dataLoadReq, dataPoints);

    //Random delay for "_onDataLoad" callback to simulate loading data from real server
    setTimeout($.proxy(this._onDataLoad, this, dataLoadReq, dataPoints), delay);

  };

  JGS.ServerDataSimulator.prototype._onDataLoad = function (dataLoadReq, dataPoints) {
    var dataLoadResp = {
      dataPoints: dataPoints
    };
    this.onServerDataLoadCallbacks.fire(dataLoadReq, dataLoadResp);
  };

  /**
   Generates the simulated raw dataset. To make the demo compelling, we want obvious larger trends in the data over time, with
   more detail viewable only when zooming in. That's basically what this does. Date ranges and randomizing is hardcoded, but
   could be easily parameterized.

   NOTE:[2013-08-19 JGS]  This method is a bit of a mess. I just wanted something that generated semi-compelling
   data consistently and played around with a multitude of variations.

   @method _generateServerData
   @private
   */
  JGS.ServerDataSimulator.prototype._generateServerData = function () {

    var startMom = moment('2012-01-01').utc();
    var endMom = moment('2015-01-01').utc();
    //endMom.add('day', -5);

    var min = 500;
    var max = 1500;
    var majorInterval = moment.duration(11, 'days');
    var minorInterval = moment.duration(1, 'minute');

    if (this.seriesName == "Series-A") {
      majorInterval = moment.duration(11, 'days');
      minorInterval = moment.duration(1, 'minute');
    }
    else if (this.seriesName == "Series-B") {
      majorInterval = moment.duration(5, 'days');
      minorInterval = moment.duration(5, 'minute');
    }
    else {
      majorInterval = moment.duration(20, 'days');
      minorInterval = moment.duration(10, 'minute');
    }

    var data = [];

    var totalDur = endMom.valueOf() - startMom.valueOf();

    var currTime = startMom.valueOf();
    var numPoints = (endMom.valueOf() - startMom.valueOf()) / minorInterval.valueOf();

    var period = majorInterval.valueOf();
    var periodNum = currTime / period;

    // just need a number that can change as we iterate, but stays
    // the same for each reload of data set given same start/end dates. This makes the overall trend look the same every time
    // for a given series, and might avoid some confusion in the demo.
    var periodIncr;
    var detailFactor;

    if (this.seriesName == "Series-A") {
      periodIncr = startMom.date() / 31.0; //1-31
      detailFactor = 50 + (Math.random() * 450);
    }
    else if (this.seriesName == "Series-B") {
      periodIncr = ((endMom.date() - 5) / 31.0) * 2; //1-31
      detailFactor = 150 + (Math.random() * 650);
    }
    else {
      periodIncr = Math.random();
      detailFactor = 20 + (Math.random() * 250);
    }

    var lastY = min;

    for (var i = 0; i < numPoints; i++) {
      if (Math.floor(currTime / period) != periodNum) {
        periodNum = Math.floor(currTime / period);
        periodIncr = moment(currTime).date() / 31.0;
        periodIncr = periodIncr * ((0.09) - (0.09 / 2));
      }
      else {

        if (lastY > (max + min) / 2)
          periodIncr = periodIncr - (lastY / (max + min)) * .000002;
        else
          periodIncr = periodIncr + ((max + min - lastY) / (max + min)) * .000002;
      }

      if (Math.floor(currTime / (period / 4) != periodNum)) {
        detailFactor = 50 + (Math.random() * 450);
      }

      lastY += periodIncr;
      if (lastY > max) {
        periodIncr = periodIncr * -1;
      }
      else if (lastY < min) {
        periodIncr = periodIncr * -1;
      }

      var detailY = lastY + (Math.random() - 0.5) * detailFactor;
//      if (detailY > max)
//        detailY = max;
//      if (detailY < min)
//        detailY = min;

      data.push({ x: currTime, y: detailY});
      currTime += minorInterval.valueOf();
    }

    this.serverData = data;

  }

}(window.JGS = window.JGS || {}, jQuery));<commit_msg>Make simulated data range dynamic off of current date<commit_after>(function (JGS, $, undefined) {
  "use strict";
  /**
   * This class simulates a backend time series data service.  It makes it possible to provide a live demo without
   * having any backend service dependencies.  The server-side downsampling can be done an infinite number of ways
   * and will depend on nature of project, data purpose, size, etc.  Relational databases can be used, but often not
   * ideal.  For those looking for time series specific databases,  I would recommend checking out OpenTSDB, KairoDB,
   * or my newest favorite, tempo-db.com.
   *
   * This class simulates a randomized loading delay, default from 100 - 1000ms.
   *
   *
   @class ServerDataSimulator
   @constructor
   */
  JGS.ServerDataSimulator = function (seriesName, minDelay, maxDelay) {
    this.seriesName = seriesName;
    this.minDelay = minDelay === undefined ? 100 : minDelay;
    this.maxDelay = maxDelay === undefined ? 1000 : maxDelay;

    this.onServerDataLoadCallbacks = $.Callbacks();
  };

  JGS.ServerDataSimulator.prototype.loadData = function (dataLoadReq) {
    //console.log("loadData", dataLoadReq);

    //Generate fake raw data set on first call
    if (!this.serverData) {
      this._generateServerData();
    }

    //Do down sampling per the dataLoadReq
    var dataPoints = [];

    var timePerInterval = (dataLoadReq.endDateTm.getTime() - dataLoadReq.startDateTm.getTime()) / dataLoadReq.numIntervals;
    var currTime = dataLoadReq.startDateTm.getTime();

    //Find start of load request in the raw dataset
    var currIdx = 0;
    for (var i = 0; i < this.serverData.length; i++) {

      if (this.serverData[currIdx].x < (currTime - timePerInterval))
        currIdx++;
      else
        break;
    }

    // Calculate average/min/max while downsampling
    while (currIdx < this.serverData.length && currTime < dataLoadReq.endDateTm.getTime()) {
      var numPoints = 0;
      var sum = 0;
      var min = 9007199254740992;
      var max = -9007199254740992;

      while (currIdx < this.serverData.length && this.serverData[currIdx].x < currTime) {
        sum += this.serverData[currIdx].y;
        min = Math.min(min, this.serverData[currIdx].y);
        max = Math.max(max, this.serverData[currIdx].y);
        currIdx++;
        numPoints++;
      }

      var avg = sum / numPoints

      if (numPoints == 0) {
        if (dataLoadReq.includeMinMax) {
          dataPoints.push({
            x: currTime,
            avg: null,
            min: null,
            max: null
          });
        }
        else {
          dataPoints.push({x: currTime, avg: null});
        }
      }
      else {
        if (dataLoadReq.includeMinMax) {
          dataPoints.push({
            x: currTime,
            avg: Math.round(avg),
            min: Math.round(min),
            max: Math.round(max)
          });
        }
        else {
          dataPoints.push({x: currTime, avg: avg});
        }
      }

      currTime += timePerInterval;
      currTime = Math.round(currTime);
    }

    var delay = (Math.random() * (this.maxDelay - this.minDelay)) + this.minDelay;

    //console.log("dataPoints", dataLoadReq, dataPoints);

    //Random delay for "_onDataLoad" callback to simulate loading data from real server
    setTimeout($.proxy(this._onDataLoad, this, dataLoadReq, dataPoints), delay);

  };

  JGS.ServerDataSimulator.prototype._onDataLoad = function (dataLoadReq, dataPoints) {
    var dataLoadResp = {
      dataPoints: dataPoints
    };
    this.onServerDataLoadCallbacks.fire(dataLoadReq, dataLoadResp);
  };

  /**
   Generates the simulated raw dataset. To make the demo compelling, we want obvious larger trends in the data over time, with
   more detail viewable only when zooming in. That's basically what this does. Date ranges and randomizing is hardcoded, but
   could be easily parameterized.

   NOTE:[2013-08-19 JGS]  This method is a bit of a mess. I just wanted something that generated semi-compelling
   data consistently and played around with a multitude of variations.

   @method _generateServerData
   @private
   */
  JGS.ServerDataSimulator.prototype._generateServerData = function () {

    var startMom = moment().utc();
	startMom.add('year',-3);

    var endMom = moment().utc();
    endMom.add('day', 30);

    var min = 500;
    var max = 1500;
    var majorInterval = moment.duration(11, 'days');
    var minorInterval = moment.duration(1, 'minute');

    if (this.seriesName == "Series-A") {
      majorInterval = moment.duration(11, 'days');
      minorInterval = moment.duration(1, 'minute');
    }
    else if (this.seriesName == "Series-B") {
      majorInterval = moment.duration(5, 'days');
      minorInterval = moment.duration(5, 'minute');
    }
    else {
      majorInterval = moment.duration(20, 'days');
      minorInterval = moment.duration(10, 'minute');
    }

    var data = [];

    var totalDur = endMom.valueOf() - startMom.valueOf();

    var currTime = startMom.valueOf();
    var numPoints = (endMom.valueOf() - startMom.valueOf()) / minorInterval.valueOf();

    var period = majorInterval.valueOf();
    var periodNum = currTime / period;

    // just need a number that can change as we iterate, but stays
    // the same for each reload of data set given same start/end dates. This makes the overall trend look the same every time
    // for a given series, and might avoid some confusion in the demo.
    var periodIncr;
    var detailFactor;

    if (this.seriesName == "Series-A") {
      periodIncr = startMom.date() / 31.0; //1-31
      detailFactor = 50 + (Math.random() * 450);
    }
    else if (this.seriesName == "Series-B") {
      periodIncr = ((endMom.date() - 5) / 31.0) * 2; //1-31
      detailFactor = 150 + (Math.random() * 650);
    }
    else {
      periodIncr = Math.random();
      detailFactor = 20 + (Math.random() * 250);
    }

    var lastY = min;

    for (var i = 0; i < numPoints; i++) {
      if (Math.floor(currTime / period) != periodNum) {
        periodNum = Math.floor(currTime / period);
        periodIncr = moment(currTime).date() / 31.0;
        periodIncr = periodIncr * ((0.09) - (0.09 / 2));
      }
      else {

        if (lastY > (max + min) / 2)
          periodIncr = periodIncr - (lastY / (max + min)) * .000002;
        else
          periodIncr = periodIncr + ((max + min - lastY) / (max + min)) * .000002;
      }

      if (Math.floor(currTime / (period / 4) != periodNum)) {
        detailFactor = 50 + (Math.random() * 450);
      }

      lastY += periodIncr;
      if (lastY > max) {
        periodIncr = periodIncr * -1;
      }
      else if (lastY < min) {
        periodIncr = periodIncr * -1;
      }

      var detailY = lastY + (Math.random() - 0.5) * detailFactor;
//      if (detailY > max)
//        detailY = max;
//      if (detailY < min)
//        detailY = min;

      data.push({ x: currTime, y: detailY});
      currTime += minorInterval.valueOf();
    }

    this.serverData = data;

  }

}(window.JGS = window.JGS || {}, jQuery));<|endoftext|>


<commit_before>google.charts.load('current', {'packages':['timeline']});
google.charts.setOnLoadCallback(drawChart);

function drawChart() {
	var description = {
		'Masters':'
M.S. Computer Science

San Jose State University

GPA 3.5
',

		'Insight':'
Insight Global

GPOM Validation Engineering Supervisor

Compliance of Gas Pipeline Operations and Maintenance
',
		'Assistant':'
Topics in Wireless Mobile Networking

Teacher\'s Assistant

San Jose State University

Docker, Nutanix Cloud, Slack, VirtualBox, Mininet, OpenDayLight, NS3
',
		'Intern':'
Findlaw

Software Intern

Project coordinating

Migration of web content to Adobe CQ 5.6.1

Development of web page and components

Redirection of Legacy Domains
',
		'Engineer':'
Chicago Bridge &amp Iron

Project Engineer and Coordinator

Customer Success

On-Site project management

System design, commissioning, troubleshooting, and operations of treatment systems

Testing and troubleshooting of logic controllers and control systems
',

		'ChemConvert':'
ChemConvert

A conversion tool for chemical compounds

React, ESlint, Webpack, Sass
',
		'IntTips':'
IntTips

A simple tip calculator that ceiling rounds to the next integer value

express, node, html, css, javascript, jquery
',
		'Currency':'
Currency Exchange

A currency exchange rate calculator

express, node, html, css, sass, javascript, jquery, gulp, minification, browsersync, typescript
',
		'Park':'
Park Information

A list of group picnic areas in the Tri-Valley

Tomcat, apache, html, css, javascript, jquery, jsp, servlet, ajax, mysql
',
		'SignUp':'
Sign Up Sheet

A simple sign up sheet for a group of users with no admin

Docker, apache, html, css, javascript, jquery, php, ajax, mysql
',

		'Simple':'
Aeris IoT Workshop

Simple Bottle

1st Place

Simple pill bottle to prevent prescription drug abuse
',

		'Node':'
Node Summit

Volunteer

San Francisco, CA
',
		'Structure':'
Structure Conference

Volunteer

San Francisco, CA
',

		'timeline':'
Timeline

This timeline
',
		'treemap':'
TreeMap

TreeMap of my Resume
',
	}

	var today = new Date();
	var data = new google.visualization.DataTable();

	data.addColumn({ type: 'string', id: 'Position' });
    data.addColumn({ type: 'string', id: 'Event' });
    data.addColumn({ type: 'string', role: 'tooltip', 'p': {'html': true }});
    data.addColumn({ type: 'date', id: 'Start' });
    data.addColumn({ type: 'date', id: 'End' });

	data.addRows([
		['Education','Master\'s Degree', description['Masters'], new Date(2014, 1, 1), new Date(2017, 5, 1)],

		['Experience', 'Validation Engineer', description['Insight'], new Date(2017, 11, 1), new Date(today.getFullYear(), today.getMonth()+1, today.getDate())],
		['Experience', 'Teaching Assistant', description['Assistant'], new Date(2017, 1, 1), new Date(2017, 5, 1)],
		['Experience', 'Software Intern', description['Intern'], new Date(2015, 8, 1), new Date(2016, 8, 4)],
		['Experience', 'Project Engineer', description['Engineer'], new Date(2014, 1, 1), new Date(2015, 8, 1)],

		['Additional Info', 'ChemConvert', description['ChemConvert'], new Date(2017, 10, 1), new Date(2017, 11, 18)],
		['Additional Info', 'IntTips', description['IntTips'], new Date(2017, 8, 1), new Date(2017, 8, 30)],
		['Additional Info', 'Currency Exchange', description['Currency'], new Date(2017, 9, 1), new Date(2017, 9, 30)],
		['Additional Info', 'Park Information', description['Park'], new Date(2017, 7, 1), new Date(2017, 7, 30)],
		['Additional Info', 'Sign Up Sheet', description['SignUp'], new Date(2017, 6, 1), new Date(2017, 6, 30)],
		['Additional Info', 'Simple Bottle', description['Simple'], new Date(2016, 3, 19), new Date(2016, 4, 2)],
		['Additional Info', 'Node Summit', description['Node'], new Date(2016, 7, 26), new Date(2016, 7, 28)],
		['Additional Info', 'Structure Conference', description['Structure'], new Date(2016, 11, 8), new Date(2016, 11, 9)],
		['Additional Info', 'Timeline', description['timeline'], new Date(2017, 10, 21), new Date(2017, 10, 30)],
		['Additional Info', 'TreeMap', description['treemap'], new Date(2017, 10, 2), new Date(2017, 10, 20)],
	]);

	var container = document.getElementById('timeline2');
    var chart = new google.visualization.Timeline(container);

	chart.draw(data, options);

}<commit_msg>added AoG<commit_after>google.charts.load('current', {'packages':['timeline']});
google.charts.setOnLoadCallback(drawChart);

function drawChart() {
	var description = {
		'Masters':'
M.S. Computer Science

San Jose State University

GPA 3.5
',

		'AoG':'
Accenture at Google

Global Operations Lead

Actions on Google
',
		'Insight':'
Insight Global

GPOM Validation Engineering Supervisor

Compliance of Gas Pipeline Operations and Maintenance
',
		'Assistant':'
Topics in Wireless Mobile Networking

Teacher\'s Assistant

San Jose State University

Docker, Nutanix Cloud, Slack, VirtualBox, Mininet, OpenDayLight, NS3
',
		'Intern':'
Findlaw

Software Intern

Project coordinating

Migration of web content to Adobe CQ 5.6.1

Development of web page and components

Redirection of Legacy Domains
',
		'Engineer':'
Chicago Bridge &amp Iron

Project Engineer and Coordinator

Customer Success

On-Site project management

System design, commissioning, troubleshooting, and operations of treatment systems

Testing and troubleshooting of logic controllers and control systems
',

		'ChemConvert':'
ChemConvert

A conversion tool for chemical compounds

React, ESlint, Webpack, Sass
',
		'IntTips':'
IntTips

A simple tip calculator that ceiling rounds to the next integer value

express, node, html, css, javascript, jquery
',
		'Currency':'
Currency Exchange

A currency exchange rate calculator

express, node, html, css, sass, javascript, jquery, gulp, minification, browsersync, typescript
',
		'Park':'
Park Information

A list of group picnic areas in the Tri-Valley

Tomcat, apache, html, css, javascript, jquery, jsp, servlet, ajax, mysql
',
		'SignUp':'
Sign Up Sheet

A simple sign up sheet for a group of users with no admin

Docker, apache, html, css, javascript, jquery, php, ajax, mysql
',

		'Simple':'
Aeris IoT Workshop

Simple Bottle

1st Place

Simple pill bottle to prevent prescription drug abuse
',

		'Node':'
Node Summit

Volunteer

San Francisco, CA
',
		'Structure':'
Structure Conference

Volunteer

San Francisco, CA
',

		'timeline':'
Timeline

This timeline
',
		'treemap':'
TreeMap

TreeMap of my Resume
',
	}

	var today = new Date();
	var data = new google.visualization.DataTable();

	data.addColumn({ type: 'string', id: 'Position' });
    data.addColumn({ type: 'string', id: 'Event' });
    data.addColumn({ type: 'string', role: 'tooltip', 'p': {'html': true }});
    data.addColumn({ type: 'date', id: 'Start' });
    data.addColumn({ type: 'date', id: 'End' });

	data.addRows([
		['Education','Master\'s Degree', description['Masters'], new Date(2014, 1, 1), new Date(2017, 5, 1)],

		['Experience', 'Global Operations Lead', description['AoG'], new Date(2018, 2, 21), new Date(today.getFullYear(), today.getMonth()+1, today.getDate())],
		['Experience', 'Validation Engineer', description['Insight'], new Date(2017, 11, 1), new Date(2018, 2, 16],
		['Experience', 'Teaching Assistant', description['Assistant'], new Date(2017, 1, 1), new Date(2017, 5, 1)],
		['Experience', 'Software Intern', description['Intern'], new Date(2015, 8, 1), new Date(2016, 8, 4)],
		['Experience', 'Project Engineer', description['Engineer'], new Date(2014, 1, 1), new Date(2015, 8, 1)],

		['Additional Info', 'ChemConvert', description['ChemConvert'], new Date(2017, 10, 1), new Date(2017, 11, 18)],
		['Additional Info', 'IntTips', description['IntTips'], new Date(2017, 8, 1), new Date(2017, 8, 30)],
		['Additional Info', 'Currency Exchange', description['Currency'], new Date(2017, 9, 1), new Date(2017, 9, 30)],
		['Additional Info', 'Park Information', description['Park'], new Date(2017, 7, 1), new Date(2017, 7, 30)],
		['Additional Info', 'Sign Up Sheet', description['SignUp'], new Date(2017, 6, 1), new Date(2017, 6, 30)],
		['Additional Info', 'Simple Bottle', description['Simple'], new Date(2016, 3, 19), new Date(2016, 4, 2)],
		['Additional Info', 'Node Summit', description['Node'], new Date(2016, 7, 26), new Date(2016, 7, 28)],
		['Additional Info', 'Structure Conference', description['Structure'], new Date(2016, 11, 8), new Date(2016, 11, 9)],
		['Additional Info', 'Timeline', description['timeline'], new Date(2017, 10, 21), new Date(2017, 10, 30)],
		['Additional Info', 'TreeMap', description['treemap'], new Date(2017, 10, 2), new Date(2017, 10, 20)],
	]);

	var container = document.getElementById('timeline2');
    var chart = new google.visualization.Timeline(container);

	chart.draw(data, options);

}<|endoftext|>


<commit_before>/*
 * jQuery UI Nested Sortable
 * v 1.3.5 / 21 jun 2012
 * http://mjsarfatti.com/code/nestedSortable
 *
 * Depends on:
 *	 jquery.ui.sortable.js 1.8+
 *
 * Copyright (c) 2010-2012 Manuele J Sarfatti
 * Licensed under the MIT License
 * http://www.opensource.org/licenses/mit-license.php
 */

(function($) {

	$.widget("mjs.nestedSortable", $.extend({}, $.ui.sortable.prototype, {

		options: {
			tabSize: 20,
			disableNesting: 'mjs-nestedSortable-no-nesting',
			errorClass: 'mjs-nestedSortable-error',
			doNotClear: false,
			listType: 'ol',
			maxLevels: 0,
			protectRoot: false,
			rootID: null,
			rtl: false,
			isAllowed: function(item, parent) { return true; }
		},

		_create: function() {
			this.element.data('sortable', this.element.data('nestedSortable'));

			if (!this.element.is(this.options.listType))
				throw new Error('nestedSortable: Please check the listType option is set to your actual list type');

			return $.ui.sortable.prototype._create.apply(this, arguments);
		},

		destroy: function() {
			this.element
				.removeData("nestedSortable")
				.unbind(".nestedSortable");
			return $.ui.sortable.prototype.destroy.apply(this, arguments);
		},

		_mouseDrag: function(event) {

			//Compute the helpers position
			this.position = this._generatePosition(event);
			this.positionAbs = this._convertPositionTo("absolute");

			if (!this.lastPositionAbs) {
				this.lastPositionAbs = this.positionAbs;
			}

			var o = this.options;

			//Do scrolling
			if(this.options.scroll) {
				var scrolled = false;
				if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML') {

					if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
						this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
					else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity)
						this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;

					if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
						this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
					else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity)
						this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;

				} else {

					if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
						scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
					else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
						scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);

					if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
						scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
					else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
						scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);

				}

				if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
					$.ui.ddmanager.prepareOffsets(this, event);
			}

			//Regenerate the absolute position used for position checks
			this.positionAbs = this._convertPositionTo("absolute");

      // Find the top offset before rearrangement,
      var previousTopOffset = this.placeholder.offset().top;

			//Set the helper position
			if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
			if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';

			//Rearrange
			for (var i = this.items.length - 1; i >= 0; i--) {

				//Cache variables and intersection, continue if no intersection
				var item = this.items[i], itemElement = item.item[0], intersection = this._intersectsWithPointer(item);
				if (!intersection) continue;

				if(itemElement != this.currentItem[0] //cannot intersect with itself
					&&	this.placeholder[intersection == 1 ? "next" : "prev"]()[0] != itemElement //no useless actions that have been done before
					&&	!$.contains(this.placeholder[0], itemElement) //no action if the item moved is the parent of the item checked
					&& (this.options.type == 'semi-dynamic' ? !$.contains(this.element[0], itemElement) : true)
					//&& itemElement.parentNode == this.placeholder[0].parentNode // only rearrange items within the same container
				) {

					$(itemElement).mouseenter();

					this.direction = intersection == 1 ? "down" : "up";

					if (this.options.tolerance == "pointer" || this._intersectsWithSides(item)) {
						$(itemElement).mouseleave();
						this._rearrange(event, item);
					} else {
						break;
					}

					// Clear emtpy ul's/ol's
					this._clearEmpty(itemElement);

					this._trigger("change", event, this._uiHash());
					break;
				}
			}

			var parentItem = (this.placeholder[0].parentNode.parentNode &&
							 $(this.placeholder[0].parentNode.parentNode).closest('.ui-sortable').length)
				       			? $(this.placeholder[0].parentNode.parentNode)
				       			: null,
			    level = this._getLevel(this.placeholder),
			    childLevels = this._getChildLevels(this.helper);

      // To find the previous sibling in the list, keep backtracking until we hit a valid list item.
			var previousItem = this.placeholder[0].previousSibling ? $(this.placeholder[0].previousSibling) : null;
			if (previousItem != null) {
				while (previousItem[0].nodeName.toLowerCase() != 'li' || previousItem[0] == this.currentItem[0] || previousItem[0] == this.helper[0]) {
					if (previousItem[0].previousSibling) {
						previousItem = $(previousItem[0].previousSibling);
					} else {
						previousItem = null;
						break;
					}
				}
			}

      // To find the next sibling in the list, keep stepping forward until we hit a valid list item.
      var nextItem = this.placeholder[0].nextSibling ? $(this.placeholder[0].nextSibling) : null;
      if (nextItem != null) {
        while (nextItem[0].nodeName.toLowerCase() != 'li' || nextItem[0] == this.currentItem[0] || nextItem[0] == this.helper[0]) {
          if (nextItem[0].nextSibling) {
            nextItem = $(nextItem[0].nextSibling);
          } else {
            nextItem = null;
            break;
          }
        }
      }

			var newList = document.createElement(o.listType);

			this.beyondMaxLevels = 0;

			// If the item is moved to the left, send it to its parent's level unless there are siblings below it.
			if (parentItem != null && nextItem == null &&
					(o.rtl && (this.positionAbs.left + this.helper.outerWidth() > parentItem.offset().left + parentItem.outerWidth()) ||
					!o.rtl && (this.positionAbs.left < parentItem.offset().left))) {
				parentItem.after(this.placeholder[0]);
				this._clearEmpty(parentItem[0]);
				this._trigger("change", event, this._uiHash());
			}
			// If the item is below a sibling and is moved to the right, make it a child of that sibling.
			else if (previousItem != null &&
						(o.rtl && (this.positionAbs.left + this.helper.outerWidth() < previousItem.offset().left + previousItem.outerWidth() - o.tabSize) ||
						!o.rtl && (this.positionAbs.left > previousItem.offset().left + o.tabSize))) {
				this._isAllowed(previousItem, level, level+childLevels+1);
				if (!previousItem.children(o.listType).length) {
					previousItem[0].appendChild(newList);
				}
        // If this item is being moved from the top, add it to the top of the list.
        if (previousTopOffset && (previousTopOffset <= previousItem.offset().top)) {
          previousItem.children(o.listType).prepend(this.placeholder);
        }
        // Otherwise, add it to the bottom of the list.
        else {
				  previousItem.children(o.listType)[0].appendChild(this.placeholder[0]);
        }
				this._trigger("change", event, this._uiHash());
			}
			else {
				this._isAllowed(parentItem, level, level+childLevels);
			}

			//Post events to containers
			this._contactContainers(event);

			//Interconnect with droppables
			if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);

			//Call callbacks
			this._trigger('sort', event, this._uiHash());

			this.lastPositionAbs = this.positionAbs;
			return false;

		},

		_mouseStop: function(event, noPropagation) {

			// If the item is in a position not allowed, send it back
			if (this.beyondMaxLevels) {

				this.placeholder.removeClass(this.options.errorClass);

				if (this.domPosition.prev) {
					$(this.domPosition.prev).after(this.placeholder);
				} else {
					$(this.domPosition.parent).prepend(this.placeholder);
				}

				this._trigger("revert", event, this._uiHash());

			}

			// Clean last empty ul/ol
			for (var i = this.items.length - 1; i >= 0; i--) {
				var item = this.items[i].item[0];
				this._clearEmpty(item);
			}

			$.ui.sortable.prototype._mouseStop.apply(this, arguments);

		},

		serialize: function(options) {

			var o = $.extend({}, this.options, options),
				items = this._getItemsAsjQuery(o && o.connected),
			    str = [];

			$(items).each(function() {
				var res = ($(o.item || this).attr(o.attribute || 'id') || '')
						.match(o.expression || (/(.+)[-=_](.+)/)),
				    pid = ($(o.item || this).parent(o.listType)
						.parent(o.items)
						.attr(o.attribute || 'id') || '')
						.match(o.expression || (/(.+)[-=_](.+)/));

				if (res) {
					str.push(((o.key || res[1]) + '[' + (o.key && o.expression ? res[1] : res[2]) + ']')
						+ '='
						+ (pid ? (o.key && o.expression ? pid[1] : pid[2]) : o.rootID));
				}
			});

			if(!str.length && o.key) {
				str.push(o.key + '=');
			}

			return str.join('&');

		},

		toHierarchy: function(options) {

			var o = $.extend({}, this.options, options),
				sDepth = o.startDepthCount || 0,
			    ret = [];

			$(this.element).children(o.items).each(function () {
				var level = _recursiveItems(this);
				ret.push(level);
			});

			return ret;

			function _recursiveItems(item) {
				var id = ($(item).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));

				var restricted = $(item).data("restricted");
				var slug = $(item).data("slug");
				var visible = $(item).data("visible");

				if (id) {
					var currentItem = {"id" : id[2], "slug" : slug, "restricted" : restricted, "visible" : visible};
					if ($(item).children(o.listType).children(o.items).length > 0) {
						currentItem.children = [];
						$(item).children(o.listType).children(o.items).each(function() {
							var level = _recursiveItems(this);
							currentItem.children.push(level);
						});
					}
					return currentItem;
				}
			}
		},

		toArray: function(options) {

			var o = $.extend({}, this.options, options),
				sDepth = o.startDepthCount || 0,
			    ret = [],
			    left = 2;

			ret.push({
				"item_id": o.rootID,
				"parent_id": 'none',
				"depth": sDepth,
				"left": '1',
				"right": ($(o.items, this.element).length + 1) * 2
			});

			$(this.element).children(o.items).each(function () {
				left = _recursiveArray(this, sDepth + 1, left);
			});

			ret = ret.sort(function(a,b){ return (a.left - b.left); });

			return ret;

			function _recursiveArray(item, depth, left) {

				var right = left + 1,
				    id,
				    pid;

				if ($(item).children(o.listType).children(o.items).length > 0) {
					depth ++;
					$(item).children(o.listType).children(o.items).each(function () {
						right = _recursiveArray($(this), depth, right);
					});
					depth --;
				}

				id = ($(item).attr(o.attribute || 'id')).match(o.expression || (/(.+)[-=_](.+)/));

				if (depth === sDepth + 1) {
					pid = o.rootID;
				} else {
					var parentItem = ($(item).parent(o.listType)
											 .parent(o.items)
											 .attr(o.attribute || 'id'))
											 .match(o.expression || (/(.+)[-=_](.+)/));
					pid = parentItem[2];
				}

				if (id) {
						ret.push({"item_id": id[2], "parent_id": pid, "depth": depth, "left": left, "right": right});
				}

				left = right + 1;
				return left;
			}

		},

		_clearEmpty: function(item) {

			var emptyList = $(item).children(this.options.listType);
			if (emptyList.length && !emptyList.children().length && !this.options.doNotClear) {
				emptyList.remove();
			}

		},

		_getLevel: function(item) {

			var level = 1;

			if (this.options.listType) {
				var list = item.closest(this.options.listType);
				while (list && list.length > 0 &&
                    	!list.is('.ui-sortable')) {
					level++;
					list = list.parent().closest(this.options.listType);
				}
			}

			return level;
		},

		_getChildLevels: function(parent, depth) {
			var self = this,
			    o = this.options,
			    result = 0;
			depth = depth || 0;

			$(parent).children(o.listType).children(o.items).each(function (index, child) {
					result = Math.max(self._getChildLevels(child, depth + 1), result);
			});

			return depth ? result + 1 : result;
		},

		_isAllowed: function(parentItem, level, levels) {
			var o = this.options,
				isRoot = $(this.domPosition.parent).hasClass('ui-sortable') ? true : false,
				maxLevels = this.placeholder.closest('.ui-sortable').nestedSortable('option', 'maxLevels'); // this takes into account the maxLevels set to the recipient list

			// Is the root protected?
			// Are we trying to nest under a no-nest?
			// Are we nesting too deep?
			if (!o.isAllowed(this.currentItem, parentItem) ||
				parentItem && parentItem.hasClass(o.disableNesting) ||
				o.protectRoot && (parentItem == null && !isRoot || isRoot && level > 1)) {
					this.placeholder.addClass(o.errorClass);
					if (maxLevels < levels && maxLevels != 0) {
						this.beyondMaxLevels = levels - maxLevels;
					} else {
						this.beyondMaxLevels = 1;
					}
			} else {
				if (maxLevels < levels && maxLevels != 0) {
					this.placeholder.addClass(o.errorClass);
					this.beyondMaxLevels = levels - maxLevels;
				} else {
					this.placeholder.removeClass(o.errorClass);
					this.beyondMaxLevels = 0;
				}
			}
		}

	}));

	$.mjs.nestedSortable.prototype.options = $.extend({}, $.ui.sortable.prototype.options, $.mjs.nestedSortable.prototype.options);
})(jQuery);
<commit_msg>Remove nestedSortable jQuery plugin<commit_after><|endoftext|>


<commit_before>const
    moment = require('moment'),
    waterfall = require("async/waterfall")
;

/**
 * Another option would be to get all the today offers and compare based on the user's categories
 * @param datastore
 * @param userObject
 * @returns {Promise}
 */
module.exports = function (datastore, userObject) {
    function fromDatastore (obj) {
        let newObj = obj.messageData;
        newObj.id = obj[datastore.KEY].id;
        return newObj;
    }

    return new Promise((resolve, reject) => {
        let
            today = moment().format('YYYY-MM-DD'),
            posts = []
        ;

        function checkDup (postEntity) {
            let dup = false;
            for (let post of posts) {
                if (post.id === postEntity.id) dup = true;
            }
            return dup;
        }

        function createWaterfallFunction (category) {
            return function (cb) {
                let query = datastore.createQuery("Post")
                    .filter('sentDate','>=', new Date(today))
                    .filter('category', '=', category)
                ;
                query.runStream()
                    .on('error', err => {
                        console.error("Error transation",err);
                    })
                    .on('info', info => {
                        console.log('getting posts today offers info', info)
                    })
                    .on('end', () => {
                        cb()
                    })
                    .on('data', entity => {
                        let post = fromDatastore(entity);
                        if (!checkDup(post)) {
                            posts.push(post);
                        }
                    })
            }
        }

        let functions = [];
        for (let category of userObject.interest) {
            createWaterfallFunction(category);
        }

        waterfall(functions, (err, result) => {
            if (posts.length > 0) {
                resolve(posts);
            } else {
                if (err) {
                    console.error(err);
                } else
                    console.error("today offers query did not throwed results", entities);
                reject();
            }
        });
    });
};

/*[
    {
        title: "Carlin's gay",
        subtitle: "Next-generation virtual reality",
        item_url: "https://www.oculus.com/en-us/rift/",
        image_url: "http://messengerdemo.parseapp.com/img/rift.png",
        buttons: [{
            type: "web_url",
            url: "https://goo.gl/maps/oguRzdieeyy",
            title: "Ver ubicación"
        }, {
            type: "postback",
            title: "Compartir anuncio",
            payload: "Payload for first bubble",
        }],
    }, {
        title: "touch",
        subtitle: "Your Hands, Now in VR",
        item_url: "https://www.oculus.com/en-us/touch/",
        image_url: "http://messengerdemo.parseapp.com/img/touch.png",
        buttons: [{
            type: "web_url",
            url: "https://www.facebook.com/sharer/sharer.php?u=https%3A//goo.gl/maps/oguRzdieeyy",
            title: "Share this con FB!"
        }, {
            type: "element_share",
        }]
    }
];*/
<commit_msg>Implementing carousel of today offers<commit_after>const
    moment = require('moment'),
    waterfall = require("async/waterfall")
;

/**
 * Another option would be to get all the today offers and compare based on the user's categories
 * @param datastore
 * @param userObject
 * @returns {Promise}
 */
module.exports = function (datastore, userObject) {
    function fromDatastore (obj) {
        let newObj = obj.messageData;
        newObj.id = obj[datastore.KEY].id;
        return newObj;
    }

    return new Promise((resolve, reject) => {
        let
            today = moment().format('YYYY-MM-DD'),
            posts = []
        ;

        function checkDup (postEntity) {
            let dup = false;
            for (let post of posts) {
                if (post.id === postEntity.id) dup = true;
            }
            return dup;
        }

        function createWaterfallFunction (category) {
            return function (cb) {
                let query = datastore.createQuery("Post")
                    .filter('sentDate','>=', new Date(today))
                    .filter('category', '=', category)
                ;
                query.runStream()
                    .on('error', err => {
                        console.error("Error transation",err);
                    })
                    .on('info', info => {
                        console.log('getting posts today offers info', info)
                    })
                    .on('end', () => {
                        cb()
                    })
                    .on('data', entity => {
                        let post = fromDatastore(entity);
                        if (!checkDup(post)) {
                            posts.push(post);
                        }
                    })
            }
        }

        let functions = [];
        for (let category of userObject.interest) {
            createWaterfallFunction(category);
        }

        waterfall(functions, (err, result) => {
            if (posts.length > 0) {
                resolve(posts);
            } else {
                if (err) {
                    console.error(err);
                } else
                    console.error("today offers query did not throwed results", userObject.interest);
                reject();
            }
        });
    });
};

/*[
    {
        title: "Carlin's gay",
        subtitle: "Next-generation virtual reality",
        item_url: "https://www.oculus.com/en-us/rift/",
        image_url: "http://messengerdemo.parseapp.com/img/rift.png",
        buttons: [{
            type: "web_url",
            url: "https://goo.gl/maps/oguRzdieeyy",
            title: "Ver ubicación"
        }, {
            type: "postback",
            title: "Compartir anuncio",
            payload: "Payload for first bubble",
        }],
    }, {
        title: "touch",
        subtitle: "Your Hands, Now in VR",
        item_url: "https://www.oculus.com/en-us/touch/",
        image_url: "http://messengerdemo.parseapp.com/img/touch.png",
        buttons: [{
            type: "web_url",
            url: "https://www.facebook.com/sharer/sharer.php?u=https%3A//goo.gl/maps/oguRzdieeyy",
            title: "Share this con FB!"
        }, {
            type: "element_share",
        }]
    }
];*/
<|endoftext|>