App-SocialCalc-Multiplayer
view release on metacpan or search on metacpan
socialcalc/socialcalc-3.js view on Meta::CPAN
// font: fontfamily, fontlook, fontsize
if (!newattribs.fontlook.def || !newattribs.fontsize.def || !newattribs.fontfamily.def) {
value =
(newattribs.fontlook.def ? "* " : newattribs.fontlook.val + " ") +
(newattribs.fontsize.def ? "* " : newattribs.fontsize.val + " ") +
(newattribs.fontfamily.def ? "*" : newattribs.fontfamily.val);
}
else {
value = "";
}
if (value != (sheet.fonts[attribs.defaultfont] || "")) {
DoCmd("defaultfont "+value);
}
// color: textcolor
CheckChanges("textcolor", sheet.colors[attribs.defaultcolor], "defaultcolor");
// bgcolor: bgcolor
CheckChanges("bgcolor", sheet.colors[attribs.defaultbgcolor], "defaultbgcolor");
// formatting: numberformat, textformat
CheckChanges("numberformat", sheet.valueformats[attribs.defaultnontextvalueformat], "defaultnontextvalueformat");
CheckChanges("textformat", sheet.valueformats[attribs.defaulttextvalueformat], "defaulttextvalueformat");
// recalc: recalc
CheckChanges("recalc", sheet.attribs.recalc, "recalc");
// if any changes return command(s)
if (changed) {
return cmdstr;
}
else {
return null;
}
}
// *************************************
//
// Sheet command routines
//
// *************************************
//
// SocialCalc.SheetCommandInfo - object with information used during command execution
//
SocialCalc.SheetCommandInfo = { // only one of these
sheetobj: null, // sheet being operated on
parseobj: null, // SocialCalc.Parse object with the command string, etc.
timerobj: null, // used for timeslicing
firsttimerdelay: 50, // wait before starting cmds (for Chrome - to give time to update)
timerdelay: 1, // wait between slices
maxtimeslice: 100, // do another slice after this many milliseconds
saveundo: false, // arg for ExecuteSheetCommand
CmdExtensionCallbacks: {}, // for startcmdextension, in form: cmdname, {func:function(cmdname, data, sheet, SocialCalc.Parse object, saveundo), data:whatever}
cmdextensionbusy: "" // if length>0, command loop waits for SocialCalc.ResumeFromCmdExtension()
// statuscallback: null, // called during execution - obsolete: use sheet obj's
// statuscallbackparams: null
};
//
// SocialCalc.ScheduleSheetCommands
//
// statuscallback is called at the beginning (cmdstart) and end (cmdend).
//
SocialCalc.ScheduleSheetCommands = function(sheet, cmdstr, saveundo, isRemote) {
if (SocialCalc.Callbacks.broadcast && !isRemote) {
if (cmdstr != 'redisplay' && cmdstr != 'set sheet defaulttextvalueformat text-wiki') {
SocialCalc.Callbacks.broadcast('execute', { cmdstr: cmdstr, saveundo: saveundo });
}
}
var sci = SocialCalc.SheetCommandInfo;
sci.sheetobj = sheet;
sci.parseobj = new SocialCalc.Parse(cmdstr);
sci.saveundo = saveundo;
if (sci.sheetobj.statuscallback) { // notify others if requested
sheet.statuscallback(sci, "cmdstart", "", sci.sheetobj.statuscallbackparams);
}
if (sci.saveundo) {
sci.sheetobj.changes.PushChange(""); // add a step to undo stack
}
sci.timerobj = window.setTimeout(SocialCalc.SheetCommandsTimerRoutine, sci.firsttimerdelay);
}
SocialCalc.SheetCommandsTimerRoutine = function() {
var errortext;
var sci = SocialCalc.SheetCommandInfo;
var starttime = new Date();
sci.timerobj = null;
while (!sci.parseobj.EOF()) { // go through all commands (separated by newlines)
errortext = SocialCalc.ExecuteSheetCommand(sci.sheetobj, sci.parseobj, sci.saveundo);
if (errortext) alert(errortext);
sci.parseobj.NextLine();
if (sci.cmdextensionbusy.length > 0) { // forced wait
if (sci.sheetobj.statuscallback) { // notify others if requested
sci.sheetobj.statuscallback(sci, "cmdextension", sci.cmdextensionbusy, sci.sheetobj.statuscallbackparams);
}
return;
}
if (((new Date()) - starttime) >= sci.maxtimeslice) { // if taking too long, give up CPU for a while
sci.timerobj = window.setTimeout(SocialCalc.SheetCommandsTimerRoutine, sci.timerdelay);
return;
}
}
if (sci.sheetobj.statuscallback) { // notify others if requested
sci.sheetobj.statuscallback(sci, "cmdend", "", sci.sheetobj.statuscallbackparams);
}
}
SocialCalc.ResumeFromCmdExtension = function() {
var sci = SocialCalc.SheetCommandInfo;
sci.cmdextensionbusy = "";
SocialCalc.SheetCommandsTimerRoutine();
}
//
// errortext = SocialCalc.ExecuteSheetCommand(sheet, cmd, saveundo)
//
// cmd is a SocialCalc.Parse object.
//
// Executes commands that modify the sheet data.
// Sets sheet "needsrecalc" as needed.
// Sets sheet "changedrendervalues" as needed.
//
// The cmd string may be multiple commands, separated by newlines. In that case
// only one "step" is put on the undo stack representing all the commands.
// Note that because of this, in "set A1 text ..." and "set A1 comment ..." text is
// treated as encoded (newline => \n, \ => \b, : => \c).
//
// The commands are in the forms:
//
// set sheet attributename value (plus lastcol and lastrow)
// set 22 attributename value
// set B attributename value
// set A1 attributename value1 value2... (see each attribute in code for details)
// set A1:B5 attributename value1 value2...
// erase/copy/cut/paste/fillright/filldown A1:B5 all/formulas/format
// loadclipboard save-encoded-clipboard-data
// clearclipboard
// merge C3:F3
// unmerge C3
// insertcol/insertrow C5
// deletecol/deleterow C5:E7
// movepaste/moveinsert A1:B5 A8 all/formulas/format (if insert, destination must be in same rows or columns or else paste done)
// sort cr1:cr2 col1 up/down col2 up/down col3 up/down
// name define NAME definition
// name desc NAME description
// name delete NAME
// recalc
// redisplay
// changedrendervalues
// startcmdextension extension rest-of-command
//
// If saveundo is true, then undo information is saved in sheet.changes.
//
socialcalc/socialcalc-3.js view on Meta::CPAN
//!!!! HANDLE RANGE EXTENT MOVES
ttext = tokenOpExpansion[ttext] || ttext; // make sure short tokens (e.g., "G") go back full (">=")
}
if (ttype == token_coord) {
cr = SocialCalc.coordToCr(ttext); // get parts
coord = SocialCalc.crToCoord(cr.col, cr.row); // get "clean" reference
if (movedto[coord] && !sheetref) { // this is a reference to a moved cell
cr = SocialCalc.coordToCr(movedto[coord]); // get new row and col
if (ttext.charAt(0)=="$") { // copy absolute ref marks if present
newcr = "$"+SocialCalc.rcColname(cr.col);
}
else {
newcr = SocialCalc.rcColname(cr.col);
}
if (ttext.indexOf("$", 1)!=-1) {
newcr += "$" + cr.row;
}
else {
newcr += cr.row;
}
ttext = newcr;
}
}
else if (ttype == token_string) {
if (ttext.indexOf('"') >= 0) { // quotes to double
ttext = '"' + ttext.replace(/"/, '""') + '"';
}
else ttext = '"' + ttext + '"';
}
updatedformula += ttext;
}
return updatedformula;
}
// ************************
//
// Recalc Loop Code
//
// ************************
//
// How recalc works:
//
// !!!!!!!!!!!!!!
//
// SocialCalc.RecalcInfo - object with global recalc info
SocialCalc.RecalcInfo = {
sheet: null, // which sheet is being recalced
currentState: 0, // current state
state: {start_calc: 1, order: 2, calc: 3, start_wait: 4, done_wait: 5}, // allowed state values
recalctimer: null, // value to cancel timer
maxtimeslice: 100, // maximum milliseconds per slice of recalc time before a wait
timeslicedelay: 1, // milliseconds to wait between recalc time slices
starttime: 0, // when recalc started
// LoadSheet: a function that returns true if started a load or false if not.
//
LoadSheet: function(sheetname) {return false;} // default returns not found
}
// SocialCalc.RecalcData - object with recalc info while determining recalc order and afterward
SocialCalc.RecalcData = function() { // initialize a RecalcData object
this.inrecalc = true; // if true, doing a recalc
this.celllist = []; // list with all potential cells to calculate
this.celllistitem = 0; // cell to check next when ordering
this.calclist = null; // object which is the chained list of cells to calculate
// each in the form of "coord: nextcoord"
// e.g., if B8 is calculated right after A8, then calclist.A8=="B8"
// if null, need to create the list
this.calclistlength = 0; // number of items in calclist
this.firstcalc = null; // start of the calc list - a string or null
this.lastcalc = null; // last one on chain (used to add more to the end)
this.nextcalc = null; // used to keep track during background recalc to make it restartable
this.count = 0; // number calculated
// checkinfo is used when determining calc order:
this.checkinfo = {}; // attributes are coords; if no attrib for a coord, it wasn't checked or doesn't need it
// values are RecalcCheckInfo objects while checking or TRUE when complete
}
// SocialCalc.RecalcCheckInfo - object that stores checking info while determining recalc order
SocialCalc.RecalcCheckInfo = function() { // initialize a RecalcCheckInfo object
this.oldcoord = null; // chain back up of cells referring to cells
this.parsepos = 0; // which token we are up to
// range info
this.inrange = false; // if true, in the process of checking a range of coords
this.inrangestart = false; // if true, have not yet filled in range loop values
this.cr1 = null; // range first coord as a cr object
this.cr2 = null; // range second coord as a cr object
this.c1 = null; // range extents
this.c2 = null;
this.r1 = null;
this.r2 = null;
this.c = null; // looping values
this.r = null;
}
// Recalc the entire sheet
SocialCalc.RecalcSheet = function(sheet) {
var coord, err, recalcdata;
var scri = SocialCalc.RecalcInfo;
delete sheet.attribs.circularreferencecell; // reset recalc-wide things
SocialCalc.Formula.FreshnessInfoReset();
SocialCalc.RecalcClearTimeout();
scri.sheet = sheet; // set values needed by background recalc
scri.currentState = scri.state.start_calc;
scri.starttime = new Date();
if (sheet.statuscallback) {
sheet.statuscallback(scri, "calcstart", null, sheet.statuscallbackparams);
}
SocialCalc.RecalcSetTimeout();
}
//
// SocialCalc.RecalcSetTimeout - set a timer for next recalc step
//
SocialCalc.RecalcSetTimeout = function() {
var scri = SocialCalc.RecalcInfo;
scri.recalctimer = window.setTimeout(SocialCalc.RecalcTimerRoutine, scri.timeslicedelay);
}
//
// SocialCalc.RecalcClearTimeout - cancel any timeouts
//
SocialCalc.RecalcClearTimeout = function() {
var scri = SocialCalc.RecalcInfo;
if (scri.recalctimer) {
window.clearTimeout(scri.recalctimer);
scri.recalctimer = null;
}
}
//
// SocialCalc.RecalcLoadedSheet(sheetname, str, recalcneeded)
//
// Called when a sheet finishes loading with name, string, and t/f whether it should be recalced.
// If loaded sheet has sheet.attribs.recalc=="off", then no recalc done.
// If sheetname is null, then the sheetname waiting for will be used.
//
SocialCalc.RecalcLoadedSheet = function(sheetname, str, recalcneeded) {
var sheet;
var scri = SocialCalc.RecalcInfo;
var scf = SocialCalc.Formula;
sheet = SocialCalc.Formula.AddSheetToCache(sheetname || scf.SheetCache.waitingForLoading, str);
if (recalcneeded && sheet && sheet.attribs.recalc!="off") { // if recalcneeded, and not manual sheet, chain in this new sheet to recalc loop
sheet.previousrecalcsheet = scri.sheet;
scri.sheet = sheet;
scri.currentState = scri.state.start_calc;
}
scf.SheetCache.waitingForLoading = null;
SocialCalc.RecalcSetTimeout();
}
//
// SocialCalc.RecalcTimerRoutine - handles the actual order determination and cell-by-cell recalculation in the background
//
SocialCalc.RecalcTimerRoutine = function() {
var eresult, cell, coord, err, status;
var starttime = new Date();
var count = 0;
var scf = SocialCalc.Formula;
if (!scf) {
return "Need SocialCalc.Formula";
}
var scri = SocialCalc.RecalcInfo;
var sheet = scri.sheet;
if (!sheet) {
return;
}
var recalcdata = sheet.recalcdata;
var do_statuscallback = function(status, arg) { // routine to do callback if required
if (sheet.statuscallback) {
sheet.statuscallback(recalcdata, status, arg, sheet.statuscallbackparams);
}
}
SocialCalc.RecalcClearTimeout();
if (scri.currentState == scri.state.start_calc) {
recalcdata = new SocialCalc.RecalcData();
sheet.recalcdata = recalcdata;
for (coord in sheet.cells) { // get list of cells to check for order
if (!coord) continue;
recalcdata.celllist.push(coord);
}
recalcdata.calclist = {}; // start with empty list
scri.currentState = scri.state.order; // drop through to determining recalc order
}
if (scri.currentState == scri.state.order) {
while (recalcdata.celllistitem < recalcdata.celllist.length) { // check all the cells to see if they should be on the list
coord = recalcdata.celllist[recalcdata.celllistitem++];
err = SocialCalc.RecalcCheckCell(sheet, coord);
if (((new Date()) - starttime) >= scri.maxtimeslice) { // if taking too long, give up CPU for a while
do_statuscallback("calcorder", {coord: coord, total: recalcdata.celllist.length, count: recalcdata.celllistitem});
SocialCalc.RecalcSetTimeout();
return;
}
}
do_statuscallback("calccheckdone", recalcdata.calclistlength);
recalcdata.nextcalc = recalcdata.firstcalc; // start at the beginning of the recalc chain
scri.currentState = scri.state.calc; // loop through cells on next timer call
SocialCalc.RecalcSetTimeout();
return;
}
if (scri.currentState == scri.state.start_wait) { // starting to wait for something
scri.currentState = scri.state.done_wait; // finished on next timer call
if (scri.LoadSheet) {
status = scri.LoadSheet(scf.SheetCache.waitingForLoading);
if (status) { // started a load operation
return;
}
}
SocialCalc.RecalcLoadedSheet(null, "", false);
return;
}
if (scri.currentState == scri.state.done_wait) {
scri.currentState = scri.state.calc; // loop through cells on next timer call
SocialCalc.RecalcSetTimeout();
return;
}
// otherwise should be scri.state.calc
if (scri.currentState != scri.state.calc) {
alert("Recalc state error: "+scri.currentState+". Error in SocialCalc code.");
}
coord = sheet.recalcdata.nextcalc;
while (coord) {
cell = sheet.cells[coord];
eresult = scf.evaluate_parsed_formula(cell.parseinfo, sheet, false);
if (scf.SheetCache.waitingForLoading) { // wait until restarted
recalcdata.nextcalc = coord; // start with this cell again
recalcdata.count += count;
do_statuscallback("calcloading", {sheetname: scf.SheetCache.waitingForLoading});
scri.currentState = scri.state.start_wait; // start load on next timer call
SocialCalc.RecalcSetTimeout();
return;
}
if (scf.RemoteFunctionInfo.waitingForServer) { // wait until restarted
recalcdata.nextcalc = coord; // start with this cell again
recalcdata.count += count;
do_statuscallback("calcserverfunc",
{funcname: scf.RemoteFunctionInfo.waitingForServer, coord: coord, total: recalcdata.calclistlength, count: recalcdata.count});
scri.currentState = scri.state.done_wait; // start load on next timer call
return; // return and wait for next recalc timer event
}
if (cell.datavalue != eresult.value ||
cell.valuetype != eresult.type) { // only update if changed from last time
cell.datavalue = eresult.value;
cell.valuetype = eresult.type;
delete cell.displaystring;
sheet.recalcchangedavalue = true; // remember something changed in case other code wants to know
}
if (eresult.error) {
cell.errors = eresult.error;
}
count++;
coord = sheet.recalcdata.calclist[coord];
if (((new Date()) - starttime) >= scri.maxtimeslice) { // if taking too long, give up CPU for a while
recalcdata.nextcalc = coord; // start with next cell on chain
recalcdata.count += count;
do_statuscallback("calcstep", {coord: coord, total: recalcdata.calclistlength, count: recalcdata.count});
SocialCalc.RecalcSetTimeout();
return;
}
}
recalcdata.inrecalc = false;
delete sheet.recalcdata; // save memory and clear out for name lookup formula evaluation
delete sheet.attribs.needsrecalc; // remember recalc done
scri.sheet = sheet.previousrecalcsheet || null; // chain back if doing recalc of loaded sheets
if (scri.sheet) {
scri.currentState = scri.state.calc; // start where we left off
SocialCalc.RecalcSetTimeout();
return;
}
scf.FreshnessInfo.recalc_completed = true; // say freshness info is complete
do_statuscallback("calcfinished", (new Date()) - scri.starttime);
}
//
// circref = SocialCalc.RecalcCheckCell(sheet, coord)
//
// Checks cell to put on calclist, looking at parsed tokens.
// Also checks cells this cell is dependent upon
// if it contains a formula with cell references.
// If circular reference, returns non-null.
//
SocialCalc.RecalcCheckCell = function(sheet, startcoord) {
var parseinfo, ttext, ttype, i, rangecoord, circref, value, pos, pos2, cell, coordvals;
var scf = SocialCalc.Formula;
if (!scf) {
return "Need SocialCalc.Formula";
}
( run in 0.490 second using v1.01-cache-2.11-cpan-d8267643d1d )