App-SocialCalc-Multiplayer
view release on metacpan or search on metacpan
socialcalc/socialcalctableeditor.js view on Meta::CPAN
// Initially coded by Dan Bricklin of Software Garden, Inc., for Socialtext, Inc.
// Based in part on the SocialCalc 1.1.0 code written in Perl.
// The SocialCalc 1.1.0 code was:
// Portions (c) Copyright 2005, 2006, 2007 Software Garden, Inc.
// All Rights Reserved.
// Portions (c) Copyright 2007 Socialtext, Inc.
// All Rights Reserved.
// The Perl SocialCalc started as modifications to the wikiCalc(R) program, version 1.0.
// wikiCalc 1.0 was written by Software Garden, Inc.
// Unless otherwise specified, referring to "SocialCalc" in comments refers to this
// JavaScript version of the code, not the SocialCalc Perl code.
//
/*
See the comments in the main SocialCalc code module file of the SocialCalc package.
*/
var SocialCalc;
if (!SocialCalc) { // created here, too, in case load order is wrong, but main routines are required
SocialCalc = {};
}
// *************************************
//
// Table Editor class:
//
// *************************************
// Constructor:
SocialCalc.TableEditor = function(context) {
var scc = SocialCalc.Constants;
// Properties:
this.context = context; // editing context
this.toplevel = null; // top level HTML element for this table editor
this.fullgrid = null; // rendered editing context
this.noEdit = false; // if true, disable all edit UI and make read-only
this.width = null;
this.tablewidth = null;
this.height = null;
this.tableheight = null;
this.inputBox = null;
this.inputEcho = null;
this.verticaltablecontrol = null;
this.horizontaltablecontrol = null;
this.logo = null;
this.cellhandles = null;
// Dynamic properties:
this.timeout = null; // if non-null, timer id for position calculations
this.busy = false; // true when executing command, calculating, etc.
this.ensureecell = false; // if true, ensure ecell is visible after timeout
this.deferredCommands = []; // commands to execute after busy, in form: {cmdstr: "cmds", saveundo: t/f}
this.gridposition = null; // screen coords of full grid
this.headposition = null; // screen coords of upper left of grid within header rows
this.firstscrollingrow = null; // row number of top row in last (the scrolling) pane
this.firstscrollingrowtop = null; // position of top row in last (the scrolling) pane
this.lastnonscrollingrow = null; // row number of last displayed row in last non-scrolling
// pane, or zero (for thumb position calculations)
this.lastvisiblerow = null; // used for paging down
this.firstscrollingcol = null; // column number of top col in last (the scrolling) pane
this.firstscrollingcolleft = null; // position of top col in last (the scrolling) pane
this.lastnonscrollingcol = null; // col number of last displayed column in last non-scrolling
// pane, or zero (for thumb position calculations)
this.lastvisiblecol = null; // used for paging right
this.rowpositions = []; // screen positions of the top of some rows
this.colpositions = []; // screen positions of the left side of some rows
this.rowheight = []; // size in pixels of each row when last checked, or null/undefined, for page up
this.colwidth = []; // size in pixels of each column when last checked, or null/undefined, for page left
this.ecell = null; // either null or {coord: c, row: r, col: c}
this.state = "start"; // the keyboard states: see EditorProcessKey
this.workingvalues = {}; // values used during keyboard editing, etc.
// Constants:
this.imageprefix = scc.defaultImagePrefix; // URL prefix for images (e.g., "/images/sc")
this.idPrefix = scc.defaultTableEditorIDPrefix;
this.pageUpDnAmount = scc.defaultPageUpDnAmount; // number of rows to move cursor on PgUp/PgDn keys (numeric)
// Callbacks
// recalcFunction: if present, function(editor) {...}, called to do a recalc
// Default (sheet.RecalcSheet) does all the right stuff.
this.recalcFunction = function(editor) {
if (editor.context.sheetobj.RecalcSheet) {
editor.context.sheetobj.RecalcSheet(SocialCalc.EditorSheetStatusCallback, editor);
}
else return null;
};
// ctrlkeyFunction: if present, function(editor, charname) {...}, called to handle ctrl-V, etc., at top level
// Returns true (pass through for continued processing) or false (stop processing this key).
this.ctrlkeyFunction = function(editor, charname) {
var ta, ha, cell, position, cmd, sel, cliptext;
switch (charname) {
case "[ctrl-c]":
case "[ctrl-x]":
ta = editor.pasteTextarea;
ta.value = "";
cell=SocialCalc.GetEditorCellElement(editor, editor.ecell.row, editor.ecell.col);
if (cell) {
position = SocialCalc.GetElementPosition(cell.element);
socialcalc/socialcalctableeditor.js view on Meta::CPAN
return;
case "calcstart":
editor.busy = true;
break;
case "calccheckdone":
case "calcorder":
case "calcstep":
case "calcloading":
case "calcserverfunc":
break;
case "calcfinished":
signalstatus(status);
editor.ScheduleRender();
return;
case "schedrender":
editor.busy = true; // in case got here without cmd or recalc
break;
case "renderdone":
break;
case "schedposcalc":
editor.busy = true; // in case got here without cmd or recalc
break;
case "doneposcalc":
if (editor.deferredCommands.length) {
signalstatus(status);
dcmd = editor.deferredCommands.shift();
editor.EditorScheduleSheetCommands(dcmd.cmdstr, dcmd.saveundo, true);
}
else {
editor.busy = false;
signalstatus(status);
if (editor.state=="start") editor.DisplayCellContents(); // make sure up to date
}
return;
default:
addmsg("Unknown status: "+status);
break;
}
signalstatus(status);
return;
}
// Timer-driven steps for use with SocialCalc.EditorSheetStatusCallback
SocialCalc.EditorStepInfo = {
// status: "", // saved value to pass to callback
editor: null // for callback
// arg: null, // for callback
// timerobj: null
};
/*
SocialCalc.EditorStepSet = function(editor, status, arg) {
var esi = SocialCalc.EditorStepInfo;
addmsg("step: "+status);
if (esi.timerobj) {
alert("Already waiting. Old/new: "+esi.status+"/"+status);
}
esi.editor = editor;
esi.status = status;
esi.timerobj = window.setTimeout(SocialCalc.EditorStepDone, 1);
}
SocialCalc.EditorStepDone = function() {
var esi = SocialCalc.EditorStepInfo;
esi.timerobj = null;
SocialCalc.EditorSheetStatusCallback(null, esi.status, null, esi.editor);
}
*/
//
// str = SocialCalc.EditorGetStatuslineString(editor, status, arg, params)
//
// Assumes params is an object where it can use "calculating" and "command"
// to keep track of state.
// Returns string for status line.
//
SocialCalc.EditorGetStatuslineString = function(editor, status, arg, params) {
var scc = SocialCalc.Constants;
var sstr, progress, coord, circ, r, c, cell, sum, ele;
progress = "";
switch (status) {
case "moveecell":
case "rangechange":
case "startup":
break;
case "cmdstart":
params.command = true;
document.body.style.cursor = "progress";
editor.griddiv.style.cursor = "progress";
progress = scc.s_statusline_executing;
break;
case "cmdextension":
progress = "Command Extension: "+arg;
break;
case "cmdend":
params.command = false;
break;
case "schedrender":
progress = scc.s_statusline_displaying;
break;
case "renderdone":
progress = " ";
break;
case "schedposcalc":
progress = scc.s_statusline_displaying;
break;
case "cmdendnorender":
case "doneposcalc":
document.body.style.cursor = "default";
editor.griddiv.style.cursor = "default";
break;
case "calcorder":
progress = scc.s_statusline_ordering+Math.floor(100*arg.count/(arg.total||1))+"%";
break;
case "calcstep":
progress = scc.s_statusline_calculating+Math.floor(100*arg.count/(arg.total||1))+"%";
break;
case "calcloading":
progress = scc.s_statusline_calculatingls+": "+arg.sheetname;
break;
socialcalc/socialcalctableeditor.js view on Meta::CPAN
var editor = mouseinfo.editor;
if (!editor) return; // not us, ignore
element = mouseinfo.element;
var viewport = SocialCalc.GetViewportInfo();
var clientX = event.clientX + viewport.horizontalScroll;
if (event.stopPropagation) event.stopPropagation(); // DOM Level 2
else event.cancelBubble = true; // IE 5+
if (event.preventDefault) event.preventDefault(); // DOM Level 2
else event.returnValue = false; // IE 5+
if (document.removeEventListener) { // DOM Level 2
document.removeEventListener("mousemove", SocialCalc.ProcessEditorColsizeMouseMove, true);
document.removeEventListener("mouseup", SocialCalc.ProcessEditorColsizeMouseUp, true);
}
else if (editor.toplevel.detachEvent) { // IE
editor.toplevel.detachEvent("onlosecapture", SocialCalc.ProcessEditorColsizeMouseUp);
editor.toplevel.detachEvent("onmouseup", SocialCalc.ProcessEditorColsizeMouseUp);
editor.toplevel.detachEvent("onmousemove", SocialCalc.ProcessEditorColsizeMouseMove);
editor.toplevel.releaseCapture();
}
var newsize = (editor.context.colwidth[mouseinfo.mouseresizecolnum]-0) + (clientX - mouseinfo.mousedownclientx);
if (newsize < SocialCalc.Constants.defaultMinimumColWidth) newsize = SocialCalc.Constants.defaultMinimumColWidth;
editor.EditorScheduleSheetCommands("set "+mouseinfo.mouseresizecol+" width "+newsize, true, false);
if (editor.timeout) window.clearTimeout(editor.timeout);
editor.timeout = window.setTimeout(SocialCalc.FinishColsize, 1); // wait - Firefox 2 has a bug otherwise with next mousedown
return false;
}
SocialCalc.FinishColsize = function() {
var mouseinfo = SocialCalc.EditorMouseInfo;
var editor = mouseinfo.editor;
if (!editor) return;
editor.toplevel.removeChild(mouseinfo.mouseresizedisplay);
mouseinfo.mouseresizedisplay = null;
// editor.FitToEditTable();
// editor.EditorRenderSheet();
// editor.SchedulePositionCalculations();
mouseinfo.editor = null;
return;
}
//
// Handle auto-repeat of dragging the cursor into the borders of the sheet
//
SocialCalc.AutoRepeatInfo = {
timer: null, // timer object for repeating
mouseinfo: null, // result from SocialCalc.GridMousePosition
repeatinterval: 1000, // milliseconds to wait between repeats
editor: null, // editor object to use when it repeats
repeatcallback: null // used instead of default when repeating (e.g., for cellhandles)
// called as: repeatcallback(newcoord, direction)
};
// Control auto-repeat. If mouseinfo==null, cancel.
SocialCalc.SetDragAutoRepeat = function(editor, mouseinfo, callback) {
var repeatinfo = SocialCalc.AutoRepeatInfo;
var coord, direction;
repeatinfo.repeatcallback = callback; // null in regular case
if (!mouseinfo) { // cancel
if (repeatinfo.timer) { // If was repeating, stop
window.clearTimeout(repeatinfo.timer); // cancel timer
repeatinfo.timer = null;
}
repeatinfo.mouseinfo = null;
return; // done
}
repeatinfo.editor = editor;
if (repeatinfo.mouseinfo) { // check for change while repeating
if (mouseinfo.rowheader || mouseinfo.rowfooter) {
if (mouseinfo.row != repeatinfo.mouseinfo.row) { // changed row while dragging sidewards
coord = SocialCalc.crToCoord(editor.ecell.col, mouseinfo.row); // change to it
if (repeatinfo.repeatcallback) {
if (mouseinfo.row < repeatinfo.mouseinfo.row) {
direction = "left";
}
else if (mouseinfo.row > repeatinfo.mouseinfo.row) {
direction = "right";
}
else {
direction = "";
}
repeatinfo.repeatcallback(coord, direction);
}
else {
editor.MoveECell(coord);
editor.MoveECell(coord);
editor.RangeExtend();
editor.EditorMouseRange(coord);
}
}
}
else if (mouseinfo.colheader || mouseinfo.colfooter) {
if (mouseinfo.col != repeatinfo.mouseinfo.col) { // changed col while dragging vertically
coord = SocialCalc.crToCoord(mouseinfo.col, editor.ecell.row); // change to it
if (repeatinfo.repeatcallback) {
if (mouseinfo.row < repeatinfo.mouseinfo.row) {
direction = "left";
}
else if (mouseinfo.row > repeatinfo.mouseinfo.row) {
direction = "right";
}
else {
direction = "";
}
repeatinfo.repeatcallback(coord, direction);
}
else {
editor.MoveECell(coord);
editor.RangeExtend();
editor.EditorMouseRange(coord);
}
}
}
}
repeatinfo.mouseinfo = mouseinfo;
if (mouseinfo.distance < 5) repeatinfo.repeatinterval = 333;
else if (mouseinfo.distance < 10) repeatinfo.repeatinterval = 250;
else if (mouseinfo.distance < 25) repeatinfo.repeatinterval = 100;
else if (mouseinfo.distance < 35) repeatinfo.repeatinterval = 75;
else { // too far - stop repeating
if (repeatinfo.timer) { // if repeating, cancel it
window.clearTimeout(repeatinfo.timer); // cancel timer
repeatinfo.timer = null;
}
return;
}
if (!repeatinfo.timer) { // start if not already running
repeatinfo.timer = window.setTimeout(SocialCalc.DragAutoRepeat, repeatinfo.repeatinterval);
}
return;
}
//
// DragAutoRepeat()
//
SocialCalc.DragAutoRepeat = function() {
var repeatinfo = SocialCalc.AutoRepeatInfo;
var mouseinfo = repeatinfo.mouseinfo;
var direction, coord, cr;
if (mouseinfo.rowheader) direction = "left";
else if (mouseinfo.rowfooter) direction = "right";
else if (mouseinfo.colheader) direction = "up";
else if (mouseinfo.colfooter) direction = "down";
if (repeatinfo.repeatcallback) {
cr = SocialCalc.coordToCr(repeatinfo.editor.ecell.coord);
if (direction == "left" && cr.col > 1) cr.col--;
else if (direction == "right") cr.col++;
else if (direction == "up" && cr.row > 1) cr.row--;
else if (direction == "down") cr.row++;
coord = SocialCalc.crToCoord(cr.col, cr.row);
repeatinfo.repeatcallback(coord, direction);
}
else {
coord = repeatinfo.editor.MoveECellWithKey("[a"+direction+"]shifted");
if (coord) repeatinfo.editor.EditorMouseRange(coord);
}
repeatinfo.timer = window.setTimeout(SocialCalc.DragAutoRepeat, repeatinfo.repeatinterval);
}
//
// Handling Clicking
//
SocialCalc.ProcessEditorDblClick = function(e) {
var editor, result, coord, textarea, wval, range;
var event = e || window.event;
var viewport = SocialCalc.GetViewportInfo();
var clientX = event.clientX + viewport.horizontalScroll;
var clientY = event.clientY + viewport.verticalScroll;
var mouseinfo = SocialCalc.EditorMouseInfo;
var ele = event.target || event.srcElement; // source object is often within what we want
var mobj;
if (mouseinfo.ignore) return; // ignore this
for (mobj=null; !mobj && ele; ele=ele.parentNode) { // go up tree looking for one of our elements
mobj = SocialCalc.LookupElement(ele, mouseinfo.registeredElements);
}
if (!mobj) {
mouseinfo.editor = null;
return; // not one of our elements
}
editor = mobj.editor;
result = SocialCalc.GridMousePosition(editor, clientX, clientY);
if (!result || !result.coord) return; // not within cell area - ignore
mouseinfo.editor = editor; // remember for later
mouseinfo.element = ele;
range = editor.range;
sheetobj = editor.context.sheetobj;
switch (editor.state) {
case "start":
SocialCalc.EditorOpenCellEdit(editor);
break;
case "input":
break;
default:
break;
}
if (event.stopPropagation) event.stopPropagation(); // DOM Level 2
else event.cancelBubble = true; // IE 5+
if (event.preventDefault) event.preventDefault(); // DOM Level 2
else event.returnValue = false; // IE 5+
return;
socialcalc/socialcalctableeditor.js view on Meta::CPAN
editor.state = "input";
var wval = editor.workingvalues;
wval.partialexpr = "";
wval.ecoord = editor.ecell.coord;
wval.erow = editor.ecell.row;
wval.ecol = editor.ecell.col;
};
//
// SocialCalc.InputBoxOnMouseDown(e)
//
// This is called when the input box gets the focus. It then responds to keystrokes
// and pass them off to SocialCalc.ProcessKey, but in a different editing state.
//
SocialCalc.InputBoxOnMouseDown = function(e) {
var editor = SocialCalc.Keyboard.focusTable; // get TableEditor doing keyboard stuff
if (!editor) return true; // we're not handling it -- let browser do default
var wval = editor.workingvalues;
switch (editor.state) {
case "start":
editor.state="inputboxdirect";
wval.partialexpr = "";
wval.ecoord = editor.ecell.coord;
wval.erow = editor.ecell.row;
wval.ecol = editor.ecell.col;
editor.inputEcho.ShowInputEcho(true);
break;
case "input":
wval.partialexpr = ""; // make sure not pointing
editor.MoveECell(wval.ecoord);
editor.state="inputboxdirect";
SocialCalc.KeyboardFocus(); // may have come here from outside of grid
break;
case "inputboxdirect":
break;
}
}
// *************************************
//
// InputEcho class:
//
// This object creates and controls an element that echos what's in the InputBox during editing
// It is draggable.
//
// *************************************
SocialCalc.InputEcho = function(editor) {
var scc = SocialCalc.Constants;
this.editor = editor; // the TableEditor this belongs to
this.text = ""; // current value of what is displayed
this.interval = null; // timer handle
this.container = null; // element containing main echo as well as prompt line
this.main = null; // main echo area
this.prompt = null;
this.functionbox = null; // function chooser dialog
this.container = document.createElement("div");
SocialCalc.setStyles(this.container, "display:none;position:absolute;zIndex:10;");
this.main = document.createElement("div");
if (scc.defaultInputEchoClass) this.main.className = scc.defaultInputEchoClass;
if (scc.defaultInputEchoStyle) SocialCalc.setStyles(this.main, scc.defaultInputEchoStyle);
this.main.innerHTML = " ";
this.container.appendChild(this.main);
this.prompt = document.createElement("div");
if (scc.defaultInputEchoPromptClass) this.prompt.className = scc.defaultInputEchoPromptClass;
if (scc.defaultInputEchoPromptStyle) SocialCalc.setStyles(this.prompt, scc.defaultInputEchoPromptStyle);
this.prompt.innerHTML = "";
this.container.appendChild(this.prompt);
SocialCalc.DragRegister(this.main, true, true, {MouseDown: SocialCalc.DragFunctionStart, MouseMove: SocialCalc.DragFunctionPosition,
MouseUp: SocialCalc.DragFunctionPosition,
Disabled: null, positionobj: this.container});
editor.toplevel.appendChild(this.container);
}
// Methods:
SocialCalc.InputEcho.prototype.ShowInputEcho = function(show) {return SocialCalc.ShowInputEcho(this, show);};
SocialCalc.InputEcho.prototype.SetText = function(str) {return SocialCalc.SetInputEchoText(this, str);};
// Functions:
SocialCalc.ShowInputEcho = function(inputecho, show) {
var cell, position;
var editor = inputecho.editor;
if (!editor) return;
if (show) {
editor.cellhandles.ShowCellHandles(false);
cell=SocialCalc.GetEditorCellElement(editor, editor.ecell.row, editor.ecell.col);
if (cell) {
position = SocialCalc.GetElementPosition(cell.element);
inputecho.container.style.left = (position.left-1)+"px";
inputecho.container.style.top = (position.top-1)+"px";
}
inputecho.container.style.display = "block";
if (inputecho.interval) window.clearInterval(inputecho.interval); // just in case
inputecho.interval = window.setInterval(SocialCalc.InputEchoHeartbeat, 50);
}
else {
if (inputecho.interval) window.clearInterval(inputecho.interval);
socialcalc/socialcalctableeditor.js view on Meta::CPAN
break;
}
if (editor.colpositions[col+1]-30<editor.headposition.left) {
break;
}
cellhandles.draghandle.style.left = (editor.colpositions[col+1]-1)+"px";
cellhandles.draghandle.style.top = (editor.rowpositions[row+1]-1)+"px";
cellhandles.draghandle.style.display = "block";
if (moveshow) {
cellhandles.draghandle.style.display = "none";
cellhandles.dragpalette.style.left = (editor.colpositions[col+1]-45)+"px";
cellhandles.dragpalette.style.top = (editor.rowpositions[row+1]-45)+"px";
cellhandles.dragpalette.style.display = "block";
viewport = SocialCalc.GetViewportInfo();
cellhandles.dragtooltip.style.right = (viewport.width-(editor.colpositions[col+1]-1))+"px";
cellhandles.dragtooltip.style.bottom = (viewport.height-(editor.rowpositions[row+1]-1))+"px";
cellhandles.dragtooltip.style.display = "none";
}
doshow = true;
}
while (false); // only do once
if (!doshow) {
cellhandles.draghandle.style.display = "none";
}
if (!moveshow) {
cellhandles.dragpalette.style.display = "none";
cellhandles.dragtooltip.style.display = "none";
}
}
SocialCalc.CellHandlesMouseMoveOnHandle = function(e) {
var scc = SocialCalc.Constants;
var event = e || window.event;
var target = event.target || event.srcElement
var viewport = SocialCalc.GetViewportInfo();
var clientX = event.clientX + viewport.horizontalScroll;
var clientY = event.clientY + viewport.verticalScroll;
editor = SocialCalc.Keyboard.focusTable; // get TableEditor doing keyboard stuff
if (!editor) return true; // we're not handling it -- let browser do default
var cellhandles = editor.cellhandles;
if (!cellhandles.editor) return true; // no handles
if (!editor.cellhandles.mouseDown) {
editor.cellhandles.ShowCellHandles(true, true); // show move handles, too
if (target == cellhandles.dragpalette) {
var whichhandle = SocialCalc.SegmentDivHit([scc.CH_radius1, scc.CH_radius2], editor.cellhandles.dragpalette, clientX, clientY);
if (whichhandle==0) { // off of active part of palette
SocialCalc.CellHandlesHoverTimeout();
return;
}
if (cellhandles.tooltipstimer) {
window.clearTimeout(cellhandles.tooltipstimer);
cellhandles.tooltipstimer = null;
}
cellhandles.tooltipswhichhandle = whichhandle;
cellhandles.tooltipstimer = window.setTimeout(SocialCalc.CellHandlesTooltipsTimeout, 700);
}
if (cellhandles.timer) {
window.clearTimeout(cellhandles.timer);
cellhandles.timer = null;
}
cellhandles.timer = window.setTimeout(SocialCalc.CellHandlesHoverTimeout, 3000);
}
return;
}
//
// whichsegment = SocialCalc.SegmentDivHit(segtable, divWithMouseHit, x, y)
//
// Takes segtable = [upperleft quadrant, upperright, bottomright, bottomleft]
// where each quadrant is either:
// 0 = ignore hits here
// number = return this value
// array = a new segtable for this subquadrant
//
// Alternatively, segtable can be:
// [radius 1, radius 2] and it returns 0 if no hit,
// -1, -2, -3, -4 for inner quadrants, and +1...+4 for outer quadrants
//
SocialCalc.SegmentDivHit = function(segtable, divWithMouseHit, x, y) {
var width = divWithMouseHit.offsetWidth;
var height = divWithMouseHit.offsetHeight;
var left = divWithMouseHit.offsetLeft;
var top = divWithMouseHit.offsetTop;
var v = 0;
var table = segtable;
var len = Math.sqrt(Math.pow(x-left-(width/2.0-.5), 2)+Math.pow(y-top-(height/2.0-.5), 2));
if (table.length==2) { // type 2 segtable
if (x >= left && x < left+width/2 && y >= top && y < top+height/2) { // upper left
if (len <= segtable[0]) v = -1;
else if (len <= segtable[1]) v = 1;
}
if (x >= left+width/2 && x < left+width && y >= top && y < top+height/2) { // upper right
if (len <= segtable[0]) v = -2;
else if (len <= segtable[1]) v = 2;
}
if (x >= left+width/2 && x < left+width && y >= top+height/2 && y < top+height) { // bottom right
if (len <= segtable[0]) v = -3;
else if (len <= segtable[1]) v = 3;
}
if (x >= left && x < left+width/2 && y >= top+height/2 && y < top+height) { // bottom right
if (len <= segtable[0]) v = -4;
else if (len <= segtable[1]) v = 4;
}
return v;
}
while (true) {
if (x >= left && x < left+width/2 && y >= top && y < top+height/2) { // upper left
quadrant += "1";
v = table[0];
if (typeof v == "number") {
break;
}
table = v;
width = width/2;
height = height/2;
continue;
}
if (x >= left+width/2 && x < left+width && y >= top && y < top+height/2) { // upper right
quadrant += "2";
v = table[1];
if (typeof v == "number") {
break;
}
table = v;
width = width/2;
left = left+width;
height = height/2;
continue;
}
if (x >= left+width/2 && x < left+width && y >= top+height/2 && y < top+height) { // bottom right
quadrant += "3";
v = table[2];
if (typeof v == "number") {
break;
}
table = v;
width = width/2;
left = left + width;
height = height/2;
top = top + height;
continue;
}
if (x >= left && x < left+width/2 && y >= top+height/2 && y < top+height) { // bottom right
quadrant += "4";
v = table[3];
if (typeof v == "number") {
break;
}
table = v;
width = width/2;
height = height/2;
top = top + height;
continue;
}
return 0; // didn't match
}
//addmsg((x-divWithMouseHit.offsetLeft)+","+(y-divWithMouseHit.offsetTop)+"="+quadrant+" "+v);
return v;
}
SocialCalc.CellHandlesHoverTimeout = function() {
editor = SocialCalc.Keyboard.focusTable; // get TableEditor doing keyboard stuff
if (!editor) return true; // we're not handling it -- let browser do default
var cellhandles = editor.cellhandles;
if (cellhandles.timer) {
window.clearTimeout(cellhandles.timer);
cellhandles.timer = null;
}
if (cellhandles.tooltipstimer) {
window.clearTimeout(cellhandles.tooltipstimer);
cellhandles.tooltipstimer = null;
}
editor.cellhandles.ShowCellHandles(true, false); // hide move handles
}
SocialCalc.CellHandlesTooltipsTimeout = function() {
editor = SocialCalc.Keyboard.focusTable; // get TableEditor doing keyboard stuff
if (!editor) return true; // we're not handling it -- let browser do default
var cellhandles = editor.cellhandles;
if (cellhandles.tooltipstimer) {
window.clearTimeout(cellhandles.tooltipstimer);
cellhandles.tooltipstimer = null;
}
var whichhandle = cellhandles.tooltipswhichhandle;
if (whichhandle==0) { // off of active part of palette
SocialCalc.CellHandlesHoverTimeout();
return;
}
if (whichhandle==-3) {
cellhandles.dragtooltip.innerHTML = scc.s_CHfillAllTooltip;
}
else if (whichhandle==3) {
cellhandles.dragtooltip.innerHTML = scc.s_CHfillContentsTooltip;
}
else if (whichhandle==-2) {
cellhandles.dragtooltip.innerHTML = scc.s_CHmovePasteAllTooltip;
}
else if (whichhandle==-4) {
cellhandles.dragtooltip.innerHTML = scc.s_CHmoveInsertAllTooltip;
}
else if (whichhandle==2) {
cellhandles.dragtooltip.innerHTML = scc.s_CHmovePasteContentsTooltip;
}
else if (whichhandle==4) {
cellhandles.dragtooltip.innerHTML = scc.s_CHmoveInsertContentsTooltip;
}
else {
cellhandles.dragtooltip.innerHTML = " ";
cellhandles.dragtooltip.style.display = "none";
return;
}
cellhandles.dragtooltip.style.display = "block";
}
SocialCalc.CellHandlesMouseDown = function(e) {
var scc = SocialCalc.Constants;
var editor, result, coord, textarea, wval, range;
var event = e || window.event;
var viewport = SocialCalc.GetViewportInfo();
var clientX = event.clientX + viewport.horizontalScroll;
var clientY = event.clientY + viewport.verticalScroll;
var mouseinfo = SocialCalc.EditorMouseInfo;
editor = SocialCalc.Keyboard.focusTable; // get TableEditor doing keyboard stuff
if (!editor) return true; // we're not handling it -- let browser do default
if (editor.busy) return; // don't do anything when busy (is this correct?)
var cellhandles = editor.cellhandles;
cellhandles.movedmouse = false; // detect no-op
if (cellhandles.timer) { // cancel timer
window.clearTimeout(cellhandles.timer);
cellhandles.timer = null;
}
if (cellhandles.tooltipstimer) {
window.clearTimeout(cellhandles.tooltipstimer);
cellhandles.tooltipstimer = null;
}
cellhandles.dragtooltip.innerHTML = " ";
cellhandles.dragtooltip.style.display = "none";
range = editor.range;
var whichhandle = SocialCalc.SegmentDivHit([scc.CH_radius1, scc.CH_radius2], editor.cellhandles.dragpalette, clientX, clientY);
if (whichhandle==1 || whichhandle==-1 || whichhandle==0) {
cellhandles.ShowCellHandles(true, false); // hide move handles
return;
}
mouseinfo.ignore = true; // stop other code from looking at the mouse
if (whichhandle==-3) {
cellhandles.dragtype = "Fill";
// mouseinfo.element = editor.cellhandles.fillhandle;
cellhandles.noCursorSuffix = false;
}
else if (whichhandle==3) {
cellhandles.dragtype = "FillC";
// mouseinfo.element = editor.cellhandles.fillhandle;
cellhandles.noCursorSuffix = false;
}
else if (whichhandle==-2) {
cellhandles.dragtype = "Move";
// mouseinfo.element = editor.cellhandles.movehandle1;
cellhandles.noCursorSuffix = true;
}
else if (whichhandle==-4) {
cellhandles.dragtype = "MoveI";
// mouseinfo.element = editor.cellhandles.movehandle2;
cellhandles.noCursorSuffix = false;
}
else if (whichhandle==2) {
cellhandles.dragtype = "MoveC";
// mouseinfo.element = editor.cellhandles.movehandle1;
cellhandles.noCursorSuffix = true;
}
else if (whichhandle==4) {
cellhandles.dragtype = "MoveIC";
// mouseinfo.element = editor.cellhandles.movehandle2;
cellhandles.noCursorSuffix = false;
}
cellhandles.filltype = null;
switch (cellhandles.dragtype) {
case "Fill":
case "FillC":
if (!range.hasrange) {
editor.RangeAnchor();
}
break;
case "Move":
case "MoveI":
case "MoveC":
case "MoveIC":
if (!range.hasrange) {
socialcalc/socialcalctableeditor.js view on Meta::CPAN
draginfo.draggingElement = null;
return false;
}
//
// DragFunctionStart(event, draginfo, dobj)
//
SocialCalc.DragFunctionStart = function(event, draginfo, dobj) {
var val;
var element = dobj.functionobj.positionobj || dobj.element;
val = element.style.top.match(/\d*/);
draginfo.offsetY = (val ? val[0]-0 : 0) - draginfo.clientY;
val = element.style.left.match(/\d*/);
draginfo.offsetX = (val ? val[0]-0 : 0) - draginfo.clientX;
}
//
// DragFunctionPosition(event, draginfo, dobj)
//
SocialCalc.DragFunctionPosition = function(event, draginfo, dobj) {
var element = dobj.functionobj.positionobj || dobj.element;
if (dobj.vertical) element.style.top = (draginfo.clientY + draginfo.offsetY)+"px";
if (dobj.horizontal) element.style.left = (draginfo.clientX + draginfo.offsetX)+"px";
}
// *************************************
//
// Tooltip functions:
//
// *************************************
SocialCalc.TooltipInfo = {
// There is only one of these -- no "new" is done.
// Only one tooltip operation can be active at a time.
// The registeredElements array is used to identify items.
// One item for each element with a tooltip, each an object with:
// .element, .tiptext, .functionobj
// Currently .functionobj can only contain .offsetx and .offsety.
// If present they are used instead of the default ones.
registeredElements: [],
registered: false, // if true, an event handler has been registered for this functionality
// Items used during hover over an element
tooltipElement: null, // item being processed (.element is the actual element)
timer: null, // timer object waiting to see if holding over element
popupElement: null, // tooltip element being displayed
clientX: 0, // modifyable version to restrict movement
clientY: 0,
offsetX: SocialCalc.Constants.TooltipOffsetX, // modifyable version to allow positioning
offsetY: SocialCalc.Constants.TooltipOffsetY
}
//
// TooltipRegister(element, tiptext, functionobj) - make element have a tooltip
//
SocialCalc.TooltipRegister = function(element, tiptext, functionobj) {
var tooltipinfo = SocialCalc.TooltipInfo;
tooltipinfo.registeredElements.push(
{element: element, tiptext: tiptext, functionobj: functionobj}
);
if (tooltipinfo.registered) return; // only need to add event listener once
if (document.addEventListener) { // DOM Level 2 -- Firefox, et al
document.addEventListener("mousemove", SocialCalc.TooltipMouseMove, false);
}
else if (document.attachEvent) { // IE 5+
document.attachEvent("onmousemove", SocialCalc.TooltipMouseMove);
}
else { // don't handle this
throw SocialCalc.Constants.s_BrowserNotSupported;
}
tooltipinfo.registered = true; // remember
return;
}
//
// TooltipMouseMove(event)
//
SocialCalc.TooltipMouseMove = function(event) {
var e = event || window.event;
var tooltipinfo = SocialCalc.TooltipInfo;
tooltipinfo.viewport = SocialCalc.GetViewportInfo();
tooltipinfo.clientX = e.clientX + tooltipinfo.viewport.horizontalScroll;
tooltipinfo.clientY = e.clientY + tooltipinfo.viewport.verticalScroll;
var tobj = SocialCalc.LookupElement(e.target || e.srcElement, tooltipinfo.registeredElements);
if (tooltipinfo.timer) { // waiting to see if holding still: didn't hold still
window.clearTimeout(tooltipinfo.timer); // cancel timer
tooltipinfo.timer = null;
}
if (tooltipinfo.popupElement) { // currently displaying a tip: hide it
SocialCalc.TooltipHide();
}
tooltipinfo.tooltipElement = tobj || null;
if (!tobj || SocialCalc.ButtonInfo.buttonDown) return; // if not an object with a tip or a "button" is down, ignore
tooltipinfo.timer = window.setTimeout(SocialCalc.TooltipWaitDone, 700);
if (tooltipinfo.tooltipElement.element.addEventListener) { // Register event for mouse down which cancels tooltip stuff
tooltipinfo.tooltipElement.element.addEventListener("mousedown", SocialCalc.TooltipMouseDown, false);
}
else if (tooltipinfo.tooltipElement.element.attachEvent) { // IE
tooltipinfo.tooltipElement.element.attachEvent("onmousedown", SocialCalc.TooltipMouseDown);
}
return;
}
//
// TooltipMouseDown(event)
//
SocialCalc.TooltipMouseDown = function(event) {
var e = event || window.event;
var tooltipinfo = SocialCalc.TooltipInfo;
if (tooltipinfo.timer) {
window.clearTimeout(tooltipinfo.timer); // cancel timer
tooltipinfo.timer = null;
}
if (tooltipinfo.popupElement) { // currently displaying a tip: hide it
SocialCalc.TooltipHide();
}
if (tooltipinfo.tooltipElement) {
if (tooltipinfo.tooltipElement.element.removeEventListener) { // DOM Level 2 -- Firefox, et al
tooltipinfo.tooltipElement.element.removeEventListener("mousedown", SocialCalc.TooltipMouseDown, false);
}
else if (tooltipinfo.tooltipElement.element.attachEvent) { // IE 5+
tooltipinfo.tooltipElement.element.detachEvent("onmousedown", SocialCalc.TooltipMouseDown);
}
tooltipinfo.tooltipElement = null;
}
return;
}
//
// TooltipDisplay(tobj)
//
SocialCalc.TooltipDisplay = function(tobj) {
var tooltipinfo = SocialCalc.TooltipInfo;
var scc = SocialCalc.Constants;
var offsetX = (tobj.functionobj && ((typeof tobj.functionobj.offsetx) == "number")) ? tobj.functionobj.offsetx : tooltipinfo.offsetX;
var offsetY = (tobj.functionobj && ((typeof tobj.functionobj.offsety) == "number")) ? tobj.functionobj.offsety : tooltipinfo.offsetY;
tooltipinfo.popupElement = document.createElement("div");
if (scc.TDpopupElementClass) tooltipinfo.popupElement.className = scc.TDpopupElementClass;
SocialCalc.setStyles(tooltipinfo.popupElement, scc.TDpopupElementStyle);
tooltipinfo.popupElement.innerHTML = tobj.tiptext;
if (tooltipinfo.clientX > tooltipinfo.viewport.width/2) { // on right side of screen
tooltipinfo.popupElement.style.bottom = (tooltipinfo.viewport.height - tooltipinfo.clientY + offsetY)+"px";
tooltipinfo.popupElement.style.right = (tooltipinfo.viewport.width - tooltipinfo.clientX + offsetX)+"px";
}
else { // on left side of screen
tooltipinfo.popupElement.style.bottom = (tooltipinfo.viewport.height - tooltipinfo.clientY + offsetY)+"px";
tooltipinfo.popupElement.style.left = (tooltipinfo.clientX + offsetX)+"px";
}
if (tooltipinfo.clientY < 50) { // make sure fits on screen if nothing above grid
tooltipinfo.popupElement.style.bottom = (tooltipinfo.viewport.height - tooltipinfo.clientY + offsetY - 50)+"px";
}
document.body.appendChild(tooltipinfo.popupElement);
}
//
// TooltipHide()
//
SocialCalc.TooltipHide = function() {
var tooltipinfo = SocialCalc.TooltipInfo;
if (tooltipinfo.popupElement) {
tooltipinfo.popupElement.parentNode.removeChild(tooltipinfo.popupElement);
tooltipinfo.popupElement = null;
}
}
//
// TooltipWaitDone()
//
SocialCalc.TooltipWaitDone = function() {
var tooltipinfo = SocialCalc.TooltipInfo;
tooltipinfo.timer = null;
SocialCalc.TooltipDisplay(tooltipinfo.tooltipElement);
}
// *************************************
//
// Button functions:
//
// *************************************
SocialCalc.ButtonInfo = {
// There is only one of these -- no "new" is done.
// Only one button operation can be active at a time.
// The registeredElements array is used to identify items.
// One item for each clickable element, each an object with:
// .element, .normalstyle, .hoverstyle, .downstyle, .repeatinterval, .functionobj
//
// .functionobj is an object with optional function objects for:
// mouseover, mouseout, mousedown, repeatinterval, mouseup, disabled
registeredElements: [],
// Items used during hover over an element, clicking, repeating, etc.
buttonElement: null, // item being processed, hover or down (.element is the actual element)
doingHover: false, // true if mouse is over one of our elements
buttonDown: false, // true if button down and buttonElement not null
timer: null, // timer object for repeating
// Used while processing an event
horizontalScroll: 0,
verticalScroll: 0,
clientX: 0,
clientY: 0
}
//
// ButtonRegister(element, paramobj, functionobj) - make element clickable
//
// The arguments (other than element) may be null (meaning no change for style and no repeat)
// The paramobj has the optional normalstyle, hoverstyle, downstyle, repeatwait, repeatinterval settings
SocialCalc.ButtonRegister = function(element, paramobj, functionobj) {
var buttoninfo = SocialCalc.ButtonInfo;
if (!paramobj) paramobj = {};
buttoninfo.registeredElements.push(
{name: paramobj.name, element: element, normalstyle: paramobj.normalstyle, hoverstyle: paramobj.hoverstyle, downstyle: paramobj.downstyle,
repeatwait: paramobj.repeatwait, repeatinterval: paramobj.repeatinterval, functionobj: functionobj}
);
if (element.addEventListener) { // DOM Level 2 -- Firefox, et al
element.addEventListener("mousedown", SocialCalc.ButtonMouseDown, false);
element.addEventListener("mouseover", SocialCalc.ButtonMouseOver, false);
element.addEventListener("mouseout", SocialCalc.ButtonMouseOut, false);
}
else if (element.attachEvent) { // IE 5+
element.attachEvent("onmousedown", SocialCalc.ButtonMouseDown);
element.attachEvent("onmouseover", SocialCalc.ButtonMouseOver);
element.attachEvent("onmouseout", SocialCalc.ButtonMouseOut);
}
else { // don't handle this
throw SocialCalc.Constants.s_BrowserNotSupported;
}
return;
}
//
// ButtonMouseOver(event)
//
SocialCalc.ButtonMouseOver = function(event) {
var e = event || window.event;
var buttoninfo = SocialCalc.ButtonInfo;
var bobj = SocialCalc.LookupElement(e.target || e.srcElement, buttoninfo.registeredElements);
if (!bobj) return;
if (buttoninfo.buttonDown) {
socialcalc/socialcalctableeditor.js view on Meta::CPAN
buttoninfo.doingHover = false;
}
if (bobj && bobj.functionobj && bobj.functionobj.MouseOut) bobj.functionobj.MouseOut(e, buttoninfo, bobj);
return;
}
//
// ButtonMouseDown(event)
//
SocialCalc.ButtonMouseDown = function(event) {
var e = event || window.event;
var buttoninfo = SocialCalc.ButtonInfo;
var viewportinfo = SocialCalc.GetViewportInfo();
var bobj = SocialCalc.LookupElement(e.target || e.srcElement, buttoninfo.registeredElements);
if (!bobj) return; // not one of our elements
if (bobj && bobj.functionobj && bobj.functionobj.Disabled) {
if (bobj.functionobj.Disabled(e, buttoninfo, bobj)) {
return;
}
}
buttoninfo.buttonElement = bobj;
buttoninfo.buttonDown = true;
SocialCalc.setStyles(bobj.element, buttoninfo.buttonElement.downstyle);
// Register event handler for mouse up
// Event code from JavaScript, Flanagan, 5th Edition, pg. 422
if (document.addEventListener) { // DOM Level 2 -- Firefox, et al
document.addEventListener("mouseup", SocialCalc.ButtonMouseUp, true); // capture everywhere
}
else if (bobj.element.attachEvent) { // IE 5+
bobj.element.setCapture();
bobj.element.attachEvent("onmouseup", SocialCalc.ButtonMouseUp);
bobj.element.attachEvent("onlosecapture", SocialCalc.ButtonMouseUp);
}
if (e.stopPropagation) e.stopPropagation(); // DOM Level 2
else e.cancelBubble = true; // IE 5+
if (e.preventDefault) e.preventDefault(); // DOM Level 2
else e.returnValue = false; // IE 5+
buttoninfo.horizontalScroll = viewportinfo.horizontalScroll;
buttoninfo.verticalScroll = viewportinfo.verticalScroll;
buttoninfo.clientX = e.clientX + buttoninfo.horizontalScroll; // get document-relative coordinates
buttoninfo.clientY = e.clientY + buttoninfo.verticalScroll;
if (bobj && bobj.functionobj && bobj.functionobj.MouseDown) bobj.functionobj.MouseDown(e, buttoninfo, bobj);
if (bobj.repeatwait) { // if a repeat wait is set, then starting waiting for first repetition
buttoninfo.timer = window.setTimeout(SocialCalc.ButtonRepeat, bobj.repeatwait);
}
return;
}
//
// ButtonMouseUp(event)
//
SocialCalc.ButtonMouseUp = function(event) {
var e = event || window.event;
var buttoninfo = SocialCalc.ButtonInfo;
var bobj = buttoninfo.buttonElement;
if (buttoninfo.timer) { // if repeating, cancel it
window.clearTimeout(buttoninfo.timer); // cancel timer
buttoninfo.timer = null;
}
if (!buttoninfo.buttonDown) return; // already did this (e.g., in IE, releaseCapture fires losecapture)
if (e.stopPropagation) e.stopPropagation(); // DOM Level 2
else e.cancelBubble = true; // IE 5+
if (e.preventDefault) e.preventDefault(); // DOM Level 2
else e.returnValue = false; // IE 5+
if (document.removeEventListener) { // DOM Level 2
document.removeEventListener("mouseup", SocialCalc.ButtonMouseUp, true);
}
else if (document.detachEvent) { // IE
bobj.element.detachEvent("onlosecapture", SocialCalc.ButtonMouseUp);
bobj.element.detachEvent("onmouseup", SocialCalc.ButtonMouseUp);
bobj.element.releaseCapture();
}
if (buttoninfo.buttonElement.downstyle) {
if (buttoninfo.doingHover)
SocialCalc.setStyles(bobj.element, buttoninfo.buttonElement.hoverstyle);
else
SocialCalc.setStyles(bobj.element, buttoninfo.buttonElement.normalstyle);
}
buttoninfo.buttonDown = false;
if (bobj && bobj.functionobj && bobj.functionobj.MouseUp) bobj.functionobj.MouseUp(e, buttoninfo, bobj);
}
//
// ButtonRepeat()
//
SocialCalc.ButtonRepeat = function() {
var buttoninfo = SocialCalc.ButtonInfo;
var bobj = buttoninfo.buttonElement;
if (!bobj) return;
if (bobj && bobj.functionobj && bobj.functionobj.Repeat) bobj.functionobj.Repeat(null, buttoninfo, bobj);
buttoninfo.timer = window.setTimeout(SocialCalc.ButtonRepeat, bobj.repeatinterval || 100);
}
// *************************************
//
// MouseWheel functions:
//
// *************************************
SocialCalc.MouseWheelInfo = {
// There is only one of these -- no "new" is done.
// The mousewheel only affects the one area the mouse pointer is over
// The registeredElements array is used to identify items.
// One item for each element to respond to the mousewheel, each an object with:
// .element, .functionobj
registeredElements: []
}
//
// MouseWheelRegister(element, functionobj) - make element respond to mousewheel
//
SocialCalc.MouseWheelRegister = function(element, functionobj) {
var mousewheelinfo = SocialCalc.MouseWheelInfo;
mousewheelinfo.registeredElements.push(
{element: element, functionobj: functionobj}
);
if (element.addEventListener) { // DOM Level 2 -- Firefox, et al
element.addEventListener("DOMMouseScroll", SocialCalc.ProcessMouseWheel, false);
element.addEventListener("mousewheel", SocialCalc.ProcessMouseWheel, false); // Opera needs this
}
else if (element.attachEvent) { // IE 5+
element.attachEvent("onmousewheel", SocialCalc.ProcessMouseWheel);
}
else { // don't handle this
throw SocialCalc.Constants.s_BrowserNotSupported;
}
return;
}
SocialCalc.ProcessMouseWheel = function(e) {
var event = e || window.event;
var delta;
if (SocialCalc.Keyboard.passThru) return; // ignore
var mousewheelinfo = SocialCalc.MouseWheelInfo;
var ele = event.target || event.srcElement; // source object is often within what we want
var wobj;
( run in 2.482 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )