<commit_before>Core.Object.extend('Core.Event',
/** @lends Core.Event#*/
{
	defaultPrevented: false,
	type: undefined,
	bubbles: true,
	cancelable: true,
	view: undefined,
	target: undefined,
	cancelBubble: false,

	/**
    *   @constructs
	*	@class
    *   @extends Core.Object
	*/
	constructor: function(config) {
		if(config.type != undefined)
			this.type = config.type;
	},

	preventDefault: function() {
		this.defaultPrevented = true;
	},

	stopPropagation: function() {
		this.cancelBubble = true;
	},

	stopImmediatePropagation: function() {
		// TODO
	},

	dispatchEvent: function(target) {
		this.target = target;

		if(this.bubbles) {
			var stack = [];

			var current = this.target;
			while(current != undefined) {
				if('dispatchEvent' in current)
					stack.push(current);
//				if('offsetParent' in current)
//					current = current.offsetParent;
//				else
					current = current.parentNode;
			}
			stack.push(window);

			// mode capture
			for(var i = stack.length - 1; (i >= 0) && (!this.cancelBubble); i--) {
				current = stack[i];
				if((current.__extendCaptureEvents != undefined) && (current.__extendCaptureEvents[this.type] != undefined)) {
					var list = [];
					for(var i2 = 0; i2 < current.__extendCaptureEvents[this.type].length; i2++)
						list.push(current.__extendCaptureEvents[this.type][i2]);
					for(var i2 = 0; i2 < list.length; i2++)
						list[i2].call(current, this);
				}
			}

			// bubble mode
			for(var i = 0; (i < stack.length) && (!this.cancelBubble); i++) {
				current = stack[i];
				if((current.__extendEvents != undefined) && (current.__extendEvents[this.type] != undefined)) {
					var list = [];
					for(var i2 = 0; i2 < current.__extendEvents[this.type].length; i2++)
						list.push(current.__extendEvents[this.type][i2]);
					for(var i2 = 0; i2 < list.length; i2++)
						list[i2].call(current, this);
				}
			}
		}
		else {
			if((target.__extendCaptureEvents != undefined) && (target.__extendCaptureEvents[this.type] != undefined)) {
				var list = [];
				for(var i2 = 0; i2 < target.__extendCaptureEvents[this.type].length; i2++)
					list.push(target.__extendCaptureEvents[this.type][i2]);
				for(var i = 0; i < list.length; i++)
					list[i].call(target, this);
			}
			if((target.__extendEvents != undefined) && (target.__extendEvents[this.type] != undefined)) {
				var list = [];
				for(var i2 = 0; i2 < target.__extendEvents[this.type].length; i2++)
					list.push(target.__extendEvents[this.type][i2]);
				for(var i = 0; i < list.length; i++)
					list[i].call(target, this);
			}
		}
		return this.defaultPrevented;
	}
},
{},
/** @lends Core.Event*/
{
	events: {},

	types: {},

	registerEvent: function(eventName, type) {
		Core.Event.events[eventName] = type;
	},

	getEvent: function(eventName) {
		return Core.Event.events[eventName];
	},

	register: function(eventType, type) {
		Core.Event.types[eventType] = type;
	},

	getType: function(eventType) {
		return Core.Event.types[eventType];
	},

	dispatchEvent: function(event) {
		if(Core.Event.hasInstance(event))
			return event.dispatchEvent(this);
		else if(this.__dispatchEvent != undefined)
			return this.__dispatchEvent(event);
	},

	addEventListener: function(eventName, callback, capture) {
		if(Core.Event.getType(eventName) != undefined) {
			if(capture) {
				if(this.__extendCaptureEvents == undefined)
					this.__extendCaptureEvents = {};
				if(this.__extendCaptureEvents[eventName] == undefined)
					this.__extendCaptureEvents[eventName] = [];
				this.__extendCaptureEvents[eventName].push(callback);
			}
			else {
				if(this.__extendEvents == undefined)
					this.__extendEvents = {};
				if(this.__extendEvents[eventName] == undefined)
					this.__extendEvents[eventName] = [];
				this.__extendEvents[eventName].push(callback);
			}
		}
		else if(this.__addEventListener != undefined)
			return this.__addEventListener(eventName, callback, capture);
		/**#nocode+ Avoid Jsdoc warnings...*/
		else if(this.attachEvent != undefined) {
			var wrapper = function() {
				// correct IE < 9 event diff
				if(arguments.length == 1) {
					var newEvent = {};
					for(var key in arguments[0])
						newEvent[key] = arguments[0][key];
					if(('keyCode' in arguments[0]) && !('which' in arguments[0]))
						newEvent.which = arguments[0].keyCode;
					newEvent.preventDefault = function() {
						this.defaultPrevented = true;
						this.returnValue = false;
					};
					newEvent.stopPropagation = function() {
						this.cancelBubble = true;
					};
					newEvent.target = newEvent.srcElement;
					var res = arguments.callee.callback.call(arguments.callee.scope, newEvent);
					arguments[0].returnValue = newEvent.returnValue;
					arguments[0].cancelBubble = newEvent.cancelBubble;
					return res;
				}
				else
					return arguments.callee.callback.apply(arguments.callee.scope, arguments);
			}
			wrapper.scope = this;
			wrapper.callback = callback;
			wrapper.eventName = eventName;
			wrapper.capture = capture;
			this.attachEvent('on'+eventName, wrapper);
			if(this.__ieevents == undefined)
				this.__ieevents = [];
			this.__ieevents.push(wrapper);
		}
		/**#nocode-*/
	},

	removeEventListener: function(eventName, callback, capture) {
		if(Core.Event.getType(eventName) != undefined) {
			if(capture) {
				if(this.__extendCaptureEvents != undefined) {
					for(var i = 0; i < this.__extendCaptureEvents[eventName].length; i++) {
						if(this.__extendCaptureEvents[eventName][i] == callback) {
							this.__extendCaptureEvents[eventName].splice(i, 1);
							break;
						}
					}
				}
			}
			else {
				if(this.__extendEvents != undefined) {
					for(var i = 0; i < this.__extendEvents[eventName].length; i++) {
						if(this.__extendEvents[eventName][i] == callback) {
							this.__extendEvents[eventName].splice(i, 1);
							break;
						}
					}
				}
			}
		}
		else if(this.__removeEventListener != undefined)
			return this.__removeEventListener(eventName, callback, capture);
		else if(this.detachEvent != undefined) {
			for(var i = 0; i < this.__ieevents.length; i++) {
				var wrapper = this.__ieevents[i];
				if((wrapper.scope == this) && (wrapper.eventName == eventName)) {
					if((callback != undefined) && (wrapper.callback != callback))
						continue;
					this.detachEvent(wrapper.eventName, wrapper);
					this.__ieevents.splice(i, 1);
					i--;
				}
			}
		}
	},

	createEvent: function(eventName) {
		var type = Core.Event.getEvent(eventName);
		if(type != undefined)
			return new type();
		else if(document.__createEvent != undefined)
			return document.__createEvent.call(document, eventName);
	},

	createElement: function(elementName) {
		var element = document.__createElement.call(document, elementName);
		if(element.dispatchEvent != Core.Event.dispatchEvent) {
			element.__dispatchEvent = element.dispatchEvent;
			element.dispatchEvent = Core.Event.dispatchEvent;
		}
		if(element.addEventListener != Core.Event.addEventListener) {
			element.__addEventListener = element.addEventListener;
			element.addEventListener = Core.Event.addEventListener;
		}
		if(element.removeEventListener != Core.Event.removeEventListener) {
			element.__removeEventListener = element.removeEventListener;
			element.removeEventListener = Core.Event.removeEventListener;
		}
		return element;
	},

	cleanTarget: function(target) {
		var current = target;
		while(current != undefined) {
			if(('dispatchEvent' in current) && ('innerHTML' in current))
				return current;
			if('offsetParent' in current)
				current = current.offsetParent;
			else
				current = current.parentNode;
		}
		return window;
	}
});

if('HTMLElement' in window) {
	HTMLElement.prototype.__dispatchEvent = HTMLElement.prototype.dispatchEvent;
	HTMLElement.prototype.dispatchEvent = Core.Event.dispatchEvent;
	HTMLElement.prototype.__addEventListener = HTMLElement.prototype.addEventListener;
	HTMLElement.prototype.addEventListener = Core.Event.addEventListener;
	HTMLElement.prototype.__removeEventListener = HTMLElement.prototype.removeEventListener;
	HTMLElement.prototype.removeEventListener = Core.Event.removeEventListener;
}
if('SVGElement' in window) {
	SVGElement.prototype.__dispatchEvent = SVGElement.prototype.dispatchEvent;
	SVGElement.prototype.dispatchEvent = Core.Event.dispatchEvent;
	SVGElement.prototype.__addEventListener = SVGElement.prototype.addEventListener;
	SVGElement.prototype.addEventListener = Core.Event.addEventListener;
	SVGElement.prototype.__removeEventListener = SVGElement.prototype.removeEventListener;
	SVGElement.prototype.removeEventListener = Core.Event.removeEventListener;
}

window.__dispatchEvent = window.dispatchEvent;
window.dispatchEvent = Core.Event.dispatchEvent;
window.__addEventListener = window.addEventListener;
window.addEventListener = Core.Event.addEventListener;
window.__removeEventListener = window.removeEventListener;
window.removeEventListener = Core.Event.removeEventListener;

