<commit_before>"use strict";
/* globals $, app */

(function(Poll) {

	var View = function(pollData) {
		this.pollData = pollData;
	};

	View.prototype.load = function() {
		var self = this;

		require(['components'], function(components) {
			var posts = components.get('post');
			if (posts.length > 0 && parseInt(posts.eq(0).data('pid'), 10) === parseInt(self.pollData.info.pid, 10)) {
				app.parseAndTranslate('poll/view', { poll: self.pollData }, function(panel) {
					posts.eq(0).find('[component="post/content"]').prepend(panel);

					self.dom = {
						panel: panel,
						votingForm: panel.find('.poll-voting-form'),
						messages: panel.find('.poll-view-messages'),
						votingPanel: panel.find('.poll-view-voting'),
						resultsPanel: panel.find('.poll-view-results'),
						voteButton: panel.find('.poll-button-vote'),
						updateVoteButton: panel.find('.poll-button-update-vote'),
						removeVoteButton: panel.find('.poll-button-remove-vote'),
						votingPanelButton: panel.find('.poll-button-voting'),
						resultsPanelButton: panel.find('.poll-button-results'),
						editButton: panel.find('.poll-button-edit')
					};

					self.hideMessage();

					self.pollEndedOrDeleted();
					self.hasVotedAndVotingUpdateDisallowed();

					if (!app.user.uid || self.pollData.hasVoted) {
						self.showResultsPanel();
					} else {
						self.showVotingPanel();
					}

					Actions.forEach(function(action) {
						action.register(self);
					});
				});
			}
		});
	};

	View.prototype.hasPollEndedOrDeleted = function() {
		return (parseInt(this.pollData.info.ended, 10) === 1 || parseInt(this.pollData.info.deleted, 10) === 1);
	};

	View.prototype.voteUpdateAllowed = function() {
		return parseInt(this.pollData.settings.disallowVoteUpdate, 10) !== 1;
	};

	View.prototype.pollEndedOrDeleted = function() {
		if (this.hasPollEndedOrDeleted()) {
			this.showResultsPanel();
			this.hideVotingPanelButton();
			this.showMessage('[[poll:voting_unavailable_title]]', '[[poll:voting_unavailable_message]]');
		}
	};

	View.prototype.hasVotedAndVotingUpdateDisallowed = function() {
		if (this.pollData.hasVoted && !this.voteUpdateAllowed()) {
			this.showResultsPanel();
			this.hideVotingPanelButton();
			this.showMessage('[[poll:voting_unavailable_title]]', '[[poll:voting_update_disallowed_message]]');
		}
	};

	View.prototype.update = function(pollData) {
		this.pollData = pollData;

		this.pollEndedOrDeleted();

		this.pollData.options.forEach(function(option) {
			var el = this.dom.resultsPanel.find('[data-poll-option-id=' + option.id + ']');
			el.find('.poll-result-votecount span').text(option.voteCount);
			el.find('.poll-result-progressbar').css('width', option.percentage + '%')
				.find('span').text(option.percentage);
		}, this);
	};

	View.prototype.showMessage = function(title, content) {
		var self = this;

		app.parseAndTranslate('poll/view/messages', {title: title, content: content}, function(html) {
			self.dom.messages.html(html).removeClass('hidden');
		});
	};

	View.prototype.hideMessage = function() {
		this.dom.messages.addClass('hidden');
	};

	View.prototype.showOptionDetails = function(details) {
		app.parseAndTranslate('poll/view/details', details, function(html) {
			bootbox.dialog({
				message: html,
				buttons: {
					close: {
						label: 'Close'
					}
				}
			});
		})
	};

	View.prototype.fillVotingForm = function() {
		var self = this;
		this.resetVotingForm();
		if (this.pollData.vote && this.pollData.vote.options) {
			this.pollData.vote.options.forEach(function (id) {
				self.dom.votingForm
					.find('[data-poll-option-id="' + id + '"].poll-view-option input')
					.prop('checked', true);
			});
		}
	};

	View.prototype.resetVotingForm = function() {
		this.dom.votingForm.get(0).reset();
	};

	View.prototype.showVotingPanel = function() {
		this.hideResultsPanel();
		this.showResultsPanelButton();
		if (this.pollData.hasVoted) {
			if (this.voteUpdateAllowed() && !this.hasPollEndedOrDeleted()) {
				this.showUpdateVoteButton();
				this.showRemoveVoteButton();
				this.fillVotingForm();
			}
		} else {
			this.showVoteButton();
		}
		this.dom.votingPanel.removeClass('hidden');
	};

	View.prototype.hideVotingPanel = function() {
		this.hideResultsPanelButton();
		this.hideUpdateVoteButton();
		this.hideRemoveVoteButton();
		this.resetVotingForm();
		this.hideVoteButton();
		this.dom.votingPanel.addClass('hidden');
	};

	View.prototype.showResultsPanel = function() {
		this.hideVotingPanel();
		if (this.voteUpdateAllowed() && !this.hasPollEndedOrDeleted()) {
			this.showVotingPanelButton();
		} else {
			this.hideVotingPanelButton();
		}
		this.dom.resultsPanel.removeClass('hidden');
	};

	View.prototype.hideResultsPanel = function() {
		this.hideVotingPanelButton();
		this.dom.resultsPanel.addClass('hidden');
	};

	View.prototype.showVoteButton = function() {
		this.dom.voteButton.removeClass('hidden');
	};

	View.prototype.hideVoteButton = function() {
		this.dom.voteButton.addClass('hidden');
	};

	View.prototype.showUpdateVoteButton = function() {
		this.dom.updateVoteButton.removeClass('hidden');
	};

	View.prototype.hideUpdateVoteButton = function() {
		this.dom.updateVoteButton.addClass('hidden');
	};

	View.prototype.showRemoveVoteButton = function() {
		this.dom.removeVoteButton.removeClass('hidden');
	};

	View.prototype.hideRemoveVoteButton = function() {
		this.dom.removeVoteButton.addClass('hidden');
	};

	View.prototype.showVotingPanelButton = function() {
		this.dom.votingPanelButton.removeClass('hidden');
	};

	View.prototype.hideVotingPanelButton = function() {
		this.dom.votingPanelButton.addClass('hidden');
	};

	View.prototype.showResultsPanelButton = function() {
		this.dom.resultsPanelButton.removeClass('hidden');
	};

	View.prototype.hideResultsPanelButton = function() {
		this.dom.resultsPanelButton.addClass('hidden');
	};

	var Actions = [
		{
			// Voting
			register: function(view) {
				var self = this;
				view.dom.voteButton.off('click').on('click', function() {
					self.handle(view);
				});
			},
			handle: function(view) {
				var form = view.dom.votingPanel.find('form');
				var votes = form.serializeArray().map(function(option) {
					return parseInt(option.value, 10);
				});

				if (votes.length > 0) {
					var voteData = {
						pollId: view.pollData.info.pollId,
						options: votes
					};

					Poll.sockets.vote(voteData, function(err, result) {
						if (err) {
							return app.alertError(err.message);
						}

						view.showResultsPanel();
					});
				}
			}
		},
		{
			// Voting
			register: function(view) {
				var self = this;
				view.dom.updateVoteButton.off('click').on('click', function() {
					self.handle(view);
				});
			},
			handle: function(view) {
				var form = view.dom.votingPanel.find('form');
				var votes = form.serializeArray().map(function(option) {
					return parseInt(option.value, 10);
				});

				if (votes.length > 0) {
					var voteData = {
						pollId: view.pollData.info.pollId,
						options: votes
					};

					Poll.sockets.updateVote(voteData, function(err, result) {
						if (err) {
							return app.alertError(err.message);
						}
						view.showResultsPanel();
					});
				}
			}
		},
		{
			// Remove vote
			register: function(view) {
				var self = this;
				view.dom.removeVoteButton.off('click').on('click', function() {
					self.handle(view);
				});
			},
			handle: function(view) {
				var voteData = { pollId: view.pollData.info.pollId };

				Poll.sockets.removeVote(voteData, function(err, result) {
					if (err) {
						return app.alertError(err.message);
					}
					view.showResultsPanel();
				});
			}
		},
		{
			// Results button
			register: function(view) {
				var self = this;
				view.dom.resultsPanelButton.off('click').on('click', function() {
					self.handle(view);
				});
			},
			handle: function(view) {
				view.showResultsPanel();
			}
		},
		{
			// To Voting button
			register: function(view) {
				var self = this;
				view.dom.votingPanelButton.off('click').on('click', function() {
					self.handle(view);
				});
			},
			handle: function(view) {
				view.showVotingPanel();
			}
		},
		{
			// Option details
			register: function(view) {
				var self = this;
				view.dom.resultsPanel.off('click').on('click', '.poll-result-votecount', function(e) {
					self.handle(view, e);
				});
			},
			handle: function(view, e) {
				var optionId = $(e.currentTarget).parents('[data-poll-option-id]').data('poll-option-id');

				Poll.sockets.getOptionDetails({
					pollId: view.pollData.info.pollId,
					optionId: optionId
				}, function(err, details) {
					if (err) {
						return app.alertError(err.message);
					}

					view.showOptionDetails(details);
				});
			}
		},
		{
			// Editing
			register: function(view) {
				var self = this;
				view.dom.editButton.off('click').on('click', function() {
					self.handle(view);
				});
			},
			handle: function(view) {
				Poll.sockets.getConfig(null, function(err, config) {
					Poll.creator.show(view.pollData, config, function(data) {
					});
				});
			}
		}
	];

	Poll.view = {
		polls: {},
		load: function(pollData) {
			var view = new View(pollData);
			this.polls[pollData.info.pollId] = view;

			view.load();
		},
		update: function(pollData) {
			var pollId = pollData.info.pollId;
			if (this.polls.hasOwnProperty(pollId)) {
				this.polls[pollId].update(pollData);
			}
		}
	};
})(window.Poll);
<commit_msg>Update view.js<commit_after>"use strict";
/* globals $, app,config  */

