<commit_before>'use strict';

angular.module('jlogApp.services')
    .factory('backup', [function() {
        return {
            read_result: null,
            save_url: null,
            save_name: 'battle_list.txt',
            read: function backupRead(file, success_cbk, error_cbk) {
                var success_cbk_ = typeof(success_cbk) === 'function' ?
                    success_cbk :
                    function(data) { console.log(data); };
                var error_cbk_ = typeof(error_cbk) === 'function' ?
                    error_cbk :
                    function(error) { console.log(error); };
                this.read_result = null;
                var reader = new FileReader();
                reader.onload = function(e) {
                    var data;
                    try {
                        data = JSON.parse(e.target.result);
                        success_cbk_(data);
                    }
                    catch(e) {
                        error_cbk_('invalid file');
                    }
                };
                reader.onerror = function(e) {
                    error_cbk_('error reading file');
                };
                reader.onabort = function(e) {
                    error_cbk_('abort reading file');
                };
                reader.readAsText(file);
            },
            generate: function backupGenerate(data) {
                console.log('generate backup file');
                var old_url = this.save_url;
                this.save_url = null;
                if(old_url !== null) {
                    URL.revokeObjectURL(old_url);
                }
                var string = JSON.stringify(data);
                var blob = new Blob([string], {type: 'application/octet-stream'});
                window.URL = window.URL || window.webkitURL;
                var url = window.URL.createObjectURL(blob);
                var today = new Date();
                this.save_name = 'battle_list_' + today.getTime() + '.txt';
                this.save_url = url;
            }
        };
    }]);
<commit_msg>Changed backup file mime type to text/plain.<commit_after>'use strict';

angular.module('jlogApp.services')
    .factory('backup', [function() {
        return {
            read_result: null,
            save_url: null,
            save_name: 'battle_list.txt',
            read: function backupRead(file, success_cbk, error_cbk) {
                var success_cbk_ = typeof(success_cbk) === 'function' ?
                    success_cbk :
                    function(data) { console.log(data); };
                var error_cbk_ = typeof(error_cbk) === 'function' ?
                    error_cbk :
                    function(error) { console.log(error); };
                this.read_result = null;
                var reader = new FileReader();
                reader.onload = function(e) {
                    var data;
                    try {
                        data = JSON.parse(e.target.result);
                        success_cbk_(data);
                    }
                    catch(e) {
                        error_cbk_('invalid file');
                    }
                };
                reader.onerror = function(e) {
                    error_cbk_('error reading file');
                };
                reader.onabort = function(e) {
                    error_cbk_('abort reading file');
                };
                reader.readAsText(file);
            },
            generate: function backupGenerate(data) {
                console.log('generate backup file');
                var old_url = this.save_url;
                this.save_url = null;
                if(old_url !== null) {
                    URL.revokeObjectURL(old_url);
                }
                var string = JSON.stringify(data);
                var blob = new Blob([string], {type: 'text/plain'});
                window.URL = window.URL || window.webkitURL;
                var url = window.URL.createObjectURL(blob);
                var today = new Date();
                this.save_name = 'battle_list_' + today.getTime() + '.txt';
                this.save_url = url;
            }
        };
    }]);
<|endoftext|>


<commit_before>/**
 * @preserve Flux Slider v@VERSION
 * http://www.joelambert.co.uk/flux
 *
 * Copyright 2011, Joe Lambert. All rights reserved
 * Free to use under the MIT license.
 * http://www.opensource.org/licenses/mit-license.php
 */

// Flux namespace
var flux = {};

flux.slider = function(elem, opts) {
	if(!flux.browser.supportsTransitions)
	{
		if(window.console && window.console.error)
			console.error("Flux Slider requires a browser that supports CSS3 transitions");

		return;
	}

	var _this = this;

	this.element = $(elem);

	// Make a list of all available transitions
	this.transitions = [];
	for(var fx in flux.transitions)
		this.transitions.push(fx);

	this.options = $.extend({
		autoplay: true,
		transitions: this.transitions,
		delay: 4000,
		pagination: true,
		controls: true
	}, opts);

	// Get a list the images to use
	this.images = new Array();
	this.imageLoadedCount = 0;
	this.currentImageIndex = 0;
	this.nextImageIndex = 1;
	this.playing = false;

	this.element.find('img').each(function(index, found_img){
		_this.images.push(found_img.cloneNode(false));

		var image = new Image();
		image.onload = function() {
			_this.imageLoadedCount++;

			_this.width  = this.width;
			_this.height = this.height;

			if(_this.imageLoadedCount >= _this.images.length)
			{
				_this.finishedLoading();
				_this.setupImages();
			}
		};

		// Load the image to ensure its cached by the browser
		image.src = found_img.src;

		// Remove the images from the DOM
		$(found_img).remove();
	});

	this.container = $('

');
	this.element.append(this.container);

	this.imageContainer = $('

').css({
		'position': 'relative',
		'overflow': 'hidden',
		'min-height': '100px'
	});
	this.container.append(this.imageContainer);

	this.image1 = $('

');
	this.imageContainer.append(this.image1);

	this.image2 = $('

');
	this.imageContainer.append(this.image2);

	$(this.image1).css({
		'position': 'absolute',
		'top': '0px',
		'left': '0px'
	});

	$(this.image2).css({
		'position': 'absolute',
		'top': '0px',
		'left': '0px'
	});

	// Should we auto start the slider?
	if(this.options.autoplay)
		this.start();
};

flux.slider.prototype = {
	constructor: flux.slider,
	start: function() {
		var _this = this;
		this.interval = setInterval(function() {
			_this.transition();
		}, this.options.delay);
	},
	stop: function() {
		clearInterval(this.interval);
	},
	next: function() {
		this.showImage(this.currentImageIndex+1);
	},
	prev: function() {
		this.showImage(this.currentImageIndex-1);
	},
	showImage: function(index) {
		this.setNextIndex(index);

		this.stop();
		this.setupImages();
		this.transition();
		this.start();
	},
	finishedLoading: function() {
		var _this = this;

		this.container.css({
			width: this.width+'px',
			height: this.height+'px'
		});

		this.imageContainer.removeClass('loading');

		// Should we setup a pagination view?
		if(this.options.pagination)
		{
			// TODO: Attach to touch events if appropriate
			this.pagination = $('

').css({
				margin: '0px',
				padding: '0px',
				'text-align': 'center'
			});

			this.pagination.bind('click', function(event){
				event.preventDefault();
				_this.showImage($(event.target).data('index'));
			});

			$(this.images).each(function(index, image){
				var li = $('
'+(index+1)+'
').css({
					display: 'inline-block',
					'margin-left': '0.5em',
					'cursor': 'pointer'
				});

				_this.pagination.append(li);

				if(index == 0)
					li.css('margin-left', 0).addClass('current');
			});

			this.container.append(this.pagination);
		}

		// Resize
		$(this.imageContainer).css({
			width: this.width+'px',
			height: this.height+'px'
		});

		$(this.image1).css({
			width: this.width+'px',
			height: this.height+'px'
		});

		$(this.image2).css({
			width: this.width+'px',
			height: this.height+'px'
		});

		this.container.css({
			width: this.width+'px',
			height: this.height+(this.options.pagination?this.pagination.height():0)+'px'
		});
	},
	setupImages: function() {
		this.image1.css({
			'background-image': 'url("'+this.getImage(this.currentImageIndex).src+'")',
			'z-index': 101
		}).children().remove();

		this.image2.css({
			'background-image': 'url("'+this.getImage(this.nextImageIndex).src+'")',
			'z-index': 100
		});

		if(this.options.pagination)
		{
			this.pagination.find('li.current').removeClass('current');
			$(this.pagination.find('li')[this.currentImageIndex]).addClass('current');
		}
	},
	transition: function() {
		// Pick a transition at random
		var index = Math.floor(Math.random()*(this.options.transitions.length));

		var tran = new flux.transitions[this.options.transitions[index]](this);
		//var tran = new flux.transitions.concentric(this);
		tran.run();

		this.currentImageIndex = this.nextImageIndex;
		this.setNextIndex(this.currentImageIndex+1);
	},
	getImage: function(index) {
		index = index % this.images.length;

		return this.images[index];
	},
	setNextIndex: function(nextIndex)
	{
		if(nextIndex == undefined)
			nextIndex = this.currentImageIndex+1;

		this.nextImageIndex = nextIndex;

		if(this.nextImageIndex > this.images.length-1)
			this.nextImageIndex = 0;

		if(this.nextImageIndex < 0)
			this.nextImageIndex = this.images.length-1;
	},
	increment: function() {
		this.currentImageIndex++;
		if(this.currentImageIndex > this.images.length-1)
			this.currentImageIndex = 0;
	}
}<commit_msg>Fixed a bug where clicking on the pagination would start the slider playing, even if the autoplay property is set as false<commit_after>/**
 * @preserve Flux Slider v@VERSION
 * http://www.joelambert.co.uk/flux
 *
 * Copyright 2011, Joe Lambert. All rights reserved
 * Free to use under the MIT license.
 * http://www.opensource.org/licenses/mit-license.php
 */

// Flux namespace
var flux = {};

