Index: experimental/conways_life/life.js |
diff --git a/experimental/conways_life/life.js b/experimental/conways_life/life.js |
deleted file mode 100644 |
index 02f85271597103d95eb45bcac3007935d762e13c..0000000000000000000000000000000000000000 |
--- a/experimental/conways_life/life.js |
+++ /dev/null |
@@ -1,425 +0,0 @@ |
-// Copyright 2011 (c) The Native Client Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-/** |
- * @file |
- * The life Application object. This object instantiates a Dragger object and |
- * connects it to the element named @a life_module. |
- */ |
- |
-goog.provide('life.Application'); |
- |
-goog.require('goog.Disposable'); |
-goog.require('goog.array'); |
-goog.require('goog.events.EventType'); |
-goog.require('goog.style'); |
- |
-goog.require('life.controllers.ViewController'); |
-goog.require('stamp.StampPanel'); |
- |
-/** |
- * Constructor for the Application class. Use the run() method to populate |
- * the object with controllers and wire up the events. |
- * @constructor |
- * @extends {goog.Disposable} |
- */ |
-life.Application = function() { |
- goog.Disposable.call(this); |
-} |
-goog.inherits(life.Application, goog.Disposable); |
- |
-/** |
- * The view controller for the application. A DOM element that encapsulates |
- * the grayskull plugin; this is allocated at run time. Connects to the |
- * element with id life.Application.DomIds_.VIEW. |
- * @type {life.ViewController} |
- * @private |
- */ |
-life.Application.prototype.viewController_ = null; |
- |
-/** |
- * The automaton rule string. It is expressed as SSS/BB, where S is the |
- * neighbour count that keeps a cell alive, and B is the count that causes a |
- * cell to become alive. See the .LIF 1.05 section in |
- * http://psoup.math.wisc.edu/mcell/ca_files_formats.html for more info. |
- * @default 23/3 the "Normal" Conway rules. |
- * @type {Object.<Array>} |
- * @private |
- */ |
-life.Application.prototype.automatonRules_ = { |
- birthRule: [3], |
- keepAliveRule: [2, 3] |
-}; |
- |
-/** |
- * The id of the current stamp. Defaults to the DEFAULT_STAMP_ID. |
- * @type {string} |
- * @private |
- */ |
-life.Application.prototype.currentStampId_ = |
- life.controllers.ViewController.DEFAULT_STAMP_ID; |
- |
-/** |
- * The ids used for elements in the DOM. The Life Application expects these |
- * elements to exist. |
- * @enum {string} |
- * @private |
- */ |
-life.Application.DomIds_ = { |
- BIRTH_FIELD: 'birth_field', // Text field with the birth rule string. |
- CLEAR_BUTTON: 'clear_button', // The clear button element. |
- KEEP_ALIVE_FIELD: 'keep_alive_field', // Keep alive rule string. |
- MODULE: 'life_module', // The <embed> element representing the NaCl module. |
- PLAY_MODE_SELECT: 'play_mode_select', // The <select> element for play mode. |
- PLAY_BUTTON: 'play_button', // The play button element. |
- SOUND_SELECT: 'sound_select', // The <select> element for the stamp sound. |
- VIEW: 'life_view' // The <div> containing the NaCl element. |
-}; |
- |
-/** |
- * The Run/Stop button attribute labels. These are used to determine the state |
- * and label of the button. |
- * @enum {string} |
- * @private |
- */ |
-life.Application.PlayButtonAttributes_ = { |
- ALT_IMAGE: 'altimage', // Image to display in the "on" state. |
- STATE: 'state' // The button's state. |
-}; |
- |
-/** |
- * Override of disposeInternal() to dispose of retained objects. |
- * @override |
- */ |
-life.Application.prototype.disposeInternal = function() { |
- this.terminate(); |
- life.Application.superClass_.disposeInternal.call(this); |
-} |
- |
-/** |
- * Called by the module loading function once the module has been loaded. Wire |
- * up a Dragger object to @a this. |
- * @param {?String} opt_naclModuleId The id of an <EMBED> element which |
- * contains the NaCl module. If unspecified, defaults to DomIds_.MODULE. |
- * If the DOM element doesn't exist, the program asserts and exits. |
- */ |
-life.Application.prototype.moduleDidLoad = function(opt_naclModuleId) { |
- // Listen for 'unload' in order to terminate cleanly. |
- goog.events.listen(window, goog.events.EventType.UNLOAD, this.terminate); |
- |
- // Set up the stamp editor. |
- var stampEditorButton = |
- document.getElementById(stamp.StampPanel.DomIds.STAMP_EDITOR_BUTTON); |
- this.stampPanel_ = new stamp.StampPanel(stampEditorButton); |
- var stampEditorButtons = { |
- mainPanel: |
- document.getElementById(stamp.StampPanel.DomIds.STAMP_EDITOR_PANEL), |
- editorContainer: |
- document.getElementById(stamp.StampPanel.DomIds.STAMP_EDITOR_CONTAINER), |
- addColumnButton: |
- document.getElementById(stamp.StampPanel.DomIds.ADD_COLUMN_BUTTON), |
- removeColumnButton: |
- document.getElementById(stamp.StampPanel.DomIds.REMOVE_COLUMN_BUTTON), |
- addRowButton: |
- document.getElementById(stamp.StampPanel.DomIds.ADD_ROW_BUTTON), |
- removeRowButton: |
- document.getElementById(stamp.StampPanel.DomIds.REMOVE_ROW_BUTTON), |
- cancelButton: |
- document.getElementById(stamp.StampPanel.DomIds.CANCEL_BUTTON), |
- okButton: document.getElementById(stamp.StampPanel.DomIds.OK_BUTTON) |
- }; |
- this.stampPanel_.makeStampEditorPanel(stampEditorButtons); |
- |
- // When the stamp editor panel is about to open, set its stamp to the |
- // current stamp. |
- goog.events.listen(this.stampPanel_, stamp.StampPanel.Events.PANEL_WILL_OPEN, |
- this.handlePanelWillOpen_, false, this); |
- |
- // Listen for the "PANEL_DID_SAVE" event posted by the stamp editor. |
- goog.events.listen(this.stampPanel_, stamp.StampPanel.Events.PANEL_DID_SAVE, |
- this.handlePanelDidSave_, false, this); |
- |
- // Set up the view controller, it contains the NaCl module. |
- var naclModuleId = opt_naclModuleId || life.Application.DomIds_.MODULE; |
- this.viewController_ = new life.controllers.ViewController( |
- document.getElementById(naclModuleId)); |
- this.viewController_.setAutomatonRules(this.automatonRules_); |
- // Initialize the module with the default stamp. |
- this.currentStampId_ = this.viewController_.DEFAULT_STAMP_ID; |
- this.viewController_.selectStamp(this.currentStampId_); |
- |
- this.viewController_.setStampSoundUrl('sounds/boing_x.wav'); |
- |
- // Wire up the various controls. |
- var playModeSelect = |
- document.getElementById(life.Application.DomIds_.PLAY_MODE_SELECT); |
- if (playModeSelect) { |
- goog.events.listen(playModeSelect, goog.events.EventType.CHANGE, |
- this.selectPlayMode, false, this); |
- } |
- |
- var soundSelect = |
- document.getElementById(life.Application.DomIds_.SOUND_SELECT); |
- if (playModeSelect) { |
- goog.events.listen(soundSelect, goog.events.EventType.CHANGE, |
- this.selectSound, false, this); |
- } |
- |
- var clearButton = |
- document.getElementById(life.Application.DomIds_.CLEAR_BUTTON); |
- if (clearButton) { |
- goog.events.listen(clearButton, goog.events.EventType.CLICK, |
- this.clear, false, this); |
- } |
- |
- var playButton = |
- document.getElementById(life.Application.DomIds_.PLAY_BUTTON); |
- if (playButton) { |
- goog.events.listen(playButton, goog.events.EventType.CLICK, |
- this.togglePlayButton, false, this); |
- } |
- |
- var birthField = |
- document.getElementById(life.Application.DomIds_.BIRTH_FIELD); |
- if (birthField) { |
- goog.events.listen(birthField, goog.events.EventType.CHANGE, |
- this.updateBirthRule, false, this); |
- } |
- |
- var keepAliveField = |
- document.getElementById(life.Application.DomIds_.KEEP_ALIVE_FIELD); |
- if (keepAliveField) { |
- goog.events.listen(keepAliveField, goog.events.EventType.CHANGE, |
- this.updateKeepAliveRule, false, this); |
- } |
-} |
- |
-/** |
- * Change the play mode. |
- * @param {!goog.events.Event} changeEvent The CHANGE event that triggered this |
- * handler. |
- */ |
-life.Application.prototype.selectPlayMode = function(changeEvent) { |
- changeEvent.stopPropagation(); |
- this.viewController_.setPlayMode(changeEvent.target.value); |
-} |
- |
-/** |
- * Change the stamp sound. |
- * @param {!goog.events.Event} changeEvent The CHANGE event that triggered this |
- * handler. |
- */ |
-life.Application.prototype.selectSound = function(changeEvent) { |
- changeEvent.stopPropagation(); |
- this.viewController_.setStampSoundUrl(changeEvent.target.value); |
-} |
- |
-/** |
- * Toggle the simulation. |
- * @param {!goog.events.Event} clickEvent The CLICK event that triggered this |
- * handler. |
- */ |
-life.Application.prototype.togglePlayButton = function(clickEvent) { |
- clickEvent.stopPropagation(); |
- var button = clickEvent.target; |
- var buttonImage = button.style.backgroundImage; |
- var altImage = button.getAttribute( |
- life.Application.PlayButtonAttributes_.ALT_IMAGE); |
- var state = button.getAttribute( |
- life.Application.PlayButtonAttributes_.STATE); |
- // Switch the inner and alternate labels. |
- goog.style.setStyle(button, { 'backgroundImage': altImage }); |
- button.setAttribute(life.Application.PlayButtonAttributes_.ALT_IMAGE, |
- buttonImage); |
- if (state == 'off') { |
- button.setAttribute( |
- life.Application.PlayButtonAttributes_.STATE, 'on'); |
- this.viewController_.run(); |
- } else { |
- button.setAttribute( |
- life.Application.PlayButtonAttributes_.STATE, 'off'); |
- this.viewController_.stop(); |
- } |
-} |
- |
-/** |
- * Handle the "panel will open" event: set the stamp in the stamp editor to |
- * the current stamp. |
- * @param {!goog.events.Event} event The event that triggered this handler. |
- * @private |
- */ |
-life.Application.prototype.handlePanelWillOpen_ = function(event) { |
- event.stopPropagation(); |
- var currentStamp = |
- this.viewController_.stampWithId(this.currentStampId_); |
- if (currentStamp) |
- this.stampPanel_.setStampFromString(currentStamp); |
- return true; |
-} |
- |
-/** |
- * Handle the "panel did save" event: grab the stamp from the stamp editor, |
- * add it to the dictionary of stamps and set it as the current stamp. |
- * @param {!goog.events.Event} event The event that triggered this handler. |
- * @private |
- */ |
-life.Application.prototype.handlePanelDidSave_ = function(event) { |
- event.stopPropagation(); |
- var stampString = this.stampPanel_.getStampAsString(); |
- this.viewController_.addStampWithId(stampString, this.currentStampId_); |
- this.viewController_.selectStamp(this.currentStampId_); |
-} |
- |
-/** |
- * Clear the current simulation. |
- * @param {!goog.events.Event} clickEvent The CLICK event that triggered this |
- */ |
-life.Application.prototype.clear = function(clickEvent) { |
- clickEvent.stopPropagation(); |
- this.viewController_.clear(); |
-} |
- |
-/** |
- * Read the text input and change it from a comma-separated list into a string |
- * of the form BB, where B is a digit in [0..9] that represents the neighbour |
- * count that causes a cell to come to life. |
- * @param {!goog.events.Event} changeEvent The CHANGE event that triggered this |
- * handler. |
- */ |
-life.Application.prototype.updateBirthRule = function(changeEvent) { |
- changeEvent.stopPropagation(); |
- var birthRule = this.parseAutomatonRule_(changeEvent.target.value); |
- // Put the sanitized version of the rule string back into the text field. |
- changeEvent.target.value = birthRule.join(','); |
- // Make the new rule string and tell the NaCl module. |
- this.automatonRules_.birthRule = birthRule; |
- this.viewController_.setAutomatonRules(this.automatonRules_); |
-} |
- |
-/** |
- * Read the text input and change it from a comma-separated list into a string |
- * of the form SSS, where S is a digit in [0..9] that represents the neighbour |
- * count that allows a cell to stay alive. |
- * @param {!goog.events.Event} changeEvent The CHANGE event that triggered this |
- * handler. |
- */ |
-life.Application.prototype.updateKeepAliveRule = function(changeEvent) { |
- changeEvent.stopPropagation(); |
- var keepAliveRule = this.parseAutomatonRule_(changeEvent.target.value); |
- // Put the sanitized version of the rule string back into the text field. |
- changeEvent.target.value = keepAliveRule.join(','); |
- // Make the new rule string and tell the NaCl module. |
- this.automatonRules_.keepAliveRule = keepAliveRule; |
- this.viewController_.setAutomatonRules(this.automatonRules_); |
-} |
- |
-/** |
- * Parse a user-input string representing an automaton rule into an array of |
- * neighbour counts. |ruleString| is expected to be a comma-separated string |
- * of integers in range [0..9]. This routine attempts to sanitize non- |
- * conforming values by clipping (numbers outside [0..9] are clipped), and |
- * replaces non-numberic input with 0. The resulting array is sorted, and each |
- * value is unique. For example: '1,3,2,2' will produce [1, 2, 3]. |
- * @param {!string} ruleString The user-input string. |
- * @return {Array.<number>} An array of neighbour counts that can be used to |
- * create an automaton rule. |
- * @private |
- */ |
-life.Application.prototype.parseAutomatonRule_ = function(ruleString) { |
- var rule = ruleString.split(','); |
- |
- /** |
- * Helper function to parse a single rule element: trim off any leading or |
- * trailing white-space, then attempt to convert the resulting string into |
- * an integer. Clip the integer to range [0..8]. Replace the element in |
- * |array| with the resulting number. Note: non-numeric values are replaced |
- * with 0. |
- * @param {string} ruleString The string to parse. |
- * @param {number} index The index of the element in the original array. |
- * @param {Array} ruleArray The array of rules. |
- */ |
- function parseOneRule(ruleString, index, ruleArray) { |
- var neighbourCount = parseInt(ruleString.trim()); |
- if (isNaN(neighbourCount) || neighbourCount < 0) |
- neighbourCount = 0; |
- if (neighbourCount > 8) |
- neighbourCount = 8; |
- ruleArray[index] = neighbourCount; |
- } |
- |
- // Each rule has to be an integer in [0..8] |
- goog.array.forEach(rule, parseOneRule, this); |
- // Sort the rules numerically. |
- rule.sort(function(a, b) { return a - b; }); |
- goog.array.removeDuplicates(rule); |
- return rule; |
-} |
- |
-/** |
- * Asserts that cond is true; issues an alert and throws an Error otherwise. |
- * @param {bool} cond The condition. |
- * @param {String} message The error message issued if cond is false. |
- */ |
-life.Application.prototype.assert = function(cond, message) { |
- if (!cond) { |
- message = "Assertion failed: " + message; |
- alert(message); |
- throw new Error(message); |
- } |
-} |
- |
-/** |
- * The run() method starts and 'runs' the application. An <EMBED> tag is |
- * injected into the <DIV> element |opt_viewDivName| which causes the NaCl |
- * module to be loaded. Once loaded, the moduleDidLoad() method is called via |
- * a 'load' event handler that is attached to the <DIV> element. |
- * @param {?String} opt_viewDivName The id of a DOM element in which to |
- * embed the NaCl module. If unspecified, defaults to DomIds_.VIEW. The |
- * DOM element must exist. |
- */ |
-life.Application.prototype.run = function(opt_viewDivName) { |
- var viewDivName = opt_viewDivName || life.Application.DomIds_.VIEW; |
- var viewDiv = document.getElementById(viewDivName); |
- this.assert(viewDiv, "Missing DOM element '" + viewDivName + "'"); |
- |
- // A small handler for the 'load' event. It stops propagation of the 'load' |
- // event and then calls moduleDidLoad(). The handler is bound to |this| so |
- // that the calling context of the closure makes |this| point to this |
- // instance of the life.Applicaiton object. |
- var loadEventHandler = function(loadEvent) { |
- this.moduleDidLoad(life.Application.DomIds_.MODULE); |
- } |
- |
- // Note that the <EMBED> element is wrapped inside a <DIV>, which has a 'load' |
- // event listener attached. This method is used instead of attaching the |
- // 'load' event listener directly to the <EMBED> element to ensure that the |
- // listener is active before the NaCl module 'load' event fires. |
- viewDiv.addEventListener('load', goog.bind(loadEventHandler, this), true); |
- |
- var viewSize = goog.style.getSize(viewDiv); |
- viewDiv.innerHTML = '<embed id="' + life.Application.DomIds_.MODULE + '" ' + |
- ' class="autosize"' + |
- ' width=' + viewSize.width + |
- ' height=' + viewSize.height + |
- ' src="life.nmf"' + |
- ' type="application/x-nacl" />' |
-} |
- |
-/** |
- * Shut down the application instance. This unhooks all the event listeners |
- * and deletes the objects created in moduleDidLoad(). |
- */ |
-life.Application.prototype.terminate = function() { |
- goog.events.removeAll(); |
- this.viewController_ = null; |
-} |
- |
-/** |
- * Extend the String class to trim whitespace. |
- * @return {string} the original string with leading and trailing whitespace |
- * removed. |
- */ |
-String.prototype.trim = function () { |
- return this.replace(/^\s*/, '').replace(/\s*$/, ''); |
-} |