if('HTMLDocument' in window) {
	HTMLDocument.prototype.__createEvent = HTMLDocument.prototype.createEvent;
	HTMLDocument.prototype.createEvent = Core.Event.createEvent;
	HTMLDocument.prototype.__createElement = HTMLDocument.prototype.createElement;
	HTMLDocument.prototype.createElement = Core.Event.createElement;
}
else {
	document.__createEvent = document.createEvent;
	document.createEvent = Core.Event.createEvent;
	document.__createElement = document.createElement;
	document.createElement = Core.Event.createElement;
}
<commit_msg>improve cleanTarget for Firefox mobile support<commit_after>Core.Object.extend('Core.Event',
/** @lends Core.Event#*/
{
	defaultPrevented: false,
	type: undefined,
	bubbles: true,
	cancelable: true,
	view: undefined,
	target: undefined,
	cancelBubble: false,

	/**
    *   @constructs
	*	@class
    *   @extends Core.Object
	*/
	constructor: function(config) {
		if(config.type != undefined)
			this.type = config.type;
	},

	preventDefault: function() {
		this.defaultPrevented = true;
	},

	stopPropagation: function() {
		this.cancelBubble = true;
	},

	stopImmediatePropagation: function() {
		// TODO
	},

	dispatchEvent: function(target) {
		this.target = target;

		if(this.bubbles) {
			var stack = [];

			var current = this.target;
			while(current != undefined) {
				if('dispatchEvent' in current)
					stack.push(current);
//				if('offsetParent' in current)
//					current = current.offsetParent;
//				else
					current = current.parentNode;
			}
			stack.push(window);

			// mode capture
			for(var i = stack.length - 1; (i >= 0) && (!this.cancelBubble); i--) {
				current = stack[i];
				if((current.__extendCaptureEvents != undefined) && (current.__extendCaptureEvents[this.type] != undefined)) {
					var list = [];
					for(var i2 = 0; i2 < current.__extendCaptureEvents[this.type].length; i2++)
						list.push(current.__extendCaptureEvents[this.type][i2]);
					for(var i2 = 0; i2 < list.length; i2++)
						list[i2].call(current, this);
				}
			}

			// bubble mode
			for(var i = 0; (i < stack.length) && (!this.cancelBubble); i++) {
				current = stack[i];
				if((current.__extendEvents != undefined) && (current.__extendEvents[this.type] != undefined)) {
					var list = [];
					for(var i2 = 0; i2 < current.__extendEvents[this.type].length; i2++)
						list.push(current.__extendEvents[this.type][i2]);
					for(var i2 = 0; i2 < list.length; i2++)
						list[i2].call(current, this);
				}
			}
		}
		else {
			if((target.__extendCaptureEvents != undefined) && (target.__extendCaptureEvents[this.type] != undefined)) {
				var list = [];
				for(var i2 = 0; i2 < target.__extendCaptureEvents[this.type].length; i2++)
					list.push(target.__extendCaptureEvents[this.type][i2]);
				for(var i = 0; i < list.length; i++)
					list[i].call(target, this);
			}
			if((target.__extendEvents != undefined) && (target.__extendEvents[this.type] != undefined)) {
				var list = [];
				for(var i2 = 0; i2 < target.__extendEvents[this.type].length; i2++)
					list.push(target.__extendEvents[this.type][i2]);
				for(var i = 0; i < list.length; i++)
					list[i].call(target, this);
			}
		}
		return this.defaultPrevented;
	}
},
{},
/** @lends Core.Event*/
{
	events: {},

	types: {},

	registerEvent: function(eventName, type) {
		Core.Event.events[eventName] = type;
	},

	getEvent: function(eventName) {
		return Core.Event.events[eventName];
	},

	register: function(eventType, type) {
		Core.Event.types[eventType] = type;
	},

	getType: function(eventType) {
		return Core.Event.types[eventType];
	},

	dispatchEvent: function(event) {
		if(Core.Event.hasInstance(event))
			return event.dispatchEvent(this);
		else if(this.__dispatchEvent != undefined)
			return this.__dispatchEvent(event);
	},

	addEventListener: function(eventName, callback, capture) {
		if(Core.Event.getType(eventName) != undefined) {
			if(capture) {
				if(this.__extendCaptureEvents == undefined)
					this.__extendCaptureEvents = {};
				if(this.__extendCaptureEvents[eventName] == undefined)
					this.__extendCaptureEvents[eventName] = [];
				this.__extendCaptureEvents[eventName].push(callback);
			}
			else {
				if(this.__extendEvents == undefined)
					this.__extendEvents = {};
				if(this.__extendEvents[eventName] == undefined)
					this.__extendEvents[eventName] = [];
				this.__extendEvents[eventName].push(callback);
			}
		}
		else if(this.__addEventListener != undefined)
			return this.__addEventListener(eventName, callback, capture);
		/**#nocode+ Avoid Jsdoc warnings...*/
		else if(this.attachEvent != undefined) {
			var wrapper = function() {
				// correct IE < 9 event diff
				if(arguments.length == 1) {
					var newEvent = {};
					for(var key in arguments[0])
						newEvent[key] = arguments[0][key];
					if(('keyCode' in arguments[0]) && !('which' in arguments[0]))
						newEvent.which = arguments[0].keyCode;
					newEvent.preventDefault = function() {
						this.defaultPrevented = true;
						this.returnValue = false;
					};
					newEvent.stopPropagation = function() {
						this.cancelBubble = true;
					};
					newEvent.target = newEvent.srcElement;
					var res = arguments.callee.callback.call(arguments.callee.scope, newEvent);
					arguments[0].returnValue = newEvent.returnValue;
					arguments[0].cancelBubble = newEvent.cancelBubble;
					return res;
				}
				else
					return arguments.callee.callback.apply(arguments.callee.scope, arguments);
			}
			wrapper.scope = this;
			wrapper.callback = callback;
			wrapper.eventName = eventName;
			wrapper.capture = capture;
			this.attachEvent('on'+eventName, wrapper);
			if(this.__ieevents == undefined)
				this.__ieevents = [];
			this.__ieevents.push(wrapper);
		}
		/**#nocode-*/
	},

	removeEventListener: function(eventName, callback, capture) {
		if(Core.Event.getType(eventName) != undefined) {
			if(capture) {
				if(this.__extendCaptureEvents != undefined) {
					for(var i = 0; i < this.__extendCaptureEvents[eventName].length; i++) {
						if(this.__extendCaptureEvents[eventName][i] == callback) {
							this.__extendCaptureEvents[eventName].splice(i, 1);
							break;
						}
					}
				}
			}
			else {
				if(this.__extendEvents != undefined) {
					for(var i = 0; i < this.__extendEvents[eventName].length; i++) {
						if(this.__extendEvents[eventName][i] == callback) {
							this.__extendEvents[eventName].splice(i, 1);
							break;
						}
					}
				}
			}
		}
		else if(this.__removeEventListener != undefined)
			return this.__removeEventListener(eventName, callback, capture);
		else if(this.detachEvent != undefined) {
			for(var i = 0; i < this.__ieevents.length; i++) {
				var wrapper = this.__ieevents[i];
				if((wrapper.scope == this) && (wrapper.eventName == eventName)) {
					if((callback != undefined) && (wrapper.callback != callback))
						continue;
					this.detachEvent(wrapper.eventName, wrapper);
					this.__ieevents.splice(i, 1);
					i--;
				}
			}
		}
	},

	createEvent: function(eventName) {
		var type = Core.Event.getEvent(eventName);
		if(type != undefined)
			return new type();
		else if(document.__createEvent != undefined)
			return document.__createEvent.call(document, eventName);
	},

	createElement: function(elementName) {
		var element = document.__createElement.call(document, elementName);
		if(element.dispatchEvent != Core.Event.dispatchEvent) {
			element.__dispatchEvent = element.dispatchEvent;
			element.dispatchEvent = Core.Event.dispatchEvent;
		}
		if(element.addEventListener != Core.Event.addEventListener) {
			element.__addEventListener = element.addEventListener;
			element.addEventListener = Core.Event.addEventListener;
		}
		if(element.removeEventListener != Core.Event.removeEventListener) {
			element.__removeEventListener = element.removeEventListener;
			element.removeEventListener = Core.Event.removeEventListener;
		}
		return element;
	},

	cleanTarget: function(target) {
		var current = target;
		while(current != undefined) {
			if(('dispatchEvent' in current) && ('innerHTML' in current) &&
			   (current.dispatchEvent === Core.Event.dispatchEvent))
				return current;
			if('offsetParent' in current)
				current = current.offsetParent;
			else
				current = current.parentNode;
		}
		return window;
	}
});

if('HTMLElement' in window) {
	HTMLElement.prototype.__dispatchEvent = HTMLElement.prototype.dispatchEvent;
	HTMLElement.prototype.dispatchEvent = Core.Event.dispatchEvent;
	HTMLElement.prototype.__addEventListener = HTMLElement.prototype.addEventListener;
	HTMLElement.prototype.addEventListener = Core.Event.addEventListener;
	HTMLElement.prototype.__removeEventListener = HTMLElement.prototype.removeEventListener;
	HTMLElement.prototype.removeEventListener = Core.Event.removeEventListener;
}
if('SVGElement' in window) {
	SVGElement.prototype.__dispatchEvent = SVGElement.prototype.dispatchEvent;
	SVGElement.prototype.dispatchEvent = Core.Event.dispatchEvent;
	SVGElement.prototype.__addEventListener = SVGElement.prototype.addEventListener;
	SVGElement.prototype.addEventListener = Core.Event.addEventListener;
	SVGElement.prototype.__removeEventListener = SVGElement.prototype.removeEventListener;
	SVGElement.prototype.removeEventListener = Core.Event.removeEventListener;
}

window.__dispatchEvent = window.dispatchEvent;
window.dispatchEvent = Core.Event.dispatchEvent;
window.__addEventListener = window.addEventListener;
window.addEventListener = Core.Event.addEventListener;
window.__removeEventListener = window.removeEventListener;
window.removeEventListener = Core.Event.removeEventListener;

if('HTMLDocument' in window) {
	HTMLDocument.prototype.__createEvent = HTMLDocument.prototype.createEvent;
	HTMLDocument.prototype.createEvent = Core.Event.createEvent;
	HTMLDocument.prototype.__createElement = HTMLDocument.prototype.createElement;
	HTMLDocument.prototype.createElement = Core.Event.createElement;
}
else {
	document.__createEvent = document.createEvent;
	document.createEvent = Core.Event.createEvent;
	document.__createElement = document.createElement;
	document.createElement = Core.Event.createElement;
}
<|endoftext|>


<commit_before>'use strict';

describe('Controller: AdminAccountCtrl', function () {

  // load the controller's module
  beforeEach(module('fahrtenbuchApp'));
  var AdminAccountCtrl, $scope, $location, $httpBackend, fakeResponse;

  // Initialize the controller and a mock scope
  beforeEach(inject(function ($controller, $rootScope, _$location_, _$httpBackend_) {

    $scope = $rootScope.$new();
    $location = _$location_;
    $httpBackend = _$httpBackend_;
    fakeResponse = '';

    AdminAccountCtrl = $controller('AdminAccountCtrl', {
      $scope: $scope
    });

    $location.path('/admin/account');
  }));

	it('Ensure that the method was invoked', function () {
  	$httpBackend.expectGET('/api/accounts').respond([{test: "test"}]);
  	$httpBackend.when('GET', 'app/admin/admin.account.html').respond(fakeResponse);
    $httpBackend.flush();

	  expect($location.path()).toBe('/admin/account');
	  expect($scope.accounts).toBeDefined();
	  expect($scope.accounts).toEqual([{test: "test"}]);
	});

	// it('Ensure that the method was invoked', function () {
 //  	// $httpBackend.expectGET('/api/accounts').respond([{test: "test"}]);
 //  	// $httpBackend.when('GET', 'app/admin/admin.account.html').respond(fakeResponse);
 //   //  $httpBackend.flush();

	//   expect($location.path()).toBe('/admin/account');

 //   	// var valid = scope.addAccount();
 //    // expect(valid).toBeFalsy();

 //  	$httpBackend.expectGET('/api/accounts').respond([{_id: 1}, {_id: 2}, {_id: 3}]);
 //  	$httpBackend.when('GET', 'app/admin/admin.account.html').respond(fakeResponse);
 //  	$httpBackend.when('DELETE', '/api/accounts/1').respond(fakeResponse);
 //    $httpBackend.flush();

 //    console.log($scope.accounts);

 //    $scope.deleteAccount($scope.accounts[0]);

 //    $scope.$watch();
 //    $scope.$apply();
 //    console.log($scope.accounts);

    // var account = scope.accounts[0];
    // console.log(account);

    // angular.forEach(scope.accounts, function(a, i) {
    // 	console.log(a);
    //   if (a === account) {
    //   	console.log("hi");
    //     scope.accounts.splice(i, 1);
    //   }
    // });
    // console.log(scope.accounts);

//     expect($scope.accounts).toEqual([{_id: 2}, {_id: 3}]);
// 	});
});

// describe('Controller: AdminUserCtrl', function () {

//   // load the controller's module
//   beforeEach(module('fahrtenbuchApp'));
//   var AdminUserCtrl, scope, $httpBackend, testTrip;

//   // Initialize the controller and a mock scope
//   beforeEach(inject(function (_$httpBackend_, $controller, $rootScope, $routeParams) {

