392 行
9.8 KiB
JavaScript
392 行
9.8 KiB
JavaScript
// Copyright 2011 David Galles, University of San Francisco. All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without modification, are
|
|
// permitted provided that the following conditions are met:
|
|
//
|
|
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
|
// conditions and the following disclaimer.
|
|
//
|
|
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
|
// of conditions and the following disclaimer in the documentation and/or other materials
|
|
// provided with the distribution.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
|
|
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
//
|
|
// The views and conclusions contained in the software and documentation are those of the
|
|
// authors and should not be interpreted as representing official policies, either expressed
|
|
// or implied, of the University of San Francisco
|
|
|
|
function addLabelToAlgorithmBar(labelName)
|
|
{
|
|
var element = document.createTextNode(labelName);
|
|
|
|
var tableEntry = document.createElement("td");
|
|
tableEntry.appendChild(element);
|
|
|
|
|
|
var controlBar = document.getElementById("AlgorithmSpecificControls");
|
|
|
|
//Append the element in page (in span).
|
|
controlBar.appendChild(tableEntry);
|
|
return element;
|
|
}
|
|
|
|
// TODO: Make this stackable like radio butons
|
|
// (keep backwards compatible, thought)
|
|
function addCheckboxToAlgorithmBar(boxLabel)
|
|
{
|
|
var element = document.createElement("input");
|
|
|
|
element.setAttribute("type", "checkbox");
|
|
element.setAttribute("value", boxLabel);
|
|
|
|
var label = document.createTextNode(boxLabel);
|
|
|
|
var tableEntry = document.createElement("td");
|
|
tableEntry.appendChild(element);
|
|
tableEntry.appendChild(label);
|
|
|
|
var controlBar = document.getElementById("AlgorithmSpecificControls");
|
|
|
|
//Append the element in page (in span).
|
|
controlBar.appendChild(tableEntry);
|
|
return element;
|
|
}
|
|
|
|
function addRadioButtonGroupToAlgorithmBar(buttonNames, groupName)
|
|
{
|
|
var buttonList = [];
|
|
var newTable = document.createElement("table");
|
|
|
|
for (var i = 0; i < buttonNames.length; i++)
|
|
{
|
|
var midLevel = document.createElement("tr");
|
|
var bottomLevel = document.createElement("td");
|
|
|
|
var button = document.createElement("input");
|
|
button.setAttribute("type", "radio");
|
|
button.setAttribute("name", groupName);
|
|
button.setAttribute("value", buttonNames[i]);
|
|
bottomLevel.appendChild(button);
|
|
midLevel.appendChild(bottomLevel);
|
|
var txtNode = document.createTextNode(" " + buttonNames[i]);
|
|
bottomLevel.appendChild(txtNode);
|
|
newTable.appendChild(midLevel);
|
|
buttonList.push(button);
|
|
}
|
|
|
|
var topLevelTableEntry = document.createElement("td");
|
|
topLevelTableEntry.appendChild(newTable);
|
|
|
|
var controlBar = document.getElementById("AlgorithmSpecificControls");
|
|
controlBar.appendChild(topLevelTableEntry);
|
|
|
|
return buttonList
|
|
}
|
|
|
|
|
|
function addControlToAlgorithmBar(type, name) {
|
|
|
|
var element = document.createElement("input");
|
|
|
|
element.setAttribute("type", type);
|
|
element.setAttribute("value", name);
|
|
// element.setAttribute("name", name);
|
|
|
|
|
|
var tableEntry = document.createElement("td");
|
|
|
|
tableEntry.appendChild(element);
|
|
|
|
|
|
var controlBar = document.getElementById("AlgorithmSpecificControls");
|
|
|
|
//Append the element in page (in span).
|
|
controlBar.appendChild(tableEntry);
|
|
return element;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function Algorithm(am)
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Algorithm.prototype.setCodeAlpha = function(code, newAlpha)
|
|
{
|
|
var i,j;
|
|
for (i = 0; i < code.length; i++)
|
|
for (j = 0; j < code[i].length; j++) {
|
|
this.cmd("SetAlpha", code[i][j], newAlpha);
|
|
}
|
|
}
|
|
|
|
|
|
Algorithm.prototype.addCodeToCanvasBase = function(code, start_x, start_y, line_height, standard_color, layer)
|
|
{
|
|
layer = typeof layer !== 'undefined' ? layer : 0;
|
|
var codeID = Array(code.length);
|
|
var i, j;
|
|
for (i = 0; i < code.length; i++)
|
|
{
|
|
codeID[i] = new Array(code[i].length);
|
|
for (j = 0; j < code[i].length; j++)
|
|
{
|
|
codeID[i][j] = this.nextIndex++;
|
|
this.cmd("CreateLabel", codeID[i][j], code[i][j], start_x, start_y + i * line_height, 0);
|
|
this.cmd("SetForegroundColor", codeID[i][j], standard_color);
|
|
this.cmd("SetLayer", codeID[i][j], layer);
|
|
if (j > 0)
|
|
{
|
|
this.cmd("AlignRight", codeID[i][j], codeID[i][j-1]);
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
return codeID;
|
|
}
|
|
|
|
|
|
Algorithm.prototype.init = function(am, w, h)
|
|
{
|
|
this.animationManager = am;
|
|
am.addListener("AnimationStarted", this, this.disableUI);
|
|
am.addListener("AnimationEnded", this, this.enableUI);
|
|
am.addListener("AnimationUndo", this, this.undo);
|
|
this.canvasWidth = w;
|
|
this.canvasHeight = h;
|
|
|
|
this.actionHistory = [];
|
|
this.recordAnimation = true;
|
|
this.commands = []
|
|
}
|
|
|
|
|
|
// Overload in subclass
|
|
Algorithm.prototype.sizeChanged = function(newWidth, newHeight)
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Algorithm.prototype.implementAction = function(funct, val)
|
|
{
|
|
var nxt = [funct, val];
|
|
this.actionHistory.push(nxt);
|
|
var retVal = funct(val);
|
|
this.animationManager.StartNewAnimation(retVal);
|
|
}
|
|
|
|
|
|
Algorithm.prototype.isAllDigits = function(str)
|
|
{
|
|
for (var i = str.length - 1; i >= 0; i--)
|
|
{
|
|
if (str.charAt(i) < "0" || str.charAt(i) > "9")
|
|
{
|
|
return false;
|
|
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
Algorithm.prototype.normalizeNumber = function(input, maxLen)
|
|
{
|
|
return parseInt(input);
|
|
}
|
|
|
|
Algorithm.prototype.disableUI = function(event)
|
|
{
|
|
// to be overridden in base class
|
|
}
|
|
|
|
Algorithm.prototype.enableUI = function(event)
|
|
{
|
|
// to be overridden in base class
|
|
}
|
|
|
|
|
|
|
|
function controlKey(keyASCII)
|
|
{
|
|
return keyASCII == 8 || keyASCII == 9 || keyASCII == 37 || keyASCII == 38 ||
|
|
keyASCII == 39 || keyASCII == 40 || keyASCII == 46;
|
|
}
|
|
|
|
|
|
|
|
Algorithm.prototype.returnSubmitFloat = function(field, funct, maxsize)
|
|
{
|
|
if (maxsize != undefined)
|
|
{
|
|
field.size = maxsize;
|
|
}
|
|
return function(event)
|
|
{
|
|
var keyASCII = 0;
|
|
if(window.event) // IE
|
|
{
|
|
keyASCII = event.keyCode
|
|
}
|
|
else if (event.which) // Netscape/Firefox/Opera
|
|
{
|
|
keyASCII = event.which
|
|
}
|
|
// Submit on return
|
|
if (keyASCII == 13)
|
|
{
|
|
funct();
|
|
}
|
|
// Control keys (arrows, del, etc) are always OK
|
|
else if (controlKey(keyASCII))
|
|
{
|
|
return;
|
|
}
|
|
// - (minus sign) only OK at beginning of number
|
|
// (For now we will allow anywhere -- hard to see where the beginning of the
|
|
// number is ...)
|
|
//else if (keyASCII == 109 && field.value.length == 0)
|
|
else if (keyASCII == 109)
|
|
{
|
|
return;
|
|
}
|
|
// Digis are OK if we have enough space
|
|
else if ((maxsize != undefined || field.value.length < maxsize) &&
|
|
(keyASCII >= 48 && keyASCII <= 57))
|
|
{
|
|
return;
|
|
}
|
|
// . (Decimal point) is OK if we haven't had one yet, and there is space
|
|
else if ((maxsize != undefined || field.value.length < maxsize) &&
|
|
(keyASCII == 190) && field.value.indexOf(".") == -1)
|
|
|
|
{
|
|
return;
|
|
}
|
|
// Nothing else is OK
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
Algorithm.prototype.returnSubmit = function(field, funct, maxsize, intOnly)
|
|
{
|
|
if (maxsize != undefined)
|
|
{
|
|
field.size = maxsize;
|
|
}
|
|
return function(event)
|
|
{
|
|
var keyASCII = 0;
|
|
if(window.event) // IE
|
|
{
|
|
keyASCII = event.keyCode
|
|
}
|
|
else if (event.which) // Netscape/Firefox/Opera
|
|
{
|
|
keyASCII = event.which
|
|
}
|
|
|
|
if (keyASCII == 13 && funct !== null)
|
|
{
|
|
funct();
|
|
}
|
|
else if (keyASCII == 190 || keyASCII == 59 || keyASCII == 173 || keyASCII == 189)
|
|
{
|
|
return false;
|
|
|
|
}
|
|
else if ((maxsize != undefined && field.value.length >= maxsize) ||
|
|
intOnly && (keyASCII < 48 || keyASCII > 57))
|
|
{
|
|
if (!controlKey(keyASCII))
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Algorithm.prototype.addReturnSubmit = function(field, action)
|
|
{
|
|
field.onkeydown = this.returnSubmit(field, action, 4, false);
|
|
}
|
|
|
|
Algorithm.prototype.reset = function()
|
|
{
|
|
// to be overriden in base class
|
|
// (Throw exception here?)
|
|
}
|
|
|
|
Algorithm.prototype.undo = function(event)
|
|
{
|
|
// Remvoe the last action (the one that we are going to undo)
|
|
this.actionHistory.pop();
|
|
// Clear out our data structure. Be sure to implement reset in
|
|
// every AlgorithmAnimation subclass!
|
|
this.reset();
|
|
// Redo all actions from the beginning, throwing out the animation
|
|
// commands (the animation manager will update the animation on its own).
|
|
// Note that if you do something non-deterministic, you might cause problems!
|
|
// Be sure if you do anything non-deterministic (that is, calls to a random
|
|
// number generator) you clear out the undo stack here and in the animation
|
|
// manager.
|
|
//
|
|
// If this seems horribly inefficient -- it is! However, it seems to work well
|
|
// in practice, and you get undo for free for all algorithms, which is a non-trivial
|
|
// gain.
|
|
var len = this.actionHistory.length;
|
|
this.recordAnimation = false;
|
|
for (var i = 0; i < len; i++)
|
|
{
|
|
this.actionHistory[i][0](this.actionHistory[i][1]);
|
|
}
|
|
this.recordAnimation = true;
|
|
}
|
|
|
|
|
|
Algorithm.prototype.clearHistory = function()
|
|
{
|
|
this.actionHistory = [];
|
|
}
|
|
|
|
// Helper method to add text input with nice border.
|
|
// AS3 probably has a built-in way to do this. Replace when found.
|
|
|
|
|
|
// Helper method to create a command string from a bunch of arguments
|
|
Algorithm.prototype.cmd = function()
|
|
{
|
|
if (this.recordAnimation)
|
|
{
|
|
var command = arguments[0];
|
|
for(i = 1; i < arguments.length; i++)
|
|
{
|
|
command = command + "<;>" + String(arguments[i]);
|
|
}
|
|
this.commands.push(command);
|
|
}
|
|
|
|
}
|