flux.slider = function(elem, opts) {
	if(!flux.browser.supportsTransitions)
	{
		if(window.console && window.console.error)
			console.error("Flux Slider requires a browser that supports CSS3 transitions");

		return;
	}

	var _this = this;

	this.element = $(elem);

	// Make a list of all available transitions
	this.transitions = [];
	for(var fx in flux.transitions)
		this.transitions.push(fx);

	this.options = $.extend({
		autoplay: true,
		transitions: this.transitions,
		delay: 4000,
		pagination: true,
		controls: true
	}, opts);

	// Get a list the images to use
	this.images = new Array();
	this.imageLoadedCount = 0;
	this.currentImageIndex = 0;
	this.nextImageIndex = 1;
	this.playing = false;

	this.element.find('img').each(function(index, found_img){
		_this.images.push(found_img.cloneNode(false));

		var image = new Image();
		image.onload = function() {
			_this.imageLoadedCount++;

			_this.width  = this.width;
			_this.height = this.height;

			if(_this.imageLoadedCount >= _this.images.length)
			{
				_this.finishedLoading();
				_this.setupImages();
			}
		};

		// Load the image to ensure its cached by the browser
		image.src = found_img.src;

		// Remove the images from the DOM
		$(found_img).remove();
	});

	this.container = $('

');
	this.element.append(this.container);

	this.imageContainer = $('

').css({
		'position': 'relative',
		'overflow': 'hidden',
		'min-height': '100px'
	});
	this.container.append(this.imageContainer);

	this.image1 = $('

');
	this.imageContainer.append(this.image1);

	this.image2 = $('

');
	this.imageContainer.append(this.image2);

	$(this.image1).css({
		'position': 'absolute',
		'top': '0px',
		'left': '0px'
	});

	$(this.image2).css({
		'position': 'absolute',
		'top': '0px',
		'left': '0px'
	});

	// Should we auto start the slider?
	if(this.options.autoplay)
		this.start();
};

flux.slider.prototype = {
	constructor: flux.slider,
	start: function() {
		var _this = this;
		this.interval = setInterval(function() {
			_this.transition();
		}, this.options.delay);
	},
	stop: function() {
		clearInterval(this.interval);
	},
	next: function() {
		this.showImage(this.currentImageIndex+1);
	},
	prev: function() {
		this.showImage(this.currentImageIndex-1);
	},
	showImage: function(index) {
		this.setNextIndex(index);

		this.stop();
		this.setupImages();
		this.transition();

		if(this.options.autoplay)
			this.start();
	},
	finishedLoading: function() {
		var _this = this;

		this.container.css({
			width: this.width+'px',
			height: this.height+'px'
		});

		this.imageContainer.removeClass('loading');

		// Should we setup a pagination view?
		if(this.options.pagination)
		{
			// TODO: Attach to touch events if appropriate
			this.pagination = $('

').css({
				margin: '0px',
				padding: '0px',
				'text-align': 'center'
			});

			this.pagination.bind('click', function(event){
				event.preventDefault();
				_this.showImage($(event.target).data('index'));
			});

			$(this.images).each(function(index, image){
				var li = $('
'+(index+1)+'
').css({
					display: 'inline-block',
					'margin-left': '0.5em',
					'cursor': 'pointer'
				});

				_this.pagination.append(li);

				if(index == 0)
					li.css('margin-left', 0).addClass('current');
			});

			this.container.append(this.pagination);
		}

		// Resize
		$(this.imageContainer).css({
			width: this.width+'px',
			height: this.height+'px'
		});

		$(this.image1).css({
			width: this.width+'px',
			height: this.height+'px'
		});

		$(this.image2).css({
			width: this.width+'px',
			height: this.height+'px'
		});

		this.container.css({
			width: this.width+'px',
			height: this.height+(this.options.pagination?this.pagination.height():0)+'px'
		});
	},
	setupImages: function() {
		this.image1.css({
			'background-image': 'url("'+this.getImage(this.currentImageIndex).src+'")',
			'z-index': 101
		}).children().remove();

		this.image2.css({
			'background-image': 'url("'+this.getImage(this.nextImageIndex).src+'")',
			'z-index': 100
		});

		if(this.options.pagination)
		{
			this.pagination.find('li.current').removeClass('current');
			$(this.pagination.find('li')[this.currentImageIndex]).addClass('current');
		}
	},
	transition: function() {
		// Pick a transition at random
		var index = Math.floor(Math.random()*(this.options.transitions.length));

		var tran = new flux.transitions[this.options.transitions[index]](this);
		//var tran = new flux.transitions.concentric(this);
		tran.run();

		this.currentImageIndex = this.nextImageIndex;
		this.setNextIndex(this.currentImageIndex+1);
	},
	getImage: function(index) {
		index = index % this.images.length;

		return this.images[index];
	},
	setNextIndex: function(nextIndex)
	{
		if(nextIndex == undefined)
			nextIndex = this.currentImageIndex+1;

		this.nextImageIndex = nextIndex;

		if(this.nextImageIndex > this.images.length-1)
			this.nextImageIndex = 0;

		if(this.nextImageIndex < 0)
			this.nextImageIndex = this.images.length-1;
	},
	increment: function() {
		this.currentImageIndex++;
		if(this.currentImageIndex > this.images.length-1)
			this.currentImageIndex = 0;
	}
}<|endoftext|>


<commit_before>(function() {
    //functions managing & updating the game state data, consisting of the `store` object and
    //visualization in the browser.

    var Diversibee = {};

    /**
     * Private methods
     */
    function setUpBoardState(cellsWide, cellsHigh) {
        // initializes board state

        var cells = [],
            distanceFromSeedCell,
            isForest,
            i,
            //The likelihood a cell contains a 'seed'; probability bound 0 <= seedRate <= 1
            seedRate = 0.02,
            //Cells containing an initial 'seed'. Seeds are used to determine the inital forest growth
            seededCells = generateUniqueRandomCells(cellsHigh, cellsWide, (cellsHigh*cellsWide*seedRate));

        for(i=0; i<cellsWide*cellsHigh; i++) {
            distanceFromSeedCell = distancefromSeed(i, cellsWide, seededCells);
            //Does this cell initially become a forest based on the cell's seed proximity
            isForest = shouldGrowForest(distanceFromSeedCell);

            if(isForest) {
                cells[i] = {
                    type: 'forest',
                    beePop: [Math.floor(Math.random()*1000), Math.floor(Math.random()*1000), Math.floor(Math.random()*1000), Math.floor(Math.random()*1000)],
                    beeGrowth: [0,0,0,0]
                };
            } else {
                cells[i] = {
                    type: 'grass',
                    beePop: [0,0,0,0],
                    beeGrowth: [0,0,0,0]
                };
            }
        }

        return cells;
    }

    function setAnimation(i) {
        //set up the animation for cell i

        Diversibee.store.mapCell[i] = new createjs.Sprite(Diversibee.store.spriteSheet, Diversibee.store.state[i].type);
        Diversibee.store.mapCell[i].x = 20*(i%Diversibee.store.width);
        Diversibee.store.mapCell[i].y = 20*Math.floor(i/Diversibee.store.width);

        Diversibee.store.mapCell[i].addEventListener('mousedown', clickCell.bind(this, i));
        Diversibee.store.mapCell[i].addEventListener('mouseover', handleMouseOver(i));

        Diversibee.store.stage.addChild(Diversibee.store.mapCell[i]);
        Diversibee.store.mapCell[i].play(Diversibee.store.state[i].type);
    }

    function repaintBoard() {
        //re-paints the board state

        if(Diversibee.store.animationLoop) {
            clearInterval(Diversibee.store.animationLoop);
        }

        Diversibee.store.stage.update();
        Diversibee.store.animationLoop = setInterval(function() {Diversibee.store.stage.update();}, 300);
    }

    function handleMouseOver(i) {
        return function(e) {
            if (e.nativeEvent.buttons === 1 || e.nativeEvent.buttons === 3) {
                clickCell(i);
            }
            showStats(i);
        };
    }

    function clickCell(i) {
        //handle when a player clicks on the ith cell

        //change the clicked cell to a blueberry patch
        if(Diversibee.store.cash >=100 && Diversibee.store.state[i].type !== 'blueberries') {
            Diversibee.store.state[i] = {
                type: 'blueberries',
                beePop: [0,0,0,0],
                beeGrowth: [0,0,0,0]
            };
            updateCash(-1*Diversibee.store.blueberryBuildPrice);
            setAnimation(i);
            repaintBoard();
        }
    }

    function updateCash(income) {
        //update the farmer's bank account

        Diversibee.store.cash += income;
        document.getElementById('profitText').innerHTML = '$'+Diversibee.store.cash;
    }

    function advanceTurn() {
        //trigger to advance to next turn

        var i;

        //update bee populations in forests & gather profits from blueberries
        for(i=0; i<Diversibee.store.width*Diversibee.store.height; i++) {
            if(Diversibee.store.state[i].type === 'forest') {
                updateBeePop(i);
                updateBeeGrowth(i, Utils.adjacentCells(i));
            } else if(Diversibee.store.state[i].type === 'blueberries') {
                updateProfits(i, Utils.adjacentCells(i));
            }
        }
    }

    function updateBeePop(i) {
        //update the bee population in cell i based on its current growth rates

        var j;

        for(j=0; j<Diversibee.store.state[i].beePop.length; j++) {
            Diversibee.store.state[i].beePop[j] += Diversibee.store.state[i].beeGrowth[j];
            Diversibee.store.state[i].beePop[j] = Math.max(0, Diversibee.store.state[i].beePop[j]);
        }
    }

    function updateBeeGrowth(i, neighbours) {
        //update the growth rate for the bee populations in cell i based on effects from the cell's nearest neighbours

        BeeDynamics.baseBeeSpawning(i);
        BeeDynamics.forestEdgeEffect(i, neighbours);
    }

    function updateProfits(i, neighbours) {
        //collect profits from cell i, modified by its nearest neghbours

        updateCash(Profits.basicProfits(neighbours));
    }

    function distancefromSeed(cell, width, seededCells) {
        //Returns the distance from a cell to the closest seed (seeds are given as an array of cell locations)
        var distance = Number.MAX_SAFE_INTEGER,
            i,
            temp_dist;

        for(i=0; i<seededCells.length; i++) {
            temp_dist = distanceBetweenCells(seededCells[i], cell, width);
            distance = Math.min(distance, temp_dist);
        }
        return distance;
    }

    function distanceBetweenCells(cellA, cellB, boardWidth) {
        //returns the distance between cell A and cell B

        var xDiff = (cellA % boardWidth) - (cellB % boardWidth),
            yDiff = (Math.floor(cellA / boardWidth)) - (Math.floor(cellB / boardWidth));
        return Math.abs(xDiff) + Math.abs(yDiff);
    }

    function shouldGrowForest(distanceFromSeed) {
        //A simple forest growth algorithm which clusters around seed locations

        return distanceFromSeed + distanceFromSeed * Math.random() < 5;
    }

    function generateUniqueRandomCells(height, width, numberOfCells) {
        //Returns an array of unique random cells

        var uniqueRandomCells = [],
            i,
            //we can safely choose the first unique random cell number
            cellNumber = Math.floor(Math.random() * (height * width));

        for(i=0; i<numberOfCells; i++) {
            while ( Utils.inArray(cellNumber, uniqueRandomCells) ) {
                cellNumber = Math.floor(Math.random() * (height * width));
            }
            uniqueRandomCells.push(cellNumber);
        }
        return uniqueRandomCells;
    }

    /**
     * Public methods
     */
    Diversibee.init = function(width, height) {
        // Initialize the play area on load

        var i;

        //declare global store with default values
        Diversibee.store.width = width;
        Diversibee.store.height = height;
        Diversibee.store.state = setUpBoardState(Diversibee.store.width, Diversibee.store.height);
        Diversibee.store.animationData = {
            images: ['img/spriteSheet.png'],
            frames: {width:20, height:20},
            animations: {
                grass: [0,3],
                forest: [4,7],
                blueberries: [8,11]
            }
        };
        Diversibee.store.stage = new createjs.Stage('board');
        Diversibee.store.stage.enableMouseOver(10);
        Diversibee.store.mapCell = [];
        Diversibee.store.spriteSheet = new createjs.SpriteSheet(Diversibee.store.animationData);
        Diversibee.store.w = Diversibee.store.stage.canvas.width;
        Diversibee.store.h = Diversibee.store.stage.canvas.height;
        Diversibee.store.cash = 1000;
        Diversibee.store.blueberryBuildPrice = 100;

        //set up initial cell animationsL
        for(i=0; i<Diversibee.store.width*Diversibee.store.height; i++) {
            setAnimation(i);
        }
        //paint the initial state of the board
        repaintBoard();

        //trigger a turn to finish configuring the board
        Diversibee.advanceTurn();
    };

    Diversibee.advanceTurn = function() {
        //trigger to advance to next turn

        var i;

        //update bee populations in forests & gather profits from blueberries
        for(i=0; i<Diversibee.store.width*Diversibee.store.height; i++) {
            if(Diversibee.store.state[i].type === 'forest') {
                updateBeePop(i);
                updateBeeGrowth(i, Utils.adjacentCells(i));
            } else if(Diversibee.store.state[i].type === 'blueberries') {
                updateProfits(i, Utils.adjacentCells(i));
            }
        }
    };

    Diversibee.store = {};

    window.Diversibee = Diversibee;
})();
<commit_msg>Removed status() function on mouse click handle event<commit_after>(function() {
    //functions managing & updating the game state data, consisting of the `store` object and
    //visualization in the browser.

    var Diversibee = {};

    /**
     * Private methods
     */
    function setUpBoardState(cellsWide, cellsHigh) {
        // initializes board state

        var cells = [],
            distanceFromSeedCell,
            isForest,
            i,
            //The likelihood a cell contains a 'seed'; probability bound 0 <= seedRate <= 1
            seedRate = 0.02,
            //Cells containing an initial 'seed'. Seeds are used to determine the inital forest growth
            seededCells = generateUniqueRandomCells(cellsHigh, cellsWide, (cellsHigh*cellsWide*seedRate));

        for(i=0; i<cellsWide*cellsHigh; i++) {
            distanceFromSeedCell = distancefromSeed(i, cellsWide, seededCells);
            //Does this cell initially become a forest based on the cell's seed proximity
            isForest = shouldGrowForest(distanceFromSeedCell);

            if(isForest) {
                cells[i] = {
                    type: 'forest',
                    beePop: [Math.floor(Math.random()*1000), Math.floor(Math.random()*1000), Math.floor(Math.random()*1000), Math.floor(Math.random()*1000)],
                    beeGrowth: [0,0,0,0]
                };
            } else {
                cells[i] = {
                    type: 'grass',
                    beePop: [0,0,0,0],
                    beeGrowth: [0,0,0,0]
                };
            }
        }

        return cells;
    }

    function setAnimation(i) {
        //set up the animation for cell i

        Diversibee.store.mapCell[i] = new createjs.Sprite(Diversibee.store.spriteSheet, Diversibee.store.state[i].type);
        Diversibee.store.mapCell[i].x = 20*(i%Diversibee.store.width);
        Diversibee.store.mapCell[i].y = 20*Math.floor(i/Diversibee.store.width);

        Diversibee.store.mapCell[i].addEventListener('mousedown', clickCell.bind(this, i));
        Diversibee.store.mapCell[i].addEventListener('mouseover', handleMouseOver(i));

        Diversibee.store.stage.addChild(Diversibee.store.mapCell[i]);
        Diversibee.store.mapCell[i].play(Diversibee.store.state[i].type);
    }

    function repaintBoard() {
        //re-paints the board state

        if(Diversibee.store.animationLoop) {
            clearInterval(Diversibee.store.animationLoop);
        }

        Diversibee.store.stage.update();
        Diversibee.store.animationLoop = setInterval(function() {Diversibee.store.stage.update();}, 300);
    }

    function handleMouseOver(i) {
        return function(e) {
            if (e.nativeEvent.buttons === 1 || e.nativeEvent.buttons === 3) {
                clickCell(i);
            }
        };
    }

    function clickCell(i) {
        //handle when a player clicks on the ith cell

        //change the clicked cell to a blueberry patch
        if(Diversibee.store.cash >=100 && Diversibee.store.state[i].type !== 'blueberries') {
            Diversibee.store.state[i] = {
                type: 'blueberries',
                beePop: [0,0,0,0],
                beeGrowth: [0,0,0,0]
            };
            updateCash(-1*Diversibee.store.blueberryBuildPrice);
            setAnimation(i);
            repaintBoard();
        }
    }

    function updateCash(income) {
        //update the farmer's bank account

        Diversibee.store.cash += income;
        document.getElementById('profitText').innerHTML = '$'+Diversibee.store.cash;
    }

    function advanceTurn() {
        //trigger to advance to next turn

        var i;

        //update bee populations in forests & gather profits from blueberries
        for(i=0; i<Diversibee.store.width*Diversibee.store.height; i++) {
            if(Diversibee.store.state[i].type === 'forest') {
                updateBeePop(i);
                updateBeeGrowth(i, Utils.adjacentCells(i));
            } else if(Diversibee.store.state[i].type === 'blueberries') {
                updateProfits(i, Utils.adjacentCells(i));
            }
        }
    }

    function updateBeePop(i) {
        //update the bee population in cell i based on its current growth rates

        var j;

        for(j=0; j<Diversibee.store.state[i].beePop.length; j++) {
            Diversibee.store.state[i].beePop[j] += Diversibee.store.state[i].beeGrowth[j];
            Diversibee.store.state[i].beePop[j] = Math.max(0, Diversibee.store.state[i].beePop[j]);
        }
    }

    function updateBeeGrowth(i, neighbours) {
        //update the growth rate for the bee populations in cell i based on effects from the cell's nearest neighbours

        BeeDynamics.baseBeeSpawning(i);
        BeeDynamics.forestEdgeEffect(i, neighbours);
    }

    function updateProfits(i, neighbours) {
        //collect profits from cell i, modified by its nearest neghbours

        updateCash(Profits.basicProfits(neighbours));
    }

    function distancefromSeed(cell, width, seededCells) {
        //Returns the distance from a cell to the closest seed (seeds are given as an array of cell locations)
        var distance = Number.MAX_SAFE_INTEGER,
            i,
            temp_dist;

        for(i=0; i<seededCells.length; i++) {
            temp_dist = distanceBetweenCells(seededCells[i], cell, width);
            distance = Math.min(distance, temp_dist);
        }
        return distance;
    }

    function distanceBetweenCells(cellA, cellB, boardWidth) {
        //returns the distance between cell A and cell B

        var xDiff = (cellA % boardWidth) - (cellB % boardWidth),
            yDiff = (Math.floor(cellA / boardWidth)) - (Math.floor(cellB / boardWidth));
        return Math.abs(xDiff) + Math.abs(yDiff);
    }

    function shouldGrowForest(distanceFromSeed) {
        //A simple forest growth algorithm which clusters around seed locations

        return distanceFromSeed + distanceFromSeed * Math.random() < 5;
    }

    function generateUniqueRandomCells(height, width, numberOfCells) {
        //Returns an array of unique random cells

        var uniqueRandomCells = [],
            i,
            //we can safely choose the first unique random cell number
            cellNumber = Math.floor(Math.random() * (height * width));

        for(i=0; i<numberOfCells; i++) {
            while ( Utils.inArray(cellNumber, uniqueRandomCells) ) {
                cellNumber = Math.floor(Math.random() * (height * width));
            }
            uniqueRandomCells.push(cellNumber);
        }
        return uniqueRandomCells;
    }

    /**
     * Public methods
     */
    Diversibee.init = function(width, height) {
        // Initialize the play area on load

        var i;

        //declare global store with default values
        Diversibee.store.width = width;
        Diversibee.store.height = height;
        Diversibee.store.state = setUpBoardState(Diversibee.store.width, Diversibee.store.height);
        Diversibee.store.animationData = {
            images: ['img/spriteSheet.png'],
            frames: {width:20, height:20},
            animations: {
                grass: [0,3],
                forest: [4,7],
                blueberries: [8,11]
            }
        };
        Diversibee.store.stage = new createjs.Stage('board');
        Diversibee.store.stage.enableMouseOver(10);
        Diversibee.store.mapCell = [];
        Diversibee.store.spriteSheet = new createjs.SpriteSheet(Diversibee.store.animationData);
        Diversibee.store.w = Diversibee.store.stage.canvas.width;
        Diversibee.store.h = Diversibee.store.stage.canvas.height;
        Diversibee.store.cash = 1000;
        Diversibee.store.blueberryBuildPrice = 100;

        //set up initial cell animationsL
        for(i=0; i<Diversibee.store.width*Diversibee.store.height; i++) {
            setAnimation(i);
        }
        //paint the initial state of the board
        repaintBoard();

        //trigger a turn to finish configuring the board
        Diversibee.advanceTurn();
    };

    Diversibee.advanceTurn = function() {
        //trigger to advance to next turn

        var i;

        //update bee populations in forests & gather profits from blueberries
        for(i=0; i<Diversibee.store.width*Diversibee.store.height; i++) {
            if(Diversibee.store.state[i].type === 'forest') {
                updateBeePop(i);
                updateBeeGrowth(i, Utils.adjacentCells(i));
            } else if(Diversibee.store.state[i].type === 'blueberries') {
                updateProfits(i, Utils.adjacentCells(i));
            }
        }
    };

    Diversibee.store = {};

    window.Diversibee = Diversibee;
})();
<|endoftext|>


<commit_before>var disease = (function(module) {

    /**
     * @param parentEl the selector for the element into which the vennDiagramView will be placed
     * @param graph data
     * @param totalPopulation some big number
     */
    module.vennDiagramView = function(parentEl, graph, totalPopulation) {

        var margin = {top: 10, right: 10, bottom: 10, left: 10};

        /** all circles will be relative to the test positive circle */
        var TEST_POS_CIRCLE_RADIUS = 200;

        var my = {};

        /** Add the initial svg structure */
        function init() {
            // append the svg canvas to the page
            var rootSvg = d3.selectAll(parentEl).append("svg");
            var svg = rootSvg.append("g")
                .attr("transform",
                    "translate(" + margin.left + "," + margin.top + ")");

            var mask = svg.append("defs")
                .append("mask")
                .attr("id", "test-pos-mask");
            mask.append("circle")
                .attr("class", "population-circle")
                .style("fill", "#ffffff");
            mask.append("circle")
                .attr("class", "test-positive-circle")
                .style("fill", "#000000");

            svg.append("circle")
                .attr("class", "population-circle")
                .attr("opacity", 0.3).attr("fill", disease.TEST_NEG_HEALTHY)
                .append("title").text("The whole population. Those outside the red circle are healthy");
            svg.append("circle")
                .attr("class", "test-positive-circle")
                .attr("opacity", 0.4).attr("fill", disease.POSITIVE_COLOR);

            svg.append("circle")
                .attr("class", "diseased-circle")
                .attr("opacity", 0.2).attr("fill", disease.DISEASED_COLOR);
                //.append("title").text("These are healthy, but tested positive");
            svg.append("circle").on("click", function() {alert("hi")})
                .attr("class", "diseased-circle")
                .attr("opacity", 0.6).attr("fill", disease.TEST_NEG_DISEASED)
                .style("mask", "url(#test-pos-mask)")
                .append("title").text("These people have the disease, but they tested negative, so they will die");

            svg.append("circle")
                .attr("class", "test-positive-circle")
                .attr("opacity", 0.0)
                .append("title").text("Tested positive but Healthy");

            svg.append("path")
                .attr("class", "test-positive-diseased-intersection")
                .attr("opacity", 0.4)
                .attr("fill", "#ffaa00")
                .append("title");
        }

        /** update the Venn diagram */
        my.render = function() {
            var chartWidth = $(parentEl).width();
            var chartHeight = $(parentEl).height();
            var chartWidthD2 = chartWidth / 2;
            var chartHeightD2 = chartHeight / 2;

            var svg = d3.select(parentEl + " svg")
                .attr("width", chartWidth)
                .attr("height", chartHeight);

            var numPositiveAndDiseased = graph.links[1].value;
            var numPositiveAndHealthy =  graph.links[2].value;
            var testNegButDiseased = graph.links[0].value;
            var numDiseased = testNegButDiseased + numPositiveAndDiseased;
            var numPositive = numPositiveAndDiseased + numPositiveAndHealthy;

            var testPositiveRad = TEST_POS_CIRCLE_RADIUS;
            var scaleFactor = Math.sqrt(totalPopulation / numPositive);
            var diseasedRad = testPositiveRad * Math.sqrt(numDiseased / numPositive);
            var popRad = testPositiveRad * scaleFactor;
            var popArea = Math.PI = popRad * popRad;
            var diseaseArea = Math.PI * diseasedRad * diseasedRad;
            var overlap = diseaseArea * numPositiveAndDiseased / numDiseased;

            console.log("diseaseArea= " + diseaseArea + " popArea= "+ popArea
                + " numDiseased= " + numDiseased + " pop= " + totalPopulation
                + " rat1=" + diseaseArea/popArea + " rat2="+ numDiseased/totalPopulation);

            //console.log("numPositiveAndDiseased = " + numPositiveAndDiseased + " numDiseased = " + numDiseased + " overlap="+ overlap);
            var distance = findCircleSeparation({
                radiusA: testPositiveRad,
                radiusB: diseasedRad,
                overlap: overlap
            });
            //console.log("dist=" + distance);
            var centerX = chartWidthD2 + testPositiveRad - 80;
            var diseasedCenterY = chartHeightD2 - distance;

            svg.selectAll("circle.population-circle")
                .attr("cx", Math.max(chartWidthD2 - popRad, 0) + popRad + 40)
                .attr("cy", chartHeightD2)
                .attr("r", popRad);
            svg.selectAll("circle.test-positive-circle")
                .attr("cx", centerX)
                .attr("cy", chartHeightD2)
                .attr("r", testPositiveRad);
            svg.selectAll("circle.diseased-circle")
                .attr("cx", centerX)
                .attr("cy", diseasedCenterY)
                .attr("r", diseasedRad);

            // See https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths
            var path = svg.selectAll("path.test-positive-diseased-intersection");
            path.attr("d", pathFunc(centerX, chartHeightD2, testPositiveRad,
                    centerX, diseasedCenterY, diseasedRad));
            path.select("title").text("Tested positive and they have the Disease ("+ numPositiveAndHealthy +")" );

        };

        var pathFunc = function(x1, y1, rad1, x2, y2, rad2) {
            var interPoints = circleIntersection(x1, y1, rad1,  x2, y2, rad2);
            return "M" + interPoints[0] + "," + interPoints[2] + "A" + rad2 + "," + rad2 +
                " 0 1,1 " + interPoints[1] + "," + interPoints[3]+ "A" + rad1 + "," + rad1 +
                " 0 0,1 " + interPoints[0] + "," + interPoints[2];
        };

        /**
         * Given circle A with radiusA, and circle B with radiusB, and a desired amount of overlap,
         * find the distance between the center of A and B.
         *
         * Circle A is at the origin. Circle B starts radA + radB to the right where intersection = 0.
         * When they are both at the origin, the overlap is min(areaA, areaB)
         * Move them closer until the amount of overlap is equal to the support.
         *
         * @param circleInfo radiusA, radiusB, overlap
         * @return the center distance between circles A and B.
         */
        var findCircleSeparation = function(circleInfo) {

            var radA = circleInfo.radiusA;
            var radB = circleInfo.radiusB;
            var radAsq = radA * radA;
            var radBsq = radB * radB;
            var maxDistance = radA + radB;
            var maxOverlap = Math.PI * Math.min(radAsq, radBsq);
            //console.log("radA=" + radA + " radB="+ radB + " maxDist=" + maxDistance + " maxOver="+ maxOverlap + " overlap=" + circleInfo.overlap);

            // This function returns the area of intersection when the two circles are x apart.
            var y = function (x) {
                if (x == 0) {
                    return maxOverlap;
                }
                var cosCBD = (radBsq + x * x - radAsq) / (2.0 * radB * x);
                var cosCAD = (radAsq + x * x - radBsq) / (2.0 * radA * x);
                if (Math.abs(cosCBD) > 1 || Math.abs(cosCAD) > 1) {
                    // then the two circles do not intersect at all
                    return maxOverlap;
                }
                var angleCBD = 2.0 * Math.acos(cosCBD);
                var angleCAD = 2.0 * Math.acos(cosCAD);
                return 0.5 * (angleCBD * radBsq - radBsq * Math.sin(angleCBD)
                    + angleCAD * radAsq - radAsq * Math.sin(angleCAD));
            };

            return findXForY(circleInfo.overlap, y, maxDistance, maxOverlap);
        };

        /**
         * @param overlap the overlapping area value we want to find x for.
         * @param y the function of x that will yield the support value.
         * @param maxDistance the maximum distance the two circles can be appart before they no longer overlap.
         * @param maxOverlap the maximum amount of overlap possible. The min of the two circle areas.
         * @return the x value for the given y(x)
         */
        var findXForY = function(overlap, y, maxDistance, maxOverlap) {

            // if they totally overlay, then we know the distance is 0;
            if (overlap == maxOverlap) {
                return 0;
            }
            var lower = 0;
            var upper = maxDistance;
            var currentGuess = maxDistance / 2.0;
            var currentY = y(currentGuess);
            if (isNaN(currentY)) {
                throw "y is NaN for " + currentGuess;
            }
            var EPS = 0.1;
            // if an answer is not found after 20 iterations something is wrong
            var MAX_ITERATIONS = 20;
            var ct = 0;

            while (Math.abs(overlap - currentY) > EPS && ct++ < MAX_ITERATIONS) {
                if (currentY > overlap) {
                    // then move circles further apart
                    currentGuess = (upper + currentGuess) / 2;
                }
                else {
                    // then move circles closer together
                    currentGuess = (lower + currentGuess) / 2;
                }
                currentY = y(currentGuess);
                if (currentY > overlap) {
                    lower = currentGuess;
                }
                else {
                    upper = currentGuess;
                }
            }

            if (ct >= MAX_ITERATIONS) {
                throw "It was not possible to find the separation for overlap = " + overlap + " when maxOverlap is "
                + maxOverlap + " and maxDistance is " + maxDistance + ". Current range = [" + lower + ", " + upper + "]";
            }
            return currentGuess;
        };

        /**
         * @return the points that define the intersection region of two circles.
         */
        function circleIntersection(x0, y0, r0, x1, y1, r1) {
            var a, dx, dy, distance, h, rx, ry;
            var x2, y2;

            dx = x1 - x0;
            dy = y1 - y0;
            distance = Math.sqrt((dy * dy) + (dx * dx));

            if (distance > (r0 + r1)) {
                throw "No solution. circles do not intersect";
            }
            if (distance < Math.abs(r0 - r1)) {
                throw "No solution. one circle is contained in the other"
            }

            // Determine the distance from point 0 to point 2.
            // point 2 is the point where the line through the circle
            //intersection points crosses the line between the circle centers.
            a = ((r0 * r0) - (r1 * r1) + (distance * distance)) / (2.0 * distance);

            // Determine the coordinates of point 2.
            x2 = x0 + (dx * a / distance);
            y2 = y0 + (dy * a / distance);

            // Determine the distance from point 2 to either of the
            // intersection points.
            h = Math.sqrt((r0 * r0) - (a * a));

            // Determine the offsets of the intersection points from point 2.
            rx = -dy * (h / distance);
            ry = dx * (h / distance);

            // Determine the absolute intersection points.
            var xi = x2 + rx;
            var xi_prime = x2 - rx;
            var yi = y2 + ry;
            var yi_prime = y2 - ry;

            return [xi, xi_prime, yi, yi_prime];
        }

        init();
        return my;
    };

    return module;
} (disease || {}));<commit_msg>add numbers to tooltips.<commit_after>var disease = (function(module) {

    /**
     * @param parentEl the selector for the element into which the vennDiagramView will be placed
     * @param graph data
     * @param totalPopulation some big number
     */
    module.vennDiagramView = function(parentEl, graph, totalPopulation) {

        var margin = {top: 10, right: 10, bottom: 10, left: 10};

        /** all circles will be relative to the test positive circle */
        var TEST_POS_CIRCLE_RADIUS = 200;

        var my = {};

        /** Add the initial svg structure */
        function init() {
            // append the svg canvas to the page
            var rootSvg = d3.selectAll(parentEl).append("svg");
            var svg = rootSvg.append("g")
                .attr("transform",
                    "translate(" + margin.left + "," + margin.top + ")");

            var mask = svg.append("defs")
                .append("mask")
                .attr("id", "test-pos-mask");
            mask.append("circle")
                .attr("class", "population-circle")
                .style("fill", "#ffffff");
            mask.append("circle")
                .attr("class", "test-positive-circle")
                .style("fill", "#000000");

            svg.append("circle")
                .attr("class", "population-circle")
                .attr("opacity", 0.3).attr("fill", disease.TEST_NEG_HEALTHY)
                .append("title").text("The whole population. Those outside the red circle are healthy");
            svg.append("circle")
                .attr("class", "test-positive-circle")
                .attr("opacity", 0.4).attr("fill", disease.POSITIVE_COLOR);

            svg.append("circle")
                .attr("class", "diseased-circle")
                .attr("opacity", 0.2).attr("fill", disease.DISEASED_COLOR)
                .append("title");
            svg.append("circle").on("click", function() {alert("hi")})
                .attr("class", "diseased-circle")
                .attr("opacity", 0.6).attr("fill", disease.TEST_NEG_DISEASED)
                .style("mask", "url(#test-pos-mask)")
                .append("title").text("These people have the disease, but they tested negative, so they will die");

            svg.append("circle")
                .attr("class", "test-positive-circle")
                .attr("opacity", 0.0)
                .append("title").text("Tested positive but Healthy");

            svg.append("path")
                .attr("class", "test-positive-diseased-intersection")
                .attr("opacity", 0.4)
                .attr("fill", "#ffaa00")
                .append("title");
        }

        /** update the Venn diagram */
        my.render = function() {
            var chartWidth = $(parentEl).width();
            var chartHeight = $(parentEl).height();
            var chartWidthD2 = chartWidth / 2;
            var chartHeightD2 = chartHeight / 2;

            var svg = d3.select(parentEl + " svg")
                .attr("width", chartWidth)
                .attr("height", chartHeight);

            var numPositiveAndDiseased = graph.links[1].value;
            var numPositiveAndHealthy =  graph.links[2].value;
            var testNegButDiseased = Math.round(graph.links[0].value);
            var numDiseased = testNegButDiseased + numPositiveAndDiseased;
            var numPositive = numPositiveAndDiseased + numPositiveAndHealthy;

            var testPositiveRad = TEST_POS_CIRCLE_RADIUS;
            var scaleFactor = Math.sqrt(totalPopulation / numPositive);
            var diseasedRad = testPositiveRad * Math.sqrt(numDiseased / numPositive);
            var popRad = testPositiveRad * scaleFactor;
            var popArea = Math.PI = popRad * popRad;
            var diseaseArea = Math.PI * diseasedRad * diseasedRad;
            var overlap = diseaseArea * numPositiveAndDiseased / numDiseased;

            console.log("diseaseArea= " + diseaseArea + " popArea= "+ popArea
                + " numDiseased= " + numDiseased + " pop= " + totalPopulation
                + " rat1=" + diseaseArea/popArea + " rat2="+ numDiseased/totalPopulation);

            //console.log("numPositiveAndDiseased = " + numPositiveAndDiseased + " numDiseased = " + numDiseased + " overlap="+ overlap);
            var distance = findCircleSeparation({
                radiusA: testPositiveRad,
                radiusB: diseasedRad,
                overlap: overlap
            });
            //console.log("dist=" + distance);
            var centerX = chartWidthD2 + testPositiveRad - 80;
            var diseasedCenterY = chartHeightD2 - distance;

            svg.selectAll("circle.population-circle")
                .attr("cx", Math.max(chartWidthD2 - popRad, 0) + popRad + 40)
                .attr("cy", chartHeightD2)
                .attr("r", popRad);
            svg.selectAll("circle.test-positive-circle")
                .attr("cx", centerX)
                .attr("cy", chartHeightD2)
                .attr("r", testPositiveRad);
            svg.selectAll("circle.diseased-circle")
                .attr("cx", centerX)
                .attr("cy", diseasedCenterY)
                .attr("r", diseasedRad)
                .select("title").text("These are healthy, but tested positive ("
                     + testNegButDiseased.toLocaleString() + ")");

            // See https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths
            var path = svg.selectAll("path.test-positive-diseased-intersection");
            path.attr("d", pathFunc(centerX, chartHeightD2, testPositiveRad,
                    centerX, diseasedCenterY, diseasedRad));
            path.select("title").text("Tested positive and they have the Disease ("
                + numPositiveAndDiseased.toLocaleString() +")" );

        };

        var pathFunc = function(x1, y1, rad1, x2, y2, rad2) {
            var interPoints = circleIntersection(x1, y1, rad1,  x2, y2, rad2);
            return "M" + interPoints[0] + "," + interPoints[2] + "A" + rad2 + "," + rad2 +
                " 0 1,1 " + interPoints[1] + "," + interPoints[3]+ "A" + rad1 + "," + rad1 +
                " 0 0,1 " + interPoints[0] + "," + interPoints[2];
        };

        /**
         * Given circle A with radiusA, and circle B with radiusB, and a desired amount of overlap,
         * find the distance between the center of A and B.
         *
         * Circle A is at the origin. Circle B starts radA + radB to the right where intersection = 0.
         * When they are both at the origin, the overlap is min(areaA, areaB)
         * Move them closer until the amount of overlap is equal to the support.
         *
         * @param circleInfo radiusA, radiusB, overlap
         * @return the center distance between circles A and B.
         */
        var findCircleSeparation = function(circleInfo) {

            var radA = circleInfo.radiusA;
            var radB = circleInfo.radiusB;
            var radAsq = radA * radA;
            var radBsq = radB * radB;
            var maxDistance = radA + radB;
            var maxOverlap = Math.PI * Math.min(radAsq, radBsq);
            //console.log("radA=" + radA + " radB="+ radB + " maxDist=" + maxDistance + " maxOver="+ maxOverlap + " overlap=" + circleInfo.overlap);

            // This function returns the area of intersection when the two circles are x apart.
            var y = function (x) {
                if (x == 0) {
                    return maxOverlap;
                }
                var cosCBD = (radBsq + x * x - radAsq) / (2.0 * radB * x);
                var cosCAD = (radAsq + x * x - radBsq) / (2.0 * radA * x);
                if (Math.abs(cosCBD) > 1 || Math.abs(cosCAD) > 1) {
                    // then the two circles do not intersect at all
                    return maxOverlap;
                }
                var angleCBD = 2.0 * Math.acos(cosCBD);
                var angleCAD = 2.0 * Math.acos(cosCAD);
                return 0.5 * (angleCBD * radBsq - radBsq * Math.sin(angleCBD)
                    + angleCAD * radAsq - radAsq * Math.sin(angleCAD));
            };

            return findXForY(circleInfo.overlap, y, maxDistance, maxOverlap);
        };

        /**
         * @param overlap the overlapping area value we want to find x for.
         * @param y the function of x that will yield the support value.
         * @param maxDistance the maximum distance the two circles can be appart before they no longer overlap.
         * @param maxOverlap the maximum amount of overlap possible. The min of the two circle areas.
         * @return the x value for the given y(x)
         */
        var findXForY = function(overlap, y, maxDistance, maxOverlap) {

            // if they totally overlay, then we know the distance is 0;
            if (overlap == maxOverlap) {
                return 0;
            }
            var lower = 0;
            var upper = maxDistance;
            var currentGuess = maxDistance / 2.0;
            var currentY = y(currentGuess);
            if (isNaN(currentY)) {
                throw "y is NaN for " + currentGuess;
            }
            var EPS = 0.1;
            // if an answer is not found after 20 iterations something is wrong
            var MAX_ITERATIONS = 20;
            var ct = 0;

            while (Math.abs(overlap - currentY) > EPS && ct++ < MAX_ITERATIONS) {
                if (currentY > overlap) {
                    // then move circles further apart
                    currentGuess = (upper + currentGuess) / 2;
                }
                else {
                    // then move circles closer together
                    currentGuess = (lower + currentGuess) / 2;
                }
                currentY = y(currentGuess);
                if (currentY > overlap) {
                    lower = currentGuess;
                }
                else {
                    upper = currentGuess;
                }
            }

            if (ct >= MAX_ITERATIONS) {
                throw "It was not possible to find the separation for overlap = " + overlap + " when maxOverlap is "
                + maxOverlap + " and maxDistance is " + maxDistance + ". Current range = [" + lower + ", " + upper + "]";
            }
            return currentGuess;
        };

        /**
         * @return the points that define the intersection region of two circles.
         */
        function circleIntersection(x0, y0, r0, x1, y1, r1) {
            var a, dx, dy, distance, h, rx, ry;
            var x2, y2;

            dx = x1 - x0;
            dy = y1 - y0;
            distance = Math.sqrt((dy * dy) + (dx * dx));

            if (distance > (r0 + r1)) {
                throw "No solution. circles do not intersect";
            }
            if (distance < Math.abs(r0 - r1)) {
                throw "No solution. one circle is contained in the other"
            }

            // Determine the distance from point 0 to point 2.
            // point 2 is the point where the line through the circle
            //intersection points crosses the line between the circle centers.
            a = ((r0 * r0) - (r1 * r1) + (distance * distance)) / (2.0 * distance);

            // Determine the coordinates of point 2.
            x2 = x0 + (dx * a / distance);
            y2 = y0 + (dy * a / distance);

            // Determine the distance from point 2 to either of the
            // intersection points.
            h = Math.sqrt((r0 * r0) - (a * a));

            // Determine the offsets of the intersection points from point 2.
            rx = -dy * (h / distance);
            ry = dx * (h / distance);

            // Determine the absolute intersection points.
            var xi = x2 + rx;
            var xi_prime = x2 - rx;
            var yi = y2 + ry;
            var yi_prime = y2 - ry;

            return [xi, xi_prime, yi, yi_prime];
        }

        init();
        return my;
    };

    return module;
} (disease || {}));<|endoftext|>