//     $httpBackend = _$httpBackend_;
//     scope = $rootScope.$new();
//     AdminUserCtrl = $controller('AdminUserCtrl', {
//       $scope: scope,
//       $routeParams: {id: 12345678}
//     });
//     testTrip = {_id: 12345678, stays: [{destination: "test"}]};
//   }));

//   it('should get trips', inject(function() {

//     $httpBackend.expectGET('/api/trips/' + testTrip._id).respond(testTrip);
//     $httpBackend.flush();

//     expect(scope.trip).toEqual({_id: 12345678, stays: [{destination: "test"}]});
//     expect(scope.destinations).toEqual("test");
//   }));
// });

// @todo AdminCarCtrl
// @todo AdminCarAddCtrl

<commit_msg>testing delete account admin<commit_after>'use strict';

describe('Controller: AdminAccountCtrl', function () {

  // load the controller's module
  beforeEach(module('fahrtenbuchApp'));
 	// beforeEach(function() {
	//   module('fahrtenbuchApp');
	//   module('templates');
	// });
  var AdminAccountCtrl, $scope, $location, $httpBackend, fakeResponse;

  // Initialize the controller and a mock scope
  beforeEach(inject(function ($controller, $rootScope, _$location_, _$httpBackend_) {

    $scope = $rootScope.$new();
    $location = _$location_;
    $httpBackend = _$httpBackend_;
    fakeResponse = '';

    AdminAccountCtrl = $controller('AdminAccountCtrl', {
      $scope: $scope
    });

    $location.path('/admin/account');
  }));

	it('call AdminAccountCtrl', function () {
  	$httpBackend.expectGET('/api/accounts').respond([{test: "test"}]);
  	$httpBackend.when('GET', 'app/admin/admin.account.html').respond(fakeResponse);
    $httpBackend.flush();

	  expect($location.path()).toBe('/admin/account');
	  expect($scope.accounts).toBeDefined();
	  expect($scope.accounts).toEqual([{test: "test"}]);
	});

	it('test add function', function () {
	  expect($location.path()).toBe('/admin/account');

	  var account = {name: "test"};
  	$httpBackend.expectGET('/api/accounts').respond([{_id: 1}, {_id: 2}, {_id: 3}]);
  	$httpBackend.when('GET', 'app/admin/admin.account.html').respond(fakeResponse);
  	// $httpBackend.when('POST', '/api/accounts').respond(fakeResponse);
    $httpBackend.flush();

		$scope.$digest();
    expect($scope.form.$valid).toBeTruthy();
    $scope.addAccount($scope.form, account);

    // expect($scope.accounts).toEqual([{_id: 2}, {_id: 3}]);
	});

	it('test delete function', function () {
	  expect($location.path()).toBe('/admin/account');

  	$httpBackend.expectGET('/api/accounts').respond([{_id: 1}, {_id: 2}, {_id: 3}]);
  	$httpBackend.when('GET', 'app/admin/admin.account.html').respond(fakeResponse);
  	$httpBackend.when('DELETE', '/api/accounts/1').respond(fakeResponse);
    $httpBackend.flush();

    $scope.deleteAccount($scope.accounts[0]);
    $httpBackend.flush();

    expect($scope.accounts).toEqual([{_id: 2}, {_id: 3}]);
	});
});

// describe('Controller: AdminUserCtrl', function () {

//   // load the controller's module
//   beforeEach(module('fahrtenbuchApp'));
//   var AdminUserCtrl, scope, $httpBackend, testTrip;

//   // Initialize the controller and a mock scope
//   beforeEach(inject(function (_$httpBackend_, $controller, $rootScope, $routeParams) {

//     $httpBackend = _$httpBackend_;
//     scope = $rootScope.$new();
//     AdminUserCtrl = $controller('AdminUserCtrl', {
//       $scope: scope,
//       $routeParams: {id: 12345678}
//     });
//     testTrip = {_id: 12345678, stays: [{destination: "test"}]};
//   }));

//   it('should get trips', inject(function() {

//     $httpBackend.expectGET('/api/trips/' + testTrip._id).respond(testTrip);
//     $httpBackend.flush();

//     expect(scope.trip).toEqual({_id: 12345678, stays: [{destination: "test"}]});
//     expect(scope.destinations).toEqual("test");
//   }));
// });

// @todo AdminCarCtrl
// @todo AdminCarAddCtrl

<|endoftext|>


<commit_before>var tower_Operate = {
    run: function(tower, thisRoom, towerNumber) {
        var thisTower = Game.getObjectById(tower);
        var towerRange = 70;

        var UnderAttackPos = Memory.roomsUnderAttack.indexOf(thisRoom.name);
        var salvagerPrepPos = Memory.roomsPrepSalvager.indexOf(thisRoom.name);
        if (salvagerPrepPos >= 0) {
            towerRange = 70;
        }
        if (UnderAttackPos >= 0 && thisTower.energy > 0) {
            var closestHostile = thisTower.pos.findInRange(FIND_HOSTILE_CREEPS, towerRange, {
                filter: (creep) => (creep.getActiveBodyparts(WORK) > 0 || creep.getActiveBodyparts(CARRY) > 0 || creep.getActiveBodyparts(ATTACK) > 0 || creep.getActiveBodyparts(RANGED_ATTACK) > 0 || creep.getActiveBodyparts(HEAL) > 0) || (creep.hits <= 500)
            });
            if (closestHostile.length > 0) {
                //Target healing creeps first
                if (towerNumber == 1) {
                    closestHostile.sort(targetHealer);
                } else {
                    closestHostile.sort(targetOther);
                }
                thisTower.attack(closestHostile[0]);
            }
        } else if (thisTower.energy > (thisTower.energyCapacity * 0.5)) {
            //Save 50% of the tower's energy to use on repelling attackers
            var closestDamagedCreep = thisTower.pos.findClosestByRange(FIND_MY_CREEPS, {
                filter: (creep) => creep.hits < creep.hitsMax - 150
            });
            if (closestDamagedCreep) {
                thisTower.heal(closestDamagedCreep);
            }
        }
        //Enable to see tower coverage
        //thisRoom.visual.rect(thisTower.pos.x - 15, thisTower.pos.y - 15, 30, 30, {fill: '#ff0019', opacity: 0.2});
    }
};

module.exports = tower_Operate;

function targetHealer(a, b) {
    if (a.getActiveBodyparts(HEAL) < b.getActiveBodyparts(HEAL))
        return 1;
    if (a.getActiveBodyparts(HEAL) > b.getActiveBodyparts(HEAL))
        return -1;
    return 0;
}

function targetOther(a, b) {
    if (a.getActiveBodyparts(HEAL) < b.getActiveBodyparts(HEAL))
        return -1;
    if (a.getActiveBodyparts(HEAL) > b.getActiveBodyparts(HEAL))
        return 1;
    return 0;
}<commit_msg>SHOOT EVERYTHING. SHOOT!<commit_after>var tower_Operate = {
    run: function(tower, thisRoom, towerNumber) {
        var thisTower = Game.getObjectById(tower);
        var towerRange = 70;

        var UnderAttackPos = Memory.roomsUnderAttack.indexOf(thisRoom.name);
        var salvagerPrepPos = Memory.roomsPrepSalvager.indexOf(thisRoom.name);
        if (salvagerPrepPos >= 0) {
            towerRange = 70;
        }
        if (UnderAttackPos >= 0 && thisTower.energy > 0) {
            var closestHostile = thisTower.pos.findInRange(FIND_HOSTILE_CREEPS, towerRange);
            if (closestHostile.length > 0) {
                //Target healing creeps first
                if (towerNumber == 1) {
                    closestHostile.sort(targetHealer);
                } else {
                    closestHostile.sort(targetOther);
                }
                thisTower.attack(closestHostile[0]);
            }
        } else if (thisTower.energy > (thisTower.energyCapacity * 0.5)) {
            //Save 50% of the tower's energy to use on repelling attackers
            var closestDamagedCreep = thisTower.pos.findClosestByRange(FIND_MY_CREEPS, {
                filter: (creep) => creep.hits < creep.hitsMax - 150
            });
            if (closestDamagedCreep) {
                thisTower.heal(closestDamagedCreep);
            }
        }
        //Enable to see tower coverage
        //thisRoom.visual.rect(thisTower.pos.x - 15, thisTower.pos.y - 15, 30, 30, {fill: '#ff0019', opacity: 0.2});
    }
};

module.exports = tower_Operate;

function targetHealer(a, b) {
    if (a.getActiveBodyparts(HEAL) < b.getActiveBodyparts(HEAL))
        return 1;
    if (a.getActiveBodyparts(HEAL) > b.getActiveBodyparts(HEAL))
        return -1;
    return 0;
}

function targetOther(a, b) {
    if (a.getActiveBodyparts(HEAL) < b.getActiveBodyparts(HEAL))
        return -1;
    if (a.getActiveBodyparts(HEAL) > b.getActiveBodyparts(HEAL))
        return 1;
    return 0;
}<|endoftext|>


<commit_before>import { h, Component } 				from 'preact/preact';
import Shallow			 				from 'shallow/shallow';

import ButtonBase						from '../button-base/base';
import ButtonLink						from '../button-link/link';
import SVGIcon 							from 'com/svg-icon/icon';
import NavLink 							from 'com/nav-link/link';

import NavSpinner						from 'com/nav-spinner/spinner';

//import DropdownUser 					from 'com/dropdown-user/user';
import DropdownNotification				from 'com/dropdown-notification/notification';

//import $Node							from '../../shrub/js/node/node';
import $Notification					from '../../shrub/js/notification/notification';

function make_url( url ) {
	return url + window.location.search;
}

export default class ViewBar extends Component {
	constructor( props ) {
		super(props);

		this.state - {
			notifications: 0,
			notificationCountAdjustment: 0,
		};
	}

	checkNotificationCount() {
		const loggedIn = this.props.user && this.props.user.id > -1;

		if (loggedIn) {
			$Notification.GetCountUnread()
			.then((r) => {
				if (this.state.notifications != r.count) {
					this.setState({notifications: r.count, notificationCountAdjustment: 0});
				}
				setTimeout(() => this.checkNotificationCount(), 60000);
			})
			.catch((e) => {
				setTimeout(() => this.checkNotificationCount(), 5 * 60000);
				console.log('[Notificaton error]', e);
			});
		} else {
			setTimeout(() => this.checkNotificationCount(), 5 *  60000);
		}
	}

	componentDidMount() {
		document.body.classList.add('_use-view-bar');

		this.checkNotificationCount();
	}

	componentWillUnmount() {
		document.body.classList.remove('_use-view-bar');
		document.body.classList.remove('_static-view-bar');
	}

//	componentWillReceiveProps( nextProps ) {
//		if ( Shallow.Diff(nextProps, this.props) ) {
////			console.log('featured',nextProps.featured);
//		}
//	}

	shouldComponentUpdate( nextProps, nextState ) {
		var ret = Shallow.Compare(this, nextProps, nextState);
		//console.log(ret, nextProps.featured);
		return ret;
	}

	renderLeft() {
	}