(function(Poll) {

	var View = function(pollData) {
		this.pollData = pollData;
	};

	View.prototype.load = function() {
		var self = this;

		require(['components'], function(components) {
			var posts = components.get('post');
			if (posts.length > 0 && parseInt(posts.eq(0).data('pid'), 10) === parseInt(self.pollData.info.pid, 10)) {
				app.parseAndTranslate('poll/view', { poll: self.pollData }, function(panel) {
					posts.eq(0).find('[component="post/content"]').prepend(panel);

					self.dom = {
						panel: panel,
						votingForm: panel.find('.poll-voting-form'),
						messages: panel.find('.poll-view-messages'),
						votingPanel: panel.find('.poll-view-voting'),
						resultsPanel: panel.find('.poll-view-results'),
						voteButton: panel.find('.poll-button-vote'),
						updateVoteButton: panel.find('.poll-button-update-vote'),
						removeVoteButton: panel.find('.poll-button-remove-vote'),
						votingPanelButton: panel.find('.poll-button-voting'),
						resultsPanelButton: panel.find('.poll-button-results'),
						editButton: panel.find('.poll-button-edit')
					};

					self.hideMessage();

					self.pollEndedOrDeleted();
					self.hasVotedAndVotingUpdateDisallowed();

					if (!app.user.uid || self.pollData.hasVoted) {
						self.showResultsPanel();
					} else {
						self.showVotingPanel();
					}

					Actions.forEach(function(action) {
						action.register(self);
					});
				});
			}
		});
	};

	View.prototype.hasPollEndedOrDeleted = function() {
		return (parseInt(this.pollData.info.ended, 10) === 1 || parseInt(this.pollData.info.deleted, 10) === 1);
	};

	View.prototype.voteUpdateAllowed = function() {
		return parseInt(this.pollData.settings.disallowVoteUpdate, 10) !== 1;
	};

	View.prototype.pollEndedOrDeleted = function() {
		if (this.hasPollEndedOrDeleted()) {
			this.showResultsPanel();
			this.hideVotingPanelButton();
			this.showMessage('[[poll:voting_unavailable_title]]', '[[poll:voting_unavailable_message]]');
		}
	};

	View.prototype.hasVotedAndVotingUpdateDisallowed = function() {
		if (this.pollData.hasVoted && !this.voteUpdateAllowed()) {
			this.showResultsPanel();
			this.hideVotingPanelButton();
			this.showMessage('[[poll:voting_unavailable_title]]', '[[poll:voting_update_disallowed_message]]');
		}
	};

	View.prototype.update = function(pollData) {
		this.pollData = pollData;

		this.pollEndedOrDeleted();

		this.pollData.options.forEach(function(option) {
			var el = this.dom.resultsPanel.find('[data-poll-option-id=' + option.id + ']');
			el.find('.poll-result-votecount span').text(option.voteCount);
			el.find('.poll-result-progressbar').css('width', option.percentage + '%')
				.find('span').text(option.percentage);
		}, this);
	};

	View.prototype.showMessage = function(title, content) {
		var self = this;

		app.parseAndTranslate('poll/view/messages', {title: title, content: content}, function(html) {
			self.dom.messages.html(html).removeClass('hidden');
		});
	};

	View.prototype.hideMessage = function() {
		this.dom.messages.addClass('hidden');
	};

	View.prototype.showOptionDetails = function(details) {
		app.parseAndTranslate('poll/view/details', details, function(html) {
			bootbox.dialog({
				message: html,
				buttons: {
					close: {
						label: 'Close'
					}
				}
			});
		})
	};

	View.prototype.fillVotingForm = function() {
		var self = this;
		this.resetVotingForm();
		if (this.pollData.vote && this.pollData.vote.options) {
			this.pollData.vote.options.forEach(function (id) {
				self.dom.votingForm
					.find('[data-poll-option-id="' + id + '"].poll-view-option input')
					.prop('checked', true);
			});
		}
	};

	View.prototype.resetVotingForm = function() {
		this.dom.votingForm.get(0).reset();
	};

	View.prototype.showVotingPanel = function() {
		this.hideResultsPanel();
		this.showResultsPanelButton();
		if (this.pollData.hasVoted) {
			if (this.voteUpdateAllowed() && !this.hasPollEndedOrDeleted()) {
				this.showUpdateVoteButton();
				this.showRemoveVoteButton();
				this.fillVotingForm();
			}
		} else {
			this.showVoteButton();
		}
		this.dom.votingPanel.removeClass('hidden');
	};

	View.prototype.hideVotingPanel = function() {
		this.hideResultsPanelButton();
		this.hideUpdateVoteButton();
		this.hideRemoveVoteButton();
		this.resetVotingForm();
		this.hideVoteButton();
		this.dom.votingPanel.addClass('hidden');
	};

	View.prototype.showResultsPanel = function() {
		this.hideVotingPanel();
		if (this.voteUpdateAllowed() && !this.hasPollEndedOrDeleted()) {
			this.showVotingPanelButton();
		} else {
			this.hideVotingPanelButton();
		}
		this.dom.resultsPanel.removeClass('hidden');
	};

	View.prototype.hideResultsPanel = function() {
		this.hideVotingPanelButton();
		this.dom.resultsPanel.addClass('hidden');
	};

	View.prototype.showVoteButton = function() {
		this.dom.voteButton.removeClass('hidden');
	};

	View.prototype.hideVoteButton = function() {
		this.dom.voteButton.addClass('hidden');
	};

	View.prototype.showUpdateVoteButton = function() {
		this.dom.updateVoteButton.removeClass('hidden');
	};

	View.prototype.hideUpdateVoteButton = function() {
		this.dom.updateVoteButton.addClass('hidden');
	};

	View.prototype.showRemoveVoteButton = function() {
		this.dom.removeVoteButton.removeClass('hidden');
	};

	View.prototype.hideRemoveVoteButton = function() {
		this.dom.removeVoteButton.addClass('hidden');
	};

	View.prototype.showVotingPanelButton = function() {
		this.dom.votingPanelButton.removeClass('hidden');
	};

	View.prototype.hideVotingPanelButton = function() {
		this.dom.votingPanelButton.addClass('hidden');
	};

	View.prototype.showResultsPanelButton = function() {
		this.dom.resultsPanelButton.removeClass('hidden');
	};

	View.prototype.hideResultsPanelButton = function() {
		this.dom.resultsPanelButton.addClass('hidden');
	};

	var Actions = [
		{
			// Voting
			register: function(view) {
				var self = this;
				view.dom.voteButton.off('click').on('click', function() {
					self.handle(view);
				});
			},
			handle: function(view) {
				var form = view.dom.votingPanel.find('form');
				var votes = form.serializeArray().map(function(option) {
					return parseInt(option.value, 10);
				});

				if (votes.length > 0) {
					var voteData = {
						pollId: view.pollData.info.pollId,
						options: votes
					};

					Poll.sockets.vote(voteData, function(err, result) {

						if(!config.loggedIn)
						{
							$(window).trigger('action:poll.vote.notloggedin');
						}

						if (err) {
							return app.alertError(err.message);
						}

						view.showResultsPanel();
					});
				}
			}
		},
		{
			// Voting
			register: function(view) {
				var self = this;
				view.dom.updateVoteButton.off('click').on('click', function() {
					self.handle(view);
				});
			},
			handle: function(view) {
				var form = view.dom.votingPanel.find('form');
				var votes = form.serializeArray().map(function(option) {
					return parseInt(option.value, 10);
				});

				if (votes.length > 0) {
					var voteData = {
						pollId: view.pollData.info.pollId,
						options: votes
					};

					Poll.sockets.updateVote(voteData, function(err, result) {
						if (err) {
							return app.alertError(err.message);
						}
						view.showResultsPanel();
					});
				}
			}
		},
		{
			// Remove vote
			register: function(view) {
				var self = this;
				view.dom.removeVoteButton.off('click').on('click', function() {
					self.handle(view);
				});
			},
			handle: function(view) {
				var voteData = { pollId: view.pollData.info.pollId };

				Poll.sockets.removeVote(voteData, function(err, result) {
					if (err) {
						return app.alertError(err.message);
					}
					view.showResultsPanel();
				});
			}
		},
		{
			// Results button
			register: function(view) {
				var self = this;
				view.dom.resultsPanelButton.off('click').on('click', function() {
					self.handle(view);
				});
			},
			handle: function(view) {
				view.showResultsPanel();
			}
		},
		{
			// To Voting button
			register: function(view) {
				var self = this;
				view.dom.votingPanelButton.off('click').on('click', function() {
					self.handle(view);
				});
			},
			handle: function(view) {
				view.showVotingPanel();
			}
		},
		{
			// Option details
			register: function(view) {
				var self = this;
				view.dom.resultsPanel.off('click').on('click', '.poll-result-votecount', function(e) {
					self.handle(view, e);
				});
			},
			handle: function(view, e) {
				var optionId = $(e.currentTarget).parents('[data-poll-option-id]').data('poll-option-id');

				Poll.sockets.getOptionDetails({
					pollId: view.pollData.info.pollId,
					optionId: optionId
				}, function(err, details) {
					if (err) {
						return app.alertError(err.message);
					}

					view.showOptionDetails(details);
				});
			}
		},
		{
			// Editing
			register: function(view) {
				var self = this;
				view.dom.editButton.off('click').on('click', function() {
					self.handle(view);
				});
			},
			handle: function(view) {
				Poll.sockets.getConfig(null, function(err, config) {
					Poll.creator.show(view.pollData, config, function(data) {
					});
				});
			}
		}
	];

	Poll.view = {
		polls: {},
		load: function(pollData) {
			var view = new View(pollData);
			this.polls[pollData.info.pollId] = view;

			view.load();
		},
		update: function(pollData) {
			var pollId = pollData.info.pollId;
			if (this.polls.hasOwnProperty(pollId)) {
				this.polls[pollId].update(pollData);
			}
		}
	};
})(window.Poll);
<|endoftext|>


<commit_before>function resizeNarrative() {
    $('#map,#narrative,#story,#map,body,html,#loadingDiv').css({
        'height': $(window).height()-54
    });
}
resizeNarrative();

function resetLoadingLocation() {
    $("#loading").css("left", ($("#mapDiv").width() - $("#narrative").width()) / 2);
}
resetLoadingLocation();

var resizeTimeout = -1;

$(window).resize(function() {
    clearTimeout(resizeTimeout);
    resizeTimeout = setTimeout(resizeNarrative, 500);
});

var map, layer, symbol, infoTemplate, query, featureLayer;
var visible = [];
var firstSet = true;
var oldLayer = 0;

require([
    "esri/Color",
    "esri/map",
    "esri/geometry/Extent",
    "esri/dijit/Scalebar",
    "esri/layers/FeatureLayer",
    "esri/layers/LayerDrawingOptions",
    "esri/layers/DynamicLayerInfo",
    "esri/layers/ArcGISDynamicMapServiceLayer",
    "esri/renderers/DotDensityRenderer",
    "esri/renderers/ScaleDependentRenderer",
    "esri/InfoTemplate",
    "esri/layers/ArcGISTiledMapServiceLayer",
    "esri/tasks/query",
    "esri/tasks/FeatureSet",
    "esri/SpatialReference",
    "esri/tasks/QueryTask",
    "esri/graphic",
    "dojo/query",
    "dojo/domReady!"
], function(Color, Map, Extent, Scalebar, FeatureLayer, LayerDrawingOptions, DynamicLayerInfo, ArcGISDynamicMapServiceLayer, DotDensityRenderer, ScaleDependentRenderer, InfoTemplate, ArcGISTiledMapServiceLayer, Query, FeatureSet, SpatialReference, QueryTask, Graphic, query) {
    console.log($("#loading").css('left'));
        //Loads in basemap topo instead of county layer
    map = new Map("mapDiv", {
                  basemap: "topo",
                  center: [-100.78, 46.80], //Longitude and Latitude of where the center of the map will be
                  zoom: 7,
        //maxScale: 1000000,
        //minScale: 5000000
        //basemap: "topo"
    });
        var layer = new FeatureLayer("http://undgeography.und.edu/geographyund/rest/services/ND125/WebMapND125/MapServer/36"); //First layer is the county

        var featureLayer = new FeatureLayer("http://undgeography.und.edu/geographyund/rest/services/ND125/WebMapND125/MapServer/35", {
                                            mode: FeatureLayer.SELECTION,
                                            InfoTemplate: new InfoTemplate("Built: ${Built3}", "${*}"),
                                            outFields: ["SOURCE_ID", "RAIL_TYPE", "ABAND_YR", "BUILT_YR", "Built2", "Built3", "miles"]
                                            }); //Layer that contains the rails for the railroad

        map.addLayer(layer);
        map.addLayer(featureLayer);
    // create our slider to show every 5 years after 1886
    layer.on("load", function(e) {
        values = [];
        for(i=1886; i<=2015; i++) {
             if (i == 1886) {
             values.push(i);
             }
             if (i % 5 == 0) {
                values.push(i);
             }
        }
        console.log(values);

        min = values[0];
        max = values[values.length-1];
        difference = (max - min);
        range = {
            'min': min,
            'max': max
        };

        $.each(values, function(index, value) {
            range['' + (100 - ((max - value) / difference * 100.0)) + '%'] = value;
        });
        console.log(range);

        $("#toggleSlider").noUiSlider({
            start: values[0],
            range: range,
            snap: true
        });

        $('#toggleSlider').noUiSlider_pips({
            mode: 'values',
            density: 10,
            values: values,
            stepped: true
        });

        // create a function for when the slider value is set
        $('#toggleSlider').on('set', function() {
            if (!firstSet) {
                              console.log("Clearing map");
                map.graphics.clear();
            } else {
                              console.log("Not Clearing map");
                firstSet = false;
            }

            map.infoWindow.hide();
            map.infoWindow.clearFeatures();
            showLoading();

            // find the index of this layer
            //currentLayers.push(getCurrentLayer());

            // show the visible layers
            //layer.setVisibleLayers(visible);

            // see if we should scroll to a new layer

            oldYear = $(this).val();

            // remove all styling from legendDiv
            $("#legendDiv").attr("style", "");

            // FILTER WITH
            var iYear = Math.floor($(this).val());
                              //creating a query that looks for features in the layer that match a return geometry == true and the varibale 'Built3' less than the current year selected on the slider bar
                              var query = new Query();
                              query.returnGeometry = true;
                              query.where = "Built3 < " + iYear;

                              console.log(query); //Ensuring a correct query was created
                              //featureLayer.clear();
                              //featureLayer.selectFeatures(query, FeatureLayer.SELECTION_SUBTRACT);
                              //featureLayer.refresh();

                              featureLayer.queryFeatures(query, function(FeatureSet) {
                                                         console.log("Queried Map!");
                                                         //Queries through featurelayer based on what the query required
                                                         //featureLayer.clear();
                                                         console.log(FeatureSet.features); //view all the features selected
                                                         var feature;
                                                         var features = FeatureSet.features;
                                                         var inBuffer = []; //Retrive all the features and push them into an array
                                                         for (var i = 0; i < features.length; i++) {
                                                            feature = features[i];
                                                            inBuffer.push(feature.attributes[featureLayer.objectIdField]);
                                                         }
                                                         console.log("Got features!");

                                                         var query = new Query();
                                                         query.objectIds = inBuffer;
                                                         //Create a new object query based off the ids found from the last query
                                                         console.log(inBuffer);
                                                         featureLayer.selectFeatures(query);
                                                         //select those features to view on the map
                                                         console.log("Selected features");
                                                         /*

                                                         var graphicRails = featureLayer.getSelectedFeatures();
                                                         for (var x = 0; x < graphicRails.length; x++) {
                                                            var graphic = graphicRails[x];
                                                            map.graphics.add(graphic);
                                                         }
                                                         console.log("Added Graphics");

                                                          */
                                                         featureLayer.redraw();
                                                         //Redraw feature layer -- not known if needed or not

                                                         console.log("Map Redraw!");
                                                         featureLayer.refresh();
                            });

                              /*
                               TODOs:
                                Have the correct features on the layer show on the map

                                Have the correct counties for whatever year

                                Correctly reload map after a change in year

                                Add narritive to the theme

                               */

                              /*
            var query = new Query();
            //query.objectIds = [features[0].attr.OBJECTIDS];
            query.outFields =["*"];
            query.returnGeometry = true;
            query.where = "Built3 < " + iYear;
            query.spatialReference = {"wkid":102720};

            console.log(iYear);
            console.log(query);

            featureLayer.queryFeatures(query, function(FeatureSet) {
                //remove all graphics on the maps graphics layer
                map.graphics.clear();
                console.log(FeatureSet.features);

                                       var numFeatures = FeatureSet.features.length;
                                       for (var x=0; x<numFeatures; x++) {
                                        var graphic = FeatureSet.features[x];
                                        //console.log(graphic);
                                        map.graphics.add(graphic);
                                        map.graphics.redraw();

                                       }

                //QueryTask returns a featureSet.  Loop through features in the featureSet and add them to the map.
                dojo.forEach(FeatureSet.features, function(feature) {
                    // add the features
                    var graphic = feature;
                    graphic.setSymbol(symbol);

                    //Add graphic to the map graphics layer.
                    map.graphics.attr(graphic);
                });
                map.graphics.redraw();

            });

            featureLayer.refresh();
               */
                              //map.removeLayer(featureLayer);
    });

        // set as the current layer
        oldLayer = 0;
        $('#toggleSlider').val(min);
        //layer.setVisibility(true);
    });
    // hide the loading icon when the dynamic layer finishes updating
    layer.on("update-end", hideLoading);

    // add the layer to the map
    //map.addLayer(layer);
    //map.addLayer(featureLayer);
    // show the scalebar
    var scalebar = new Scalebar({
        map: map,
        scalebarUnit: "dual"
    });
});

function getCurrentLayer() {
    // find the index of this layer
    return 33;
}

function ToggleLayer(id) {
    map.graphics.clear();
    map.infoWindow.hide();
    map.infoWindow.clearFeatures();

    showLoading();
}

function hideLoading() {
    $("#loadingDiv").hide();
    $("#togglableLayers").removeAttr('disabled');
}

function showLoading() {
    $("#loadingDiv").show();
    $("#togglableLayers").attr('disabled', 'disabled');
}

function railsBuilt(buffer) {
    var feature;
    var features = buffer.features;
    var inBuffer = [];
    for (var i = 0; i < features.length; i++) {
        feature = features[i];
        inBuffer.push(feature.attributes[featureLayer.objectIdField]);
    }
    var query = new Query();
    query.objectIds = inBuffer;
    featureLayer.selectFeatures(query, FeatureLayer.SELECTION_NEW);
}

$('#slider').click(function() {
    if ($(this).hasClass('expanded')) {
        $('#story').fadeOut(function() {
            $('#narrative').animate({width: 45});
            $('#mapWrapper').animate({right: 45}, function() {
                map.resize(true);
                map.reposition(true);
            });
            $('#slider').removeClass('expanded').find('i').removeClass('glyphicon-chevron-right').addClass('glyphicon-chevron-left');
            resetLoadingLocation();
        });
    }
    else {
        $('#narrative').animate({width: 460}, function() {
            $('#slider').addClass('expanded').find('i').addClass('glyphicon-chevron-right').removeClass('glyphicon-chevron-left');
            $('#story').fadeIn();
            $('#mapWrapper').animate({right: 460}, function() {
                map.resize(true);
                map.reposition(true);
            });
            resetLoadingLocation();
        });
    }
});

$(function() {
    $('[data-toggle="tooltip"]').tooltip();
});
<commit_msg>Changes to Railroads<commit_after>function resizeNarrative() {
    $('#map,#narrative,#story,#map,body,html,#loadingDiv').css({
        'height': $(window).height()-54
    });
}
resizeNarrative();

function resetLoadingLocation() {
    $("#loading").css("left", ($("#mapDiv").width() - $("#narrative").width()) / 2);
}
resetLoadingLocation();

var resizeTimeout = -1;

$(window).resize(function() {
    clearTimeout(resizeTimeout);
    resizeTimeout = setTimeout(resizeNarrative, 500);
});

var map, layer, symbol, infoTemplate, query, featureLayer;
var visible = [];
var firstSet = true;
var oldLayer = 0;

require([
    "esri/Color",
    "esri/map",
    "esri/geometry/Extent",
    "esri/dijit/Scalebar",
    "esri/layers/FeatureLayer",
    "esri/layers/LayerDrawingOptions",
    "esri/layers/DynamicLayerInfo",
    "esri/layers/ArcGISDynamicMapServiceLayer",
    "esri/symbols/SimpleMarkerSymbol",
    "esri/symbols/SimpleLineSymbol",
    "esri/renderers/SimpleRenderer",
    "esri/renderers/DotDensityRenderer",
    "esri/renderers/ScaleDependentRenderer",
    "esri/InfoTemplate",
    "esri/layers/ArcGISTiledMapServiceLayer",
    "esri/tasks/query",
    "esri/tasks/FeatureSet",
    "esri/SpatialReference",
    "esri/tasks/QueryTask",
    "esri/graphic",
    "dojo/query",
    "dojo/domReady!"
], function(Color, Map, Extent, Scalebar, FeatureLayer, LayerDrawingOptions, DynamicLayerInfo, ArcGISDynamicMapServiceLayer, SimpleMarkerSymbol, SimpleLineSymbol, SimpleRenderer, DotDensityRenderer, ScaleDependentRenderer, InfoTemplate, ArcGISTiledMapServiceLayer, Query, FeatureSet, SpatialReference, QueryTask, Graphic, query) {
    console.log($("#loading").css('left'));
        //Loads in basemap topo instead of county layer
    map = new Map("mapDiv", {
                  basemap: "topo",
                  center: [-100.78, 46.80], //Longitude and Latitude of where the center of the map will be
                  zoom: 7,
    });
        var layer = new FeatureLayer("http://undgeography.und.edu/geographyund/rest/services/ND125/WebMapND125/MapServer/36"); //First layer is the county

        var featureLayer = new FeatureLayer("http://undgeography.und.edu/geographyund/rest/services/ND125/WebMapND125/MapServer/35", {
                InfoTemplate: new InfoTemplate("Built: ${Built3}", "${*}"),
                outFields: ["SOURCE_ID", "RAIL_TYPE", "ABAND_YR", "BUILT_YR", "Built2", "Built3", "miles"]
        }); //Layer that contains the rails for the railroad

        var nullSymbol = new SimpleMarkerSymbol().setSize(0);
        featureLayer.setRenderer(new SimpleRenderer(nullSymbol));

        var symbol = new SimpleLineSymbol (
            SimpleLineSymbol.STYLE_SOLID,
            new Color([255,0,0]),
            2
        );
        featureLayer.setSelectionSymbol(symbol);

        map.addLayer(layer);
        map.addLayer(featureLayer);
    // create our slider to show every 5 years after 1886
    layer.on("load", function(e) {
        values = [];
        for(i=1886; i<=2015; i++) {
             if (i == 1886) {
             values.push(i);
             }
             if (i % 5 == 0) {
                values.push(i);
             }
        }
        console.log(values);

        min = values[0];
        max = values[values.length-1];
        difference = (max - min);
        range = {
            'min': min,
            'max': max
        };

        $.each(values, function(index, value) {
            range['' + (100 - ((max - value) / difference * 100.0)) + '%'] = value;
        });
        console.log(range);

        $("#toggleSlider").noUiSlider({
            start: values[0],
            range: range,
            snap: true
        });

        $('#toggleSlider').noUiSlider_pips({
            mode: 'values',
            density: 10,
            values: values,
            stepped: true
        });

        // create a function for when the slider value is set
        $('#toggleSlider').on('set', function() {
            if (!firstSet) {
                              console.log("Clearing map");
                map.graphics.clear();
            } else {
                              console.log("Not Clearing map");
                firstSet = false;
            }

            map.infoWindow.hide();
            map.infoWindow.clearFeatures();
            showLoading();

            // find the index of this layer
            //currentLayers.push(getCurrentLayer());

            // show the visible layers
            //layer.setVisibleLayers(visible);

            // see if we should scroll to a new layer

            oldYear = $(this).val();

            // remove all styling from legendDiv
            $("#legendDiv").attr("style", "");

            // FILTER WITH
            var iYear = Math.floor($(this).val());
                              //creating a query that looks for features in the layer that match a return geometry == true and the varibale 'Built3' less than the current year selected on the slider bar
                              var query = new Query();
                              query.returnGeometry = true;
                              query.where = "Built3 < " + iYear;
                              console.log(query);
                              featureLayer.queryFeatures(query, function(response) {
                              var feature;
                              var features = response.features;
                              console.log(features);
                              var inBuffer = [];
                              for (var i = 0; i < features.length; i++) {
                                feature = features[i];
                                inBuffer.push(feature.attributes[featureLayer.objectIdField]);
                              }
                              console.log(inBuffer);
                              var query = new Query();
                              query.objectIds = inBuffer;
                              featureLayer.selectFeatures(query, FeatureLayer.SELECTION_NEW);

                              });
    });

        // set as the current layer
        oldLayer = 0;
        $('#toggleSlider').val(min);
        layer.setVisibility(true);
    });
    // hide the loading icon when the dynamic layer finishes updating
    layer.on("update-end", hideLoading);

    // add the layer to the map
    //map.addLayer(layer);
    //map.addLayer(featureLayer);
    // show the scalebar
    var scalebar = new Scalebar({
        map: map,
        scalebarUnit: "dual"
    });
});

function getCurrentLayer() {
    // find the index of this layer
    return 33;
}

function ToggleLayer(id) {
    map.graphics.clear();
    map.infoWindow.hide();
    map.infoWindow.clearFeatures();

    showLoading();
}

function hideLoading() {
    $("#loadingDiv").hide();
    $("#togglableLayers").removeAttr('disabled');
}

function showLoading() {
    $("#loadingDiv").show();
    $("#togglableLayers").attr('disabled', 'disabled');
}

function railsBuilt(buffer) {
    var feature;
    var features = buffer.features;
    var inBuffer = [];
    for (var i = 0; i < features.length; i++) {
        feature = features[i];
        inBuffer.push(feature.attributes[featureLayer.objectIdField]);
    }
    var query = new Query();
    query.objectIds = inBuffer;
    featureLayer.selectFeatures(query, FeatureLayer.SELECTION_NEW);
}

$('#slider').click(function() {
    if ($(this).hasClass('expanded')) {
        $('#story').fadeOut(function() {
            $('#narrative').animate({width: 45});
            $('#mapWrapper').animate({right: 45}, function() {
                map.resize(true);
                map.reposition(true);
            });
            $('#slider').removeClass('expanded').find('i').removeClass('glyphicon-chevron-right').addClass('glyphicon-chevron-left');
            resetLoadingLocation();
        });
    }
    else {
        $('#narrative').animate({width: 460}, function() {
            $('#slider').addClass('expanded').find('i').addClass('glyphicon-chevron-right').removeClass('glyphicon-chevron-left');
            $('#story').fadeIn();
            $('#mapWrapper').animate({right: 460}, function() {
                map.resize(true);
                map.reposition(true);
            });
            resetLoadingLocation();
        });
    }
});

$(function() {
    $('[data-toggle="tooltip"]').tooltip();
});
<|endoftext|>


<commit_before>// Game state vars
var saveFlowTimeout = null;
var wordcount = 0;
var score = 0;
var sv_time = 0;
var flowing = false;

// Game mode vars
var game_started = false;
var game_start_time = null;
var game_mode = null;
var timer_goal = null;
var wordcount_goal = null;
var kittens_mode = null;
var final_game_length = null;
var kitten_gifs = null;
var kitten_count = 0;

// Stats collection vars
var longest_flow = -1;

function saveFlow() {
    var text = $('#playingfield').val();
    var chars = text.length;
    var words = getWordCount(text);
    var flow_time = getElapsedSeconds(sv_time);

    freezeFlowAndReset(text);

    wordcount += words;
    current_score = getScore(text, flow_time);
    score += current_score;

    if (words > longest_flow) {
        longest_flow = words;
    }

    updateFlowStatus();
    displayEncouragement(current_score);

    if(kittens_mode){
        displayKitten();
    }

    if (checkEndGame()) {
        endGame();
    }

    flowing = false;
}

function checkEndGame() {
    if (game_mode == "wordcount" && wordcount >= wordcount_goal) {
        return true;
    }
    if (game_mode == "timer" && getElapsedSeconds(game_start_time) >= timer_goal) {
        return true;
    }
    return false;
}

function disablePlayingField() {
    $('#playingfield').css('display', 'none').attr('disabled','disabled');
}

function enableSaveButton() {
    $('.btn-save').css('display', 'block');
    $('.btn-save').on('click', function() {
        saveFlowToServer();
    });
}

function endGame() {
    final_game_length = getElapsedSeconds(game_start_time);
    hideEncouragement();
    disablePlayingField();
    enableSaveButton();
    $('#game-over').css('display', 'block');
    $('#title').css('display', 'block');
}

function displayEncouragement(score) {
    var word;
    word = "poor";
    if (score > 50) { word = "okay"; }
    if (score > 75) { word = "fair"; }
    if (score > 125) { word = "cool"; }
    if (score > 200) { word = "nice"; }
    if (score > 300) { word = "sweet"; }
    if (score > 400) { word = "great"; }
    if (score > 600) { word = "awesome"; }
    if (score > 800) { word = "amazing"; }
    if (score > 1000) { word = "incredible"; }
    if (score > 1400) { word = "spectacular"; }
    if (score > 1800) { word = "unbelievable"; }
    if (score > 2200) { word = "extraordinary"; }
    $('.encouragement').html("
" + word + " flow!
");
    $('.encouragement').css("display", "block");
}

function displayKitten() {
    var caturl = kitten_gifs[kitten_count]['images']['fixed_height'].url;
    if (kitten_count < kitten_gifs.length){
        kitten_count++;
    }

    $('p').addClass('kitten-p');
    $('.encouragement').addClass('white-stroke');
    $('.stats').addClass('white-stroke');
    $('#scoreboard').addClass('white-stroke');
    $('footer').addClass('white-stroke');
    $('body').css('background', 'url("' + caturl + '")');
}

function hideKitten() {
    $('body').css('background', 'url("/assets/images/debut_light.png")');

}

// currently only pulls 50 gifs.
function callGiphy() {
    var xhr = $.get("http://api.giphy.com/v1/gifs/search?q=kittens&api_key=dc6zaTOxFJmzC&limit=50");
    xhr.done(function(data) {
        console.log("success got data", data.data);
        kitten_gifs = data.data;
    });
}

function hideEncouragement() {
    $('.encouragement').css("display", "none");
}

function freezeFlowAndReset(text) {
    $('.tekstflyt').before("

" + text + "\n" + "

");
    $('#playingfield').val("");
}

function scoreBoardUpdate(wordcount, score, words, this_score) {
    var total_wordcount = wordcount + words;
    var total_score = score + this_score;
    if (game_mode == "wordcount") {
        countdown = "| Words left: " + (wordcount_goal - total_wordcount) + ""
    }
    if (game_mode == "timer") {
        countdown = "| Secs left: " + (timer_goal - getElapsedSeconds(game_start_time)).toFixed(1) + ""
    }
    $('#scoreboard').html("
Words: " + total_wordcount + " | Score: " + total_score + " " + countdown + "
");
}

function scroll() {
    $("html, body").animate({ scrollTop: $('#playingfield').offset().top }, "slow");
}

// scores should be higher the longer the text is
// scores should be higher the longer the time is
// scores should be higher the higher the ratio of text to time is
function getScore(text, time) {
    var chars = text.length;
    var words = getWordCount(text);
    var wpm = (words / time) * 60;
    var score = Math.round(words*5*wpm/40);

    return score;
}

function getWordCount(text) {
    if (text == "") {
        return 0;
    }

    s = text.replace(/(^\s*)|(\s*$)/gi,"");
    s = s.replace(/[ ]{2,}/gi," ");
    s = s.replace(/\n /,"\n");
    return s.split(' ').length;
}

updateFlowStatus = function() {
    var text = $('#playingfield').val();
    var chars = text.length;
    var words = getWordCount(text);
    var seconds = getElapsedSeconds(sv_time);
    var wpm = Math.round(words / seconds * 60);
    $('.stats').html('
Words: ' + words + ' (+' + words*5 + ' pts) | WPM: ' + wpm + ' (x' + (wpm / 40).toFixed(1) + ' bonus)

');
    var this_score = getScore(text, seconds);

    scoreBoardUpdate(wordcount, score, words, this_score);
}

function startGame() {
    game_mode = $('#tekst-mode').val();
    wordcount_goal = parseInt($('#tekst-wordcount').val());
    // seconds
    timer_goal = 60 * parseInt($('#tekst-timer').val());
    kittens_mode = $('#tekst-kittens').val();

    // call Giphy at the start of the round
    if (kittens_mode) {
        callGiphy();
    }
    game_start_time = getTime();
    game_started = true;
    window.setInterval(updateFlowStatus, 100);
}

function startNewFlow() {
    sv_time = getTime();
    hideEncouragement();
    flowing = true;
}

updateFlowState = function() {
    if (! game_started) {
        startGame();
    }
    if (! flowing) {
        startNewFlow();
    }
    if (saveFlowTimeout) {
        window.clearTimeout(saveFlowTimeout);
    }

    updateFlowStatus();

    saveFlowTimeout = window.setTimeout(saveFlow, 500);
}

function getTime() {
    return (new Date).getTime();
}

function getElapsedSeconds(time) {
    return (getTime() - time) / 1000;
}

document.getElementById("playingfield").oninput = updateFlowState;

function saveFlowToServer() {
    $('.flow-save').css('display', 'block');
    saveFlow();
    var total_text = $('.text-content').text();
    var title = $('#title').val();
    $.post('/flow', { text: total_text, score: score, mode: game_mode, timer: final_game_length, wordcount: wordcount, title: title, longest_flow: longest_flow }, function(data) {
        l = window.location;
        window.location.replace(l.protocol + "//" + l.host + data);
    });
}
<commit_msg>more white stroking on text in kitten mode<commit_after>// Game state vars
var saveFlowTimeout = null;
var wordcount = 0;
var score = 0;
var sv_time = 0;
var flowing = false;

// Game mode vars
var game_started = false;
var game_start_time = null;
var game_mode = null;
var timer_goal = null;
var wordcount_goal = null;
var kittens_mode = null;
var final_game_length = null;
var kitten_gifs = null;
var kitten_count = 0;

// Stats collection vars
var longest_flow = -1;

function saveFlow() {
    var text = $('#playingfield').val();
    var chars = text.length;
    var words = getWordCount(text);
    var flow_time = getElapsedSeconds(sv_time);

    freezeFlowAndReset(text);

    wordcount += words;
    current_score = getScore(text, flow_time);
    score += current_score;

    if (words > longest_flow) {
        longest_flow = words;
    }

    updateFlowStatus();
    displayEncouragement(current_score);

    if(kittens_mode){
        displayKitten();
    }

    if (checkEndGame()) {
        endGame();
    }

    flowing = false;
}

function checkEndGame() {
    if (game_mode == "wordcount" && wordcount >= wordcount_goal) {
        return true;
    }
    if (game_mode == "timer" && getElapsedSeconds(game_start_time) >= timer_goal) {
        return true;
    }
    return false;
}

function disablePlayingField() {
    $('#playingfield').css('display', 'none').attr('disabled','disabled');
}

function enableSaveButton() {
    $('.btn-save').css('display', 'block');
    $('.btn-save').on('click', function() {
        saveFlowToServer();
    });
}

function endGame() {
    final_game_length = getElapsedSeconds(game_start_time);
    hideEncouragement();
    disablePlayingField();
    enableSaveButton();
    $('#game-over').css('display', 'block');
    $('#title').css('display', 'block');
}

function displayEncouragement(score) {
    var word;
    word = "poor";
    if (score > 50) { word = "okay"; }
    if (score > 75) { word = "fair"; }
    if (score > 125) { word = "cool"; }
    if (score > 200) { word = "nice"; }
    if (score > 300) { word = "sweet"; }
    if (score > 400) { word = "great"; }
    if (score > 600) { word = "awesome"; }
    if (score > 800) { word = "amazing"; }
    if (score > 1000) { word = "incredible"; }
    if (score > 1400) { word = "spectacular"; }
    if (score > 1800) { word = "unbelievable"; }
    if (score > 2200) { word = "extraordinary"; }
    $('.encouragement').html("
" + word + " flow!
");
    $('.encouragement').css("display", "block");
}

function displayKitten() {
    var caturl = kitten_gifs[kitten_count]['images']['fixed_height'].url;
    if (kitten_count < kitten_gifs.length){
        kitten_count++;
    }

    $('p').addClass('kitten-p');
    $('.encouragement').addClass('white-stroke');
    $('.stats').addClass('white-stroke');
    $('#scoreboard').addClass('white-stroke');
    $('footer').addClass('white-stroke');
    $('#game-over').addClass('white-stroke');
    $('body').css('background', 'url("' + caturl + '")');
}

function hideKitten() {
    $('body').css('background', 'url("/assets/images/debut_light.png")');

}

// currently only pulls 50 gifs.
function callGiphy() {
    var xhr = $.get("http://api.giphy.com/v1/gifs/search?q=kittens&api_key=dc6zaTOxFJmzC&limit=50");
    xhr.done(function(data) {
        console.log("success got data", data.data);
        kitten_gifs = data.data;
    });
}

function hideEncouragement() {
    $('.encouragement').css("display", "none");
}

function freezeFlowAndReset(text) {
    $('.tekstflyt').before("

" + text + "\n" + "

");
    $('#playingfield').val("");
}

function scoreBoardUpdate(wordcount, score, words, this_score) {
    var total_wordcount = wordcount + words;
    var total_score = score + this_score;
    if (game_mode == "wordcount") {
        countdown = "| Words left: " + (wordcount_goal - total_wordcount) + ""
    }
    if (game_mode == "timer") {
        countdown = "| Secs left: " + (timer_goal - getElapsedSeconds(game_start_time)).toFixed(1) + ""
    }
    $('#scoreboard').html("
Words: " + total_wordcount + " | Score: " + total_score + " " + countdown + "
");
}

function scroll() {
    $("html, body").animate({ scrollTop: $('#playingfield').offset().top }, "slow");
}

// scores should be higher the longer the text is
// scores should be higher the longer the time is
// scores should be higher the higher the ratio of text to time is
function getScore(text, time) {
    var chars = text.length;
    var words = getWordCount(text);
    var wpm = (words / time) * 60;
    var score = Math.round(words*5*wpm/40);

    return score;
}

function getWordCount(text) {
    if (text == "") {
        return 0;
    }

    s = text.replace(/(^\s*)|(\s*$)/gi,"");
    s = s.replace(/[ ]{2,}/gi," ");
    s = s.replace(/\n /,"\n");
    return s.split(' ').length;
}

updateFlowStatus = function() {
    var text = $('#playingfield').val();
    var chars = text.length;
    var words = getWordCount(text);
    var seconds = getElapsedSeconds(sv_time);
    var wpm = Math.round(words / seconds * 60);
    $('.stats').html('
Words: ' + words + ' (+' + words*5 + ' pts) | WPM: ' + wpm + ' (x' + (wpm / 40).toFixed(1) + ' bonus)

');
    var this_score = getScore(text, seconds);

    scoreBoardUpdate(wordcount, score, words, this_score);
}

function startGame() {
    game_mode = $('#tekst-mode').val();
    wordcount_goal = parseInt($('#tekst-wordcount').val());
    // seconds
    timer_goal = 60 * parseInt($('#tekst-timer').val());
    kittens_mode = $('#tekst-kittens').val();

    // call Giphy at the start of the round
    if (kittens_mode) {
        callGiphy();
    }
    game_start_time = getTime();
    game_started = true;
    window.setInterval(updateFlowStatus, 100);
}

function startNewFlow() {
    sv_time = getTime();
    hideEncouragement();
    flowing = true;
}

updateFlowState = function() {
    if (! game_started) {
        startGame();
    }
    if (! flowing) {
        startNewFlow();
    }
    if (saveFlowTimeout) {
        window.clearTimeout(saveFlowTimeout);
    }

    updateFlowStatus();

    saveFlowTimeout = window.setTimeout(saveFlow, 500);
}

function getTime() {
    return (new Date).getTime();
}

function getElapsedSeconds(time) {
    return (getTime() - time) / 1000;
}

document.getElementById("playingfield").oninput = updateFlowState;

function saveFlowToServer() {
    $('.flow-save').css('display', 'block');
    saveFlow();
    var total_text = $('.text-content').text();
    var title = $('#title').val();
    $.post('/flow', { text: total_text, score: score, mode: game_mode, timer: final_game_length, wordcount: wordcount, title: title, longest_flow: longest_flow }, function(data) {
        l = window.location;
        window.location.replace(l.protocol + "//" + l.host + data);
    });
}
<|endoftext|>


<commit_before>/*
 * windowsocket
 * https://github.com/goliatone/windowsocket
 * Created with gbase.
 * Copyright (c) 2014 goliatone
 * Licensed under the MIT license.
 */
/* jshint strict: false, plusplus: true */
/*global define: false, require: false, module: false, exports: false */
(function(root, name, deps, factory) {
    "use strict";
    // Node
    if (typeof deps === 'function') {
        factory = deps;
        deps = [];
    }

    if (typeof exports === 'object') {
        module.exports = factory.apply(root, deps.map(require));
    } else if (typeof define === 'function' && 'amd' in define) {
        //require js, here we assume the file is named as the lower
        //case module name.
        define(name.toLowerCase(), deps, factory);
    } else {
        // Browser
        var d, i = 0,
            global = root,
            old = global[name],
            mod;
        while ((d = deps[i]) !== undefined) deps[i++] = root[d];
        global[name] = mod = factory.apply(global, deps);
        //Export no 'conflict module', aliases the module.
        mod.noConflict = function() {
            global[name] = old;
            return mod;
        };
    }
}(this, 'windowsocket', ['extend'], function(extend) {

    /**
     * Extend method.
     * @param  {Object} target Source object
     * @return {Object}        Resulting object from
     *                         meging target to params.
     */
    var _extend = extend;

    /**
     * Shim console, make sure that if no console
     * available calls do not generate errors.
     * @return {Object} Console shim.
     */
    var _shimConsole = function(con) {

        if (con) return con;

        con = {};
        var empty = {},
            noop = function() {},
            properties = 'memory'.split(','),
            methods = ('assert,clear,count,debug,dir,dirxml,error,exception,group,' +
                'groupCollapsed,groupEnd,info,log,markTimeline,profile,profileEnd,' +
                'table,time,timeEnd,timeStamp,trace,warn').split(','),
            prop,
            method;

        while (method = methods.pop()) con[method] = noop;
        while (prop = properties.pop()) con[prop] = empty;

        return con;
    };

    ///////////////////////////////////////////////////
    // CONSTRUCTOR
    ///////////////////////////////////////////////////

    var OPTIONS = {
        autoinitialize: true,

        binaryType: 'blob',
        bufferedAmount: 0,
        extensions: '',
        readyState: 0,

        SCHEME:'tcp://',

        provider: function(){
            return WindowServer.instance();
        },

        onerror: function() {},
        onclose: function() {},
        onmessage: function() {},
        onopen: function() {}
    };

    var ERROR_REQUIRED_ARGUMENT = 'Failed to construct \'WindowSocket\': 1 argument required, but only 0 present.';
    var ERROR_URL_SCHEME = 'Failed to construct \'WindowSocket\': The URL\'s scheme must be \'{{scheme}}\'.';
    var ERROR_CONNECT_URL = 'WindowSocket connection to \'{{url}}\' failed: Connection closed before receiving a handshake response.'
    var ERROR_INVALID_STATE = 'WindowSocket connection has not been established';
    var ERROR_UNKNOWN_EVENT_TYPE = 'WindowSocket unkown event type {{type}}';
    /**
     * WindowSocket constructor
     *
     * @param  {String} url       The URL to which to connect; this
     *                            should be the URL to which the
     *                            WindowSocket
     *                            server will respond.
     * @param  {String|Array} protocols Optional. Either a single protocol
     *                                  string or an array of protocol strings.
     *                                  These strings are used to indicate
     *                                  sub-protocols, so that a single server
     *                                  can implement multiple WebSocket
     *                                  sub-protocols (for example, you might
     *                                  want one server to be able to handle
     *                                  different types of interactions depending
     *                                  on the specified protocol). If you don't
     *                                  specify a protocol string, an empty string
     *                                  is assumed.
     * @param  {object} config Configuration object.
     */
    var WindowSocket = function(url, protocols, config) {
        this.url = url;
        this.protocols = protocols;

        this._listeners = {};

        config = _extend({}, this.constructor.DEFAULTS, config);

        this.SERVER = config.provider();

        if (config.autoinitialize) this.init(config);
    };

    WindowSocket.name = WindowSocket.prototype.name = 'WindowSocket';

    WindowSocket.VERSION = '0.0.0';

    /**
     * Make default options available so we
     * can override.
     */
    WindowSocket.DEFAULTS = OPTIONS;

    /**
     * The connection is not yet open.
     */
    WindowSocket.CONNECTING = 0;
    /**
     * The connection is open and ready to communicate.
     */
    WindowSocket.OPEN = 1;
    /**
     * The connection is in the process of closing.
     */
    WindowSocket.CLOSING = 2;
    /**
     * The connection is closed or couldn't be opened.
     */
    WindowSocket.CLOSED = 3;

    ///////////////////////////////////////////////////
    // PUBLIC METHODS
    ///////////////////////////////////////////////////

    /**
     * The WindowSocket object provides the API for creating
     * and managing a WindowSocket connection to a server,
     * as well as for sending and receiving data
     * on the connection.
     *
     * @param  {String} url       The URL to which to connect; this
     *                            should be the URL to which the
     *                            WindowSocket
     *                            server will respond.
     * @param  {String|Array} protocols Optional. Either a single protocol
     *                                  string or an array of protocol strings.
     *                                  These strings are used to indicate
     *                                  sub-protocols, so that a single server
     *                                  can implement multiple WebSocket
     *                                  sub-protocols (for example, you might
     *                                  want one server to be able to handle
     *                                  different types of interactions depending
     *                                  on the specified protocol). If you don't
     *                                  specify a protocol string, an empty string
     *                                  is assumed.
     * @throws SECURITY_ERR The port to which the connection is being attempted is being blocked.
     */
    WindowSocket.prototype.init = function(config) {
        if (this.initialized) return this.logger.warn('Already initialized');
        this.initialized = true;

        this.ID = Date.now();

        console.log('WindowSocket: Init!');
        _extend(this, config);

        if(!this.url) throw new TypeError(ERROR_REQUIRED_ARGUMENT);
        if(this.url.indexOf(this.SCHEME) !== 0) throw new SyntaxError(ERROR_REQUIRED_ARGUMENT);
        if(!this.SERVER.connect(this)) throw new Error(ERROR_CONNECT_URL);

        return 'This is just a stub!';
    };

    /**
     * Transmits data to the server over the WindowSocket
     * connection.
     * @param  {String} data A text string to send to
     *                       the server
     * @throws {INVALID_STATE_ERR} If connection is not currently OPEN.
     * @throws {SYNTAX_ERR} The data is a string that has unpaired surrogates.
     * @return {void}
     */
    WindowSocket.prototype.send = function(data) {
        //Since we send the connection event on the next frame
        //we might construct a new WindowSocket instance and
        //call send right away. If state is CONNECTING we should
        //probably throw an Error?
        //TODO: Confirm WebSocket behavior.
        if(this.readyState === WindowSocket.CONNECTING){
            return;
        }

        if (this.readyState !== WindowSocket.OPEN) {
            throw new Error(ERROR_INVALID_STATE);
        }

        this.SERVER.send(this, data);
    };

    /**
     * Closes the WindowSocket connection or connection
     * attempt, if any. If the connection is already
     * CLOSED, this method does nothing.
     * @return {void}
     *
     * @param  {Number} code   A numeric value indicating the status
     *                         code explaining why the connection is
     *                         being closed. If this parameter is not
     *                         specified, a default value of 1000
     *                         (indicating a normal "transaction complete"
     *                         closure) is assumed. See the list of status
     *                         codes on the CloseEvent page for
     *                         permitted values.
     * @param  {String} reason A human-readable string explaining why the
     *                         connection is closing. This string must be
     *                         no longer than 123 bytes of UTF-8 text
     *                         (not characters).
     * @throws {INVALID_ACCESS_ERR} If an invalid code was specified.
     * @throws {SYNTAX_ERR} The reason string is too long or contains
     *                      unpaired surrogates.
     * @return {void}
     */
    WindowSocket.prototype.close = function(code, reason) {
        //TODO: Guard against initial frame delay!
        //
        if(this.readyState === WindowSocket.CLOSED || this.readyState === WindowSocket.CLOSING){
            return;
        }

        this.readyState = WindowSocket.CLOSING;
        this.SERVER.close(this);
    };

    ///////////////////////////////////////////////////
    // EventTarget Interface
    // @see http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration
    ///////////////////////////////////////////////////

    /**
     * DOM 2 EventTarget Interface implementation.
     * @param {String} type
     * @param {Function} listener
     * @param {Boolean} useCapture
     * @return {void}
     */
    WindowSocket.prototype.addEventListener = function(type, listener, useCapture){
        if(!(type in this._listeners)) this._listeners[type] = [];
        this._listeners[type].push(listener);
    };

    /**
     * DOM 2 EventTarget Interface implementation.
     * @param {String} type
     * @param {Function} listener
     * @param {Boolean} useCapture
     * @return {void}
     */
    WindowSocket.prototype.removeEventListener = function(type, listener, useCapture){
        if(!(type in this._listeners)) return;
        var events = this._listeners[type];
        var i = events.length - 1;
        for (i; i >= 0; --i) {
            if (events[i] !== listener) continue;
            events.splice(i, 1);
            break;
        }
    };

    /**
     * DOM 2 EventTarget Interface implementation.
     * @param {Event} event
     * @return {void}
     */
    WindowSocket.prototype.dispatchEvent = function(event){
        var events = this._listeners[event.type] || [];
        var t = events.length, i = 0;
        for(; i < t; ++i){
            events[i](event);
        }
        //apply delegated callbacks:
        var handler = this['on' + event.type];
        if(typeof handler === 'function') handler.call(this, event);
    };

    ///////////////////////////////////////////////////
    // PRIVATE METHODS
    ///////////////////////////////////////////////////
    /**
     * Method to trigger external events.
     * @param  {Object} e
     * @return {void}
     */
    WindowSocket.prototype._triggerEvent = function(e){
        if(e.readyState) this.readyState = e.readyState;

        //TODO: Clean this up. It's really gross!
        var event,
            events = ['open', 'error', 'close'];
        if(events.indexOf(e.type) !== -1){
            event = this._buildEvent(e.type, e);
        } else if(e.type === 'message') {
            event = this._buildMessageEvent(e.type, e.message);
        } else {
            //TODO: ErrorMessage factory, replace strings.
            throw new Error(ERROR_UNKNOWN_EVENT_TYPE);
        }

        this.dispatchEvent(event);
    };

    /**
     * Create an Event instance.
     * Interface DocumentEvent DOM Level 2.
     *
     * @see http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-DocumentEvent
     * @see https://developer.mozilla.org/en-US/docs/Web/API/document.createEvent
     * @param  {String} type
     * @return {void}
     */
    WindowSocket.prototype._buildEvent = function(e){
        //assert(e.type);
        if(document.createEvent && window.Event){
            var event = document.createEvent('Event');
            event.initEvent(e.type, false, false);
            return event;
        }
        return {type:e.type, bubbles:false, cancelable:false};
    };

    /**
     * Create an MessageEvent instance.
     * Interface DocumentEvent DOM Level 2.
     *
     * @see http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-DocumentEvent
     * @see https://developer.mozilla.org/en-US/docs/Web/API/document.createEvent
     * @param  {String} data
     * @return {void}
     */
    WindowSocket.prototype._buildMessageEvent = function(e){
        //TODO: assert(e.message);
        var data = e.message;

        if(window.MessageEvent && typeof MessageEvent === 'function'){
            return new MessageEvent('message', {
                'view':window,
                'bubbles':false,
                'cancelable':false,
                'data':data
            });
        }

        if(document.createEvent && window.MessageEvent){
            var event = document.createEvent('MessageEvent');
            event.initWithMessage('message', false, false, data, null, null, window, null);
            return event;
        }

        return {type:'message', data:data, bubbles:false, cancelable:false};
    };

    /**
     * @see  https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
     * @param  {Event} e
     * @return {void}
     */
    WindowSocket.prototype._buildCloseEvent = function(e){
        //TODO: assert(e.code, e.reason, e.clean);
        var event = this._buildEvent('close');
        event.code = e.code;
        event.reason = e.reason;
        event.wasClean = e.clean;

        return event;
    };

    WindowSocket.prototype._throwError = function(message){
        var args = [].splice.call(arguments, 1);
        //TODO: replace message tokens, throw error
    };

    /**
     * Logger method, meant to be implemented by
     * mixin. As a placeholder, we use console if available
     * or a shim if not present.
     */
    WindowSocket.prototype.logger = _shimConsole(console);

    function WindowServer(){
        this._instances = {};
    }

    WindowServer.instance = function(){
        if(this._instance) return this._instance;
        this._instance = new WindowServer();
        this._instance.connection = window._tcpManager();
        return this._instance;
    };

    WindowServer.prototype.init = function(){
        if(this.initialized) return;
        this.initialized = true;
    };

    WindowServer.prototype.connect = function(instance){
        if(!this.connection) return false;

        this._instances[instance.ID] = instance;
        var url = instance.url;

        //execute next
        setTimeout(function(){
            instance.readyState = WindowSocket.OPEN;
            instance.onopen(new Event('open'));
        },0);

        return true;
    };

    WindowServer.prototype.handle = function(){

    };

    WindowServer.prototype.send = function(instance, data){
        _tcpManager().send(instance.url, data);
    };

    WindowServer.prototype.close = function(instance){
        _tcpManager().close(instance.url);
        instance.readyState = WindowSocket.CLOSED;
    };

    return WindowSocket;
}));

function _tcpManager(){
    this.onMessage = function(){};
    this.send=function(url, msg){console.log('SENDING:', url, msg)};
    this.close=function(url){console.log('CLOSING')};
    return this;
}<commit_msg>WindowSocket: WIP external interface.<commit_after>/*
 * windowsocket
 * https://github.com/goliatone/windowsocket
 * Created with gbase.
 * Copyright (c) 2014 goliatone
 * Licensed under the MIT license.
 */
/* jshint strict: false, plusplus: true */
/*global define: false, require: false, module: false, exports: false */
(function(root, name, deps, factory) {
    "use strict";
    // Node
    if (typeof deps === 'function') {
        factory = deps;
        deps = [];
    }

    if (typeof exports === 'object') {
        module.exports = factory.apply(root, deps.map(require));
    } else if (typeof define === 'function' && 'amd' in define) {
        //require js, here we assume the file is named as the lower
        //case module name.
        define(name.toLowerCase(), deps, factory);
    } else {
        // Browser
        var d, i = 0,
            global = root,
            old = global[name],
            mod;
        while ((d = deps[i]) !== undefined) deps[i++] = root[d];
        global[name] = mod = factory.apply(global, deps);
        //Export no 'conflict module', aliases the module.
        mod.noConflict = function() {
            global[name] = old;
            return mod;
        };
    }
}(this, 'windowsocket', ['extend'], function(extend) {

    /**
     * Extend method.
     * @param  {Object} target Source object
     * @return {Object}        Resulting object from
     *                         meging target to params.
     */
    var _extend = extend;

    /**
     * Shim console, make sure that if no console
     * available calls do not generate errors.
     * @return {Object} Console shim.
     */
    var _shimConsole = function(con) {

        if (con) return con;

        con = {};
        var empty = {},
            noop = function() {},
            properties = 'memory'.split(','),
            methods = ('assert,clear,count,debug,dir,dirxml,error,exception,group,' +
                'groupCollapsed,groupEnd,info,log,markTimeline,profile,profileEnd,' +
                'table,time,timeEnd,timeStamp,trace,warn').split(','),
            prop,
            method;

        while (method = methods.pop()) con[method] = noop;
        while (prop = properties.pop()) con[prop] = empty;

        return con;
    };

    ///////////////////////////////////////////////////
    // CONSTRUCTOR
    ///////////////////////////////////////////////////

    var OPTIONS = {
        autoinitialize: true,

        binaryType: 'blob',
        bufferedAmount: 0,
        extensions: '',
        readyState: 0,

        SCHEME: 'tcp://',

        provider: function() {
            return WindowServer.instance();
        },

        onerror: function() {},
        onclose: function() {},
        onmessage: function() {},
        onopen: function() {}
    };

    var ERROR_REQUIRED_ARGUMENT = 'Failed to construct \'WindowSocket\': 1 argument required, but only 0 present.';
    var ERROR_URL_SCHEME = 'Failed to construct \'WindowSocket\': The URL\'s scheme must be \'{{scheme}}\'.';
    var ERROR_CONNECT_URL = 'WindowSocket connection to \'{{url}}\' failed: Connection closed before receiving a handshake response.'
    var ERROR_INVALID_STATE = 'WindowSocket connection has not been established';
    var ERROR_UNKNOWN_EVENT_TYPE = 'WindowSocket unkown event type {{type}}';
    /**
     * WindowSocket constructor
     *
     * @param  {String} url       The URL to which to connect; this
     *                            should be the URL to which the
     *                            WindowSocket
     *                            server will respond.
     * @param  {String|Array} protocols Optional. Either a single protocol
     *                                  string or an array of protocol strings.
     *                                  These strings are used to indicate
     *                                  sub-protocols, so that a single server
     *                                  can implement multiple WebSocket
     *                                  sub-protocols (for example, you might
     *                                  want one server to be able to handle
     *                                  different types of interactions depending
     *                                  on the specified protocol). If you don't
     *                                  specify a protocol string, an empty string
     *                                  is assumed.
     * @param  {object} config Configuration object.
     */
    var WindowSocket = function(url, protocols, config) {
        this.url = url;
        this.protocol = protocols;

        this._listeners = {};

        config = _extend({}, this.constructor.DEFAULTS, config);

        this.SERVER = config.provider();

        if (config.autoinitialize) this.init(config);
    };

    WindowSocket.name = WindowSocket.prototype.name = 'WindowSocket';

    WindowSocket.VERSION = '0.0.0';

    /**
     * Make default options available so we
     * can override.
     */
    WindowSocket.DEFAULTS = OPTIONS;

    /**
     * The connection is not yet open.
     */
    WindowSocket.CONNECTING = 0;
    /**
     * The connection is open and ready to communicate.
     */
    WindowSocket.OPEN = 1;
    /**
     * The connection is in the process of closing.
     */
    WindowSocket.CLOSING = 2;
    /**
     * The connection is closed or couldn't be opened.
     */
    WindowSocket.CLOSED = 3;

    ///////////////////////////////////////////////////
    // PUBLIC METHODS
    ///////////////////////////////////////////////////

    /**
     * The WindowSocket object provides the API for creating
     * and managing a WindowSocket connection to a server,
     * as well as for sending and receiving data
     * on the connection.
     *
     * @param  {String} url       The URL to which to connect; this
     *                            should be the URL to which the
     *                            WindowSocket
     *                            server will respond.
     * @param  {String|Array} protocols Optional. Either a single protocol
     *                                  string or an array of protocol strings.
     *                                  These strings are used to indicate
     *                                  sub-protocols, so that a single server
     *                                  can implement multiple WebSocket
     *                                  sub-protocols (for example, you might
     *                                  want one server to be able to handle
     *                                  different types of interactions depending
     *                                  on the specified protocol). If you don't
     *                                  specify a protocol string, an empty string
     *                                  is assumed.
     * @throws SECURITY_ERR The port to which the connection is being attempted is being blocked.
     */
    WindowSocket.prototype.init = function(config) {
        if (this.initialized) return this.logger.warn('Already initialized');
        this.initialized = true;

        this.ID = Date.now();

        console.log('WindowSocket: Init!');
        _extend(this, config);

        if (!this.url) throw new TypeError(ERROR_REQUIRED_ARGUMENT);
        if (this.url.indexOf(this.SCHEME) !== 0) throw new SyntaxError(ERROR_REQUIRED_ARGUMENT);
        if (!this.SERVER.connect(this)) throw new Error(ERROR_CONNECT_URL);

        return 'This is just a stub!';
    };

    /**
     * Transmits data to the server over the WindowSocket
     * connection.
     * @param  {String} data A text string to send to
     *                       the server
     * @throws {INVALID_STATE_ERR} If connection is not currently OPEN.
     * @throws {SYNTAX_ERR} The data is a string that has unpaired surrogates.
     * @return {void}
     */
    WindowSocket.prototype.send = function(data) {
        //Since we send the connection event on the next frame
        //we might construct a new WindowSocket instance and
        //call send right away. If state is CONNECTING we should
        //probably throw an Error?
        //TODO: Confirm WebSocket behavior.
        if (this.readyState === WindowSocket.CONNECTING) {
            return;
        }

        if (this.readyState !== WindowSocket.OPEN) {
            throw new Error(ERROR_INVALID_STATE);
        }

        this.SERVER.send(this, data);
    };

    /**
     * Closes the WindowSocket connection or connection
     * attempt, if any. If the connection is already
     * CLOSED, this method does nothing.
     * @return {void}
     *
     * @param  {Number} code   A numeric value indicating the status
     *                         code explaining why the connection is
     *                         being closed. If this parameter is not
     *                         specified, a default value of 1000
     *                         (indicating a normal "transaction complete"
     *                         closure) is assumed. See the list of status
     *                         codes on the CloseEvent page for
     *                         permitted values.
     * @param  {String} reason A human-readable string explaining why the
     *                         connection is closing. This string must be
     *                         no longer than 123 bytes of UTF-8 text
     *                         (not characters).
     * @throws {INVALID_ACCESS_ERR} If an invalid code was specified.
     * @throws {SYNTAX_ERR} The reason string is too long or contains
     *                      unpaired surrogates.
     * @return {void}
     */
    WindowSocket.prototype.close = function(code, reason) {
        //TODO: Guard against initial frame delay!
        //
        if (this.readyState === WindowSocket.CLOSED || this.readyState === WindowSocket.CLOSING) {
            return;
        }

        this.readyState = WindowSocket.CLOSING;
        this.SERVER.close(this);
    };

    ///////////////////////////////////////////////////
    // EventTarget Interface
    // @see http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration
    ///////////////////////////////////////////////////

    /**
     * DOM 2 EventTarget Interface implementation.
     * @param {String} type
     * @param {Function} listener
     * @param {Boolean} useCapture
     * @return {void}
     */
    WindowSocket.prototype.addEventListener = function(type, listener, useCapture) {
        if (!(type in this._listeners)) this._listeners[type] = [];
        this._listeners[type].push(listener);
    };

    /**
     * DOM 2 EventTarget Interface implementation.
     * @param {String} type
     * @param {Function} listener
     * @param {Boolean} useCapture
     * @return {void}
     */
    WindowSocket.prototype.removeEventListener = function(type, listener, useCapture) {
        if (!(type in this._listeners)) return;
        var events = this._listeners[type];
        var i = events.length - 1;
        for (i; i >= 0; --i) {
            if (events[i] !== listener) continue;
            events.splice(i, 1);
            break;
        }
    };

    /**
     * DOM 2 EventTarget Interface implementation.
     * @param {Event} event
     * @return {void}
     */
    WindowSocket.prototype.dispatchEvent = function(event) {
        var events = this._listeners[event.type] || [];
        var t = events.length,
            i = 0;
        for (; i < t; ++i) {
            events[i](event);
        }
        //apply delegated callbacks:
        var handler = this['on' + event.type];
        if (typeof handler === 'function') handler.call(this, event);
    };

    ///////////////////////////////////////////////////
    // PRIVATE METHODS
    ///////////////////////////////////////////////////
    /**
     * Method to trigger external events.
     * @param  {Object} e
     * @return {void}
     */
    WindowSocket.prototype._triggerEvent = function(e) {
        if (e.readyState) this.readyState = e.readyState;

        //TODO: Clean this up. It's really gross!
        var event,
            events = ['open', 'error', 'close'];
        if (events.indexOf(e.type) !== -1) {
            event = this._buildEvent(e.type, e);
        } else if (e.type === 'message') {
            event = this._buildMessageEvent(e.type, e.message);
        } else {
            //TODO: ErrorMessage factory, replace strings.
            throw new Error(ERROR_UNKNOWN_EVENT_TYPE);
        }

        this.dispatchEvent(event);
    };

    /**
     * Create an Event instance.
     * Interface DocumentEvent DOM Level 2.
     *
     * @see http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-DocumentEvent
     * @see https://developer.mozilla.org/en-US/docs/Web/API/document.createEvent
     * @param  {String} type
     * @return {void}
     */
    WindowSocket.prototype._buildEvent = function(e) {
        //assert(e.type);
        if (document.createEvent && window.Event) {
            var event = document.createEvent('Event');
            event.initEvent(e.type, false, false);
            return event;
        }
        return {
            type: e.type,
            bubbles: false,
            cancelable: false
        };
    };

    /**
     * Create an MessageEvent instance.
     * Interface DocumentEvent DOM Level 2.
     *
     * @see http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-DocumentEvent
     * @see https://developer.mozilla.org/en-US/docs/Web/API/document.createEvent
     * @param  {String} data
     * @return {void}
     */
    WindowSocket.prototype._buildMessageEvent = function(e) {
        //TODO: assert(e.message);
        var data = e.message;

        if (window.MessageEvent && typeof MessageEvent === 'function') {
            return new MessageEvent('message', {
                'view': window,
                'bubbles': false,
                'cancelable': false,
                'data': data
            });
        }

        if (document.createEvent && window.MessageEvent) {
            var event = document.createEvent('MessageEvent');
            event.initWithMessage('message', false, false, data, null, null, window, null);
            return event;
        }

        return {
            type: 'message',
            data: data,
            bubbles: false,
            cancelable: false
        };
    };

    /**
     * @see  https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
     * @param  {Event} e
     * @return {void}
     */
    WindowSocket.prototype._buildCloseEvent = function(e) {
        //TODO: assert(e.code, e.reason, e.clean);
        var event = this._buildEvent('close');
        event.code = e.code;
        event.reason = e.reason;
        event.wasClean = e.clean;

        return event;
    };

    WindowSocket.prototype._throwError = function(message) {
        var args = [].splice.call(arguments, 1);
        //TODO: replace message tokens, throw error
    };

    /**
     * Logger method, meant to be implemented by
     * mixin. As a placeholder, we use console if available
     * or a shim if not present.
     */
    WindowSocket.prototype.logger = _shimConsole(console);

    function WindowServer() {
        this._instances = {};
    }

    WindowServer.instance = function() {
        if (this._instance) return this._instance;
        this._instance = new WindowServer();
        this._instance.connection = window._tcpManager();
        return this._instance;
    };

    WindowServer.prototype.init = function() {
        if (this.initialized) return;
        this.initialized = true;
    };

    WindowServer.prototype.connect = function(instance) {
        if (!this.connection) return false;

        this._instances[instance.ID] = instance;
        var url = instance.url;

        //execute next
        setTimeout(function() {
            instance.readyState = WindowSocket.OPEN;
            instance.onopen(new Event('open'));
        }, 0);

        return true;
    };

    WindowServer.prototype.handle = function(events) {
        //event => should have type
        //message
        events.map(function(event) {
            this._instances[event.clientId]._triggerEvent(event);
        }, this);
    };

    WindowServer.prototype.send = function(instance, data) {
        _tcpManager().send(instance.url, data);
    };

    WindowServer.prototype.close = function(instance) {
        _tcpManager().close(instance.url);
        instance.readyState = WindowSocket.CLOSED;
    };

    return WindowSocket;
}));

function _tcpManager() {
    this.onMessage = function() {};
    this.send = function(url, msg) {
        console.log('SENDING:', url, msg)
    };
    this.close = function(url) {
        console.log('CLOSING')
    };
    return this;
}<|endoftext|>