Created
May 8, 2012 14:25
-
-
Save dzucconi/2635538 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// The following program is written by Leslie Carr, IAM Research Group, Southampton University, UK | |
// and is based on work Zigzag(TM) work owned by Ted Nelson. | |
// | |
// This file may be distributed freely for research purposes only. No commercial product or service | |
// may be based on it or on any part of version of it. | |
// All queries should be directed to the author at [email protected] | |
// | |
// VERSION 0.5.2 large (40 cell) table, deleting, hopping | |
// VERSION 0.5.5 experimental zigzag traversal | |
// VERSION 0.6 clones, saving in XML, saving in HTML | |
// | |
// TO DO: | |
// fix keyboard focus problem | |
// new visualisations | |
// | |
var zzVersion = "0.6" | |
var zzCurrentN = 1; | |
var zzDimOne = ""; | |
var zzDimTwo = ""; | |
var zzMaxDims = 100 | |
var zzTopDim = -1; | |
var zzDims = new Array(zzMaxDims) | |
var zzDimDescs = new Array(zzMaxDims) | |
var zzDimCols = new Array(zzMaxDims) | |
var onlyTurnAtCurrent = false | |
var horizonLimit = true | |
var maxTurns = 1 | |
var zzTourN = 1 | |
var zzTourSteps = new Array(zzMaxItems) | |
var zzExternalWindow = false | |
var zzMaxX = 20 | |
var zzMaxY = 20 | |
var zzMaxItems = 10000 | |
var zzNegWard = 0, | |
zzPosWard = 1 | |
var zzItems = new Array(zzMaxItems) | |
var zzLinks = new Array(zzMaxItems) | |
var zzFilled = new Array(zzMaxItems) | |
var zzFilledN = 0 | |
var zzEditing = 0 | |
var zzAdding = false | |
var zzDeleting = false | |
var zzChugging = false | |
var zzDebug = 0 | |
var zzTitleStr = "" | |
var zzExplainStr = "" | |
var ieBrowser = document.all | |
var zzMarked = null | |
var zzLinking = false | |
var zzUnLinking = false | |
var nSaves = 0 | |
var undefined // a variable whose value is undefined | |
function startUp() { | |
zzInitialise() | |
checkURLfrag() | |
reveal(true) | |
} | |
function reveal() { | |
zzClear() | |
zzSetDimensions() | |
zzRenderDimension(zzCurrentN, zzDimOne, zzDimTwo, 11, 11); | |
//zzRenderDimension(zzCurrentN, zzDimTwo, zzDimOne, 11, 11); | |
zzTour() | |
if (zzEditing < 1) zzNarrate() | |
} | |
function revealThis(n, x, y, d1, d2) { | |
var c | |
for (c = 0; zzDims[c] != null && zzDims[c] != d1; c++); | |
if (zzDims[c] != null) document.getElementById("dimSel1").selectedIndex = c; | |
for (c = 0; zzDims[c] != null && zzDims[c] != d2; c++); | |
if (zzDims[c] != null) document.getElementById("dimSel2").selectedIndex = c; | |
zzCurrentN = n | |
reveal() | |
} | |
function zzPressY() { | |
si = document.getElementById("dimSel1").selectedIndex | |
si++ | |
if (si >= document.getElementById("dimSel1").length) si = 0 | |
document.getElementById("dimSel1").selectedIndex = si | |
reveal(false); | |
} | |
function zzPressX() { | |
si = document.getElementById("dimSel2").selectedIndex | |
si++ | |
if (si >= document.getElementById("dimSel2").length) si = 0 | |
document.getElementById("dimSel2").selectedIndex = si | |
reveal(false); | |
} | |
function zzEval() { | |
var fs = zzContentsOf(zzCurrentN) | |
var fd = zzStepInDimension(zzCurrentN, "defn", 1) | |
var fn | |
if (fd > 0) { | |
fs = zzContentsOf(fd) | |
fn = new Function("n", "return " + fs) | |
} | |
else fn = new Function("n", "return " + fs + "(n)") | |
alert(fn(zzCurrentN)) | |
} | |
function zzEvaluate(n, fs) { | |
var fn = new Function("n", "return " + fs) | |
if (n > 0) return (fn(n)) | |
else return "" | |
} | |
function zzGoDown() { | |
var cl, m; | |
m = zzStepInDimension(zzCurrentN, zzDimOne, 1) | |
if (zzAdding) { | |
var newCell = zzMakeNewCellWithContents("Empty Cell"); | |
zzLinkCells(zzCurrentN, newCell, zzDimOne); | |
if (m > 0) zzLinkCells(newCell, m, zzDimOne); | |
m = newCell; | |
zzAdding = false; | |
} | |
if (zzUnLinking) { | |
zzUnLinkCells(m, zzCurrentN, zzDimOne); | |
m = zzCurrentN; | |
window.status = "" | |
} | |
if (zzLinking) { | |
zzLinkCells(zzMarked, zzCurrentN, zzDimOne); | |
m = zzCurrentN; | |
window.status = "" | |
} | |
if (zzDeleting) { | |
zzUnLinkCell(m, zzDimOne); | |
m = zzCurrentN; // normal semantics are other way round | |
window.status = "Deleted cell " + m | |
} | |
if (zzChugging) { | |
if (m < 1) return; | |
zzChugCells(zzCurrentN, m, zzDimOne); | |
m = zzCurrentN; // normal semantics are other way round | |
} | |
if (m > 0) { | |
zzCurrentN = m | |
reveal(true) | |
} | |
} | |
function zzGoUp() { | |
var m; | |
m = zzStepInDimension(zzCurrentN, zzDimOne, -1) | |
if (zzAdding) { | |
var newCell = zzMakeNewCellWithContents("Empty Cell"); | |
zzLinkCells(newCell, zzCurrentN, zzDimOne); | |
if (m > 0) zzLinkCells(m, newCell, zzDimOne); | |
m = newCell; | |
zzAdding = false; | |
} | |
if (zzLinking) { | |
zzLinkCells(zzCurrentN, zzMarked, zzDimOne); | |
m = zzCurrentN; | |
window.status = "" | |
} | |
if (zzUnLinking) { | |
zzUnLinkCells(zzCurrentN, m, zzDimOne); | |
m = zzCurrentN; | |
window.status = "" | |
} | |
if (zzDeleting) { | |
zzUnLinkCell(m, zzDimOne); | |
m = zzCurrentN; // normal semantics are other way round | |
window.status = "Deleted cell " + m | |
} | |
if (zzChugging) { | |
if (m < 1) return; | |
zzChugCells(m, zzCurrentN, zzDimOne); | |
m = zzCurrentN; // normal semantics are other way round | |
} | |
if (m > 0) { | |
zzCurrentN = m | |
reveal(true) | |
} | |
} | |
function zzGoLeft() { | |
var m; | |
m = zzStepInDimension(zzCurrentN, zzDimTwo, -1) | |
if (zzAdding) { | |
var newCell = zzMakeNewCellWithContents("Empty Cell"); | |
zzLinkCells(newCell, zzCurrentN, zzDimTwo); | |
if (m > 0) zzLinkCells(m, newCell, zzDimTwo); | |
m = newCell; | |
zzAdding = false; | |
} | |
if (zzLinking) { | |
zzLinkCells(zzMarked, zzCurrentN, zzDimTwo); | |
m = zzCurrentN; | |
window.status = "" | |
} | |
if (zzUnLinking) { | |
zzUnLinkCells(zzCurrentN, m, zzDimTwo); | |
m = zzCurrentN; | |
window.status = "" | |
} | |
if (zzDeleting) { | |
zzUnLinkCell(m, zzDimTwo); | |
m = zzCurrentN; // normal semantics are other way round | |
window.status = "Deleted cell " + m | |
} | |
if (zzChugging) { | |
if (m < 1) return; | |
zzChugCells(m, zzCurrentN, zzDimTwo); | |
m = zzCurrentN; // normal semantics are other way round | |
} | |
if (m > 0) { | |
zzCurrentN = m | |
reveal(true) | |
} | |
} | |
function zzGoRight() { | |
var m; | |
m = zzStepInDimension(zzCurrentN, zzDimTwo, 1) | |
if (zzAdding) { | |
var newCell = zzMakeNewCellWithContents("Empty Cell"); | |
zzLinkCells(zzCurrentN, newCell, zzDimTwo); | |
if (m > 0) zzLinkCells(newCell, m, zzDimTwo); | |
m = newCell; | |
zzAdding = false; | |
} | |
if (zzLinking) { | |
zzLinkCells(zzCurrentN, zzMarked, zzDimTwo); | |
window.status = "" | |
m = zzCurrentN; | |
} | |
if (zzUnLinking) { | |
zzUnLinkCells(m, zzCurrentN, zzDimTwo); | |
m = zzCurrentN; | |
window.status = "" | |
} | |
if (zzDeleting) { | |
zzUnLinkCell(m, zzDimTwo); | |
m = zzCurrentN; // normal semantics are other way round | |
window.status = "Deleted cell " + m | |
} | |
if (zzChugging) { | |
if (m < 1) return; | |
zzChugCells(zzCurrentN, m, zzDimTwo); | |
m = zzCurrentN; // normal semantics are other way round | |
} | |
if (m > 0) { | |
zzCurrentN = m | |
reveal(true) | |
} | |
} | |
function zzStepInDimension(n, d, direction) { | |
var dir | |
if (direction > 0) dir = zzPosWard; | |
else dir = zzNegWard; | |
var nnn = zzLinks[d]; | |
if (nnn == null || nnn == undefined) return 0; | |
var nn = zzLinks[d][dir]; | |
if (nn == null || nn == undefined) return 0 | |
nn = zzLinks[d][dir][n]; | |
if (nn > 1) return nn; | |
var i | |
for (i = 1; i < zzMaxItems && zzItems[i]; i++) | |
if (zzLinks[d][1 - dir][i] == n) return i; | |
return 0 | |
//clone stuff (currently unused) | |
if (nn == null || nn == undefined) { | |
var cl | |
if ((cl = zzGetCell(n).clone) > 0) nn = zzLinks[d][dir][cl]; | |
if (nn == null || nn == undefined) return 0 | |
else return nn | |
} | |
else return nn; | |
} | |
function zzAt(x, y, s) { | |
if (y <= 0 || y >= zzMaxY) return | |
if (x <= 0 || x >= zzMaxX) return | |
//var o=document.getElementById("r"+y*2+"c"+x*2) | |
//if(o.innerText!=" ")return | |
//o.innerHTML=s | |
var coords = "r" + y * 2 + "c" + x * 2 | |
setText(coords, s) | |
zzFilled[zzFilledN++] = coords | |
} | |
function zzBetween(x1, y1, x2, y2, s) { | |
var x = (x1 * 2 + x2 * 2) / 2 | |
var y = (y1 * 2 + y2 * 2) / 2 | |
if (y <= 0 || y >= zzMaxY * 2) return | |
if (x <= 0 || x >= zzMaxX * 2) return | |
//var o=document.all["r"+y+"c"+x] | |
//if(o.innerText!=" ")return | |
//o.innerHTML=s | |
var coords = "r" + y + "c" + x | |
setText(coords, s) | |
zzFilled[zzFilledN++] = coords | |
} | |
function zzClear() { | |
var a, b, o | |
for (a = 0; a < zzFilledN; a++) { | |
o = zzFilled[a] | |
//o.innerText=" " | |
setText(o, " "); | |
} | |
zzFilledN = 0 | |
//make sure that the table is going to receive the key presses, | |
//not the menus! | |
//zzTable.focus() | |
//it doesn't work! | |
} | |
function zzRenderDimension(n, d, d2, x, y) { | |
zzRenderDimensionAux(n, d, d2, x, y, 1) | |
zzRenderDimensionAux(n, d, d2, x, y, -1) | |
} | |
function zzRenderDimensionAux(n, d, d2, x, y, incr) { | |
var n2; | |
while (n > 0 && x >= 0 && y > 0 && (x <= zzMaxX && y <= zzMaxY || !horizonLimit)) { | |
zzAt(x, y, zzRenderCell(n)) | |
//used to check for n==zzCurrentN && ... | |
if ((!onlyTurnAtCurrent || n == zzCurrentN) && maxTurns >= 1 && d2 != null && d2.length > 0) { | |
//chase down the orthogonal dimension | |
zzRenderSecondaryDimensionAux(n, d2, d, x, y, 1) | |
zzRenderSecondaryDimensionAux(n, d2, d, x, y, -1) | |
} | |
n2 = zzStepInDimension(n, d, incr) | |
if (n2 != -99) { ///used to be !=0, but I want to have the arrows after the last one | |
var whichn; | |
//var glyph=unescape("%EA") | |
var glyph = "ê" | |
if (incr > 0) whichn = n; | |
else whichn = n2; | |
if (n2 == 0) glyph = "" | |
zzBetween(x, y, x, y + incr, "<CENTER>" + zzAvailableDimensions(whichn, -1, 1) + "<span title='" + d + ":" + n2 + "' style='text-weight: bold; font-family: Wingdings; color:" + zzColorOf(d) + "'>" + glyph + "</span>" + zzAvailableDimensions(whichn, 1, 1) + "</CENTER>"); | |
} | |
y = y + incr | |
n = n2 | |
} | |
} | |
function zzRenderSecondaryDimensionAux(n, d, d3, x, y, incr) { | |
//var glyph=unescape("%E8") | |
var glyph = "è" | |
n = zzStepInDimension(n, d, incr) | |
while (n > 0 && x >= 0 && y > 0 && (x <= zzMaxX && y <= zzMaxY || !horizonLimit)) { | |
zzBetween(x, y, x + incr, y, zzAvailableDimensions(n, -1, 2) + zzAvailableDimensions(n, 1, 2) + "<span style='font-family: Wingdings; font-weight: bold; color:" + zzColorOf(d) + "'>" + glyph + "</span>"); | |
x = x + incr; | |
zzAt(x, y, zzRenderCell(n)) | |
if (!onlyTurnAtCurrent && maxTurns >= 2) zzRenderTertiaryDimensionAux(n, d3, x, y, 1) | |
if (!onlyTurnAtCurrent && maxTurns >= 2) zzRenderTertiaryDimensionAux(n, d3, x, y, -1) | |
n = zzStepInDimension(n, d, incr) | |
} | |
} | |
function zzRenderTertiaryDimensionAux(n, d, x, y, incr) { | |
n = zzStepInDimension(n, d, incr) | |
while (n > 0 && x >= 0 && y > 0 && (x <= zzMaxX && y <= zzMaxY || !horizonLimit)) { | |
y = y + incr; | |
zzAt(x, y, zzRenderCell(n)) | |
n = zzStepInDimension(n, d, incr) | |
} | |
} | |
function zzRenderCell(n) { | |
var zzcell, res = ""; | |
var col = ""; | |
zzcell = zzGetCell(n) | |
if (zzcell == null) return | |
res = "" | |
//if(true)res+="("+n+")" | |
if (zzcell.clone) { | |
zzcell = zzGetCell(zzcell.clone); | |
col = " color: orange" | |
} | |
if (zzcell.title != "") res += "<b>" + zzcell.title + "</b> " | |
if (zzcell.eval != "") res += "<br>" + zzEvaluate(n, zzcell.eval) | |
if (zzcell.pic != "") { | |
if (res != "") res += "<br>"; | |
res += "<img width=100 src='" + zzcell.pic + "'>"; | |
} | |
if (zzContentsOf(n) != "") { | |
if (res != "") res += "<br>"; | |
if (zzcell.content.substring(0, 7) == "http://") res += "<a target='_new' href='" + zzcell.content + "'>" + zzcell.content + "</a>" | |
else if (zzcell.content.substring(0, 3) == "C:\\") res += "<a target='_new' href='" + zzcell.content + "'>" + zzcell.content + "</a>" | |
else if (zzcell.content.substring(0, 3) == "C:/") res += "<a target='_new' href='" + zzcell.content + "'>" + zzcell.content + "</a>" | |
else res += zzcell.content; | |
} | |
var ed = "" | |
if (zzEditing == n && n == zzCurrentN) ed = "contentEditable='true'" | |
if (n == zzCurrentN) { | |
if (zzcell.url != "") { | |
// if(zzExternalWindow!=zzcell.url)window.open(zzcell.url, "zzURL") | |
// zzExternalWindow=zzcell.url | |
res += "<a href='" + zzcell.url + "' target='_new'><iframe style='zoom:20%; border-width: 6; border-style: inset' width='100%' height='500' scrolling='no' src='" + zzcell.url + "'></iframe></a>" | |
} | |
} | |
var clik = ""; | |
if (n != zzCurrentN) clik = " onClick=revealThis(" + n + ",0,0,'" + zzDimOne + "','" + zzDimTwo + "')"; | |
var st | |
if (n != zzCurrentN) st = "border-width:0.5pt;" | |
else st = "border-width:2pt; border-color:red;" | |
return "<CENTER" + clik + " " + ed + " style='border:solid; " + st + col + "'>" + res + "</CENTER>" | |
} | |
function zzSummarise(n) { | |
var zzcell, res = "", | |
conN; | |
zzcell = zzGetCell(n) | |
if (zzcell == null) return "" | |
if (zzcell.title != null) return zzcell.title | |
conN = zzcell.content; | |
if (conN == null) return "" | |
res = conN | |
if (res.length <= 12) return res | |
return res.substring(0, 12) + "..." | |
} | |
function zzContentsOf(n) { | |
var zzcell, conN; | |
zzcell = zzGetCell(n) | |
if (zzcell == null) return "" | |
if (zzcell.clone) return zzContentsOf(zzcell.clone); | |
conN = zzcell.content; | |
if (conN == null) return "" | |
return conN | |
} | |
function zzNarrativeContentsOf(n) { | |
var zzcell, conN, titN, res; | |
zzcell = zzGetCell(n) | |
if (zzcell == null) return "" | |
conN = zzcell.content; | |
titN = zzcell.title; | |
res = "" | |
if (titN != "") { | |
res = titN | |
} | |
else { | |
if (conN != null) res += conN | |
} | |
return "<span id=narr" + n + " contentEditable='true' onMouseEnter=zzNarrativeMouse('enter',this," + n + ") onMouseLeave=zzNarrativeMouse('leave',this," + n + ") onClick=zzNarrativeMouse('click',this," + n + ")>" + res + "</span>" | |
} | |
function zzGetCell(n) { | |
return zzItems[n] | |
} | |
function zzSetDimensions() { | |
zzDimOne = zzDims[document.getElementById("dimSel1").selectedIndex] | |
zzDimTwo = zzDims[document.getElementById("dimSel2").selectedIndex] | |
} | |
function zzInitialise() { | |
var nOpt | |
for (i = 0; i <= zzTopDim; i++) { | |
nOpt = document.createElement("OPTION") | |
nOpt.text = zzDimDescs[i] | |
nOpt.value = i | |
if (ieBrowser) dimSel1.add(nOpt); | |
else document.getElementById("dimSel1").appendChild(nOpt) | |
nOpt = document.createElement("OPTION") | |
nOpt.text = zzDimDescs[i] | |
nOpt.value = i | |
if (ieBrowser) dimSel2.add(nOpt); | |
else document.getElementById("dimSel2").appendChild(nOpt) | |
} | |
document.getElementById("dimSel1").selectedIndex = 0 | |
zzDimOne = zzDims[0] | |
document.getElementById("dimSel2").selectedIndex = 1 | |
zzDimTwo = zzDims[1] | |
//title | |
setText("zzTitle", zzTitleStr) | |
setText("zzVersion", zzVersion) | |
//explanation | |
if (zzExplainStr) setText("zzExplain", zzExplainStr) | |
else setText("zzExplain", "Use the arrow keys and menus (right) to zigzag through the demo, alternatively use the x,y, s, f, e, c keys like the Perl implementation.") | |
} | |
function zzSaveEdit(n) { | |
var oldText = zzContentsOf(n) | |
//replace next with the correct narrative element | |
var narrCell = document.getElementById["narr" + n] | |
var newText = null; | |
if (narrCell) newText = narrCell.innerText; | |
else newText = document.getElementById("r22c22").innerText; | |
//can't tell whether the editing took place in the narrative area or the cell area | |
//if(oldText==newText)newText=document.getElementById("r11c11").innerText; | |
if (oldText == newText) { | |
window.status = "No changes made to cell " + n + " '" + oldText + "'"; | |
alert("No changes made to cell " + n); | |
return; | |
} | |
else { | |
window.status = "Cell " + n + " changed from '" + oldText + "' to '" + newText + "'"; | |
} | |
var cell = zzGetCell(n) | |
if (cell.clone) zzItems[cell.clone].content = newText; | |
else cell.content = newText; | |
if (zzLinks["version"]) { | |
//create a new cell with the old contents | |
var nc = zzMakeNewCellWithContents(oldText); | |
var pv = zzStepInDimension(n, "version", -1) | |
zzLinks["version"][zzNegWard][n] = nc | |
zzLinks["version"][zzPosWard][nc] = n | |
if (pv > 1) zzLinks["version"][zzNegWard][nc] = pv | |
} | |
} | |
function zzMakeNewDim() { | |
var newDescr = prompt("Description of new dimension: ", "") | |
var newName = prompt("Id of dimension (one word):", "") | |
var newColor = prompt("Color to use for dimension: ", "black") | |
zzNewDim(newName, newDescr, newColor) | |
} | |
document.onkeypress = function keyHandler(e) { | |
var k; | |
if (e) k = e.which; | |
else k = window.event.keyCode | |
var ae = document.activeElement; | |
if (ae == null || ae == document.getElementById("zzTitle")) return; | |
if (zzEditing > 0) { | |
if (ieBrowser && k == 27) zzStopEditing(); | |
return; | |
} | |
else if (k == 81 /* Q */ ) { | |
if (!ieBrowser) { | |
var newText = prompt("Cell contents:", "") | |
var cell = zzGetCell(zzCurrentN) | |
if (cell.clone) zzItems[cell.clone].content = newText; | |
else cell.content = newText; | |
reveal() | |
} | |
else zzStartEditing(zzCurrentN); | |
return; | |
} | |
if (k == 33 /* ! */ ) { | |
exportToXML(); | |
return; | |
} | |
if (k == 90 /* Z */ ) { | |
dumpXML(); | |
return; | |
} | |
if (k == 87 /* W */ ) { | |
writeJS(); | |
return; | |
} | |
if (k == 68 /* D */ ) { | |
zzMakeNewDim(); | |
return; | |
} | |
if (k == 120 || k == 88 /* X/x */ ) zzPressX() | |
if (k == 121 || k == 89 /* Y/y */ ) zzPressY() | |
if (k == 69 || k == 73 || k == 101 || k == 105 /* EeIi */ ) zzGoUp() | |
if (k == 67 || k == 99 || k == 44 || k == 60 /* Cc lt, */ ) zzGoDown() | |
if (k == 115 || k == 83 || k == 106 || k == 74 /* SsKk */ ) zzGoLeft() | |
if (k == 102 || k == 70 || k == 108 || k == 76 /* Ff:; */ ) zzGoRight() | |
if (k == 65 || k == 97 /* E/e */ ) zzEval() | |
if (k == 109 || k == 77 /* M/m */ ) { | |
zzMarked = zzCurrentN; | |
window.status = "Marked " + zzMarked; | |
} | |
if (k == 84 /* T */ ) { // transpose | |
var di1 = document.getElementById("dimSel1").selectedIndex | |
var di2 = document.getElementById("dimSel2").selectedIndex | |
document.getElementById("dimSel1").selectedIndex = di2 | |
document.getElementById("dimSel2").selectedIndex = di1 | |
reveal(false); | |
} | |
if (k == 116 /* t */ ) { // topple (?) back to standard dimensions | |
document.getElementById("dimSel1").selectedIndex = 0 | |
document.getElementById("dimSel2").selectedIndex = 1 | |
reveal(false); | |
} | |
if (k == 71 /* g */ ) { | |
zzGoto(); | |
}; | |
if (k == 94 /* ^ */ ) { | |
zzExtremeOfRank(zzDimOne, -1); | |
}; | |
if (k == 118 /* v */ ) { | |
zzExtremeOfRank(zzDimOne, 1); | |
}; | |
if (k == 91 /* [ */ ) { | |
zzExtremeOfRank(zzDimTwo, -1); | |
} | |
if (k == 93 /* ] */ ) { | |
zzExtremeOfRank(zzDimTwo, 1); | |
} | |
if (k == 45 /* - */ ) zzLinking = true; | |
else zzLinking = false; | |
if (k == 79 || k == 111 /* O/o */ ) zzCloneCell(zzCurrentN, zzMarked); | |
if (k == 117 /* u */ ) zzUnLinking = true; | |
else zzUnLinking = false; | |
if (k == 110 || k == 78 /* N/n */ ) zzAdding = true; | |
else zzAdding = false; | |
if (k == 35 /* # */ ) zzDeleting = true; | |
else zzDeleting = false; | |
if (k == 126 /* ~ */ ) zzChugging = true; | |
else zzChugging = false; | |
if (k == 72 /* H */ ) { | |
revealThis(1, 0, 0, zzDims[0], zzDims[1]); | |
} | |
if (k == 63 /* ? */ ) { | |
zzSaveHTML(zzTitleStr, zzScavenge()) | |
} | |
if (k == 64 /* @ */ ) { | |
var d = "email" | |
//d=prompt("Scavenge for which dimension?",zzDimOne); | |
window.open("mailto:" + zzScavenge2(d)) | |
} | |
} | |
function zzAvailableDimensions0() { | |
var availDims = "" | |
var c = 0 | |
for (c = 0; zzDims[c] != null; c++) { | |
var d = zzDims[c] | |
if (d != zzDimOne && d != zzDimTwo) { | |
if (zzStepInDimension(zzCurrentN, d, 1) || zzStepInDimension(zzCurrentN, d, -1)) availDims += d + " " | |
} | |
} | |
return availDims | |
} | |
function zzAvailableDimensions1(n) { | |
var availDims = "" | |
var c = 0 | |
for (c = 0; zzDims[c] != null; c++) { | |
var d = zzDims[c] | |
if (d != "narrative" && d != "version") { | |
if (zzStepInDimension(n, d, 1) || zzStepInDimension(n, d, -1)) availDims += d + " " | |
} | |
} | |
return availDims | |
} | |
function zzAvailableDimensions(n, delta, depth) { | |
var availDims = "", | |
nextDim = "", | |
summary = "" | |
var glyph | |
//if(delta<0)glyph=unescape("%ED") | |
//else glyph=unescape("%EE") | |
if (delta < 0) glyph = "í" | |
else glyph = "î" | |
var c = 0, | |
n2 = 0, | |
dq = '"' | |
for (c = 0; zzDims[c] != null; c++) { | |
var d = zzDims[c] | |
if ((depth == 99) || (d != zzDimOne && d != zzDimTwo)) { | |
if ((n2 = zzStepInDimension(n, d, delta)) > 0) { | |
var d1, d2 | |
if (depth == 1) { | |
d1 = zzDimOne; | |
d2 = d; | |
} | |
else { | |
d1 = d; | |
d2 = zzDimTwo; | |
} | |
summary = "(" + n + ") " + zzDimDescs[c] + "->" + zzSummarise(n2) | |
nextDim = "<a href='javascript:revealThis(" + n + ",2,3," + dq + d1 + dq + "," + dq + d2 + dq + ")' onMouseEnter='status=" + dq + summary + dq + "' title='" + summary + "' style='text-decoration: none; color: " + zzColorOf(d) + "; font-family: Wingdings; text-weight: bold;'><small><sup>" + glyph + "</sup></small></a>" | |
if (delta < 0) availDims = nextDim + availDims | |
else availDims += nextDim | |
nextDim = "" | |
} | |
} | |
} | |
return availDims | |
} | |
function zzTour() { | |
if (zzTourN < 1) return | |
var ts = zzTourSteps[zzTourN]; | |
if (ts == null) { | |
zzTourN = 0 | |
return | |
} | |
//ok, so are the conditions for this tour step fulfilled? | |
if (ts.n != zzCurrentN) return; | |
if (ts.down != zzDimOne) return; | |
if (ts.across != zzDimTwo) return; | |
var t = "" | |
t += ts.comment | |
t += "<br><b>" + ts.doit + "</b>"; | |
setText("zzExplain", t) | |
zzTourN++ | |
} | |
function doNowt() {} | |
function zzColorOf(d) { | |
var dc = 0 | |
while (zzDims[dc] != null && zzDims[dc] != d) | |
dc++ | |
if (zzDims[dc] == null) return "black" | |
else return zzDimCols[dc]; | |
} | |
function plus(a, b) { | |
return a + b | |
} | |
function minus(a, b) { | |
return a - b | |
} | |
function times(a, b) { | |
return a * b | |
} | |
function divide(a, b) { | |
return a / b | |
} | |
function accumulate(initial, fn, n, direction, step) { | |
var res = initial; | |
n = zzStepInDimension(n, direction, step) | |
while (n > 0) { | |
res = fn(res, (1.0 * zzContentsOf(n))) | |
n = zzStepInDimension(n, direction, step) | |
} | |
return res | |
} | |
function zzNarrate() { | |
if (document.getElementById("narrative") == undefined) return | |
var nlast = zzCurrentN | |
var n = zzStepInDimension(zzCurrentN, "narrative", -1) | |
if (n <= 0) { | |
n = zzStepInDimension(zzCurrentN, "narrative", 1) | |
if (n <= 0) { | |
setText("narrative", ""); | |
return | |
} | |
//back up to the beginning of the narrative | |
n = zzCurrentN | |
} | |
else { | |
//back up to the beginning of the narrative | |
while (n > 0) { | |
nlast = n | |
n = zzStepInDimension(n, "narrative", -1) | |
} | |
} | |
//build up the narrative | |
var narr = "" | |
var punct = "!.,;:?" | |
n = nlast; | |
while (n > 0) { | |
var narScrap = zzNarrativeContentsOf(n) | |
var lastChar = narScrap.charAt(narScrap.length - 1) | |
var pos = narScrap.indexOf('>') | |
if (punct.indexOf(narScrap.charAt(pos + 1)) >= 0) narr = narr.substring(0, narr.length - 1) | |
narr += narScrap + " " | |
n = zzStepInDimension(n, "narrative", 1) | |
} | |
setText("narrative", narr + "<hr>") | |
} | |
function zzNarrativeMouse(op, obj, n) { | |
if (op == "click") { | |
//revealThis(n,0,0,zzDimOne,zzDimTwo); | |
zzStartEditing(n); | |
} | |
if (zzAvailableDimensions1(n) == "") return | |
if (op == "enter") obj.style.backgroundColor = 'gray' | |
else if (op == "leave") obj.style.backgroundColor = 'white' | |
} | |
function getText(node, patt) { | |
var nd = node.selectSingleNode(patt); | |
if (nd == null) return "" | |
else return nd.text; | |
} | |
function zzMakeNewCell(basis) { | |
var c | |
for (c = 1; c < zzMaxItems; c++) | |
if (zzItems[c] == null) break; | |
if (c == zzMaxItems) return -1; | |
var o = new Object(); | |
o.pic = "" | |
o.title = "" | |
o.eval = "" | |
o.url = "" | |
o.content = "" | |
if (basis > 0) { | |
o.content = zzItems[basis].content; | |
} | |
zzItems[c] = o; | |
return c; | |
} | |
function zzMakeNewCellWithContents(str) { | |
var c = zzMakeNewCell(0); | |
zzItems[c].content = str; | |
return c; | |
} | |
function zzStartEditing(n) { | |
if (n == zzEditing) return; | |
if (n > 0 && zzEditing > 0) zzSaveEdit(zzEditing); | |
zzEditing = n; | |
reveal(); | |
if (false) { | |
// this can't work in Netscape | |
// and has no effect in Explorer! | |
//whatever I do I can't get the keypresses | |
//diverted | |
var r = document.body.createTextRange(); | |
r.moveToElementText(document.getElementById("r22c22")); | |
r.select(); | |
document.getElementById("r22c22").focus(); | |
} | |
window.status = "Editing cell " + n | |
} | |
function zzStopEditing() { | |
if (zzEditing < 1) return; | |
zzSaveEdit(zzEditing); | |
zzEditing = 0; | |
reveal(); | |
} | |
function zzUnLinkCells(a, b, d) { | |
zzLinks[d][zzNegWard][a] = null; | |
zzLinks[d][zzPosWard][b] = null; | |
} | |
function zzBlatFromDimension(d, a) { | |
var i | |
for (i = 1; i < zzMaxItems && zzItems[i]; i++) { | |
if (zzLinks[d][zzNegWard][i] == a) { | |
zzLinks[d][zzNegWard][i] = null; | |
} | |
} | |
for (i = 1; i < zzMaxItems && zzItems[i]; i++) { | |
if (zzLinks[d][zzPosWard][i] == a) { | |
zzLinks[d][zzPosWard][i] = null; | |
} | |
} | |
} | |
function zzLinkCells(a, b, d) { | |
//a and b can't be linked to anything else in d | |
zzBlatFromDimension(d, a); | |
zzBlatFromDimension(d, b); | |
zzLinks[d][zzPosWard][a] = b; | |
zzLinks[d][zzNegWard][b] = a; | |
} | |
function zzCloneCell(a, b) { | |
//a and b can't be linked to anything else in d | |
var zzcell, zzcell2 | |
zzcell = zzGetCell(a) | |
if (zzcell == null) return | |
zzcell2 = zzGetCell(b) | |
if (zzcell2 == null) return | |
zzcell.clone = b; | |
reveal() | |
} | |
function zzNewDim(name, desc, col) { | |
if (zzTopDim == zzMaxDims) { | |
alert("Too many dimensions to create dimension '" + name + "'"); | |
return; | |
} | |
zzTopDim++; | |
zzLinks[name] = new Array(2) | |
zzLinks[name][zzNegWard] = new Array(zzMaxItems) | |
zzLinks[name][zzPosWard] = new Array(zzMaxItems) | |
if (col == "") zzDimCols[zzTopDim] = "black" | |
else zzDimCols[zzTopDim] = col | |
var descr = desc | |
if (descr == "") descr = name | |
zzDims[zzTopDim] = name | |
zzDimDescs[zzTopDim] = descr | |
var nOpt = document.createElement("OPTION") | |
nOpt.text = descr | |
nOpt.value = zzTopDim | |
if (ieBrowser) dimSel1.add(nOpt); | |
else document.getElementById("dimSel1").appendChild(nOpt) | |
var nOpt2 = document.createElement("OPTION") | |
nOpt2.text = descr | |
nOpt2.value = zzTopDim | |
if (ieBrowser) dimSel2.add(nOpt2); | |
else document.getElementById("dimSel2").appendChild(nOpt2) | |
} | |
function setText(l, v) { | |
var e = document.getElementById(l) | |
if (!e) return; | |
if (ieBrowser) { | |
e.innerHTML = v; | |
} | |
else { | |
e.innerHTML = v | |
//sih(l,v) | |
//if(e.firstChild)e=e.firstChild | |
//e.nodeValue=v | |
} | |
} | |
function getText(l) { | |
var e = document.getElementById(l) | |
if (!e) return; | |
if (ieBrowser) { | |
return e.innerText; | |
} | |
else { | |
return e.innerText; | |
//sih(l,v) | |
//if(e.firstChild)e=e.firstChild | |
//e.nodeValue=v | |
} | |
} | |
function sih(id, str) { | |
var o = document.getElementById(id) | |
var r = o.ownerDocument.createRange(); | |
r.selectNodeContents(o); | |
r.deleteContents(); | |
var df = r.createContextualFragment(str); | |
o.appendChild(df); | |
} | |
function exportToXML() { | |
if (ieBrowser) newSaveData(zzTitleStr, convertToXML(), "xml") | |
else oldSaveData(zzTitleStr, convertToXML()) | |
} | |
function writeJS() { | |
if (ieBrowser) newSaveData(zzTitleStr, convertToJS(), "html") | |
else oldSaveData(zzTitleStr, convertToJS()) | |
} | |
function dumpXML() { | |
oldSaveData(zzTitleStr, convertToXML()) | |
} | |
function newSaveData(t, d, suffix) { | |
nSaves++ | |
var s = document.location.href; | |
var i = s.lastIndexOf('/'); | |
if (i < 0) { | |
alert("Can't find document name"); | |
return; | |
} | |
var j = s.lastIndexOf('.'); | |
if (nSaves == 1) alert("Saving to your disk overrides browser security.\nPlease click 'Yes' on the next alert box!"); | |
var fso = new ActiveXObject("Scripting.FileSystemObject"); | |
var a = fso.CreateTextFile(s.substring(i + 1, j) + "." + suffix, true); | |
a.Write(d); | |
a.Close(); | |
if (nSaves > 1) alert("Saved file on your Desktop"); | |
} | |
function oldSaveData(t, d) { | |
w = window.open("", "_new"); | |
w.document.write("<H1>XML Zigzag Data: " + t + "</H1>\n") | |
w.document.write("<FORM method='POST' action='http://ernie.ecs.soton.ac.uk/~lac/zzWrite.cgi' >") | |
w.document.write("<textarea name='xmldata' rows='20' cols='80'>" + canon(d) + "</textarea>") | |
w.document.write("<p>The above is the raw XML that constitutes the ZigZag data. Either cut and paste it into a text editor and save it, or click on 'Save on Web'.\n"); | |
w.document.write("<input type=submit value='Save on Web'/>\n"); | |
w.document.write("<hr>\n<i>Zigzag for XML was written by Leslie Carr, IAM Research, University of Southampton</i>\n"); | |
w.document.write("For more information, see the <a href='http://www.ecs.soton.ac.uk/~lac/zigzag/'>Zigzag for XML page</a>."); | |
w.document.close(); | |
} | |
function convertToXML() { | |
var n | |
s = "<!DOCTYPE zigzag SYSTEM 'orthoplex.dtd'>\n" | |
s += "<?xml-stylesheet type='text/xsl' href='orthoplex.xsl'?>" | |
s += "<zigzag><title>" + getText("zzTitle") + "</title>\n" | |
//s+="<explain>"+getText("zzExplain")+"</explain>\n" | |
if (zzTourSteps[1]) { | |
s += "<tour>\n" | |
n = 1 | |
while (zzTourSteps[n]) { | |
var ts = zzTourSteps[n] | |
s += "<step n='" + ts.n + "' across='" + ts.across + "' down='" + ts.down + "'>\n" | |
s += "<comment>" + ts.comment + "</comment>\n" | |
s += "<doit>" + ts.doit + "</doit>\n" | |
s += "</step>\n\n" | |
n++ | |
} | |
s += "</tour>\n\n" | |
} | |
n = 0 | |
s += "<dimensions>\n" | |
while (zzDims[n]) { | |
s += "<dimension name='" + zzDims[n] + "' description='" + zzDimDescs[n] + "'" | |
if (zzDimCols[n]) s += " color='" + zzDimCols[n] + "'" | |
s += "/>\n" | |
n++ | |
} | |
s += "</dimensions>\n\n" | |
n = 1 | |
s += "<cells>\n" | |
while (zzItems[n]) { | |
var it = zzItems[n] | |
s += "<cell n='" + n + "'>\n" | |
if (it.title) s += "<title>" + it.title + "</title>\n" | |
if (it.clone) s += "<content clone='" + it.clone + "'/>\n" | |
else s += "<content>" + it.content + "</content>\n" | |
if (it.pic) s += "<pic src='" + it.pic + "'/>\n" | |
if (it.url) s += "<url>" + it.url + "</url>\n" | |
if (it.eval) s += "<eval>" + it.eval + "</eval>\n" | |
var d = 0 | |
while (zzDims[d]) { | |
var dim = zzDims[d] | |
var pos = "", | |
neg = "" | |
if (zzLinks[dim][zzPosWard][n] > 0) pos = " posward='" + zzLinks[dim][zzPosWard][n] + "'" | |
if (zzLinks[dim][zzNegWard][n] > 0) neg = " negward='" + zzLinks[dim][zzNegWard][n] + "'" | |
if (pos != "" || neg != "") s += "<link direction='" + dim + "'" + pos + neg + "/>" | |
d++ | |
} | |
s += "</cell>\n" | |
n++ | |
} | |
s += "</cells>\n" | |
s += "</zigzag>" | |
return s | |
} | |
function convertToJS() { | |
var n = 1 | |
var d = 0 | |
var s = "" | |
s = "<HTML>\n<HEAD>\n" | |
s += "<TITLE>" + getText("zzTitle") + "</TITLE>\n" | |
s += '<SCRIPT SRC="orthoplex.js" language="JavaScript"></SCRIPT><SCRIPT language="JavaScript">\n' | |
var i = 0 | |
for (d = 0; d <= zzTopDim; d++) { | |
var dn = zzDims[d] | |
s += "zzDims[" + d + "]='" + dn + "'\n" | |
s += "zzDimCols[" + d + "]='" + zzDimCols[d] + "'\n" | |
s += "zzDimDescs[" + d + "]='" + zzDimDescs[d] + "'\n" | |
s += "zzLinks['" + dn + "']=new Array(2)\n" | |
s += "zzLinks['" + dn + "'][zzNegWard]=new Array(zzMaxItems)\n" | |
s += "zzLinks['" + dn + "'][zzPosWard]=new Array(zzMaxItems)\n" | |
} | |
s += "var n=0\n"; | |
while (zzItems[n]) { | |
var it = zzItems[n] | |
s += "o=new ZZCell();\n" | |
if (it.title) s += "o.title='" + it.title + "'\n" | |
s += "o.content='" + it.content + "'\n" | |
if (it.pic) s += "o.pic='" + it.pic + "'\n" | |
if (it.url) s += "o.url='" + it.url + "'\n" | |
if (it.eval) s += "o.eval='" + it.eval + "'\n" | |
if (it.clone) s += "o.clone=" + it.clone + "\n" | |
s += "zzItems[" + n + "]=o;\n" | |
d = 0 | |
while (zzDims[d]) { | |
var dim = zzDims[d] | |
if (zzLinks[dim][zzPosWard][n] > 0) s += "zzLinks['" + dim + "'][zzPosWard][" + n + "]=" + zzLinks[dim][zzPosWard][n] + "\n" | |
if (zzLinks[dim][zzNegWard][n] > 0) s += "zzLinks['" + dim + "'][zzNegWard][" + n + "]=" + zzLinks[dim][zzNegWard][n] + "\n" | |
d++ | |
} | |
s += "n++\n" | |
n++ | |
} | |
s += "zzTopDim=" + (d - 1) + "\n" | |
s += "zzTitleStr='" + getText("zzTitle") + "'\n" | |
s += "zzExplainStr=''\n" | |
s += '</SCRIPT>\n</HEAD>\n<BODY onLoad="startUp()">\n<table valign="top" border="0">\n<tr>\n<td rowspan="3">\n<H1>\n<span contentEditable="true" id="zzTitle">ZigZag Demo</span>\n</H1>\n<span id="zzExplain">Xse the arrow keys and menus (right) to zigzag through the demo, alternatively use the x,y, s, f, e, c keys like the Perl implementation.</span></td><td></td><td align="center"><input type="button" onclick="zzGoUp()" value="^"></td><td></td><td rowspan="3">\nDown: <select id="dimSel1" onChange="reveal(false)"></select>\n<br>\nAcross: <select id="dimSel2" onChange="reveal(false)"></select>\n<br>\nYou Can Turn in These Directions: <i><span id="zzOtherDirections"></span></i></td>\n</tr>\n<tr>\n<td><input type="button" onclick="zzGoLeft()" value="<"></td><td></td><td><input type="button" onclick="zzGoRight()" value=">"></td>\n</tr>\n<tr>\n<td></td><td align="center"><input type="button" onclick="zzGoDown()" value="v"></td><td></td>\n</tr>\n</table>\n<hr>\n<span id="celltext"></span>\n' | |
var t = '<table border="0">\n' | |
for (i = 1; i <= 40; i++) { | |
t += "<tr>\n" | |
for (j = 1; j <= 40; j++) | |
t += '<td><span id="r' + i + 'c' + j + '"></span></td>' | |
t += "\n</tr>\n" | |
} | |
t += '</table>\n' | |
s += t | |
s += '<hr>\n<i>Zigzag for XML was written by Leslie Carr, IAM Research, University of Southampton</i>\n<br>\nFor more information, see the <a href="http://www.ecs.soton.ac.uk/~lac/zigzag/">Zigzag for XML page</a>.<br>\n</BODY>\n</HTML>\n' | |
return s | |
} | |
function canon(s) { | |
var t = "" | |
var i | |
for (i = 0; i < s.length; i++) { | |
var ch = s.charAt(i) | |
if (ch == "<") t += "<"; | |
else if (ch == ">") t += ">"; | |
else if (ch == "&") t += "&"; | |
else t += ch | |
} | |
return t | |
} | |
function checkURLfrag() { | |
var s = document.location.href; | |
var i = s.indexOf('#'); | |
if (i > 0) { | |
var frag = s.substring(i + 1, 999) | |
zzCurrentN = frag * 1 | |
} | |
} | |
function zzUnLinkCell(n, d) { | |
var pre = zzLinks[d][zzNegWard][n] | |
var post = zzLinks[d][zzPosWard][n] | |
//alert("PRE="+zzGetCell(pre).content) | |
//alert("POST="+zzGetCell(post).content) | |
zzLinks[d][zzPosWard][n] = null; | |
zzLinks[d][zzNegWard][n] = null; | |
zzLinks[d][zzPosWard][pre] = post; | |
zzLinks[d][zzNegWard][post] = pre; | |
} | |
function zzChugCells(n, n2, d) { | |
var pre = zzLinks[d][zzNegWard][n] | |
var post = zzLinks[d][zzPosWard][n2] | |
zzLinks[d][zzPosWard][n] = post; | |
zzLinks[d][zzNegWard][n] = n2; | |
zzLinks[d][zzPosWard][n2] = n; | |
zzLinks[d][zzNegWard][n2] = pre; | |
zzLinks[d][zzPosWard][pre] = n2; | |
zzLinks[d][zzNegWard][post] = n; | |
} | |
function zzGoto() { | |
var c = prompt("Go to which cell?", zzCurrentN) | |
if (c > 1) { | |
zzCurrentN = c | |
reveal(); | |
} | |
} | |
function zzScavenge() { | |
return hhh(zzDimOne, zzDimTwo, zzCurrentN, func, 1); | |
} | |
function hhh(d1, d2, n, fn, level) { | |
var res = "", | |
nn | |
while (n > 0) { | |
res += fn(zzScavengeCell(n), level) | |
if ((nn = zzStepInDimension(n, d1, 1)) > 0) res += hhh(d2, d1, nn, fn, level + 1) | |
n = zzStepInDimension(n, d2, 1) | |
} | |
return res | |
} | |
function zzScavengeCell(n) { | |
if (n > 0) return zzGetCell(n).content | |
else return "" | |
} | |
function zzIdentityFn(s) { | |
return s; | |
} | |
var funcll = 0; | |
function func(s, level) { | |
var res | |
if (level == 1) { | |
if (funcll == 0) res = "<H2>" + s + "</H2>" | |
else { | |
var ll = level | |
res = "" | |
while (ll < funcll) { | |
res += "</UL>" | |
ll++ | |
} | |
res += "<H2>" + s + "</H2>" | |
} | |
} | |
else { | |
if (level > funcll) res = "<UL><LI>" + s | |
else if (level < funcll) { | |
var ll = level | |
res = "" | |
while (ll < funcll) { | |
res += "</UL>" | |
ll++ | |
} | |
res += "<LI>" + s | |
} | |
else res = "<LI>" + s | |
} | |
funcll = level | |
return res + "\n" | |
} | |
function ggg(d, d1, d2, n) { | |
var res = "", | |
nn | |
while (n > 0) { | |
res += zzScavenge2Cell(n, d) | |
if ((nn = zzStepInDimension(n, d1, 1)) > 0) res += ggg(d, d2, d1, nn) | |
n = zzStepInDimension(n, d2, 1) | |
} | |
return res | |
} | |
function zzScavenge2(d) { | |
return ggg(d, zzDimOne, zzDimTwo, zzCurrentN); | |
} | |
function zzScavenge2Cell(n, d) { | |
var n2; | |
var res = "" | |
n2 = zzStepInDimension(n, d, 1); | |
if (n2 > 0) return zzGetCell(n2).content + "; " | |
else return "" | |
} | |
function zzSaveHTML(tit, doc) { | |
nSaves++ | |
if (nSaves == 1) alert("Saving to your disk overrides browser security.\nPlease click 'Yes' on the next alert box!"); | |
var fso = new ActiveXObject("Scripting.FileSystemObject"); | |
var a = fso.CreateTextFile("zigzagOut.html"); | |
a.Write("<TITLE>" + tit + "</TITLE>\n<H1><hr>Zigzag Slice: " + tit + "<hr></H1>"); | |
a.Write(doc); | |
a.Close(); | |
if (nSaves > 1) alert("Saved zigzagOut.html in your Desktop"); | |
} | |
function zzFind() {} | |
function zzExtremeOfRank(d, delta) { | |
var n, nextn | |
for (nextn = zzCurrentN; nextn > 0; nextn = zzStepInDimension(nextn, d, delta)) | |
n = nextn; | |
zzCurrentN = n; | |
reveal() | |
} | |
// Define ZZCell class constructor | |
function ZZCell() { | |
this.content = ""; | |
this.pic = ""; | |
this.url = ""; | |
this.title = ""; | |
this.eval = ""; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment