App-SocialCalc-Multiplayer

 view release on metacpan or  search on metacpan

socialcalc/socialcalc-3.js  view on Meta::CPAN

// Callbacks

SocialCalc.Callbacks = {

   // The next two are used by SocialCalc.format_text_for_display

   // The function to expand wiki text - should be set if you want wikitext expansion
   // The form is: expand_wiki(displayvalue, sheetobj, linkstyle, valueformat)
   //    valueformat is text-wiki followed by optional sub-formats, e.g., text-wikipagelink

   expand_wiki: null,

   expand_markup: function(displayvalue, sheetobj, linkstyle) // the old function to expand wiki text - may be replaced
                   {return SocialCalc.default_expand_markup(displayvalue, sheetobj, linkstyle);},

   // MakePageLink is used to create the href for a link to another "page"
   // The form is: MakePageLink(pagename, workspacename, linktyle, valueformat), returns string

   MakePageLink: null,

   // NormalizeSheetName is used to make different variations of sheetnames use the same cache slot

   NormalizeSheetName: null // use default - lowercase

   };

// Shared flags

   // none at present


// *************************************
//
// Cell class:
//
// *************************************

//
// Class SocialCalc.Cell
//
// Usage: var s = new SocialCalc.Cell(coord);
//
// Cell attributes include:
//
//    coord: the column/row as a string, e.g., "A1"
//    datavalue: the value to be used for computation and formatting for display,
//               string or numeric (tolerant of numbers stored as strings)
//    datatype: if present, v=numeric value, t=text value, f=formula,
//              or c=constant that is not a simple number (like "$1.20")
//    formula: if present, the formula (without leading "=") for computation or the constant
//    valuetype: first char is main type, the following are sub-types.
//               Main types are b=blank cell, n=numeric, t=text, e=error
//               Examples of using sub-types would be "nt" for a numeric time value, "n$" for currency, "nl" for logical
//    displayvalue: if present, rendered version of datavalue with formatting attributes applied
//    parseinfo: if present, cached parsed version of formula
//
//    The following optional values, if present, are mainly used in rendering, overriding defaults:
//
//    bt, br, bb, bl: number of border's definition
//    layout: layout (vertical alignment, padding) definition number
//    font: font definition number
//    color: text color definition number
//    bgcolor: background color definition number
//    cellformat: cell format (horizontal alignment) definition number
//    nontextvalueformat: custom format definition number for non-text values, e.g., numbers
//    textvalueformat: custom format definition number for text values
//    colspan, rowspan: number of cells to span for merged cells (only on main cell)
//    cssc: custom css classname for cell, as text (no special chars)
//    csss: custom css style definition
//    mod: modification allowed flag "y" if present
//    comment: cell comment string
//

SocialCalc.Cell = function(coord) {

   this.coord = coord;
   this.datavalue = "";
   this.datatype = null;
   this.formula = "";
   this.valuetype = "b";

   }

// The types of cell properties
//
// Type 1: Base, Type 2: Attribute, Type 3: Special (e.g., displaystring, parseinfo)

SocialCalc.CellProperties = {
   coord: 1, datavalue: 1, datatype: 1, formula: 1, valuetype: 1, errors: 1, comment: 1,
   bt: 2, br: 2, bb: 2, bl: 2, layout: 2, font: 2, color: 2, bgcolor: 2,
   cellformat: 2, nontextvalueformat: 2, textvalueformat: 2, colspan: 2, rowspan: 2,
   cssc: 2, csss: 2, mod: 2,
   displaystring: 3, // used to cache rendered HTML of cell contents
   parseinfo: 3, // used to cache parsed formulas
   hcolspan: 3, hrowspan: 3 // spans taking hidden cols/rows into account (!!! NOT YET !!!)
   };

SocialCalc.CellPropertiesTable = {
   bt: "borderstyle", br: "borderstyle", bb: "borderstyle", bl: "borderstyle",
   layout: "layout", font: "font", color: "color", bgcolor: "color",
   cellformat: "cellformat", nontextvalueformat: "valueformat", textvalueformat: "valueformat"
   };

// *************************************
//
// Sheet class:
//
// *************************************

//
// Class SocialCalc.Sheet
//
// Usage: var s = new SocialCalc.Sheet();
//

SocialCalc.Sheet = function() {

   SocialCalc.ResetSheet(this);

   // Set other values:
   //
   // sheet.statuscallback(data, status, arg, this.statuscallbackparams) is called
   // during recalc and commands.
   //
   // During recalc, data is the current recalcdata.
   // The values for status and the corresponding arg are:
   //
   //    calcorder, {coord: coord, total: celllist length, count: count} [0 or more times per recalc]
   //    calccheckdone, calclist length [once per recalc]
   //    calcstep, {coord: coord, total: calclist length, count: count} [0 or more times per recalc]
   //    calcloading, {sheetname: name-of-sheet}
   //    calcserverfunc, {funcname: name-of-function, coord: coord, total: calclist length, count: count}
   //    calcfinished, time in milliseconds [once per recalc]
   //
   // During commands, data is SocialCalc.SheetCommandInfo.
   // These values for status and arg are:
   //
   //    cmdstart, cmdstr
   //    cmdend
   //

   this.statuscallback = null; // routine called with cmdstart, calcstart, etc., status and args:
                                // sheet.statuscallback(data, status, arg, params)
   this.statuscallbackparams = null; // parameters passed to that routine

   }

//
// SocialCalc.ResetSheet(sheet)
//
// Resets (and/or initializes) sheet data values.
// 

SocialCalc.ResetSheet = function(sheet, reload) {

   // properties:

   sheet.cells = {}; // at least one for each non-blank cell: coord: cell-object
   sheet.attribs = // sheet attributes
      {
         lastcol: 1,
         lastrow: 1,
         defaultlayout: 0
      };
   sheet.rowattribs =
      {
         hide: {}, // access by row number
         height: {}
      };
   sheet.colattribs =
      {
         width: {}, // access by col name
         hide: {}
      };
   sheet.names={}; // Each is: {desc: "optional description", definition: "B5, A1:B7, or =formula"}
   sheet.layouts=[];
   sheet.layouthash={};
   sheet.fonts=[];
   sheet.fonthash={};
   sheet.colors=[];
   sheet.colorhash={};
   sheet.borderstyles=[];
   sheet.borderstylehash={};
   sheet.cellformats=[];
   sheet.cellformathash={};
   sheet.valueformats=[];
   sheet.valueformathash={};

   sheet.copiedfrom = ""; // if a range, then this was loaded from a saved range as clipboard content

   sheet.changes = new SocialCalc.UndoStack();

   sheet.renderneeded = false;

   sheet.changedrendervalues = true; // if true, spans and/or fonts have changed (set by ExecuteSheetCommand & GetStyle)

   sheet.recalcchangedavalue = false; // true if a recalc resulted in a change to a cell's calculated value

   }

// Methods:

SocialCalc.Sheet.prototype.ResetSheet = function() {SocialCalc.ResetSheet(this);};
SocialCalc.Sheet.prototype.AddCell = function(newcell) {return this.cells[newcell.coord]=newcell;};
SocialCalc.Sheet.prototype.GetAssuredCell = function(coord) {
   return this.cells[coord] || this.AddCell(new SocialCalc.Cell(coord));
   };
SocialCalc.Sheet.prototype.ParseSheetSave = function(savedsheet) {SocialCalc.ParseSheetSave(savedsheet,this);};
SocialCalc.Sheet.prototype.CellFromStringParts = function(cell, parts, j) {return SocialCalc.CellFromStringParts(this, cell, parts, j);};
SocialCalc.Sheet.prototype.CreateSheetSave = function(range, canonicalize) {return SocialCalc.CreateSheetSave(this, range, canonicalize);};
SocialCalc.Sheet.prototype.CellToString = function(cell) {return SocialCalc.CellToString(this, cell);};
SocialCalc.Sheet.prototype.CanonicalizeSheet = function(full) {return SocialCalc.CanonicalizeSheet(this, full);};
SocialCalc.Sheet.prototype.EncodeCellAttributes = function(coord) {return SocialCalc.EncodeCellAttributes(this, coord);};
SocialCalc.Sheet.prototype.EncodeSheetAttributes = function() {return SocialCalc.EncodeSheetAttributes(this);};
SocialCalc.Sheet.prototype.DecodeCellAttributes = function(coord, attribs, range) {return SocialCalc.DecodeCellAttributes(this, coord, attribs, range);};
SocialCalc.Sheet.prototype.DecodeSheetAttributes = function(attribs) {return SocialCalc.DecodeSheetAttributes(this, attribs);};

SocialCalc.Sheet.prototype.ScheduleSheetCommands = function(cmd, saveundo, isRemote) {return SocialCalc.ScheduleSheetCommands(this, cmd, saveundo, isRemote);};
SocialCalc.Sheet.prototype.SheetUndo = function() {return SocialCalc.SheetUndo(this);};
SocialCalc.Sheet.prototype.SheetRedo = function() {return SocialCalc.SheetRedo(this);};
SocialCalc.Sheet.prototype.CreateAuditString = function() {return SocialCalc.CreateAuditString(this);};
SocialCalc.Sheet.prototype.GetStyleNum = function(atype, style) {return SocialCalc.GetStyleNum(this, atype, style);};
SocialCalc.Sheet.prototype.GetStyleString = function(atype, num) {return SocialCalc.GetStyleString(this, atype, num);};
SocialCalc.Sheet.prototype.RecalcSheet = function() {return SocialCalc.RecalcSheet(this);};

//
// Sheet save format:
//
// linetype:param1:param2:...
//
// Linetypes are:
//
//    version:versionname - version of this format. Currently 1.4.
//
//    cell:coord:type:value...:type:value... - Types are as follows:
//
//       v:value - straight numeric value
//       t:value - straight text/wiki-text in cell, encoded to handle \, :, newlines
//       vt:fulltype:value - value with value type/subtype
//       vtf:fulltype:value:formulatext - formula resulting in value with value type/subtype, value and text encoded
//       vtc:fulltype:value:valuetext - formatted text constant resulting in value with value type/subtype, value and text encoded
//       vf:fvalue:formulatext - formula resulting in value, value and text encoded (obsolete: only pre format version 1.1)
//          fvalue - first char is "N" for numeric value, "T" for text value, "H" for HTML value, rest is the value
//       e:errortext - Error text. Non-blank means formula parsing/calculation results in error.
//       b:topborder#:rightborder#:bottomborder#:leftborder# - border# in sheet border list or blank if none
//       l:layout# - number in cell layout list
//       f:font# - number in sheet fonts list
//       c:color# - sheet color list index for text
//       bg:color# - sheet color list index for background color
//       cf:format# - sheet cell format number for explicit format (align:left, etc.)
//       cvf:valueformat# - sheet cell value format number (obsolete: only pre format v1.2)
//       tvf:valueformat# - sheet cell text value format number
//       ntvf:valueformat# - sheet cell non-text value format number
//       colspan:numcols - number of columns spanned in merged cell
//       rowspan:numrows - number of rows spanned in merged cell
//       cssc:classname - name of CSS class to be used for cell when published instead of one calculated here
//       csss:styletext - explicit CSS style information, encoded to handle :, etc.
//       mod:allow - if "y" allow modification of cell for live "view" recalc
//       comment:value - encoded text of comment for this cell (added in v1.5)
//
//    col:
//       w:widthval - number, "auto" (no width in <col> tag), number%, or blank (use default)
//       hide: - yes/no, no is assumed if missing
//    row:
//       hide - yes/no, no is assumed if missing
//
//    sheet:
//       c:lastcol - number
//       r:lastrow - number
//       w:defaultcolwidth - number, "auto", number%, or blank (default->80)
//       h:defaultrowheight - not used
//       tf:format# - cell format number for sheet default for text values
//       ntf:format# - cell format number for sheet default for non-text values (i.e., numbers)
//       layout:layout# - default cell layout number in cell layout list
//       font:font# - default font number in sheet font list
//       vf:valueformat# - default number value format number in sheet valueformat list (obsolete: only pre format version 1.2)
//       ntvf:valueformat# - default non-text (number) value format number in sheet valueformat list
//       tvf:valueformat# - default text value format number in sheet valueformat list
//       color:color# - default number for text color in sheet color list
//       bgcolor:color# - default number for background color in sheet color list
//       circularreferencecell:coord - cell coord with a circular reference
//       recalc:value - on/off (on is default). If not "off", appropriate changes to the sheet cause a recalc
//       needsrecalc:value - yes/no (no is default). If "yes", formula values are not up to date
//
//    name:name:description:value - name definition, name in uppercase, with value being "B5", "A1:B7", or "=formula";
//                                  description and value are encoded.
//    font:fontnum:value - text of font definition (style weight size family) for font fontnum
//                         "*" for "style weight", size, or family, means use default (first look to sheet, then builtin)
//    color:colornum:rgbvalue - text of color definition (e.g., rgb(255,255,255)) for color colornum
//    border:bordernum:value - text of border definition (thickness style color) for border bordernum
//    layout:layoutnum:value - text of vertical alignment and padding style for cell layout layoutnum (* for default):
//                             vertical-alignment:vavalue;padding:topval rightval bottomval leftval;
//    cellformat:cformatnum:value - text of cell alignment (left/center/right) for cellformat cformatnum
//    valueformat:vformatnum:value - text of number format (see FormatValueForDisplay) for valueformat vformatnum (changed in v1.2)
//    clipboardrange:upperleftcoord:bottomrightcoord - ignored -- from wikiCalc
//    clipboard:coord:type:value:... - ignored -- from wikiCalc
//
// If this is clipboard contents, then there is also information to facilitate pasting:
//
//    copiedfrom:upperleftcoord:bottomrightcoord - range from which this was copied
//

// Functions:

SocialCalc.ParseSheetSave = function(savedsheet,sheetobj) {

   var lines=savedsheet.split(/\r\n|\n/);
   var parts=[];
   var line;
   var i, j, t, v, coord, cell, attribs, name;
   var scc = SocialCalc.Constants;

   for (i=0;i<lines.length;i++) {
      line=lines[i];
      parts = line.split(":");
      switch (parts[0]) {
         case "cell":
            cell=sheetobj.GetAssuredCell(parts[1]);
            j=2;
            sheetobj.CellFromStringParts(cell, parts, j);
            break;

         case "col":
            coord=parts[1];
            j=2;
            while (t=parts[j++]) {
               switch (t) {
                  case "w":
                     sheetobj.colattribs.width[coord]=parts[j++]; // must be text - could be auto or %, etc.
                     break;
                  case "hide":
                     sheetobj.colattribs.hide[coord]=parts[j++];
                     break;
                  default:
                     throw scc.s_pssUnknownColType+" '"+t+"'";
                     break;
                  }
               }
            break;

         case "row":
            coord=parts[1]-0;
            j=2;
            while (t=parts[j++]) {
               switch (t) {
                  case "h":
                     sheetobj.rowattribs.height[coord]=parts[j++]-0;
                     break;
                  case "hide":
                     sheetobj.rowattribs.hide[coord]=parts[j++];
                     break;
                  default:
                     throw scc.s_pssUnknownRowType+" '"+t+"'";
                     break;
                  }
               }
            break;

         case "sheet":
            attribs=sheetobj.attribs;
            j=1;
            while (t=parts[j++]) {
               switch (t) {
                  case "c":
                     attribs.lastcol=parts[j++]-0;
                     break;
                  case "r":
                     attribs.lastrow=parts[j++]-0;
                     break;
                  case "w":
                     attribs.defaultcolwidth=parts[j++]+"";
                     break;
                  case "h":
                     attribs.defaultrowheight=parts[j++]-0;
                     break;
                  case "tf":
                     attribs.defaulttextformat=parts[j++]-0;
                     break;
                  case "ntf":
                     attribs.defaultnontextformat=parts[j++]-0;
                     break;
                  case "layout":
                     attribs.defaultlayout=parts[j++]-0;
                     break;
                  case "font":
                     attribs.defaultfont=parts[j++]-0;
                     break;
                  case "tvf":
                     attribs.defaulttextvalueformat=parts[j++]-0;
                     break;
                  case "ntvf":
                     attribs.defaultnontextvalueformat=parts[j++]-0;
                     break;
                  case "color":
                     attribs.defaultcolor=parts[j++]-0;
                     break;
                  case "bgcolor":
                     attribs.defaultbgcolor=parts[j++]-0;
                     break;
                  case "circularreferencecell":
                     attribs.circularreferencecell=parts[j++];
                     break;
                  case "recalc":
                     attribs.recalc=parts[j++];
                     break;
                  case "needsrecalc":
                     attribs.needsrecalc=parts[j++];
                     break;
                  default:
                     j+=1;
                     break;
                  }
               }
            break;

         case "name":
            name = SocialCalc.decodeFromSave(parts[1]).toUpperCase();
            sheetobj.names[name] = {desc: SocialCalc.decodeFromSave(parts[2])};
            sheetobj.names[name].definition = SocialCalc.decodeFromSave(parts[3]);
            break;

         case "layout":
            parts=lines[i].match(/^layout\:(\d+)\:(.+)$/); // layouts can have ":" in them
            sheetobj.layouts[parts[1]-0]=parts[2];
            sheetobj.layouthash[parts[2]]=parts[1]-0;
            break;

         case "font":
            sheetobj.fonts[parts[1]-0]=parts[2];
            sheetobj.fonthash[parts[2]]=parts[1]-0;
            break;

         case "color":
            sheetobj.colors[parts[1]-0]=parts[2];
            sheetobj.colorhash[parts[2]]=parts[1]-0;
            break;

         case "border":
            sheetobj.borderstyles[parts[1]-0]=parts[2];
            sheetobj.borderstylehash[parts[2]]=parts[1]-0;
            break;

         case "cellformat":
            v=SocialCalc.decodeFromSave(parts[2]);
            sheetobj.cellformats[parts[1]-0]=v;
            sheetobj.cellformathash[v]=parts[1]-0;
            break;

         case "valueformat":
            v=SocialCalc.decodeFromSave(parts[2]);
            sheetobj.valueformats[parts[1]-0]=v;
            sheetobj.valueformathash[v]=parts[1]-0;
            break;

         case "version":
            break;

         case "copiedfrom":
            sheetobj.copiedfrom = parts[1]+":"+parts[2];
            break;

         case "clipboardrange": // in save versions up to 1.3. Ignored.
         case "clipboard":
            break;

         case "":
            break;

         default:
alert(scc.s_pssUnknownLineType+" '"+parts[0]+"'");
            throw scc.s_pssUnknownLineType+" '"+parts[0]+"'";
            break;
         }
      parts = null;
      }

   }

//
// SocialCalc.CellFromStringParts(sheet, cell, parts, j)
//
// Takes string that has been split by ":" in parts, starting at item j,
// and fills in cell assuming save format.
//

SocialCalc.CellFromStringParts = function(sheet, cell, parts, j) {

   var cell, t, v;

   while (t=parts[j++]) {
      switch (t) {
         case "v":
            cell.datavalue=SocialCalc.decodeFromSave(parts[j++])-0;
            cell.datatype="v";
            cell.valuetype="n";
            break;
         case "t":
            cell.datavalue=SocialCalc.decodeFromSave(parts[j++]);
            cell.datatype="t";
            cell.valuetype=SocialCalc.Constants.textdatadefaulttype; 
            break;
         case "vt":
            v=parts[j++];
            cell.valuetype=v;
            if (v.charAt(0)=="n") {
               cell.datatype="v";
               cell.datavalue=SocialCalc.decodeFromSave(parts[j++])-0;
               }
            else {
               cell.datatype="t";
               cell.datavalue=SocialCalc.decodeFromSave(parts[j++]);
               }
            break;
         case "vtf":
            v=parts[j++];
            cell.valuetype=v;
            if (v.charAt(0)=="n") {
               cell.datavalue=SocialCalc.decodeFromSave(parts[j++])-0;
               }
            else {
               cell.datavalue=SocialCalc.decodeFromSave(parts[j++]);
               }
            cell.formula=SocialCalc.decodeFromSave(parts[j++]);
            cell.datatype="f";
            break;
         case "vtc":
            v=parts[j++];
            cell.valuetype=v;
            if (v.charAt(0)=="n") {
               cell.datavalue=SocialCalc.decodeFromSave(parts[j++])-0;
               }
            else {
               cell.datavalue=SocialCalc.decodeFromSave(parts[j++]);
               }
            cell.formula=SocialCalc.decodeFromSave(parts[j++]);
            cell.datatype="c";
            break;
         case "e":
            cell.errors=SocialCalc.decodeFromSave(parts[j++]);
            break;
         case "b":
            cell.bt=parts[j++]-0;
            cell.br=parts[j++]-0;
            cell.bb=parts[j++]-0;
            cell.bl=parts[j++]-0;
            break;
         case "l":
            cell.layout=parts[j++]-0;
            break;
         case "f":
            cell.font=parts[j++]-0;
            break;
         case "c":
            cell.color=parts[j++]-0;
            break;
         case "bg":
            cell.bgcolor=parts[j++]-0;
            break;
         case "cf":
            cell.cellformat=parts[j++]-0;
            break;
         case "ntvf":
            cell.nontextvalueformat=parts[j++]-0;
            break;
         case "tvf":
            cell.textvalueformat=parts[j++]-0;
            break;
         case "colspan":
            cell.colspan=parts[j++]-0;
            break;
         case "rowspan":
            cell.rowspan=parts[j++]-0;
            break;
         case "cssc":
            cell.cssc=parts[j++];
            break;
         case "csss":
            cell.csss=SocialCalc.decodeFromSave(parts[j++]);
            break;
         case "mod":
            j+=1;
            break;
         case "comment":
            cell.comment=SocialCalc.decodeFromSave(parts[j++]);
            break;
         default:
            throw SocialCalc.Constants.s_cfspUnknownCellType+" '"+t+"'";
            break;
         }
      }

   }


SocialCalc.sheetfields = ["defaultrowheight", "defaultcolwidth", "circularreferencecell", "recalc", "needsrecalc"];
SocialCalc.sheetfieldsshort = ["h", "w", "circularreferencecell", "recalc", "needsrecalc"];

SocialCalc.sheetfieldsxlat = ["defaulttextformat", "defaultnontextformat",
                              "defaulttextvalueformat", "defaultnontextvalueformat",
                              "defaultcolor", "defaultbgcolor", "defaultfont", "defaultlayout"];
SocialCalc.sheetfieldsxlatshort = ["tf", "ntf", "tvf", "ntvf", "color", "bgcolor", "font", "layout"];
SocialCalc.sheetfieldsxlatxlt = ["cellformat", "cellformat", "valueformat", "valueformat",
                                  "color", "color", "font", "layout"];

//
// sheetstr = SocialCalc.CreateSheetSave(sheetobj, range, canonicalize)
//
// Creates a text representation of the sheetobj data.
// If the range is present then only those cells are saved
// (as clipboard data with "copiedfrom" set).
//

SocialCalc.CreateSheetSave = function(sheetobj, range, canonicalize) {

   var cell, cr1, cr2, row, col, coord, attrib, line, value, formula, i, t, r, b, l, name, blanklen;
   var result=[];

   var prange;

   sheetobj.CanonicalizeSheet(canonicalize || SocialCalc.Constants.doCanonicalizeSheet);
   var xlt = sheetobj.xlt;

   if (range) {
      prange = SocialCalc.ParseRange(range);
      }
   else {
      prange = {cr1: {row: 1, col:1},
                cr2: {row: xlt.maxrow, col: xlt.maxcol}};
      }
   cr1 = prange.cr1;
   cr2 = prange.cr2;

   result.push("version:1.5");

   for (row=cr1.row; row <= cr2.row; row++) {
      for (col=cr1.col; col <= cr2.col; col++) {
         coord = SocialCalc.crToCoord(col, row);
         cell=sheetobj.cells[coord];
         if (!cell) continue;
         line=sheetobj.CellToString(cell);
         if (line.length==0) continue; // ignore completely empty cells
         line="cell:"+coord+line;
         result.push(line);
         }
      }

   for (col=1; col <= xlt.maxcol; col++) {
      coord = SocialCalc.rcColname(col);
      if (sheetobj.colattribs.width[coord])
         result.push("col:"+coord+":w:"+sheetobj.colattribs.width[coord]);
      if (sheetobj.colattribs.hide[coord])
         result.push("col:"+coord+":hide:"+sheetobj.colattribs.hide[coord]);
      }

   for (row=1; row <= xlt.maxrow; row++) {
      if (sheetobj.rowattribs.height[row])
         result.push("row:"+row+":h:"+sheetobj.rowattribs.height[row]);
      if (sheetobj.rowattribs.hide[row])
         result.push("row:"+row+":hide:"+sheetobj.rowattribs.hide[row]);
      }

   line="sheet:c:"+xlt.maxcol+":r:"+xlt.maxrow;

   for (i=0; i<SocialCalc.sheetfields.length; i++) { // non-xlated values
      value = SocialCalc.encodeForSave(sheetobj.attribs[SocialCalc.sheetfields[i]]);
      if (value) line+=":"+SocialCalc.sheetfieldsshort[i]+":"+value;
      }
   for (i=0; i<SocialCalc.sheetfieldsxlat.length; i++) { // xlated values
      value = sheetobj.attribs[SocialCalc.sheetfieldsxlat[i]];
      if (value) line+=":"+SocialCalc.sheetfieldsxlatshort[i]+":"+xlt[SocialCalc.sheetfieldsxlatxlt[i]+"sxlat"][value];
      }

   result.push(line);

   for (i=1;i<xlt.newborderstyles.length;i++) {
      result.push("border:"+i+":"+xlt.newborderstyles[i]);
      }

   for (i=1;i<xlt.newcellformats.length;i++) {
      result.push("cellformat:"+i+":"+SocialCalc.encodeForSave(xlt.newcellformats[i]));
      }

   for (i=1;i<xlt.newcolors.length;i++) {
      result.push("color:"+i+":"+xlt.newcolors[i]);
      }

   for (i=1;i<xlt.newfonts.length;i++) {
      result.push("font:"+i+":"+xlt.newfonts[i]);
      }

   for (i=1;i<xlt.newlayouts.length;i++) {
      result.push("layout:"+i+":"+xlt.newlayouts[i]);
      }

   for (i=1;i<xlt.newvalueformats.length;i++) {
      result.push("valueformat:"+i+":"+SocialCalc.encodeForSave(xlt.newvalueformats[i]));
      }

   for (i=0; i<xlt.namesorder.length; i++) {
      name = xlt.namesorder[i];
      result.push("name:"+SocialCalc.encodeForSave(name).toUpperCase()+":"+
                   SocialCalc.encodeForSave(sheetobj.names[name].desc)+":"+
                   SocialCalc.encodeForSave(sheetobj.names[name].definition));
      }

   if (range) {
      result.push("copiedfrom:"+SocialCalc.crToCoord(cr1.col, cr1.row)+":"+
                  SocialCalc.crToCoord(cr2.col, cr2.row));
      }

   result.push(""); // one extra to get extra \n

   delete sheetobj.xlt; // clean up

   return result.join("\n");
   }

//
// line = SocialCalc.CellToString(sheet, cell)
//

SocialCalc.CellToString = function(sheet, cell) {

   var cell, line, value, formula, t, r, b, l, xlt;

   line = "";

   if (!cell) return line;

   value = SocialCalc.encodeForSave(cell.datavalue);
   if (cell.datatype=="v") {
      if (cell.valuetype=="n") line += ":v:"+value;
      else line += ":vt:"+cell.valuetype+":"+value;
      }
   else if (cell.datatype=="t") {
      if (cell.valuetype==SocialCalc.Constants.textdatadefaulttype)
         line += ":t:"+value;
      else line += ":vt:"+cell.valuetype+":"+value;
      }
   else {
      formula = SocialCalc.encodeForSave(cell.formula);
      if (cell.datatype=="f") {
         line += ":vtf:"+cell.valuetype+":"+value+":"+formula;
         }
      else if (cell.datatype=="c") {
         line += ":vtc:"+cell.valuetype+":"+value+":"+formula;
         }
      }
   if (cell.errors) {
      line += ":e:"+SocialCalc.encodeForSave(cell.errors);
      }
   t = cell.bt || "";
   r = cell.br || "";
   b = cell.bb || "";
   l = cell.bl || "";

   if (sheet.xlt) { // if have canonical save info
      xlt = sheet.xlt;
      if (t || r || b || l)
      line += ":b:"+xlt.borderstylesxlat[t||0]+":"+xlt.borderstylesxlat[r||0]+":"+xlt.borderstylesxlat[b||0]+":"+xlt.borderstylesxlat[l||0];
      if (cell.layout) line += ":l:"+xlt.layoutsxlat[cell.layout];
      if (cell.font) line += ":f:"+xlt.fontsxlat[cell.font];
      if (cell.color) line += ":c:"+xlt.colorsxlat[cell.color];
      if (cell.bgcolor) line += ":bg:"+xlt.colorsxlat[cell.bgcolor];
      if (cell.cellformat) line += ":cf:"+xlt.cellformatsxlat[cell.cellformat];
      if (cell.textvalueformat) line += ":tvf:"+xlt.valueformatsxlat[cell.textvalueformat];
      if (cell.nontextvalueformat) line += ":ntvf:"+xlt.valueformatsxlat[cell.nontextvalueformat];
      }
   else {
      if (t || r || b || l)
      line += ":b:"+t+":"+r+":"+b+":"+l;
      if (cell.layout) line += ":l:"+cell.layout;
      if (cell.font) line += ":f:"+cell.font;
      if (cell.color) line += ":c:"+cell.color;
      if (cell.bgcolor) line += ":bg:"+cell.bgcolor;
      if (cell.cellformat) line += ":cf:"+cell.cellformat;
      if (cell.textvalueformat) line += ":tvf:"+cell.textvalueformat;
      if (cell.nontextvalueformat) line += ":ntvf:"+cell.nontextvalueformat;
      }
   if (cell.colspan) line += ":colspan:"+cell.colspan;
   if (cell.rowspan) line += ":rowspan:"+cell.rowspan;
   if (cell.cssc) line += ":cssc:"+cell.cssc;
   if (cell.csss) line += ":csss:"+SocialCalc.encodeForSave(cell.csss);
   if (cell.mod) line += ":mod:"+cell.mod;
   if (cell.comment) line += ":comment:"+SocialCalc.encodeForSave(cell.comment);

   return line;

   }

//
// SocialCalc.CanonicalizeSheet(sheetobj, full)
//
// Goes through the sheet and fills in sheetobj.xlt with the following:
//
//   .maxrow, .maxcol - lastrow and lastcol are as small as possible
//   .newlayouts - new version of sheetobj.layouts without unused ones and all in ascending order
//   .layoutsxlat - maps old layouts index to new one
//   same ".new" and ".xlat" for fonts, colors, borderstyles, cell and value formats
//   .namesorder - array with names sorted
//
// If full or SocialCalc.Constants.doCanonicalizeSheet are not true, then the values will leave things unchanged (to save time, etc.)
//
// sheetobj.xlt should be deleted when you are finished using it
//

SocialCalc.CanonicalizeSheet = function(sheetobj, full) {

   var l, coord, cr, cell, filled, an, a, newa, newxlat, used, ahash, i, v;
   var maxrow = 0;
   var maxcol = 0;
   var alist = ["borderstyle", "cellformat", "color", "font", "layout", "valueformat"];

   var xlt = {};

   xlt.namesorder = []; // always return a sorted list
   for (a in sheetobj.names) {
      xlt.namesorder.push(a);
      }
   xlt.namesorder.sort();

   if (!SocialCalc.Constants.doCanonicalizeSheet || !full) { // return make-no-changes values if not wanted
      for (an=0; an<alist.length; an++) {
         a = alist[an];
         xlt["new"+a+"s"] = sheetobj[a+"s"];
         l = sheetobj[a+"s"].length;
         newxlat = new Array(l);
         newxlat[0] = "";
         for (i=1; i<l; i++) {
            newxlat[i] = i;
            }
         xlt[a+"sxlat"] = newxlat;
         }

      xlt.maxrow = sheetobj.attribs.lastrow;
      xlt.maxcol = sheetobj.attribs.lastcol;

      sheetobj.xlt = xlt;

      return;
      }

   for (an=0; an<alist.length; an++) {
      a = alist[an];
      xlt[a+"sUsed"] = {};
      }

   var colorsUsed = xlt.colorsUsed;
   var borderstylesUsed = xlt.borderstylesUsed;
   var fontsUsed = xlt.fontsUsed;
   var layoutsUsed = xlt.layoutsUsed;
   var cellformatsUsed = xlt.cellformatsUsed;
   var valueformatsUsed = xlt.valueformatsUsed;

   for (coord in sheetobj.cells) { // check all cells to see which values are used
      cr = SocialCalc.coordToCr(coord);
      cell = sheetobj.cells[coord];
      filled = false;

      if (cell.valuetype && cell.valuetype!="b") filled = true;

      if (cell.color) {
         colorsUsed[cell.color] = 1;
         filled = true;
         }

      if (cell.bgcolor) {
         colorsUsed[cell.bgcolor] = 1;
         filled = true;
         }

      if (cell.bt) {
         borderstylesUsed[cell.bt] = 1;
         filled = true;
         }
      if (cell.br) {
         borderstylesUsed[cell.br] = 1;
         filled = true;
         }
      if (cell.bb) {
         borderstylesUsed[cell.bb] = 1;
         filled = true;
         }
      if (cell.bl) {
         borderstylesUsed[cell.bl] = 1;
         filled = true;
         }

      if (cell.layout) {
         layoutsUsed[cell.layout] = 1;
         filled = true;
         }

      if (cell.font) {
         fontsUsed[cell.font] = 1;
         filled = true;
         }

      if (cell.cellformat) {
         cellformatsUsed[cell.cellformat] = 1;
         filled = true;
         }

      if (cell.textvalueformat) {
         valueformatsUsed[cell.textvalueformat] = 1;
         filled = true;
         }

      if (cell.nontextvalueformat) {
         valueformatsUsed[cell.nontextvalueformat] = 1;
         filled = true;
         }

      if (filled) {
         if (cr.row > maxrow) maxrow = cr.row;
         if (cr.col > maxcol) maxcol = cr.col;
         }
      }

   for (i=0; i<SocialCalc.sheetfieldsxlat.length; i++) { // do sheet values, too
      v = sheetobj.attribs[SocialCalc.sheetfieldsxlat[i]];
      if (v) {
         xlt[SocialCalc.sheetfieldsxlatxlt[i]+"sUsed"][v] = 1;
         }
      }

   a = {"height": 1, "hide": 1}; // look at explicit row settings
   for (v in a) {
      for (cr in sheetobj.rowattribs[v]) {
         if (cr > maxrow) maxrow = cr;
         }
      }
   a = {"hide": 1, "width": 1}; // look at explicit col settings
   for (v in a) {
      for (coord in sheetobj.colattribs[v]) {
         cr = SocialCalc.coordToCr(coord+"1");
         if (cr.col > maxcol) maxcol = cr.col;
         }
      }

   for (an=0; an<alist.length; an++) { // go through the attribs we want
      a = alist[an];

      newa = [];
      used = xlt[a+"sUsed"];
      for (v in used) {
         newa.push(sheetobj[a+"s"][v]);
         }
      newa.sort();
      newa.unshift("");

      newxlat = [""];
      ahash = sheetobj[a+"hash"];

      for (i=1; i<newa.length; i++) {

socialcalc/socialcalc-3.js  view on Meta::CPAN


   sheetobj.xlt = xlt; // leave for use by caller

   }

//
// result = SocialCalc.EncodeCellAttributes(sheet, coord)
//
// Returns the cell's attributes in an object, each in the following form:
//
//    attribname: {def: true/false, val: full-value}
//

SocialCalc.EncodeCellAttributes = function(sheet, coord) {

   var value, i, b, bb, parts;
   var result = {};

   var InitAttrib = function(name) {
      result[name] = {def: true, val: ""};
      }

   var InitAttribs = function(namelist) {
      for (var i=0; i<namelist.length; i++) {
         InitAttrib(namelist[i]);
         }
      }

   var SetAttrib = function(name, v) {
      result[name].def = false;
      result[name].val = v || "";
      }

   var SetAttribStar = function(name, v) {
      if (v=="*") return;
      result[name].def = false;
      result[name].val = v;
      }

   var cell = sheet.GetAssuredCell(coord);

   // cellformat: alignhoriz

   InitAttrib("alignhoriz");
   if (cell.cellformat) {
      SetAttrib("alignhoriz", sheet.cellformats[cell.cellformat]);
      }

   // layout: alignvert, padtop, padright, padbottom, padleft

   InitAttribs(["alignvert", "padtop", "padright", "padbottom", "padleft"]);
   if (cell.layout) {
      parts = sheet.layouts[cell.layout].match(/^padding:\s*(\S+)\s+(\S+)\s+(\S+)\s+(\S+);vertical-align:\s*(\S+);/);
      SetAttribStar("padtop", parts[1]);
      SetAttribStar("padright", parts[2]);
      SetAttribStar("padbottom", parts[3]);
      SetAttribStar("padleft", parts[4]);
      SetAttribStar("alignvert", parts[5]);
      }

   // font: fontfamily, fontlook, fontsize

   InitAttribs(["fontfamily", "fontlook", "fontsize"]);
   if (cell.font) {
      parts = sheet.fonts[cell.font].match(/^(\*|\S+? \S+?) (\S+?) (\S.*)$/);
      SetAttribStar("fontfamily", parts[3]);
      SetAttribStar("fontsize", parts[2]);
      SetAttribStar("fontlook", parts[1]);
      }

   // color: textcolor

   InitAttrib("textcolor");
   if (cell.color) {
      SetAttrib("textcolor", sheet.colors[cell.color]);
      }

   // bgcolor: bgcolor

   InitAttrib("bgcolor");
   if (cell.bgcolor) {
      SetAttrib("bgcolor", sheet.colors[cell.bgcolor]);
      }

   // formatting: numberformat, textformat

   InitAttribs(["numberformat", "textformat"]);
   if (cell.nontextvalueformat) {
      SetAttrib("numberformat", sheet.valueformats[cell.nontextvalueformat]);
      }
   if (cell.textvalueformat) {
      SetAttrib("textformat", sheet.valueformats[cell.textvalueformat]);
      }

   // merges: colspan, rowspan

   InitAttribs(["colspan", "rowspan"]);
   SetAttrib("colspan", cell.colspan || 1);
   SetAttrib("rowspan", cell.rowspan || 1);

   // borders: bXthickness, bXstyle, bXcolor for X = t, r, b, and l

   for (i=0; i<4; i++) {
      b = "trbl".charAt(i);
      bb = "b"+b;
      InitAttrib(bb);
      SetAttrib(bb, cell[bb] ? sheet.borderstyles[cell[bb]] : "");
      InitAttrib(bb+"thickness");
      InitAttrib(bb+"style");
      InitAttrib(bb+"color");
      if (cell[bb]) {
         parts = sheet.borderstyles[cell[bb]].match(/(\S+)\s+(\S+)\s+(\S.+)/);
         SetAttrib(bb+"thickness", parts[1]);
         SetAttrib(bb+"style", parts[2]);
         SetAttrib(bb+"color", parts[3]);
         }
      }
 
   // misc: cssc, csss, mod

   InitAttribs(["cssc", "csss", "mod"]);
   SetAttrib("cssc", cell.cssc || ""); 
   SetAttrib("csss", cell.csss || "");
   SetAttrib("mod", cell.mod || "n");

   return result;

   }

socialcalc/socialcalc-3.js  view on Meta::CPAN

   var attribs = sheet.attribs;
   var result = {};

   var InitAttrib = function(name) {
      result[name] = {def: true, val: ""};
      }

   var InitAttribs = function(namelist) {
      for (var i=0; i<namelist.length; i++) {
         InitAttrib(namelist[i]);
         }
      }

   var SetAttrib = function(name, v) {
      result[name].def = false;
      result[name].val = v || value;
      }

   var SetAttribStar = function(name, v) {
      if (v=="*") return;
      result[name].def = false;
      result[name].val = v;
      }

   // sizes: colwidth, rowheight

   InitAttrib("colwidth");
   if (attribs.defaultcolwidth) {
      SetAttrib("colwidth", attribs.defaultcolwidth);
      }

   InitAttrib("rowheight");
   if (attribs.rowheight) {
      SetAttrib("rowheight", attribs.defaultrowheight);
      }

   // cellformat: textalignhoriz, numberalignhoriz

   InitAttrib("textalignhoriz");
   if (attribs.defaulttextformat) {
      SetAttrib("textalignhoriz", sheet.cellformats[attribs.defaulttextformat]);
      }

   InitAttrib("numberalignhoriz");
   if (attribs.defaultnontextformat) {
      SetAttrib("numberalignhoriz", sheet.cellformats[attribs.defaultnontextformat]);
      }

   // layout: alignvert, padtop, padright, padbottom, padleft

   InitAttribs(["alignvert", "padtop", "padright", "padbottom", "padleft"]);
   if (attribs.defaultlayout) {
      parts = sheet.layouts[attribs.defaultlayout].match(/^padding:\s*(\S+)\s+(\S+)\s+(\S+)\s+(\S+);vertical-align:\s*(\S+);/);
      SetAttribStar("padtop", parts[1]);
      SetAttribStar("padright", parts[2]);
      SetAttribStar("padbottom", parts[3]);
      SetAttribStar("padleft", parts[4]);
      SetAttribStar("alignvert", parts[5]);
      }

   // font: fontfamily, fontlook, fontsize

   InitAttribs(["fontfamily", "fontlook", "fontsize"]);
   if (attribs.defaultfont) {
      parts = sheet.fonts[attribs.defaultfont].match(/^(\*|\S+? \S+?) (\S+?) (\S.*)$/);
      SetAttribStar("fontfamily", parts[3]);
      SetAttribStar("fontsize", parts[2]);
      SetAttribStar("fontlook", parts[1]);
      }

   // color: textcolor

   InitAttrib("textcolor");
   if (attribs.defaultcolor) {
      SetAttrib("textcolor", sheet.colors[attribs.defaultcolor]);
      }

   // bgcolor: bgcolor

   InitAttrib("bgcolor");
   if (attribs.defaultbgcolor) {
      SetAttrib("bgcolor", sheet.colors[attribs.defaultbgcolor]);
      }

   // formatting: numberformat, textformat

   InitAttribs(["numberformat", "textformat"]);
   if (attribs.defaultnontextvalueformat) {
      SetAttrib("numberformat", sheet.valueformats[attribs.defaultnontextvalueformat]);
      }
   if (attribs.defaulttextvalueformat) {
      SetAttrib("textformat", sheet.valueformats[attribs.defaulttextvalueformat]);
      }

   // recalc: recalc

   InitAttrib("recalc");
   if (attribs.recalc) {
      SetAttrib("recalc", attribs.recalc);
      }

   return result;

   }

//
// cmdstr = SocialCalc.DecodeCellAttributes(sheet, coord, attribs, range)
//
// Takes cell attributes in an object, each in the following form:
//
//    attribname: {def: true/false, val: full-value}
//
// and returns the sheet commands to make the actual attributes correspond.
// Returns a non-null string if any commands are to be executed, null otherwise.
//
// If range is provided, the commands are executed on the whole range.
//

SocialCalc.DecodeCellAttributes = function(sheet, coord, newattribs, range) {

   var value, b, bb;

   var cell = sheet.GetAssuredCell(coord);

   var changed = false;

   var CheckChanges = function(attribname, oldval, cmdname) {
      var val;
      if (newattribs[attribname]) {
         if (newattribs[attribname].def) {
            val = "";
            }
         else {
            val = newattribs[attribname].val;
            }
         if (val != (oldval || "")) {
            DoCmd(cmdname+" "+val);
            }
         }
      }

   var cmdstr = "";

   var DoCmd = function(str) {
      if (cmdstr) cmdstr += "\n";
      cmdstr += "set "+(range || coord)+" "+str;
      changed = true;
      }

   // cellformat: alignhoriz

   CheckChanges("alignhoriz", sheet.cellformats[cell.cellformat], "cellformat");

   // layout: alignvert, padtop, padright, padbottom, padleft

   if (!newattribs.alignvert.def || !newattribs.padtop.def || !newattribs.padright.def ||
       !newattribs.padbottom.def || !newattribs.padleft.def) {
      value = "padding:" +
         (newattribs.padtop.def ? "* " : newattribs.padtop.val + " ") +
         (newattribs.padright.def ? "* " : newattribs.padright.val + " ") +
         (newattribs.padbottom.def ? "* " : newattribs.padbottom.val + " ") +
         (newattribs.padleft.def ? "*" : newattribs.padleft.val) +
         ";vertical-align:" +
         (newattribs.alignvert.def ? "*;" : newattribs.alignvert.val+";");
      }
   else {
      value = "";
      }

   if (value != (sheet.layouts[cell.layout] || "")) {
      DoCmd("layout "+value);
      }

   // 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[cell.font] || "")) {
      DoCmd("font "+value);
      }

   // color: textcolor

   CheckChanges("textcolor", sheet.colors[cell.color], "color");

   // bgcolor: bgcolor

   CheckChanges("bgcolor", sheet.colors[cell.bgcolor], "bgcolor");

   // formatting: numberformat, textformat

   CheckChanges("numberformat", sheet.valueformats[cell.nontextvalueformat], "nontextvalueformat");

   CheckChanges("textformat", sheet.valueformats[cell.textvalueformat], "textvalueformat");

   // merges: colspan, rowspan - NOT HANDLED: IGNORED!

   // borders: bX for X = t, r, b, and l; bXthickness, bXstyle, bXcolor ignored

   for (var i=0; i<4; i++) {
      b = "trbl".charAt(i);
      bb = "b"+b;
      CheckChanges(bb, sheet.borderstyles[cell[bb]], bb);
      }

   // misc: cssc, csss, mod

   CheckChanges("cssc", cell.cssc, "cssc");

   CheckChanges("csss", cell.csss, "csss");

   if (newattribs.mod) {
      if (newattribs.mod.def) {
         value = "n";
         }
      else {
         value = newattribs.mod.val;
         }
      if (value != (cell.mod || "n")) {
         if (value=="n") value = ""; // restrict to "y" and "" normally
         DoCmd("mod "+value);
         }
      }

   // if any changes return command(s)

   if (changed) {
       return cmdstr;
      }
   else {
      return null;
      }

   }


//
// changed = SocialCalc.DecodeSheetAttributes(sheet, newattribs)
//

socialcalc/socialcalc-3.js  view on Meta::CPAN


   var value;
   var attribs = sheet.attribs;
   var changed = false;

   var CheckChanges = function(attribname, oldval, cmdname) {
      var val;
      if (newattribs[attribname]) {
         if (newattribs[attribname].def) {
            val = "";
            }
         else {
            val = newattribs[attribname].val;
            }
         if (val != (oldval || "")) {
            DoCmd(cmdname+" "+val);
            }
         }
      }

   var cmdstr = "";

   var DoCmd = function(str) {
      if (cmdstr) cmdstr += "\n";
      cmdstr += "set sheet "+str;
      changed = true;
      }

   // sizes: colwidth, rowheight

   CheckChanges("colwidth", attribs.defaultcolwidth, "defaultcolwidth");

   CheckChanges("rowheight", attribs.defaultrowheight, "defaultrowheight");

   // cellformat: textalignhoriz, numberalignhoriz

   CheckChanges("textalignhoriz", sheet.cellformats[attribs.defaulttextformat], "defaulttextformat");

   CheckChanges("numberalignhoriz", sheet.cellformats[attribs.defaultnontextformat], "defaultnontextformat");

   // layout: alignvert, padtop, padright, padbottom, padleft

   if (!newattribs.alignvert.def || !newattribs.padtop.def || !newattribs.padright.def ||
       !newattribs.padbottom.def || !newattribs.padleft.def) {
      value = "padding:" +
         (newattribs.padtop.def ? "* " : newattribs.padtop.val + " ") +
         (newattribs.padright.def ? "* " : newattribs.padright.val + " ") +
         (newattribs.padbottom.def ? "* " : newattribs.padbottom.val + " ") +
         (newattribs.padleft.def ? "*" : newattribs.padleft.val) +
         ";vertical-align:" +
         (newattribs.alignvert.def ? "*;" : newattribs.alignvert.val+";");
      }
   else {
      value = "";
      }

   if (value != (sheet.layouts[attribs.defaultlayout] || "")) {
      DoCmd("defaultlayout "+value);
      }

   // 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/socialcalc-3.js  view on Meta::CPAN

SocialCalc.ExecuteSheetCommand = function(sheet, cmd, saveundo) {

   var cmdstr, cmd1, rest, what, attrib, num, pos, pos2, errortext, undostart, val;
   var cr1, cr2, col, row, cr, cell, newcell;
   var fillright, rowstart, colstart, crbase, rowoffset, coloffset, basecell;
   var clipsheet, cliprange, numcols, numrows, attribtable;
   var colend, rowend, newcolstart, newrowstart, newcolend, newrowend, rownext, colnext, colthis, cellnext;
   var lastrow, lastcol, rowbefore, colbefore, oldformula, oldcr;
   var cols, dirs, lastsortcol, i, sortlist, sortcells, sortvalues, sorttypes;
   var sortfunction, slen, valtype, originalrow, sortedcr;
   var name, v1, v2;
   var cmdextension;

   var attribs = sheet.attribs;
   var changes = sheet.changes;
   var cellProperties = SocialCalc.CellProperties;
   var scc = SocialCalc.Constants;

   var ParseRange =
      function() {
         var prange = SocialCalc.ParseRange(what);
         cr1 = prange.cr1;
         cr2 = prange.cr2;
         if (cr2.col > attribs.lastcol) attribs.lastcol = cr2.col;
         if (cr2.row > attribs.lastrow) attribs.lastrow = cr2.row;
         };

   errortext = "";

   cmdstr = cmd.RestOfStringNoMove();
   if (saveundo) {
      sheet.changes.AddDo(cmdstr);
      }

   cmd1 = cmd.NextToken();

   switch (cmd1) {

      case "set":
         what = cmd.NextToken();
         attrib = cmd.NextToken();
         rest = cmd.RestOfString();
         undostart = "set "+what+" "+attrib;

         if (what=="sheet") {
            sheet.renderneeded = true;
            switch (attrib) {
               case "defaultcolwidth":
                  if (saveundo) changes.AddUndo(undostart, attribs[attrib]);
                  attribs[attrib] = rest;
                  break;
               case "defaultcolor":
               case "defaultbgcolor":
                  if (saveundo) changes.AddUndo(undostart, sheet.GetStyleString("color", attribs[attrib]));
                  attribs[attrib] = sheet.GetStyleNum("color", rest);
                  break;
               case "defaultlayout":
                  if (saveundo) changes.AddUndo(undostart, sheet.GetStyleString("layout", attribs[attrib]));
                  attribs[attrib] = sheet.GetStyleNum("layout", rest);
                  break;
               case "defaultfont":
                  if (saveundo) changes.AddUndo(undostart, sheet.GetStyleString("font", attribs[attrib]));
                  if (rest=="* * *") rest = ""; // all default
                  attribs[attrib] = sheet.GetStyleNum("font", rest);
                  break;
               case "defaulttextformat":
               case "defaultnontextformat":
                  if (saveundo) changes.AddUndo(undostart, sheet.GetStyleString("cellformat", attribs[attrib]));
                  attribs[attrib] = sheet.GetStyleNum("cellformat", rest);
                  break;
               case "defaulttextvalueformat":
               case "defaultnontextvalueformat":
                  if (saveundo) changes.AddUndo(undostart, sheet.GetStyleString("valueformat", attribs[attrib]));
                  attribs[attrib] = sheet.GetStyleNum("valueformat", rest);
                  for (cr in sheet.cells) { // forget all cached display strings
                     delete sheet.cells[cr].displaystring;
                     }
                  break;
               case "lastcol":
               case "lastrow":
                  if (saveundo) changes.AddUndo(undostart, attribs[attrib]-0);
                  num = rest-0;
                  if (typeof num == "number") attribs[attrib] = num > 0 ? num : 1;
                  break;
               case "recalc":
                  if (saveundo) changes.AddUndo(undostart, attribs[attrib]);
                  if (rest == "off") {
                     attribs.recalc = rest; // manual recalc, not auto
                     }
                  else { // all values other than "off" mean "on"
                     delete attribs.recalc;
                     }
                  break;
               default:
                  errortext = scc.s_escUnknownSheetCmd+cmdstr;
                  break;
               }
            }

         else if (/(^[A-Z])([A-Z])?(:[A-Z][A-Z]?){0,1}$/i.test(what)) { // col attributes
            sheet.renderneeded = true;
            what = what.toUpperCase();
            pos = what.indexOf(":");
            if (pos>=0) {
               cr1 = SocialCalc.coordToCr(what.substring(0,pos)+"1");
               cr2 = SocialCalc.coordToCr(what.substring(pos+1)+"1");
               }
            else {
               cr1 = SocialCalc.coordToCr(what+"1");
               cr2 = cr1;
               }
            for (col=cr1.col; col <= cr2.col; col++) {
               if (attrib=="width") {
                  cr = SocialCalc.rcColname(col);
                  if (saveundo) changes.AddUndo("set "+cr+" width", sheet.colattribs.width[cr]);
                  if (rest.length > 0 ) {
                     sheet.colattribs.width[cr] = rest;
                     }
                  else {
                     delete sheet.colattribs.width[cr];
                     }
                  }
               }
            }

socialcalc/socialcalc-3.js  view on Meta::CPAN

                     attribs.needsrecalc = "yes";
                     }
                  else if (attrib=="formula") { // set coord formula formula-body-less-initial-=
                     cell.datavalue = 0; // until recalc
                     delete cell.errors;
                     cell.datatype = "f";
                     cell.valuetype = "e#N/A"; // until recalc
                     cell.formula = rest;
                     delete cell.displaystring;
                     delete cell.parseinfo;
                     attribs.needsrecalc = "yes";
                     }
                  else if (attrib=="constant") { // set coord constant type numeric-value source-text
                     pos = rest.indexOf(" ");
                     pos2 = rest.substring(pos+1).indexOf(" ");
                     cell.datavalue = rest.substring(pos+1,pos+1+pos2)-0;
                     cell.valuetype = rest.substring(0,pos);
                     if (cell.valuetype.charAt(0)=="e") { // error
                        cell.errors = cell.valuetype.substring(1);
                        }
                     else {
                        delete cell.errors;
                        }
                     cell.datatype = "c";
                     cell.formula = rest.substring(pos+pos2+2);
                     delete cell.displaystring;
                     delete cell.parseinfo;
                     attribs.needsrecalc = "yes";
                     }
                  else if (attrib=="empty") { // erase value
                     cell.datavalue = "";
                     delete cell.errors;
                     cell.datatype = null;
                     cell.formula = "";
                     cell.valuetype = "b";
                     delete cell.displaystring;
                     delete cell.parseinfo;
                     attribs.needsrecalc = "yes";
                     }
                  else if (attrib=="all") { // set coord all :this:val1:that:val2...
                     if (rest.length>0) {
                        cell = new SocialCalc.Cell(cr);
                        sheet.CellFromStringParts(cell, rest.split(":"), 1);
                        sheet.cells[cr] = cell;
                        }
                     else {
                        delete sheet.cells[cr];
                        }
                     attribs.needsrecalc = "yes";
                     }
                  else if (/^b[trbl]$/.test(attrib)) { // set coord bt 1px solid black
                     cell[attrib] = sheet.GetStyleNum("borderstyle", rest);
                     sheet.renderneeded = true; // affects more than just one cell
                     }
                  else if (attrib=="color" || attrib=="bgcolor") {
                     cell[attrib] = sheet.GetStyleNum("color", rest);
                     }
                  else if (attrib=="layout" || attrib=="cellformat") {
                     cell[attrib] = sheet.GetStyleNum(attrib, rest);
                     }
                  else if (attrib=="font") { // set coord font style weight size family
                     if (rest=="* * *") rest = "";
                     cell[attrib] = sheet.GetStyleNum("font", rest);
                     }
                  else if (attrib=="textvalueformat" || attrib=="nontextvalueformat") {
                     cell[attrib] = sheet.GetStyleNum("valueformat", rest);
                     delete cell.displaystring;
                     }
                  else if (attrib=="cssc") {
                     rest = rest.replace(/[^a-zA-Z0-9\-]/g, "");
                     cell.cssc = rest;
                     }
                  else if (attrib=="csss") {
                     rest = rest.replace(/\n/g, "");
                     cell.csss = rest;
                     }
                  else if (attrib=="mod") {
                     rest = rest.replace(/[^yY]/g, "").toLowerCase();
                     cell.mod = rest;
                     }
                  else if (attrib=="comment") {
                     cell.comment = SocialCalc.decodeFromSave(rest);
                     }
                  else {
                     errortext = scc.s_escUnknownSetCoordCmd+cmdstr;
                     }
                  }
               }

            }
         break;

      case "merge":
         sheet.renderneeded = true;
         what = cmd.NextToken();
         rest = cmd.RestOfString();
         ParseRange();
         cell=sheet.GetAssuredCell(cr1.coord);
         if (saveundo) changes.AddUndo("unmerge "+cr1.coord);

         if (cr2.col > cr1.col) cell.colspan = cr2.col - cr1.col + 1;
         else delete cell.colspan;
         if (cr2.row > cr1.row) cell.rowspan = cr2.row - cr1.row + 1;
         else delete cell.rowspan;

         sheet.changedrendervalues = true;

         break;

      case "unmerge":
         sheet.renderneeded = true;
         what = cmd.NextToken();
         rest = cmd.RestOfString();
         ParseRange();
         cell=sheet.GetAssuredCell(cr1.coord);
         if (saveundo) changes.AddUndo("merge "+cr1.coord+":"+SocialCalc.crToCoord(cr1.col+(cell.colspan||1)-1, cr1.row+(cell.rowspan||1)-1));

         delete cell.colspan;
         delete cell.rowspan;

         sheet.changedrendervalues = true;

         break;

socialcalc/socialcalc-3.js  view on Meta::CPAN

SocialCalc.Clipboard = {

   // properties:

   clipboard:  "" // empty or string in save format with "copiedfrom:" set to a range

   }


// *************************************
//
// RenderContext class:
//
// *************************************

SocialCalc.RenderContext = function(sheetobj) {

   var parts, num, s;
   var attribs = sheetobj.attribs;
   var scc = SocialCalc.Constants;

   // properties:

   this.sheetobj = sheetobj;
   this.hideRowsCols = false; // Rendering with panes only works with "false"
                              // !!!! Note: not implemented yet in rendering, just saved as an attribute
   this.showGrid = false;
   this.showRCHeaders = false;
   this.rownamewidth = scc.defaultRowNameWidth;
   this.pixelsPerRow = scc.defaultAssumedRowHeight;

   this.cellskip = {}; // if present, coord of cell covering this cell
   this.coordToCR = {}; // for cells starting spans, coordToCR[coord]={row:row, col:col}
   this.colwidth = []; // precomputed column widths, taking into account defaults
   this.totalwidth = 0; // precomputed total table width

   this.rowpanes = []; // for each pane, {first: firstrow, last: lastrow}
   this.colpanes = []; // for each pane, {first: firstrow, last: lastrow}
   this.maxcol=0; // max col and row to display, adding long spans, etc.
   this.maxrow=0;

   this.highlights = {}; // for each cell with special display: coord:highlightType (see this.highlightTypes)
   this.cursorsuffix = ""; // added to highlights[cr]=="cursor" to get type to lookup

   this.highlightTypes = // attributes to change when highlit
      {
         cursor: {style: scc.defaultHighlightTypeCursorStyle, className: scc.defaultHighlightTypeCursorClass},
         range: {style: scc.defaultHighlightTypeRangeStyle, className: scc.defaultHighlightTypeRangeClass},
         cursorinsertup: {style: "color:#FFF;backgroundColor:#A6A6A6;backgroundRepeat:repeat-x;backgroundPosition:top left;backgroundImage:url("+scc.defaultImagePrefix+"cursorinsertup.gif);", className: scc.defaultHighlightTypeCursorClass},
         cursorinsertleft: {style: "color:#FFF;backgroundColor:#A6A6A6;backgroundRepeat:repeat-y;backgroundPosition:top left;backgroundImage:url("+scc.defaultImagePrefix+"cursorinsertleft.gif);", className: scc.defaultHighlightTypeCursorClass},
         range2: {style: "color:#000;backgroundColor:#FFF;backgroundImage:url("+scc.defaultImagePrefix+"range2.gif);", className: ""}
      }

   this.cellIDprefix = scc.defaultCellIDPrefix; // if non-null, each cell will render with an ID

   this.defaultlinkstyle = null; // default linkstyle object (allows you to pass values to link renderer)
   this.defaultHTMLlinkstyle = {type: "html"}; // default linkstyle for standalone HTML

   // constants:

   this.defaultfontstyle = scc.defaultCellFontStyle;
   this.defaultfontsize = scc.defaultCellFontSize;
   this.defaultfontfamily = scc.defaultCellFontFamily;

   this.defaultlayout = scc.defaultCellLayout;

   this.defaultpanedividerwidth = scc.defaultPaneDividerWidth;
   this.defaultpanedividerheight = scc.defaultPaneDividerHeight;

   this.gridCSS = scc.defaultGridCSS;

   this.commentClassName = scc.defaultCommentClass; // for cells with non-blank comments when this.showGrid is true
   this.commentCSS = scc.defaultCommentStyle; // any combination of classnames and styles may be used
   this.commentNoGridClassName = scc.defaultCommentNoGridClass; // for cells when this.showGrid is false
   this.commentNoGridCSS = scc.defaultCommentNoGridStyle; // any combination of classnames and styles may be used

   this.classnames = // any combination of classnames and explicitStyles can be used
      {
         colname: scc.defaultColnameClass,
         rowname: scc.defaultRownameClass,
         selectedcolname: scc.defaultSelectedColnameClass,
         selectedrowname: scc.defaultSelectedRownameClass,
         upperleft: scc.defaultUpperLeftClass,
         skippedcell: scc.defaultSkippedCellClass,
         panedivider: scc.defaultPaneDividerClass
      };

   this.explicitStyles = // these may be used so you won't need a stylesheet with the classnames
      {
         colname: scc.defaultColnameStyle,
         rowname: scc.defaultRownameStyle,
         selectedcolname: scc.defaultSelectedColnameStyle,
         selectedrowname: scc.defaultSelectedRownameStyle,
         upperleft: scc.defaultUpperLeftStyle,
         skippedcell: scc.defaultSkippedCellStyle,
         panedivider: scc.defaultPaneDividerStyle
      };

   // processed info about cell skipping

   this.cellskip = null;
   this.needcellskip = true;

   // precomputed values, filling in defaults indicated by "*"

   this.fonts=[]; // for each fontnum, {style: fs, weight: fw, size: fs, family: ff}
   this.layouts=[]; // for each layout, "padding:Tpx Rpx Bpx Lpx;vertical-align:va;"

   this.needprecompute = true; // need to call PrecomputeSheetFontsAndLayouts

   // if have a sheet object, initialize constants and precomputed values

   if (sheetobj) {
      this.rowpanes[0] = {first: 1, last: attribs.lastrow};
      this.colpanes[0] = {first: 1, last: attribs.lastcol};

      }
   else throw scc.s_rcMissingSheet;

   }

// Methods:

SocialCalc.RenderContext.prototype.PrecomputeSheetFontsAndLayouts = function() {SocialCalc.PrecomputeSheetFontsAndLayouts(this);};
SocialCalc.RenderContext.prototype.CalculateCellSkipData = function() {SocialCalc.CalculateCellSkipData(this);};
SocialCalc.RenderContext.prototype.CalculateColWidthData = function() {SocialCalc.CalculateColWidthData(this);};
SocialCalc.RenderContext.prototype.SetRowPaneFirstLast = function(panenum, first, last) {this.rowpanes[panenum]={first:first, last:last};};
SocialCalc.RenderContext.prototype.SetColPaneFirstLast = function(panenum, first, last) {this.colpanes[panenum]={first:first, last:last};};
SocialCalc.RenderContext.prototype.CoordInPane = function(coord, rowpane, colpane) {return SocialCalc.CoordInPane(this, coord, rowpane, colpane);};
SocialCalc.RenderContext.prototype.CellInPane = function(row, col, rowpane, colpane) {return SocialCalc.CellInPane(this, row, col, rowpane, colpane);};
SocialCalc.RenderContext.prototype.InitializeTable = function(tableobj) {SocialCalc.InitializeTable(this, tableobj);};
SocialCalc.RenderContext.prototype.RenderSheet = function(oldtable, linkstyle) {return SocialCalc.RenderSheet(this, oldtable, linkstyle);};
SocialCalc.RenderContext.prototype.RenderColGroup = function() {return SocialCalc.RenderColGroup(this);};
SocialCalc.RenderContext.prototype.RenderColHeaders = function() {return SocialCalc.RenderColHeaders(this);};
SocialCalc.RenderContext.prototype.RenderSizingRow = function() {return SocialCalc.RenderSizingRow(this);};
SocialCalc.RenderContext.prototype.RenderRow = function(rownum, rowpane, linkstyle) {return SocialCalc.RenderRow(this, rownum, rowpane, linkstyle);};
SocialCalc.RenderContext.prototype.RenderSpacingRow = function() {return SocialCalc.RenderSpacingRow(this);};
SocialCalc.RenderContext.prototype.RenderCell = function(rownum, colnum, rowpane, colpane, noElement, linkstyle)
      {return SocialCalc.RenderCell(this, rownum, colnum, rowpane, colpane, noElement, linkstyle);};

// Functions:

SocialCalc.PrecomputeSheetFontsAndLayouts = function(context) {

   var defaultfont, parts, layoutre, dparts, sparts, num, s, i;
   var sheetobj = context.sheetobj;
   var attribs =  sheetobj.attribs;

   if (attribs.defaultfont) {
      defaultfont = sheetobj.fonts[attribs.defaultfont];
      defaultfont = defaultfont.replace(/^\*/,SocialCalc.Constants.defaultCellFontStyle);
      defaultfont = defaultfont.replace(/(.+)\*(.+)/,"$1"+SocialCalc.Constants.defaultCellFontSize+"$2");
      defaultfont = defaultfont.replace(/\*$/,SocialCalc.Constants.defaultCellFontFamily);
      parts=defaultfont.match(/^(\S+? \S+?) (\S+?) (\S.*)$/);
      context.defaultfontstyle = parts[1];
      context.defaultfontsize = parts[2];
      context.defaultfontfamily = parts[3];
      }

   for (num=1; num<sheetobj.fonts.length; num++) { // precompute fonts by filling in the *'s
      s=sheetobj.fonts[num];
      s=s.replace(/^\*/,context.defaultfontstyle);
      s=s.replace(/(.+)\*(.+)/,"$1"+context.defaultfontsize+"$2");
      s=s.replace(/\*$/,context.defaultfontfamily);
      parts=s.match(/^(\S+?) (\S+?) (\S+?) (\S.*)$/);
      context.fonts[num] = {style: parts[1], weight: parts[2], size: parts[3], family: parts[4]};

      }

   layoutre = /^padding:\s*(\S+)\s+(\S+)\s+(\S+)\s+(\S+);vertical-align:\s*(\S+);/;
   dparts = SocialCalc.Constants.defaultCellLayout.match(layoutre); // get built-in defaults

   if (attribs.defaultlayout) {
      sparts = sheetobj.layouts[attribs.defaultlayout].match(layoutre); // get sheet defaults, if set
      }
   else {
      sparts = ["", "*", "*", "*", "*", "*"];
      }

   for (num=1; num<sheetobj.layouts.length; num++) { // precompute layouts by filling in the *'s
      s=sheetobj.layouts[num];
      parts = s.match(layoutre);
      for (i=1; i<=5; i++) {
         if (parts[i]=="*") {
            parts[i] = (sparts[i] != "*" ? sparts[i] : dparts[i]); // if *, sheet default or built-in
            }
         }
      context.layouts[num] = "padding:"+parts[1]+" "+parts[2]+" "+parts[3]+" "+parts[4]+
         ";vertical-align:"+parts[5]+";";
      }

   context.needprecompute = false;

   }

SocialCalc.CalculateCellSkipData = function(context) {

   var row, col, coord, cell, contextcell, colspan, rowspan, skiprow, skipcol, skipcoord;

   var sheetobj=context.sheetobj;
   var sheetrowattribs=sheetobj.rowattribs;
   var sheetcolattribs=sheetobj.colattribs;
   context.maxrow=0;
   context.maxcol=0;
   context.cellskip = {}; // reset

   // Calculate cellskip data

   for (row=1; row<=sheetobj.attribs.lastrow; row++) {
      for (col=1; col<=sheetobj.attribs.lastcol; col++) { // look for spans and set cellskip for skipped cells
         coord=SocialCalc.crToCoord(col, row);
         cell=sheetobj.cells[coord];
         // don't look at undefined cells (they have no spans) or skipped cells
         if (cell===undefined || context.cellskip[coord]) continue;
         colspan=cell.colspan || 1;
         rowspan=cell.rowspan || 1;
         if (colspan>1 || rowspan>1) {
            for (skiprow=row; skiprow<row+rowspan; skiprow++) {
               for (skipcol=col; skipcol<col+colspan; skipcol++) { // do the setting on individual cells
                  skipcoord=SocialCalc.crToCoord(skipcol,skiprow);
                  if (skipcoord==coord) { // for coord, remember row and col
                     context.coordToCR[coord]={row: row, col: col};
                     }
                  else { // for other cells, flag with coord of here
                     context.cellskip[skipcoord]=coord;
                     }

socialcalc/socialcalc-3.js  view on Meta::CPAN

      if (context.CoordInPane(context.cellskip[coord], rowpane, colpane)) {
         return null; // span starts in this pane -- so just skip
         }
      result=noElement ? SocialCalc.CreatePseudoElement() : document.createElement("td"); // span start is scrolled away, so make a special cell
      if (context.classnames.skippedcell) result.className=context.classnames.skippedcell;
      if (context.explicitStyles.skippedcell) result.style.cssText=context.explicitStyles.skippedcell;
      result.innerHTML="&nbsp;"; // put something there so height is OK
      // !!! Really need to add borders in case there isn't anything else shown in the pane to get height
      return result;
      }

   result=noElement ? SocialCalc.CreatePseudoElement() : document.createElement("td");

   if (context.cellIDprefix) {
      result.id = context.cellIDprefix+coord;
      }

   cell=sheetobj.cells[coord];

   if (!cell) {
      cell=new SocialCalc.Cell(coord);
      }

   sheetattribs=sheetobj.attribs;
   scc=SocialCalc.Constants;

   if (cell.colspan>1) {
      span=1;
      for (num=1; num<cell.colspan; num++) {
          if (sheetobj.colattribs.hide[SocialCalc.rcColname(colnum+num)]!="yes" &&
                context.CellInPane(rownum, colnum+num, rowpane, colpane)) {
             span++;
             }
          }
      result.colSpan=span;
      }

   if (cell.rowspan>1) {
      span=1;
      for (num=1; num<cell.rowspan; num++) {
          if (sheetobj.rowattribs.hide[(rownum+num)+""]!="yes" &&
                context.CellInPane(rownum+num, colnum, rowpane, colpane))
             span++;
         }
      result.rowSpan=span;
      }

   if (cell.displaystring==undefined) { // cache the display value
      cell.displaystring = SocialCalc.FormatValueForDisplay(sheetobj, cell.datavalue, coord, (linkstyle || context.defaultlinkstyle));
      }
   result.innerHTML = cell.displaystring;

   num=cell.layout || sheetattribs.defaultlayout;
   if (num) {
      stylestr+=context.layouts[num]; // use precomputed layout with "*"'s filled in
      }
   else {
      stylestr+=scc.defaultCellLayout;
      }

   num=cell.font || sheetattribs.defaultfont;
   if (num) { // get expanded font strings in context
      t = context.fonts[num]; // do each - plain "font:" style sets all sorts of other values, too (Safari font-stretch problem on cssText)
      stylestr+="font-style:"+t.style+";font-weight:"+t.weight+";font-size:"+t.size+";font-family:"+t.family+";";
      }
   else {
      if (scc.defaultCellFontSize) {
         stylestr+="font-size:"+scc.defaultCellFontSize+";";
         }
      if (scc.defaultCellFontFamily) {
         stylestr+="font-family:"+scc.defaultCellFontFamily+";";
         }
      }

   num=cell.color || sheetattribs.defaultcolor;
   if (num) stylestr+="color:"+sheetobj.colors[num]+";";

   num=cell.bgcolor || sheetattribs.defaultbgcolor;
   if (num) stylestr+="background-color:"+sheetobj.colors[num]+";";

   num=cell.cellformat;
   if (num) {
      stylestr+="text-align:"+sheetobj.cellformats[num]+";";
      }
   else {
      t=cell.valuetype.charAt(0);
      if (t=="t") {
         num=sheetattribs.defaulttextformat;
         if (num) stylestr+="text-align:"+sheetobj.cellformats[num]+";";
         }
      else if (t="n") {
         num=sheetattribs.defaultnontextformat;
         if (num) {
            stylestr+="text-align:"+sheetobj.cellformats[num]+";";
            }
         else {
            stylestr+="text-align:right;";
            }
         }
      else stylestr+="text-align:left;";
      }

   num=cell.bt;
   if (num) stylestr+="border-top:"+sheetobj.borderstyles[num]+";";

   num=cell.br;
   if (num) stylestr+="border-right:"+sheetobj.borderstyles[num]+";";
   else if (context.showGrid) {
      if (context.CellInPane(rownum, colnum+(cell.colspan || 1), rowpane, colpane))
         t=SocialCalc.crToCoord(colnum+(cell.colspan || 1), rownum);
      else t="nomatch";
      if (context.cellskip[t]) t=context.cellskip[t];
      if (!sheetobj.cells[t] || !sheetobj.cells[t].bl)
         stylestr+="border-right:"+context.gridCSS;
      }

   num=cell.bb;
   if (num) stylestr+="border-bottom:"+sheetobj.borderstyles[num]+";";
   else if (context.showGrid) {
      if (context.CellInPane(rownum+(cell.rowspan || 1), colnum, rowpane, colpane))
         t=SocialCalc.crToCoord(colnum, rownum+(cell.rowspan || 1));
      else t="nomatch";
      if (context.cellskip[t]) t=context.cellskip[t];
      if (!sheetobj.cells[t] || !sheetobj.cells[t].bt)
         stylestr+="border-bottom:"+context.gridCSS;
      }

   num=cell.bl;
   if (num) stylestr+="border-left:"+sheetobj.borderstyles[num]+";";

   if (cell.comment) {



( run in 2.578 seconds using v1.01-cache-2.11-cpan-c221a9de4ec )