	renderRight( user, featured ) {
		var Search = null;
//		var Search = (
//			<ButtonBase class="-icon" onclick={e => { console.log('search'); window.location.hash = "#search"; }}>
//				<SVGIcon baseline>search</SVGIcon>
//			</ButtonBase>
//		);

		var ShowCalendar = (
			<ButtonBase class="-button if-no-sidebar-block" onclick={e => { console.log('calendar'); window.location.hash = "#cal"; }}>
				<SVGIcon baseline>calendar</SVGIcon>

Schedule

			</ButtonBase>
		);

		var ShowJoin = null;
		var ShowMyGame = null;
		var NewPost = null;
		let Notification = null;
		var ShowUser = null;
		var Register = null;
		var Login = null;
		var GoSecure = null;
		var ShowSpinner = null;
		let ShowNotifications = null;

		// Disallow insecure login
		if ( SECURE_LOGIN_ONLY && (location.protocol !== 'https:') ) {
			let SecureURL = 'https://'+location.hostname+location.pathname+location.search+location.hash;
			GoSecure = (
				<ButtonBase class="-button" href={SecureURL} onclick={e => {console.log('secure'); location.href = SecureURL;}}>
					<SVGIcon>unlocked</SVGIcon>

Go to Secure Site

				</ButtonBase>
			);
		}
		// Both user and user.id means logged in
		else if ( user && user.id ) {
			if ( featured && featured.id ) {
				if ( featured.focus ) {
					ShowMyGame = (
						<ButtonLink href={featured.what_node[featured.focus].path} class="-button">
							<SVGIcon>gamepad</SVGIcon>

My Game

						</ButtonLink>
					);

					NewPost = (
						<ButtonBase class="-button" onclick={e => { window.location.hash = "#create/"+featured.focus+"/post"; }}>
							<SVGIcon>edit</SVGIcon>

New

						</ButtonBase>
					);
				}
				else if ( node_CanCreate(featured) ) {
					ShowJoin = (
						<ButtonBase class="-button" onclick={e => { window.location.hash = "#create/"+featured.id+"/item/game"; }}>
							<SVGIcon>publish</SVGIcon>

Join Event

						</ButtonBase>
					);
				}
			}

			// Notifications
			let NotificationCount = null;
			const notificationCount = Math.max(0, this.state.notifications - this.state.notificationCountAdjustment);
			if (notificationCount > 0) {
				NotificationCount = (
{notificationCount}
);
			}

			if (this.state.showNotifications) {
				ShowNotifications = (<DropdownNotification getNew={notificationCount > 0} totalNew={notificationCount} countCallback={ (offset) => this.setState({notificationCountAdjustment: offset}) } hideCallback={ () => this.setState({showNotifications: false}) } />);
			}

			Notification = (
				<ButtonBase class="-icon" onclick={(e) => {
					// TODO: if the main content is the notifications feed, clicking the button should
					// probably not show the dropdown, but load new comments into the feed.
					this.setState({showNotifications: !this.state.showNotifications});
				}}>
					<SVGIcon baseline>bubble</SVGIcon>
					{NotificationCount}
				</ButtonBase>
			);

			// TODO: Pull this out of the user meta, else use a dummy
			let Avatar = (user.meta && user.meta.avatar) ?  : ;
			//'/other/logo/mike/Chicken64.png';
			let MyURL = '/users/'+user.slug+'/';
			ShowUser = [
				<ButtonBase class="-user">
					<NavLink href={MyURL}>{Avatar}</NavLink>
				</ButtonBase>,
//				<DropdownUser />
			];
		}
		// If user has finished loading (and is not logged in)
		else if ( user ) {
			Register = (
				<ButtonBase class="-button" onclick={e => { console.log('register'); window.location.hash = "#user-register"; }}>
					<SVGIcon>user-plus</SVGIcon>

Create Account

				</ButtonBase>
			);
			Login = (
				<ButtonBase class="-button" onclick={e => { console.log('login'); window.location.hash = "#user-login"; }}>
					<SVGIcon>key</SVGIcon>

Login

				</ButtonBase>
			);
		}
		// Still waiting
		else {
			ShowSpinner = <NavSpinner />;
		}

		if ( ShowSpinner ) {
			return (

					{ShowSpinner}

			);
		}
		else {
			return (

					{ShowJoin}
					{ShowMyGame}
					{NewPost}
					{ShowCalendar}
					{Search}
					{Notification}
					{ShowNotifications}
					{ShowUser}
					{Register}
					{Login}
					{GoSecure}

			);
		}
	}

	render( {user, featured}, {} ) {
		return (

						<ButtonLink href="/" class="-logo">
							<SVGIcon class="if-sidebar-block" baseline>ludum</SVGIcon><SVGIcon class="if-sidebar-block" baseline>dare</SVGIcon>
							<SVGIcon class="if-no-sidebar-block" baseline>l-udum</SVGIcon><SVGIcon class="if-no-sidebar-block" baseline>d-are</SVGIcon>
						</ButtonLink>

					{this.renderRight(user, featured)}

		);
	}
}
<commit_msg>Importing from new location<commit_after>import { h, Component } 				from 'preact/preact';
import Shallow			 				from 'shallow/shallow';

import ButtonBase						from '../button-base/base';
import ButtonLink						from '../button-link/link';
import SVGIcon 							from 'com/svg-icon/icon';
import NavLink 							from 'com/nav-link/link';

import NavSpinner						from 'com/nav-spinner/spinner';

//import DropdownUser 					from 'com/dropdown-user/user';
import DropdownNotification				from 'com/view-bar/bar-notifications';

//import $Node							from '../../shrub/js/node/node';
import $Notification					from '../../shrub/js/notification/notification';

function make_url( url ) {
	return url + window.location.search;
}

export default class ViewBar extends Component {
	constructor( props ) {
		super(props);

		this.state - {
			notifications: 0,
			notificationCountAdjustment: 0,
		};
	}

	checkNotificationCount() {
		const loggedIn = this.props.user && this.props.user.id > -1;

		if (loggedIn) {
			$Notification.GetCountUnread()
			.then((r) => {
				if (this.state.notifications != r.count) {
					this.setState({notifications: r.count, notificationCountAdjustment: 0});
				}
				setTimeout(() => this.checkNotificationCount(), 60000);
			})
			.catch((e) => {
				setTimeout(() => this.checkNotificationCount(), 5 * 60000);
				console.log('[Notificaton error]', e);
			});
		} else {
			setTimeout(() => this.checkNotificationCount(), 5 *  60000);
		}
	}

	componentDidMount() {
		document.body.classList.add('_use-view-bar');

		this.checkNotificationCount();
	}

	componentWillUnmount() {
		document.body.classList.remove('_use-view-bar');
		document.body.classList.remove('_static-view-bar');
	}

//	componentWillReceiveProps( nextProps ) {
//		if ( Shallow.Diff(nextProps, this.props) ) {
////			console.log('featured',nextProps.featured);
//		}
//	}

	shouldComponentUpdate( nextProps, nextState ) {
		var ret = Shallow.Compare(this, nextProps, nextState);
		//console.log(ret, nextProps.featured);
		return ret;
	}

	renderLeft() {
	}

	renderRight( user, featured ) {
		var Search = null;
//		var Search = (
//			<ButtonBase class="-icon" onclick={e => { console.log('search'); window.location.hash = "#search"; }}>
//				<SVGIcon baseline>search</SVGIcon>
//			</ButtonBase>
//		);

		var ShowCalendar = (
			<ButtonBase class="-button if-no-sidebar-block" onclick={e => { console.log('calendar'); window.location.hash = "#cal"; }}>
				<SVGIcon baseline>calendar</SVGIcon>

Schedule

			</ButtonBase>
		);

		var ShowJoin = null;
		var ShowMyGame = null;
		var NewPost = null;
		let Notification = null;
		var ShowUser = null;
		var Register = null;
		var Login = null;
		var GoSecure = null;
		var ShowSpinner = null;
		let ShowNotifications = null;

		// Disallow insecure login
		if ( SECURE_LOGIN_ONLY && (location.protocol !== 'https:') ) {
			let SecureURL = 'https://'+location.hostname+location.pathname+location.search+location.hash;
			GoSecure = (
				<ButtonBase class="-button" href={SecureURL} onclick={e => {console.log('secure'); location.href = SecureURL;}}>
					<SVGIcon>unlocked</SVGIcon>

Go to Secure Site

				</ButtonBase>
			);
		}
		// Both user and user.id means logged in
		else if ( user && user.id ) {
			if ( featured && featured.id ) {
				if ( featured.focus ) {
					ShowMyGame = (
						<ButtonLink href={featured.what_node[featured.focus].path} class="-button">
							<SVGIcon>gamepad</SVGIcon>

My Game

						</ButtonLink>
					);

					NewPost = (
						<ButtonBase class="-button" onclick={e => { window.location.hash = "#create/"+featured.focus+"/post"; }}>
							<SVGIcon>edit</SVGIcon>

New

						</ButtonBase>
					);
				}
				else if ( node_CanCreate(featured) ) {
					ShowJoin = (
						<ButtonBase class="-button" onclick={e => { window.location.hash = "#create/"+featured.id+"/item/game"; }}>
							<SVGIcon>publish</SVGIcon>

Join Event

						</ButtonBase>
					);
				}
			}

			// Notifications
			let NotificationCount = null;
			const notificationCount = Math.max(0, this.state.notifications - this.state.notificationCountAdjustment);
			if (notificationCount > 0) {
				NotificationCount = (
{notificationCount}
);
			}

			if (this.state.showNotifications) {
				ShowNotifications = (<DropdownNotification getNew={notificationCount > 0} totalNew={notificationCount} countCallback={ (offset) => this.setState({notificationCountAdjustment: offset}) } hideCallback={ () => this.setState({showNotifications: false}) } />);
			}

			Notification = (
				<ButtonBase class="-icon" onclick={(e) => {
					// TODO: if the main content is the notifications feed, clicking the button should
					// probably not show the dropdown, but load new comments into the feed.
					this.setState({showNotifications: !this.state.showNotifications});
				}}>
					<SVGIcon baseline>bubble</SVGIcon>
					{NotificationCount}
				</ButtonBase>
			);

			// TODO: Pull this out of the user meta, else use a dummy
			let Avatar = (user.meta && user.meta.avatar) ?  : ;
			//'/other/logo/mike/Chicken64.png';
			let MyURL = '/users/'+user.slug+'/';
			ShowUser = [
				<ButtonBase class="-user">
					<NavLink href={MyURL}>{Avatar}</NavLink>
				</ButtonBase>,
//				<DropdownUser />
			];
		}
		// If user has finished loading (and is not logged in)
		else if ( user ) {
			Register = (
				<ButtonBase class="-button" onclick={e => { console.log('register'); window.location.hash = "#user-register"; }}>
					<SVGIcon>user-plus</SVGIcon>

Create Account

				</ButtonBase>
			);
			Login = (
				<ButtonBase class="-button" onclick={e => { console.log('login'); window.location.hash = "#user-login"; }}>
					<SVGIcon>key</SVGIcon>

Login

				</ButtonBase>
			);
		}
		// Still waiting
		else {
			ShowSpinner = <NavSpinner />;
		}

		if ( ShowSpinner ) {
			return (

					{ShowSpinner}

			);
		}
		else {
			return (

					{ShowJoin}
					{ShowMyGame}
					{NewPost}
					{ShowCalendar}
					{Search}
					{Notification}
					{ShowNotifications}
					{ShowUser}
					{Register}
					{Login}
					{GoSecure}

			);
		}
	}

	render( {user, featured}, {} ) {
		return (

						<ButtonLink href="/" class="-logo">
							<SVGIcon class="if-sidebar-block" baseline>ludum</SVGIcon><SVGIcon class="if-sidebar-block" baseline>dare</SVGIcon>
							<SVGIcon class="if-no-sidebar-block" baseline>l-udum</SVGIcon><SVGIcon class="if-no-sidebar-block" baseline>d-are</SVGIcon>
						</ButtonLink>

					{this.renderRight(user, featured)}

		);
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2012 VMware, Inc. All Rights Reserved.
//= require underscore
//= require jquery-ui
//= require array.remove

/**
 * serve autocomplete results from local + remote cache.
 *
 * options:
 *  remoteDelay - delay in millis before firing remote request (default=200)
 *  remoteSource - function to create xhr to perform ajax request
 *
 * usage:
 *  var cache = [];
 *  $('input').autocomplete({
 *    cache: cache,
 *    remoteDelay: 200,
 *    remoteSource: function(request, response) {
 *      return $.ajax({
 *        url: '/path/to/search',
 *        data: {q: request.term},
 *        success: function(data) {
 *          response(data.entries);
 *        }
 *      });
 *    }
 *   },
 *  });
 */
(function($) {
  var proto = $.ui.autocomplete.prototype;
  var origInitSource = proto._initSource;

  proto.options.cache = null;
  proto.options.remoteDelay = 200;
  proto.options.filter = $.ui.autocomplete.filter;

  $.extend(proto, {
    _initSource: function() {
      if (this.options.cache) {
        //move options to top level object to fix scoping
        this.remoteSource = this.options.remoteSource;
        this.cache = this.options.cache;
        this.requestIndex = 0;

        var self = this;
        this.element.on('autocompletesearch', function() {
          self.abort();
        });
        this.element.on('autocompleteclose', function() {
          self.abort();
        });

        this.source = this.localAndRemoteResponse;
      } else {
        origInitSource.call(this);
      }
    },
    currentXhr: null,
    // abort current xhr
    abort: function() {
      if (this.currentXhr) {
        this.currentXhr.abort();
        this.currentXhr = null;
      }
    },
    /**
     * allow for appending new results to existing autocomplete results
     */
    amendResponse: function(results, requestIndex) {
      var self = this;
      var newEntries = _.reject(results, function(entry) {
        return _.detect(self.cache, function(e) { return e.value === entry.value; });
      });
      this.cache = this.cache.concat(newEntries);
      if (requestIndex && requestIndex !== this.requestIndex) {
        return;
      }

      this.element.removeClass('ui-autocomplete-loading');
      if (this.menu.element.is(':visible')) {
        _.each(newEntries, function(item) {
          self._renderItem(self.menu.element, item);
        });
        this.menu.refresh();
        this._resizeMenu();
      } else {
        this.__response.apply( this, [newEntries] );
      }
    },
    /**
     * remove an element from the cache
     */
    removeFromCache: function(element) {
      this.cache.remove(element);
    },
    /**
     * show results from local cache immediately and fire off ajax request to load more results
     */
    localAndRemoteResponse: function(request, response) {
      if (this.remoteSourceDelay) {
        clearTimeout(this.remoteSourceDelay);
      }

      this.pending = 1;

      var localResults = this.options.filter.call(this, this.cache, request.term);
      response(localResults);

      var self = this;
      this.remoteSourceDelay = setTimeout(function() {
        var event = null;
        if (self._trigger("search", event) === false) {
          return;
        }

        var uniqueRequestId = ++self.requestIndex;
        self.element.addClass('ui-autocomplete-loading');
        self.currentXhr = self.remoteSource(request, function() { self.amendResponse.apply(self, arguments); });
        self.currentXhr.fail(function() {
          self.amendResponse([], uniqueRequestId);
        });
        self.currentXhr.complete(function() {
          self.pending = 0;
          self.element.removeClass('ui-autocomplete-loading');
        });
      }, this.options.remoteDelay);
    }
  });
})(jQuery);
<commit_msg>add legal disclaimer<commit_after>// Copyright (c) 2012 VMware, Inc. All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.

//= require underscore
//= require jquery-ui
//= require array.remove

/**
 * serve autocomplete results from local + remote cache.
 *
 * options:
 *  remoteDelay - delay in millis before firing remote request (default=200)
 *  remoteSource - function to create xhr to perform ajax request
 *
 * usage:
 *  var cache = [];
 *  $('input').autocomplete({
 *    cache: cache,
 *    remoteDelay: 200,
 *    remoteSource: function(request, response) {
 *      return $.ajax({
 *        url: '/path/to/search',
 *        data: {q: request.term},
 *        success: function(data) {
 *          response(data.entries);
 *        }
 *      });
 *    }
 *   },
 *  });
 */
(function($) {
  var proto = $.ui.autocomplete.prototype;
  var origInitSource = proto._initSource;

  proto.options.cache = null;
  proto.options.remoteDelay = 200;
  proto.options.filter = $.ui.autocomplete.filter;

  $.extend(proto, {
    _initSource: function() {
      if (this.options.cache) {
        //move options to top level object to fix scoping
        this.remoteSource = this.options.remoteSource;
        this.cache = this.options.cache;
        this.requestIndex = 0;

        var self = this;
        this.element.on('autocompletesearch', function() {
          self.abort();
        });
        this.element.on('autocompleteclose', function() {
          self.abort();
        });

        this.source = this.localAndRemoteResponse;
      } else {
        origInitSource.call(this);
      }
    },
    currentXhr: null,
    // abort current xhr
    abort: function() {
      if (this.currentXhr) {
        this.currentXhr.abort();
        this.currentXhr = null;
      }
    },
    /**
     * allow for appending new results to existing autocomplete results
     */
    amendResponse: function(results, requestIndex) {
      var self = this;
      var newEntries = _.reject(results, function(entry) {
        return _.detect(self.cache, function(e) { return e.value === entry.value; });
      });
      this.cache = this.cache.concat(newEntries);
      if (requestIndex && requestIndex !== this.requestIndex) {
        return;
      }

      this.element.removeClass('ui-autocomplete-loading');
      if (this.menu.element.is(':visible')) {
        _.each(newEntries, function(item) {
          self._renderItem(self.menu.element, item);
        });
        this.menu.refresh();
        this._resizeMenu();
      } else {
        this.__response.apply( this, [newEntries] );
      }
    },
    /**
     * remove an element from the cache
     */
    removeFromCache: function(element) {
      this.cache.remove(element);
    },
    /**
     * show results from local cache immediately and fire off ajax request to load more results
     */
    localAndRemoteResponse: function(request, response) {
      if (this.remoteSourceDelay) {
        clearTimeout(this.remoteSourceDelay);
      }

      this.pending = 1;

      var localResults = this.options.filter.call(this, this.cache, request.term);
      response(localResults);

      var self = this;
      this.remoteSourceDelay = setTimeout(function() {
        var event = null;
        if (self._trigger("search", event) === false) {
          return;
        }

        var uniqueRequestId = ++self.requestIndex;
        self.element.addClass('ui-autocomplete-loading');
        self.currentXhr = self.remoteSource(request, function() { self.amendResponse.apply(self, arguments); });
        self.currentXhr.fail(function() {
          self.amendResponse([], uniqueRequestId);
        });
        self.currentXhr.complete(function() {
          self.pending = 0;
          self.element.removeClass('ui-autocomplete-loading');
        });
      }, this.options.remoteDelay);
    }
  });
})(jQuery);
<|endoftext|>


<commit_before>import debug from 'debug';
import reqwest from 'reqwest';

import { clearControlError, closeWebsocket, openWebsocket, receiveError,
  receiveApiDetails, receiveNodesDelta, receiveNodeDetails, receiveControlError,
  receiveControlPipe, receiveControlPipeStatus, receiveControlSuccess,
  receiveTopologies, receiveNotFound } from '../actions/app-actions';

import { API_INTERVAL, TOPOLOGY_INTERVAL } from '../constants/timer';

const log = debug('scope:web-api-utils');

const reconnectTimerInterval = 5000;
const updateFrequency = '5s';

let socket;
let reconnectTimer = 0;
let currentUrl = null;
let currentOptions = null;
let topologyTimer = 0;
let apiDetailsTimer = 0;
let controlErrorTimer = 0;

function buildOptionsQuery(options) {
  if (options) {
    return options.reduce(function(query, value, param) {
      return `${query}&${param}=${value}`;
    }, '');
  }
  return '';
}

export function basePath(urlPath) {
  //
  // "/scope/terminal.html" -> "/scope"
  // "/scope/" -> "/scope"
  // "/scope" -> "/scope"
  // "/" -> ""
  //
  const parts = urlPath.split('/');
  // if the last item has a "." in it, e.g. foo.html...
  if (parts[parts.length - 1].indexOf('.') !== -1) {
    return parts.slice(0, -1).join('/');
  }
  return parts.join('/').replace(/\/$/, '');
}

export function basePathSlash(urlPath) {
  //
  // "/scope/terminal.html" -> "/scope/"
  // "/scope/" -> "/scope/"
  // "/scope" -> "/scope/"
  // "/" -> "/"
  //
  return basePath(urlPath) + '/';
}

const wsProto = location.protocol === 'https:' ? 'wss' : 'ws';
const wsUrl = wsProto + '://' + location.host + basePath(location.pathname);

function createWebsocket(topologyUrl, optionsQuery) {
  if (socket) {
    socket.onclose = null;
    socket.onerror = null;
    socket.close();
  }

  socket = new WebSocket(wsUrl + topologyUrl
    + '/ws?t=' + updateFrequency + '&' + optionsQuery);

  socket.onopen = function() {
    openWebsocket();
  };

  socket.onclose = function() {
    clearTimeout(reconnectTimer);
    socket = null;
    closeWebsocket();
    log('Closed websocket to ' + topologyUrl);

    reconnectTimer = setTimeout(function() {
      createWebsocket(topologyUrl, optionsQuery);
    }, reconnectTimerInterval);
  };

  socket.onerror = function() {
    log('Error in websocket to ' + topologyUrl);
    receiveError(currentUrl);
  };

  socket.onmessage = function(event) {
    const msg = JSON.parse(event.data);
    receiveNodesDelta(msg);
  };
}

/* keep URLs relative */

export function getTopologies(options) {
  clearTimeout(topologyTimer);
  const optionsQuery = buildOptionsQuery(options);
  const url = `api/topology?${optionsQuery}`;
  reqwest({
    url: url,
    success: function(res) {
      receiveTopologies(res);
      topologyTimer = setTimeout(function() {
        getTopologies(options);
      }, TOPOLOGY_INTERVAL);
    },
    error: function(err) {
      log('Error in topology request: ' + err);
      receiveError(url);
      topologyTimer = setTimeout(function() {
        getTopologies(options);
      }, TOPOLOGY_INTERVAL / 2);
    }
  });
}

export function getNodesDelta(topologyUrl, options) {
  const optionsQuery = buildOptionsQuery(options);

  // only recreate websocket if url changed
  if (topologyUrl && (topologyUrl !== currentUrl || currentOptions !== optionsQuery)) {
    createWebsocket(topologyUrl, optionsQuery);
    currentUrl = topologyUrl;
    currentOptions = optionsQuery;
  }
}

export function getNodeDetails(topologyUrlsById, nodeMap) {
  // get details for all opened nodes
  const obj = nodeMap.last();
  if (obj && topologyUrlsById.has(obj.topologyId)) {
    const topologyUrl = topologyUrlsById.get(obj.topologyId);
    const url = [topologyUrl, '/', encodeURIComponent(obj.id)]
      .join('').substr(1);
    reqwest({
      url: url,
      success: function(res) {
        // make sure node is still selected
        if (nodeMap.has(res.node.id)) {
          receiveNodeDetails(res.node);
        }
      },
      error: function(err) {
        log('Error in node details request: ' + err.responseText);
        // dont treat missing node as error
        if (err.status === 404) {
          receiveNotFound(obj.id);
        } else {
          receiveError(topologyUrl);
        }
      }
    });
  } else {
    log('No details or url found for ', obj);
  }
}

export function getApiDetails() {
  clearTimeout(apiDetailsTimer);
  const url = 'api';
  reqwest({
    url: url,
    success: function(res) {
      receiveApiDetails(res);
      apiDetailsTimer = setTimeout(getApiDetails, API_INTERVAL);
    },
    error: function(err) {
      log('Error in api details request: ' + err);
      receiveError(url);
      apiDetailsTimer = setTimeout(getApiDetails, API_INTERVAL / 2);
    }
  });
}

export function doControlRequest(nodeId, control) {
  clearTimeout(controlErrorTimer);
  const url = `api/control/${encodeURIComponent(control.probeId)}/`
    + `${encodeURIComponent(control.nodeId)}/${control.id}`;
  reqwest({
    method: 'POST',
    url: url,
    success: function(res) {
      receiveControlSuccess(nodeId);
      if (res && res.pipe) {
        receiveControlPipe(res.pipe, nodeId, res.raw_tty, true);
      }
    },
    error: function(err) {
      receiveControlError(nodeId, err.response);
      controlErrorTimer = setTimeout(function() {
        clearControlError(nodeId);
      }, 10000);
    }
  });
}

export function deletePipe(pipeId) {
  const url = `api/pipe/${encodeURIComponent(pipeId)}`;
  reqwest({
    method: 'DELETE',
    url: url,
    success: function() {
      log('Closed the pipe!');
    },
    error: function(err) {
      log('Error closing pipe:' + err);
      receiveError(url);
    }
  });
}

export function getPipeStatus(pipeId) {
  const url = `/api/pipe/${encodeURIComponent(pipeId)}/check`;
  reqwest({
    method: 'GET',
    url: url,
    error: function(err) {
      log('ERROR: unexpected response:', err);
    },
    success: function(res) {
      const status = {
        200: 'PIPE_ALIVE',
        204: 'PIPE_DELETED'
      }[res.status];

      if (!status) {
        log('Unexpected pipe status:', res.status);
        return;
      }

      receiveControlPipeStatus(pipeId, status);
    }
  });
}
<commit_msg>Fixes the pipe-alive check when running scope-ui under a path<commit_after>import debug from 'debug';
import reqwest from 'reqwest';

import { clearControlError, closeWebsocket, openWebsocket, receiveError,
  receiveApiDetails, receiveNodesDelta, receiveNodeDetails, receiveControlError,
  receiveControlPipe, receiveControlPipeStatus, receiveControlSuccess,
  receiveTopologies, receiveNotFound } from '../actions/app-actions';

import { API_INTERVAL, TOPOLOGY_INTERVAL } from '../constants/timer';

const log = debug('scope:web-api-utils');

const reconnectTimerInterval = 5000;
const updateFrequency = '5s';

let socket;
let reconnectTimer = 0;
let currentUrl = null;
let currentOptions = null;
let topologyTimer = 0;
let apiDetailsTimer = 0;
let controlErrorTimer = 0;

function buildOptionsQuery(options) {
  if (options) {
    return options.reduce(function(query, value, param) {
      return `${query}&${param}=${value}`;
    }, '');
  }
  return '';
}

export function basePath(urlPath) {
  //
  // "/scope/terminal.html" -> "/scope"
  // "/scope/" -> "/scope"
  // "/scope" -> "/scope"
  // "/" -> ""
  //
  const parts = urlPath.split('/');
  // if the last item has a "." in it, e.g. foo.html...
  if (parts[parts.length - 1].indexOf('.') !== -1) {
    return parts.slice(0, -1).join('/');
  }
  return parts.join('/').replace(/\/$/, '');
}

export function basePathSlash(urlPath) {
  //
  // "/scope/terminal.html" -> "/scope/"
  // "/scope/" -> "/scope/"
  // "/scope" -> "/scope/"
  // "/" -> "/"
  //
  return basePath(urlPath) + '/';
}

const wsProto = location.protocol === 'https:' ? 'wss' : 'ws';
const wsUrl = wsProto + '://' + location.host + basePath(location.pathname);

function createWebsocket(topologyUrl, optionsQuery) {
  if (socket) {
    socket.onclose = null;
    socket.onerror = null;
    socket.close();
  }

  socket = new WebSocket(wsUrl + topologyUrl
    + '/ws?t=' + updateFrequency + '&' + optionsQuery);

  socket.onopen = function() {
    openWebsocket();
  };

  socket.onclose = function() {
    clearTimeout(reconnectTimer);
    socket = null;
    closeWebsocket();
    log('Closed websocket to ' + topologyUrl);

    reconnectTimer = setTimeout(function() {
      createWebsocket(topologyUrl, optionsQuery);
    }, reconnectTimerInterval);
  };

  socket.onerror = function() {
    log('Error in websocket to ' + topologyUrl);
    receiveError(currentUrl);
  };

  socket.onmessage = function(event) {
    const msg = JSON.parse(event.data);
    receiveNodesDelta(msg);
  };
}

/* keep URLs relative */

export function getTopologies(options) {
  clearTimeout(topologyTimer);
  const optionsQuery = buildOptionsQuery(options);
  const url = `api/topology?${optionsQuery}`;
  reqwest({
    url: url,
    success: function(res) {
      receiveTopologies(res);
      topologyTimer = setTimeout(function() {
        getTopologies(options);
      }, TOPOLOGY_INTERVAL);
    },
    error: function(err) {
      log('Error in topology request: ' + err);
      receiveError(url);
      topologyTimer = setTimeout(function() {
        getTopologies(options);
      }, TOPOLOGY_INTERVAL / 2);
    }
  });
}

export function getNodesDelta(topologyUrl, options) {
  const optionsQuery = buildOptionsQuery(options);

  // only recreate websocket if url changed
  if (topologyUrl && (topologyUrl !== currentUrl || currentOptions !== optionsQuery)) {
    createWebsocket(topologyUrl, optionsQuery);
    currentUrl = topologyUrl;
    currentOptions = optionsQuery;
  }
}

export function getNodeDetails(topologyUrlsById, nodeMap) {
  // get details for all opened nodes
  const obj = nodeMap.last();
  if (obj && topologyUrlsById.has(obj.topologyId)) {
    const topologyUrl = topologyUrlsById.get(obj.topologyId);
    const url = [topologyUrl, '/', encodeURIComponent(obj.id)]
      .join('').substr(1);
    reqwest({
      url: url,
      success: function(res) {
        // make sure node is still selected
        if (nodeMap.has(res.node.id)) {
          receiveNodeDetails(res.node);
        }
      },
      error: function(err) {
        log('Error in node details request: ' + err.responseText);
        // dont treat missing node as error
        if (err.status === 404) {
          receiveNotFound(obj.id);
        } else {
          receiveError(topologyUrl);
        }
      }
    });
  } else {
    log('No details or url found for ', obj);
  }
}

export function getApiDetails() {
  clearTimeout(apiDetailsTimer);
  const url = 'api';
  reqwest({
    url: url,
    success: function(res) {
      receiveApiDetails(res);
      apiDetailsTimer = setTimeout(getApiDetails, API_INTERVAL);
    },
    error: function(err) {
      log('Error in api details request: ' + err);
      receiveError(url);
      apiDetailsTimer = setTimeout(getApiDetails, API_INTERVAL / 2);
    }
  });
}

export function doControlRequest(nodeId, control) {
  clearTimeout(controlErrorTimer);
  const url = `api/control/${encodeURIComponent(control.probeId)}/`
    + `${encodeURIComponent(control.nodeId)}/${control.id}`;
  reqwest({
    method: 'POST',
    url: url,
    success: function(res) {
      receiveControlSuccess(nodeId);
      if (res && res.pipe) {
        receiveControlPipe(res.pipe, nodeId, res.raw_tty, true);
      }
    },
    error: function(err) {
      receiveControlError(nodeId, err.response);
      controlErrorTimer = setTimeout(function() {
        clearControlError(nodeId);
      }, 10000);
    }
  });
}

export function deletePipe(pipeId) {
  const url = `api/pipe/${encodeURIComponent(pipeId)}`;
  reqwest({
    method: 'DELETE',
    url: url,
    success: function() {
      log('Closed the pipe!');
    },
    error: function(err) {
      log('Error closing pipe:' + err);
      receiveError(url);
    }
  });
}

export function getPipeStatus(pipeId) {
  const url = `api/pipe/${encodeURIComponent(pipeId)}/check`;
  reqwest({
    method: 'GET',
    url: url,
    error: function(err) {
      log('ERROR: unexpected response:', err);
    },
    success: function(res) {
      const status = {
        200: 'PIPE_ALIVE',
        204: 'PIPE_DELETED'
      }[res.status];

      if (!status) {
        log('Unexpected pipe status:', res.status);
        return;
      }

      receiveControlPipeStatus(pipeId, status);
    }
  });
}
<|endoftext|>


<commit_before>
var pmHistory = new pmItems()

pmHistory.model.name = "history"
pmHistory.model.linePerPage = 30;

pmHistory.cancelTask = function(item_id)
{
    return $.ajax({
        url: "/api/v1/history/"+item_id+"/cancel/",
        type: "POST",
        contentType:'application/json',
        beforeSend: function(xhr, settings) {
            if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
                // Only send the token to relative URLs i.e. locally.
                xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
            }
        },
        success: function(data)
        {
            $.notify("Cancel", "success");
        },
        error:function(e)
        {
            polemarch.showErrors(e.responseJSON)
        }
    })
}

pmHistory.showSearchResults = function(holder, menuInfo, data)
{
    var thisObj = this;
    return $.when(this.sendSearchQuery({playbook:decodeURIComponent(data.reg[1])})).done(function()
    {
        $(holder).html(spajs.just.render(thisObj.model.name+'_list', {query:decodeURIComponent(data.reg[1])}))
    }).fail(function()
    {
        $.notify("", "error");
    })
}

pmHistory.search = function(project_id, query)
{
    if(!project_id)
    {
        if(!query || !trim(query))
        {
            return spajs.open({ menuId:this.model.name, reopen:true});
        }

        return spajs.open({ menuId:this.model.name+"/search/"+encodeURIComponent(trim(query)), reopen:true});
    }

    if(!query || !trim(query))
    {
        return spajs.open({ menuId:'project/' + project_id +"/" + this.model.name, reopen:true});
    }

    return spajs.open({ menuId:'project/' + project_id +"/" + this.model.name+"/search/"+encodeURIComponent(trim(query)), reopen:true});
}

pmHistory.showListInProjects = function(holder, menuInfo, data)
{
    var thisObj = this;
    var offset = 0
    var limit = this.pageSize;
    if(data.reg && data.reg[2] > 0)
    {
        offset = this.pageSize*(data.reg[2] - 1);
    }
    var project_id = data.reg[1];

    return $.when(this.sendSearchQuery({project:project_id}, limit, offset), pmProjects.loadItem(project_id)).done(function()
    {
        $(holder).html(spajs.just.render(thisObj.model.name+'_listInProjects', {query:"", project_id:project_id}))
    }).fail(function()
    {
        $.notify("", "error");
    })
}

pmHistory.showSearchResultsInProjects = function(holder, menuInfo, data)
{
    var thisObj = this;
    var project_id = data.reg[1];
    return $.when(this.sendSearchQuery({playbook: decodeURIComponent(data.reg[2]), project:project_id}), pmProjects.loadItem(project_id)).done(function()
    {
        $(holder).html(spajs.just.render(thisObj.model.name+'_listInProjects', {query:decodeURIComponent(data.reg[2]), project_id:project_id}))
    }).fail(function()
    {
        $.notify("", "error");
    })
}

pmHistory.showItem = function(holder, menuInfo, data)
{
    var thisObj = this;
    //console.log(menuInfo, data)

    var item_id = data.reg[1];
    return $.when(this.loadItem(item_id)).done(function()
    {
        $(holder).html(spajs.just.render(thisObj.model.name+'_page', {item_id:item_id, project_id:0}))
        pmHistory.bindStdoutUpdates(item_id)
    }).fail(function()
    {
        $.notify("", "error");
    })
}

pmHistory.showItemInProjects = function(holder, menuInfo, data)
{
    var thisObj = this;
    //console.log(menuInfo, data)
    var project_id = data.reg[1];
    var item_id = data.reg[2];
    return $.when(this.loadItem(item_id), pmProjects.loadItem(project_id)).done(function()
    {
        $(holder).html(spajs.just.render(thisObj.model.name+'_pageInProjects', {item_id:item_id, project_id:project_id}))
        pmHistory.bindStdoutUpdates(item_id)
    }).fail(function()
    {
        $.notify("", "error");
    })
}

pmHistory.bindStdoutUpdates = function(item_id)
{
    var thisObj = this;
    this.loadNewLines(item_id)
    var content = $('#history-stdout')
    content.scroll(function()
    {
        // End of the document reached?
        if (content.scrollTop() < 150)
        {
            if(thisObj.stdout_minline <= 1)
            {
                return;
            }

            if(thisObj.inLoadTopData)
            {
                return;
            }

            //pmHistory.lastContentScrollHeight = $('#history-stdout').prop('scrollHeight') - content.scrollTop() + 100;

            thisObj.inLoadTopData = true;

            var stdout_minline = thisObj.model.items[item_id].stdout_minline;
            $.when(thisObj.loadLines(item_id, {before:stdout_minline, limit:thisObj.model.linePerPage})).always(function()
            {

                for(var i = stdout_minline-1; i > stdout_minline - thisObj.model.linePerPage; i = i -1)
                {
                    if(thisObj.model.items[item_id].stdout[i] != undefined)
                    {
                        $("#history-stdout").prepend(thisObj.model.items[item_id].stdout[i])
                    }
                }

                thisObj.inLoadTopData = false;
            })
        }
    });
}

/**
 * Обновляет поле модел this.model.items[item_id] и ложит туда пользователя
 */
pmHistory.loadItem = function(item_id)
{
    var def = new $.Deferred();
    var thisObj = this;

    jQuery.ajax({
        url: "/api/v1/"+this.model.name+"/"+item_id+"/",
        type: "GET",
        contentType:'application/json',
        data: "",
        beforeSend: function(xhr, settings) {
            if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
                // Only send the token to relative URLs i.e. locally.
                xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
            }
        },
        success: function(data)
        {
            //console.log("loadUser", data)
            thisObj.model.items[item_id] = data
            $.when(pmProjects.loadItem(data.project)).done(function(){
                def.resolve()
            }).fail(function(){
                def.reject()
            })
        },
        error:function(e)
        {
            console.warn("pmHistory.loadItem", e)
            polemarch.showErrors(e)
            def.reject()
        }
    });

