<commit_before>google.load("visualization", "1"); //loads visualization

var timer = null;
var currentState = null;
var receivedStates = [];

var IMAGE =
	{
		FRIDGE_CLOSED: 0,
		FRIDGE_OPEN: 1,
		FRIDGE_UNKNOWN : 2,
		FAST_POLLING: 3,
		SLOW_POLLING: 4,
		SUCCESS: 5,
		FAILURE: 6
	}

var images = [
		'/images/fridge_closed2.png',
		'/images/fridge_open2.png',
		'/images/fridge_unknown.png',
		'/images/rabbit.png',
		'/images/snail.png',
		'/images/success.png',
		'/images/failure.png'
	]

var updateURL = 'http://node.fridge-cop.com/';

$(function()
{
	preload(images);

	var endPoint = "state_changes";
	var reconnect = false;

	if (document.location.hostname == "localhost")
	{
		endPoint = "dev/" + endPoint;
	}

	if (window.io && io) //otherwise we can't connect to our node :(
	{
		var socket = io.connect(updateURL + endPoint);
		socket.on('connect', function()
		{
			if (reconnect) //then we disconnected, get the latest state again and it's party time
			{
				$.get("/current_state").done(function(data)
				{
					new StateData(JSON.parse(data)).apply()
				})
				reconnect = false;
			}
		});
		socket.on('new_states', function (data)
		{
			processState(JSON.parse(data))
		});
		socket.on('disconnect', function()
		{
			setFridgeStateUnknown();
			reconnect = true;
		});
	}

	//setup points
	if (userLoggedIn())
	{
		displayWhiteBoardPoints();
		var logoutContent = 'Log Out';
		$("#fridgeWhiteboard").on('mouseenter',  function() { $("#whiteboardLink").html(logoutContent) })
							  .on('mouseleave',  function() { displayWhiteBoardPoints() })
	}
	else
	{
		var loginContent = 'Log In';
		$("#whiteboardLink").html(loginContent); //default blank
	}

	attachEvents();

	processState(currentState);
})

function displayWhiteBoardPoints()
{
	var pointsContent = function() { return '' + fridgePoints + ''};
	$("#whiteboardLink").html(pointsContent())
};

function attachEvents()
{
	var spinner = new GameSpinner("fridgeClickVerifying");

	$("#fridgeClickOverlay").on("click", function()
	{
		if (userLoggedIn() && fridgeIsOpen())
		{
			spinner.setText("Verifying Click...");
			spinner.setImage(IMAGE.FRIDGE_CLOSED);
			spinner.spin();
			spinner.show();
			$.get("/fridge_point_click").done(function(result)
			{
				result = JSON.parse(result)
				if (result.error)
				{
					spinner.setText(result.errorMessage);
					spinner.setImage(IMAGE.FAILURE);
				}
				else
				{
					spinner.setText("+1 FRIDGE POINTS YEAHHHH");
					spinner.setImage(IMAGE.SUCCESS);
					fridgePoints = result.points;
					displayWhiteBoardPoints();
				}
			}).fail(function()
			{
				spinner.setText('Click failed! :(');
				spinner.setImage(IMAGE.FAILURE);
			}).always(function()
			{
				spinner.stop();
			})
		}
	})

	$("#statPopupButton").magnificPopup(
	{
		  items:
		  {
			src: "#statsPopup", // can be a HTML string, jQuery object, or CSS selector
			type: 'inline'
		  },
		  callbacks:
		  {
			open : function()
			{
				redrawTimeline();
			}
		  }
	})

	$("#lastOpenedOverlay").qtip({
		style:
		{
			classes: 'qtip-bootstrap',
			width: 150, // Overrides width set by CSS (but no max-width!)
		},
		content:
		{
            text: $("#lastOpenedToolTipText")
        },
		position:
		{
			my: "center left",
			at: "center right"
		}
	});
}

timelineRequests = {};
var requestIndex = 0;

function redrawTimeline()
{
	$.each(timelineRequests, function(index, request)
	{
		request.abort();
	})

	$("#timeline").html("Loading...");

	requestIndex++;

	makeRequest(requestIndex);

	function makeRequest(requestIndex)
	{

		timelineRequests[requestIndex] =
		$.get("/timeline_states")
		.done(
			function(timelineStates)
			{
				renderTimeline(JSON.parse(timelineStates));
			})
		.always(
			function()
			{
				delete timelineRequests[requestIndex]
			})
	}

	function renderTimeline(timelineStates)
	{
		// Create and populate a data table.
		var data = new google.visualization.DataTable();
		data.addColumn('datetime', 'start');
		data.addColumn('datetime', 'end');
		data.addColumn('string', 'content');

		//construct timeline

		var lookingForFridgeState = 1;

		var statePair = {};

		$.each(timelineStates, function(index, state)
		{
			var state = new StateData(state);
			var content = null;
			if (state.getState() == 1)
			{
				content = ''
			}
			else if (state.getState() == 2)
			{
				content = ''
			}
			else if (state.getState() == 3)
			{
				content = ''
			}
			if (content)
			{
				data.addRow([state.getChangeTime().toDate(), , content]);
			}
		})

		$.each(timelineStates, function(index, state)
		{
			var state = new StateData(state);
			if (state.isFridgeData())
			{
				if (!statePair.startState)
				{
					if (state.getState() == 1)
					{
						statePair.startState = state;
					}
				}
				else //we do have a start state, now we need an end state
				{
					if (state.getState() == 2)
					{
						//add the timeline elements
						data.addRow([statePair.startState.getChangeTime().toDate(),state.getChangeTime().toDate(), ""]);

						statePair = {};
					}
				}
			}
		});

		// Instantiate our timeline object.
		timeline = new links.Timeline(document.getElementById('timeline'));

		// specify options
		var options = {
			"width":  "100%",
			"height": "100%",
			"style": "box"
		};

		// Draw our timeline with the created data and options
		timeline.draw(data, options);
	}
}

function getImage(imageIndex)
{
	return images[imageIndex];
}

var stateChangeTimer = null

//this function waits the appropriate amount of time for a delayed state change
function processState(state)
{
	new StateData(state).apply();
}

function preload(arrayOfImages) {
	$("body").append('
');
	$(arrayOfImages).each(function(index, imageLocation){
		$("#imagePreloadArea").append('');
	});
}

function fridgeIsOpen()
{
	return (currentState == "fridgeStateOpen");
}

function setFridgeStateUnknown()
{
	new StateData({ "s" : 3, "type" : "fridge" }).apply();
}

function userLoggedIn()
{
	return (loggedIn)
}

function GameSpinner(id)
{
	var that = this;
	var spinner = $("#" + id)
	spinner.on("click", function() {
		that.hide();
	});

	init()

	//generate
	function init()
	{
		spinner.addClass("spinContainer gradientBackground")
		spinner.html(
			'
' +
			'
' +
			'

'
		)
	}

	this.setText = function(text)
	{
		spinner.find(".spinText").text(text);
	}

	this.spin = function()
	{
		spinner.find(".spinImage").addClass("spinning")
	}

	this.stop = function()
	{
		spinner.find(".spinImage").removeClass("spinning")
	}

	this.setImage = function(imageURL)
	{
		spinner.find(".spinImage").css("background-image", 'url(' + getImage(imageURL) + ')')
	}

	this.show = function()
	{
		spinner.show();
	}

	this.hide = function()
	{
		spinner.hide();
	}

	this.fadeOut = function()
	{
		spinner.delay(5000).fadeOut({ "duration" : 3000});
	}
}

function StateData(stateData)
{
	var that = this;
	that.stateType = stateData.type;
	that.eventTime = moment(stateData.t, serverDateFormat);
	that.lastState = stateData.ls;
	that.state = stateData.s;

	this.isFridgeData = function()
	{
		return (that.stateType == "fridge");
	}

	this.getChangeTime = function()
	{
		return that.eventTime;
	}

	this.getLastState = function()
	{
		return that.lastState;
	}

	this.getState = function()
	{
		return that.state;
	}

	this.getType = function()
	{
		return that.stateType;
	}

	this.equals = function(b)
	{
		var a = that;
		return ((a.getState() == b.getState()) && (a.getChangeTime().diff(b.getChangeTime()) == 0) && (a.getType() == b.getType()))
	}

	this.apply = function()
	{
		var fridgeStates = ["", "fridgeStateOpen", "fridgeStateClosed", "fridgeStateUnknown", "fridgeStateTransition"]
		var stateToApply = that.state;
		if (that.isFridgeData())
		{
			newState = "";
			if (stateToApply > 0 && stateToApply < fridgeStates.length)
			{
				newState = fridgeStates[stateToApply]
			}

			if (newState != currentState)
			{
				$("#fridgeStateContainer").removeClass(currentState).addClass(newState);
				currentState = newState;

				updateLastOpenedTime(that.getChangeTime())
			}
		}

		function updateLastOpenedTime(lastOpenedDate)
		{
			$("#lastOpenedToolTipText").html("Last opened
" + lastOpenedDate.format("h:mm:ss A") + "");
			$("#lastOpenedText").text(lastOpenedDate.format("hh:mm")).attr("title", lastOpenedDate.toString());
		}
	}

	this.timeDiff = function(otherState)
	{
		return that.getChangeTime().diff(otherState.getChangeTime())
	}
}<commit_msg>only show opens, not closes on the timeline<commit_after>google.load("visualization", "1"); //loads visualization

var timer = null;
var currentState = null;
var receivedStates = [];

var IMAGE =
	{
		FRIDGE_CLOSED: 0,
		FRIDGE_OPEN: 1,
		FRIDGE_UNKNOWN : 2,
		FAST_POLLING: 3,
		SLOW_POLLING: 4,
		SUCCESS: 5,
		FAILURE: 6
	}

var images = [
		'/images/fridge_closed2.png',
		'/images/fridge_open2.png',
		'/images/fridge_unknown.png',
		'/images/rabbit.png',
		'/images/snail.png',
		'/images/success.png',
		'/images/failure.png'
	]

var updateURL = 'http://node.fridge-cop.com/';

$(function()
{
	preload(images);

	var endPoint = "state_changes";
	var reconnect = false;

	if (document.location.hostname == "localhost")
	{
		endPoint = "dev/" + endPoint;
	}

	if (window.io && io) //otherwise we can't connect to our node :(
	{
		var socket = io.connect(updateURL + endPoint);
		socket.on('connect', function()
		{
			if (reconnect) //then we disconnected, get the latest state again and it's party time
			{
				$.get("/current_state").done(function(data)
				{
					new StateData(JSON.parse(data)).apply()
				})
				reconnect = false;
			}
		});
		socket.on('new_states', function (data)
		{
			processState(JSON.parse(data))
		});
		socket.on('disconnect', function()
		{
			setFridgeStateUnknown();
			reconnect = true;
		});
	}

	//setup points
	if (userLoggedIn())
	{
		displayWhiteBoardPoints();
		var logoutContent = 'Log Out';
		$("#fridgeWhiteboard").on('mouseenter',  function() { $("#whiteboardLink").html(logoutContent) })
							  .on('mouseleave',  function() { displayWhiteBoardPoints() })
	}
	else
	{
		var loginContent = 'Log In';
		$("#whiteboardLink").html(loginContent); //default blank
	}

	attachEvents();

	processState(currentState);
})

function displayWhiteBoardPoints()
{
	var pointsContent = function() { return '' + fridgePoints + ''};
	$("#whiteboardLink").html(pointsContent())
};

function attachEvents()
{
	var spinner = new GameSpinner("fridgeClickVerifying");

	$("#fridgeClickOverlay").on("click", function()
	{
		if (userLoggedIn() && fridgeIsOpen())
		{
			spinner.setText("Verifying Click...");
			spinner.setImage(IMAGE.FRIDGE_CLOSED);
			spinner.spin();
			spinner.show();
			$.get("/fridge_point_click").done(function(result)
			{
				result = JSON.parse(result)
				if (result.error)
				{
					spinner.setText(result.errorMessage);
					spinner.setImage(IMAGE.FAILURE);
				}
				else
				{
					spinner.setText("+1 FRIDGE POINTS YEAHHHH");
					spinner.setImage(IMAGE.SUCCESS);
					fridgePoints = result.points;
					displayWhiteBoardPoints();
				}
			}).fail(function()
			{
				spinner.setText('Click failed! :(');
				spinner.setImage(IMAGE.FAILURE);
			}).always(function()
			{
				spinner.stop();
			})
		}
	})

	$("#statPopupButton").magnificPopup(
	{
		  items:
		  {
			src: "#statsPopup", // can be a HTML string, jQuery object, or CSS selector
			type: 'inline'
		  },
		  callbacks:
		  {
			open : function()
			{
				redrawTimeline();
			}
		  }
	})

	$("#lastOpenedOverlay").qtip({
		style:
		{
			classes: 'qtip-bootstrap',
			width: 150, // Overrides width set by CSS (but no max-width!)
		},
		content:
		{
            text: $("#lastOpenedToolTipText")
        },
		position:
		{
			my: "center left",
			at: "center right"
		}
	});
}

timelineRequests = {};
var requestIndex = 0;

function redrawTimeline()
{
	$.each(timelineRequests, function(index, request)
	{
		request.abort();
	})

	$("#timeline").html("Loading...");

	requestIndex++;

	makeRequest(requestIndex);

	function makeRequest(requestIndex)
	{

		timelineRequests[requestIndex] =
		$.get("/timeline_states")
		.done(
			function(timelineStates)
			{
				renderTimeline(JSON.parse(timelineStates));
			})
		.always(
			function()
			{
				delete timelineRequests[requestIndex]
			})
	}

	function renderTimeline(timelineStates)
	{
		// Create and populate a data table.
		var data = new google.visualization.DataTable();
		data.addColumn('datetime', 'start');
		data.addColumn('datetime', 'end');
		data.addColumn('string', 'content');

		//construct timeline

		var lookingForFridgeState = 1;

		var statePair = {};

		$.each(timelineStates, function(index, state)
		{
			var state = new StateData(state);
			var content = null;
			if (state.getState() == 1)
			{
				content = ''
			}
			else if (state.getState() == 3)
			{
				content = ''
			}
			if (content)
			{
				data.addRow([state.getChangeTime().toDate(), , content]);
			}
		})

		$.each(timelineStates, function(index, state)
		{
			var state = new StateData(state);
			if (state.isFridgeData())
			{
				if (!statePair.startState)
				{
					if (state.getState() == 1)
					{
						statePair.startState = state;
					}
				}
				else //we do have a start state, now we need an end state
				{
					if (state.getState() == 2)
					{
						//add the timeline elements
						data.addRow([statePair.startState.getChangeTime().toDate(),state.getChangeTime().toDate(), ""]);

						statePair = {};
					}
				}
			}
		});

		// Instantiate our timeline object.
		timeline = new links.Timeline(document.getElementById('timeline'));

		// specify options
		var options = {
			"width":  "100%",
			"height": "100%",
			"style": "box"
		};

		// Draw our timeline with the created data and options
		timeline.draw(data, options);
	}
}

function getImage(imageIndex)
{
	return images[imageIndex];
}

var stateChangeTimer = null

//this function waits the appropriate amount of time for a delayed state change
function processState(state)
{
	new StateData(state).apply();
}

function preload(arrayOfImages) {
	$("body").append('
');
	$(arrayOfImages).each(function(index, imageLocation){
		$("#imagePreloadArea").append('');
	});
}

function fridgeIsOpen()
{
	return (currentState == "fridgeStateOpen");
}

function setFridgeStateUnknown()
{
	new StateData({ "s" : 3, "type" : "fridge" }).apply();
}

function userLoggedIn()
{
	return (loggedIn)
}

function GameSpinner(id)
{
	var that = this;
	var spinner = $("#" + id)
	spinner.on("click", function() {
		that.hide();
	});

	init()

	//generate
	function init()
	{
		spinner.addClass("spinContainer gradientBackground")
		spinner.html(
			'
' +
			'
' +
			'

'
		)
	}

	this.setText = function(text)
	{
		spinner.find(".spinText").text(text);
	}

	this.spin = function()
	{
		spinner.find(".spinImage").addClass("spinning")
	}

	this.stop = function()
	{
		spinner.find(".spinImage").removeClass("spinning")
	}

	this.setImage = function(imageURL)
	{
		spinner.find(".spinImage").css("background-image", 'url(' + getImage(imageURL) + ')')
	}

	this.show = function()
	{
		spinner.show();
	}

	this.hide = function()
	{
		spinner.hide();
	}

	this.fadeOut = function()
	{
		spinner.delay(5000).fadeOut({ "duration" : 3000});
	}
}

function StateData(stateData)
{
	var that = this;
	that.stateType = stateData.type;
	that.eventTime = moment(stateData.t, serverDateFormat);
	that.lastState = stateData.ls;
	that.state = stateData.s;

	this.isFridgeData = function()
	{
		return (that.stateType == "fridge");
	}

	this.getChangeTime = function()
	{
		return that.eventTime;
	}

	this.getLastState = function()
	{
		return that.lastState;
	}

	this.getState = function()
	{
		return that.state;
	}

	this.getType = function()
	{
		return that.stateType;
	}

	this.equals = function(b)
	{
		var a = that;
		return ((a.getState() == b.getState()) && (a.getChangeTime().diff(b.getChangeTime()) == 0) && (a.getType() == b.getType()))
	}

	this.apply = function()
	{
		var fridgeStates = ["", "fridgeStateOpen", "fridgeStateClosed", "fridgeStateUnknown", "fridgeStateTransition"]
		var stateToApply = that.state;
		if (that.isFridgeData())
		{
			newState = "";
			if (stateToApply > 0 && stateToApply < fridgeStates.length)
			{
				newState = fridgeStates[stateToApply]
			}

			if (newState != currentState)
			{
				$("#fridgeStateContainer").removeClass(currentState).addClass(newState);
				currentState = newState;

				updateLastOpenedTime(that.getChangeTime())
			}
		}

		function updateLastOpenedTime(lastOpenedDate)
		{
			$("#lastOpenedToolTipText").html("Last opened
" + lastOpenedDate.format("h:mm:ss A") + "");
			$("#lastOpenedText").text(lastOpenedDate.format("hh:mm")).attr("title", lastOpenedDate.toString());
		}
	}

	this.timeDiff = function(otherState)
	{
		return that.getChangeTime().diff(otherState.getChangeTime())
	}
}<|endoftext|>


<commit_before>'use babel';

import { Vue } from 'use-vue';
import { requirePackages } from 'atom-utils';
import StateMachine from 'javascript-state-machine';

import DEV from './module/dev.js';
import './module/use-vue-config.js';
import console from './module/console';

import app from './views/app.vue';

const NAME = 'tree-view-search-bar';

let { autoToggle } = !!atom.config.get(NAME);
console.log('autoToggle => ', autoToggle);

let addInfo = ( message ) => atom.notifications.addInfo(message);
let showTips = () => addInfo(`"${ NAME }" require "tree-view"`);

class TreeViewSearchBar {

    /**
     * Plugin Active State
     * @type {Boolean}
     */
    active = false;

    /**
     * Plugin Commands Collection
     * @type {Object}
     */
    commands = {};

    /**
     * Define Finite State Machine Model
     * @type {StateMachine}
     */
    fsm = StateMachine.create({
        /**
         * | Null | RequireTreeView | Created | Actived | Disabled |
         * @type {String}
         */
        initial : 'Null',
        events : [
            {
                from : 'Null',
                name : 'Init',
                to : 'RequireTreeView',
            },
            {
                from : 'RequireTreeView',
                name : 'Create',
                to : 'Created',
            },
            {
                from : ['Created', 'Disabled'],
                name : 'Activate',
                to : 'Actived',
            },
            {
                from : 'Actived',
                name : 'Deactivate',
                to : 'Disabled',
            },
            {
                from : ['Disabled', 'Actived', 'Created', 'RequireTreeView'],
                name : 'Destroy',
                to : 'Null',
            },
        ],
    });

    constructor () {

        if (DEV) {
            window.test = this;
        }

        /**
         * Init Stage
         */
        this.on('Init', async () => {
            console.log('event => Init');
            let [{ treeView }] = await requirePackages('tree-view');
            if (treeView && treeView.isVisible()) {
                this.emit('Create', treeView);
            } else {
                showTips();
                this.add('search-bar:toggle', showTips);
                let ensureTreeViewVisible = () => {
                    console.log('ensureTreeViewVisible');
                    process.nextTick(() => {
                        if (treeView && treeView.isVisible() && atom.project.rootDirectories.length) {
                            this.emit('Create', treeView);
                        }
                    });
                };
                this.add('tree-view:show', ensureTreeViewVisible);
                this.add('tree-view:toggle', ensureTreeViewVisible);
                let projectListener = atom.project.onDidChangePaths(ensureTreeViewVisible);
                this.on('leaveRequireTreeView', () => {
                    console.log('event => leaveRequireTreeView');
                    this.remove('tree-view:show');
                    this.remove('tree-view:toggle');
                    this.remove('search-bar:toggle');
                    projectListener.disposalAction();
                });
            }
        });

        /**
         * Create Stage
         */
        this.on('Create', ( event, treeView ) => {
            console.log('event => Create');
            let vm = null;
            let element = document.createElement('div');
            atom.notifications.addSuccess('"tree-view-search-bar" is running!');
            /**
             * Activate Stage
             */
            this.on('Activate', () => {
                console.log('Activate');
                if (DEV) {
                    let path = require('path');
                    let file = './views/app.vue';
                    let Module = module.constructor;
                    Module._cache[path.join(__dirname, file)] = null;
                    let app = require(file);
                    vm = new Vue(app);
                } else {
                    vm = new Vue(app);
                }
                treeView.element.parentNode.insertBefore(element, treeView.element);
                vm.$mount(element);
            });
            /**
             * Deactivate Stage
             */
            this.on('Deactivate', () => {
                console.log('Deactivate');
                if (vm) {
                    vm.$destroy();
                    vm.$el.remove();
                    vm = null;
                }
            });
            this.on('beforeDestroy', () => {
                console.log('beforeDestroy');
                if (vm) {
                    vm.$destroy();
                    vm.$el.remove();
                    vm = null;
                }
            });
            let assert = ( callback ) => {
                return () => {
                    if (treeView.isVisible()) {
                        callback();
                    } else {
                        showTips();
                    }
                };
            };
            this.add('search-bar:show', assert(() => {
                this.emit('Activate');
            }));
            this.add('search-bar:hide', assert(() => {
                this.emit('Deactivate');
            }));
            this.add('search-bar:focus', assert(() => {
                this.emit('Activate');
                vm.$emit('focus');
            }));
            this.add('search-bar:toggle', assert(() => {
                let current = this.fsm.current;
                this.emit('Activate');
                if (current === this.fsm.current) {
                    this.emit('Deactivate');
                }
            }));
            /**
             * autoToggle
             */
            if (autoToggle) {
                this.emit('Activate');
            }
        });

        /**
         * Destroy Stage
         */
        this.on('Destroy', () => {
            console.log('event => Destroy');
            this.remove('tree-view:show');
            this.remove('tree-view:toggle');
            this.remove('search-bar:show');
            this.remove('search-bar:hide');
            this.remove('search-bar:focus');
            this.remove('search-bar:toggle');
        });

    }

    /**
     * Plugin Active State
     * @return {Boolean} active
     */
    isActive () {
        return this.active;
    }

    /**
     * Atom Activate Hook
     */
    activate ( state ) {
        if (this.active) return;
        this.active = true;
        /**
         * Init Plugin
         */
        this.emit('Init');
    }

    /**
     * Atom Deactivate Hook
     */
    deactivate () {
        if (!this.active) return;
        this.active = false;
        /**
         * Destroy Plugin
         */
        this.emit('Destroy');
    }

    /**
     * Bind State Machine Event
     * @param {String} event
     * @param {Function} handler
     */
    on ( event, handler ) {
        this.fsm[`on${ event }`] = function ( name, from, to, payload ) {
            let event = { name, from, to };
            handler.call(this, event, payload);
        };
    }

    /**
     * Emit State Machine Event
     * @param {String} event
     * @param {Any} payload
     */
    emit ( event, payload ) {
        if (this.fsm.can(event)) {
            this.fsm[event](payload);
        }
    }

    /**
     * Add Atom Command
     * @param {String} command
     * @param {Function} handler
     */
    add ( command, handler ) {
        console.log(`add => ${ command }`);
        this.commands[command] && this.commands[command].dispose();
        this.commands[command] = atom.commands.add(
            'atom-workspace',
            {
                [command] : handler,
            }
        );
    }

    /**
     * Remove Atom Command
     * @param {String} command
     */
    remove ( command ) {
        console.log(`remove => ${ command }`);
        this.commands[command] && this.commands[command].dispose();
    }

}

export default new TreeViewSearchBar;
<commit_msg>update js<commit_after>'use babel';

import { Vue } from 'use-vue';
import { requirePackages } from 'atom-utils';
import StateMachine from 'javascript-state-machine';

import DEV from './module/dev.js';
import './module/use-vue-config.js';
import console from './module/console';

import app from './views/app.vue';

const NAME = 'Tree View Search Bar';

let { autoToggle } = !!atom.config.get('tree-view-search-bar');
console.log('autoToggle => ', autoToggle);

let addInfo = ( detail ) => atom.notifications.addInfo(NAME, { detail });
let showTips = () => addInfo(`Plugin require "tree-view".`);

class TreeViewSearchBar {

    /**
     * Plugin Active State
     * @type {Boolean}
     */
    active = false;

    /**
     * Plugin Commands Collection
     * @type {Object}
     */
    commands = {};

    /**
     * Define Finite State Machine Model
     * @type {StateMachine}
     */
    fsm = StateMachine.create({
        /**
         * | Null | RequireTreeView | Created | Actived | Disabled |
         * @type {String}
         */
        initial : 'Null',
        events : [
            {
                from : 'Null',
                name : 'Init',
                to : 'RequireTreeView',
            },
            {
                from : 'RequireTreeView',
                name : 'Create',
                to : 'Created',
            },
            {
                from : ['Created', 'Disabled'],
                name : 'Activate',
                to : 'Actived',
            },
            {
                from : 'Actived',
                name : 'Deactivate',
                to : 'Disabled',
            },
            {
                from : ['Disabled', 'Actived', 'Created', 'RequireTreeView'],
                name : 'Destroy',
                to : 'Null',
            },
        ],
    });

    constructor () {

        if (DEV) {
            window.test = this;
        }

        /**
         * Init Stage
         */
        this.on('Init', async () => {
            console.log('event => Init');
            let [{ treeView }] = await requirePackages('tree-view');
            if (treeView && treeView.isVisible()) {
                this.emit('Create', treeView);
            } else {
                showTips();
                this.add('search-bar:toggle', showTips);
                let ensureTreeViewVisible = () => {
                    console.log('ensureTreeViewVisible');
                    process.nextTick(() => {
                        if (treeView && treeView.isVisible() && atom.project.rootDirectories.length) {
                            this.emit('Create', treeView);
                        }
                    });
                };
                this.add('tree-view:show', ensureTreeViewVisible);
                this.add('tree-view:toggle', ensureTreeViewVisible);
                let projectListener = atom.project.onDidChangePaths(ensureTreeViewVisible);
                this.on('leaveRequireTreeView', () => {
                    console.log('event => leaveRequireTreeView');
                    this.remove('tree-view:show');
                    this.remove('tree-view:toggle');
                    this.remove('search-bar:toggle');
                    projectListener.disposalAction();
                });
            }
        });

        /**
         * Create Stage
         */
        this.on('Create', ( event, treeView ) => {
            console.log('event => Create');
            let vm = null;
            let element = document.createElement('div');
            atom.notifications.addSuccess(NAME, {
                detail : 'Plugin is running!',
            });
            /**
             * Activate Stage
             */
            this.on('Activate', () => {
                console.log('Activate');
                try {
                    if (DEV) {
                        let path = require('path');
                        let file = './views/app.vue';
                        let Module = module.constructor;
                        Module._cache[path.join(__dirname, file)] = null;
                        let app = require(file);
                        vm = new Vue(app);
                    } else {
                        vm = new Vue(app);
                    }
                    treeView.element.parentNode.insertBefore(element, treeView.element);
                    vm.$mount(element);
                } catch ( err ) {
                    // TODO: show a link to open a github issue
                    // TODO: cancel this event
                    atom.notifications.addError(NAME, {
                        detail : err.toString(),
                    });
                    return false;
                }
            });
            /**
             * Deactivate Stage
             */
            this.on('Deactivate', () => {
                console.log('Deactivate');
                if (vm) {
                    vm.$destroy();
                    vm.$el.remove();
                    vm = null;
                }
            });
            this.on('beforeDestroy', () => {
                console.log('beforeDestroy');
                if (vm) {
                    vm.$destroy();
                    vm.$el.remove();
                    vm = null;
                }
            });
            let assert = ( callback ) => {
                return () => {
                    if (treeView.isVisible()) {
                        callback();
                    } else {
                        showTips();
                    }
                };
            };
            this.add('search-bar:show', assert(() => {
                this.emit('Activate');
            }));
            this.add('search-bar:hide', assert(() => {
                this.emit('Deactivate');
            }));
            this.add('search-bar:focus', assert(() => {
                this.emit('Activate');
                vm.$emit('focus');
            }));
            this.add('search-bar:toggle', assert(() => {
                let current = this.fsm.current;
                this.emit('Activate');
                if (current === this.fsm.current) {
                    this.emit('Deactivate');
                }
            }));
            /**
             * autoToggle
             */
            if (autoToggle) {
                this.emit('Activate');
            }
        });

        /**
         * Destroy Stage
         */
        this.on('Destroy', () => {
            console.log('event => Destroy');
            this.remove('tree-view:show');
            this.remove('tree-view:toggle');
            this.remove('search-bar:show');
            this.remove('search-bar:hide');
            this.remove('search-bar:focus');
            this.remove('search-bar:toggle');
        });

    }

    /**
     * Plugin Active State
     * @return {Boolean} active
     */
    isActive () {
        return this.active;
    }

    /**
     * Atom Activate Hook
     */
    activate ( state ) {
        if (this.active) return;
        this.active = true;
        /**
         * Init Plugin
         */
        this.emit('Init');
    }

    /**
     * Atom Deactivate Hook
     */
    deactivate () {
        if (!this.active) return;
        this.active = false;
        /**
         * Destroy Plugin
         */
        this.emit('Destroy');
    }

    /**
     * Bind State Machine Event
     * @param {String} event
     * @param {Function} handler
     */
    on ( event, handler ) {
        this.fsm[`on${ event }`] = function ( name, from, to, payload ) {
            let event = { name, from, to };
            return handler.call(this, event, payload);
        };
    }

    /**
     * Emit State Machine Event
     * @param {String} event
     * @param {Any} payload
     */
    emit ( event, payload ) {
        if (this.fsm.can(event)) {
            this.fsm[event](payload);
        }
    }

    /**
     * Add Atom Command
     * @param {String} command
     * @param {Function} handler
     */
    add ( command, handler ) {
        console.log(`add => ${ command }`);
        this.commands[command] && this.commands[command].dispose();
        this.commands[command] = atom.commands.add(
            'atom-workspace',
            {
                [command] : handler,
            }
        );
    }

    /**
     * Remove Atom Command
     * @param {String} command
     */
    remove ( command ) {
        console.log(`remove => ${ command }`);
        this.commands[command] && this.commands[command].dispose();
    }

}

export default new TreeViewSearchBar;
<|endoftext|>


<commit_before>"use babel";

let {BufferedProcess, CompositeDisposable} = require('atom')
let URL                 = require('url')
PodPreviewView          = require('./pod-preview')
Perl6LinterProvider     = require('./syntax-check-linter')
Perl6BuildProvider      = require('./builder')
Perl6HyperclickProvider = require('./help-hyperclick')
Perl6FileIconsProvider  = require('./file-icons')

module.exports = {

  // Plugin settings
  "config": {
      "helpHyperclickEnabled": {
        "title"   : "Toggle context-sensitive help hyperclick support",
        "type"    : "boolean",
        "default" : true
      },
      "syntaxCheckLinterEnabled": {
        "title"   : "Toggle syntax check linter",
        "type"    : "boolean",
        "default" : true
      },
      "fileIconsEnabled": {
        "title"   : "Toggle file icons in tree view",
        "type"    : "boolean",
        "default" : true
      }
  },

  // This is called on plugin activation by the Atom environment
  activate(state) {
    // Install package-deps section in package.json without user intervention
    require('atom-package-deps')
      .install()
      .then( () => {
        // All dependencies are now installed, let's roll
        this.activateAfterDepsCheck()
      })
  },

  // This is called once the dependencies are properly installed
  activateAfterDepsCheck() {

    // Disable language-perl if it is enabled!
    if (! atom.packages.isPackageDisabled("language-perl")) {
      atom.packages.disablePackage("language-perl")
      atom.notifications.addInfo("language-perl has been disabled for a more Perl 6 fun editing experience")
    }

    // Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
    this.subscriptions = new CompositeDisposable

    // Register command that toggles this view
    this.subscriptions.add(
      atom.commands.add('atom-workspace', { 'main:pod-preview': () => { return module.exports.podPreview() } } )
    )

    atom.workspace.addOpener( (uriToOpen) => {
      let {protocol, host, pathname} = URL.parse(uriToOpen)

      if(protocol != 'pod-preview:') {
        return
      }

      try {
        if (pathname) {
          pathname = decodeURI(pathname)
        }
      } catch(error) {
        console.error(error)
        return
      }

      if(host == 'editor') {
        return new PodPreviewView( { editorId: pathname.substring(1) } )
      } else {
        return new PodPreviewView( { filePath: pathname } )
      }
    })
  },

  run_tests() {
    // TODO take command from config parameter
    command   = 'prove'
    args      = ['-v', '-e', 'perl6']

    // Find project directory for the text editor provided
    // and revert to first project path if otherwise
    textEditor = atom.workspace.getActiveTextEditor()
    projectDirs = atom.project.getDirectories()
    projectDir = projectDirs.find(
      (dir) => {
        return textEditor && dir.contains(textEditor.getPath() )
      }
    )
    projectDir = projectDir.path ? projectDir.path : projectDir[0];

    options = {
      cwd: cwd = atom.project.getDirectories()[0].path,
      env: process.env
    }

    stdout  = (text) => {
      atom.notifications.addSuccess(text)
      return
    }

    stderr  = (text) => {
      atom.notifications.addError(text)
      return
    }

    exit    = (code) => {
      atom.notifications.addInfo(`'${command} ${args.join(" ")}' exited with ${code}`)
      return
    }

    // Run `prove -v -e "perl6 -Ilib"`
    atom.notifications.addInfo(`Starting running ${command} ${args.join(" ")}'...`)
    new BufferedProcess({command, args, options, stdout, stderr, exit})
  },

  podPreview() {
    // Open POD Preview on a valid editor and warn if otherwise
    editor = atom.workspace.getActiveTextEditor()
    if (!editor) {
      atom.notifications.addWarning("No editor found. Aborting...")
      return
    }

    // Open POD Preview only on a valid Perl6 editor and warn if otherwise
    grammar = editor.getGrammar()
    if (!  (grammar && grammar.scopeName && grammar.scopeName.startsWith("source.perl6")) ) {
      atom.notifications.addWarning("No Perl 6 editor found. Aborting...")
      return
    }

    // Open POD Preview only if Pod::To::HTML is installed and warn if otherwise
    module.exports.checkForPodToHtml( () => { module.exports.openPodPreview(editor) } )

    return
  },

  openPodPreview(editor) {
    // Double check that the editor is valid
    if(!editor) {
      return
    }

    uri = `pod-preview://editor/${editor.id}`

    previewPane = atom.workspace.paneForURI(uri)
    if(previewPane) {
      previewPane.destroyItem(previewPane.itemForURI(uri))
      return
    }

    previousActivePane = atom.workspace.getActivePane()
    atom.workspace.open(uri, { split: 'right', searchAllPanes: true} ).done( (podPreviewView) => {
      if (podPreviewView instanceof PodPreviewView) {
        podPreviewView.renderHTML()
        previousActivePane.activate()
      }
    });
  },

  deactivate: () => {
    this.subscriptions.dispose()
  },

  checkForPodToHtml(onSuccess) {
    // TODO take command from config parameter
    command   = 'perl6'
    args      = ["-e use Pod::To::HTML; 0"]

    exit    = (code) => {
      if (code == 0) {
        onSuccess()
      } else {
        atom.notifications.addWarning(
          `Pod::To::HTML is not installed.

Please type the following in your command line:

panda install Pod::To::HTML
`,
          { dismissable: true }
        )
      }
      return
    }

    // Run perl6 -e 'use Pod::To::HTML'
    process   = new BufferedProcess({command, args, exit})
  },

  // Perl 6 linter
  provideLinter() {
    if (atom.config.get("atom-perl6-editor-tools.syntaxCheckLinterEnabled")) {
      return new Perl6LinterProvider
    }

    return
  },

  // Perl 6 builder
  provideBuilder() {
    return Perl6BuildProvider
  },

  // Perl 6 context-sensitive help
  provideHyperclick() {
    if (atom.config.get("atom-perl6-editor-tools.helpHyperclickEnabled")) {
      return new Perl6HyperclickProvider
    }

    return
  },

  // Perl 6 file icons
  provideFileIcons() {
    if (atom.config.get("atom-perl6-editor-tools.fileIconsEnabled")) {
      return new Perl6FileIconsProvider
    }

    return
  }
}<commit_msg>Add Atom restart needed<commit_after>"use babel";

let {BufferedProcess, CompositeDisposable} = require('atom')
let URL                 = require('url')
PodPreviewView          = require('./pod-preview')
Perl6LinterProvider     = require('./syntax-check-linter')
Perl6BuildProvider      = require('./builder')
Perl6HyperclickProvider = require('./help-hyperclick')
Perl6FileIconsProvider  = require('./file-icons')

module.exports = {

  // Plugin settings
  "config": {
      "helpHyperclickEnabled": {
        "title"   : "Toggle context-sensitive help hyperclick support (Atom restart needed)",
        "type"    : "boolean",
        "default" : true
      },
      "syntaxCheckLinterEnabled": {
        "title"   : "Toggle syntax check linter (Atom restart needed)",
        "type"    : "boolean",
        "default" : true
      },
      "fileIconsEnabled": {
        "title"   : "Toggle file icons in the tree view (Atom restart needed)",
        "type"    : "boolean",
        "default" : true
      }
  },

  // This is called on plugin activation by the Atom environment
  activate(state) {
    // Install package-deps section in package.json without user intervention
    require('atom-package-deps')
      .install()
      .then( () => {
        // All dependencies are now installed, let's roll
        this.activateAfterDepsCheck()
      })
  },

  // This is called once the dependencies are properly installed
  activateAfterDepsCheck() {

    // Disable language-perl if it is enabled!
    if (! atom.packages.isPackageDisabled("language-perl")) {
      atom.packages.disablePackage("language-perl")
      atom.notifications.addInfo("language-perl has been disabled for a more Perl 6 fun editing experience")
    }

    // Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
    this.subscriptions = new CompositeDisposable

    // Register command that toggles this view
    this.subscriptions.add(
      atom.commands.add('atom-workspace', { 'main:pod-preview': () => { return module.exports.podPreview() } } )
    )

    atom.workspace.addOpener( (uriToOpen) => {
      let {protocol, host, pathname} = URL.parse(uriToOpen)

      if(protocol != 'pod-preview:') {
        return
      }

      try {
        if (pathname) {
          pathname = decodeURI(pathname)
        }
      } catch(error) {
        console.error(error)
        return
      }

      if(host == 'editor') {
        return new PodPreviewView( { editorId: pathname.substring(1) } )
      } else {
        return new PodPreviewView( { filePath: pathname } )
      }
    })
  },

  run_tests() {
    // TODO take command from config parameter
    command   = 'prove'
    args      = ['-v', '-e', 'perl6']

    // Find project directory for the text editor provided
    // and revert to first project path if otherwise
    textEditor = atom.workspace.getActiveTextEditor()
    projectDirs = atom.project.getDirectories()
    projectDir = projectDirs.find(
      (dir) => {
        return textEditor && dir.contains(textEditor.getPath() )
      }
    )
    projectDir = projectDir.path ? projectDir.path : projectDir[0];

    options = {
      cwd: cwd = atom.project.getDirectories()[0].path,
      env: process.env
    }

    stdout  = (text) => {
      atom.notifications.addSuccess(text)
      return
    }

    stderr  = (text) => {
      atom.notifications.addError(text)
      return
    }

    exit    = (code) => {
      atom.notifications.addInfo(`'${command} ${args.join(" ")}' exited with ${code}`)
      return
    }

    // Run `prove -v -e "perl6 -Ilib"`
    atom.notifications.addInfo(`Starting running ${command} ${args.join(" ")}'...`)
    new BufferedProcess({command, args, options, stdout, stderr, exit})
  },

  podPreview() {
    // Open POD Preview on a valid editor and warn if otherwise
    editor = atom.workspace.getActiveTextEditor()
    if (!editor) {
      atom.notifications.addWarning("No editor found. Aborting...")
      return
    }

    // Open POD Preview only on a valid Perl6 editor and warn if otherwise
    grammar = editor.getGrammar()
    if (!  (grammar && grammar.scopeName && grammar.scopeName.startsWith("source.perl6")) ) {
      atom.notifications.addWarning("No Perl 6 editor found. Aborting...")
      return
    }

    // Open POD Preview only if Pod::To::HTML is installed and warn if otherwise
    module.exports.checkForPodToHtml( () => { module.exports.openPodPreview(editor) } )

    return
  },

  openPodPreview(editor) {
    // Double check that the editor is valid
    if(!editor) {
      return
    }

    uri = `pod-preview://editor/${editor.id}`

    previewPane = atom.workspace.paneForURI(uri)
    if(previewPane) {
      previewPane.destroyItem(previewPane.itemForURI(uri))
      return
    }

    previousActivePane = atom.workspace.getActivePane()
    atom.workspace.open(uri, { split: 'right', searchAllPanes: true} ).done( (podPreviewView) => {
      if (podPreviewView instanceof PodPreviewView) {
        podPreviewView.renderHTML()
        previousActivePane.activate()
      }
    });
  },

  deactivate: () => {
    this.subscriptions.dispose()
  },

  checkForPodToHtml(onSuccess) {
    // TODO take command from config parameter
    command   = 'perl6'
    args      = ["-e use Pod::To::HTML; 0"]

    exit    = (code) => {
      if (code == 0) {
        onSuccess()
      } else {
        atom.notifications.addWarning(
          `Pod::To::HTML is not installed.

Please type the following in your command line:

panda install Pod::To::HTML
`,
          { dismissable: true }
        )
      }
      return
    }

    // Run perl6 -e 'use Pod::To::HTML'
    process   = new BufferedProcess({command, args, exit})
  },

  // Perl 6 linter
  provideLinter() {
    if (atom.config.get("atom-perl6-editor-tools.syntaxCheckLinterEnabled")) {
      return new Perl6LinterProvider
    }

    return
  },

  // Perl 6 builder
  provideBuilder() {
    return Perl6BuildProvider
  },

  // Perl 6 context-sensitive help
  provideHyperclick() {
    if (atom.config.get("atom-perl6-editor-tools.helpHyperclickEnabled")) {
      return new Perl6HyperclickProvider
    }

    return
  },

  // Perl 6 file icons
  provideFileIcons() {
    if (atom.config.get("atom-perl6-editor-tools.fileIconsEnabled")) {
      return new Perl6FileIconsProvider
    }

    return
  }
}<|endoftext|>


<commit_before>var World = function(game) {
    this.regions = [];

    this.game = game;

    this.generator = new TerrainGenerator(this.game.tileRegistry);

    // Regions are 8x8 tiles
    this.regionSize = 8;
    // With 24 by 24 px tiles
    this.imgResolution = 24;

    this.displayCaches = [];
    for(var i = 0; i < 100; i ++) {
        var canvas = document.createElement("canvas");
        canvas.width = canvas.height = this.regionSize * this.imgResolution;
        this.displayCaches[i] = {
            canvas: canvas,
            ctx: canvas.getContext("2d"),
            region: undefined
        };
    }

    this.setFocus(0, 0);
};
/**
 * Request a display cache to a certain region, unloading it from another of
 * lower importance
 * @param {Region} newRegion The region that will use the display cache
 * @return {Object} The display cache
 */
World.prototype.requestDisplayCache = function(newRegion) {
    var selected = -1, maxDist = 0,
        halfRegion = this.regionSize * this.imgResolution / 2;

    // Get the furthest away one (least important)
    for(var i = 0; i < this.displayCaches.length; i ++) {
        if (!this.displayCaches[i].region) {
            selected = i;
            break;
        }
        var dx = (this.displayCaches[i].region.x + halfRegion - this.focusX),
            dy = (this.displayCaches[i].region.y + halfRegion - this.focusY);
        if (dx * dx + dy * dy > maxDist) {
            selected = i;
            maxDist = dx * dx + dy * dy;
        }
    }

    // Release it from old region, if needed
    if (this.displayCaches[selected].region)
        this.displayCaches[selected].region.freeDisplayCache();
    this.displayCaches[selected].region = newRegion;
    return (this.displayCaches[selected]);

};
World.prototype.getRegion = function(x, y) {
    // Verify it exists
    if (this.regions[x] === undefined)
        this.regions[x] = [];
    if (this.regions[x][y] === undefined)
        this.regions[x][y] = new Region(this, this.regionSize, x, y);
    return (this.regions[x][y]);
};
World.prototype.get = function(x, y) {
    // Get the tile in the selected area
    return (this.getRegion(
                Math.floor(x / this.regionSize),
                Math.floor(y / this.regionSize))
                .get(x % this.regionSize, y % this.regionSize));
};
World.prototype.set = function(tile, x, y) {
    x = Math.floor(x);
    y = Math.floor(y);
    this.getRegion(
            Math.floor(x / this.regionSize),
            Math.floor(y / this.regionSize))
            .set(tile, x % this.regionSize, y % this.regionSize);
};
/**
 * Set the current focus of the world.
 * @param {type} x The pixel x-component to focus the viewplane on
 * @param {type} y The pixel y-component to focus the viewplane on
 */
World.prototype.setFocus = function(x, y) {
    this.focusX = x;
    this.focusY = y;
};
World.prototype.render = function() {
    //  Calculations for screen-only rendering of regions
    // Region size in px
    var r = this.regionSize * this.imgResolution;
    var startX  = Math.floor((this.focusX - this.game.canvas.width  / 2) / r),
        startY  = Math.floor((this.focusY - this.game.canvas.height / 2) / r),
        endX    = Math.floor((this.focusX + this.game.canvas.width  / 2) / r),
        endY    = Math.floor((this.focusY + this.game.canvas.height / 2) / r);

    this.game.ctx.beginPath();

    for(var x = startX; x <= endX; x ++)
    for(var y = startY; y <= endY; y ++) {
        this.getRegion(x, y).render(this.game.ctx, x * r, y * r);

        this.game.ctx.moveTo(x * r + r, y * r);
        this.game.ctx.lineTo(x * r, y * r);
        this.game.ctx.lineTo(x * r, y * r + r);
    }
    this.game.ctx.stroke();
};

var Region = function(world, size, x, y, compressedData) {
    this.world = world;
    this.size = size;
    this.x = x;
    this.y = y;

    this.displayCache = undefined;
    this.ctx = undefined;

    this.data = undefined;
    this.ctxDirty = false;

    if (!compressedData) {
        // Dirty flag; set to true if anything is modified, requiring another
        // compress to update.
        this.compressedDirty = true;
        this.compressedData = "";
    } else {
        // No compression update needed yet
        this.compressedDirty = false;
        this.compressedData = compressedData;
    }

    this.generate();
};
Region.prototype.render = function(ctx, x, y) {
    if (this.ctxDirty || !this.displayCache)
        this.updateDisplayCache();
    ctx.drawImage(this.displayCache, x, y);
};
Region.prototype.freeDisplayCache = function() {
    this.displayCache = undefined;
    this.ctx = undefined;
};
Region.prototype.updateDisplayCache = function() {
    if (!this.displayCache) {
        var cache = this.world.requestDisplayCache(this);
        this.displayCache = cache.canvas;
        this.ctx = cache.ctx;
    }

    this.ctx.clearRect(0, 0, this.displayCache.width, this.displayCache.height);

    for(var x = 0; x < this.size; x++)
    for(var y = 0; y < this.size; y++)
        this.world.game.tileRegistry.renderTile(this.ctx, this.get(x, y),
            x * this.world.imgResolution, y * this.world.imgResolution);

    this.ctxDirty = false;
};
Region.prototype.generate = function() {
    // Generate or load

    if (!this.compressedData) {
        this.data = [];
        var rSize = this.world.regionSize;
        for(var x = 0; x < this.size; x ++) {
            this.data[x] = [];
            for(var y = 0; y < this.size; y ++)
                this.data[x][y] =
                    this.world.generator.tileGen(
                    this.x * rSize + x, this.y * rSize + y);
        };
    } else
        this.data = JSON.parse(
            LZString.decompressFromUTF16(this.compressedData));
};
Region.prototype.compress = function() {
    // Only compress if needed
    if (this.compressedDirty) {
        this.compressedDirty = false;
        this.compressedData =
                LZString.compressToUTF16(JSON.stringify(this.data));
    }
};
Region.prototype.get = function(x, y) {
    // Fix negative modulus
    if (x < 0)
        x += this.size;
    if (y < 0)
        y += this.size;
    return (this.data[x][y]);
};
Region.prototype.set = function(tile, x, y) {
    // Fix negative modulus
    if (x < 0)
        x += this.size;
    if (y < 0)
        y += this.size;
    this.data[x][y] = tile;

    this.ctxDirty = true;
    this.compressedDirty = true;
};

var TerrainGenerator = function(tileRegistry, method) {
    this.tileRegistry = tileRegistry;
    this.method = method || {
        scale: 0.05,
        shapes: [
            {
                start: Number.MIN_SAFE_INTEGER,
                eval: new Evaluable(0),
                types: [{
                    start: 0, tile: "air" }
                ]
            }, {
                start: -5,
                eval: new Evaluable("*", {
                    a: new Evaluable("ratio"),
                    b: new Evaluable("noise")
                }),
                types: [{
                    start: 0, tile: "air" }, {
                    start: 0.1, tile: "stone" }
                ]
            }, {
                start: 5,
                eval: new Evaluable(0),
                types: [{
                    start: 0, tile: "stone" }
                ]
            }, {
                start: 20,
                eval: new Evaluable("*", {
                    a: new Evaluable("noise", 0),
                    b: new Evaluable("lerp", {
                        a: new Evaluable("noise", {
                            id: 1,
                            scale: 0.3
                        }),
                        b: 1,
                        x: new Evaluable("ratio")
                    })
                }),
                types: [{
                    start: 0, tile: "stone" }, {
                    start: 0.36, tile: "air" }, {
                    start: 0.64, tile: "stone" }
                ]
            }, {
                start: 1024,
                eval: new Evaluable("noise"),
                types: [{
                    start: 0, tile: "stone" }, {
                    start: 0.36, tile: "air" }, {
                    start: 0.64, tile: "stone" }
                ]
            }
        ]
    };
};
/**
 * Generates a single tile at x, y
 * @param {type} x x-coordinate of the tile
 * @param {type} y y-coordinate of the tile
 * @returns {Tile/Number} id or Tile object
 */
TerrainGenerator.prototype.tileGen = function(x, y) {
    var i = 0, shapes =  this.method.shapes;
    while (i < shapes.length && y >= shapes[i].start)
       i++;
    // Now one index too high
    i--;
    var ratio;
    if (shapes[i + 1] !== undefined)
        ratio = (y - shapes[i].start) / (shapes[i + 1].start - shapes[i].start);
    else
        ratio = 0;
    var val = this.method.shapes[i].eval.eval(x * this.method.scale,
                                              y * this.method.scale, ratio);

    var j = 0, types =  shapes[i].types;
    while (j < types.length && val >= types[j].start)
       j++;
    j--;

    return (this.tileRegistry.makeTile(types[j].tile));
};

// Evaluable module, used as a safer alternative to window.eval
var Evaluable = (function() {
    var self = function () {
        if (typeof arguments[0] === "number") {
            this.opr = "const";
            this.data = arguments[0];
        } else {
            this.opr = arguments[0];
            this.data = arguments[1];
        }
    };

    var noiseGenerators = {};
    var getNoise = function(x, y, id) {
        if (!noiseGenerators[id])
            noiseGenerators[id] = new SimplexNoise(Math);
        return (0.5 * (1 + noiseGenerators[id].noise(x, y)));
    };

    function process(val, x, y, ratio) {
        if (val === undefined)
            return (0);
        if (typeof val === "number") return val;
        return (val.eval(x, y, ratio));
    }

    self.prototype.eval = function (x, y, ratio) {
        switch (this.opr) {
            case "const":
                return (process(this.data, x, y, ratio));
            case "ratio":
                return (ratio);
            case "x":
                return (x);
            case "y":
                return (y);

            // Now for actual operands

            case "+":
                return (process(this.data.a, x, y, ratio) +
                        process(this.data.b, x, y, ratio));
            case "*":
                return (process(this.data.a, x, y, ratio) *
                        process(this.data.b, x, y, ratio));
            case "-":
                return (process(this.data.a, x, y, ratio) -
                        process(this.data.b, x, y, ratio));
            case "/":
                return (process(this.data.a, x, y, ratio) /
                        process(this.data.b, x, y, ratio));
            case "noise":
                var id, scale = 1;
                if (typeof this.data === "object") {
                    id = process(this.data.id, x, y, ratio);
                    scale = process(this.data.scale, x, y, ratio);
                } else
                    id = process(this.data, x, y, ratio);
                return (getNoise(x * scale, y * scale, id));
            case "lerp":
                var r = process(this.data.x, x, y, ratio);
                return ((1 - r) * process(this.data.a, x, y, ratio) +
                        (    r) * process(this.data.b, x, y, ratio));
            case "inrange":
                var x = process(this.data.x, x, y, ratio);
                return (process(this.data.a, x, y, ratio) <= x &&
                        process(this.data.b, x, y, ratio) > x);
        }
    };

    return (self);
})();

var Tile = function(id, data) {
    this.id = id;
    this.data = data;
};

var TileRegistry = function(game) {
    this.game = game;
    this.tileDef = [];
    // Tile indexes by name
    this.tileIndexes = {};
};
/**
 *
 * @param {String} name Name of the tile
 * @param {String} imgSrc Source location for the 24x24 image
 * @param {Boolean} solid True if the tile is solid
 * @param {function} create Callback for creation event of block
 * @param {function} update Callback for update event of block
 * @param {function} active Callback for mouse click events on the block
 * @param {Object} data Default extra data for the block.
 * @returns {TileRegistry.tileIndexes} id of the tile
 */
TileRegistry.prototype.add = function(
        name, imgSrc, solid, create, update, active, data) {
    this.tileIndexes[name] = this.tileDef.length;
    // True by default
    if (solid === undefined)
        solid = true;
    this.tileDef.push({
        name:   name,
        imgSrc: imgSrc,
        solid:  solid,
        data:   data,
        create: create,
        update: update,
        active: active
    });
    return (this.tileIndexes[name]);
};
TileRegistry.prototype.requireResources = function() {
    for(var i = 0;i < this.tileDef.length; i++)
        this.game.res.require(this.tileDef[i].imgSrc);
};
TileRegistry.prototype.isSolid = function(kind) {
    if (kind === undefined)
        kind = "invalid";
    var id;
    if (typeof kind === "string")
        id = this.tileIndexes[kind];
    else
        id = kind;
        return (this.tileDef[id].solid);
};
/**
 * Makes a new tile, either by id or object with extra data
 * @param {Number/String} kind - The id or name of the tile
 * @return {Tile/Number} id or Tile object
 */
TileRegistry.prototype.makeTile = function(kind) {
    var id;
    if (typeof kind === "string")
        id = this.tileIndexes[kind];
    else
        id = kind;
    // Set to invalid if something goes wrong
    if (this.tileDef[id] === undefined)
        id = this.tileIndexes["invalid"];

    if (this.tileDef[id].hasData)
        return (new Tile(id, this.tileDef[id].data));
    return (id);
};
TileRegistry.prototype.renderTile = function(ctx, kind, x, y) {
    var id;
    if (typeof kind === "string")
        id = this.tileIndexes[kind];
    else
        id = kind;
    if (this.tileDef[id] === undefined)
        return;
    var img = this.game.res.get(this.tileDef[id].imgSrc);
    if (img)
        ctx.drawImage(img, x, y);
};

<commit_msg>Fixed Evaluable inrange operator to return 0 or 1 instead of true/false<commit_after>var World = function(game) {
    this.regions = [];

    this.game = game;

    this.generator = new TerrainGenerator(this.game.tileRegistry);

    // Regions are 8x8 tiles
    this.regionSize = 8;
    // With 24 by 24 px tiles
    this.imgResolution = 24;

    this.displayCaches = [];
    for(var i = 0; i < 100; i ++) {
        var canvas = document.createElement("canvas");
        canvas.width = canvas.height = this.regionSize * this.imgResolution;
        this.displayCaches[i] = {
            canvas: canvas,
            ctx: canvas.getContext("2d"),
            region: undefined
        };
    }

    this.setFocus(0, 0);
};
/**
 * Request a display cache to a certain region, unloading it from another of
 * lower importance
 * @param {Region} newRegion The region that will use the display cache
 * @return {Object} The display cache
 */
World.prototype.requestDisplayCache = function(newRegion) {
    var selected = -1, maxDist = 0,
        halfRegion = this.regionSize * this.imgResolution / 2;

    // Get the furthest away one (least important)
    for(var i = 0; i < this.displayCaches.length; i ++) {
        if (!this.displayCaches[i].region) {
            selected = i;
            break;
        }
        var dx = (this.displayCaches[i].region.x + halfRegion - this.focusX),
            dy = (this.displayCaches[i].region.y + halfRegion - this.focusY);
        if (dx * dx + dy * dy > maxDist) {
            selected = i;
            maxDist = dx * dx + dy * dy;
        }
    }

    // Release it from old region, if needed
    if (this.displayCaches[selected].region)
        this.displayCaches[selected].region.freeDisplayCache();
    this.displayCaches[selected].region = newRegion;
    return (this.displayCaches[selected]);
};
World.prototype.getRegion = function(x, y) {
    // Verify it exists
    if (this.regions[x] === undefined)
        this.regions[x] = [];
    if (this.regions[x][y] === undefined)
        this.regions[x][y] = new Region(this, this.regionSize, x, y);
    return (this.regions[x][y]);
};
World.prototype.get = function(x, y) {
    // Get the tile in the selected area
    return (this.getRegion(
                Math.floor(x / this.regionSize),
                Math.floor(y / this.regionSize))
                .get(x % this.regionSize, y % this.regionSize));
};
World.prototype.set = function(tile, x, y) {
    x = Math.floor(x);
    y = Math.floor(y);
    this.getRegion(
            Math.floor(x / this.regionSize),
            Math.floor(y / this.regionSize))
            .set(tile, x % this.regionSize, y % this.regionSize);
};
/**
 * Set the current focus of the world.
 * @param {type} x The pixel x-component to focus the viewplane on
 * @param {type} y The pixel y-component to focus the viewplane on
 */
World.prototype.setFocus = function(x, y) {
    this.focusX = x;
    this.focusY = y;
};
World.prototype.render = function() {
    //  Calculations for screen-only rendering of regions
    // Region size in px
    var r = this.regionSize * this.imgResolution;
    var startX  = Math.floor((this.focusX - this.game.canvas.width  / 2) / r),
        startY  = Math.floor((this.focusY - this.game.canvas.height / 2) / r),
        endX    = Math.floor((this.focusX + this.game.canvas.width  / 2) / r),
        endY    = Math.floor((this.focusY + this.game.canvas.height / 2) / r);

    this.game.ctx.beginPath();

    for(var x = startX; x <= endX; x ++)
    for(var y = startY; y <= endY; y ++) {
        this.getRegion(x, y).render(this.game.ctx, x * r, y * r);

        this.game.ctx.moveTo(x * r + r, y * r);
        this.game.ctx.lineTo(x * r, y * r);
        this.game.ctx.lineTo(x * r, y * r + r);
    }
    this.game.ctx.stroke();
};

var Region = function(world, size, x, y, compressedData) {
    this.world = world;
    this.size = size;
    this.x = x;
    this.y = y;

    this.displayCache = undefined;
    this.ctx = undefined;

    this.data = undefined;
    this.ctxDirty = false;

    if (!compressedData) {
        // Dirty flag; set to true if anything is modified, requiring another
        // compress to update.
        this.compressedDirty = true;
        this.compressedData = "";
    } else {
        // No compression update needed yet
        this.compressedDirty = false;
        this.compressedData = compressedData;
    }

    this.generate();
};
Region.prototype.render = function(ctx, x, y) {
    if (this.ctxDirty || !this.displayCache)
        this.updateDisplayCache();
    ctx.drawImage(this.displayCache, x, y);
};
Region.prototype.freeDisplayCache = function() {
    this.displayCache = undefined;
    this.ctx = undefined;
};
Region.prototype.updateDisplayCache = function() {
    if (!this.displayCache) {
        var cache = this.world.requestDisplayCache(this);
        this.displayCache = cache.canvas;
        this.ctx = cache.ctx;
    }

    this.ctx.clearRect(0, 0, this.displayCache.width, this.displayCache.height);

    for(var x = 0; x < this.size; x++)
    for(var y = 0; y < this.size; y++)
        this.world.game.tileRegistry.renderTile(this.ctx, this.get(x, y),
            x * this.world.imgResolution, y * this.world.imgResolution);

    this.ctxDirty = false;
};
Region.prototype.generate = function() {
    // Generate or load

    if (!this.compressedData) {
        this.data = [];
        var rSize = this.world.regionSize;
        for(var x = 0; x < this.size; x ++) {
            this.data[x] = [];
            for(var y = 0; y < this.size; y ++)
                this.data[x][y] =
                    this.world.generator.tileGen(
                    this.x * rSize + x, this.y * rSize + y);
        };
    } else
        this.data = JSON.parse(
            LZString.decompressFromUTF16(this.compressedData));
};
Region.prototype.compress = function() {
    // Only compress if needed
    if (this.compressedDirty) {
        this.compressedDirty = false;
        this.compressedData =
                LZString.compressToUTF16(JSON.stringify(this.data));
    }
};
Region.prototype.get = function(x, y) {
    // Fix negative modulus
    if (x < 0)
        x += this.size;
    if (y < 0)
        y += this.size;
    return (this.data[x][y]);
};
Region.prototype.set = function(tile, x, y) {
    // Fix negative modulus
    if (x < 0)
        x += this.size;
    if (y < 0)
        y += this.size;
    this.data[x][y] = tile;

    this.ctxDirty = true;
    this.compressedDirty = true;
};

var TerrainGenerator = function(tileRegistry, method) {
    this.tileRegistry = tileRegistry;
    this.method = method || {
        scale: 0.05,
        shapes: [
            {
                start: Number.MIN_SAFE_INTEGER,
                eval: new Evaluable(0),
                types: [{
                    start: 0, tile: "air" }
                ]
            }, {
                start: -5,
                eval: new Evaluable("*", {
                    a: new Evaluable("ratio"),
                    b: new Evaluable("noise")
                }),
                types: [{
                    start: 0, tile: "air" }, {
                    start: 0.1, tile: "stone" }
                ]
            }, {
                start: 5,
                eval: new Evaluable(0),
                types: [{
                    start: 0, tile: "stone" }
                ]
            }, {
                start: 20,
                eval: new Evaluable("*", {
                    a: new Evaluable("noise", 0),
                    b: new Evaluable("lerp", {
                        a: new Evaluable("noise", {
                            id: 1,
                            scale: 0.3
                        }),
                        b: 1,
                        x: new Evaluable("ratio")
                    })
                }),
                types: [{
                    start: 0, tile: "stone" }, {
                    start: 0.36, tile: "air" }, {
                    start: 0.64, tile: "stone" }
                ]
            }, {
                start: 1024,
                eval: new Evaluable("noise"),
                types: [{
                    start: 0, tile: "stone" }, {
                    start: 0.36, tile: "air" }, {
                    start: 0.64, tile: "stone" }
                ]
            }
        ]
    };
};
/**
 * Generates a single tile at x, y
 * @param {type} x x-coordinate of the tile
 * @param {type} y y-coordinate of the tile
 * @returns {Tile/Number} id or Tile object
 */
TerrainGenerator.prototype.tileGen = function(x, y) {
    var i = 0, shapes =  this.method.shapes;
    while (i < shapes.length && y >= shapes[i].start)
       i++;
    // Now one index too high
    i--;
    var ratio;
    if (shapes[i + 1] !== undefined)
        ratio = (y - shapes[i].start) / (shapes[i + 1].start - shapes[i].start);
    else
        ratio = 0;
    var val = this.method.shapes[i].eval.eval(x * this.method.scale,
                                              y * this.method.scale, ratio);

    var j = 0, types =  shapes[i].types;
    while (j < types.length && val >= types[j].start)
       j++;
    j--;

    return (this.tileRegistry.makeTile(types[j].tile));
};

// Evaluable module, used as a safer alternative to window.eval
var Evaluable = (function() {
    var self = function () {
        if (typeof arguments[0] === "number") {
            this.opr = "const";
            this.data = arguments[0];
        } else {
            this.opr = arguments[0];
            this.data = arguments[1];
        }
    };

    var noiseGenerators = {};
    var getNoise = function(x, y, id) {
        if (!noiseGenerators[id])
            noiseGenerators[id] = new SimplexNoise(Math);
        return (0.5 * (1 + noiseGenerators[id].noise(x, y)));
    };

    function process(val, x, y, ratio) {
        if (val === undefined)
            return (0);
        if (typeof val === "number") return val;
        return (val.eval(x, y, ratio));
    }

    self.prototype.eval = function (x, y, ratio) {
        switch (this.opr) {
            case "const":
                return (process(this.data, x, y, ratio));
            case "ratio":
                return (ratio);
            case "x":
                return (x);
            case "y":
                return (y);

            // Now for actual operands

            case "+":
                return (process(this.data.a, x, y, ratio) +
                        process(this.data.b, x, y, ratio));
            case "*":
                return (process(this.data.a, x, y, ratio) *
                        process(this.data.b, x, y, ratio));
            case "-":
                return (process(this.data.a, x, y, ratio) -
                        process(this.data.b, x, y, ratio));
            case "/":
                return (process(this.data.a, x, y, ratio) /
                        process(this.data.b, x, y, ratio));
            case "noise":
                var id, scale = 1;
                if (typeof this.data === "object") {
                    id = process(this.data.id, x, y, ratio);
                    scale = process(this.data.scale, x, y, ratio);
                } else
                    id = process(this.data, x, y, ratio);
                return (getNoise(x * scale, y * scale, id));
            case "lerp":
                var r = process(this.data.x, x, y, ratio);
                return ((1 - r) * process(this.data.a, x, y, ratio) +
                        (    r) * process(this.data.b, x, y, ratio));
            case "inrange":
                var x = process(this.data.x, x, y, ratio);
                return (process(this.data.a, x, y, ratio) <= x &&
                        process(this.data.b, x, y, ratio) > x ? 1 : 0);
        }
    };

    return (self);
})();

var Tile = function(id, data) {
    this.id = id;
    this.data = data;
};

var TileRegistry = function(game) {
    this.game = game;
    this.tileDef = [];
    // Tile indexes by name
    this.tileIndexes = {};
};
/**
 *
 * @param {String} name Name of the tile
 * @param {String} imgSrc Source location for the 24x24 image
 * @param {Boolean} solid True if the tile is solid
 * @param {function} create Callback for creation event of block
 * @param {function} update Callback for update event of block
 * @param {function} active Callback for mouse click events on the block
 * @param {Object} data Default extra data for the block.
 * @returns {TileRegistry.tileIndexes} id of the tile
 */
TileRegistry.prototype.add = function(
        name, imgSrc, solid, create, update, active, data) {
    this.tileIndexes[name] = this.tileDef.length;
    // True by default
    if (solid === undefined)
        solid = true;
    this.tileDef.push({
        name:   name,
        imgSrc: imgSrc,
        solid:  solid,
        data:   data,
        create: create,
        update: update,
        active: active
    });
    return (this.tileIndexes[name]);
};
TileRegistry.prototype.requireResources = function() {
    for(var i = 0;i < this.tileDef.length; i++)
        this.game.res.require(this.tileDef[i].imgSrc);
};
TileRegistry.prototype.isSolid = function(kind) {
    if (kind === undefined)
        kind = "invalid";
    var id;
    if (typeof kind === "string")
        id = this.tileIndexes[kind];
    else
        id = kind;
        return (this.tileDef[id].solid);
};
/**
 * Makes a new tile, either by id or object with extra data
 * @param {Number/String} kind - The id or name of the tile
 * @return {Tile/Number} id or Tile object
 */
TileRegistry.prototype.makeTile = function(kind) {
    var id;
    if (typeof kind === "string")
        id = this.tileIndexes[kind];
    else
        id = kind;
    // Set to invalid if something goes wrong
    if (this.tileDef[id] === undefined)
        id = this.tileIndexes["invalid"];

    if (this.tileDef[id].hasData)
        return (new Tile(id, this.tileDef[id].data));
    return (id);
};
TileRegistry.prototype.renderTile = function(ctx, kind, x, y) {
    var id;
    if (typeof kind === "string")
        id = this.tileIndexes[kind];
    else
        id = kind;
    if (this.tileDef[id] === undefined)
        return;
    var img = this.game.res.get(this.tileDef[id].imgSrc);
    if (img)
        ctx.drawImage(img, x, y);
};

<|endoftext|>


<commit_before>GS.GameVersion = "build 8 - june 27, 2014";

GS.GameStates = {
	Dispose: 0,
	PreLoad: 1,
	Loading: 2,
	PostLoad: 3,
	Play: 4,
	Menu: 5,
};

GS.Game = function() {
	GS.Base.call(this);

	GS.Settings.loadSettings();

	this.state = GS.GameStates.PreLoad;
	this.nextState = null;
	this.updated = false;
	this.firstLoad = true;
	this.firstPlay = true;
	this.mapWon = false;
	this.restartedLevel = false;

	this.antialias = false;
	this.clearColor = 0x336699;
	this.cameraFov = GS.Settings.fov;
	this.cameraFar = 1500;

	this.noMenu = false;
	this.useAssetsZip = false;

	if (GS.BuildOverride === true) {
		this.useAssetsZip = true;
	}

	this.showFPS = GS.Settings.showFPS;
	this.showPerformanceDebugMeters = false;
};

GS.Game.prototype = GS.inherit(GS.Base, {
	constructor: GS.Game,

	init: function() {
		GS.DebugUI.init();
		GS.DebugUI.visible = false;

		this.loadingUI = new GS.LoadingUI();
		this.loadingUI.init();

		this.uiManager = new GS.UIManager();
		this.uiManager.init();

		this.soundManager = new GS.SoundManager();
		this.soundManager.init();

		this.musicManager = new GS.MusicManager();
		this.musicManager.init();

		this.initAssetLoader();

		this.keys = {
			Enter: 13,
			Escape: 27,
			Tab: 9,
		};

		GS.Base.prototype.init.call(this);

		this.graphicsManager = new GS.GraphicsManager(this.renderer, this.camera);
		this.graphicsManager.init();
	},

	preLoad: function() {
		this.uiManager.reset();

		this.loadingUI.percentLoaded = 0;
		this.loadingUI.show();

		this.scene = new THREE.Scene();
		this.scene.fog = new THREE.Fog(new THREE.Color().setRGB(0, 0, 0).getHex(), 500, 900);

		if (!this.assetLoader.loaded) {
			this.assetLoader.load();
			this.nextState = GS.GameStates.Loading;
		} else {
			this.nextState = GS.GameStates.PostLoad;
		}
	},

	initAssetLoader: function() {
		var that = this;

		if (this.useAssetsZip) {
			this.assetLoader = new GS.ZipAssetLoader(this.soundManager.ctx);
		} else {
			this.assetLoader = new GS.AssetLoader(this.soundManager.ctx);
		}

		this.assetLoader.init();
		this.assetLoader.addEventListener("progress", function(e) {
			that.loadingUI.updateProgress(e);
		});
		this.assetLoader.addEventListener("load", function(e) {
			that.loadingUI.updateProgress(e);
			that.nextState = GS.GameStates.PostLoad;
		});
	},

	postLoad: function() {
		if (this.firstLoad) {
			this.loadingUI.spinnerOnly = true;
			this.uiManager.initComponents(this.assetLoader.assets);
			this.openMenu();
			this.firstLoad = false;

			if (this.noMenu) {
				this.newGame();
			}
		} else {
			this.initComponents(this.assetLoader.assets);
			this.uiManager.initComponents(this.assetLoader.assets, this.grid);
			this.uiManager.useIngameMenu();

			// GAME.grid.exportMapToOBJ();

			this.nextState = GS.GameStates.Play;
			this.graphicsManager.monochromeEnabled = false;

			if (this.grid.aiManager.script !== undefined && !this.restartedLevel) {
				this.musicManager.playTrack(this.grid.aiManager.script.musicTrack);
			}
			this.restartedLevel = false;

			if (this.firstPlay) {
				this.firstPlay = false;
			}
		}
	},

	play: function() {
		GS.InputHelper.checkPressedKeys();

		if (!GS.InputHelper.keysPressed && GS.InputHelper.isKeyDown(this.keys.Escape)) {
			this.openMenu();
		}

		if (!this.grid.aiManager.mapWon && !GS.InputHelper.keysPressed && GS.InputHelper.isKeyDown(this.keys.Tab)) {
			this.uiManager.automap.visible = !this.uiManager.automap.visible;
			this.uiManager.overrideRedraw = true;
			this.graphicsManager.monochromeEnabled = this.uiManager.automap.visible;
		}

		if (this.grid.player.dead && !GS.InputHelper.keysPressed && GS.InputHelper.isKeyDown(this.keys.Enter)) {
			this.restartLevel();
		}

		if (this.grid.aiManager.mapWon && !GS.InputHelper.keysPressed && GS.InputHelper.isKeyDown(this.keys.Enter)) {
			if (this.grid.aiManager.script.nextMap !== undefined) {
				this.playerPersistencePackage = this.grid.player.getPersistencePackage();
				this.loadLevel(this.grid.aiManager.script.nextMap);
			}
		}

		if (!this.mapWon && this.grid.aiManager.mapWon) {
			this.mapWon = true;
			this.onMapWon();
		}

		if (!this.grid.aiManager.mapWon) {
			this.grid.update();
			TWEEN.update();
		}
		GS.DebugUI.update();

		this.uiManager.update();
	},

	onMapWon: function() {
		this.graphicsManager.monochromeEnabled = false;
	},

	menu: function() {
		GS.InputHelper.checkPressedKeys();

		if (!GS.InputHelper.keysPressed && GS.InputHelper.isKeyDown(this.keys.Escape)) {
			if (this.grid !== undefined) {
				this.closeMenu();
			}
		}

		this.uiManager.update();
	},

	openMenu: function() {
		this.nextState = GS.GameStates.Menu;

		if (this.grid !== undefined) {
			this.grid.player.inMenu = true;
			this.grid.player.controls.disable();
			this.grid.player.controls.detachEvents();
		}

		this.graphicsManager.monochromeEnabled = true;
		this.uiManager.menuActive = true;

		GS.DebugUI.visible = false;
	},

	closeMenu: function() {
		this.nextState = GS.GameStates.Play;

		if (this.grid !== undefined) {
			this.grid.player.inMenu = false;
			this.grid.player.controls.attachEvents();
			this.grid.player.controls.enable();
			this.grid.aiManager.resume();

			if (this.mapWon || !this.uiManager.automap.visible) {
				this.graphicsManager.monochromeEnabled = false;
			}
		} else {
			this.graphicsManager.monochromeEnabled = false;
		}

		this.uiManager.menuActive = false;
		GS.DebugUI.visible = GS.Settings.showHUD;
	},

	restartLevel: function() {
		this.nextState = GS.GameStates.Dispose;
		this.restartedLevel = true;
	},

	loadLevel: function(name) {
		if (this.uiManager.menuActive) {
			this.closeMenu();
		}

		this.mapName = name;
		this.nextState = GS.GameStates.Dispose;
	},

	newGame: function() {
		if (this.uiManager.menuActive) {
			this.closeMenu();
		}

		this.mapName = "airstrip1";
		this.nextState = GS.GameStates.Dispose;
	},

	initComponents: function(assets) {
		var that = this;
		var map = this.assetLoader.mapLoader.parse(assets[GS.AssetTypes.Map][this.mapName]);
		this.mapWon = false;

		var viewFactory = new GS.ViewFactory(this.renderer, map, assets);
		viewFactory.init();
		var gridFactory = new GS.GridFactory(viewFactory, this.soundManager, this.renderer, this.scene, this.camera);
		this.grid = gridFactory.getGrid(map);

		this.soundManager.initSounds(assets[GS.AssetTypes.Sound]);
		this.musicManager.initTracks(assets[GS.AssetTypes.MusicTrack]);

		if (this.playerPersistencePackage !== undefined) {
			this.grid.player.applyPersistencePackage(this.playerPersistencePackage);
			this.playerPersistencePackage = undefined;
		}

		this.grid.update();
		this.graphicsManager.setGrid(this.grid);

		this.grid.player.controls.addEventListener("pointerLockDisabled", function() { that.openMenu(); });

		// console.log("collision triangles", viewFactory.triangleCount);
	},

	update: function() {
		if (this.showPerformanceDebugMeters) {
			var time = window.performance.now();
		}

		if (this.state == GS.GameStates.Dispose) {
			if (!this.updated) {
				this.updated = true;
				this.dispose();
			}
		} else
		if (this.state == GS.GameStates.PreLoad) {
			if (!this.updated) {
				this.updated = true;
				this.preLoad();
			}
		} else
		if (this.state == GS.GameStates.Loading) {
			this.updated = true;
		} else
		if (this.state == GS.GameStates.PostLoad) {
			if (!this.updated) {
				this.updated = true;
				this.postLoad();
			}
		} else
		if (this.state == GS.GameStates.Play) {
			this.updated = true;
			this.play();
		} else
		if (this.state == GS.GameStates.Menu) {
			this.updated = true;
			this.menu();
		}

		if (this.nextState != null) {
			if (this.updated) {
				if (this.nextState == GS.GameStates.Play || this.nextState == GS.GameStates.Menu) {
					this.loadingUI.hide();
					this.uiManager.show();
				}

				this.state = this.nextState;
				this.nextState = null;
				this.updated = false;
			}
		}

		if (this.showPerformanceDebugMeters) {
			this.updateTime = (window.performance.now() - time).toFixed(2);
			GS.DebugUI.trackNumericValue("updateTime", this.updateTime);
		}
	},

	draw: function() {
		if (this.showPerformanceDebugMeters) {
			var time = window.performance.now();
		}

		if (this.state == GS.GameStates.PreLoad) {
			this.loadingUI.draw();
		} else
		if (this.state == GS.GameStates.Loading) {
			this.loadingUI.draw();
		} else
		if (this.state == GS.GameStates.PostLoad) {
			this.loadingUI.draw();
		} else
		if (this.state == GS.GameStates.Play || this.state == GS.GameStates.Menu) {
			if (this.grid !== undefined) {
				this.graphicsManager.draw();
			}

			this.uiManager.draw();
		}

		if (this.showPerformanceDebugMeters) {
			this.drawTime = (window.performance.now() - time).toFixed(2);
			GS.DebugUI.trackNumericValue("drawTime", this.drawTime);
		}
	},

	onResize: function() {
		GS.Base.prototype.onResize.call(this);

		this.uiManager.onResize();
		this.loadingUI.onResize();

		if (this.graphicsManager !== undefined) {
			this.graphicsManager.onResize();
		}

		if (this.grid !== undefined) {
			this.grid.onResize();
			this.grid.player.playerView.onResize();
		}
	},

	updateFov: function() {
		this.cameraFov = GS.Settings.fov;
		this.camera.fov = GS.Settings.fov;
		this.camera.updateProjectionMatrix();

		if (this.state == GS.GameStates.Play) {
			this.grid.updateFov();
		}
	},

	disposeEnd: function() {
		TWEEN.removeAll();
		this.grid = undefined;
		this.graphicsManager.reset();
		this.scene = undefined;
		this.uiManager.dispose();

		this.nextState = GS.GameStates.PreLoad;
	},

	dispose: function() {
		var that = this;

		if (this.grid !== undefined) {
			this.grid.player.controls.dispose(function() {
				that.disposeEnd();
			});
		} else {
			this.disposeEnd();
		}
	},
});

var GAME;
window.addEventListener("load", function() {
	GS.Detector.run(function() {
		GAME = new GS.Game();
		GAME.init();
	});
}, false);<commit_msg>build 9<commit_after>GS.GameVersion = "build 9 - july 7, 2014";

GS.GameStates = {
	Dispose: 0,
	PreLoad: 1,
	Loading: 2,
	PostLoad: 3,
	Play: 4,
	Menu: 5,
};

GS.Game = function() {
	GS.Base.call(this);

	GS.Settings.loadSettings();

	this.state = GS.GameStates.PreLoad;
	this.nextState = null;
	this.updated = false;
	this.firstLoad = true;
	this.firstPlay = true;
	this.mapWon = false;
	this.restartedLevel = false;

	this.antialias = false;
	this.clearColor = 0x336699;
	this.cameraFov = GS.Settings.fov;
	this.cameraFar = 1500;

	this.noMenu = false;
	this.useAssetsZip = false;

	if (GS.BuildOverride === true) {
		this.useAssetsZip = true;
	}

	this.showFPS = GS.Settings.showFPS;
	this.showPerformanceDebugMeters = false;
};

GS.Game.prototype = GS.inherit(GS.Base, {
	constructor: GS.Game,

	init: function() {
		GS.DebugUI.init();
		GS.DebugUI.visible = false;

		this.loadingUI = new GS.LoadingUI();
		this.loadingUI.init();

		this.uiManager = new GS.UIManager();
		this.uiManager.init();

		this.soundManager = new GS.SoundManager();
		this.soundManager.init();

		this.musicManager = new GS.MusicManager();
		this.musicManager.init();

		this.initAssetLoader();

		this.keys = {
			Enter: 13,
			Escape: 27,
			Tab: 9,
		};

		GS.Base.prototype.init.call(this);

		this.graphicsManager = new GS.GraphicsManager(this.renderer, this.camera);
		this.graphicsManager.init();
	},

	preLoad: function() {
		this.uiManager.reset();

		this.loadingUI.percentLoaded = 0;
		this.loadingUI.show();

		this.scene = new THREE.Scene();
		this.scene.fog = new THREE.Fog(new THREE.Color().setRGB(0, 0, 0).getHex(), 500, 900);

		if (!this.assetLoader.loaded) {
			this.assetLoader.load();
			this.nextState = GS.GameStates.Loading;
		} else {
			this.nextState = GS.GameStates.PostLoad;
		}
	},

	initAssetLoader: function() {
		var that = this;

		if (this.useAssetsZip) {
			this.assetLoader = new GS.ZipAssetLoader(this.soundManager.ctx);
		} else {
			this.assetLoader = new GS.AssetLoader(this.soundManager.ctx);
		}

		this.assetLoader.init();
		this.assetLoader.addEventListener("progress", function(e) {
			that.loadingUI.updateProgress(e);
		});
		this.assetLoader.addEventListener("load", function(e) {
			that.loadingUI.updateProgress(e);
			that.nextState = GS.GameStates.PostLoad;
		});
	},

	postLoad: function() {
		if (this.firstLoad) {
			this.loadingUI.spinnerOnly = true;
			this.uiManager.initComponents(this.assetLoader.assets);
			this.openMenu();
			this.firstLoad = false;

			if (this.noMenu) {
				this.newGame();
			}
		} else {
			this.initComponents(this.assetLoader.assets);
			this.uiManager.initComponents(this.assetLoader.assets, this.grid);
			this.uiManager.useIngameMenu();

			// GAME.grid.exportMapToOBJ();

			this.nextState = GS.GameStates.Play;
			this.graphicsManager.monochromeEnabled = false;

			if (this.grid.aiManager.script !== undefined && !this.restartedLevel) {
				this.musicManager.playTrack(this.grid.aiManager.script.musicTrack);
			}
			this.restartedLevel = false;

			if (this.firstPlay) {
				this.firstPlay = false;
			}
		}
	},

	play: function() {
		GS.InputHelper.checkPressedKeys();

		if (!GS.InputHelper.keysPressed && GS.InputHelper.isKeyDown(this.keys.Escape)) {
			this.openMenu();
		}

		if (!this.grid.aiManager.mapWon && !GS.InputHelper.keysPressed && GS.InputHelper.isKeyDown(this.keys.Tab)) {
			this.uiManager.automap.visible = !this.uiManager.automap.visible;
			this.uiManager.overrideRedraw = true;
			this.graphicsManager.monochromeEnabled = this.uiManager.automap.visible;
		}

		if (this.grid.player.dead && !GS.InputHelper.keysPressed && GS.InputHelper.isKeyDown(this.keys.Enter)) {
			this.restartLevel();
		}

		if (this.grid.aiManager.mapWon && !GS.InputHelper.keysPressed && GS.InputHelper.isKeyDown(this.keys.Enter)) {
			if (this.grid.aiManager.script.nextMap !== undefined) {
				this.playerPersistencePackage = this.grid.player.getPersistencePackage();
				this.loadLevel(this.grid.aiManager.script.nextMap);
			}
		}

		if (!this.mapWon && this.grid.aiManager.mapWon) {
			this.mapWon = true;
			this.onMapWon();
		}

		if (!this.grid.aiManager.mapWon) {
			this.grid.update();
			TWEEN.update();
		}
		GS.DebugUI.update();

		this.uiManager.update();
	},

	onMapWon: function() {
		this.graphicsManager.monochromeEnabled = false;
	},

	menu: function() {
		GS.InputHelper.checkPressedKeys();

		if (!GS.InputHelper.keysPressed && GS.InputHelper.isKeyDown(this.keys.Escape)) {
			if (this.grid !== undefined) {
				this.closeMenu();
			}
		}

		this.uiManager.update();
	},

	openMenu: function() {
		this.nextState = GS.GameStates.Menu;

		if (this.grid !== undefined) {
			this.grid.player.inMenu = true;
			this.grid.player.controls.disable();
			this.grid.player.controls.detachEvents();
		}

		this.graphicsManager.monochromeEnabled = true;
		this.uiManager.menuActive = true;

		GS.DebugUI.visible = false;
	},

	closeMenu: function() {
		this.nextState = GS.GameStates.Play;

		if (this.grid !== undefined) {
			this.grid.player.inMenu = false;
			this.grid.player.controls.attachEvents();
			this.grid.player.controls.enable();
			this.grid.aiManager.resume();

			if (this.mapWon || !this.uiManager.automap.visible) {
				this.graphicsManager.monochromeEnabled = false;
			}
		} else {
			this.graphicsManager.monochromeEnabled = false;
		}

		this.uiManager.menuActive = false;
		GS.DebugUI.visible = GS.Settings.showHUD;
	},

	restartLevel: function() {
		this.nextState = GS.GameStates.Dispose;
		this.restartedLevel = true;
	},

	loadLevel: function(name) {
		if (this.uiManager.menuActive) {
			this.closeMenu();
		}

		this.mapName = name;
		this.nextState = GS.GameStates.Dispose;
	},

	newGame: function() {
		if (this.uiManager.menuActive) {
			this.closeMenu();
		}

		this.mapName = "airstrip1";
		this.nextState = GS.GameStates.Dispose;
	},

	initComponents: function(assets) {
		var that = this;
		var map = this.assetLoader.mapLoader.parse(assets[GS.AssetTypes.Map][this.mapName]);
		this.mapWon = false;

		var viewFactory = new GS.ViewFactory(this.renderer, map, assets);
		viewFactory.init();
		var gridFactory = new GS.GridFactory(viewFactory, this.soundManager, this.renderer, this.scene, this.camera);
		this.grid = gridFactory.getGrid(map);

		this.soundManager.initSounds(assets[GS.AssetTypes.Sound]);
		this.musicManager.initTracks(assets[GS.AssetTypes.MusicTrack]);

		if (this.playerPersistencePackage !== undefined) {
			this.grid.player.applyPersistencePackage(this.playerPersistencePackage);
			this.playerPersistencePackage = undefined;
		}

		this.grid.update();
		this.graphicsManager.setGrid(this.grid);

		this.grid.player.controls.addEventListener("pointerLockDisabled", function() { that.openMenu(); });

		// console.log("collision triangles", viewFactory.triangleCount);
	},

	update: function() {
		if (this.showPerformanceDebugMeters) {
			var time = window.performance.now();
		}

		if (this.state == GS.GameStates.Dispose) {
			if (!this.updated) {
				this.updated = true;
				this.dispose();
			}
		} else
		if (this.state == GS.GameStates.PreLoad) {
			if (!this.updated) {
				this.updated = true;
				this.preLoad();
			}
		} else
		if (this.state == GS.GameStates.Loading) {
			this.updated = true;
		} else
		if (this.state == GS.GameStates.PostLoad) {
			if (!this.updated) {
				this.updated = true;
				this.postLoad();
			}
		} else
		if (this.state == GS.GameStates.Play) {
			this.updated = true;
			this.play();
		} else
		if (this.state == GS.GameStates.Menu) {
			this.updated = true;
			this.menu();
		}

		if (this.nextState != null) {
			if (this.updated) {
				if (this.nextState == GS.GameStates.Play || this.nextState == GS.GameStates.Menu) {
					this.loadingUI.hide();
					this.uiManager.show();
				}

				this.state = this.nextState;
				this.nextState = null;
				this.updated = false;
			}
		}

		if (this.showPerformanceDebugMeters) {
			this.updateTime = (window.performance.now() - time).toFixed(2);
			GS.DebugUI.trackNumericValue("updateTime", this.updateTime);
		}
	},

	draw: function() {
		if (this.showPerformanceDebugMeters) {
			var time = window.performance.now();
		}

		if (this.state == GS.GameStates.PreLoad) {
			this.loadingUI.draw();
		} else
		if (this.state == GS.GameStates.Loading) {
			this.loadingUI.draw();
		} else
		if (this.state == GS.GameStates.PostLoad) {
			this.loadingUI.draw();
		} else
		if (this.state == GS.GameStates.Play || this.state == GS.GameStates.Menu) {
			if (this.grid !== undefined) {
				this.graphicsManager.draw();
			}

			this.uiManager.draw();
		}

		if (this.showPerformanceDebugMeters) {
			this.drawTime = (window.performance.now() - time).toFixed(2);
			GS.DebugUI.trackNumericValue("drawTime", this.drawTime);
		}
	},

	onResize: function() {
		GS.Base.prototype.onResize.call(this);

		this.uiManager.onResize();
		this.loadingUI.onResize();

		if (this.graphicsManager !== undefined) {
			this.graphicsManager.onResize();
		}

		if (this.grid !== undefined) {
			this.grid.onResize();
			this.grid.player.playerView.onResize();
		}
	},

	updateFov: function() {
		this.cameraFov = GS.Settings.fov;
		this.camera.fov = GS.Settings.fov;
		this.camera.updateProjectionMatrix();

		if (this.state == GS.GameStates.Play) {
			this.grid.updateFov();
		}
	},

	disposeEnd: function() {
		TWEEN.removeAll();
		this.grid = undefined;
		this.graphicsManager.reset();
		this.scene = undefined;
		this.uiManager.dispose();

		this.nextState = GS.GameStates.PreLoad;
	},

	dispose: function() {
		var that = this;

		if (this.grid !== undefined) {
			this.grid.player.controls.dispose(function() {
				that.disposeEnd();
			});
		} else {
			this.disposeEnd();
		}
	},
});

var GAME;
window.addEventListener("load", function() {
	GS.Detector.run(function() {
		GAME = new GS.Game();
		GAME.init();
	});
}, false);<|endoftext|>