<commit_before>var app = app || {};

app.UserEditView = Backbone.View.extend({
    template: _.template($('#user-edit-template').html()),
    tagName: 'div',
    className: 'user-edit',

    events: {
        'keyup .search': 'onUsersKeyUp',
        'click #user-select-btn': 'onUserSelectBtnClick',
        'click .save-permissions-btn': 'onSaveClick',
        'click #clear-console': 'onClearConsoleClick',
        'click .export-permissions': 'onExportBtnClick',
        'click .purge-user-btn': 'onPurgeBtnClick',
        'click .search-clear': 'onSearchClear',
        'click .search': 'onSearchClick',
        'blur .search': 'onSearchBlur',
        'focus .search': 'onSearchFocus'
    },

    initialize: function(options) {
        Backbone.pubSub.on('user:selected-permissions-fetched', this.onSelectedPermissionsFetched, this);
        this.state_map = {
            user_permissions: null,
            pendingSaves: 0,
            pendingRemoves: 0,
            totalUpdates: 0,
            progressUpdateRatio: 0,
            currentProgress: 0,
            failed: {
                add: [],
                remove: [],
                purge: []
            },
            success: {
                add: [],
                remove: [],
                purge: []
            }
        }

    },

    render: function() {
        this.$el.html(this.template(this.model.toJSON()));
        this.$users = this.$('#users');
        this.$user_info = this.$('#user-info');
        this.$user_search_container = this.$('.search-container');
        this.$user_search = this.$('.search');
        this.$user_attributes = this.$('.user-attr');
        this.$name = this.$('#name');
        this.$name_container = this.$('#name-container');
        this.$messages = this.$('.messages');
        this.$notify = this.$('.notify');
        this.$progress_meter = this.$('.meter');
        this.$progressbar = this.$('.progress');
        this.$progress_text = this.$('.current-progress');
        this.$search_clear = this.$('.search-clear');
        this.$search = this.$('.search');
        this.$user_select_btn = this.$('#user-select-btn');
        this.$save_btn = this.$('.save-permissions-btn');
        this.$purge_btn = this.$('.purge-btn');
        this.$export_btn = this.$('.export-permissions');

        if (this.model.get('name') == '') {
            this.toggleButtons(false);
        }

        this.libraryViewUsers = new app.LibraryUserView({
            el: this.$users[0],
            collection: app.UserCollection,
            itemView: app.UserView
        });

        this.libraryViewUsers.render();

        Backbone.pubSub.on('user:select', this.userSelect, this);
        if (this.model.get('id') != '') {
            this.userSelect(this.model, false);
        }
        return this;
    },

    search: function() {
        var val = this.$user_search.val(),
            options,
            searchAllAttributes = false;

        if (val.length > 0) {
            this.$search_clear.removeClass('hidden');
        } else {
            this.$search_clear.addClass('hidden');
        }

        //check for search operand character '~'
        if (val.indexOf('~') == 0 && val.length > 1) {
            //set val to exclude '~'
            val = val.substring(1);
            searchAllAttributes = true;
        } else if (val.indexOf('~') == 0) {
            //don't commit to search yet since not enough info has been entered.
            return;
        }

        options = (searchAllAttributes ? {
            val: val
        } : {
            key: 'name',
            val: val
        });

        Backbone.pubSub.trigger('library_users:search', options);
    },
    onSearchClear: function(e) {
        this.$search.val('');
        this.$search.trigger('keyup');
        e.stopPropagation();
        this.$user_search.focus();
    },
    onUsersKeyUp: function(e) {
        this.search();
    },
    onSelectedPermissionsFetched: function(selected_permissions) {
        var user_permissions = new Backbone.Collection(this.model.get('permissions')),
            user_permission_index,
            add_permissions_arr = [],
            remove_permissions_arr = [],
            user = this.model.attributes;

        this.toggleButtons(false);

        //iterate through selected permissions
        selected_permissions.forEach(function(permission) {
            //check if permissions is in user permissions
            //user_permission_index = user_permissions_arr.indexOf(permission);
            user_permission = user_permissions.findWhere({
                name: permission
            });
            if (typeof user_permission !== 'undefined') {
                //remove permission from user permissions
                user_permissions.remove(user_permission);

            } else {
                //add permission to add permission array
                add_permissions_arr.push(permission);
            }
        });
        this.state_map.user_permissions = user_permissions;
        //set remove permissions to the remaining user permissions...for contextual reasons
        remove_permissions_arr = user_permissions.map(function(obj) {
                return obj.get('name');
            }),

            //set state map variables
            this.state_map.pendingSaves = add_permissions_arr.length;
        this.state_map.pendingRemoves = remove_permissions_arr.length;
        this.state_map.totalUpdates = this.state_map.pendingRemoves + this.state_map.pendingSaves;
        this.state_map.progressUpdateRatio = 100 / this.state_map.totalUpdates;
        if (add_permissions_arr.length == 0 && remove_permissions_arr.length == 0) {
            return;
        }

        this.$messages.append('' + app.utility.getDateTime() + '
Modifying [' + user.name + ']\'s permissions
');
        //add user permissions
        if (add_permissions_arr.length > 0) {
            (function(that) {
                app.data.modifyPermissions(add_permissions_arr, 0, user, app.config.url, 'add', function(results) {
                    that.processPermissionModify(results);
                });
            })(this);
        }
        //remove user permissions
        if (remove_permissions_arr.length > 0) {
            (function(that) {
                app.data.modifyPermissions(remove_permissions_arr, 0, user, app.config.url, 'remove', function(results) {
                    that.processPermissionModify(results);
                });
            })(this);
        }
    },

    onSearchClick: function(e) {
        e.stopPropagation();
    },

    onSaveClick: function(e) {
        if ($(e.currentTarget).hasClass('disabled')) {
            return;
        }
        this.resetStateMap();
        //update progress bar
        this.$progress_meter.width('0%');
        this.$progress_text.text('0%');
        //publish user selected event
        Backbone.pubSub.trigger('user:save-permissions');
    },

    onClearConsoleClick: function(e) {
        this.clearConsole();
    },

    onExportBtnClick: function(e) {
        var permissions = (this.model ? this.model.get('permissions') : false),
            ua = window.navigator.userAgent,
            msie = ua.indexOf("MSIE "),
            permissionsElement;

        if ($(e.currentTarget).hasClass('disabled')) {
            return;
        }

        if (msie > 0) { // If Internet Explorer, return version number
            permissionsElement = '
' + this.model.get('name') + '\'s Permissions

';
            permissionsElement += '
' + this.model.get('name') + '
';
            permissionsElement += '
' + this.model.get('loginname') + '
';
            permissionsElement += '
' + this.models.get('email') + '
';

            if (permissions.length > 1) {
                permissionsElement += permissions.reduce(function(memo, obj) {
                    return (typeof memo == "string" ? memo : '
' + memo.name + '
') + '
' + obj.name + '
';
                });
            } else {
                permissionsElement = '
' + permissions[0].name + '
';
            }

            permissionsElement += '
';
            app.utility.printToNewWindow(permissionsElement);
        } else {
            app.utility.JSONToCSVConvertor(permissions, this.model.get('name') + ' Permissions', true);
        }

    },

    onPurgeBtnClick: function(e) {
        var user = (this.model ? this.model.attributes : false);

        if ($(e.currentTarget).hasClass('disabled')) {
            return;
        }

        this.$messages.append('' + app.utility.getDateTime() + '
Purging [' + user.name + ']
');
        this.$messages.scrollTop(this.$messages[0].scrollHeight);
        (function(that) {
            app.data.removeUserFromWeb(app.config.url, user, function(results) {
                that.model.collection.remove(this.model);
                that.onRemoveUserComplete(results);
                app.router.navigate('edit/user/', true);
            });
        })(this);

    },

    onRemoveUserComplete: function(results) {
        var type = results.type,
            data = results.data,
            name = this.model.get('name'),
            message = '';

        if (type != 'error') {
            message = 'Succesfully removed ' + name + ' from site.';
        } else {
            message = 'Unsuccessfully removed ' + name + ' from site.';
        }

        this.$messages.append('' + app.utility.getDateTime() + '
' + message + '
');
        this.$messages.scrollTop(this.$messages[0].scrollHeight);
    },

    clearConsole: function() {
        this.$messages.html('');
    },

    processPermissionModify: function(results) {
        var operation = results.operation,
            type = results.type,
            data = results.data,
            permission = results.permission,
            message = '',
            class_map = {
                'success': 'success',
                'error': 'error'
            };

        if (type == 'success') {
            this.state_map.success[operation].push(permission);
            message = '[' + permission + '] succesfully ' + (operation == 'add' ? 'added.' : 'removed.');
        } else { //error
            this.state_map.failed[operation].push(permission);
            message = 'Unable to ' + operation + ' [' + permission + '].'
        }
        this.updateProgress(operation);
        this.$messages.append('' + app.utility.getDateTime() + '
' + message + '
');
        this.$messages.scrollTop(this.$messages[0].scrollHeight);
        if (this.state_map.pendingSaves == 0 && this.state_map.pendingRemoves == 0) {
            this.permissionModifyComplete();
        }
    },
    permissionModifyComplete: function() {
        var user = this.model;
        this.$messages.append('' + app.utility.getDateTime() + '
Completed modifying [' + user.get('name') + ']\'s permissions
');
        this.$messages.scrollTop(this.$messages[0].scrollHeight);

        this.userSelect(user, true);
    },
    updateProgress: function(operation) {
        this.state_map.currentProgress += this.state_map.progressUpdateRatio;
        //clip progress if needed
        this.state_map.currentProgress = (this.state_map.currentProgress >= 100 ? 100 : this.state_map.currentProgress);
        this.state_map.currentProgress = Math.ceil(this.state_map.currentProgress * 100) * 0.01;
        this.state_map.pendingRemoves -= (operation == 'remove' ? 1 : 0);
        this.state_map.pendingSaves -= (operation == 'add' ? 1 : 0);

        //update progress bar
        this.$progress_meter.width(this.state_map.currentProgress + '%');
        this.$progress_text.text(this.state_map.currentProgress + '%');
    },
    resetStateMap: function() {
        this.state_map.pendingRemoves = 0;
        this.state_map.pendingSaves = 0;
        this.state_map.pendingSaves = 0;
        this.state_map.pendingRemoves = 0;
        this.state_map.totalUpdates = 0;
        this.state_map.progressUpdateRatio = 0;
        this.state_map.failed.add = [];
        this.state_map.failed.remove = [];
        this.state_map.failed.purge = [];
        this.state_map.success.add = [];
        this.state_map.success.remove = [];
        this.state_map.success.purge = [];
    },
    userSelect: function(user, options) {
        if (!user.hasOwnProperty('attributes')) {
            return;
        }
        this.model = user;
        this.$user_attributes.each(function(i, el) {
            $(el).val(user.attributes[el.id]);
        });

        if (!user.attributes.hasOwnProperty('loginname')) {
            return false;
        }

        this.$search.val(user.get('name'));
        this.$search_clear.show();

        //fetch user permissions
        this.getUserPermissions(user.attributes.loginname);

        //update message
        this.$messages.append('' + app.utility.getDateTime() + '
Fetching [' + this.model.get('name') + ']\'s permissions
');
        this.$messages.scrollTop(this.$messages[0].scrollHeight);
        //update progress bar
        this.$progress_meter.width('0%');
        this.$progress_text.text('0%');
        //publish user selected event
        //set router
        if (options && options.route) {
            app.router.navigate('edit/user/' + user.attributes.loginname.replace('/', '\\'), false);

            Backbone.pubSub.trigger('user:selected');
        } else {
            app.router.navigate('edit/user/' + user.attributes.loginname.replace('/', '\\'), false);
            Backbone.pubSub.trigger('user:selected');
        }
        Backbone.pubSub.trigger('breadcrumbs');

    },
    getUserPermissions: function(loginname) {
        (function(that) {
            app.data.getPermissions(app.config.url, loginname, function(results) {
                //format results
                results = app.utility.processData(results);
                //set selected user model permissions
                that.model.set({
                    permissions: results
                });
                //endable edit buttons
                that.toggleButtons(true);
                //publish results globally
                Backbone.pubSub.trigger('user:permissions-fetched', results);
                that.$messages.append('' + app.utility.getDateTime() + '
Completed fetching [' + that.model.get('name') + ']\'s permissions
');
                that.$messages.scrollTop(that.$messages[0].scrollHeight);
                that.$progress_meter.width('100%');
                that.$progress_text.text('100%');
            });
        })(this);
    },
    toggleButtons: function(enable) {
        if (enable) {
            this.$save_btn.removeClass('disabled');
            this.$purge_btn.removeClass('disabled');
            this.$export_btn.removeClass('disabled');
        } else {
            this.$save_btn.addClass('disabled');
            this.$purge_btn.addClass('disabled');
            this.$export_btn.addClass('disabled');
        }
    },
    clearInfo: function(enable) {
        this.$user_attributes.each(function(i, el) {
            $(el).val('');
        });
    },

    onSearchFocus: function(e) {
        this.$users.show();
    },

    onSearchBlur: function(e) {
        (function(that) {
            setTimeout(function() {
                var $currentTarget = $(document.activeElement);
                if($currentTarget === that.$user_info){
                    return;
                }
                that.$users.hide();
            },100);
        })(this);
    }
});
<commit_msg>select element for comparison instead of selector<commit_after>var app = app || {};

app.UserEditView = Backbone.View.extend({
    template: _.template($('#user-edit-template').html()),
    tagName: 'div',
    className: 'user-edit',

    events: {
        'keyup .search': 'onUsersKeyUp',
        'click #user-select-btn': 'onUserSelectBtnClick',
        'click .save-permissions-btn': 'onSaveClick',
        'click #clear-console': 'onClearConsoleClick',
        'click .export-permissions': 'onExportBtnClick',
        'click .purge-user-btn': 'onPurgeBtnClick',
        'click .search-clear': 'onSearchClear',
        'click .search': 'onSearchClick',
        'blur .search': 'onSearchBlur',
        'focus .search': 'onSearchFocus'
    },

    initialize: function(options) {
        Backbone.pubSub.on('user:selected-permissions-fetched', this.onSelectedPermissionsFetched, this);
        this.state_map = {
            user_permissions: null,
            pendingSaves: 0,
            pendingRemoves: 0,
            totalUpdates: 0,
            progressUpdateRatio: 0,
            currentProgress: 0,
            failed: {
                add: [],
                remove: [],
                purge: []
            },
            success: {
                add: [],
                remove: [],
                purge: []
            }
        }

    },

    render: function() {
        this.$el.html(this.template(this.model.toJSON()));
        this.$users = this.$('#users');
        this.$user_info = this.$('#user-info');
        this.$user_search_container = this.$('.search-container');
        this.$user_search = this.$('.search');
        this.$user_attributes = this.$('.user-attr');
        this.$name = this.$('#name');
        this.$name_container = this.$('#name-container');
        this.$messages = this.$('.messages');
        this.$notify = this.$('.notify');
        this.$progress_meter = this.$('.meter');
        this.$progressbar = this.$('.progress');
        this.$progress_text = this.$('.current-progress');
        this.$search_clear = this.$('.search-clear');
        this.$search = this.$('.search');
        this.$user_select_btn = this.$('#user-select-btn');
        this.$save_btn = this.$('.save-permissions-btn');
        this.$purge_btn = this.$('.purge-btn');
        this.$export_btn = this.$('.export-permissions');

        if (this.model.get('name') == '') {
            this.toggleButtons(false);
        }

        this.libraryViewUsers = new app.LibraryUserView({
            el: this.$users[0],
            collection: app.UserCollection,
            itemView: app.UserView
        });

        this.libraryViewUsers.render();

        Backbone.pubSub.on('user:select', this.userSelect, this);
        if (this.model.get('id') != '') {
            this.userSelect(this.model, false);
        }
        return this;
    },

    search: function() {
        var val = this.$user_search.val(),
            options,
            searchAllAttributes = false;

        if (val.length > 0) {
            this.$search_clear.removeClass('hidden');
        } else {
            this.$search_clear.addClass('hidden');
        }

        //check for search operand character '~'
        if (val.indexOf('~') == 0 && val.length > 1) {
            //set val to exclude '~'
            val = val.substring(1);
            searchAllAttributes = true;
        } else if (val.indexOf('~') == 0) {
            //don't commit to search yet since not enough info has been entered.
            return;
        }

        options = (searchAllAttributes ? {
            val: val
        } : {
            key: 'name',
            val: val
        });

        Backbone.pubSub.trigger('library_users:search', options);
    },
    onSearchClear: function(e) {
        this.$search.val('');
        this.$search.trigger('keyup');
        e.stopPropagation();
        this.$user_search.focus();
    },
    onUsersKeyUp: function(e) {
        this.search();
    },
    onSelectedPermissionsFetched: function(selected_permissions) {
        var user_permissions = new Backbone.Collection(this.model.get('permissions')),
            user_permission_index,
            add_permissions_arr = [],
            remove_permissions_arr = [],
            user = this.model.attributes;

        this.toggleButtons(false);

        //iterate through selected permissions
        selected_permissions.forEach(function(permission) {
            //check if permissions is in user permissions
            //user_permission_index = user_permissions_arr.indexOf(permission);
            user_permission = user_permissions.findWhere({
                name: permission
            });
            if (typeof user_permission !== 'undefined') {
                //remove permission from user permissions
                user_permissions.remove(user_permission);

            } else {
                //add permission to add permission array
                add_permissions_arr.push(permission);
            }
        });
        this.state_map.user_permissions = user_permissions;
        //set remove permissions to the remaining user permissions...for contextual reasons
        remove_permissions_arr = user_permissions.map(function(obj) {
                return obj.get('name');
            }),

            //set state map variables
            this.state_map.pendingSaves = add_permissions_arr.length;
        this.state_map.pendingRemoves = remove_permissions_arr.length;
        this.state_map.totalUpdates = this.state_map.pendingRemoves + this.state_map.pendingSaves;
        this.state_map.progressUpdateRatio = 100 / this.state_map.totalUpdates;
        if (add_permissions_arr.length == 0 && remove_permissions_arr.length == 0) {
            return;
        }

        this.$messages.append('' + app.utility.getDateTime() + '
Modifying [' + user.name + ']\'s permissions
');
        //add user permissions
        if (add_permissions_arr.length > 0) {
            (function(that) {
                app.data.modifyPermissions(add_permissions_arr, 0, user, app.config.url, 'add', function(results) {
                    that.processPermissionModify(results);
                });
            })(this);
        }
        //remove user permissions
        if (remove_permissions_arr.length > 0) {
            (function(that) {
                app.data.modifyPermissions(remove_permissions_arr, 0, user, app.config.url, 'remove', function(results) {
                    that.processPermissionModify(results);
                });
            })(this);
        }
    },

    onSearchClick: function(e) {
        e.stopPropagation();
    },

    onSaveClick: function(e) {
        if ($(e.currentTarget).hasClass('disabled')) {
            return;
        }
        this.resetStateMap();
        //update progress bar
        this.$progress_meter.width('0%');
        this.$progress_text.text('0%');
        //publish user selected event
        Backbone.pubSub.trigger('user:save-permissions');
    },

    onClearConsoleClick: function(e) {
        this.clearConsole();
    },

    onExportBtnClick: function(e) {
        var permissions = (this.model ? this.model.get('permissions') : false),
            ua = window.navigator.userAgent,
            msie = ua.indexOf("MSIE "),
            permissionsElement;

        if ($(e.currentTarget).hasClass('disabled')) {
            return;
        }

        if (msie > 0) { // If Internet Explorer, return version number
            permissionsElement = '
' + this.model.get('name') + '\'s Permissions

';
            permissionsElement += '
' + this.model.get('name') + '
';
            permissionsElement += '
' + this.model.get('loginname') + '
';
            permissionsElement += '
' + this.models.get('email') + '
';

            if (permissions.length > 1) {
                permissionsElement += permissions.reduce(function(memo, obj) {
                    return (typeof memo == "string" ? memo : '
' + memo.name + '
') + '
' + obj.name + '
';
                });
            } else {
                permissionsElement = '
' + permissions[0].name + '
';
            }

            permissionsElement += '
';
            app.utility.printToNewWindow(permissionsElement);
        } else {
            app.utility.JSONToCSVConvertor(permissions, this.model.get('name') + ' Permissions', true);
        }

    },

    onPurgeBtnClick: function(e) {
        var user = (this.model ? this.model.attributes : false);

        if ($(e.currentTarget).hasClass('disabled')) {
            return;
        }

        this.$messages.append('' + app.utility.getDateTime() + '
Purging [' + user.name + ']
');
        this.$messages.scrollTop(this.$messages[0].scrollHeight);
        (function(that) {
            app.data.removeUserFromWeb(app.config.url, user, function(results) {
                that.model.collection.remove(this.model);
                that.onRemoveUserComplete(results);
                app.router.navigate('edit/user/', true);
            });
        })(this);

    },

    onRemoveUserComplete: function(results) {
        var type = results.type,
            data = results.data,
            name = this.model.get('name'),
            message = '';

        if (type != 'error') {
            message = 'Succesfully removed ' + name + ' from site.';
        } else {
            message = 'Unsuccessfully removed ' + name + ' from site.';
        }

        this.$messages.append('' + app.utility.getDateTime() + '
' + message + '
');
        this.$messages.scrollTop(this.$messages[0].scrollHeight);
    },

    clearConsole: function() {
        this.$messages.html('');
    },

    processPermissionModify: function(results) {
        var operation = results.operation,
            type = results.type,
            data = results.data,
            permission = results.permission,
            message = '',
            class_map = {
                'success': 'success',
                'error': 'error'
            };

        if (type == 'success') {
            this.state_map.success[operation].push(permission);
            message = '[' + permission + '] succesfully ' + (operation == 'add' ? 'added.' : 'removed.');
        } else { //error
            this.state_map.failed[operation].push(permission);
            message = 'Unable to ' + operation + ' [' + permission + '].'
        }
        this.updateProgress(operation);
        this.$messages.append('' + app.utility.getDateTime() + '
' + message + '
');
        this.$messages.scrollTop(this.$messages[0].scrollHeight);
        if (this.state_map.pendingSaves == 0 && this.state_map.pendingRemoves == 0) {
            this.permissionModifyComplete();
        }
    },
    permissionModifyComplete: function() {
        var user = this.model;
        this.$messages.append('' + app.utility.getDateTime() + '
Completed modifying [' + user.get('name') + ']\'s permissions
');
        this.$messages.scrollTop(this.$messages[0].scrollHeight);

        this.userSelect(user, true);
    },
    updateProgress: function(operation) {
        this.state_map.currentProgress += this.state_map.progressUpdateRatio;
        //clip progress if needed
        this.state_map.currentProgress = (this.state_map.currentProgress >= 100 ? 100 : this.state_map.currentProgress);
        this.state_map.currentProgress = Math.ceil(this.state_map.currentProgress * 100) * 0.01;
        this.state_map.pendingRemoves -= (operation == 'remove' ? 1 : 0);
        this.state_map.pendingSaves -= (operation == 'add' ? 1 : 0);

        //update progress bar
        this.$progress_meter.width(this.state_map.currentProgress + '%');
        this.$progress_text.text(this.state_map.currentProgress + '%');
    },
    resetStateMap: function() {
        this.state_map.pendingRemoves = 0;
        this.state_map.pendingSaves = 0;
        this.state_map.pendingSaves = 0;
        this.state_map.pendingRemoves = 0;
        this.state_map.totalUpdates = 0;
        this.state_map.progressUpdateRatio = 0;
        this.state_map.failed.add = [];
        this.state_map.failed.remove = [];
        this.state_map.failed.purge = [];
        this.state_map.success.add = [];
        this.state_map.success.remove = [];
        this.state_map.success.purge = [];
    },
    userSelect: function(user, options) {
        if (!user.hasOwnProperty('attributes')) {
            return;
        }
        this.model = user;
        this.$user_attributes.each(function(i, el) {
            $(el).val(user.attributes[el.id]);
        });

        if (!user.attributes.hasOwnProperty('loginname')) {
            return false;
        }

        this.$search.val(user.get('name'));
        this.$search_clear.show();

        //fetch user permissions
        this.getUserPermissions(user.attributes.loginname);

        //update message
        this.$messages.append('' + app.utility.getDateTime() + '
Fetching [' + this.model.get('name') + ']\'s permissions
');
        this.$messages.scrollTop(this.$messages[0].scrollHeight);
        //update progress bar
        this.$progress_meter.width('0%');
        this.$progress_text.text('0%');
        //publish user selected event
        //set router
        if (options && options.route) {
            app.router.navigate('edit/user/' + user.attributes.loginname.replace('/', '\\'), false);

            Backbone.pubSub.trigger('user:selected');
        } else {
            app.router.navigate('edit/user/' + user.attributes.loginname.replace('/', '\\'), false);
            Backbone.pubSub.trigger('user:selected');
        }
        Backbone.pubSub.trigger('breadcrumbs');

    },
    getUserPermissions: function(loginname) {
        (function(that) {
            app.data.getPermissions(app.config.url, loginname, function(results) {
                //format results
                results = app.utility.processData(results);
                //set selected user model permissions
                that.model.set({
                    permissions: results
                });
                //endable edit buttons
                that.toggleButtons(true);
                //publish results globally
                Backbone.pubSub.trigger('user:permissions-fetched', results);
                that.$messages.append('' + app.utility.getDateTime() + '
Completed fetching [' + that.model.get('name') + ']\'s permissions
');
                that.$messages.scrollTop(that.$messages[0].scrollHeight);
                that.$progress_meter.width('100%');
                that.$progress_text.text('100%');
            });
        })(this);
    },
    toggleButtons: function(enable) {
        if (enable) {
            this.$save_btn.removeClass('disabled');
            this.$purge_btn.removeClass('disabled');
            this.$export_btn.removeClass('disabled');
        } else {
            this.$save_btn.addClass('disabled');
            this.$purge_btn.addClass('disabled');
            this.$export_btn.addClass('disabled');
        }
    },
    clearInfo: function(enable) {
        this.$user_attributes.each(function(i, el) {
            $(el).val('');
        });
    },

    onSearchFocus: function(e) {
        this.$users.show();
    },

    onSearchBlur: function(e) {
        (function(that) {
            setTimeout(function() {
                if(document.activeElement === that.$user_info[0]){
                    return;
                }
                that.$users.hide();
            },100);
        })(this);
    }
});
<|endoftext|>


<commit_before>var JSActiveModelSync = function(){
};

JSActiveModelSync.prototype = new JSActiveModel();
JSActiveModelSync.prototype.constructor = JSActiveModelSync;
JSActiveModelSync.prototype.parent = JSActiveModel.prototype;

JSActiveModel.inheritKlass(JSActiveModelSync, JSActiveModel);

JSActiveModelSync.parms = {
}

//proto methods
JSActiveModelSync.prototype.init = function(parms){
  console.log('second: init');
  JSActiveModelSync.prototype.dbsetup(parms);
}
JSActiveModelSync.prototype.dbopen = function(parms){
  console.log('fourth: dbopen');
  //these vars should have been set on some global var
  if(window.openDatabase){
    var DATABASE = parms['DATABASE'];
    var DBVERSION = parms['DBVERSION'];
    var DBDESCRIPTION = parms['DBDESCRIPTION'];
    var DBSIZE = parms['DBSIZE'];
    db = openDatabase(DATABASE, DBVERSION, DBDESCRIPTION, DBSIZE);
    return true;
  }else{
    return false;
  }
}
JSActiveModelSync.prototype.dbsetup = function(parms){
  console.log('third: dbsetup');
  if(JSActiveModelSync.prototype.dbopen(parms)){
    var DBTABLE = parms['DBTABLE'];
    var DBCOLUMNS = parms['DBCOLUMNS'];
    DBCOLUMNS = DBCOLUMNS + ', local_storage_id';
    db.transaction(function (tx) {
      tx.executeSql('CREATE TABLE IF NOT EXISTS ' + DBTABLE + ' (' + DBCOLUMNS + ')');
      tx.executeSql('CREATE TABLE IF NOT EXISTS ' + DBTABLE + '_AUDIT (' + DBCOLUMNS + ', api_url)');
    });
  }else{
    //methods.get_records( url );
  }
}
JSActiveModelSync.prototype.update = function(){
  console.log('PARMS: ', this.parms);
}
JSActiveModelSync.prototype.create = function(){
}
JSActiveModelSync.prototype.destroy = function(){
}

//klass methods
JSActiveModelSync.klass = {
  all : function(){
    console.log('first: all');
    JSActiveModelSync.prototype.init(this.parms); //make sure db is available
    DBTABLE = this.parms['DBTABLE'];
    db.transaction(function (tx) {
      console.log(DBTABLE);
      tx.executeSql('SELECT * FROM ' + DBTABLE, function (tx, results) {
        console.log('here i am');
        data = [];
        var len = results.rows.length, i;
        for (i = 0; i < len; i++) {
          //data.push({DBTABLE:{'name': results.rows.item(i).name}});
        }
        //allHandle(data);
      });
    });
  },
  find : function(){
          //get results localy
          db.transaction(function (tx) {
            tx.executeSql('SELECT * FROM '+ DBTABLE +' WHERE local_storage_id = ?', [id], function (tx, results) {
              data = [];
              var len = results.rows.length, i;
              for (i = 0; i < len; i++) {
                data.push({DBTABLE:{'name': results.rows.item(i).name}});
                //alert(results.rows.item(i).name);
              }
              findHandle(data);
            });
          });
  },
  where : function(){
  },
  find_by_sql : function(){
  }
}

JSActiveModel.hideKlass(JSActiveModelSync);

<commit_msg>tweek to test all method<commit_after>var JSActiveModelSync = function(){
};

JSActiveModelSync.prototype = new JSActiveModel();
JSActiveModelSync.prototype.constructor = JSActiveModelSync;
JSActiveModelSync.prototype.parent = JSActiveModel.prototype;

JSActiveModel.inheritKlass(JSActiveModelSync, JSActiveModel);

JSActiveModelSync.parms = {
}
//proto methods
JSActiveModelSync.prototype.init = function(parms){
  JSActiveModelSync.prototype.dbsetup(parms);
}
JSActiveModelSync.prototype.dbopen = function(parms){
  //these vars should have been set on some global var
  if(window.openDatabase){
    var DATABASE = parms['DATABASE'];
    var DBVERSION = parms['DBVERSION'];
    var DBDESCRIPTION = parms['DBDESCRIPTION'];
    var DBSIZE = parms['DBSIZE'];
    db = openDatabase(DATABASE, DBVERSION, DBDESCRIPTION, DBSIZE);
    return true;
  }else{
    return false;
  }
}
JSActiveModelSync.prototype.dbsetup = function(parms){
  if(JSActiveModelSync.prototype.dbopen(parms)){
    var DBTABLE = parms['DBTABLE'];
    var DBCOLUMNS = parms['DBCOLUMNS'];
    DBCOLUMNS = DBCOLUMNS + ', local_storage_id';
    db.transaction(function (tx) {
      tx.executeSql('CREATE TABLE IF NOT EXISTS ' + DBTABLE + ' (' + DBCOLUMNS + ')');
      tx.executeSql('CREATE TABLE IF NOT EXISTS ' + DBTABLE + '_AUDIT (' + DBCOLUMNS + ', api_url)');
    });
  }else{
    //methods.get_records( url );
  }
}
JSActiveModelSync.prototype.update = function(){
}
JSActiveModelSync.prototype.create = function(){
}
JSActiveModelSync.prototype.destroy = function(){
}

//klass methods
JSActiveModelSync.klass = {
  all : function(){
    JSActiveModelSync.prototype.init(this.parms); //make sure db is available and open
    DBTABLE = this.parms['DBTABLE'];
    db.transaction(function (tx) {
      tx.executeSql('SELECT * FROM ' + DBTABLE, [], successHandler, errorHandler);
    });
    successHandler = function(tx, results){
      if(results.rows.length == 0){
        console.log('got no rows');
        data = '0';
          //insert data because the table's empty
      } else {
        console.log('got some rows');
        data = [];
        var len = results.rows.length, i;
        for (i = 0; i < len; i++) {
          //data.push({DBTABLE:{'name': results.rows.item(i).name}});
        }
      }
    };
    errorHandler = function(tx, results){
      console.log('got an error');
    };
  },
  find : function(){
    db.transaction(function (tx) {
      tx.executeSql('SELECT * FROM '+ DBTABLE +' WHERE local_storage_id = ?', [id], function (tx, results) {
        data = [];
        var len = results.rows.length, i;
        for (i = 0; i < len; i++) {
          data.push({DBTABLE:{'name': results.rows.item(i).name}});
          //alert(results.rows.item(i).name);
        }
        findHandle(data);
      });
    });
  },
  where : function(){
  },
  find_by_sql : function(){
  }
}

JSActiveModel.hideKlass(JSActiveModelSync);

<|endoftext|>