    return def.promise();
}

pmHistory.sendSearchQuery = function(query, limit, offset)
{
    if(!limit)
    {
        limit = 999;
    }

    if(!offset)
    {
        offset = 0;
    }

    var q = [];
    for(var i in query)
    {
        q.push(encodeURIComponent(i)+"="+encodeURIComponent(query[i]))
    }

    var def = new $.Deferred();
    var thisObj = this;
    jQuery.ajax({
        url: "/api/v1/"+this.model.name+"/?"+q.join('&'),
        type: "GET",
        contentType:'application/json',
        data: "limit="+encodeURIComponent(limit)+"&offset="+encodeURIComponent(offset),
        beforeSend: function(xhr, settings) {
            if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
                // Only send the token to relative URLs i.e. locally.
                xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
            }
        },
        success: function(data)
        {
            //console.log("update Items", data)
            data.limit = limit
            data.offset = offset
            thisObj.model.itemslist = data
            //thisObj.model.items = {}

            var projects = [];
            for(var i in data.results)
            {
                var val = data.results[i]
                thisObj.model.items[val.id] = val

                if(!pmProjects.model.items[val.project] && projects.indexOf(val.project) == -1)
                {
                    projects.push(val.project)
                }
            }

            $.when(pmProjects.sendSearchQuery({id:projects.join(',')})).done(function(){
                def.resolve()
            }).fail(function(){
                def.reject()
            })
        },
        error:function(e)
        {
            console.warn(e)
            polemarch.showErrors(e)
            def.reject()
        }
    });

    return def.promise();
}

