App-SocialCalc-Multiplayer
view release on metacpan or search on metacpan
socialcalc/socialcalctableeditor.js view on Meta::CPAN
// 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);
ta.style.left = (position.left-1)+"px";
ta.style.top = (position.top-1)+"px";
}
socialcalc/socialcalctableeditor.js view on Meta::CPAN
// StatusCallback: all values are called at appropriate times, add with unique name, delete when done
//
// Each value must be an object in the form of:
//
// func: function(editor, status, arg, params) {...},
// params: params value to call func with
//
// The values for status and arg are:
//
// all the SocialCalc RecalcSheet statuscallbacks, including:
//
// calccheckdone, calclist length
// calcorder, {coord: coord, total: celllist length, count: count}
// calcstep, {coord: coord, total: calclist length, count: count}
// calcfinished, time in milliseconds
//
// the command callbacks, like cmdstart and cmdend
// cmdendnorender
//
// calcstart, null
// moveecell, new ecell coord
// rangechange, "coord:coord" or "coord" or ""
// specialkey, keyname ("[esc]")
//
this.StatusCallback = {};
this.MoveECellCallback = {}; // all values are called with editor as arg; add with unique name, delete when done
this.RangeChangeCallback = {}; // all values are called with editor as arg; add with unique name, delete when done
this.SettingsCallbacks = {}; // See SocialCalc.SaveEditorSettings
// Set initial cursor
this.ecell = {coord: "A1", row: 1, col: 1};
context.highlights[this.ecell.coord] = "cursor";
// Initialize range data
// Range has at least hasrange (true/false).
// It may also have: anchorcoord, anchorrow, anchorcol, top, bottom, left, and right.
this.range = {hasrange: false};
// Initialize range2 data (used to show selections, such as for move)
// Range2 has at least hasrange (true/false).
// It may also have: top, bottom, left, and right.
this.range2 = {hasrange: false};
}
// Methods:
SocialCalc.TableEditor.prototype.CreateTableEditor = function(width, height) {return SocialCalc.CreateTableEditor(this, width, height);};
SocialCalc.TableEditor.prototype.ResizeTableEditor = function(width, height) {return SocialCalc.ResizeTableEditor(this, width, height);};
SocialCalc.TableEditor.prototype.SaveEditorSettings = function() {return SocialCalc.SaveEditorSettings(this);};
SocialCalc.TableEditor.prototype.LoadEditorSettings = function(str, flags) {return SocialCalc.LoadEditorSettings(this, str, flags);};
SocialCalc.TableEditor.prototype.EditorRenderSheet = function() {SocialCalc.EditorRenderSheet(this);};
SocialCalc.TableEditor.prototype.EditorScheduleSheetCommands = function(cmdstr, saveundo, ignorebusy) {SocialCalc.EditorScheduleSheetCommands(this, cmdstr, saveundo, ignorebusy);};
SocialCalc.TableEditor.prototype.ScheduleSheetCommands = function(cmdstr, saveundo) {
this.context.sheetobj.ScheduleSheetCommands(cmdstr, saveundo);
};
SocialCalc.TableEditor.prototype.SheetUndo = function() {
this.context.sheetobj.SheetUndo();
};
SocialCalc.TableEditor.prototype.SheetRedo = function() {
this.context.sheetobj.SheetRedo();
};
SocialCalc.TableEditor.prototype.EditorStepSet = function(status, arg) {SocialCalc.EditorStepSet(this, status, arg);};
SocialCalc.TableEditor.prototype.GetStatuslineString = function(status, arg, params) {return SocialCalc.EditorGetStatuslineString(this, status, arg, params);};
SocialCalc.TableEditor.prototype.EditorMouseRegister = function() {return SocialCalc.EditorMouseRegister(this);};
SocialCalc.TableEditor.prototype.EditorMouseUnregister = function() {return SocialCalc.EditorMouseUnregister(this);};
SocialCalc.TableEditor.prototype.EditorMouseRange = function(coord) {return SocialCalc.EditorMouseRange(this, coord);};
SocialCalc.TableEditor.prototype.EditorProcessKey = function(ch, e) {return SocialCalc.EditorProcessKey(this, ch, e);};
SocialCalc.TableEditor.prototype.EditorAddToInput = function(str, prefix) {return SocialCalc.EditorAddToInput(this, str, prefix);};
SocialCalc.TableEditor.prototype.DisplayCellContents = function() {return SocialCalc.EditorDisplayCellContents(this);};
SocialCalc.TableEditor.prototype.EditorSaveEdit = function(text) {return SocialCalc.EditorSaveEdit(this, text);};
SocialCalc.TableEditor.prototype.EditorApplySetCommandsToRange = function(cmdline, type) {return SocialCalc.EditorApplySetCommandsToRange(this, cmdline, type);};
SocialCalc.TableEditor.prototype.MoveECellWithKey = function(ch) {return SocialCalc.MoveECellWithKey(this, ch);};
SocialCalc.TableEditor.prototype.MoveECell = function(newcell) {return SocialCalc.MoveECell(this, newcell);};
SocialCalc.TableEditor.prototype.ReplaceCell = function(cell, row, col) {SocialCalc.ReplaceCell(this, cell, row, col);};
SocialCalc.TableEditor.prototype.UpdateCellCSS = function(cell, row, col) {SocialCalc.UpdateCellCSS(this, cell, row, col);};
SocialCalc.TableEditor.prototype.SetECellHeaders = function(selected) {SocialCalc.SetECellHeaders(this, selected);};
SocialCalc.TableEditor.prototype.EnsureECellVisible = function() {SocialCalc.EnsureECellVisible(this);};
SocialCalc.TableEditor.prototype.RangeAnchor = function(coord) {SocialCalc.RangeAnchor(this, coord);};
SocialCalc.TableEditor.prototype.RangeExtend = function(coord) {SocialCalc.RangeExtend(this, coord);};
SocialCalc.TableEditor.prototype.RangeRemove = function() {SocialCalc.RangeRemove(this);};
SocialCalc.TableEditor.prototype.Range2Remove = function() {SocialCalc.Range2Remove(this);};
SocialCalc.TableEditor.prototype.FitToEditTable = function() {SocialCalc.FitToEditTable(this);};
SocialCalc.TableEditor.prototype.CalculateEditorPositions = function() {SocialCalc.CalculateEditorPositions(this);};
SocialCalc.TableEditor.prototype.ScheduleRender = function() {SocialCalc.ScheduleRender(this);};
SocialCalc.TableEditor.prototype.DoRenderStep = function() {SocialCalc.DoRenderStep(this);};
SocialCalc.TableEditor.prototype.SchedulePositionCalculations = function() {SocialCalc.SchedulePositionCalculations(this);};
SocialCalc.TableEditor.prototype.DoPositionCalculations = function() {SocialCalc.DoPositionCalculations(this);};
SocialCalc.TableEditor.prototype.CalculateRowPositions = function(panenum, positions, sizes) {return SocialCalc.CalculateRowPositions(this, panenum, positions, sizes);};
SocialCalc.TableEditor.prototype.CalculateColPositions = function(panenum, positions, sizes) {return SocialCalc.CalculateColPositions(this, panenum, positions, sizes);};
SocialCalc.TableEditor.prototype.ScrollRelative = function(vertical, amount) {SocialCalc.ScrollRelative(this, vertical, amount);};
SocialCalc.TableEditor.prototype.ScrollRelativeBoth = function(vamount, hamount) {SocialCalc.ScrollRelativeBoth(this, vamount, hamount);};
SocialCalc.TableEditor.prototype.PageRelative = function(vertical, direction) {SocialCalc.PageRelative(this, vertical, direction);};
SocialCalc.TableEditor.prototype.LimitLastPanes = function() {SocialCalc.LimitLastPanes(this);};
SocialCalc.TableEditor.prototype.ScrollTableUpOneRow = function() {return SocialCalc.ScrollTableUpOneRow(this);};
SocialCalc.TableEditor.prototype.ScrollTableDownOneRow = function() {return SocialCalc.ScrollTableDownOneRow(this);};
SocialCalc.TableEditor.prototype.ScrollTableLeftOneCol = function() {return SocialCalc.ScrollTableLeftOneCol(this);};
SocialCalc.TableEditor.prototype.ScrollTableRightOneCol = function() {return SocialCalc.ScrollTableRightOneCol(this);};
// Functions:
SocialCalc.CreateTableEditor = function(editor, width, height) {
var scc = SocialCalc.Constants;
var AssignID = SocialCalc.AssignID;
editor.toplevel = document.createElement("div");
socialcalc/socialcalctableeditor.js view on Meta::CPAN
case "ecell":
editor.ecell = SocialCalc.coordToCr(parts[1]);
editor.ecell.coord = parts[1];
highlights[parts[1]] = "cursor";
break;
case "range":
range.hasrange = true;
range.anchorcoord = parts[1];
cr = SocialCalc.coordToCr(range.anchorcoord);
range.anchorrow = cr.row;
range.anchorcol = cr.col;
range.top = parts[2]-0;
range.bottom = parts[3]-0;
range.left = parts[4]-0;
range.right = parts[5]-0;
for (row=range.top; row<=range.bottom; row++) {
for (col=range.left; col<=range.right; col++) {
coord = SocialCalc.crToCoord(col, row);
if (highlights[coord]!="cursor") {
highlights[coord] = "range";
}
}
}
break;
default:
if (editor.SettingsCallbacks[setting]) {
editor.SettingsCallbacks[setting].load(editor, setting, line, flags);
}
break;
}
}
return;
}
//
// EditorRenderSheet(editor)
//
// Renders the sheet and updates editor.fullgrid.
// Sets event handlers.
//
SocialCalc.EditorRenderSheet = function(editor) {
editor.EditorMouseUnregister();
editor.fullgrid = editor.context.RenderSheet(editor.fullgrid);
if (editor.ecell) editor.SetECellHeaders("selected");
SocialCalc.AssignID(editor, editor.fullgrid, "fullgrid"); // give it an id
editor.EditorMouseRegister();
}
//
// EditorScheduleSheetCommands(editor, cmdstr, saveundo, ignorebusy)
//
SocialCalc.EditorScheduleSheetCommands = function(editor, cmdstr, saveundo, ignorebusy) {
if (editor.state!="start" && !ignorebusy) { // ignore commands if editing a cell
return;
}
if (editor.busy && !ignorebusy) { // hold off on commands if doing one
editor.deferredCommands.push({cmdstr: cmdstr, saveundo: saveundo});
return;
}
switch (cmdstr) {
case "recalc":
case "redisplay":
editor.context.sheetobj.ScheduleSheetCommands(cmdstr, false);
break;
case "undo":
editor.SheetUndo();
break;
case "redo":
editor.SheetRedo();
break;
default:
editor.context.sheetobj.ScheduleSheetCommands(cmdstr, saveundo);
break;
}
}
//
// EditorSheetStatusCallback(recalcdata, status, arg, editor)
//
// Called during recalc, executing commands, etc.
//
SocialCalc.EditorSheetStatusCallback = function(recalcdata, status, arg, editor) {
var f, cell, dcmd;
var sheetobj = editor.context.sheetobj;
var signalstatus = function(s) {
for (f in editor.StatusCallback) {
if (editor.StatusCallback[f].func) {
editor.StatusCallback[f].func(editor, s, arg, editor.StatusCallback[f].params);
}
}
}
switch (status) {
case "startup":
break;
case "cmdstart":
editor.busy = true;
sheetobj.celldisplayneeded = "";
break;
case "cmdextension":
break;
case "cmdend":
signalstatus(status);
if (sheetobj.changedrendervalues) {
editor.context.PrecomputeSheetFontsAndLayouts();
editor.context.CalculateCellSkipData();
sheetobj.changedrendervalues = false;
}
if (sheetobj.celldisplayneeded && !sheetobj.renderneeded) {
cr = SocialCalc.coordToCr(sheetobj.celldisplayneeded);
cell = SocialCalc.GetEditorCellElement(editor, cr.row, cr.col);
editor.ReplaceCell(cell, cr.row, cr.col);
}
if (editor.deferredCommands.length) {
dcmd = editor.deferredCommands.shift();
editor.EditorScheduleSheetCommands(dcmd.cmdstr, dcmd.saveundo, true);
return;
}
if (sheetobj.attribs.needsrecalc &&
(sheetobj.attribs.recalc!="off" || sheetobj.recalconce)
&& editor.recalcFunction) {
editor.FitToEditTable();
sheetobj.renderneeded = false; // recalc will force a render
if (sheetobj.recalconce) delete sheetobj.recalconce; // only do once
editor.recalcFunction(editor);
}
else {
if (sheetobj.renderneeded) {
editor.FitToEditTable();
sheetobj.renderneeded = false;
editor.ScheduleRender();
}
else {
editor.SchedulePositionCalculations(); // just in case command changed positions
// editor.busy = false;
// signalstatus("cmdendnorender");
}
}
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 = "";
socialcalc/socialcalctableeditor.js view on Meta::CPAN
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;
case "calcserverfunc":
progress = scc.s_statusline_calculating+Math.floor(100*arg.count/(arg.total||1))+"%, "+scc.s_statusline_doingserverfunc+arg.funcname+scc.s_statusline_incell+arg.coord;
break;
case "calcstart":
params.calculating = true;
document.body.style.cursor = "progress";
editor.griddiv.style.cursor = "progress"; // griddiv has an explicit cursor style
progress = scc.s_statusline_calcstart;
break;
case "calccheckdone":
break;
case "calcfinished":
params.calculating = false;
break;
default:
progress = status;
break;
}
if (!progress && params.calculating) {
progress = scc.s_statusline_calculating;
}
// if there is a range, calculate sum (not during busy times)
if (!params.calculating && !params.command && !progress && editor.range.hasrange
&& (editor.range.left!=editor.range.right || editor.range.top!=editor.range.bottom)) {
sum = 0;
for (r=editor.range.top; r <= editor.range.bottom; r++) {
for (c=editor.range.left; c <= editor.range.right; c++) {
cell = editor.context.sheetobj.cells[SocialCalc.crToCoord(c, r)];
if (!cell) continue;
if (cell.valuetype && cell.valuetype.charAt(0)=="n") {
sum += cell.datavalue-0;
}
}
}
sum = SocialCalc.FormatNumber.formatNumberWithFormat(sum, "[,]General", "");
coord = SocialCalc.crToCoord(editor.range.left, editor.range.top) + ":" +
SocialCalc.crToCoord(editor.range.right, editor.range.bottom);
progress = coord + " (" + (editor.range.right-editor.range.left+1) + "x" + (editor.range.bottom-editor.range.top+1) +
") "+scc.s_statusline_sum+"=" + sum + " " + progress;
}
sstr = editor.ecell.coord+" "+progress;
if (!params.calculating && editor.context.sheetobj.attribs.needsrecalc=="yes") {
sstr += ' '+scc.s_statusline_recalcneeded;
}
circ = editor.context.sheetobj.attribs.circularreferencecell;
if (circ) {
circ = circ.replace(/\|/, " referenced by ");
sstr += ' '+scc.s_statusline_circref + circ + '</span>';
}
return sstr;
}
//
// Mouse stuff
//
SocialCalc.EditorMouseInfo = {
// The registeredElements array is used to identify editor grid in which the mouse is doing things.
// One item for each active editor, each an object with:
// .element, .editor
registeredElements: [],
editor: null, // editor being processed (between mousedown and mouseup)
element: null, // element being processed
ignore: false, // if true, mousedowns are ignored
mousedowncoord: "", // coord where mouse went down for drag range
mouselastcoord: "", // coord where mouse last was during drag
mouseresizecol: "", // col being resized
mouseresizeclientx: null, // where resize started
mouseresizedisplay: null // element tracking new size
socialcalc/socialcalctableeditor.js view on Meta::CPAN
else if (value.length==0) {
type = "empty";
}
else {
valueinfo = SocialCalc.DetermineValueType(value)
if (valueinfo.type=="n" && value==(valueinfo.value+"")) { // see if don't need "constant"
type = "value n";
}
else if (valueinfo.type.charAt(0)=="t") {
type = "text "+valueinfo.type;
}
else if (valueinfo.type=="") {
type = "text t";
}
else {
type = "constant "+valueinfo.type+" "+valueinfo.value;
}
}
if (type.charAt(0)=="t") { // text
value = SocialCalc.encodeForSave(value); // newlines, :, and \ are escaped
}
cmdline = "set "+wval.ecoord+" "+type+" "+value;
editor.EditorScheduleSheetCommands(cmdline, true, false);
return;
}
//
// SocialCalc.EditorApplySetCommandsToRange(editor, cmd)
//
// Takes ecell or range and does a "set" command with cmd.
//
SocialCalc.EditorApplySetCommandsToRange = function(editor, cmd) {
var cell, row, col, line, errortext;
var sheetobj = editor.context.sheetobj;
var ecell = editor.ecell;
var range = editor.range;
if (range.hasrange) {
coord = SocialCalc.crToCoord(range.left, range.top)+":"+SocialCalc.crToCoord(range.right, range.bottom);
line = "set "+coord+" "+cmd;
errortext = editor.EditorScheduleSheetCommands(line, true, false);
}
else {
line = "set "+ecell.coord+" "+cmd;
errortext = editor.EditorScheduleSheetCommands(line, true, false);
}
editor.DisplayCellContents();
}
SocialCalc.EditorProcessMouseWheel = function(event, delta, mousewheelinfo, wobj) {
if (wobj.functionobj.editor.busy) return; // ignore if busy
if (delta > 0) {
wobj.functionobj.editor.ScrollRelative(true, -1);
}
if (delta < 0) {
wobj.functionobj.editor.ScrollRelative(true, +1);
}
}
//
// GridMousePosition(editor, clientX, clientY)
//
// Returns an object with row and col numbers and coord (spans handled for coords),
// and rowheader/colheader true if in header (where coord will be undefined).
// If in colheader, will return coltoresize if on appropriate place in col header.
// Also, there is rowfooter (on right) and colfooter (on bottom).
// In row/col header/footer, returns "distance" as pixels over the edge.
//
SocialCalc.GridMousePosition = function(editor, clientX, clientY) {
var row, col, colpane;
var result = {};
for (row=1; row<editor.rowpositions.length; row++) {
if (!editor.rowheight[row]) continue; // not rendered yet -- may be above or below us
if (editor.rowpositions[row]+editor.rowheight[row]>clientY) {
break;
}
}
for (col=1; col<editor.colpositions.length; col++) {
if (!editor.colwidth[col]) continue;
if (editor.colpositions[col]+editor.colwidth[col]>clientX) {
break;
}
}
result.row = row;
result.col = col;
if (editor.headposition) {
if (clientX < editor.headposition.left && clientX >= editor.gridposition.left) {
result.rowheader = true;
result.distance = editor.headposition.left - clientX;
return result;
}
else if (clientY < editor.headposition.top && clientY > editor.gridposition.top) { // > because of sizing row
result.colheader = true;
result.distance = editor.headposition.top - clientY;
result.coltoresize = col-(editor.colpositions[col]+editor.colwidth[col]/2>clientX?1:0) || 1;
for (colpane=0; colpane<editor.context.colpanes.length; colpane++) {
if (result.coltoresize >= editor.context.colpanes[colpane].first &&
result.coltoresize <= editor.context.colpanes[colpane].last) { // visible column
return result;
}
}
delete result.coltoresize;
return result;
}
socialcalc/socialcalctableeditor.js view on Meta::CPAN
editor.RangeExtend();
else
editor.RangeRemove();
}
return coord;
}
//
// cellcoord = MoveECell(editor, newecell)
//
// Takes a coordinate and returns the new edit cell coordinate (which may be
// different if newecell is covered by a span).
//
SocialCalc.MoveECell = function(editor, newcell) {
var cell, f;
var highlights = editor.context.highlights;
if (editor.ecell) {
if (editor.ecell.coord==newcell) return newcell; // already there - don't do anything and don't tell anybody
if (SocialCalc.Callbacks.broadcast) {
SocialCalc.Callbacks.broadcast('ecell', { original: editor.ecell.coord, ecell: newcell });
}
cell=SocialCalc.GetEditorCellElement(editor, editor.ecell.row, editor.ecell.col);
delete highlights[editor.ecell.coord];
if (editor.range2.hasrange &&
editor.ecell.row>=editor.range2.top && editor.ecell.row<=editor.range2.bottom &&
editor.ecell.col>=editor.range2.left && editor.ecell.col<=editor.range2.right) {
highlights[editor.ecell.coord] = "range2";
}
editor.UpdateCellCSS(cell, editor.ecell.row, editor.ecell.col);
editor.SetECellHeaders(""); // set to regular col/rowname styles
editor.cellhandles.ShowCellHandles(false);
}
else if (SocialCalc.Callbacks.broadcast) {
SocialCalc.Callbacks.broadcast('ecell', { ecell: newcell });
}
newcell = editor.context.cellskip[newcell] || newcell;
editor.ecell = SocialCalc.coordToCr(newcell);
editor.ecell.coord = newcell;
cell=SocialCalc.GetEditorCellElement(editor, editor.ecell.row, editor.ecell.col);
highlights[newcell] = "cursor";
for (f in editor.MoveECellCallback) { // let others know
editor.MoveECellCallback[f](editor);
}
editor.UpdateCellCSS(cell, editor.ecell.row, editor.ecell.col);
editor.SetECellHeaders("selected");
for (f in editor.StatusCallback) { // let status line, etc., know
editor.StatusCallback[f].func(editor, "moveecell", newcell, editor.StatusCallback[f].params);
}
if (editor.busy) {
editor.ensureecell = true; // wait for when not busy
}
else {
editor.ensureecell = false;
editor.EnsureECellVisible();
}
return newcell;
}
SocialCalc.EnsureECellVisible = function(editor) {
var vamount = 0;
var hamount = 0;
if (editor.ecell.row > editor.lastnonscrollingrow) {
if (editor.ecell.row < editor.firstscrollingrow) {
vamount = editor.ecell.row - editor.firstscrollingrow;
}
else if (editor.ecell.row > editor.lastvisiblerow) {
vamount = editor.ecell.row - editor.lastvisiblerow;
}
}
if (editor.ecell.col > editor.lastnonscrollingcol) {
if (editor.ecell.col < editor.firstscrollingcol) {
hamount = editor.ecell.col - editor.firstscrollingcol;
}
else if (editor.ecell.col > editor.lastvisiblecol) {
hamount = editor.ecell.col- editor.lastvisiblecol;
}
}
if (vamount!=0 || hamount!=0) {
editor.ScrollRelativeBoth(vamount, hamount);
}
else {
editor.cellhandles.ShowCellHandles(true);
}
}
SocialCalc.ReplaceCell = function(editor, cell, row, col) {
var newelement, a;
if (!cell) return;
newelement = editor.context.RenderCell(row, col, cell.rowpane, cell.colpane, true, null);
if (newelement) {
// Don't use a real element and replaceChild, which seems to have focus issues with IE, Firefox, and speed issues
cell.element.innerHTML = newelement.innerHTML;
cell.element.style.cssText = "";
cell.element.className = newelement.className;
for (a in newelement.style) {
if (newelement.style[a]!="cssText")
cell.element.style[a] = newelement.style[a];
}
}
}
SocialCalc.UpdateCellCSS = function(editor, cell, row, col) {
socialcalc/socialcalctableeditor.js view on Meta::CPAN
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) {
socialcalc/socialcalctableeditor.js view on Meta::CPAN
// Functions:
SocialCalc.CreateTableControl = function(control) {
var s, functions, params;
var AssignID = SocialCalc.AssignID;
var setStyles = SocialCalc.setStyles;
var scc = SocialCalc.Constants;
var TooltipRegister = function(element, etype, vh) {
if (scc["s_"+etype+"Tooltip"+vh]) {
SocialCalc.TooltipRegister(element, scc["s_"+etype+"Tooltip"+vh], null);
}
}
var imageprefix = control.editor.imageprefix;
var vh = control.vertical ? "v" : "h";
control.main = document.createElement("div");
s = control.main.style;
s.height = (control.vertical ? control.size : control.controlthickness)+"px";
s.width = (control.vertical ? control.controlthickness : control.size)+"px";
s.zIndex = 0;
setStyles(control.main, scc.TCmainStyle);
s.backgroundImage="url("+imageprefix+"main-"+vh+".gif)";
if (scc.TCmainClass) control.main.className = scc.TCmainClass;
control.main.style.display="none"; // wait for layout
control.endcap = document.createElement("div");
s = control.endcap.style;
s.height = control.controlthickness+"px";
s.width = control.controlthickness+"px";
s.zIndex = 1;
s.overflow = "hidden"; // IE will make the DIV at least font-size height...so use this
s.position = "absolute";
setStyles(control.endcap, scc.TCendcapStyle);
s.backgroundImage="url("+imageprefix+"endcap-"+vh+".gif)";
if (scc.TCendcapClass) control.endcap.className = scc.TCendcapClass;
AssignID(control.editor, control.endcap, "endcap"+vh);
control.main.appendChild(control.endcap);
control.paneslider = document.createElement("div");
s = control.paneslider.style;
s.height = (control.vertical ? control.sliderthickness : control.controlthickness)+"px";
s.overflow = "hidden"; // IE will make the DIV at least font-size height...so use this
s.width = (control.vertical ? control.controlthickness : control.sliderthickness)+"px";
s.position = "absolute";
s[control.vertical?"top":"left"] = "4px";
s.zIndex = 3;
setStyles(control.paneslider, scc.TCpanesliderStyle);
s.backgroundImage="url("+imageprefix+"paneslider-"+vh+".gif)";
if (scc.TCpanesliderClass) control.paneslider.className = scc.TCpanesliderClass;
AssignID(control.editor, control.paneslider, "paneslider"+vh);
TooltipRegister(control.paneslider, "paneslider", vh);
functions = {MouseDown:SocialCalc.TCPSDragFunctionStart,
MouseMove: SocialCalc.TCPSDragFunctionMove,
MouseUp: SocialCalc.TCPSDragFunctionStop,
Disabled: function() {return control.editor.busy;}};
functions.control = control; // make sure this is there
SocialCalc.DragRegister(control.paneslider, control.vertical, !control.vertical, functions);
control.main.appendChild(control.paneslider);
control.lessbutton = document.createElement("div");
s = control.lessbutton.style;
s.height = (control.vertical ? control.buttonthickness : control.controlthickness)+"px";
s.width = (control.vertical ? control.controlthickness : control.buttonthickness)+"px";
s.zIndex = 2;
s.overflow = "hidden"; // IE will make the DIV at least font-size height...so use this
s.position = "absolute";
setStyles(control.lessbutton, scc.TClessbuttonStyle);
s.backgroundImage="url("+imageprefix+"less-"+vh+"n.gif)"
if (scc.TClessbuttonClass) control.lessbutton.className = scc.TClessbuttonClass;
AssignID(control.editor, control.lessbutton, "lessbutton"+vh);
params = {repeatwait:scc.TClessbuttonRepeatWait, repeatinterval:scc.TClessbuttonRepeatInterval,
normalstyle: "backgroundImage:url("+imageprefix+"less-"+vh+"n.gif);",
downstyle: "backgroundImage:url("+imageprefix+"less-"+vh+"d.gif);",
hoverstyle: "backgroundImage:url("+imageprefix+"less-"+vh+"h.gif);"};
functions = {MouseDown:function(){if(!control.editor.busy) control.editor.ScrollRelative(control.vertical, -1);},
Repeat:function(){if(!control.editor.busy) control.editor.ScrollRelative(control.vertical, -1);},
Disabled: function() {return control.editor.busy;}};
SocialCalc.ButtonRegister(control.lessbutton, params, functions);
control.main.appendChild(control.lessbutton);
control.morebutton = document.createElement("div");
s = control.morebutton.style;
s.height = (control.vertical ? control.buttonthickness : control.controlthickness)+"px";
s.width = (control.vertical ? control.controlthickness : control.buttonthickness)+"px";
s.zIndex = 2;
s.overflow = "hidden"; // IE will make the DIV at least font-size height...so use this
s.position = "absolute";
setStyles(control.morebutton, scc.TCmorebuttonStyle);
s.backgroundImage="url("+imageprefix+"more-"+vh+"n.gif)"
if (scc.TCmorebuttonClass) control.morebutton.className = scc.TCmorebuttonClass;
AssignID(control.editor, control.morebutton, "morebutton"+vh);
params = {repeatwait:scc.TCmorebuttonRepeatWait, repeatinterval:scc.TCmorebuttonRepeatInterval,
normalstyle: "backgroundImage:url("+imageprefix+"more-"+vh+"n.gif);",
downstyle: "backgroundImage:url("+imageprefix+"more-"+vh+"d.gif);",
hoverstyle: "backgroundImage:url("+imageprefix+"more-"+vh+"h.gif);"};
functions = {MouseDown:function(){if(!control.editor.busy) control.editor.ScrollRelative(control.vertical, +1);},
Repeat:function(){if(!control.editor.busy) control.editor.ScrollRelative(control.vertical, +1);},
Disabled: function() {return control.editor.busy;}};
SocialCalc.ButtonRegister(control.morebutton, params, functions);
control.main.appendChild(control.morebutton);
control.scrollarea = document.createElement("div");
s = control.scrollarea.style;
s.height = control.controlthickness+"px";
s.width = control.controlthickness+"px";
s.zIndex = 1;
s.overflow = "hidden"; // IE will make the DIV at least font-size height...so use this
s.position = "absolute";
setStyles(control.scrollarea, scc.TCscrollareaStyle);
s.backgroundImage="url("+imageprefix+"scrollarea-"+vh+".gif)";
if (scc.TCscrollareaClass) control.scrollarea.className = scc.TCscrollareaClass;
AssignID(control.editor, control.scrollarea, "scrollarea"+vh);
params = {repeatwait:scc.TCscrollareaRepeatWait, repeatinterval:scc.TCscrollareaRepeatWait};
functions = {MouseDown:SocialCalc.ScrollAreaClick, Repeat:SocialCalc.ScrollAreaClick,
Disabled: function() {return control.editor.busy;}};
functions.control = control;
SocialCalc.ButtonRegister(control.scrollarea, params, functions);
control.main.appendChild(control.scrollarea);
control.thumb = document.createElement("div");
s = control.thumb.style;
s.height = (control.vertical ? control.thumbthickness : control.controlthickness)+"px";
s.width = (control.vertical ? control.controlthickness : control.thumbthickness)+"px";
s.zIndex = 2;
s.overflow = "hidden"; // IE will make the DIV at least font-size height...so use this
s.position = "absolute";
setStyles(control.thumb, scc.TCthumbStyle);
control.thumb.style.backgroundImage="url("+imageprefix+"thumb-"+vh+"n.gif)";
if (scc.TCthumbClass) control.thumb.className = scc.TCthumbClass;
AssignID(control.editor, control.thumb, "thumb"+vh);
functions = {MouseDown:SocialCalc.TCTDragFunctionStart,
MouseMove: SocialCalc.TCTDragFunctionMove,
MouseUp: SocialCalc.TCTDragFunctionStop,
Disabled: function() {return control.editor.busy;}};
functions.control = control; // make sure this is there
SocialCalc.DragRegister(control.thumb, control.vertical, !control.vertical, functions);
params = {normalstyle: "backgroundImage:url("+imageprefix+"thumb-"+vh+"n.gif)", name:"Thumb",
downstyle: "backgroundImage:url("+imageprefix+"thumb-"+vh+"d.gif)",
hoverstyle: "backgroundImage:url("+imageprefix+"thumb-"+vh+"h.gif)"};
SocialCalc.ButtonRegister(control.thumb, params, null); // give it button-like visual behavior
control.main.appendChild(control.thumb);
return control.main;
}
//
// ScrollAreaClick - Button function to process pageup/down clicks
//
SocialCalc.ScrollAreaClick = function(e, buttoninfo, bobj) {
var control = bobj.functionobj.control;
var bposition = SocialCalc.GetElementPosition(bobj.element);
var clickpos = control.vertical ? buttoninfo.clientY : buttoninfo.clientX;
if (control.editor.busy) { // ignore if busy - wait for next repeat
return;
}
control.editor.PageRelative(control.vertical, clickpos > control.thumbpos ? 1 : -1);
return;
}
//
// PositionTableControlElements
//
SocialCalc.PositionTableControlElements = function(control) {
var border, realend, thumbpos;
var editor = control.editor;
if (control.vertical) {
border = control.controlborder+"px";
control.endcap.style.top = control.endcapstart+"px";
control.endcap.style.left = border;
control.paneslider.style.top = control.panesliderstart+"px";
control.paneslider.style.left = border
control.lessbutton.style.top = control.lessbuttonstart+"px";
control.lessbutton.style.left = border;
control.morebutton.style.top = control.morebuttonstart+"px";
control.morebutton.style.left = border;
control.scrollarea.style.top = control.scrollareastart+"px";
control.scrollarea.style.left = border;
control.scrollarea.style.height = control.scrollareasize+"px";
realend = Math.max(editor.context.sheetobj.attribs.lastrow, editor.firstscrollingrow+1);
thumbpos = ((editor.firstscrollingrow-(editor.lastnonscrollingrow+1))*(control.scrollareasize-3*control.thumbthickness))/
(realend-(editor.lastnonscrollingrow+1))+control.scrollareastart-1;
thumbpos = Math.floor(thumbpos);
control.thumb.style.top = thumbpos+"px";
control.thumb.style.left = border;
}
else {
border = control.controlborder+"px";
control.endcap.style.left = control.endcapstart+"px";
control.endcap.style.top = border;
control.paneslider.style.left = control.panesliderstart+"px";
control.paneslider.style.top = border
control.lessbutton.style.left = control.lessbuttonstart+"px";
control.lessbutton.style.top = border;
control.morebutton.style.left = control.morebuttonstart+"px";
control.morebutton.style.top = border;
control.scrollarea.style.left = control.scrollareastart+"px";
control.scrollarea.style.top = border;
control.scrollarea.style.width = control.scrollareasize+"px";
realend = Math.max(editor.context.sheetobj.attribs.lastcol, editor.firstscrollingcol+1);
thumbpos = ((editor.firstscrollingcol-(editor.lastnonscrollingcol+1))*(control.scrollareasize-control.thumbthickness))/
(realend-editor.lastnonscrollingcol)+control.scrollareastart-1;
thumbpos = Math.floor(thumbpos);
control.thumb.style.left = thumbpos+"px";
control.thumb.style.top = border;
}
control.thumbpos = thumbpos;
control.main.style.display="block";
( run in 1.375 second using v1.01-cache-2.11-cpan-5a3173703d6 )