App-SocialCalc-Multiplayer
view release on metacpan or search on metacpan
socialcalc/socialcalc-3.js view on Meta::CPAN
var tos, i;
var didredo = sheet.changes.Redo();
if (!didredo) {
sheet.ScheduleSheetCommands("", false); // schedule doing nothing
return;
}
tos = sheet.changes.TOS();
var cmdstr = "";
for (i=0; tos && i<tos.command.length; i++) {
if (cmdstr) cmdstr += "\n"; // concatenate with separate lines
cmdstr += tos.command[i];
}
sheet.ScheduleSheetCommands(cmdstr, false); // do undo operations
}
SocialCalc.CreateAuditString = function(sheet) {
var i, j;
var result = "";
var stack = sheet.changes.stack;
var tos = sheet.changes.tos;
for (i=0; i<=tos; i++) {
for (j=0; j<stack[i].command.length; j++) {
result += stack[i].command[j] + "\n";
}
}
return result;
}
SocialCalc.GetStyleNum = function(sheet, atype, style) {
var num;
if (style.length==0) return 0; // null means use zero, which means default or global default
num = sheet[atype+"hash"][style];
if (!num) {
if (sheet[atype+"s"].length<1) sheet[atype+"s"].push("");
num = sheet[atype+"s"].push(style) - 1;
sheet[atype+"hash"][style] = num;
sheet.changedrendervalues = true;
}
return num;
}
SocialCalc.GetStyleString = function(sheet, atype, num) {
if (!num) return null; // zero, null, and undefined return null
return sheet[atype+"s"][num];
}
//
// updatedformula = SocialCalc.OffsetFormulaCoords(formula, coloffset, rowoffset)
//
// Change relative cell references by offsets (even those to other worksheets so fill, paste, sort work as expected).
// If not what you want, use absolute references.
//
SocialCalc.OffsetFormulaCoords = function(formula, coloffset, rowoffset) {
var parseinfo, ttext, ttype, i, cr, newcr;
var updatedformula = "";
var scf = SocialCalc.Formula;
if (!scf) {
return "Need SocialCalc.Formula";
}
var tokentype = scf.TokenType;
var token_op = tokentype.op;
var token_string = tokentype.string;
var token_coord = tokentype.coord;
var tokenOpExpansion = scf.TokenOpExpansion;
parseinfo = scf.ParseFormulaIntoTokens(formula);
for (i=0; i<parseinfo.length; i++) {
ttype = parseinfo[i].type;
ttext = parseinfo[i].text;
if (ttype == token_coord) {
newcr = "";
cr = SocialCalc.coordToCr(ttext);
if (ttext.charAt(0)!="$") { // add col offset unless absolute column
cr.col += coloffset;
}
else {
newcr += "$";
}
newcr += SocialCalc.rcColname(cr.col);
if (ttext.indexOf("$", 1)==-1) { // add row offset unless absolute row
cr.row += rowoffset;
}
else {
newcr += "$";
}
newcr += cr.row;
if (cr.row < 1 || cr.col < 1) {
newcr = "#REF!";
}
updatedformula += newcr;
}
else if (ttype == token_string) {
if (ttext.indexOf('"') >= 0) { // quotes to double
updatedformula += '"' + ttext.replace(/"/, '""') + '"';
}
else updatedformula += '"' + ttext + '"';
}
else if (ttype == token_op) {
updatedformula += tokenOpExpansion[ttext] || ttext; // make sure short tokens (e.g., "G") go back full (">=")
}
else { // leave everything else alone
updatedformula += ttext;
}
}
return updatedformula;
}
//
// updatedformula = SocialCalc.AdjustFormulaCoords(formula, col, coloffset, row, rowoffset)
//
// Change all cell references to cells starting with col/row by offsets
//
SocialCalc.AdjustFormulaCoords = function(formula, col, coloffset, row, rowoffset) {
var ttype, ttext, i, newcr;
var updatedformula = "";
var sheetref = false;
var scf = SocialCalc.Formula;
if (!scf) {
return "Need SocialCalc.Formula";
}
var tokentype = scf.TokenType;
var token_op = tokentype.op;
var token_string = tokentype.string;
var token_coord = tokentype.coord;
var tokenOpExpansion = scf.TokenOpExpansion;
parseinfo = SocialCalc.Formula.ParseFormulaIntoTokens(formula);
for (i=0; i<parseinfo.length; i++) {
ttype = parseinfo[i].type;
ttext = parseinfo[i].text;
if (ttype == token_op) { // references with sheet specifier are not offset
if (ttext == "!") {
sheetref = true; // found a sheet reference
}
else if (ttext != ":") { // for everything but a range, reset
sheetref = false;
}
ttext = tokenOpExpansion[ttext] || ttext; // make sure short tokens (e.g., "G") go back full (">=")
}
if (ttype == token_coord) {
cr = SocialCalc.coordToCr(ttext);
if ((coloffset < 0 && cr.col >= col && cr.col < col-coloffset) ||
(rowoffset < 0 && cr.row >= row && cr.row < row-rowoffset)) { // refs to deleted cells become invalid
if (!sheetref) {
cr.col = 0;
cr.row = 0;
}
}
if (!sheetref) {
if (cr.col >= col) {
cr.col += coloffset;
}
if (cr.row >= row) {
cr.row += rowoffset;
}
}
if (ttext.charAt(0)=="$") {
newcr = "$"+SocialCalc.rcColname(cr.col);
}
else {
newcr = SocialCalc.rcColname(cr.col);
}
if (ttext.indexOf("$", 1)!=-1) {
newcr += "$" + cr.row;
}
else {
newcr += cr.row;
}
if (cr.row < 1 || cr.col < 1) {
newcr = "#REF!";
}
ttext = newcr;
}
else if (ttype == token_string) {
if (ttext.indexOf('"') >= 0) { // quotes to double
ttext = '"' + ttext.replace(/"/, '""') + '"';
}
else ttext = '"' + ttext + '"';
}
updatedformula += ttext;
}
return updatedformula;
}
//
// updatedformula = SocialCalc.ReplaceFormulaCoords(formula, movedto)
//
// Change all cell references to cells that are keys in moveto to be to moveto[coord].
// Don't change references to other sheets.
// Handle range extents specially.
//
SocialCalc.ReplaceFormulaCoords = function(formula, movedto) {
var ttype, ttext, i, newcr, coord;
var updatedformula = "";
var sheetref = false;
var scf = SocialCalc.Formula;
if (!scf) {
return "Need SocialCalc.Formula";
}
var tokentype = scf.TokenType;
var token_op = tokentype.op;
var token_string = tokentype.string;
var token_coord = tokentype.coord;
var tokenOpExpansion = scf.TokenOpExpansion;
parseinfo = SocialCalc.Formula.ParseFormulaIntoTokens(formula);
for (i=0; i<parseinfo.length; i++) {
ttype = parseinfo[i].type;
ttext = parseinfo[i].text;
if (ttype == token_op) { // references with sheet specifier are not change
if (ttext == "!") {
sheetref = true; // found a sheet reference
}
else if (ttext != ":") { // for everything but a range, reset
sheetref = false;
}
//!!!! 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
( run in 1.053 second using v1.01-cache-2.11-cpan-e93a5daba3e )