/**
 * Обновляет поле модел this.model.itemslist и ложит туда список пользователей
 * Обновляет поле модел this.model.items и ложит туда список инфу о пользователях по их id
 */
pmHistory.loadItems = function(limit, offset)
{
    if(!limit)
    {
        limit = 30;
    }

    if(!offset)
    {
        offset = 0;
    }

    var def = new $.Deferred();
    var thisObj = this;
    jQuery.ajax({
        url: "/api/v1/"+this.model.name+"/",
        type: "GET",
        contentType:'application/json',
        data: "limit="+encodeURIComponent(limit)+"&offset="+encodeURIComponent(offset),
        beforeSend: function(xhr, settings) {
            if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
                // Only send the token to relative URLs i.e. locally.
                xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
            }
        },
        success: function(data)
        {
            //console.log("update Items", data)
            data.limit = limit
            data.offset = offset
            thisObj.model.itemslist = data
            //thisObj.model.items = {}

            var projects = [];
            for(var i in data.results)
            {
                var val = data.results[i]
                thisObj.model.items.justWatch(val.id);
                thisObj.model.items[val.id] = mergeDeep(thisObj.model.items[val.id], val)

                if(!pmProjects.model.items[val.project] && projects.indexOf(val.project) == -1)
                {
                    projects.push(val.project)
                }
            }

            $.when(pmProjects.sendSearchQuery({id:projects.join(',')})).done(function(){
                def.resolve()
            }).fail(function(){
                def.reject()
            })
        },
        error:function(e)
        {
            console.warn(e)
            polemarch.showErrors(e)
            def.reject()
        }
    });

    return def.promise();
}

pmHistory.stopUpdates = function()
{
    clearTimeout(this.model.updateTimeoutId)
    this.model.updateTimeoutId = undefined;

    clearTimeout(this.model.loadNewLines_timeoutId)
    this.model.loadNewLines_timeoutId = undefined;
}

pmHistory.loadNewLines = function(item_id)
{
    var thisObj = this;
    var last_stdout_maxline = this.model.items[item_id].stdout_maxline;
    if(!last_stdout_maxline)
    {
        last_stdout_maxline = 0;
    }

    $.when(this.loadLines(item_id, {after:last_stdout_maxline, limit:pmHistory.model.linePerPage})).always(function()
    {
        var addData = false;
        var needScrollDowun = $('#history-stdout').prop('scrollHeight') - $('#history-stdout').scrollTop() -  $("#history-stdout").css('height').replace("px", "")/1 < 100
        for(var i = last_stdout_maxline+1; i <= thisObj.model.items[item_id].stdout_maxline; i++)
        {
            if(thisObj.model.items[item_id].stdout[i] != undefined)
            {
                $("#history-stdout").append(thisObj.model.items[item_id].stdout[i])
                addData = true;
            }
        }

        if( addData && needScrollDowun)
        {
            // Прокручиваем в низ только если и так скрол был не сильно приподнят
            thisObj.scrollBottom()
        }

        if(thisObj.model.items[item_id].status == 'RUN' || thisObj.model.items[item_id].status == 'DELAY')
        {
            thisObj.loadNewLines_timeoutId = setTimeout(function(){
                thisObj.loadNewLines(item_id)
            }, 1000)
        }
    })
}

pmHistory.scrollBottom = function()
{
    jQuery('#history-stdout').scrollTop(9999999);
}
/**
 * Обновляет поле модел this.model.itemslist и ложит туда список пользователей
 * Обновляет поле модел this.model.items и ложит туда список инфу о пользователях по их id
 */
pmHistory.loadLines = function(item_id, opt)
{
    if(!opt.limit)
    {
        opt.limit = 30;
    }

    if(!opt.offset)
    {
        opt.offset = 0;
    }

    opt.format = 'json';

    var def = new $.Deferred();
    jQuery.ajax({
        url: "/api/v1/history/"+item_id+"/lines",
        type: "GET",
        contentType:'application/json',
        data: opt,
        beforeSend: function(xhr, settings) {
            if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
                // Only send the token to relative URLs i.e. locally.
                xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
            }
        },
        success: function(data)
        {
            if(!pmHistory.model.items[item_id].stdout)
            {
                pmHistory.model.items[item_id].stdout = {}
                pmHistory.model.items[item_id].stdout_count = 0
                pmHistory.model.items[item_id].stdout_maxline = 0
                pmHistory.model.items[item_id].stdout_minline = 999999999
            }

            pmHistory.model.items[item_id].stdout_count = data.count;
            for(var i in data.results)
            {
                var line_number = data.results[i].line_number

                if(pmHistory.model.items[item_id].stdout_maxline < line_number)
                {
                    pmHistory.model.items[item_id].stdout_maxline = line_number;
                }

                if(pmHistory.model.items[item_id].stdout_minline > line_number)
                {
                    pmHistory.model.items[item_id].stdout_minline = line_number;
                }

                pmHistory.model.items[item_id].stdout[line_number] = data.results[i].line
            }

            def.resolve()

        },
        error:function(e)
        {
            console.warn(e)
            polemarch.showErrors(e)
            def.reject()
        }
    });

    return def.promise();
}
<commit_msg>fix scroll in history<commit_after>
var pmHistory = new pmItems()

pmHistory.model.name = "history"
pmHistory.model.linePerPage = 100;

pmHistory.cancelTask = function(item_id)
{
    return $.ajax({
        url: "/api/v1/history/"+item_id+"/cancel/",
        type: "POST",
        contentType:'application/json',
        beforeSend: function(xhr, settings) {
            if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
                // Only send the token to relative URLs i.e. locally.
                xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
            }
        },
        success: function(data)
        {
            $.notify("Cancel", "success");
        },
        error:function(e)
        {
            polemarch.showErrors(e.responseJSON)
        }
    })
}

pmHistory.showSearchResults = function(holder, menuInfo, data)
{
    var thisObj = this;
    return $.when(this.sendSearchQuery({playbook:decodeURIComponent(data.reg[1])})).done(function()
    {
        $(holder).html(spajs.just.render(thisObj.model.name+'_list', {query:decodeURIComponent(data.reg[1])}))
    }).fail(function()
    {
        $.notify("", "error");
    })
}

pmHistory.search = function(project_id, query)
{
    if(!project_id)
    {
        if(!query || !trim(query))
        {
            return spajs.open({ menuId:this.model.name, reopen:true});
        }

        return spajs.open({ menuId:this.model.name+"/search/"+encodeURIComponent(trim(query)), reopen:true});
    }

    if(!query || !trim(query))
    {
        return spajs.open({ menuId:'project/' + project_id +"/" + this.model.name, reopen:true});
    }

    return spajs.open({ menuId:'project/' + project_id +"/" + this.model.name+"/search/"+encodeURIComponent(trim(query)), reopen:true});
}

pmHistory.showListInProjects = function(holder, menuInfo, data)
{
    var thisObj = this;
    var offset = 0
    var limit = this.pageSize;
    if(data.reg && data.reg[2] > 0)
    {
        offset = this.pageSize*(data.reg[2] - 1);
    }
    var project_id = data.reg[1];

    return $.when(this.sendSearchQuery({project:project_id}, limit, offset), pmProjects.loadItem(project_id)).done(function()
    {
        $(holder).html(spajs.just.render(thisObj.model.name+'_listInProjects', {query:"", project_id:project_id}))
    }).fail(function()
    {
        $.notify("", "error");
    })
}

pmHistory.showSearchResultsInProjects = function(holder, menuInfo, data)
{
    var thisObj = this;
    var project_id = data.reg[1];
    return $.when(this.sendSearchQuery({playbook: decodeURIComponent(data.reg[2]), project:project_id}), pmProjects.loadItem(project_id)).done(function()
    {
        $(holder).html(spajs.just.render(thisObj.model.name+'_listInProjects', {query:decodeURIComponent(data.reg[2]), project_id:project_id}))
    }).fail(function()
    {
        $.notify("", "error");
    })
}

pmHistory.showItem = function(holder, menuInfo, data)
{
    var thisObj = this;
    //console.log(menuInfo, data)

    var item_id = data.reg[1];
    return $.when(this.loadItem(item_id)).done(function()
    {
        $(holder).html(spajs.just.render(thisObj.model.name+'_page', {item_id:item_id, project_id:0}))
        pmHistory.bindStdoutUpdates(item_id)
    }).fail(function()
    {
        $.notify("", "error");
    })
}

pmHistory.showItemInProjects = function(holder, menuInfo, data)
{
    var thisObj = this;
    //console.log(menuInfo, data)
    var project_id = data.reg[1];
    var item_id = data.reg[2];
    return $.when(this.loadItem(item_id), pmProjects.loadItem(project_id)).done(function()
    {
        $(holder).html(spajs.just.render(thisObj.model.name+'_pageInProjects', {item_id:item_id, project_id:project_id}))
        pmHistory.bindStdoutUpdates(item_id)
    }).fail(function()
    {
        $.notify("", "error");
    })
}

pmHistory.bindStdoutUpdates = function(item_id)
{
    var thisObj = this;
    this.loadNewLines(item_id)
    var content = $('#history-stdout')
    content.scroll(function()
    {
        // End of the document reached?
        if (content.scrollTop() < 150)
        {
            if(thisObj.stdout_minline <= 1)
            {
                return;
            }

            if(thisObj.inLoadTopData)
            {
                return;
            }

            //pmHistory.lastContentScrollHeight = $('#history-stdout').prop('scrollHeight') - content.scrollTop() + 100;

            thisObj.inLoadTopData = true;

            var stdout_minline = thisObj.model.items[item_id].stdout_minline;
            $.when(thisObj.loadLines(item_id, {before:stdout_minline, limit:thisObj.model.linePerPage})).always(function()
            {

                for(var i = stdout_minline-1; i > stdout_minline - thisObj.model.linePerPage; i = i -1)
                {
                    if(thisObj.model.items[item_id].stdout[i] != undefined)
                    {
                        $("#history-stdout").prepend("<div>"+thisObj.model.items[item_id].stdout[i]+"</div>")
                    }
                }

                thisObj.inLoadTopData = false;
            })
        }
    });
}

/**
 * Обновляет поле модел this.model.items[item_id] и ложит туда пользователя
 */
pmHistory.loadItem = function(item_id)
{
    var def = new $.Deferred();
    var thisObj = this;

    jQuery.ajax({
        url: "/api/v1/"+this.model.name+"/"+item_id+"/",
        type: "GET",
        contentType:'application/json',
        data: "",
        beforeSend: function(xhr, settings) {
            if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
                // Only send the token to relative URLs i.e. locally.
                xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
            }
        },
        success: function(data)
        {
            //console.log("loadUser", data)
            thisObj.model.items[item_id] = data
            $.when(pmProjects.loadItem(data.project)).done(function(){
                def.resolve()
            }).fail(function(){
                def.reject()
            })
        },
        error:function(e)
        {
            console.warn("pmHistory.loadItem", e)
            polemarch.showErrors(e)
            def.reject()
        }
    });

    return def.promise();
}

pmHistory.sendSearchQuery = function(query, limit, offset)
{
    if(!limit)
    {
        limit = 999;
    }

    if(!offset)
    {
        offset = 0;
    }

    var q = [];
    for(var i in query)
    {
        q.push(encodeURIComponent(i)+"="+encodeURIComponent(query[i]))
    }

    var def = new $.Deferred();
    var thisObj = this;
    jQuery.ajax({
        url: "/api/v1/"+this.model.name+"/?"+q.join('&'),
        type: "GET",
        contentType:'application/json',
        data: "limit="+encodeURIComponent(limit)+"&offset="+encodeURIComponent(offset),
        beforeSend: function(xhr, settings) {
            if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
                // Only send the token to relative URLs i.e. locally.
                xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
            }
        },
        success: function(data)
        {
            //console.log("update Items", data)
            data.limit = limit
            data.offset = offset
            thisObj.model.itemslist = data
            //thisObj.model.items = {}

            var projects = [];
            for(var i in data.results)
            {
                var val = data.results[i]
                thisObj.model.items[val.id] = val

                if(!pmProjects.model.items[val.project] && projects.indexOf(val.project) == -1)
                {
                    projects.push(val.project)
                }
            }

            $.when(pmProjects.sendSearchQuery({id:projects.join(',')})).done(function(){
                def.resolve()
            }).fail(function(){
                def.reject()
            })
        },
        error:function(e)
        {
            console.warn(e)
            polemarch.showErrors(e)
            def.reject()
        }
    });

    return def.promise();
}

/**
 * Обновляет поле модел this.model.itemslist и ложит туда список пользователей
 * Обновляет поле модел this.model.items и ложит туда список инфу о пользователях по их id
 */
pmHistory.loadItems = function(limit, offset)
{
    if(!limit)
    {
        limit = 30;
    }

    if(!offset)
    {
        offset = 0;
    }

    var def = new $.Deferred();
    var thisObj = this;
    jQuery.ajax({
        url: "/api/v1/"+this.model.name+"/",
        type: "GET",
        contentType:'application/json',
        data: "limit="+encodeURIComponent(limit)+"&offset="+encodeURIComponent(offset),
        beforeSend: function(xhr, settings) {
            if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
                // Only send the token to relative URLs i.e. locally.
                xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
            }
        },
        success: function(data)
        {
            //console.log("update Items", data)
            data.limit = limit
            data.offset = offset
            thisObj.model.itemslist = data
            //thisObj.model.items = {}

            var projects = [];
            for(var i in data.results)
            {
                var val = data.results[i]
                thisObj.model.items.justWatch(val.id);
                thisObj.model.items[val.id] = mergeDeep(thisObj.model.items[val.id], val)

                if(!pmProjects.model.items[val.project] && projects.indexOf(val.project) == -1)
                {
                    projects.push(val.project)
                }
            }

            $.when(pmProjects.sendSearchQuery({id:projects.join(',')})).done(function(){
                def.resolve()
            }).fail(function(){
                def.reject()
            })
        },
        error:function(e)
        {
            console.warn(e)
            polemarch.showErrors(e)
            def.reject()
        }
    });

    return def.promise();
}

pmHistory.stopUpdates = function()
{
    clearTimeout(this.model.updateTimeoutId)
    this.model.updateTimeoutId = undefined;

    clearTimeout(this.model.loadNewLines_timeoutId)
    this.model.loadNewLines_timeoutId = undefined;
}

pmHistory.loadNewLines = function(item_id)
{
    var thisObj = this;
    var last_stdout_maxline = this.model.items[item_id].stdout_maxline;
    if(!last_stdout_maxline)
    {
        last_stdout_maxline = 0;
    }

    $.when(this.loadLines(item_id, {after:last_stdout_maxline, limit:pmHistory.model.linePerPage})).always(function()
    {
        var addData = false;
        var needScrollDowun = $('#history-stdout').prop('scrollHeight') - $('#history-stdout').scrollTop() -  $("#history-stdout").css('height').replace("px", "")/1 < 100
        for(var i = last_stdout_maxline+1; i <= thisObj.model.items[item_id].stdout_maxline; i++)
        {
            if(thisObj.model.items[item_id].stdout[i] != undefined)
            {
                $("#history-stdout").append("<div>"+thisObj.model.items[item_id].stdout[i]+"</div>")
                addData = true;
            }
        }

        if( addData && needScrollDowun)
        {
            // Прокручиваем в низ только если и так скрол был не сильно приподнят
            thisObj.scrollBottom()
        }

        if(thisObj.model.items[item_id].status == 'RUN' || thisObj.model.items[item_id].status == 'DELAY')
        {
            thisObj.loadNewLines_timeoutId = setTimeout(function(){
                thisObj.loadNewLines(item_id)
            }, 1000)
        }
    })
}

pmHistory.scrollBottom = function()
{
    jQuery('#history-stdout').scrollTop(9999999);
}
/**
 * Обновляет поле модел this.model.itemslist и ложит туда список пользователей
 * Обновляет поле модел this.model.items и ложит туда список инфу о пользователях по их id
 */
pmHistory.loadLines = function(item_id, opt)
{
    if(!opt.limit)
    {
        opt.limit = 30;
    }

    if(!opt.offset)
    {
        opt.offset = 0;
    }

    opt.format = 'json';

    var def = new $.Deferred();
    jQuery.ajax({
        url: "/api/v1/history/"+item_id+"/lines",
        type: "GET",
        contentType:'application/json',
        data: opt,
        beforeSend: function(xhr, settings) {
            if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
                // Only send the token to relative URLs i.e. locally.
                xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
            }
        },
        success: function(data)
        {
            if(!pmHistory.model.items[item_id].stdout)
            {
                pmHistory.model.items[item_id].stdout = {}
                pmHistory.model.items[item_id].stdout_count = 0
                pmHistory.model.items[item_id].stdout_maxline = 0
                pmHistory.model.items[item_id].stdout_minline = 999999999
            }

            pmHistory.model.items[item_id].stdout_count = data.count;
            for(var i in data.results)
            {
                var line_number = data.results[i].line_number

                if(pmHistory.model.items[item_id].stdout_maxline < line_number)
                {
                    pmHistory.model.items[item_id].stdout_maxline = line_number;
                }

                if(pmHistory.model.items[item_id].stdout_minline > line_number)
                {
                    pmHistory.model.items[item_id].stdout_minline = line_number;
                }

                pmHistory.model.items[item_id].stdout[line_number] = data.results[i].line
            }

            def.resolve()

        },
        error:function(e)
        {
            console.warn(e)
            polemarch.showErrors(e)
            def.reject()
        }
    });

    return def.promise();
}
<|endoftext|>