// @ts-nocheck
// Generated by scripts/build.js from audreyt/socialcalc. Do not hand-edit.
import { ShimNode } from './dom-shim';

export function createSocialCalcFactory() {
  const host = {
    setTimeout: function(cb) { cb(); return 0; },
    clearTimeout: function() {}
  };

  let SocialCalc;

  (function() {
    var navigator = { language: "", userAgent: "" };
    // Force the UMD's Node branch so nothing lands on the real
    // globalThis.SocialCalc. define stays undefined so AMD is also skipped.
    var module = { exports: {} };
    var define = undefined;

    // Opening half of a UMD IIFE. Inlined in build.ts — not a standalone module.
//
// Taken from https://github.com/umdjs/umd/blob/master/templates/returnExports.js
// (c) by The UMD contributors
// MIT License: https://github.com/umdjs/umd/blob/master/LICENSE.md
(function (root, factory) {
  'use strict';
  // Evaluate once and fan out to both deliveries:
  //   * browser / globalThis (root.SocialCalc) — always set
  //   * CommonJS / Node (module.exports) — when present
  // AMD was dropped from this wrapper: the npm package is its
  // canonical entry point today, and AMD loaders (RequireJS, Dojo
  // legacy) have been unmaintained for years. Anyone still wiring
  // AMD can wrap the CommonJS module themselves.
  var exported = factory.call(root, root);
  root.SocialCalc = exported;
  if (typeof module === 'object' && module && module.exports) {
    module.exports = exported;
  }
})(this, function (window) {
  'use strict';
  // Factory-local bag. Formerly created in socialcalcconstants as
  // `var SocialCalc; if (!SocialCalc) SocialCalc = {}`. Hoisted here so
  // converted TypeScript sources can typecheck against the ambient
  // `declare namespace SocialCalc` without a runtime `var SocialCalc`
  // that would shadow/collapse the namespace during `tsc`.
  var SocialCalc = {};

  // In-place TypeScript conversion of socialcalcconstants.js (SocialCalc global script).
  // Ambient API types live in socialcalcconstants.d.ts (referenced by dist/SocialCalc.d.ts).
  // Vite+ strips types with Oxc before UMD concat — no runtime tax.
  // Runtime SocialCalc bag is created in module-wrapper-top.js (UMD factory-local).
  //
  /*
// The module of the SocialCalc package with customizable constants, strings, etc.
// This is where most of the common localizations are done.
//
// (c) Copyright 2008, 2009, 2010 Socialtext, Inc.
// All Rights Reserved.
//
// The contents of this file are subject to the Artistic License 2.0; you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at http://socialcalc.org/licenses/al-20/.
//
// Some of the other files in the SocialCalc package are licensed under
// different licenses. Please note the licenses of the modules you use.
//
// Code History:
//
// Initially coded by Dan Bricklin of Software Garden, Inc., for Socialtext, Inc.
// Based in part on the SocialCalc 1.1.0 code written in Perl.
// The SocialCalc 1.1.0 code was:
//    Portions (c) Copyright 2005, 2006, 2007 Software Garden, Inc.
//    All Rights Reserved.
//    Portions (c) Copyright 2007 Socialtext, Inc.
//    All Rights Reserved.
// The Perl SocialCalc started as modifications to the wikiCalc(R) program, version 1.0.
// wikiCalc 1.0 was written by Software Garden, Inc.
// Unless otherwise specified, referring to "SocialCalc" in comments refers to this
// JavaScript version of the code, not the SocialCalc Perl code.
//
*/

  // SocialCalc bag: created by module-wrapper-top.js inside the UMD factory.

  // Implementation-only mutable view for progressive init of ambient `const` members.

  const ConstantsRoot = SocialCalc;
  ConstantsRoot.Constants = {
    cellDataType: {
      v: 'value',
      n: 'value',
      t: 'text',
      f: 'formula',
      c: 'constant',
    },
    textdatadefaulttype: 't',
    s_BrowserNotSupported: 'Browser not supported.',
    s_InternalError: 'Internal SocialCalc error (probably an internal bug): ',
    s_pssUnknownColType: 'Unknown col type item',
    s_pssUnknownRowType: 'Unknown row type item',
    s_pssUnknownLineType: 'Unknown line type',
    s_cfspUnknownCellType: 'Unknown cell type item',
    doCanonicalizeSheet: true,
    s_escUnknownSheetCmd: 'Unknown sheet command: ',
    s_escUnknownSetCoordCmd: 'Unknown set coord command: ',
    s_escUnknownCmd: 'Unknown command: ',
    s_caccCircRef: 'Circular reference to ',
    defaultRowNameWidth: '30',
    defaultAssumedRowHeight: 15,
    defaultCellIDPrefix: 'cell_',
    defaultCellLayout: 'padding:2px 2px 1px 2px;vertical-align:top;',
    defaultCellFontStyle: 'normal normal',
    defaultCellFontSize: 'small',
    defaultCellFontFamily: 'Verdana,Arial,Helvetica,sans-serif',
    defaultPaneDividerWidth: '3',
    defaultPaneDividerHeight: '3',
    defaultGridCSS: '1px solid #C0C0C0;',
    defaultCommentClass: '',
    defaultCommentStyle:
      'background-repeat:no-repeat;background-position:top right;background-image:url(images/sc-commentbg.gif);',
    defaultCommentNoGridClass: '',
    defaultCommentNoGridStyle: '',
    defaultReadonlyClass: '',
    defaultReadonlyStyle:
      'background-repeat:no-repeat;background-position:top right;background-image:url(images/sc-lockbg.gif);',
    defaultReadonlyNoGridClass: '',
    defaultReadonlyNoGridStyle: '',
    defaultReadonlyComment: 'Locked cell',
    defaultColWidth: '80',
    defaultMinimumColWidth: 10,
    defaultHighlightTypeCursorClass: '',
    defaultHighlightTypeCursorStyle: 'color:#FFF;backgroundColor:#A6A6A6;',
    defaultHighlightTypeRangeClass: '',
    defaultHighlightTypeRangeStyle: 'color:#000;backgroundColor:#E5E5E5;',
    defaultColnameClass: '',
    defaultColnameStyle:
      'overflow:visible;font-size:small;text-align:center;color:#FFFFFF;background-color:#808080;',
    defaultSelectedColnameClass: '',
    defaultSelectedColnameStyle:
      'overflow:visible;font-size:small;text-align:center;color:#FFFFFF;background-color:#404040;',
    defaultRownameClass: '',
    defaultRownameStyle:
      'position:relative;overflow:visible;font-size:small;text-align:right;color:#FFFFFF;background-color:#808080;',
    defaultSelectedRownameClass: '',
    defaultSelectedRownameStyle:
      'position:relative;overflow:visible;font-size:small;text-align:right;color:#FFFFFF;background-color:#404040;',
    defaultUpperLeftClass: '',
    defaultUpperLeftStyle: 'font-size:small;',
    defaultSkippedCellClass: '',
    defaultSkippedCellStyle: 'font-size:small;background-color:#CCC',
    defaultPaneDividerClass: '',
    defaultPaneDividerStyle: 'font-size:small;background-color:#C0C0C0;padding:0px;',
    defaultUnhideLeftClass: '',
    defaultUnhideLeftStyle:
      'float:right;width:9px;height:12px;cursor:pointer;background-image:url(images/sc-unhideleft.gif);padding:0;',
    defaultUnhideRightClass: '',
    defaultUnhideRightStyle:
      'float:left;width:9px;height:12px;cursor:pointer;background-image:url(images/sc-unhideright.gif);padding:0;',
    defaultUnhideTopClass: '',
    defaultUnhideTopStyle:
      'float:left;left:1px;position:absolute;bottom:-4px;width:12px;height:9px;cursor:pointer;background-image:url(images/sc-unhidetop.gif);padding:0;',
    defaultUnhideBottomClass: '',
    defaultUnhideBottomStyle:
      'float:left;width:12px;height:9px;cursor:pointer;background-image:url(images/sc-unhidebottom.gif);padding:0;',
    defaultColResizeBarClass: 'col-resize-bar',
    defaultRowResizeBarClass: 'row-resize-bar',
    s_rcMissingSheet: 'Render Context must have a sheet object',
    defaultLinkFormatString:
      '<span style="font-size:smaller;text-decoration:none !important;background-color:#66B;color:#FFF;">Link</span>',
    defaultPageLinkFormatString:
      '<span style="font-size:smaller;text-decoration:none !important;background-color:#66B;color:#FFF;">Page</span>',
    defaultFormatp: '#,##0.0%',
    defaultFormatc: '[$$]#,##0.00',
    defaultFormatdt: 'd-mmm-yyyy h:mm:ss',
    defaultFormatd: 'd-mmm-yyyy',
    defaultFormatt: '[h]:mm:ss',
    defaultDisplayTRUE: 'TRUE',
    defaultDisplayFALSE: 'FALSE',
    defaultImagePrefix: 'images/sc_',
    defaultTableEditorIDPrefix: 'te_',
    defaultPageUpDnAmount: 15,
    AllowCtrlS: true,
    defaultTableControlThickness: 20,
    cteGriddivClass: '',
    s_statusline_executing: 'Executing...',
    s_statusline_displaying: 'Displaying...',
    s_statusline_ordering: 'Ordering...',
    s_statusline_calculating: 'Calculating...',
    s_statusline_calculatingls: 'Calculating... Loading Sheet...',
    s_statusline_doingserverfunc: 'doing server function ',
    s_statusline_incell: ' in cell ',
    s_statusline_calcstart: 'Calculation start...',
    s_statusline_sum: 'SUM',
    s_statusline_recalcneeded: '<span style="color:#999;">(Recalc needed)</span>',
    s_statusline_circref: '<span style="color:red;">Circular reference: ',
    s_statusline_sendemail: 'Sending Email ',
    s_inputboxdisplaymultilinetext: '[Multi-line text: Click icon on right to edit]',
    defaultInputEchoClass: '',
    defaultInputEchoStyle:
      'filter:alpha(opacity=90);opacity:.9;backgroundColor:#FFD;border:1px solid #884;' +
      'fontSize:small;padding:2px 10px 1px 2px;cursor:default;',
    defaultInputEchoPromptClass: '',
    defaultInputEchoPromptStyle:
      'filter:alpha(opacity=90);opacity:.9;backgroundColor:#FFD;' +
      'borderLeft:1px solid #884;borderRight:1px solid #884;borderBottom:1px solid #884;' +
      'fontSize:small;fontStyle:italic;padding:2px 10px 1px 2px;cursor:default;',
    defaultInputEchoHintClass: '',
    defaultInputEchoHintStyle:
      'filter:alpha(opacity=80);opacity:.8;backgroundColor:#884;border:1px solid #884;' +
      'fontSize:small;fontWeight:bold;padding:2px 2px 2px 2px;color:#FFF;position:absolute;top:-20px;cursor:default;',
    ietUnknownFunction: 'Unknown function ',
    CH_radius1: 29,
    CH_radius2: 41,
    s_CHfillAllTooltip: 'Fill Contents and Formats Down/Right',
    s_CHfillContentsTooltip: 'Fill Contents Only Down/Right',
    s_CHmovePasteAllTooltip: 'Move Contents and Formats',
    s_CHmovePasteContentsTooltip: 'Move Contents Only',
    s_CHmoveInsertAllTooltip: 'Slide Contents and Formats within Row/Col',
    s_CHmoveInsertContentsTooltip: 'Slide Contents within Row/Col',
    s_CHindicatorOperationLookup: {
      Fill: 'Fill',
      FillC: 'Fill Contents',
      Move: 'Move',
      MoveI: 'Slide',
      MoveC: 'Move Contents',
      MoveIC: 'Slide Contents',
    },
    s_CHindicatorDirectionLookup: {
      Down: ' Down',
      Right: ' Right',
      Horizontal: ' Horizontal',
      Vertical: ' Vertical',
    },
    defaultTCSliderThickness: 9,
    defaultTCButtonThickness: 20,
    defaultTCThumbThickness: 15,
    TCmainStyle: 'backgroundColor:#EEE;',
    TCmainClass: '',
    TCendcapStyle: 'backgroundColor:#FFF;',
    TCendcapClass: '',
    TCpanesliderClass: 'tc-paneslider',
    s_panesliderTooltiph: 'Drag to lock pane vertically',
    s_panesliderTooltipv: 'Drag to lock pane horizontally',
    TClessbuttonStyle: 'backgroundColor:#AAA;',
    TClessbuttonClass: '',
    TClessbuttonRepeatWait: 300,
    TClessbuttonRepeatInterval: 20,
    TCmorebuttonStyle: 'backgroundColor:#AAA;',
    TCmorebuttonClass: '',
    TCmorebuttonRepeatWait: 300,
    TCmorebuttonRepeatInterval: 20,
    TCscrollareaStyle: 'backgroundColor:#DDD;',
    TCscrollareaClass: '',
    TCscrollareaRepeatWait: 500,
    TCscrollareaRepeatInterval: 100,
    TCthumbClass: '',
    TCthumbStyle: 'backgroundColor:#CCC;',
    TCPStrackinglineClass: 'tracklingine',
    TCPStrackinglineStyle: 'overflow:hidden;position:absolute;zIndex:100;',
    TCPStrackinglineThickness: '2px',
    TCTDFSthumbstatusvClass: '',
    TCTDFSthumbstatusvStyle:
      'height:20px;width:auto;border:3px solid #808080;overflow:hidden;' +
      'backgroundColor:#FFF;fontSize:small;position:absolute;zIndex:100;',
    TCTDFSthumbstatushClass: '',
    TCTDFSthumbstatushStyle:
      'height:20px;width:auto;border:1px solid black;padding:2px;' +
      'backgroundColor:#FFF;fontSize:small;position:absolute;zIndex:100;',
    TCTDFSthumbstatusrownumClass: '',
    TCTDFSthumbstatusrownumStyle:
      'color:#FFF;background-color:#808080;font-size:small;white-space:nowrap;padding:3px;',
    TCTDFStopOffsetv: 0,
    TCTDFSleftOffsetv: -80,
    s_TCTDFthumbstatusPrefixv: 'Row ',
    TCTDFStopOffseth: -30,
    TCTDFSleftOffseth: 0,
    s_TCTDFthumbstatusPrefixh: 'Col ',
    TooltipOffsetX: 2,
    TooltipOffsetY: 10,
    TDpopupElementClass: '',
    TDpopupElementStyle:
      'border:1px solid black;padding:1px 2px 2px 2px;textAlign:center;backgroundColor:#FFF;' +
      'fontSize:7pt;fontFamily:Verdana,Arial,Helvetica,sans-serif;' +
      'position:absolute;width:auto;zIndex:110;',
    SCToolbarbackground: 'background-color:#ffffff;',
    SCTabbackground: 'background-color:#CCC;',
    SCTabselectedCSS:
      'font-size:small;padding:6px 30px 6px 8px;color:#FFF;background-color:#404040;cursor:default;border-right:1px solid #CCC;',
    SCTabplainCSS:
      'font-size:small;padding:6px 30px 6px 8px;color:#FFF;background-color:#808080;cursor:default;border-right:1px solid #CCC;',
    SCToolbartext: 'font-size:x-small;font-weight:bold;color:#888',
    SCFormulabarheight: 30,
    SCStatuslineheight: 20,
    SCStatuslineCSS: 'font-size:10px;padding:3px 0px;',
    SCFormatNumberFormats:
      '[cancel]:|[break]:|%loc!Default!:|[custom]:|%loc!Automatic!:general|%loc!Auto w/ commas!:[,]General|[break]:|' +
      '00:00|000:000|0000:0000|00000:00000|[break]:|%loc!Formula!:formula|%loc!Hidden!:hidden|[newcol]:' +
      '1234:0|1,234:#,##0|1,234.5:#,##0.0|1,234.56:#,##0.00|1,234.567:#,##0.000|1,234.5678:#,##0.0000|' +
      '[break]:|1,234%:#,##0%|1,234.5%:#,##0.0%|1,234.56%:#,##0.00%|' +
      '[newcol]:|$1,234:$#,##0|$1,234.5:$#,##0.0|$1,234.56:$#,##0.00|[break]:|' +
      '(1,234):#,##0_);(#,##0)|(1,234.5):#,##0.0_);(#,##0.0)|(1,234.56):#,##0.00_);(#,##0.00)|[break]:|' +
      '($1,234):$#,##0_);($#,##0)|($1,234.5):$#,##0.0_);($#,##0.0)|($1,234.56):$#,##0.00_);($#,##0.00)|' +
      '[newcol]:|1/4/06:m/d/yy|01/04/2006:mm/dd/yyyy|2006-01-04:yyyy-mm-dd|4-Jan-06:d-mmm-yy|04-Jan-2006:dd-mmm-yyyy|January 4, 2006:mmmm d, yyyy|' +
      '[break]:|1\\c23:h:mm|1\\c23 PM:h:mm AM/PM|1\\c23\\c45:h:mm:ss|01\\c23\\c45:hh:mm:ss|26\\c23 (h\\cm):[hh]:mm|69\\c45 (m\\cs):[mm]:ss|69 (s):[ss]|' +
      '[newcol]:|2006-01-04 01\\c23\\c45:yyyy-mm-dd hh:mm:ss|January 4, 2006:mmmm d, yyyy hh:mm:ss|Wed:ddd|Wednesday:dddd|',
    SCFormatTextFormats:
      '[cancel]:|[break]:|%loc!Default!:|[custom]:|%loc!Automatic!:general|%loc!Plain Text!:text-plain|' +
      'HTML:text-html|%loc!Wikitext!:text-wiki|%loc!Link!:text-link|%loc!Formula!:formula|%loc!Hidden!:hidden|',
    SCFormatPadsizes:
      '[cancel]:|[break]:|%loc!Default!:|[custom]:|%loc!No padding!:0px|' +
      '[newcol]:|1 pixel:1px|2 pixels:2px|3 pixels:3px|4 pixels:4px|5 pixels:5px|' +
      '6 pixels:6px|7 pixels:7px|8 pixels:8px|[newcol]:|9 pixels:9px|10 pixels:10px|11 pixels:11px|' +
      '12 pixels:12px|13 pixels:13px|14 pixels:14px|16 pixels:16px|' +
      '18 pixels:18px|[newcol]:|20 pixels:20px|22 pixels:22px|24 pixels:24px|28 pixels:28px|36 pixels:36px|',
    SCFormatFontsizes:
      '[cancel]:|[break]:|%loc!Default!:|[custom]:|X-Small:x-small|Small:small|Medium:medium|Large:large|X-Large:x-large|' +
      '[newcol]:|6pt:6pt|7pt:7pt|8pt:8pt|9pt:9pt|10pt:10pt|11pt:11pt|12pt:12pt|14pt:14pt|16pt:16pt|' +
      '[newcol]:|18pt:18pt|20pt:20pt|22pt:22pt|24pt:24pt|28pt:28pt|36pt:36pt|48pt:48pt|72pt:72pt|' +
      '[newcol]:|8 pixels:8px|9 pixels:9px|10 pixels:10px|11 pixels:11px|' +
      '12 pixels:12px|13 pixels:13px|14 pixels:14px|[newcol]:|16 pixels:16px|' +
      '18 pixels:18px|20 pixels:20px|22 pixels:22px|24 pixels:24px|28 pixels:28px|36 pixels:36px|',
    SCFormatFontfamilies:
      '[cancel]:|[break]:|%loc!Default!:|[custom]:|Verdana:Verdana,Arial,Helvetica,sans-serif|' +
      "Arial:arial,helvetica,sans-serif|Courier:'Courier New',Courier,monospace|",
    SCFormatFontlook:
      '[cancel]:|[break]:|%loc!Default!:|%loc!Normal!:normal normal|%loc!Bold!:normal bold|%loc!Italic!:italic normal|' +
      '%loc!Bold Italic!:italic bold',
    SCFormatTextAlignhoriz:
      '[cancel]:|[break]:|%loc!Default!:|%loc!Left!:left|%loc!Center!:center|%loc!Right!:right|',
    SCFormatNumberAlignhoriz:
      '[cancel]:|[break]:|%loc!Default!:|%loc!Left!:left|%loc!Center!:center|%loc!Right!:right|',
    SCFormatAlignVertical:
      '[cancel]:|[break]:|%loc!Default!:|%loc!Top!:top|%loc!Middle!:middle|%loc!Bottom!:bottom|',
    SCFormatColwidth:
      '[cancel]:|[break]:|%loc!Default!:|[custom]:|[newcol]:|' +
      '20 pixels:20|40:40|60:60|80:80|100:100|120:120|140:140|160:160|' +
      '[newcol]:|180 pixels:180|200:200|220:220|240:240|260:260|280:280|300:300|',
    SCFormatRecalc: '[cancel]:|[break]:|%loc!Auto!:|%loc!Manual!:off|',
    SCFormatUserMaxCol:
      '[cancel]:|[break]:|%loc!Default!:|[custom]:|[newcol]:|' +
      'Unlimited:0|10:10|20:20|30:30|40:40|50:50|60:60|80:80|100:100|',
    SCFormatUserMaxRow:
      '[cancel]:|[break]:|%loc!Default!:|[custom]:|[newcol]:|' +
      'Unlimited:0|10:10|20:20|30:30|40:40|50:50|60:60|80:80|100:100|',
    ISCButtonNormalBackground: 'transparent',
    ISCButtonBorderNormal: 'none',
    ISCButtonBorderHover: 'none',
    ISCButtonBorderDown: 'none',
    ISCButtonDownBackground: '#888',
    s_PopupListCancel: '[Cancel]',
    s_PopupListCustom: 'Custom',
    s_loc_align_center: 'Align Center',
    s_loc_align_left: 'Align Left',
    s_loc_align_right: 'Align Right',
    s_loc_alignment: 'Alignment',
    s_loc_audit: 'Audit',
    s_loc_audit_trail_this_session: 'Audit Trail This Session',
    s_loc_auto: 'Auto',
    s_loc_auto_sum: 'Auto Sum',
    s_loc_auto_wX_commas: 'Auto w/ commas',
    s_loc_automatic: 'Automatic',
    s_loc_background: 'Background',
    s_loc_bold: 'Bold',
    s_loc_bold_XampX_italics: 'Bold &amp; Italics',
    s_loc_bold_italic: 'Bold Italic',
    s_loc_borders: 'Borders',
    s_loc_borders_off: 'Borders Off',
    s_loc_borders_on: 'Borders On',
    s_loc_bottom: 'Bottom',
    s_loc_bottom_border: 'Bottom Border',
    s_loc_cell_settings: 'CELL SETTINGS',
    s_loc_csv_format: 'CSV format',
    s_loc_cancel: 'Cancel',
    s_loc_category: 'Category',
    s_loc_center: 'Center',
    s_loc_clear: 'Clear',
    s_loc_clear_socialcalc_clipboard: 'Clear SocialCalc Clipboard',
    s_loc_clipboard: 'Clipboard',
    s_loc_color: 'Color',
    s_loc_column_: 'Column ',
    s_loc_comment: 'Comment',
    s_loc_copy: 'Copy',
    s_loc_custom: 'Custom',
    s_loc_cut: 'Cut',
    s_loc_default: 'Default',
    s_loc_default_alignment: 'Default Alignment',
    s_loc_default_column_width: 'Default Column Width',
    s_loc_default_font: 'Default Font',
    s_loc_default_format: 'Default Format',
    s_loc_default_padding: 'Default Padding',
    s_loc_delete: 'Delete',
    s_loc_delete_column: 'Delete Column',
    s_loc_delete_contents: 'Delete Cell Contents',
    s_loc_delete_row: 'Delete Row',
    s_loc_description: 'Description',
    s_loc_display_clipboard_in: 'Display Clipboard in',
    s_loc_down: 'Down',
    s_loc_edit: 'Edit',
    s_loc_existing_names: 'Existing Names',
    s_loc_family: 'Family',
    s_loc_fill_down: 'Fill Down',
    s_loc_fill_right: 'Fill Right',
    s_loc_font: 'Font',
    s_loc_format: 'Format',
    s_loc_formula: 'Formula',
    s_loc_function_list: 'Function List',
    s_loc_functions: 'Functions',
    s_loc_grid: 'Grid',
    s_loc_hidden: 'Hidden',
    s_loc_hide_column: 'Hide Column',
    s_loc_hide_row: 'Hide Row',
    s_loc_horizontal: 'Horizontal',
    s_loc_insert_column: 'Insert Column Before',
    s_loc_insert_row: 'Insert Row Before',
    s_loc_italic: 'Italic',
    s_loc_last_sort: 'Last Sort',
    s_loc_left: 'Left',
    s_loc_left_border: 'Left Border',
    s_loc_link: 'Link',
    s_loc_link_input_box: 'Link Input Box',
    s_loc_list: 'List',
    s_loc_load_socialcalc_clipboard_with_this: 'Load SocialCalc Clipboard With This',
    s_loc_lock_cell: 'Lock Cell',
    s_loc_major_sort: 'Major Sort',
    s_loc_manual: 'Manual',
    s_loc_merge_cells: 'Merge/Unmerge Cells',
    s_loc_middle: 'Middle',
    s_loc_minor_sort: 'Minor Sort',
    s_loc_move_insert: 'Move Insert',
    s_loc_move_paste: 'Move Paste',
    s_loc_multiXline_input_box: 'Multi-line Input Box',
    s_loc_name: 'Name',
    s_loc_names: 'Names',
    s_loc_no_padding: 'No padding',
    s_loc_normal: 'Normal',
    s_loc_number: 'Number',
    s_loc_number_horizontal: 'Number Horizontal',
    s_loc_ok: 'OK',
    s_loc_padding: 'Padding',
    s_loc_page_name: 'Page Name',
    s_loc_paste: 'Paste',
    s_loc_paste_formats: 'Paste Formats',
    s_loc_plain_text: 'Plain Text',
    s_loc_recalc: 'Recalc',
    s_loc_recalculation: 'Recalculation',
    s_loc_redo: 'Redo',
    s_loc_right: 'Right',
    s_loc_right_border: 'Right Border',
    s_loc_sheet_settings: 'SHEET SETTINGS',
    s_loc_save: 'Save',
    s_loc_save_to: 'Save to',
    s_loc_set_cell_contents: 'Set Cell Contents',
    s_loc_set_cells_to_sort: 'Set Cells To Sort',
    s_loc_set_value_to: 'Set Value To',
    s_loc_set_to_link_format: 'Set to Link format',
    s_loc_setXclear_move_from: 'Set/Clear Move From',
    s_loc_show_cell_settings: 'Show Cell Settings',
    s_loc_show_sheet_settings: 'Show Sheet Settings',
    s_loc_show_in_new_browser_window: 'Show in new browser window',
    s_loc_size: 'Size',
    s_loc_socialcalcXsave_format: 'SocialCalc-save format',
    s_loc_sort: 'Sort',
    s_loc_sort_: 'Sort ',
    s_loc_sort_cells: 'Sort Cells',
    s_loc_swap_colors: 'Swap Colors',
    s_loc_tabXdelimited_format: 'Tab-delimited format',
    s_loc_text: 'Text',
    s_loc_text_horizontal: 'Text Horizontal',
    s_loc_this_is_aXbrXsample: 'This is a<br>sample',
    s_loc_top: 'Top',
    s_loc_top_border: 'Top Border',
    s_loc_undone_steps: 'UNDONE STEPS',
    s_loc_url: 'URL',
    s_loc_undo: 'Undo',
    s_loc_unlock_cell: 'Unlock Cell',
    s_loc_unmerge_cells: 'Unmerge Cells',
    s_loc_up: 'Up',
    s_loc_value: 'Value',
    s_loc_vertical: 'Vertical',
    s_loc_wikitext: 'Wikitext',
    s_loc_workspace: 'Workspace',
    s_loc_XnewX: '[New]',
    s_loc_XnoneX: '[None]',
    s_loc_Xselect_rangeX: '[select range]',
    SVStatuslineheight: 20,
    SVStatuslineCSS: 'font-size:10px;padding:3px 0px;',
    FormatNumber_separatorchar: ',',
    FormatNumber_decimalchar: '.',
    FormatNumber_defaultCurrency: '$',
    s_FormatNumber_daynames: [
      'Sunday',
      'Monday',
      'Tuesday',
      'Wednesday',
      'Thursday',
      'Friday',
      'Saturday',
    ],
    s_FormatNumber_daynames3: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    s_FormatNumber_monthnames: [
      'January',
      'February',
      'March',
      'April',
      'May',
      'June',
      'July',
      'August',
      'September',
      'October',
      'November',
      'December',
    ],
    s_FormatNumber_monthnames3: [
      'Jan',
      'Feb',
      'Mar',
      'Apr',
      'May',
      'Jun',
      'Jul',
      'Aug',
      'Sep',
      'Oct',
      'Nov',
      'Dec',
    ],
    s_FormatNumber_am: 'AM',
    s_FormatNumber_am1: 'A',
    s_FormatNumber_pm: 'PM',
    s_FormatNumber_pm1: 'P',
    s_parseerrexponent: 'Improperly formed number exponent',
    s_parseerrchar: 'Unexpected character in formula',
    s_parseerrstring: 'Improperly formed string',
    s_parseerrspecialvalue: 'Improperly formed special value',
    s_parseerrtwoops: 'Error in formula (two operators inappropriately in a row)',
    s_parseerrmissingopenparen: 'Missing open parenthesis in list with comma(s). ',
    s_parseerrcloseparennoopen: 'Closing parenthesis without open parenthesis. ',
    s_parseerrmissingcloseparen: 'Missing close parenthesis. ',
    s_parseerrmissingoperand: 'Missing operand. ',
    s_parseerrerrorinformula: 'Error in formula.',
    s_calcerrerrorvalueinformula: 'Error value in formula',
    s_parseerrerrorinformulabadval: 'Error in formula resulting in bad value',
    s_formularangeresult: 'Formula results in range value:',
    s_calcerrnumericnan: 'Formula results in a bad numeric value',
    s_calcerrnumericoverflow: 'Numeric overflow',
    s_sheetunavailable: 'Sheet unavailable:',
    s_calcerrcellrefmissing: 'Cell reference missing when expected.',
    s_calcerrsheetnamemissing: 'Sheet name missing when expected.',
    s_circularnameref: 'Circular name reference to name',
    s_calcerrunknownname: 'Unknown name',
    s_calcerrincorrectargstofunction: 'Incorrect arguments to function',
    s_sheetfuncunknownfunction: 'Unknown function',
    s_sheetfunclnarg: 'LN argument must be greater than 0',
    s_sheetfunclog10arg: 'LOG10 argument must be greater than 0',
    s_sheetfunclogsecondarg: 'LOG second argument must be numeric greater than 0',
    s_sheetfunclogfirstarg: 'LOG first argument must be greater than 0',
    s_sheetfuncroundsecondarg: 'ROUND second argument must be numeric',
    s_sheetfuncddblife: 'DDB life must be greater than 1',
    s_sheetfuncslnlife: 'SLN life must be greater than 1',
    s_fdef_ABS: 'Absolute value function. ',
    s_fdef_ACOS: 'Trigonometric arccosine function. ',
    s_fdef_AND: 'True if all arguments are true. ',
    s_fdef_ASIN: 'Trigonometric arcsine function. ',
    s_fdef_ATAN: 'Trigonometric arctan function. ',
    s_fdef_ATAN2: 'Trigonometric arc tangent function (result is in radians). ',
    s_fdef_AVERAGE: 'Averages the values. ',
    s_fdef_CHOOSE: 'Returns the value specified by the index. The values may be ranges of cells. ',
    s_fdef_COLUMNS: 'Returns the number of columns in the range. ',
    s_fdef_COS: 'Trigonometric cosine function (value is in radians). ',
    s_fdef_CONCAT: 'Join Together Text & Values to Create a Single Combined Text String. ',
    s_fdef_CONCATENATE: 'Join Together Text & Values to Create a Single Combined Text String. ',
    s_fdef_COUNT: 'Counts the number of numeric values, not blank, text, or error. ',
    s_fdef_COUNTA: 'Counts the number of non-blank values. ',
    s_fdef_COUNTBLANK: 'Counts the number of blank values. (Note: "" is not blank.) ',
    s_fdef_COUNTIF:
      'Counts the number of number of cells in the range that meet the criteria. The criteria may be a value ("x", 15, 1+3) or a test (>25). ',
    s_fdef_DATE:
      'Returns the appropriate date value given numbers for year, month, and day. For example: DATE(2006,2,1) for February 1, 2006. Note: In this program, day "1" is December 31, 1899 and the year 1900 is not a leap year. Some programs use January 1, 1900, as day "1" and treat 1900 as a leap year. In both cases, though, dates on or after March 1, 1900, are the same. ',
    s_fdef_DAVERAGE:
      'Averages the values in the specified field in records that meet the criteria. ',
    s_fdef_DAY: 'Returns the day of month for a date value. ',
    s_fdef_DCOUNT:
      'Counts the number of numeric values, not blank, text, or error, in the specified field in records that meet the criteria. ',
    s_fdef_DCOUNTA:
      'Counts the number of non-blank values in the specified field in records that meet the criteria. ',
    s_fdef_DDB:
      'Returns the amount of depreciation at the given period of time (the default factor is 2 for double-declining balance).   ',
    s_fdef_DEGREES: 'Converts value in radians into degrees. ',
    s_fdef_DGET:
      'Returns the value of the specified field in the single record that meets the criteria. ',
    s_fdef_DMAX:
      'Returns the maximum of the numeric values in the specified field in records that meet the criteria. ',
    s_fdef_DMIN:
      'Returns the minimum of the numeric values in the specified field in records that meet the criteria. ',
    s_fdef_DPRODUCT:
      'Returns the result of multiplying the numeric values in the specified field in records that meet the criteria. ',
    s_fdef_DSTDEV:
      'Returns the sample standard deviation of the numeric values in the specified field in records that meet the criteria. ',
    s_fdef_DSTDEVP:
      'Returns the standard deviation of the numeric values in the specified field in records that meet the criteria. ',
    s_fdef_DSUM:
      'Returns the sum of the numeric values in the specified field in records that meet the criteria. ',
    s_fdef_DVAR:
      'Returns the sample variance of the numeric values in the specified field in records that meet the criteria. ',
    s_fdef_DVARP:
      'Returns the variance of the numeric values in the specified field in records that meet the criteria. ',
    s_fdef_EVEN: 'Rounds the value up in magnitude to the nearest even integer. ',
    s_fdef_EXACT: 'Returns "true" if the values are exactly the same, including case, type, etc. ',
    s_fdef_EXP: 'Returns e raised to the value power. ',
    s_fdef_FACT: 'Returns factorial of the value. ',
    s_fdef_FALSE: 'Returns the logical value "false". ',
    s_fdef_FIND:
      'Returns the starting position within string2 of the first occurrence of string1 at or after "start". If start is omitted, 1 is assumed. ',
    s_fdef_FV:
      'Returns the future value of repeated payments of money invested at the given rate for the specified number of periods, with optional present value (default 0) and payment type (default 0 = at end of period, 1 = beginning of period). ',
    s_fdef_HLOOKUP:
      'Look for the matching value for the given value in the range and return the corresponding value in the cell specified by the row offset. If rangelookup is 1 (the default) and not 0, match if within numeric brackets (match<=value) instead of exact match. ',
    s_fdef_HOUR: 'Returns the hour portion of a time or date/time value. ',
    s_fdef_IF:
      'Results in true-value if logical-expression is TRUE or non-zero, otherwise results in false-value. ',
    s_fdef_INDEX:
      'Returns a cell or range reference for the specified row and column in the range. If range is 1-dimensional, then only one of rownum or colnum are needed. If range is 2-dimensional and rownum or colnum are zero, a reference to the range of just the specified column or row is returned. You can use the returned reference value in a range, e.g., sum(A1:INDEX(A2:A10,4)). ',
    s_fdef_INT: 'Returns the value rounded down to the nearest integer (towards -infinity). ',
    s_fdef_IRR:
      'Returns the interest rate at which the cash flows in the range have a net present value of zero. Uses an iterative process that will return #NUM! error if it does not converge. There may be more than one possible solution. Providing the optional guess value may help in certain situations where it does not converge or finds an inappropriate solution (the default guess is 10%). ',
    s_fdef_ISBLANK: 'Returns "true" if the value is a reference to a blank cell. ',
    s_fdef_ISERR: 'Returns "true" if the value is of type "Error" but not "NA". ',
    s_fdef_ISERROR: 'Returns "true" if the value is of type "Error". ',
    s_fdef_ISLOGICAL: 'Returns "true" if the value is of type "Logical" (true/false). ',
    s_fdef_ISNA: 'Returns "true" if the value is the error type "NA". ',
    s_fdef_ISNONTEXT: 'Returns "true" if the value is not of type "Text". ',
    s_fdef_ISNUMBER: 'Returns "true" if the value is of type "Number" (including logical values). ',
    s_fdef_ISTEXT: 'Returns "true" if the value is of type "Text". ',
    s_fdef_LEFT:
      'Returns the specified number of characters from the text value. If count is omitted, 1 is assumed. ',
    s_fdef_LEN: 'Returns the number of characters in the text value. ',
    s_fdef_LN: 'Returns the natural logarithm of the value. ',
    s_fdef_LOG: 'Returns the logarithm of the value using the specified base. ',
    s_fdef_LOG10: 'Returns the base 10 logarithm of the value. ',
    s_fdef_LOWER: 'Returns the text value with all uppercase characters converted to lowercase. ',
    s_fdef_MATCH:
      'Look for the matching value for the given value in the range and return position (the first is 1) in that range. If rangelookup is 1 (the default) and not 0, match if within numeric brackets (match<=value) instead of exact match. If rangelookup is -1, act like 1 but the bracket is match>=value. ',
    s_fdef_MAX: 'Returns the maximum of the numeric values. ',
    s_fdef_MID:
      'Returns the specified number of characters from the text value starting from the specified position. ',
    s_fdef_MIN: 'Returns the minimum of the numeric values. ',
    s_fdef_MINUTE: 'Returns the minute portion of a time or date/time value. ',
    s_fdef_MOD: 'Returns the remainder of the first value divided by the second. ',
    s_fdef_MONTH: 'Returns the month part of a date value. ',
    s_fdef_N: 'Returns the value if it is a numeric value otherwise an error. ',
    s_fdef_NA: 'Returns the #N/A error value which propagates through most operations. ',
    s_fdef_NOT: 'Returns FALSE if value is true, and TRUE if it is false. ',
    s_fdef_NOW: 'Returns the current date/time. ',
    s_fdef_NPER:
      'Returns the number of periods at which payments invested each period at the given rate with optional future value (default 0) and payment type (default 0 = at end of period, 1 = beginning of period) has the given present value. ',
    s_fdef_NPV:
      'Returns the net present value of cash flows (which may be individual values and/or ranges) at the given rate. The flows are positive if income, negative if paid out, and are assumed at the end of each period. ',
    s_fdef_ODD: 'Rounds the value up in magnitude to the nearest odd integer. ',
    s_fdef_OR: 'True if any argument is true ',
    s_fdef_PI: 'The value 3.1415926... ',
    s_fdef_PMT:
      'Returns the amount of each payment that must be invested at the given rate for the specified number of periods to have the specified present value, with optional future value (default 0) and payment type (default 0 = at end of period, 1 = beginning of period). ',
    s_fdef_POWER: 'Returns the first value raised to the second value power. ',
    s_fdef_PRODUCT: 'Returns the result of multiplying the numeric values. ',
    s_fdef_PROPER:
      'Returns the text value with the first letter of each word converted to uppercase and the others to lowercase. ',
    s_fdef_PV:
      'Returns the present value of the given number of payments each invested at the given rate, with optional future value (default 0) and payment type (default 0 = at end of period, 1 = beginning of period). ',
    s_fdef_RADIANS: 'Converts value in degrees into radians. ',
    s_fdef_RATE:
      'Returns the rate at which the given number of payments each invested at the given rate has the specified present value, with optional future value (default 0) and payment type (default 0 = at end of period, 1 = beginning of period). Uses an iterative process that will return #NUM! error if it does not converge. There may be more than one possible solution. Providing the optional guess value may help in certain situations where it does not converge or finds an inappropriate solution (the default guess is 10%). ',
    s_fdef_REPLACE:
      'Returns text1 with the specified number of characters starting from the specified position replaced by text2. ',
    s_fdef_REPT: 'Returns the text repeated the specified number of times. ',
    s_fdef_RIGHT:
      'Returns the specified number of characters from the text value starting from the end. If count is omitted, 1 is assumed. ',
    s_fdef_ROUND:
      'Rounds the value to the specified number of decimal places. If precision is negative, then round to powers of 10. The default precision is 0 (round to integer). ',
    s_fdef_ROWS: 'Returns the number of rows in the range. ',
    s_fdef_SECOND:
      'Returns the second portion of a time or date/time value (truncated to an integer). ',
    s_fdef_SIN: 'Trigonometric sine function (value is in radians) ',
    s_fdef_SLN:
      'Returns the amount of depreciation at each period of time using the straight-line method. ',
    s_fdef_SQRT: 'Square root of the value ',
    s_fdef_STDEV: 'Returns the sample standard deviation of the numeric values. ',
    s_fdef_STDEVP: 'Returns the standard deviation of the numeric values. ',
    s_fdef_SUBSTITUTE:
      'Returns text1 with the all occurrences of oldtext replaced by newtext. If "occurrence" is present, then only that occurrence is replaced. ',
    s_fdef_SUM:
      'Adds the numeric values. The values to the sum function may be ranges in the form similar to A1:B5. ',
    s_fdef_SUMIF:
      'Sums the numeric values of cells in the range that meet the criteria. The criteria may be a value ("x", 15, 1+3) or a test (>25). If range2 is present, then range1 is tested and the corresponding range2 value is summed. ',
    s_fdef_SUMIFS:
      'Sums the numeric values of cells in the sum_range that meet the multiple criteria. The criteria may be a value ("x", 15, 1+3) or a test (>25).  ',
    s_fdef_SYD: "Depreciation by Sum of Year's Digits method. ",
    s_fdef_T: 'Returns the text value or else a null string. ',
    s_fdef_TAN: 'Trigonometric tangent function (value is in radians) ',
    s_fdef_TIME: 'Returns the time value given the specified hour, minute, and second. ',
    s_fdef_TODAY:
      'Returns the current date (an integer). Note: In this program, day "1" is December 31, 1899 and the year 1900 is not a leap year. Some programs use January 1, 1900, as day "1" and treat 1900 as a leap year. In both cases, though, dates on or after March 1, 1900, are the same. ',
    s_fdef_TRIM: 'Returns the text value with leading, trailing, and repeated spaces removed. ',
    s_fdef_TRUE: 'Returns the logical value "true". ',
    s_fdef_TRUNC:
      'Truncates the value to the specified number of decimal places. If precision is negative, truncate to powers of 10. ',
    s_fdef_UPPER: 'Returns the text value with all lowercase characters converted to uppercase. ',
    s_fdef_VALUE:
      'Converts the specified text value into a numeric value. Various forms that look like numbers (including digits followed by %, forms that look like dates, etc.) are handled. This may not handle all of the forms accepted by other spreadsheets and may be locale dependent. ',
    s_fdef_VAR: 'Returns the sample variance of the numeric values. ',
    s_fdef_VARP: 'Returns the variance of the numeric values. ',
    s_fdef_VLOOKUP:
      'Look for the matching value for the given value in the range and return the corresponding value in the cell specified by the column offset. If rangelookup is 1 (the default) and not 0, match if within numeric brackets (match>=value) instead of exact match. ',
    s_fdef_WEEKDAY:
      'Returns the day of week specified by the date value. If type is 1 (the default), Sunday is day and Saturday is day 7. If type is 2, Monday is day 1 and Sunday is day 7. If type is 3, Monday is day 0 and Sunday is day 6. ',
    s_fdef_YEAR: 'Returns the year part of a date value. ',
    s_fdef_SUMPRODUCT:
      'Sums the pairwise products of 2 or more ranges. The ranges must be of equal length.',
    s_fdef_CEILING:
      'Rounds the given number up to the nearest integer or multiple of significance. Significance is the value to whose multiple of ten the value is to be rounded up (.01, .1, 1, 10, etc.)',
    s_fdef_FLOOR:
      'Rounds the given number down to the nearest multiple of significance. Significance is the value to whose multiple of ten the number is to be rounded down (.01, .1, 1, 10, etc.)',
    s_farg_v: 'value',
    s_farg_vn: 'value1, value2, ...',
    s_farg_xy: 'valueX, valueY',
    s_farg_choose: 'index, value1, value2, ...',
    s_farg_range: 'range',
    s_farg_rangec: 'range, criteria',
    s_farg_date: 'year, month, day',
    s_farg_dfunc: 'databaserange, fieldname, criteriarange',
    s_farg_ddb: 'cost, salvage, lifetime, period, [factor]',
    s_farg_find: 'string1, string2, [start]',
    s_farg_fv: 'rate, n, payment, [pv, [paytype]]',
    s_farg_hlookup: 'value, range, row, [rangelookup]',
    s_farg_iffunc: 'logical-expression, true-value, [false-value]',
    s_farg_index: 'range, rownum, colnum',
    s_farg_irr: 'range, [guess]',
    s_farg_tc: 'text, count',
    s_farg_log: 'value, base',
    s_farg_match: 'value, range, [rangelookup]',
    s_farg_mid: 'text, start, length',
    s_farg_nper: 'rate, payment, pv, [fv, [paytype]]',
    s_farg_npv: 'rate, value1, value2, ...',
    s_farg_pmt: 'rate, n, pv, [fv, [paytype]]',
    s_farg_pv: 'rate, n, payment, [fv, [paytype]]',
    s_farg_rate: 'n, payment, pv, [fv, [paytype, [guess]]]',
    s_farg_replace: 'text1, start, length, text2',
    s_farg_vp: 'value, [precision]',
    s_farg_valpre: 'value, precision',
    s_farg_csl: 'cost, salvage, lifetime',
    s_farg_cslp: 'cost, salvage, lifetime, period',
    s_farg_subs: 'text1, oldtext, newtext, [occurrence]',
    s_farg_sumif: 'range1, criteria, [range2]',
    s_farg_hms: 'hour, minute, second',
    s_farg_txt: 'text',
    s_farg_vlookup: 'value, range, col, [rangelookup]',
    s_farg_weekday: 'date, [type]',
    s_farg_dt: 'date',
    s_farg_rangen: 'range1, range2, ...',
    s_farg_vsig: 'value, [significance]',
    function_classlist: [
      'all',
      'stat',
      'lookup',
      'datetime',
      'financial',
      'test',
      'math',
      'text',
      'gui',
      'action',
    ],
    s_fclass_all: 'All',
    s_fclass_stat: 'Statistics',
    s_fclass_lookup: 'Lookup',
    s_fclass_datetime: 'Date & Time',
    s_fclass_financial: 'Financial',
    s_fclass_test: 'Test',
    s_fclass_math: 'Math',
    s_fclass_text: 'Text',
    s_fclass_action: 'Email & Copy',
    s_fclass_gui: 'Button & Input',
    lastone: null,
  };
  ConstantsRoot.ConstantsDefaultClasses = {
    defaultComment: '',
    defaultCommentNoGrid: '',
    defaultHighlightTypeCursor: '',
    defaultHighlightTypeRange: '',
    defaultColname: '',
    defaultSelectedColname: '',
    defaultRowname: '',
    defaultSelectedRowname: '',
    defaultUpperLeft: '',
    defaultSkippedCell: '',
    defaultPaneDivider: '',
    cteGriddiv: '',
    defaultInputEcho: {
      classname: '',
      style: 'filter:alpha(opacity=90);opacity:.9;',
    },
    TCmain: '',
    TCendcap: '',
    TCpaneslider: '',
    TClessbutton: '',
    TCmorebutton: '',
    TCscrollarea: '',
    TCthumb: '',
    TCPStrackingline: '',
    TCTDFSthumbstatus: '',
    TDpopupElement: '',
  };
  ConstantsRoot.ConstantsSetClasses = function (prefix) {
    var defaults = SocialCalc.ConstantsDefaultClasses;
    var scc = SocialCalc.Constants;
    var item;
    prefix = prefix || '';
    for (item in defaults) {
      if (typeof defaults[item] == 'string') {
        scc[item + 'Class'] = prefix + (defaults[item] || item);
        if (scc[item + 'Style'] !== undefined) {
          scc[item + 'Style'] = '';
        }
      } else if (typeof defaults[item] == 'object') {
        scc[item + 'Class'] = prefix + (defaults[item].classname || item);
        scc[item + 'Style'] = defaults[item].style;
      }
    }
  };
  ConstantsRoot.ConstantsSetImagePrefix = function (imagePrefix) {
    var scc = SocialCalc.Constants;
    var oldPrefix = scc.defaultImagePrefix;
    var oldHyphen = oldPrefix.endsWith('_') ? oldPrefix.slice(0, -1) + '-' : oldPrefix;
    var newHyphen = imagePrefix.endsWith('_') ? imagePrefix.slice(0, -1) + '-' : imagePrefix;
    for (var item in scc) {
      if (typeof scc[item] == 'string') {
        var s = scc[item];
        if (oldPrefix) {
          s = s.split(oldPrefix).join(imagePrefix);
        }
        if (oldHyphen && oldHyphen !== oldPrefix) {
          s = s.split(oldHyphen).join(newHyphen);
        }
        scc[item] = s;
      }
    }
    scc.defaultImagePrefix = imagePrefix;
  };

  // In-place TypeScript conversion of socialcalc-3.js (SocialCalc global script).
  // Ambient API types live in socialcalc-3.d.ts (referenced by dist/SocialCalc.d.ts).
  // Vite+ strips types with Oxc before UMD concat — no runtime tax.
  // Typechecked core after removing @ts-nocheck.

  // The main SocialCalc code module of the SocialCalc package
  //
  /*
// (c) Copyright 2010 Socialtext, Inc.
// All Rights Reserved.
//
// The contents of this file are subject to the Artistic License 2.0; you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at http://socialcalc.org/licenses/al-20/.
//
// Some of the other files in the SocialCalc package are licensed under
// different licenses. Please note the licenses of the modules you use.
//
// Code History:
//
// Initially coded by Dan Bricklin of Software Garden, Inc., for Socialtext, Inc.
// Based in part on the SocialCalc 1.1.0 code written in Perl.
// The SocialCalc 1.1.0 code was:
//    Portions (c) Copyright 2005, 2006, 2007 Software Garden, Inc.
//    All Rights Reserved.
//    Portions (c) Copyright 2007 Socialtext, Inc.
//    All Rights Reserved.
// The Perl SocialCalc started as modifications to the wikiCalc(R) program, version 1.0.
// wikiCalc 1.0 was written by Software Garden, Inc.
// Unless otherwise specified, referring to "SocialCalc" in comments refers to this
// JavaScript version of the code, not the SocialCalc Perl code.
//
*/

  // Mutable progressive-init bridges for ambient nested namespaces.

  /*

**** Overview ****

This is the beginning of a library of routines for displaying and editing spreadsheet
data in a browser. The HTML that includes this does not need to have anything
specific to the spreadsheet or editor already present -- everything is dynamically
added to the DOM by this code, including the rendered sheet and any editing controls.

The library has a few parts. This is the main SocialCalc code module.
Other parts are the Table Editor module, the Formula module, and the Format Number module.
Note: The Table Editor module is licensed under a different license than this module.

The class/object style is derived from O'Reilly's JavaScript by Flanagan, 5th Edition,
section 9.3, page 157.

All of the data, object definitions, functions, etc., are stored as properties of the SocialCalc
object so as not to clutter up the global variables nor conflict with other names.

A design goal (not tested yet for success) is to make it possible to have more than one
spreadsheet active on a page, perhaps even open for editing. It is assumed, though, that
there is only one mouse and one keyboard (a good assumption on most PCs today but not in the
new "touch and surface world" Apple and Microsoft are working towards).

The testing has been on Windows Firefox (2 and 3),
Internet Explorer (6 and 7), Opera (9.23 and mainly later), Mac Safari (3.1), and Mac Firefox (2.0.0.6).
There are small issues with Firefox before 2.0 (cosmetic with drag handles) and larger ones
with Opera before 9.5 (the Delete key isn't recognized in some cases -- the 9.5 version was still
in beta and this bug affects other products like GMail, I believe).

The data is stored in a SocialCalc.Sheet object. The data is organized in a form similar to that
used by SocialCalc 1.1.0. There is a function for converting a normal SocialCalc spreadsheet
save data string (the spreadsheet part of a SocialCalc data file) into this internal form.

The SocialCalc.RenderContext class provides methods for rendering a table into the DOM representing
part of the spreadsheet. It is assumed that the spreadsheet could possibly be very large
and that rendering the whole thing at once could be too time consuming. It is also set up so
that it might be possible to have some of the sheet data only be loaded on demand (such as by Ajax).
The rendering can render cells to the right and below the already active area of the spreadsheet
so that you can scroll to that "clean" area without explicitly doing "add row/column". The class also
does simple operations such as "scrolling" within that table. The table may optionally include
row and column headers and may be split into panes. Most of the code assumes any number of panes,
but only the rightmost pane has scrolling code. In normal operation there would be one or two
panes horizontally and vertically. The panes may start on any row/column, though a given row/column
should only appear in one pane at a time (not all code enforces this, yet).

The RenderContext is designed to be rendered as part of a SocialCalc.TableEditor. The TableEditor
includes the spreadsheet grid as well as scrollbars, pane sliders, and (eventually) editing controls.
The layout is dynamic and may be recomputed on the fly, such as in response to resizing the browser
window.

The scrollbars and pane sliders are created using SocialCalc.TableControl objects. These in turn
make use of Dragging, ToolTip, Button, and MouseWheel functions.

The keyboard input is handled by keyboard code.

There are also some helper routines.

More comments yet to come...

*/

  // SocialCalc is always defined by socialcalcconstants.js (which concatenates
  // before this file in the UMD bundle). Redundant `if (!SocialCalc)` guard
  // removed.

  const SC = SocialCalc;
  const defaultSecurityPolicy = {
    sanitizeHtml: null,
    allowedUrlSchemes: ['http:', 'https:', 'mailto:'],
    allowedDataMimeTypes: [],
  };
  SC.Callbacks = {
    expand_wiki: null,
    expand_markup: function (displayvalue, sheetobj, linkstyle) {
      return SocialCalc.default_expand_markup(displayvalue, sheetobj, linkstyle);
    },
    MakePageLink: null,
    NormalizeSheetName: null,
    untrustedContent: false,
    securityPolicy: defaultSecurityPolicy,
  };
  SC.Cell = function (coord) {
    this.coord = coord;
    this.datavalue = '';
    this.datatype = null;
    this.formula = '';
    this.valuetype = 'b';
    this.readonly = false;
  };
  SC.CellProperties = {
    coord: 1,
    datavalue: 1,
    datatype: 1,
    formula: 1,
    valuetype: 1,
    errors: 1,
    comment: 1,
    readonly: 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,
    parseinfo: 3,
    hcolspan: 3,
    hrowspan: 3,
  };
  SC.CellPropertiesTable = {
    bt: 'borderstyle',
    br: 'borderstyle',
    bb: 'borderstyle',
    bl: 'borderstyle',
    layout: 'layout',
    font: 'font',
    color: 'color',
    bgcolor: 'color',
    cellformat: 'cellformat',
    nontextvalueformat: 'valueformat',
    textvalueformat: 'valueformat',
  };
  SC.Sheet = function () {
    SocialCalc.ResetSheet(this);
    this.statuscallback = null;
    this.statuscallbackparams = null;
  };
  SC.ResetSheet = function (sheet, _reload) {
    sheet.cells = {};
    sheet.attribs = {
      lastcol: 1,
      lastrow: 1,
      defaultlayout: 0,
      usermaxcol: 0,
      usermaxrow: 0,
    };
    sheet.rowattribs = {
      hide: {},
      height: {},
    };
    sheet.colattribs = {
      width: {},
      hide: {},
    };
    sheet.names = {};
    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.matched_cells = [];
    sheet.selected_search_cell = undefined;
    sheet.copiedfrom = '';
    sheet.changes = new SocialCalc.UndoStack();
    sheet.renderneeded = false;
    sheet.changedrendervalues = true;
    sheet.recalcchangedavalue = false;
    sheet.hiddencolrow = '';
    sheet.sci = new SocialCalc.SheetCommandInfo(sheet);
    sheet.ioEventTree = {};
    sheet.ioParameterList = {};
  };
  SC.Sheet.prototype.ResetSheet = function () {
    SocialCalc.ResetSheet(this);
  };
  SC.Sheet.prototype.AddCell = function (newcell) {
    return (this.cells[newcell.coord] = newcell);
  };
  SC.Sheet.prototype.LastCol = function () {
    var last_col = 1;
    for (var cell_id in this.cells) {
      var cr = SocialCalc.coordToCr(cell_id);
      if (cr.col > last_col) {
        last_col = cr.col;
      }
    }
    return last_col;
  };
  SC.Sheet.prototype.LastRow = function () {
    var last_row = 1;
    for (var cell_id in this.cells) {
      var cr = SocialCalc.coordToCr(cell_id);
      if (cr.row > last_row) {
        last_row = cr.row;
      }
    }
    return last_row;
  };
  SC.Sheet.prototype.GetAssuredCell = function (coord) {
    return this.cells[coord] || this.AddCell(new SocialCalc.Cell(coord));
  };
  SC.Sheet.prototype.ParseSheetSave = function (savedsheet) {
    SocialCalc.ParseSheetSave(savedsheet, this);
  };
  SC.Sheet.prototype.CellFromStringParts = function (cell, parts, j) {
    return SocialCalc.CellFromStringParts(this, cell, parts, j);
  };
  SC.Sheet.prototype.CreateSheetSave = function (range, canonicalize) {
    return SocialCalc.CreateSheetSave(this, range, canonicalize);
  };
  SC.Sheet.prototype.CellToString = function (cell) {
    return SocialCalc.CellToString(this, cell);
  };
  SC.Sheet.prototype.CanonicalizeSheet = function (full) {
    return SocialCalc.CanonicalizeSheet(this, full);
  };
  SC.Sheet.prototype.EncodeCellAttributes = function (coord) {
    return SocialCalc.EncodeCellAttributes(this, coord);
  };
  SC.Sheet.prototype.EncodeSheetAttributes = function () {
    return SocialCalc.EncodeSheetAttributes(this);
  };
  SC.Sheet.prototype.DecodeCellAttributes = function (coord, attribs, range) {
    return SocialCalc.DecodeCellAttributes(this, coord, attribs, range);
  };
  SC.Sheet.prototype.DecodeSheetAttributes = function (attribs) {
    return SocialCalc.DecodeSheetAttributes(this, attribs);
  };
  SC.Sheet.prototype.ScheduleSheetCommands = function (cmd, saveundo) {
    return SocialCalc.ScheduleSheetCommands(this, cmd, saveundo);
  };
  SC.Sheet.prototype.SheetUndo = function () {
    return SocialCalc.SheetUndo(this);
  };
  SC.Sheet.prototype.SheetRedo = function () {
    return SocialCalc.SheetRedo(this);
  };
  SC.Sheet.prototype.CreateAuditString = function () {
    return SocialCalc.CreateAuditString(this);
  };
  SC.Sheet.prototype.GetStyleNum = function (atype, style) {
    return SocialCalc.GetStyleNum(this, atype, style);
  };
  SC.Sheet.prototype.GetStyleString = function (atype, num) {
    return SocialCalc.GetStyleString(this, atype, num);
  };
  SC.Sheet.prototype.RecalcSheet = function () {
    return SocialCalc.RecalcSheet(this);
  };
  SC.ParseSheetSave = function (savedsheet, sheetobj) {
    var lines = savedsheet.split(/\r\n|\n/);
    var parts = [];
    var line;
    var i;
    var j;
    var t;
    var v;
    var coord;
    var cell;
    var attribs;
    var 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++];
                break;
              case 'hide':
                sheetobj.colattribs.hide[coord] = parts[j++];
                break;
              default:
                throw scc.s_pssUnknownColType + " '" + t + "'";
            }
          }
          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;
        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;
              case 'usermaxcol':
                attribs.usermaxcol = parts[j++] - 0;
                break;
              case 'usermaxrow':
                attribs.usermaxrow = parts[j++] - 0;
                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+):(.+)$/);
          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':
        case 'clipboard':
          break;
        case '':
          break;
        default:
          (function(){})(scc.s_pssUnknownLineType + " '" + parts[0] + "'");
          throw scc.s_pssUnknownLineType + " '" + parts[0] + "'";
      }
      parts = null;
    }
  };
  SC.CellFromStringParts = function (sheet, cell, parts, j) {
    var t, v, ro;
    while ((t = parts[j++])) {
      switch (t) {
        case 'v':
          cell.datavalue = +SocialCalc.decodeFromSave(parts[j++]);
          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++]);
          } 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++]);
          } 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++]);
          } else {
            cell.datavalue = SocialCalc.decodeFromSave(parts[j++]);
          }
          cell.formula = SocialCalc.decodeFromSave(parts[j++]);
          cell.datatype = 'c';
          break;
        case 'ro':
          ro = SocialCalc.decodeFromSave(parts[j++]);
          cell.readonly = ro.toLowerCase() == 'yes';
          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 + "'";
      }
    }
  };
  SC.sheetfields = [
    'defaultrowheight',
    'defaultcolwidth',
    'circularreferencecell',
    'recalc',
    'needsrecalc',
    'usermaxcol',
    'usermaxrow',
  ];
  SC.sheetfieldsshort = [
    'h',
    'w',
    'circularreferencecell',
    'recalc',
    'needsrecalc',
    'usermaxcol',
    'usermaxrow',
  ];
  SC.sheetfieldsxlat = [
    'defaulttextformat',
    'defaultnontextformat',
    'defaulttextvalueformat',
    'defaultnontextvalueformat',
    'defaultcolor',
    'defaultbgcolor',
    'defaultfont',
    'defaultlayout',
  ];
  SC.sheetfieldsxlatshort = ['tf', 'ntf', 'tvf', 'ntvf', 'color', 'bgcolor', 'font', 'layout'];
  SC.sheetfieldsxlatxlt = [
    'cellformat',
    'cellformat',
    'valueformat',
    'valueformat',
    'color',
    'color',
    'font',
    'layout',
  ];
  SC.CreateSheetSave = function (sheetobj, range, canonicalize) {
    var cell, cr1, cr2, row, col, coord, line, value, i, name;
    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;
        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++) {
      value = SocialCalc.encodeForSave(sheetobj.attribs[SocialCalc.sheetfields[i]]);
      if (value) line += ':' + SocialCalc.sheetfieldsshort[i] + ':' + value;
    }
    for (i = 0; i < SocialCalc.sheetfieldsxlat.length; i++) {
      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('');
    delete sheetobj.xlt;
    return result.join('\n');
  };
  SC.CellToString = function (sheet, cell) {
    var 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.readonly) {
      line += ':ro:yes';
    }
    if (cell.errors) {
      line += ':e:' + SocialCalc.encodeForSave(cell.errors);
    }
    t = cell.bt || '';
    r = cell.br || '';
    b = cell.bb || '';
    l = cell.bl || '';
    if (sheet.xlt) {
      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;
  };
  SC.CanonicalizeSheet = function (sheetobj, full) {
    var l;
    var coord;
    var cr;
    var cell;
    var filled;
    var an;
    var a;
    var newa;
    var newxlat;
    var used;
    var ahash;
    var i;
    var v;
    var maxrow = 0;
    var maxcol = 0;
    var alist = ['borderstyle', 'cellformat', 'color', 'font', 'layout', 'valueformat'];
    var xlt = {};
    xlt.namesorder = [];
    for (a in sheetobj.names) {
      xlt.namesorder.push(a);
    }
    xlt.namesorder.sort();
    if (!SocialCalc.Constants.doCanonicalizeSheet || !full) {
      for (an = 0; an < alist.length; an++) {
        a = alist[an];
        xlt['new' + a + 's'] = sheetobj[a + 's'];
        l = sheetobj[a + 's'].length;
        newxlat = Array.from({ length: 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) {
      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++) {
      v = sheetobj.attribs[SocialCalc.sheetfieldsxlat[i]];
      if (v) {
        xlt[SocialCalc.sheetfieldsxlatxlt[i] + 'sUsed'][v] = 1;
      }
    }
    a = {
      height: 1,
      hide: 1,
    };
    for (v in a) {
      for (cr in sheetobj.rowattribs[v]) {
        if (cr - 0 > maxrow) maxrow = cr - 0;
      }
    }
    a = {
      hide: 1,
      width: 1,
    };
    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++) {
      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++) {
        newxlat[ahash[newa[i]]] = i;
      }
      xlt[a + 'sxlat'] = newxlat;
      xlt['new' + a + 's'] = newa;
    }
    xlt.maxrow = maxrow || 1;
    xlt.maxcol = maxcol || 1;
    sheetobj.xlt = xlt;
  };
  SC.EncodeCellAttributes = function (sheet, coord) {
    var 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);
    InitAttrib('alignhoriz');
    if (cell.cellformat) {
      SetAttrib('alignhoriz', sheet.cellformats[cell.cellformat]);
    }
    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]);
    }
    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]);
    }
    InitAttrib('textcolor');
    if (cell.color) {
      SetAttrib('textcolor', sheet.colors[cell.color]);
    }
    InitAttrib('bgcolor');
    if (cell.bgcolor) {
      SetAttrib('bgcolor', sheet.colors[cell.bgcolor]);
    }
    InitAttribs(['numberformat', 'textformat']);
    if (cell.nontextvalueformat) {
      SetAttrib('numberformat', sheet.valueformats[cell.nontextvalueformat]);
    }
    if (cell.textvalueformat) {
      SetAttrib('textformat', sheet.valueformats[cell.textvalueformat]);
    }
    InitAttribs(['colspan', 'rowspan']);
    SetAttrib('colspan', cell.colspan || 1);
    SetAttrib('rowspan', cell.rowspan || 1);
    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]);
      }
    }
    InitAttribs(['cssc', 'csss', 'mod']);
    SetAttrib('cssc', cell.cssc || '');
    SetAttrib('csss', cell.csss || '');
    SetAttrib('mod', cell.mod || 'n');
    return result;
  };
  SC.EncodeSheetAttributes = function (sheet) {
    var value = undefined;
    var parts;
    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;
    };
    InitAttrib('colwidth');
    if (attribs.defaultcolwidth) {
      SetAttrib('colwidth', attribs.defaultcolwidth);
    }
    InitAttrib('rowheight');
    InitAttrib('textalignhoriz');
    if (attribs.defaulttextformat) {
      SetAttrib('textalignhoriz', sheet.cellformats[attribs.defaulttextformat]);
    }
    InitAttrib('numberalignhoriz');
    if (attribs.defaultnontextformat) {
      SetAttrib('numberalignhoriz', sheet.cellformats[attribs.defaultnontextformat]);
    }
    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]);
    }
    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]);
    }
    InitAttrib('textcolor');
    if (attribs.defaultcolor) {
      SetAttrib('textcolor', sheet.colors[attribs.defaultcolor]);
    }
    InitAttrib('bgcolor');
    if (attribs.defaultbgcolor) {
      SetAttrib('bgcolor', sheet.colors[attribs.defaultbgcolor]);
    }
    InitAttribs(['numberformat', 'textformat']);
    if (attribs.defaultnontextvalueformat) {
      SetAttrib('numberformat', sheet.valueformats[attribs.defaultnontextvalueformat]);
    }
    if (attribs.defaulttextvalueformat) {
      SetAttrib('textformat', sheet.valueformats[attribs.defaulttextvalueformat]);
    }
    InitAttrib('recalc');
    if (attribs.recalc) {
      SetAttrib('recalc', attribs.recalc);
    }
    InitAttrib('usermaxcol');
    if (attribs.usermaxcol) {
      SetAttrib('usermaxcol', attribs.usermaxcol);
    }
    InitAttrib('usermaxrow');
    if (attribs.usermaxrow) {
      SetAttrib('usermaxrow', attribs.usermaxrow);
    }
    return result;
  };
  SC.DecodeCellAttributes = function (sheet, coord, newattribs, range) {
    var value, b, bb, i;
    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;
    };
    CheckChanges('alignhoriz', sheet.cellformats[cell.cellformat], 'cellformat');
    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);
    }
    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);
    }
    CheckChanges('textcolor', sheet.colors[cell.color], 'color');
    CheckChanges('bgcolor', sheet.colors[cell.bgcolor], 'bgcolor');
    CheckChanges('numberformat', sheet.valueformats[cell.nontextvalueformat], 'nontextvalueformat');
    CheckChanges('textformat', sheet.valueformats[cell.textvalueformat], 'textvalueformat');
    for (i = 0; i < 4; i++) {
      b = 'trbl'.charAt(i);
      bb = 'b' + b;
      CheckChanges(bb, sheet.borderstyles[cell[bb]], bb);
    }
    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 = '';
        DoCmd('mod ' + value);
      }
    }
    if (changed) {
      return cmdstr;
    } else {
      return null;
    }
  };
  SC.DecodeSheetAttributes = function (sheet, newattribs) {
    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;
    };
    CheckChanges('colwidth', attribs.defaultcolwidth, 'defaultcolwidth');
    CheckChanges('rowheight', attribs.defaultrowheight, 'defaultrowheight');
    CheckChanges(
      'textalignhoriz',
      sheet.cellformats[attribs.defaulttextformat],
      'defaulttextformat',
    );
    CheckChanges(
      'numberalignhoriz',
      sheet.cellformats[attribs.defaultnontextformat],
      'defaultnontextformat',
    );
    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);
    }
    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);
    }
    CheckChanges('textcolor', sheet.colors[attribs.defaultcolor], 'defaultcolor');
    CheckChanges('bgcolor', sheet.colors[attribs.defaultbgcolor], 'defaultbgcolor');
    CheckChanges(
      'numberformat',
      sheet.valueformats[attribs.defaultnontextvalueformat],
      'defaultnontextvalueformat',
    );
    CheckChanges(
      'textformat',
      sheet.valueformats[attribs.defaulttextvalueformat],
      'defaulttextvalueformat',
    );
    CheckChanges('recalc', sheet.attribs.recalc, 'recalc');
    CheckChanges('usermaxcol', sheet.attribs.usermaxcol, 'usermaxcol');
    CheckChanges('usermaxrow', sheet.attribs.usermaxrow, 'usermaxrow');
    if (changed) {
      return cmdstr;
    } else {
      return null;
    }
  };
  SC.SheetCommandInfo = function (sheetobj) {
    this.sheetobj = sheetobj;
    this.timerobj = null;
    this.firsttimerdelay = 50;
    this.timerdelay = 1;
    this.maxtimeslice = 100;
    this.saveundo = false;
    this.CmdExtensionCallbacks = {};
  };
  SC.ScheduleSheetCommands = function (sheet, cmdstr, saveundo) {
    var sci = sheet.sci;
    var parseobj = new SocialCalc.Parse(cmdstr);
    if (sci.sheetobj.statuscallback) {
      sheet.statuscallback(sci, 'cmdstart', '', sci.sheetobj.statuscallbackparams);
    }
    if (saveundo) {
      sci.sheetobj.changes.PushChange('');
    }
    sci.timerobj = window.setTimeout(function () {
      SocialCalc.SheetCommandsTimerRoutine(sci, parseobj, saveundo);
    }, sci.firsttimerdelay);
  };
  SC.SheetCommandsTimerRoutine = function (sci, parseobj, saveundo) {
    var errortext;
    var starttime = new Date();
    sci.timerobj = null;
    while (!parseobj.EOF()) {
      try {
        errortext = SocialCalc.ExecuteSheetCommand(sci.sheetobj, parseobj, saveundo);
      } catch (err) {
        errortext = err instanceof Error ? err.message : String(err);
      }
      if (errortext) {
        if (typeof alert == 'function') {
          (function(){})(errortext);
        } else {
          console.log(errortext);
        }
      }
      parseobj.NextLine();
      if (Date.now() - starttime.getTime() >= sci.maxtimeslice) {
        sci.timerobj = window.setTimeout(function () {
          SocialCalc.SheetCommandsTimerRoutine(sci, parseobj, saveundo);
        }, sci.timerdelay);
        return;
      }
    }
    if (sci.sheetobj.statuscallback) {
      sci.sheetobj.statuscallback(sci, 'cmdend', '', sci.sheetobj.statuscallbackparams);
    }
  };
  SC.ExecuteSheetCommand = function (sheet, cmd, saveundo) {
    var cmdstr;
    var cmd1;
    var rest;
    var what;
    var attrib;
    var num;
    var pos;
    var pos2;
    var errortext;
    var undostart;
    var val;
    var cr1;
    var cr2;
    var col;
    var row;
    var cr;
    var cell;
    var newcell;
    var fillright;
    var rowstart;
    var colstart;
    var crbase;
    var rowoffset;
    var coloffset;
    var basecell;
    var clipsheet;
    var cliprange;
    var numcols;
    var numrows;
    var attribtable;
    var colend;
    var rowend;
    var newcolstart;
    var newrowstart;
    var newcolend;
    var newrowend;
    var rownext;
    var colnext;
    var colthis;
    var lastrow;
    var lastcol;
    var rowbefore;
    var colbefore;
    var oldformula;
    var oldcr;
    var olddefinition;
    var cols;
    var dirs;
    var lastsortcol;
    var i;
    var sortlist;
    var sortcells;
    var sortvalues;
    var sorttypes;
    var sortfunction;
    var valtype;
    var originalrow;
    var sortedcr;
    var name;
    var v1;
    var v2;
    var cmdextension;
    var editor;
    var undoNum;
    var trackLine;
    var sourceColname, colWidth, colHide, sourceRow, rowHide;
    var quashedCellCoord, quashedCell;
    var slast, tb;
    var value;
    var inc;
    var attribs = sheet.attribs;
    var changes = sheet.changes;
    var cellProperties = SocialCalc.CellProperties;
    var scc = SocialCalc.Constants;
    var cellChanged = false;
    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 = '';
              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) {
                delete sheet.cells[cr].displaystring;
              }
              break;
            case 'lastcol':
            case 'lastrow':
              if (saveundo) changes.AddUndo(undostart, attribs[attrib] - 0);
              num = rest - 0;
              attribs[attrib] = num > 0 ? num : 1;
              break;
            case 'recalc':
              if (saveundo) changes.AddUndo(undostart, attribs[attrib]);
              if (rest == 'off') {
                attribs.recalc = rest;
              } else {
                delete attribs.recalc;
              }
              break;
            case 'usermaxcol':
            case 'usermaxrow':
              if (saveundo) changes.AddUndo(undostart, attribs[attrib] - 0);
              num = rest - 0;
              attribs[attrib] = num > 0 ? num : 0;
              break;
            default:
              errortext = scc.s_escUnknownSheetCmd + cmdstr;
              break;
          }
        } else if (/^[a-z]{1,2}(:[a-z]{1,2})?$/i.test(what)) {
          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];
              }
            } else if (attrib == 'hide') {
              sheet.hiddencolrow = 'col';
              cr = SocialCalc.rcColname(col);
              if (saveundo) changes.AddUndo('set ' + cr + ' hide', sheet.colattribs.hide[cr]);
              if (rest.length > 0) {
                sheet.colattribs.hide[cr] = rest;
              } else {
                delete sheet.colattribs.hide[cr];
              }
            }
          }
        } else if (/^\d+(:\d+)?$/i.test(what)) {
          sheet.renderneeded = true;
          what = what.toUpperCase();
          pos = what.indexOf(':');
          if (pos >= 0) {
            cr1 = SocialCalc.coordToCr('A' + what.substring(0, pos));
            cr2 = SocialCalc.coordToCr('A' + what.substring(pos + 1));
          } else {
            cr1 = SocialCalc.coordToCr('A' + what);
            cr2 = cr1;
          }
          for (row = cr1.row; row <= cr2.row; row++) {
            if (attrib == 'height') {
              if (saveundo) changes.AddUndo('set ' + row + ' height', sheet.rowattribs.height[row]);
              if (rest.length > 0) {
                sheet.rowattribs.height[row] = rest;
              } else {
                delete sheet.rowattribs.height[row];
              }
            } else if (attrib == 'hide') {
              sheet.hiddencolrow = 'row';
              if (saveundo) changes.AddUndo('set ' + row + ' hide', sheet.rowattribs.hide[row]);
              if (rest.length > 0) {
                sheet.rowattribs.hide[row] = rest;
              } else {
                delete sheet.rowattribs.hide[row];
              }
            }
          }
        } else if (/^[a-z]{1,2}\d+(:[a-z]{1,2}\d+)?$/i.test(what)) {
          cellChanged = true;
          ParseRange();
          if (
            cr1.row != cr2.row ||
            cr1.col != cr2.col ||
            sheet.celldisplayneeded ||
            sheet.renderneeded
          ) {
            sheet.renderneeded = true;
            sheet.celldisplayneeded = '';
          } else {
            sheet.celldisplayneeded = SocialCalc.crToCoord(cr1.col, cr1.row);
          }
          for (row = cr1.row; row <= cr2.row; row++) {
            for (col = cr1.col; col <= cr2.col; col++) {
              cr = SocialCalc.crToCoord(col, row);
              cell = sheet.GetAssuredCell(cr);
              if (cell.readonly && attrib != 'readonly') continue;
              if (saveundo) changes.AddUndo('set ' + cr + ' all', sheet.CellToString(cell));
              if (attrib == 'value') {
                pos = rest.indexOf(' ');
                var vtype = rest.substring(0, pos);
                var vrest = rest.substring(pos + 1);
                delete cell.errors;
                delete cell.displaystring;
                delete cell.parseinfo;
                if (vtype == 'n') {
                  cell.datavalue = vrest - 0;
                  cell.datatype = 'v';
                  cell.valuetype = 'n';
                } else {
                  value = SocialCalc.DetermineValueType(vrest);
                  cell.datavalue = value.value;
                  cell.valuetype = value.type;
                  if (value.type.charAt(0) == 't') {
                    cell.datatype = 't';
                  } else {
                    cell.datatype = 'c';
                    cell.formula = vrest;
                  }
                }
                attribs.needsrecalc = 'yes';
              } else if (attrib == 'text') {
                pos = rest.indexOf(' ');
                cell.datavalue = SocialCalc.decodeFromSave(rest.substring(pos + 1));
                delete cell.errors;
                cell.datatype = 't';
                cell.valuetype = rest.substring(0, pos);
                delete cell.displaystring;
                delete cell.parseinfo;
                attribs.needsrecalc = 'yes';
              } else if (attrib == 'formula') {
                cell.datavalue = 0;
                delete cell.errors;
                cell.datatype = 'f';
                if (SocialCalc._app && cell.valuetype != 'e#N/A')
                  cell.prevvaluetype = cell.valuetype;
                cell.valuetype = 'e#N/A';
                cell.formula = rest;
                delete cell.displaystring;
                delete cell.parseinfo;
                attribs.needsrecalc = 'yes';
              } else if (attrib == 'constant') {
                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') {
                  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') {
                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') {
                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)) {
                cell[attrib] = sheet.GetStyleNum('borderstyle', rest);
                sheet.renderneeded = true;
              } 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') {
                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 if (attrib == 'readonly') {
                cell.readonly = rest.toLowerCase() == 'yes';
              } 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 (cell.readonly) break;
        for (row = cr1.row; row <= cr2.row; row++) {
          for (col = cr1.col; col <= cr2.col; col++) {
            if (!(row == cr1.row && col == cr1.col)) {
              quashedCellCoord = SocialCalc.crToCoord(col, row);
              quashedCell = sheet.GetAssuredCell(quashedCellCoord);
              if (saveundo)
                changes.AddUndo(
                  'set ' + quashedCellCoord + ' all',
                  sheet.CellToString(quashedCell),
                );
              delete sheet.cells[quashedCellCoord];
            }
          }
        }
        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 (cell.readonly) break;
        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;
      case 'erase':
      case 'cut':
        sheet.renderneeded = true;
        sheet.changedrendervalues = true;
        what = cmd.NextToken();
        rest = cmd.RestOfString();
        ParseRange();
        if (saveundo) changes.AddUndo('changedrendervalues');
        if (cmd1 == 'cut') {
          if (saveundo)
            changes.AddUndo(
              'loadclipboard',
              SocialCalc.encodeForSave(SocialCalc.Clipboard.clipboard),
            );
          SocialCalc.Clipboard.clipboard = SocialCalc.CreateSheetSave(sheet, what);
        }
        for (row = cr1.row; row <= cr2.row; row++) {
          for (col = cr1.col; col <= cr2.col; col++) {
            cr = SocialCalc.crToCoord(col, row);
            cell = sheet.GetAssuredCell(cr);
            if (cell.readonly) continue;
            if (saveundo) changes.AddUndo('set ' + cr + ' all', sheet.CellToString(cell));
            if (rest == 'all') {
              delete sheet.cells[cr];
            } else if (rest == 'formulas') {
              cell.datavalue = '';
              cell.datatype = null;
              cell.formula = '';
              cell.valuetype = 'b';
              delete cell.errors;
              delete cell.displaystring;
              delete cell.parseinfo;
              if (cell.comment) {
                delete cell.comment;
              }
            } else if (rest == 'formats') {
              newcell = new SocialCalc.Cell(cr);
              newcell.datavalue = cell.datavalue;
              newcell.datatype = cell.datatype;
              newcell.formula = cell.formula;
              newcell.valuetype = cell.valuetype;
              if (cell.comment) {
                newcell.comment = cell.comment;
              }
              sheet.cells[cr] = newcell;
            }
          }
        }
        attribs.needsrecalc = 'yes';
        break;
      case 'fillright':
      case 'filldown':
        sheet.renderneeded = true;
        sheet.changedrendervalues = true;
        if (saveundo) changes.AddUndo('changedrendervalues');
        what = cmd.NextToken();
        rest = cmd.RestOfString();
        ParseRange();
        function increment_amount(down, seriescol, seriesrow) {
          function valid_datatype(type) {
            return type == 'v' || type == 'c';
          }
          function increment_from_cells(startcell, endcell) {
            if (valid_datatype(startcell.datatype) && valid_datatype(endcell.datatype)) {
              return endcell.datavalue - startcell.datavalue;
            }
            return undefined;
          }
          var csco = SocialCalc.GetSpreadsheetControlObject();
          var editor = csco && csco.editor;
          var range = editor && editor.range2;
          var returnval = undefined;
          var startcell, endcell;
          if (range && range.hasrange) {
            if (
              down &&
              range.bottom - range.top == 1 &&
              seriescol >= range.left &&
              seriescol <= range.right
            ) {
              startcell = sheet.GetAssuredCell(SocialCalc.crToCoord(seriescol, range.top));
              endcell = sheet.GetAssuredCell(SocialCalc.crToCoord(seriescol, range.bottom));
              returnval = increment_from_cells(startcell, endcell);
            } else if (
              !down &&
              range.right - range.left == 1 &&
              seriesrow >= range.top &&
              seriesrow <= range.bottom
            ) {
              startcell = sheet.GetAssuredCell(SocialCalc.crToCoord(range.left, seriesrow));
              endcell = sheet.GetAssuredCell(SocialCalc.crToCoord(range.right, seriesrow));
              returnval = increment_from_cells(startcell, endcell);
            }
          }
          if (returnval === undefined) {
            if (down && cr2.row > cr1.row) {
              startcell = sheet.GetAssuredCell(SocialCalc.crToCoord(seriescol, cr1.row));
              endcell = sheet.GetAssuredCell(SocialCalc.crToCoord(seriescol, cr1.row + 1));
              returnval = increment_from_cells(startcell, endcell);
            } else if (!down && cr2.col > cr1.col) {
              startcell = sheet.GetAssuredCell(SocialCalc.crToCoord(cr1.col, seriesrow));
              endcell = sheet.GetAssuredCell(SocialCalc.crToCoord(cr1.col + 1, seriesrow));
              returnval = increment_from_cells(startcell, endcell);
            }
          }
          return returnval;
        }
        if (cmd1 == 'fillright') {
          fillright = true;
          rowstart = cr1.row;
          colstart = cr1.col + 1;
        } else {
          fillright = false;
          rowstart = cr1.row + 1;
          colstart = cr1.col;
        }
        var increments = {};
        if (fillright) {
          for (row = rowstart; row <= cr2.row; row++) {
            increments[row] = increment_amount(false, cr1.col, row);
          }
        } else {
          for (col = colstart; col <= cr2.col; col++) {
            increments[col] = increment_amount(true, col, cr1.row);
          }
        }
        var csco = SocialCalc.GetSpreadsheetControlObject();
        var editor = csco && csco.editor;
        if (editor) editor.Range2Remove();
        for (row = rowstart; row <= cr2.row; row++) {
          for (col = colstart; col <= cr2.col; col++) {
            cr = SocialCalc.crToCoord(col, row);
            cell = sheet.GetAssuredCell(cr);
            if (cell.readonly) continue;
            if (saveundo) changes.AddUndo('set ' + cr + ' all', sheet.CellToString(cell));
            if (fillright) {
              crbase = SocialCalc.crToCoord(cr1.col, row);
              coloffset = col - colstart + 1;
              rowoffset = 0;
            } else {
              crbase = SocialCalc.crToCoord(col, cr1.row);
              coloffset = 0;
              rowoffset = row - rowstart + 1;
            }
            basecell = sheet.GetAssuredCell(crbase);
            inc = increments[fillright ? row : col];
            if (rest == 'all' || rest == 'formats') {
              for (attrib in cellProperties) {
                if (cellProperties[attrib] == 1) continue;
                if (typeof basecell[attrib] === 'undefined' || cellProperties[attrib] == 3) {
                  delete cell[attrib];
                } else {
                  cell[attrib] = basecell[attrib];
                }
              }
            }
            if (rest == 'all' || rest == 'formulas') {
              if (inc !== undefined) {
                cell.datavalue = basecell.datavalue + (fillright ? coloffset : rowoffset) * inc;
              } else {
                cell.datavalue = basecell.datavalue;
              }
              cell.datatype = basecell.datatype;
              cell.valuetype = basecell.valuetype;
              if (cell.datatype == 'f') {
                cell.formula = SocialCalc.OffsetFormulaCoords(
                  basecell.formula,
                  coloffset,
                  rowoffset,
                );
              } else {
                cell.formula = basecell.formula;
              }
              delete cell.parseinfo;
              cell.errors = basecell.errors;
            }
            delete cell.displaystring;
          }
        }
        attribs.needsrecalc = 'yes';
        break;
      case 'copy':
        what = cmd.NextToken();
        rest = cmd.RestOfString();
        if (saveundo)
          changes.AddUndo(
            'loadclipboard',
            SocialCalc.encodeForSave(SocialCalc.Clipboard.clipboard),
          );
        SocialCalc.Clipboard.clipboard = SocialCalc.CreateSheetSave(sheet, what);
        break;
      case 'loadclipboard':
        rest = cmd.RestOfString();
        if (saveundo)
          changes.AddUndo(
            'loadclipboard',
            SocialCalc.encodeForSave(SocialCalc.Clipboard.clipboard),
          );
        SocialCalc.Clipboard.clipboard = SocialCalc.decodeFromSave(rest);
        break;
      case 'clearclipboard':
        if (saveundo)
          changes.AddUndo(
            'loadclipboard',
            SocialCalc.encodeForSave(SocialCalc.Clipboard.clipboard),
          );
        SocialCalc.Clipboard.clipboard = '';
        break;
      case 'paste':
        sheet.renderneeded = true;
        sheet.changedrendervalues = true;
        if (saveundo) changes.AddUndo('changedrendervalues');
        what = cmd.NextToken();
        rest = cmd.RestOfString();
        ParseRange();
        if (!SocialCalc.Clipboard.clipboard) {
          break;
        }
        clipsheet = new SocialCalc.Sheet();
        clipsheet.ParseSheetSave(SocialCalc.Clipboard.clipboard);
        cliprange = SocialCalc.ParseRange(clipsheet.copiedfrom);
        numcols = Math.max(cr2.col - cr1.col + 1, cliprange.cr2.col - cliprange.cr1.col + 1);
        numrows = Math.max(cr2.row - cr1.row + 1, cliprange.cr2.row - cliprange.cr1.row + 1);
        if (cr1.col + numcols - 1 > attribs.lastcol) attribs.lastcol = cr1.col + numcols - 1;
        if (cr1.row + numrows - 1 > attribs.lastrow) attribs.lastrow = cr1.row + numrows - 1;
        for (row = cr1.row; row < cr1.row + numrows; row++) {
          for (col = cr1.col; col < cr1.col + numcols; col++) {
            cr = SocialCalc.crToCoord(col, row);
            cell = sheet.GetAssuredCell(cr);
            if (cell.readonly) continue;
            if (saveundo) changes.AddUndo('set ' + cr + ' all', sheet.CellToString(cell));
            var currentClipCol =
              cliprange.cr1.col + ((col - cr1.col) % (cliprange.cr2.col - cliprange.cr1.col + 1));
            var currentClipRow =
              cliprange.cr1.row + ((row - cr1.row) % (cliprange.cr2.row - cliprange.cr1.row + 1));
            crbase = SocialCalc.crToCoord(currentClipCol, currentClipRow);
            basecell = clipsheet.GetAssuredCell(crbase);
            if (rest == 'all' || rest == 'formats') {
              if (row == cr1.row) {
                sourceColname = SocialCalc.rcColname(
                  cliprange.cr1.col +
                    ((col - cr1.col) % (cliprange.cr2.col - cliprange.cr1.col + 1)),
                );
                colWidth = clipsheet.colattribs.width[sourceColname];
                colHide = clipsheet.colattribs.hide[sourceColname];
                if (colWidth != null) {
                  sheet.colattribs.width[SocialCalc.rcColname(col)] = colWidth;
                }
                if (colHide != null) {
                  sheet.colattribs.hide[SocialCalc.rcColname(col)] = colHide;
                }
              }
              if (col == cr1.col) {
                sourceRow =
                  cliprange.cr1.row +
                  ((row - cr1.row) % (cliprange.cr2.row - cliprange.cr1.row + 1));
                rowHide = clipsheet.rowattribs.hide[sourceRow];
                if (rowHide != null) {
                  sheet.rowattribs.hide[row] = rowHide;
                }
              }
              for (attrib in cellProperties) {
                if (cellProperties[attrib] == 1) continue;
                if (typeof basecell[attrib] === 'undefined' || cellProperties[attrib] == 3) {
                  delete cell[attrib];
                } else {
                  attribtable = SocialCalc.CellPropertiesTable[attrib];
                  if (attribtable && basecell[attrib]) {
                    cell[attrib] = sheet.GetStyleNum(
                      attribtable,
                      clipsheet.GetStyleString(attribtable, basecell[attrib]),
                    );
                  } else {
                    cell[attrib] = basecell[attrib];
                  }
                }
              }
            }
            if (rest == 'all' || rest == 'formulas') {
              cell.datavalue = basecell.datavalue;
              cell.datatype = basecell.datatype;
              cell.valuetype = basecell.valuetype;
              if (cell.datatype == 'f') {
                cell.formula = SocialCalc.OffsetFormulaCoords(
                  basecell.formula,
                  col - currentClipCol,
                  row - currentClipRow,
                );
              } else {
                cell.formula = basecell.formula;
              }
              delete cell.parseinfo;
              cell.errors = basecell.errors;
              if (basecell.comment) {
                cell.comment = basecell.comment;
              } else if (cell.comment) {
                delete cell.comment;
              }
            }
            delete cell.displaystring;
          }
        }
        attribs.needsrecalc = 'yes';
        break;
      case 'sort':
        sheet.renderneeded = true;
        sheet.changedrendervalues = true;
        if (saveundo) changes.AddUndo('changedrendervalues');
        what = cmd.NextToken();
        ParseRange();
        cols = [];
        dirs = [];
        lastsortcol = 0;
        for (i = 0; i <= 3; i++) {
          cols[i] = cmd.NextToken();
          dirs[i] = cmd.NextToken();
          if (cols[i]) lastsortcol = i;
        }
        sortcells = {};
        sortlist = [];
        sortvalues = [];
        sorttypes = [];
        for (row = cr1.row; row <= cr2.row; row++) {
          for (col = cr1.col; col <= cr2.col; col++) {
            cr = SocialCalc.crToCoord(col, row);
            cell = sheet.cells[cr];
            if (cell) {
              sortcells[cr] = sheet.CellToString(cell);
              if (saveundo) changes.AddUndo('set ' + cr + ' all', sortcells[cr]);
            } else {
              if (saveundo) changes.AddUndo('set ' + cr + ' all');
            }
          }
          sortlist.push(sortlist.length);
          sortvalues.push([]);
          sorttypes.push([]);
          slast = sorttypes.length - 1;
          for (i = 0; i <= lastsortcol; i++) {
            cr = cols[i] + row;
            cell = sheet.GetAssuredCell(cr);
            val = cell.datavalue;
            valtype = cell.valuetype.charAt(0) || 'b';
            if (valtype == 't') val = val.toLowerCase();
            sortvalues[slast].push(val);
            sorttypes[slast].push(valtype);
          }
        }
        sortfunction = function (a, b) {
          var i, a1, b1, ta, cresult;
          for (i = 0; i <= lastsortcol; i++) {
            if (dirs[i] == 'up') {
              a1 = a;
              b1 = b;
            } else {
              a1 = b;
              b1 = a;
            }
            ta = sorttypes[a1][i];
            tb = sorttypes[b1][i];
            if (ta == 't') {
              if (tb == 't') {
                a1 = sortvalues[a1][i];
                b1 = sortvalues[b1][i];
                cresult = a1 > b1 ? 1 : a1 < b1 ? -1 : 0;
              } else if (tb == 'n') {
                cresult = 1;
              } else if (tb == 'b') {
                cresult = dirs[i] == 'up' ? -1 : 1;
              } else if (tb == 'e') {
                cresult = -1;
              }
            } else if (ta == 'n') {
              if (tb == 't') {
                cresult = -1;
              } else if (tb == 'n') {
                a1 = sortvalues[a1][i] - 0;
                b1 = sortvalues[b1][i] - 0;
                cresult = a1 > b1 ? 1 : a1 < b1 ? -1 : 0;
              } else if (tb == 'b') {
                cresult = dirs[i] == 'up' ? -1 : 1;
              } else if (tb == 'e') {
                cresult = -1;
              }
            } else if (ta == 'e') {
              if (tb == 'e') {
                a1 = sortvalues[a1][i];
                b1 = sortvalues[b1][i];
                cresult = a1 > b1 ? 1 : a1 < b1 ? -1 : 0;
              } else if (tb == 'b') {
                cresult = dirs[i] == 'up' ? -1 : 1;
              } else {
                cresult = 1;
              }
            } else if (ta == 'b') {
              if (tb == 'b') {
                cresult = 0;
              } else {
                cresult = dirs[i] == 'up' ? 1 : -1;
              }
            }
            if (cresult) {
              return cresult;
            }
          }
          cresult = a > b ? 1 : a < b ? -1 : 0;
          return cresult;
        };
        sortlist.sort(sortfunction);
        for (row = cr1.row; row <= cr2.row; row++) {
          originalrow = sortlist[row - cr1.row];
          for (col = cr1.col; col <= cr2.col; col++) {
            cr = SocialCalc.crToCoord(col, row);
            sortedcr = SocialCalc.crToCoord(col, originalrow + cr1.row);
            if (sortcells[sortedcr]) {
              cell = new SocialCalc.Cell(cr);
              sheet.CellFromStringParts(cell, sortcells[sortedcr].split(':'), 1);
              if (cell.datatype == 'f') {
                cell.formula = SocialCalc.OffsetFormulaCoords(
                  cell.formula,
                  0,
                  row - cr1.row - originalrow,
                  {
                    startCol: cr1.col,
                    endCol: cr2.col,
                    startRow: cr1.row,
                    endRow: cr2.row,
                  },
                );
              }
              sheet.cells[cr] = cell;
            } else {
              delete sheet.cells[cr];
            }
          }
        }
        attribs.needsrecalc = 'yes';
        break;
      case 'insertcol':
      case 'insertrow':
        sheet.renderneeded = true;
        sheet.changedrendervalues = true;
        sheet.widgetsClean = false;
        what = cmd.NextToken();
        rest = cmd.RestOfString();
        ParseRange();
        lastcol = attribs.lastcol;
        lastrow = attribs.lastrow;
        if (cmd1 == 'insertcol') {
          coloffset = 1;
          colend = cr1.col;
          rowoffset = 0;
          rowend = 1;
          newcolstart = cr1.col;
          newcolend = cr1.col;
          newrowstart = 1;
          newrowend = attribs.lastrow;
          if (attribs.lastcol >= 702) {
            break;
          }
          if (saveundo) changes.AddUndo('deletecol ' + cr1.coord);
        } else {
          coloffset = 0;
          colend = 1;
          rowoffset = 1;
          rowend = cr1.row;
          newcolstart = 1;
          newcolend = attribs.lastcol;
          newrowstart = cr1.row;
          newrowend = cr1.row;
          if (saveundo) changes.AddUndo('deleterow ' + cr1.coord);
        }
        for (row = lastrow; row >= rowend; row--) {
          for (col = lastcol; col >= colend; col--) {
            crbase = SocialCalc.crToCoord(col, row);
            cr = SocialCalc.crToCoord(col + coloffset, row + rowoffset);
            if (!sheet.cells[crbase]) {
              delete sheet.cells[cr];
            } else {
              sheet.cells[cr] = sheet.cells[crbase];
            }
          }
        }
        for (row = newrowstart; row <= newrowend; row++) {
          for (col = newcolstart; col <= newcolend; col++) {
            cr = SocialCalc.crToCoord(col, row);
            cell = new SocialCalc.Cell(cr);
            sheet.cells[cr] = cell;
            crbase = SocialCalc.crToCoord(col - coloffset, row - rowoffset);
            basecell = sheet.GetAssuredCell(crbase);
            for (attrib in cellProperties) {
              if (cellProperties[attrib] == 2) {
                if (attrib == 'colspan' || attrib == 'rowspan') continue;
                cell[attrib] = basecell[attrib];
              }
            }
          }
        }
        for (cr in sheet.cells) {
          cell = sheet.cells[cr];
          if (cell && cell.datatype == 'f') {
            cell.formula = SocialCalc.AdjustFormulaCoords(
              cell.formula,
              cr1.col,
              coloffset,
              cr1.row,
              rowoffset,
            );
          }
          if (cell) {
            delete cell.parseinfo;
          }
        }
        for (name in sheet.names) {
          if (sheet.names[name]) {
            v1 = sheet.names[name].definition;
            v2 = '';
            if (v1.charAt(0) == '=') {
              v2 = '=';
              v1 = v1.substring(1);
            }
            sheet.names[name].definition =
              v2 + SocialCalc.AdjustFormulaCoords(v1, cr1.col, coloffset, cr1.row, rowoffset);
          }
        }
        for (row = attribs.lastrow; row >= rowend && cmd1 == 'insertrow'; row--) {
          rownext = row + rowoffset;
          for (attrib in sheet.rowattribs) {
            val = sheet.rowattribs[attrib][row];
            if (sheet.rowattribs[attrib][rownext] != val) {
              if (val) {
                sheet.rowattribs[attrib][rownext] = val;
              } else {
                delete sheet.rowattribs[attrib][rownext];
              }
            }
          }
        }
        for (col = lastcol; col >= colend && cmd1 == 'insertcol'; col--) {
          colthis = SocialCalc.rcColname(col);
          colnext = SocialCalc.rcColname(col + coloffset);
          for (attrib in sheet.colattribs) {
            val = sheet.colattribs[attrib][colthis];
            if (sheet.colattribs[attrib][colnext] != val) {
              if (val) {
                sheet.colattribs[attrib][colnext] = val;
              } else {
                delete sheet.colattribs[attrib][colnext];
              }
            }
          }
        }
        var tempRenderContext = new SocialCalc.RenderContext(sheet);
        tempRenderContext.CalculateCellSkipData();
        var cellsToExpand = {};
        for (var skipCell in tempRenderContext.cellskip) {
          var skipCellCR = SocialCalc.coordToCr(skipCell),
            mergerCellCoords = tempRenderContext.cellskip[skipCell];
          if (cmd1 === 'insertcol' && skipCellCR.col === newcolstart) {
            if (!cellsToExpand[mergerCellCoords]) {
              sheet.GetAssuredCell(mergerCellCoords).colspan += coloffset;
            }
            cellsToExpand[mergerCellCoords] = true;
          }
          if (cmd1 === 'insertrow' && skipCellCR.row === newrowstart) {
            if (!cellsToExpand[mergerCellCoords]) {
              sheet.GetAssuredCell(mergerCellCoords).rowspan += rowoffset;
            }
            cellsToExpand[mergerCellCoords] = true;
          }
        }
        attribs.lastcol = Math.min(702, attribs.lastcol + coloffset);
        attribs.lastrow += rowoffset;
        attribs.needsrecalc = 'yes';
        break;
      case 'deletecol':
      case 'deleterow':
        sheet.renderneeded = true;
        sheet.changedrendervalues = true;
        sheet.widgetsClean = false;
        what = cmd.NextToken();
        rest = cmd.RestOfString();
        lastcol = attribs.lastcol;
        lastrow = attribs.lastrow;
        ParseRange();
        if (cmd1 == 'deletecol') {
          coloffset = cr1.col - cr2.col - 1;
          rowoffset = 0;
          colstart = cr2.col + 1;
          rowstart = 1;
        } else {
          coloffset = 0;
          rowoffset = cr1.row - cr2.row - 1;
          colstart = 1;
          rowstart = cr2.row + 1;
        }
        for (row = rowstart; row <= lastrow - rowoffset; row++) {
          for (col = colstart; col <= lastcol - coloffset; col++) {
            cr = SocialCalc.crToCoord(col + coloffset, row + rowoffset);
            cell = sheet.cells[cr];
            if (cell && cell.readonly) {
              errortext =
                'Unable to remove ' +
                (cmd1 == 'deletecol' ? 'column' : 'row') +
                ', because cell ' +
                cell.coord +
                ' is locked';
              return errortext;
            }
          }
        }
        for (row = rowstart; row <= lastrow - rowoffset; row++) {
          for (col = colstart; col <= lastcol - coloffset; col++) {
            cr = SocialCalc.crToCoord(col + coloffset, row + rowoffset);
            if (saveundo && (row < rowstart - rowoffset || col < colstart - coloffset)) {
              cell = sheet.cells[cr];
              if (!cell) {
                changes.AddUndo('erase ' + cr + ' all');
              } else {
                changes.AddUndo('set ' + cr + ' all', sheet.CellToString(cell));
              }
            }
            crbase = SocialCalc.crToCoord(col, row);
            cell = sheet.cells[crbase];
            if (!cell) {
              delete sheet.cells[cr];
            } else {
              sheet.cells[cr] = cell;
            }
          }
        }
        for (cr in sheet.cells) {
          cell = sheet.cells[cr];
          if (cell) {
            if (cell.datatype == 'f') {
              oldformula = cell.formula;
              cell.formula = SocialCalc.AdjustFormulaCoords(
                oldformula,
                cr1.col,
                coloffset,
                cr1.row,
                rowoffset,
              );
              if (cell.formula != oldformula) {
                delete cell.parseinfo;
                if (saveundo && cell.formula.indexOf('#REF!') != -1) {
                  oldcr = SocialCalc.coordToCr(cr);
                  if (cmd1 == 'deletecol') {
                    crbase = SocialCalc.crToCoord(
                      oldcr.col < cr1.col ? oldcr.col : oldcr.col - coloffset,
                      oldcr.row,
                    );
                  } else {
                    crbase = SocialCalc.crToCoord(
                      oldcr.col,
                      oldcr.row < cr1.row ? oldcr.row : oldcr.row - rowoffset,
                    );
                  }
                  changes.AddUndo('set ' + crbase + ' formula ' + oldformula);
                }
              }
            } else {
              delete cell.parseinfo;
            }
          }
        }
        for (name in sheet.names) {
          if (sheet.names[name]) {
            olddefinition = sheet.names[name].definition;
            v1 = olddefinition;
            v2 = '';
            if (v1.charAt(0) == '=') {
              v2 = '=';
              v1 = v1.substring(1);
            }
            sheet.names[name].definition =
              v2 + SocialCalc.AdjustFormulaCoords(v1, cr1.col, coloffset, cr1.row, rowoffset);
            if (saveundo && sheet.names[name].definition != olddefinition) {
              changes.AddUndo('name define ' + name + ' ' + olddefinition);
            }
          }
        }
        for (row = rowstart; row <= lastrow - rowoffset && cmd1 == 'deleterow'; row++) {
          rowbefore = row + rowoffset;
          for (attrib in sheet.rowattribs) {
            val = sheet.rowattribs[attrib][row];
            if (sheet.rowattribs[attrib][rowbefore] != val) {
              if (saveundo)
                changes.AddUndo(
                  'set ' + rowbefore + ' ' + attrib,
                  sheet.rowattribs[attrib][rowbefore],
                );
              if (val) {
                sheet.rowattribs[attrib][rowbefore] = val;
              } else {
                delete sheet.rowattribs[attrib][rowbefore];
              }
            }
          }
        }
        for (col = colstart; col <= lastcol - coloffset && cmd1 == 'deletecol'; col++) {
          colthis = SocialCalc.rcColname(col);
          colbefore = SocialCalc.rcColname(col + coloffset);
          for (attrib in sheet.colattribs) {
            val = sheet.colattribs[attrib][colthis];
            if (sheet.colattribs[attrib][colbefore] != val) {
              if (saveundo)
                changes.AddUndo(
                  'set ' + colbefore + ' ' + attrib,
                  sheet.colattribs[attrib][colbefore],
                );
              if (val) {
                sheet.colattribs[attrib][colbefore] = val;
              } else {
                delete sheet.colattribs[attrib][colbefore];
              }
            }
          }
        }
        var tempRenderContext = new SocialCalc.RenderContext(sheet);
        tempRenderContext.CalculateCellSkipData();
        var cellsToShrink = {};
        for (var skipCell in tempRenderContext.cellskip) {
          var skipCellCR = SocialCalc.coordToCr(skipCell),
            mergerCellCoords = tempRenderContext.cellskip[skipCell],
            mergerCellCR = tempRenderContext.coordToCR[mergerCellCoords];
          if (
            cmd1 === 'deletecol' &&
            skipCellCR.col === colstart + coloffset &&
            mergerCellCR.col < skipCellCR.col
          ) {
            if (!cellsToShrink[mergerCellCoords]) {
              sheet.GetAssuredCell(mergerCellCoords).colspan += coloffset;
            }
            cellsToShrink[mergerCellCoords] = true;
          }
          if (
            cmd1 === 'deleterow' &&
            skipCellCR.row === rowstart + rowoffset &&
            mergerCellCR.row < skipCellCR.row
          ) {
            if (!cellsToShrink[mergerCellCoords]) {
              sheet.GetAssuredCell(mergerCellCoords).rowspan += rowoffset;
            }
            cellsToShrink[mergerCellCoords] = true;
          }
        }
        if (saveundo) {
          if (cmd1 == 'deletecol') {
            for (col = cr1.col; col <= cr2.col; col++) {
              changes.AddUndo('insertcol ' + SocialCalc.rcColname(cr1.col));
            }
          } else {
            for (row = cr1.row; row <= cr2.row; row++) {
              changes.AddUndo('insertrow ' + cr1.row);
            }
          }
        }
        if (cmd1 == 'deletecol') {
          if (cr1.col <= lastcol) {
            if (cr2.col <= lastcol) {
              attribs.lastcol += coloffset;
            } else {
              attribs.lastcol = cr1.col - 1;
            }
          }
        } else {
          if (cr1.row <= lastrow) {
            if (cr2.row <= lastrow) {
              attribs.lastrow += rowoffset;
            } else {
              attribs.lastrow = cr1.row - 1;
            }
          }
        }
        attribs.needsrecalc = 'yes';
        break;
      case 'movepaste':
      case 'moveinsert':
        var movingcells;
        var dest;
        var destcr;
        var inserthoriz;
        var insertvert;
        var pushamount;
        var movedto;
        sheet.renderneeded = true;
        sheet.changedrendervalues = true;
        if (saveundo) changes.AddUndo('changedrendervalues');
        what = cmd.NextToken();
        dest = cmd.NextToken();
        rest = cmd.RestOfString();
        if (rest == '') rest = 'all';
        ParseRange();
        destcr = SocialCalc.coordToCr(dest);
        coloffset = destcr.col - cr1.col;
        rowoffset = destcr.row - cr1.row;
        numcols = cr2.col - cr1.col + 1;
        numrows = cr2.row - cr1.row + 1;
        movingcells = {};
        for (row = cr1.row; row <= cr2.row; row++) {
          for (col = cr1.col; col <= cr2.col; col++) {
            cr = SocialCalc.crToCoord(col, row);
            cell = sheet.GetAssuredCell(cr);
            if (cell.readonly) continue;
            if (saveundo) changes.AddUndo('set ' + cr + ' all', sheet.CellToString(cell));
            movingcells[cr] = new SocialCalc.Cell(cr);
            for (attrib in cellProperties) {
              if (typeof cell[attrib] === 'undefined') {
                continue;
              } else {
                movingcells[cr][attrib] = cell[attrib];
              }
              if (rest == 'all') {
                delete cell[attrib];
              }
              if (rest == 'formulas') {
                if (cellProperties[attrib] == 1 || cellProperties[attrib] == 3) {
                  delete cell[attrib];
                }
              }
              if (rest == 'formats') {
                if (cellProperties[attrib] == 2) {
                  delete cell[attrib];
                }
              }
            }
            if (rest == 'formulas') {
              cell.datavalue = '';
              cell.datatype = null;
              cell.formula = '';
              cell.valuetype = 'b';
            }
            if (rest == 'all') {
              delete sheet.cells[cr];
            }
          }
        }
        if (cmd1 == 'moveinsert') {
          inserthoriz = false;
          insertvert = false;
          if (rowoffset == 0 && (destcr.col < cr1.col || destcr.col > cr2.col)) {
            if (destcr.col < cr1.col) {
              pushamount = cr1.col - destcr.col;
              inserthoriz = -1;
            } else {
              destcr.col -= 1;
              coloffset = destcr.col - cr2.col;
              pushamount = destcr.col - cr2.col;
              inserthoriz = 1;
            }
          } else if (coloffset == 0 && (destcr.row < cr1.row || destcr.row > cr2.row)) {
            if (destcr.row < cr1.row) {
              pushamount = cr1.row - destcr.row;
              insertvert = -1;
            } else {
              destcr.row -= 1;
              rowoffset = destcr.row - cr2.row;
              pushamount = destcr.row - cr2.row;
              insertvert = 1;
            }
          } else {
            cmd1 = 'movepaste';
          }
        }
        movedto = {};
        if (insertvert) {
          for (row = 0; row < pushamount; row++) {
            for (col = cr1.col; col <= cr2.col; col++) {
              if (insertvert < 0) {
                crbase = SocialCalc.crToCoord(col, destcr.row + pushamount - row - 1);
                cr = SocialCalc.crToCoord(col, cr2.row - row);
              } else {
                crbase = SocialCalc.crToCoord(col, destcr.row - pushamount + row + 1);
                cr = SocialCalc.crToCoord(col, cr1.row + row);
              }
              basecell = sheet.GetAssuredCell(crbase);
              if (saveundo) changes.AddUndo('set ' + crbase + ' all', sheet.CellToString(basecell));
              cell = sheet.GetAssuredCell(cr);
              if (rest == 'all' || rest == 'formats') {
                for (attrib in cellProperties) {
                  if (cellProperties[attrib] == 1) continue;
                  if (typeof basecell[attrib] === 'undefined' || cellProperties[attrib] == 3) {
                    delete cell[attrib];
                  } else {
                    cell[attrib] = basecell[attrib];
                  }
                }
              }
              if (rest == 'all' || rest == 'formulas') {
                cell.datavalue = basecell.datavalue;
                cell.datatype = basecell.datatype;
                cell.valuetype = basecell.valuetype;
                cell.formula = basecell.formula;
                delete cell.parseinfo;
                cell.errors = basecell.errors;
              }
              delete cell.displaystring;
              movedto[crbase] = cr;
            }
          }
        }
        if (inserthoriz) {
          for (col = 0; col < pushamount; col++) {
            for (row = cr1.row; row <= cr2.row; row++) {
              if (inserthoriz < 0) {
                crbase = SocialCalc.crToCoord(destcr.col + pushamount - col - 1, row);
                cr = SocialCalc.crToCoord(cr2.col - col, row);
              } else {
                crbase = SocialCalc.crToCoord(destcr.col - pushamount + col + 1, row);
                cr = SocialCalc.crToCoord(cr1.col + col, row);
              }
              basecell = sheet.GetAssuredCell(crbase);
              if (saveundo) changes.AddUndo('set ' + crbase + ' all', sheet.CellToString(basecell));
              cell = sheet.GetAssuredCell(cr);
              if (rest == 'all' || rest == 'formats') {
                for (attrib in cellProperties) {
                  if (cellProperties[attrib] == 1) continue;
                  if (typeof basecell[attrib] === 'undefined' || cellProperties[attrib] == 3) {
                    delete cell[attrib];
                  } else {
                    cell[attrib] = basecell[attrib];
                  }
                }
              }
              if (rest == 'all' || rest == 'formulas') {
                cell.datavalue = basecell.datavalue;
                cell.datatype = basecell.datatype;
                cell.valuetype = basecell.valuetype;
                cell.formula = basecell.formula;
                delete cell.parseinfo;
                cell.errors = basecell.errors;
              }
              delete cell.displaystring;
              movedto[crbase] = cr;
            }
          }
        }
        if (destcr.col + numcols - 1 > attribs.lastcol) attribs.lastcol = destcr.col + numcols - 1;
        if (destcr.row + numrows - 1 > attribs.lastrow) attribs.lastrow = destcr.row + numrows - 1;
        for (row = cr1.row; row < cr1.row + numrows; row++) {
          for (col = cr1.col; col < cr1.col + numcols; col++) {
            cr = SocialCalc.crToCoord(col + coloffset, row + rowoffset);
            cell = sheet.GetAssuredCell(cr);
            if (cell.readonly) continue;
            if (saveundo) changes.AddUndo('set ' + cr + ' all', sheet.CellToString(cell));
            crbase = SocialCalc.crToCoord(col, row);
            movedto[crbase] = cr;
            if (rest == 'all' && !movingcells[crbase]) {
              delete sheet.cells[cr];
              continue;
            }
            basecell = movingcells[crbase];
            if (!basecell) basecell = sheet.GetAssuredCell(crbase);
            if (rest == 'all' || rest == 'formats') {
              for (attrib in cellProperties) {
                if (cellProperties[attrib] == 1) continue;
                if (typeof basecell[attrib] === 'undefined' || cellProperties[attrib] == 3) {
                  delete cell[attrib];
                } else {
                  cell[attrib] = basecell[attrib];
                }
              }
            }
            if (rest == 'all' || rest == 'formulas') {
              cell.datavalue = basecell.datavalue;
              cell.datatype = basecell.datatype;
              cell.valuetype = basecell.valuetype;
              cell.formula = basecell.formula;
              delete cell.parseinfo;
              cell.errors = basecell.errors;
              if (basecell.comment) {
                cell.comment = basecell.comment;
              } else if (cell.comment) {
                delete cell.comment;
              }
            }
            delete cell.displaystring;
          }
        }
        for (cr in sheet.cells) {
          cell = sheet.cells[cr];
          if (cell) {
            if (cell.datatype == 'f') {
              oldformula = cell.formula;
              cell.formula = SocialCalc.ReplaceFormulaCoords(oldformula, movedto);
              if (cell.formula != oldformula) {
                delete cell.parseinfo;
                if (saveundo && !movedto[cr]) {
                  changes.AddUndo('set ' + cr + ' formula ' + oldformula);
                }
              }
            } else {
              delete cell.parseinfo;
            }
          }
        }
        for (name in sheet.names) {
          if (sheet.names[name]) {
            v1 = sheet.names[name].definition;
            oldformula = v1;
            v2 = '';
            if (v1.charAt(0) == '=') {
              v2 = '=';
              v1 = v1.substring(1);
            }
            sheet.names[name].definition = v2 + SocialCalc.ReplaceFormulaCoords(v1, movedto);
            if (saveundo && sheet.names[name].definition != oldformula) {
              changes.AddUndo('name define ' + name + ' ' + oldformula);
            }
          }
        }
        attribs.needsrecalc = 'yes';
        break;
      case 'name':
        what = cmd.NextToken();
        name = cmd.NextToken();
        rest = cmd.RestOfString();
        name = name.toUpperCase().replace(/[^A-Z0-9_.]/g, '');
        if (name == '') break;
        if (what == 'define') {
          if (rest == '') break;
          if (sheet.names[name]) {
            if (saveundo)
              changes.AddUndo('name define ' + name + ' ' + sheet.names[name].definition);
            sheet.names[name].definition = rest;
          } else {
            if (saveundo) changes.AddUndo('name delete ' + name);
            sheet.names[name] = {
              definition: rest,
              desc: '',
            };
          }
        } else if (what == 'desc') {
          if (sheet.names[name]) {
            if (saveundo) changes.AddUndo('name desc ' + name + ' ' + sheet.names[name].desc);
            sheet.names[name].desc = rest;
          }
        } else if (what == 'delete') {
          if (saveundo) {
            if (sheet.names[name].desc)
              changes.AddUndo('name desc ' + name + ' ' + sheet.names[name].desc);
            changes.AddUndo('name define ' + name + ' ' + sheet.names[name].definition);
          }
          delete sheet.names[name];
        }
        attribs.needsrecalc = 'yes';
        break;
      case 'recalc':
        attribs.needsrecalc = 'yes';
        sheet.recalconce = true;
        break;
      case 'redisplay':
        sheet.renderneeded = true;
        break;
      case 'changedrendervalues':
        sheet.changedrendervalues = true;
        break;
      case 'pane':
        name = cmd.NextToken().toUpperCase();
        undoNum = 1;
        editor = SocialCalc.GetSpreadsheetControlObject().editor;
        if (name.toUpperCase() === 'ROW') {
          row = parseInt(cmd.NextToken(), 10);
          if (
            typeof editor.context.rowpanes[1] !== 'undefined' &&
            typeof editor.context.rowpanes[1].first === 'number'
          ) {
            undoNum = editor.context.rowpanes[1].first;
          }
          if (saveundo) changes.AddUndo('pane row ' + undoNum);
          while (editor.context.sheetobj.rowattribs.hide[row] == 'yes') {
            row++;
          }
          if (
            (!row || row <= editor.context.rowpanes[0].first) &&
            editor.context.rowpanes.length > 1
          ) {
            editor.context.rowpanes.length = 1;
          } else if (editor.context.rowpanes.length - 1 && !editor.timeout) {
            editor.context.SetRowPaneFirstLast(0, editor.context.rowpanes[0].first, row - 1);
            editor.context.SetRowPaneFirstLast(1, row, row);
          } else {
            editor.context.SetRowPaneFirstLast(0, editor.context.rowpanes[0].first, row - 1);
            editor.context.SetRowPaneFirstLast(1, row, row);
          }
          if (editor.griddiv) {
            trackLine = document.getElementById('trackingline-vertical');
            if (trackLine) {
              editor.griddiv.removeChild(trackLine);
              editor.FitToEditTable();
            }
          }
        } else {
          col = parseInt(cmd.NextToken(), 10);
          if (
            typeof editor.context.colpanes[1] !== 'undefined' &&
            typeof editor.context.colpanes[1].first === 'number'
          ) {
            undoNum = editor.context.colpanes[1].first;
          }
          if (saveundo) changes.AddUndo('pane col ' + undoNum);
          while (editor.context.sheetobj.colattribs.hide[SocialCalc.rcColname(col)] == 'yes') {
            col++;
          }
          if (
            (!col || col <= editor.context.colpanes[0].first) &&
            editor.context.colpanes.length > 1
          ) {
            editor.context.colpanes.length = 1;
          } else if (editor.context.colpanes.length - 1 && !editor.timeout) {
            editor.context.SetColPaneFirstLast(0, editor.context.colpanes[0].first, col - 1);
            editor.context.SetColPaneFirstLast(1, col, col);
          } else {
            editor.context.SetColPaneFirstLast(0, editor.context.colpanes[0].first, col - 1);
            editor.context.SetColPaneFirstLast(1, col, col);
          }
          if (editor.griddiv) {
            trackLine = document.getElementById('trackingline-horizon');
            if (trackLine) {
              editor.griddiv.removeChild(trackLine);
              editor.FitToEditTable();
            }
          }
        }
        sheet.renderneeded = true;
        break;
      case 'startcmdextension':
        name = cmd.NextToken();
        cmdextension = sheet.sci.CmdExtensionCallbacks[name];
        if (cmdextension) {
          cmdextension.func(name, cmdextension.data, sheet, cmd, saveundo);
        }
        break;
      case 'settimetrigger':
      case 'sendemail':
      case 'submitform':
        break;
      default:
        errortext = scc.s_escUnknownCmd + cmdstr;
        break;
    }
    if (SocialCalc._app) {
      if (cellChanged) {
        if (
          sheet.renderneeded == true &&
          attrib != 'value' &&
          attrib != 'text' &&
          attrib != 'formula' &&
          attrib != 'constant' &&
          attrib != 'empty'
        )
          sheet.widgetsClean = false;
        else if (attrib == 'all') sheet.widgetsClean = false;
      } else {
        if (sheet.renderneeded == true) sheet.widgetsClean = false;
      }
    }
    return errortext;
  };
  SC.SheetUndo = function (sheet) {
    var i;
    var tos = sheet.changes.TOS();
    var lastone = tos ? tos.undo.length - 1 : -1;
    var cmdstr = '';
    for (i = lastone; i >= 0; i--) {
      if (cmdstr) cmdstr += '\n';
      cmdstr += tos.undo[i];
    }
    sheet.changes.Undo();
    sheet.ScheduleSheetCommands(cmdstr, false);
  };
  SC.SheetRedo = function (sheet) {
    var tos, i;
    var didredo = sheet.changes.Redo();
    if (!didredo) {
      sheet.ScheduleSheetCommands('', false);
      return;
    }
    tos = sheet.changes.TOS();
    var cmdstr = '';
    for (i = 0; tos && i < tos.command.length; i++) {
      if (cmdstr) cmdstr += '\n';
      cmdstr += tos.command[i];
    }
    sheet.ScheduleSheetCommands(cmdstr, false);
  };
  SC.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;
  };
  SC.GetStyleNum = function (sheet, atype, style) {
    var num;
    if (style.length == 0) return 0;
    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;
  };
  SC.GetStyleString = function (sheet, atype, num) {
    if (!num) return null;
    return sheet[atype + 's'][num];
  };
  SC.RecalcInfo = {
    sheet: null,
    currentState: 0,
    state: {
      idle: 0,
      start_calc: 1,
      order: 2,
      calc: 3,
      start_wait: 4,
      done_wait: 5,
    },
    recalctimer: null,
    maxtimeslice: 100,
    timeslicedelay: 1,
    starttime: 0,
    queue: [],
    LoadSheet: function (_sheetname) {
      return false;
    },
  };
  SC.RecalcData = function () {
    this.inrecalc = true;
    this.celllist = [];
    this.celllistitem = 0;
    this.calclist = null;
    this.calclistlength = 0;
    this.firstcalc = null;
    this.lastcalc = null;
    this.nextcalc = null;
    this.count = 0;
    this.checkinfo = {};
  };
  SC.RecalcCheckInfo = function () {
    this.oldcoord = null;
    this.parsepos = 0;
    this.inrange = false;
    this.inrangestart = false;
    this.cr1 = null;
    this.cr2 = null;
    this.c1 = null;
    this.c2 = null;
    this.r1 = null;
    this.r2 = null;
    this.c = null;
    this.r = null;
  };
  SC.RecalcSheet = function (sheet) {
    var scri = SocialCalc.RecalcInfo;
    if (scri.currentState != scri.state.idle) {
      scri.queue.push(sheet);
      return;
    }
    delete sheet.attribs.circularreferencecell;
    SocialCalc.Formula.FreshnessInfoReset();
    SocialCalc.RecalcClearTimeout();
    scri.sheet = sheet;
    scri.currentState = scri.state.start_calc;
    scri.starttime = new Date();
    if (sheet.statuscallback) {
      sheet.statuscallback(scri, 'calcstart', null, sheet.statuscallbackparams);
    }
    SocialCalc.RecalcSetTimeout();
  };
  SC.RecalcSetTimeout = function () {
    var scri = SocialCalc.RecalcInfo;
    scri.recalctimer = window.setTimeout(SocialCalc.RecalcTimerRoutine, scri.timeslicedelay);
  };
  SC.RecalcClearTimeout = function () {
    var scri = SocialCalc.RecalcInfo;
    if (scri.recalctimer) {
      window.clearTimeout(scri.recalctimer);
      scri.recalctimer = null;
    }
  };
  SC.RecalcLoadedSheet = function (sheetname, str, recalcneeded, live) {
    var sheet;
    var scri = SocialCalc.RecalcInfo;
    var scf = SocialCalc.Formula;
    var effectiveSheetName = sheetname || scf.SheetCache.waitingForLoading;
    if (effectiveSheetName) {
      sheet = SocialCalc.Formula.AddSheetToCache(effectiveSheetName, str, live);
      if (recalcneeded && sheet && sheet.attribs.recalc != 'off') {
        sheet.previousrecalcsheet = scri.sheet;
        scri.sheet = sheet;
        scri.currentState = scri.state.start_calc;
      }
    }
    scf.SheetCache.waitingForLoading = null;
    SocialCalc.RecalcSetTimeout();
  };
  SC.RecalcTimerRoutine = function () {
    var eresult, cell, coord, status;
    var starttime = new Date();
    var count = 0;
    var scf = SocialCalc.Formula;
    var scri = SocialCalc.RecalcInfo;
    var sheet = scri.sheet;
    if (!sheet) {
      return;
    }
    var recalcdata = sheet.recalcdata || (sheet.recalcdata = {});
    var do_statuscallback = function (status, arg) {
      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) {
        if (!coord) continue;
        recalcdata.celllist.push(coord);
      }
      recalcdata.calclist = {};
      scri.currentState = scri.state.order;
    }
    if (scri.currentState == scri.state.order) {
      while (recalcdata.celllistitem < recalcdata.celllist.length) {
        coord = recalcdata.celllist[recalcdata.celllistitem++];
        SocialCalc.RecalcCheckCell(sheet, coord);
        if (Date.now() - starttime.getTime() >= scri.maxtimeslice) {
          do_statuscallback('calcorder', {
            coord,
            total: recalcdata.celllist.length,
            count: recalcdata.celllistitem,
          });
          SocialCalc.RecalcSetTimeout();
          return;
        }
      }
      do_statuscallback('calccheckdone', recalcdata.calclistlength);
      recalcdata.nextcalc = recalcdata.firstcalc;
      scri.currentState = scri.state.calc;
      SocialCalc.RecalcSetTimeout();
      return;
    }
    if (scri.currentState == scri.state.start_wait) {
      scri.currentState = scri.state.done_wait;
      if (scri.LoadSheet) {
        status = scri.LoadSheet(scf.SheetCache.waitingForLoading);
        if (status) {
          return;
        }
      }
      SocialCalc.RecalcLoadedSheet(null, '', false);
      return;
    }
    if (scri.currentState == scri.state.done_wait) {
      scri.currentState = scri.state.calc;
      SocialCalc.RecalcSetTimeout();
      return;
    }
    coord = sheet.recalcdata.nextcalc;
    while (coord) {
      cell = sheet.cells[coord];
      cell.parseinfo.coord = coord;
      eresult = scf.evaluate_parsed_formula(cell.parseinfo, sheet, false);
      if (scf.SheetCache.waitingForLoading) {
        if (scri.firstRenderScheduled != true) {
          var editor = SocialCalc.GetSpreadsheetControlObject().editor;
          editor.ScheduleRender(false);
          scri.firstRenderScheduled = true;
        }
        recalcdata.nextcalc = coord;
        recalcdata.count += count;
        do_statuscallback('calcloading', { sheetname: scf.SheetCache.waitingForLoading });
        scri.currentState = scri.state.start_wait;
        SocialCalc.RecalcSetTimeout();
        return;
      }
      if (scf.RemoteFunctionInfo.waitingForServer) {
        recalcdata.nextcalc = coord;
        recalcdata.count += count;
        do_statuscallback('calcserverfunc', {
          funcname: scf.RemoteFunctionInfo.waitingForServer,
          coord,
          total: recalcdata.calclistlength,
          count: recalcdata.count,
        });
        scri.currentState = scri.state.done_wait;
        return;
      }
      if (cell.datavalue != eresult.value || cell.valuetype != eresult.type) {
        cell.datavalue = eresult.value;
        cell.valuetype = eresult.type;
        delete cell.displaystring;
        sheet.recalcchangedavalue = true;
      }
      if (eresult.error) {
        cell.errors = eresult.error;
      }
      count++;
      coord = sheet.recalcdata.calclist[coord];
      if (Date.now() - starttime.getTime() >= scri.maxtimeslice) {
        recalcdata.nextcalc = coord;
        recalcdata.count += count;
        do_statuscallback('calcstep', {
          coord,
          total: recalcdata.calclistlength,
          count: recalcdata.count,
        });
        SocialCalc.RecalcSetTimeout();
        return;
      }
    }
    recalcdata.inrecalc = false;
    sheet.reRenderCellList = sheet.recalcdata.celllist;
    delete sheet.recalcdata;
    delete sheet.attribs.needsrecalc;
    scri.sheet = sheet.previousrecalcsheet || null;
    if (scri.sheet) {
      scri.currentState = scri.state.calc;
      SocialCalc.RecalcSetTimeout();
      return;
    }
    scf.FreshnessInfo.recalc_completed = true;
    scri.currentState = scri.state.idle;
    do_statuscallback('calcfinished', Date.now() - scri.starttime.getTime());
    if (scri.queue.length > 0) {
      sheet = scri.queue.shift();
      sheet.RecalcSheet();
    }
    return;
  };
  SC.RecalcCheckCell = function (sheet, startcoord) {
    var parseinfo, ttext, ttype, i, rangecoord, value, pos, pos2, cell, coordvals;
    var scf = SocialCalc.Formula;
    var tokentype = scf.TokenType;
    var token_op = tokentype.op;
    var token_name = tokentype.name;
    var token_coord = tokentype.coord;
    var recalcdata = sheet.recalcdata;
    var checkinfo = recalcdata.checkinfo;
    var sheetref = false;
    var oldcoord = null;
    var coord = startcoord;
    mainloop: while (coord) {
      cell = sheet.cells[coord];
      coordvals = checkinfo[coord];
      if (!cell || cell.datatype != 'f' || (coordvals && typeof coordvals != 'object')) {
        coord = oldcoord;
        if (checkinfo[coord]) oldcoord = checkinfo[coord].oldcoord;
        continue;
      }
      if (!coordvals) {
        coordvals = new SocialCalc.RecalcCheckInfo();
        checkinfo[coord] = coordvals;
      }
      if (cell.errors) {
        delete cell.errors;
      }
      if (!cell.parseinfo) {
        cell.parseinfo = scf.ParseFormulaIntoTokens(cell.formula);
      }
      parseinfo = cell.parseinfo;
      for (i = coordvals.parsepos; i < parseinfo.length; i++) {
        if (coordvals.inrange) {
          if (coordvals.inrangestart) {
            if (coordvals.cr1.col > coordvals.cr2.col) {
              coordvals.c1 = coordvals.cr2.col;
              coordvals.c2 = coordvals.cr1.col;
            } else {
              coordvals.c1 = coordvals.cr1.col;
              coordvals.c2 = coordvals.cr2.col;
            }
            coordvals.c = coordvals.c1 - 1;
            if (coordvals.cr1.row > coordvals.cr2.row) {
              coordvals.r1 = coordvals.cr2.row;
              coordvals.r2 = coordvals.cr1.row;
            } else {
              coordvals.r1 = coordvals.cr1.row;
              coordvals.r2 = coordvals.cr2.row;
            }
            coordvals.r = coordvals.r1;
            coordvals.inrangestart = false;
          } else {
          }
          coordvals.c += 1;
          if (coordvals.c > coordvals.c2) {
            coordvals.r += 1;
            if (coordvals.r > coordvals.r2) {
              coordvals.inrange = false;
              continue;
            }
            coordvals.c = coordvals.c1;
          }
          rangecoord = SocialCalc.crToCoord(coordvals.c, coordvals.r);
          coordvals.parsepos = i;
          coordvals.oldcoord = oldcoord;
          oldcoord = coord;
          coord = rangecoord;
          if (checkinfo[coord] && typeof checkinfo[coord] == 'object') {
            cell.errors = SocialCalc.Constants.s_caccCircRef + startcoord;
            checkinfo[startcoord] = true;
            if (!recalcdata.firstcalc) recalcdata.firstcalc = startcoord;
            else recalcdata.calclist[recalcdata.lastcalc] = startcoord;
            recalcdata.lastcalc = startcoord;
            recalcdata.calclistlength++;
            sheet.attribs.circularreferencecell = coord + '|' + oldcoord;
            return cell.errors;
          }
          continue mainloop;
        }
        ttype = parseinfo[i].type;
        ttext = parseinfo[i].text;
        if (ttype == token_op) {
          if (ttext == '!') {
            sheetref = true;
          } else if (ttext != ':') {
            sheetref = false;
          }
        }
        if (ttype == token_name) {
          value = scf.LookupName(sheet, ttext);
          if (value.type == 'range') {
            pos = value.value.indexOf('|');
            coordvals.cr1 = SocialCalc.coordToCr(value.value.substring(0, pos));
            pos2 = value.value.indexOf('|', pos + 1);
            coordvals.cr2 = SocialCalc.coordToCr(value.value.substring(pos + 1, pos2));
            coordvals.inrange = true;
            coordvals.inrangestart = true;
            i = i - 1;
            continue;
          } else if (value.type == 'coord') {
            ttype = token_coord;
            ttext = value.value;
          }
        }
        if (ttype == token_coord) {
          if (
            i >= 2 &&
            parseinfo[i - 1].type == token_op &&
            parseinfo[i - 1].text == ':' &&
            parseinfo[i - 2].type == token_coord &&
            !sheetref
          ) {
            coordvals.cr1 = SocialCalc.coordToCr(parseinfo[i - 2].text);
            coordvals.cr2 = SocialCalc.coordToCr(ttext);
            coordvals.inrange = true;
            coordvals.inrangestart = true;
            i = i - 1;
            continue;
          } else if (!sheetref) {
            if (ttext.indexOf('$') != -1) ttext = ttext.replace(/\$/g, '');
            coordvals.parsepos = i + 1;
            coordvals.oldcoord = oldcoord;
            oldcoord = coord;
            coord = ttext;
            if (checkinfo[coord] && typeof checkinfo[coord] == 'object') {
              cell.errors = SocialCalc.Constants.s_caccCircRef + startcoord;
              checkinfo[startcoord] = true;
              if (!recalcdata.firstcalc) {
                recalcdata.firstcalc = startcoord;
              } else {
                recalcdata.calclist[recalcdata.lastcalc] = startcoord;
              }
              recalcdata.lastcalc = startcoord;
              recalcdata.calclistlength++;
              sheet.attribs.circularreferencecell = coord + '|' + oldcoord;
              return cell.errors;
            }
            continue mainloop;
          }
        }
      }
      sheetref = false;
      checkinfo[coord] = true;
      if (!recalcdata.firstcalc) {
        recalcdata.firstcalc = coord;
      } else {
        recalcdata.calclist[recalcdata.lastcalc] = coord;
      }
      recalcdata.lastcalc = coord;
      recalcdata.calclistlength++;
      coord = oldcoord;
      oldcoord = checkinfo[coord] ? checkinfo[coord].oldcoord : null;
    }
    return '';
  };
  SC.Parse = function (str) {
    this.str = str;
    this.pos = 0;
    this.delimiter = ' ';
    this.lineEnd = str.indexOf('\n');
    if (this.lineEnd < 0) {
      this.lineEnd = str.length;
    }
  };
  SC.Parse.prototype.NextToken = function () {
    if (this.pos < 0) return '';
    var pos2 = this.str.indexOf(this.delimiter, this.pos);
    var pos1 = this.pos;
    if (pos2 > this.lineEnd) {
      pos2 = this.lineEnd;
    }
    if (pos2 >= 0) {
      this.pos = pos2 + 1;
      return this.str.substring(pos1, pos2);
    } else {
      this.pos = this.lineEnd;
      return this.str.substring(pos1, this.lineEnd);
    }
  };
  SC.Parse.prototype.RestOfString = function () {
    var oldpos = this.pos;
    if (this.pos < 0 || this.pos >= this.lineEnd) return '';
    this.pos = this.lineEnd;
    return this.str.substring(oldpos, this.lineEnd);
  };
  SC.Parse.prototype.RestOfStringNoMove = function () {
    if (this.pos < 0 || this.pos >= this.lineEnd) return '';
    return this.str.substring(this.pos, this.lineEnd);
  };
  SC.Parse.prototype.NextLine = function () {
    this.pos = this.lineEnd + 1;
    this.lineEnd = this.str.indexOf('\n', this.pos);
    if (this.lineEnd < 0) {
      this.lineEnd = this.str.length;
    }
  };
  SC.Parse.prototype.EOF = function () {
    if (this.pos < 0 || this.pos >= this.str.length) return true;
    return false;
  };
  SC.UndoStack = function () {
    this.stack = [];
    this.tos = -1;
    this.maxRedo = 0;
    this.maxUndo = 50;
  };
  SC.UndoStack.prototype.PushChange = function (type) {
    while (this.stack.length > 0 && this.stack.length - 1 > this.tos) {
      this.stack.pop();
    }
    this.stack.push({
      command: [],
      type,
      undo: [],
    });
    if (this.maxRedo && this.stack.length > this.maxRedo) {
      this.stack.shift();
    }
    if (this.maxUndo && this.stack.length > this.maxUndo) {
      this.stack[this.stack.length - this.maxUndo - 1].undo = [];
    }
    this.tos = this.stack.length - 1;
  };
  SC.UndoStack.prototype.AddDo = function () {
    if (!this.stack[this.stack.length - 1]) {
      return;
    }
    var args = [];
    for (var i = 0; i < arguments.length; i++) {
      if (arguments[i] != null) args.push(arguments[i]);
    }
    var cmd = args.join(' ');
    this.stack[this.stack.length - 1].command.push(cmd);
  };
  SC.UndoStack.prototype.AddUndo = function () {
    if (!this.stack[this.stack.length - 1]) {
      return;
    }
    var args = [];
    for (var i = 0; i < arguments.length; i++) {
      if (arguments[i] != null) args.push(arguments[i]);
    }
    var cmd = args.join(' ');
    this.stack[this.stack.length - 1].undo.push(cmd);
  };
  SC.UndoStack.prototype.TOS = function () {
    if (this.tos >= 0) return this.stack[this.tos];
    else return null;
  };
  SC.UndoStack.prototype.Undo = function () {
    if (this.tos >= 0 && (!this.maxUndo || this.tos > this.stack.length - this.maxUndo - 1)) {
      this.tos -= 1;
      return true;
    } else {
      return false;
    }
  };
  SC.UndoStack.prototype.Redo = function () {
    if (this.tos < this.stack.length - 1) {
      this.tos += 1;
      return true;
    } else {
      return false;
    }
  };
  SC.Clipboard = { clipboard: '' };
  SC.RenderContext = function (sheetobj) {
    var attribs = sheetobj.attribs;
    var scc = SocialCalc.Constants;
    this.sheetobj = sheetobj;
    this.hideRowsCols = false;
    this.showGrid = false;
    this.showRCHeaders = false;
    this.rownamewidth = scc.defaultRowNameWidth;
    this.pixelsPerRow = scc.defaultAssumedRowHeight;
    this.cellskip = {};
    this.coordToCR = {};
    this.colwidth = [];
    this.rowheight = [];
    this.totalwidth = 0;
    this.totalheight = 0;
    this.rowpanes = [];
    this.colpanes = [];
    this.colunhideleft = [];
    this.colunhideright = [];
    this.rowunhidetop = [];
    this.rowunhidebottom = [];
    this.maxcol = 0;
    this.maxrow = 0;
    this.highlights = {};
    this.cursorsuffix = '';
    this.highlightTypes = {
      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;
    this.defaultlinkstyle = null;
    this.defaultHTMLlinkstyle = { type: 'html' };
    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;
    this.commentCSS = scc.defaultCommentStyle;
    this.commentNoGridClassName = scc.defaultCommentNoGridClass;
    this.commentNoGridCSS = scc.defaultCommentNoGridStyle;
    this.readonlyClassName = scc.defaultReadonlyClass;
    this.readonlyCSS = scc.defaultReadonlyStyle;
    this.readonlyNoGridClassName = scc.defaultReadonlyNoGridClass;
    this.readonlyNoGridCSS = scc.defaultReadonlyNoGridStyle;
    this.readonlyComment = scc.defaultReadonlyComment;
    this.classnames = {
      colname: scc.defaultColnameClass,
      rowname: scc.defaultRownameClass,
      selectedcolname: scc.defaultSelectedColnameClass,
      selectedrowname: scc.defaultSelectedRownameClass,
      upperleft: scc.defaultUpperLeftClass,
      skippedcell: scc.defaultSkippedCellClass,
      panedivider: scc.defaultPaneDividerClass,
      unhideleft: scc.defaultUnhideLeftClass,
      unhideright: scc.defaultUnhideRightClass,
      unhidetop: scc.defaultUnhideTopClass,
      unhidebottom: scc.defaultUnhideBottomClass,
      colresizebar: scc.defaultColResizeBarClass,
      rowresizebar: scc.defaultRowResizeBarClass,
    };
    this.explicitStyles = {
      colname: scc.defaultColnameStyle,
      rowname: scc.defaultRownameStyle,
      selectedcolname: scc.defaultSelectedColnameStyle,
      selectedrowname: scc.defaultSelectedRownameStyle,
      upperleft: scc.defaultUpperLeftStyle,
      skippedcell: scc.defaultSkippedCellStyle,
      panedivider: scc.defaultPaneDividerStyle,
      unhideleft: scc.defaultUnhideLeftStyle,
      unhideright: scc.defaultUnhideRightStyle,
      unhidetop: scc.defaultUnhideTopStyle,
      unhidebottom: scc.defaultUnhideBottomStyle,
    };
    this.cellskip = null;
    this.needcellskip = true;
    this.fonts = [];
    this.layouts = [];
    this.needprecompute = true;
    if (attribs) {
      this.rowpanes[0] = {
        first: 1,
        last: attribs.lastrow,
      };
      this.colpanes[0] = {
        first: 1,
        last: attribs.lastcol,
      };
      this.usermaxcol = attribs.usermaxcol;
      this.usermaxrow = attribs.usermaxrow;
    } else throw scc.s_rcMissingSheet;
  };
  SC.RenderContext.prototype.PrecomputeSheetFontsAndLayouts = function () {
    SocialCalc.PrecomputeSheetFontsAndLayouts(this);
  };
  SC.RenderContext.prototype.CalculateCellSkipData = function () {
    SocialCalc.CalculateCellSkipData(this);
  };
  SC.RenderContext.prototype.CalculateColWidthData = function () {
    SocialCalc.CalculateColWidthData(this);
  };
  SC.RenderContext.prototype.CalculateRowHeightData = function () {
    SocialCalc.CalculateRowHeightData(this);
  };
  SC.RenderContext.prototype.SetRowPaneFirstLast = function (panenum, first, last) {
    this.rowpanes[panenum] = {
      first,
      last,
    };
  };
  SC.RenderContext.prototype.SetColPaneFirstLast = function (panenum, first, last) {
    this.colpanes[panenum] = {
      first,
      last,
    };
  };
  SC.RenderContext.prototype.CoordInPane = function (coord, rowpane, colpane) {
    return SocialCalc.CoordInPane(this, coord, rowpane, colpane);
  };
  SC.RenderContext.prototype.CellInPane = function (row, col, rowpane, colpane) {
    return SocialCalc.CellInPane(this, row, col, rowpane, colpane);
  };
  SC.RenderContext.prototype.InitializeTable = function (tableobj) {
    SocialCalc.InitializeTable(this, tableobj);
  };
  SC.RenderContext.prototype.RenderSheet = function (oldtable, linkstyle) {
    return SocialCalc.RenderSheet(this, oldtable, linkstyle);
  };
  SC.RenderContext.prototype.RenderColGroup = function () {
    return SocialCalc.RenderColGroup(this);
  };
  SC.RenderContext.prototype.RenderColHeaders = function () {
    return SocialCalc.RenderColHeaders(this);
  };
  SC.RenderContext.prototype.RenderSizingRow = function () {
    return SocialCalc.RenderSizingRow(this);
  };
  SC.RenderContext.prototype.RenderRow = function (rownum, rowpane, linkstyle) {
    return SocialCalc.RenderRow(this, rownum, rowpane, linkstyle);
  };
  SC.RenderContext.prototype.RenderSpacingRow = function () {
    return SocialCalc.RenderSpacingRow(this);
  };
  SC.RenderContext.prototype.RenderCell = function (
    rownum,
    colnum,
    rowpane,
    colpane,
    noElement,
    linkstyle,
  ) {
    return SocialCalc.RenderCell(this, rownum, colnum, rowpane, colpane, noElement, linkstyle);
  };
  SC.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++) {
      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.*)$/);
      if (!parts) continue;
      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);
    if (attribs.defaultlayout) {
      sparts = sheetobj.layouts[attribs.defaultlayout].match(layoutre);
    } else {
      sparts = ['', '*', '*', '*', '*', '*'];
    }
    for (num = 1; num < sheetobj.layouts.length; num++) {
      s = sheetobj.layouts[num];
      parts = s.match(layoutre);
      if (!parts) continue;
      for (i = 1; i <= 5; i++) {
        if (parts[i] == '*') {
          parts[i] = sparts[i] != '*' ? sparts[i] : dparts[i];
        }
      }
      context.layouts[num] =
        'padding:' +
        parts[1] +
        ' ' +
        parts[2] +
        ' ' +
        parts[3] +
        ' ' +
        parts[4] +
        ';vertical-align:' +
        parts[5] +
        ';';
    }
    context.needprecompute = false;
  };
  SC.CalculateCellSkipData = function (context) {
    var row, col, coord, cell, colspan, rowspan, skiprow, skipcol, skipcoord;
    var sheetobj = context.sheetobj;
    context.maxrow = 0;
    context.maxcol = 0;
    context.cellskip = {};
    for (row = 1; row <= sheetobj.attribs.lastrow; row++) {
      for (col = 1; col <= sheetobj.attribs.lastcol; col++) {
        coord = SocialCalc.crToCoord(col, row);
        cell = sheetobj.cells[coord];
        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++) {
              skipcoord = SocialCalc.crToCoord(skipcol, skiprow);
              if (skipcoord == coord) {
                context.coordToCR[coord] = {
                  row,
                  col,
                };
              } else {
                context.cellskip[skipcoord] = coord;
              }
              if (skiprow > context.maxrow) context.maxrow = skiprow;
              if (skipcol > context.maxcol) context.maxcol = skipcol;
            }
          }
        }
      }
    }
    context.needcellskip = false;
  };
  SC.CalculateColWidthData = function (context) {
    var colnum, colname, colwidth, totalwidth, colpane;
    var sheetobj = context.sheetobj;
    totalwidth = context.showRCHeaders ? context.rownamewidth - 0 : 0;
    for (colpane = 0; colpane < context.colpanes.length; colpane++) {
      for (
        colnum = context.colpanes[colpane].first;
        colnum <= context.colpanes[colpane].last;
        colnum++
      ) {
        colname = SocialCalc.rcColname(colnum);
        if (sheetobj.colattribs.hide[colname] == 'yes') {
          context.colwidth[colnum] = 0;
        } else {
          colwidth =
            sheetobj.colattribs.width[colname] ||
            sheetobj.attribs.defaultcolwidth ||
            SocialCalc.Constants.defaultColWidth;
          if (colwidth == 'blank' || colwidth == 'auto') colwidth = '';
          context.colwidth[colnum] = colwidth + '';
          totalwidth += colwidth && colwidth - 0 > 0 ? colwidth - 0 : 10;
        }
      }
    }
    context.totalwidth = totalwidth;
  };
  SC.CalculateRowHeightData = function (context) {
    var rownum, rowheight, totalheight, rowpane;
    var sheetobj = context.sheetobj;
    totalheight = context.showRCHeaders ? context.pixelsPerRow : 0;
    for (rowpane = 0; rowpane < context.rowpanes.length; rowpane++) {
      for (
        rownum = context.rowpanes[rowpane].first;
        rownum <= context.rowpanes[rowpane].last;
        rownum++
      ) {
        if (sheetobj.rowattribs.hide[rownum] === 'yes') {
          context.rowheight[rownum] = 0;
        } else {
          rowheight =
            sheetobj.rowattribs.height[rownum] ||
            sheetobj.attribs.defaultrowheight ||
            SocialCalc.Constants.defaultAssumedRowHeight;
          if (rowheight === 'blank' || rowheight === 'auto') rowheight = '';
          context.rowheight[rownum] = rowheight + '';
          totalheight += rowheight && rowheight - 0 > 0 ? rowheight - 0 : 10;
        }
      }
    }
    context.totalheight = totalheight;
  };
  SC.InitializeTable = function (context, tableobj) {
    tableobj.style.borderCollapse = 'collapse';
    tableobj.cellSpacing = '0';
    tableobj.cellPadding = '0';
    tableobj.style.width = context.totalwidth + 'px';
  };
  SC.RenderSheet = function (context, oldtable, linkstyle) {
    var newrow, rowpane, rownum;
    var tableobj, colgroupobj, tbodyobj, parentnode;
    if (context.sheetobj.changedrendervalues) {
      context.needcellskip = true;
      context.needprecompute = true;
      context.sheetobj.changedrendervalues = false;
    }
    if (context.needcellskip) {
      context.CalculateCellSkipData();
    }
    if (context.needprecompute) {
      context.PrecomputeSheetFontsAndLayouts();
    }
    context.CalculateColWidthData();
    context.CalculateRowHeightData();
    tableobj = SocialCalc.document.createElement('table');
    context.InitializeTable(tableobj);
    colgroupobj = context.RenderColGroup();
    tableobj.appendChild(colgroupobj);
    tbodyobj = SocialCalc.document.createElement('tbody');
    tbodyobj.appendChild(context.RenderSizingRow());
    if (context.showRCHeaders) {
      newrow = context.RenderColHeaders();
      if (newrow) tbodyobj.appendChild(newrow);
    }
    for (rowpane = 0; rowpane < context.rowpanes.length; rowpane++) {
      for (
        rownum = context.rowpanes[rowpane].first;
        rownum <= context.rowpanes[rowpane].last;
        rownum++
      ) {
        newrow = context.RenderRow(rownum, rowpane, linkstyle);
        tbodyobj.appendChild(newrow);
      }
      if (rowpane < context.rowpanes.length - 1) {
        newrow = context.RenderSpacingRow();
        tbodyobj.appendChild(newrow);
      }
    }
    tableobj.appendChild(tbodyobj);
    if (oldtable) {
      parentnode = oldtable.parentNode;
      if (parentnode) parentnode.replaceChild(tableobj, oldtable);
    }
    return tableobj;
  };
  SC.RenderRow = function (context, rownum, rowpane, linkstyle) {
    var sheetobj = context.sheetobj;
    var result = SocialCalc.document.createElement('tr');
    var colnum, newcol, colpane, newdiv;
    if (context.showRCHeaders) {
      newcol = SocialCalc.document.createElement('td');
      if (context.classnames) newcol.className = context.classnames.rowname;
      if (context.explicitStyles) newcol.style.cssText = context.explicitStyles.rowname;
      newcol.width = context.rownamewidth;
      newcol.height = context.rowheight[rownum];
      newcol.style.verticalAlign = 'top';
      newcol.innerHTML = rownum + '';
      if (
        rownum < context.rowpanes[context.rowpanes.length - 1].last &&
        sheetobj.rowattribs.hide[rownum + 1] == 'yes'
      ) {
        var container = SocialCalc.document.createElement('div');
        container.style.position = 'relative';
        var unhide = SocialCalc.document.createElement('div');
        if (context.classnames) unhide.className = context.classnames.unhidetop;
        if (context.explicitStyles) unhide.style.cssText = context.explicitStyles.unhidetop;
        var fixPosition =
          context.rowheight[rownum] - 0 - SocialCalc.Constants.defaultAssumedRowHeight;
        fixPosition = fixPosition === 0 ? 4 : fixPosition;
        unhide.style.bottom = '-' + fixPosition + 'px';
        context.rowunhidetop[rownum] = unhide;
        container.appendChild(unhide);
        newcol.appendChild(container);
      }
      if (rownum > 1 && sheetobj.rowattribs.hide[rownum - 1] == 'yes') {
        var unhide = SocialCalc.document.createElement('div');
        if (context.classnames) unhide.className = context.classnames.unhidebottom;
        if (context.explicitStyles) unhide.style.cssText = context.explicitStyles.unhidebottom;
        context.rowunhidebottom[rownum] = unhide;
        newcol.appendChild(unhide);
      }
      var resizeBar = SocialCalc.document.createElement('div');
      resizeBar.className = context.classnames.rowresizebar;
      newcol.appendChild(resizeBar);
      result.appendChild(newcol);
    }
    for (colpane = 0; colpane < context.colpanes.length; colpane++) {
      for (
        colnum = context.colpanes[colpane].first;
        colnum <= context.colpanes[colpane].last;
        colnum++
      ) {
        newcol = context.RenderCell(rownum, colnum, rowpane, colpane, null, linkstyle);
        if (newcol) result.appendChild(newcol);
      }
      if (colpane < context.colpanes.length - 1) {
        newcol = SocialCalc.document.createElement('td');
        newcol.width = context.defaultpanedividerwidth;
        if (context.classnames.panedivider) newcol.className = context.classnames.panedivider;
        if (context.explicitStyles.panedivider)
          newcol.style.cssText = context.explicitStyles.panedivider;
        newdiv = SocialCalc.document.createElement('div');
        newdiv.style.width = context.defaultpanedividerwidth + 'px';
        newdiv.style.overflow = 'hidden';
        newcol.appendChild(newdiv);
        result.appendChild(newcol);
      }
    }
    if (sheetobj.rowattribs.hide[rownum] == 'yes') {
      result.style.cssText += ';display:none';
    }
    return result;
  };
  SC.RenderSpacingRow = function (context) {
    var colnum, newcol, colpane, w;
    var result = SocialCalc.document.createElement('tr');
    if (context.showRCHeaders) {
      newcol = SocialCalc.document.createElement('td');
      newcol.width = context.rownamewidth;
      newcol.height = context.defaultpanedividerheight;
      if (context.classnames.panedivider) newcol.className = context.classnames.panedivider;
      if (context.explicitStyles.panedivider)
        newcol.style.cssText = context.explicitStyles.panedivider;
      result.appendChild(newcol);
    }
    for (colpane = 0; colpane < context.colpanes.length; colpane++) {
      for (
        colnum = context.colpanes[colpane].first;
        colnum <= context.colpanes[colpane].last;
        colnum++
      ) {
        newcol = SocialCalc.document.createElement('td');
        w = context.colwidth[colnum];
        if (w) newcol.width = w;
        newcol.height = context.defaultpanedividerheight;
        if (context.classnames.panedivider) newcol.className = context.classnames.panedivider;
        if (context.explicitStyles.panedivider)
          newcol.style.cssText = context.explicitStyles.panedivider;
        result.appendChild(newcol);
      }
      if (colpane < context.colpanes.length - 1) {
        newcol = SocialCalc.document.createElement('td');
        newcol.width = context.defaultpanedividerwidth;
        newcol.height = context.defaultpanedividerheight;
        if (context.classnames.panedivider) newcol.className = context.classnames.panedivider;
        if (context.explicitStyles.panedivider)
          newcol.style.cssText = context.explicitStyles.panedivider;
        result.appendChild(newcol);
      }
    }
    return result;
  };
  SC.RenderColHeaders = function (context) {
    var sheetobj = context.sheetobj;
    var result = SocialCalc.document.createElement('tr');
    var colnum, newcol, colpane;
    if (!context.showRCHeaders) return null;
    newcol = SocialCalc.document.createElement('td');
    if (context.classnames) newcol.className = context.classnames.upperleft;
    if (context.explicitStyles) newcol.style.cssText = context.explicitStyles.upperleft;
    newcol.width = context.rownamewidth;
    result.appendChild(newcol);
    for (colpane = 0; colpane < context.colpanes.length; colpane++) {
      for (
        colnum = context.colpanes[colpane].first;
        colnum <= context.colpanes[colpane].last;
        colnum++
      ) {
        newcol = SocialCalc.document.createElement('td');
        if (context.classnames) newcol.className = context.classnames.colname;
        if (context.explicitStyles) newcol.style.cssText = context.explicitStyles.colname;
        if (sheetobj.colattribs.hide[SocialCalc.rcColname(colnum)] == 'yes') {
          newcol.style.cssText += ';display:none';
        }
        newcol.innerHTML = SocialCalc.rcColname(colnum);
        if (
          colnum < context.colpanes[context.colpanes.length - 1].last &&
          sheetobj.colattribs.hide[SocialCalc.rcColname(colnum + 1)] == 'yes'
        ) {
          var unhide = SocialCalc.document.createElement('div');
          if (context.classnames) unhide.className = context.classnames.unhideleft;
          if (context.explicitStyles) unhide.style.cssText = context.explicitStyles.unhideleft;
          context.colunhideleft[colnum] = unhide;
          newcol.appendChild(unhide);
        }
        if (colnum > 1 && sheetobj.colattribs.hide[SocialCalc.rcColname(colnum - 1)] == 'yes') {
          unhide = SocialCalc.document.createElement('div');
          if (context.classnames) unhide.className = context.classnames.unhideright;
          if (context.explicitStyles) unhide.style.cssText = context.explicitStyles.unhideright;
          context.colunhideright[colnum] = unhide;
          newcol.appendChild(unhide);
        }
        var resizeBar = SocialCalc.document.createElement('span');
        resizeBar.style.height = SocialCalc.Constants.defaultAssumedRowHeight + 'px';
        resizeBar.className = context.classnames.colresizebar;
        newcol.appendChild(resizeBar);
        result.appendChild(newcol);
      }
      if (colpane < context.colpanes.length - 1) {
        newcol = SocialCalc.document.createElement('td');
        newcol.width = context.defaultpanedividerwidth;
        if (context.classnames.panedivider) newcol.className = context.classnames.panedivider;
        if (context.explicitStyles.panedivider)
          newcol.style.cssText = context.explicitStyles.panedivider;
        result.appendChild(newcol);
      }
    }
    return result;
  };
  SC.RenderColGroup = function (context) {
    var colpane, colnum, newcol, t;
    var sheetobj = context.sheetobj;
    var result = SocialCalc.document.createElement('colgroup');
    if (context.showRCHeaders) {
      newcol = SocialCalc.document.createElement('col');
      newcol.width = context.rownamewidth;
      result.appendChild(newcol);
    }
    for (colpane = 0; colpane < context.colpanes.length; colpane++) {
      for (
        colnum = context.colpanes[colpane].first;
        colnum <= context.colpanes[colpane].last;
        colnum++
      ) {
        newcol = SocialCalc.document.createElement('col');
        if (sheetobj.colattribs.hide[SocialCalc.rcColname(colnum)] == 'yes') {
          newcol.width = '1';
        } else {
          t = context.colwidth[colnum];
          if (t) newcol.width = t;
          result.appendChild(newcol);
        }
      }
      if (colpane < context.colpanes.length - 1) {
        newcol = SocialCalc.document.createElement('col');
        newcol.width = context.defaultpanedividerwidth;
        result.appendChild(newcol);
      }
    }
    return result;
  };
  SC.RenderSizingRow = function (context) {
    var colpane, colnum, newcell, t;
    var sheetobj = context.sheetobj;
    var result = SocialCalc.document.createElement('tr');
    if (context.showRCHeaders) {
      newcell = SocialCalc.document.createElement('td');
      newcell.style.width = context.rownamewidth + 'px';
      newcell.height = '1';
      result.appendChild(newcell);
    }
    for (colpane = 0; colpane < context.colpanes.length; colpane++) {
      for (
        colnum = context.colpanes[colpane].first;
        colnum <= context.colpanes[colpane].last;
        colnum++
      ) {
        newcell = SocialCalc.document.createElement('td');
        if (sheetobj.colattribs.hide[SocialCalc.rcColname(colnum)] == 'yes') {
          newcell.width = '1';
        } else {
          t = context.colwidth[colnum];
          if (t) newcell.width = t;
        }
        newcell.height = '1';
        result.appendChild(newcell);
      }
      if (colpane < context.colpanes.length - 1) {
        newcell = SocialCalc.document.createElement('td');
        newcell.width = context.defaultpanedividerwidth;
        newcell.height = '1';
        result.appendChild(newcell);
      }
    }
    return result;
  };
  SC.RenderCell = function (context, rownum, colnum, rowpane, colpane, noElement, linkstyle) {
    var sheetobj = context.sheetobj;
    var num, t, result, span, cell, endcell, sheetattribs;
    var stylestr = '';
    var scc;
    rownum = rownum - 0;
    colnum = colnum - 0;
    var coord = SocialCalc.crToCoord(colnum, rownum);
    if (context.cellskip[coord]) {
      if (context.CoordInPane(context.cellskip[coord], rowpane, colpane)) {
        return null;
      }
      result = noElement ? SocialCalc.CreatePseudoElement() : SocialCalc.document.createElement('td');
      if (context.classnames.skippedcell) result.className = context.classnames.skippedcell;
      if (context.explicitStyles.skippedcell)
        result.style.cssText = context.explicitStyles.skippedcell;
      result.innerHTML = '&nbsp;';
      return result;
    }
    result = noElement ? SocialCalc.CreatePseudoElement() : SocialCalc.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 || sheetobj.widgetsClean == false) {
      cell.displaystring = SocialCalc.FormatValueForDisplay(
        sheetobj,
        cell.datavalue,
        coord,
        linkstyle || context.defaultlinkstyle,
      );
    }
    result.innerHTML = cell.displaystring;
    num = cell.layout || sheetattribs.defaultlayout;
    if (num && typeof context.layouts[num] !== 'undefined') {
      stylestr += context.layouts[num];
    } else {
      stylestr += scc.defaultCellLayout;
    }
    num = cell.font || sheetattribs.defaultfont;
    if (num && typeof context.fonts[num] !== 'undefined') {
      t = context.fonts[num];
      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 && typeof sheetobj.colors[num] !== 'undefined')
      stylestr += 'color:' + sheetobj.colors[num] + ';';
    num = cell.bgcolor || sheetattribs.defaultbgcolor;
    if (num && typeof sheetobj.colors[num] !== 'undefined')
      stylestr += 'background-color:' + sheetobj.colors[num] + ';';
    num = cell.cellformat;
    if (num && typeof sheetobj.cellformats[num] !== 'undefined') {
      stylestr += 'text-align:' + sheetobj.cellformats[num] + ';';
    } else {
      t = cell.valuetype.charAt(0);
      if (t == 't') {
        num = sheetattribs.defaulttextformat;
        if (num && typeof sheetobj.cellformats[num] !== 'undefined')
          stylestr += 'text-align:' + sheetobj.cellformats[num] + ';';
      } else if (t == 'n') {
        num = sheetattribs.defaultnontextformat;
        if (num && typeof sheetobj.cellformats[num] !== 'undefined') {
          stylestr += 'text-align:' + sheetobj.cellformats[num] + ';';
        } else {
          stylestr += 'text-align:right;';
        }
      } else stylestr += 'text-align:left;';
    }
    if (cell.colspan > 1 || cell.rowspan > 1) {
      endcell =
        sheetobj.cells[
          SocialCalc.crToCoord(colnum + (cell.colspan || 1) - 1, rownum + (cell.rowspan || 1) - 1)
        ];
    }
    num = cell.bt;
    if (num && typeof sheetobj.borderstyles[num] !== 'undefined')
      stylestr += 'border-top:' + sheetobj.borderstyles[num] + ';';
    num = typeof endcell != 'undefined' ? endcell.br : cell.br;
    if (num && typeof sheetobj.borderstyles[num] !== 'undefined')
      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 = typeof endcell != 'undefined' ? endcell.bb : cell.bb;
    if (num && typeof sheetobj.borderstyles[num] !== 'undefined')
      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 && typeof sheetobj.borderstyles[num] !== 'undefined')
      stylestr += 'border-left:' + sheetobj.borderstyles[num] + ';';
    if (cell.comment) {
      result.title = cell.comment;
      if (context.showGrid) {
        if (context.commentClassName) {
          result.className = context.commentClassName;
        }
        stylestr += context.commentCSS;
      } else {
        if (context.commentNoGridClassName) {
          result.className = context.commentNoGridClassName;
        }
        stylestr += context.commentNoGridCSS;
      }
    }
    if (cell.readonly) {
      if (!cell.comment) {
        result.title = context.readonlyComment;
      }
      if (context.showGrid) {
        if (context.readonlyClassName) {
          result.className =
            (result.className ? result.className + ' ' : '') + context.readonlyClassName;
        }
        stylestr += context.readonlyCSS;
      } else {
        if (context.readonlyNoGridClassName) {
          result.className =
            (result.className ? result.className + ' ' : '') + context.readonlyNoGridClassName;
        }
        stylestr += context.readonlyNoGridCSS;
      }
    }
    result.style.cssText = stylestr;
    if (cell.cssc !== undefined) {
      if (noElement) {
        result.className = (result.className ? result.className + ' ' : '') + cell.cssc;
      } else {
        result.classList.add(cell.cssc);
      }
    }
    t = context.highlights[coord];
    if (t) {
      if (t == 'cursor') t += context.cursorsuffix;
      if (context.highlightTypes[t].className) {
        result.className =
          (result.className ? result.className + ' ' : '') + context.highlightTypes[t].className;
      }
      SocialCalc.setStyles(result, context.highlightTypes[t].style);
    }
    if (sheetobj.colattribs.hide[SocialCalc.rcColname(colnum)] == 'yes') {
      result.style.cssText += ';display:none';
    }
    if (sheetobj.rowattribs.hide[rownum] == 'yes') {
      result.style.cssText += ';display:none';
    }
    return result;
  };
  SC.CoordInPane = function (context, coord, rowpane, colpane) {
    var coordToCR = context.coordToCR[coord];
    if (!coordToCR || !coordToCR.row || !coordToCR.col) throw 'Bad coordToCR for ' + coord;
    return context.CellInPane(coordToCR.row, coordToCR.col, rowpane, colpane);
  };
  SC.CellInPane = function (context, row, col, rowpane, colpane) {
    var panerowlimits = context.rowpanes[rowpane];
    var panecollimits = context.colpanes[colpane];
    if (!panerowlimits || !panecollimits)
      throw 'CellInPane called with unknown panes ' + rowpane + '/' + colpane;
    if (row < panerowlimits.first || row > panerowlimits.last) return false;
    if (col < panecollimits.first || col > panecollimits.last) return false;
    return true;
  };
  SC.CreatePseudoElement = function () {
    return {
      style: { cssText: '' },
      innerHTML: '',
      className: '',
    };
  };
  SC.decodeFromSave = function (s) {
    if (typeof s != 'string') return s;
    if (s.indexOf('\\') == -1) return s;
    var r = s.replace(/\\c/g, ':');
    r = r.replace(/\\n/g, '\n');
    return r.replace(/\\b/g, '\\');
  };
  SC.decodeFromAjax = function (s) {
    if (typeof s != 'string') return s;
    if (s.indexOf('\\') == -1) return s;
    var r = s.replace(/\\c/g, ':');
    r = r.replace(/\\n/g, '\n');
    r = r.replace(/\\e/g, ']]');
    return r.replace(/\\b/g, '\\');
  };
  SC.encodeForSave = function (s) {
    if (typeof s != 'string') return s;
    if (s.indexOf('\\') != -1) s = s.replace(/\\/g, '\\b');
    if (s.indexOf(':') != -1) s = s.replace(/:/g, '\\c');
    if (s.indexOf('\n') != -1) s = s.replace(/\n/g, '\\n');
    return s;
  };
  SC.SafeUrlForRender = function (rawurl, policy = SocialCalc.Callbacks.securityPolicy) {
    const noTabsOrNewlines = rawurl.replace(/[\t\n\r]/g, '');
    let start = 0;
    let end = noTabsOrNewlines.length;
    while (start < end && noTabsOrNewlines.charCodeAt(start) <= 32) start++;
    while (end > start && noTabsOrNewlines.charCodeAt(end - 1) <= 32) end--;
    const stripped = noTabsOrNewlines.slice(start, end);
    let encoded;
    try {
      encoded = encodeURI(stripped);
    } catch {
      return null;
    }
    const schemeMatch = stripped.match(/^([a-zA-Z][a-zA-Z0-9+.-]*):/);
    if (!schemeMatch) {
      return SocialCalc.special_chars(encoded);
    }
    const scheme = schemeMatch[1].toLowerCase() + ':';
    if (scheme === 'data:') {
      const afterScheme = stripped.slice(schemeMatch[0].length);
      const mime = afterScheme.match(/^([^,;]*)/)[1].toLowerCase();
      if (!mime) return null;
      const allowedDataMimeTypes = policy.allowedDataMimeTypes;
      for (let i = 0; i < allowedDataMimeTypes.length; i++) {
        if (allowedDataMimeTypes[i].toLowerCase() === mime)
          return SocialCalc.special_chars(encoded);
      }
      return null;
    }
    const allowedUrlSchemes = policy.allowedUrlSchemes;
    for (let i = 0; i < allowedUrlSchemes.length; i++) {
      if (allowedUrlSchemes[i].toLowerCase() === scheme) return SocialCalc.special_chars(encoded);
    }
    return null;
  };
  SC.EscapeUntrustedHtml = function (html, policy = SocialCalc.Callbacks.securityPolicy) {
    if (typeof policy.sanitizeHtml === 'function') {
      return policy.sanitizeHtml(html);
    }
    return SocialCalc.special_chars(html);
  };
  SC.special_chars = function (string) {
    if (/[&<>"]/.test(string)) {
      string = string.replace(/&/g, '&amp;');
      string = string.replace(/</g, '&lt;');
      string = string.replace(/>/g, '&gt;');
      string = string.replace(/"/g, '&quot;');
    }
    return string;
  };
  SC.Lookup = function (value, list) {
    for (var i = 0; i < list.length; i++) {
      if (list[i] > value) {
        if (i > 0) return i - 1;
        else return null;
      }
    }
    return list.length - 1;
  };
  SC.setStyles = function (element, cssText) {
    var parts, part, pos, name, value;
    if (!cssText) return;
    parts = cssText.split(';');
    for (part = 0; part < parts.length; part++) {
      pos = parts[part].indexOf(':');
      if (pos != -1) {
        name = parts[part].substring(0, pos);
        value = parts[part].substring(pos + 1);
        if (name && value) {
          element.style[name] = value;
        }
      }
    }
  };
  SC.GetViewportInfo = function () {
    var result = {};
    if (window.innerWidth) {
      result.width = window.innerWidth;
      result.height = window.innerHeight;
      result.horizontalScroll = window.pageXOffset;
      result.verticalScroll = window.pageYOffset;
    } else {
      if (document.documentElement && document.documentElement.clientWidth) {
        result.width = document.documentElement.clientWidth;
        result.height = document.documentElement.clientHeight;
        result.horizontalScroll = document.documentElement.scrollLeft;
        result.verticalScroll = document.documentElement.scrollTop;
      } else if (document.body.clientWidth) {
        result.width = document.body.clientWidth;
        result.height = document.body.clientHeight;
        result.horizontalScroll = document.body.scrollLeft;
        result.verticalScroll = document.body.scrollTop;
      }
    }
    return result;
  };
  SC.GetElementPosition = function (element) {
    var offsetLeft = 0;
    var offsetTop = 0;
    while (element) {
      if (SocialCalc.GetComputedStyle(element, 'position') == 'relative') break;
      offsetLeft += element.offsetLeft;
      offsetTop += element.offsetTop;
      element = element.offsetParent;
    }
    return {
      left: offsetLeft,
      top: offsetTop,
    };
  };
  SC.GetElementPositionWithScroll = function (element) {
    var rect = element.getBoundingClientRect();
    return {
      left: rect.left,
      right: rect.right,
      top: rect.top,
      bottom: rect.bottom,
      width: rect.width ? rect.width : rect.right - rect.left,
      height: rect.height ? rect.height : rect.bottom - rect.top,
    };
  };
  SC.GetElementFixedParent = function (element) {
    while (element) {
      if (element.tagName == 'HTML') break;
      if (SocialCalc.GetComputedStyle(element, 'position') == 'fixed') return element;
      element = element.parentNode;
    }
    return false;
  };
  SC.GetComputedStyle = function (element, style) {
    var computedStyle;
    if (document.defaultView) {
      computedStyle = document.defaultView.getComputedStyle(element, null);
    } else {
      return '';
    }
    return computedStyle[style];
  };
  SC.LookupElement = function (element, array) {
    var i;
    for (i = 0; i < array.length; i++) {
      if (array[i].element == element) return array[i];
    }
    return null;
  };
  SC.AssignID = function (obj, element, id) {
    if (obj.idPrefix) {
      element.id = obj.idPrefix + id;
    }
  };
  SC.GetCellContents = function (sheetobj, coord) {
    var result = '';
    var cellobj = sheetobj.cells[coord];
    if (cellobj) {
      switch (cellobj.datatype) {
        case 'v':
          result = cellobj.datavalue + '';
          break;
        case 't':
          result = "'" + cellobj.datavalue;
          break;
        case 'f':
          result = '=' + cellobj.formula;
          break;
        case 'c':
          result = cellobj.formula;
          break;
        default:
          break;
      }
    }
    return result;
  };
  SC.FormatCellForExport = function (sheet, cell, _cr) {
    var valuetype, valueformat, sheetattribs;
    if (!cell) {
      return '';
    }
    sheetattribs = sheet.attribs;
    valuetype = cell.valuetype || 'n';
    if (valuetype.charAt(0) == 't') {
      return cell.datavalue + '';
    }
    valueformat = cell.nontextvalueformat;
    if (valueformat == null || valueformat == '') {
      valueformat = sheetattribs.defaultnontextvalueformat;
    }
    valueformat = sheet.valueformats[valueformat - 0];
    if (valueformat == null || valueformat == 'none') {
      valueformat = '';
    }
    return SocialCalc.format_number_for_display(cell.datavalue, valuetype, valueformat);
  };
  SC.FormatValueForDisplay = function (sheetobj, value, cr, linkstyle) {
    var valueformat, valuetype, valuesubtype;
    var displayvalue, valueinputwidget;
    var untrusted = Boolean(SocialCalc.Callbacks.untrustedContent);
    var sheetattribs = sheetobj.attribs;
    var cell = sheetobj.cells[cr];
    if (!cell) {
      cell = new SocialCalc.Cell(cr);
    }
    displayvalue = value;
    valuetype = cell.valuetype || '';
    valuesubtype = valuetype.substring(1);
    valueinputwidget = valuetype.charAt(1);
    var formula_name = valuetype.substring(2);
    var html_display_value = null;
    var html_formated_value = null;
    valuetype = valuetype.charAt(0);
    if (cell.errors || valuetype == 'e') {
      displayvalue = cell.errors || valuesubtype || 'Error in cell';
      return displayvalue;
    }
    if (valuetype == 't') {
      valueformat =
        sheetobj.valueformats[cell.textvalueformat - 0] ||
        sheetobj.valueformats[sheetattribs.defaulttextvalueformat - 0] ||
        '';
      if (valueformat == 'formula') {
        if (cell.datatype == 'f') {
          displayvalue = SocialCalc.special_chars('=' + cell.formula) || '&nbsp;';
        } else if (cell.datatype == 'c') {
          displayvalue = SocialCalc.special_chars("'" + cell.formula) || '&nbsp;';
        } else {
          displayvalue = SocialCalc.special_chars("'" + displayvalue) || '&nbsp;';
        }
        return displayvalue;
      }
      html_display_value = displayvalue;
      displayvalue = SocialCalc.format_text_for_display(
        displayvalue,
        cell.valuetype,
        valueformat,
        sheetobj,
        linkstyle,
        cell.nontextvalueformat,
      );
      html_formated_value = displayvalue;
    } else if (valuetype == 'n') {
      valueformat = cell.nontextvalueformat;
      if (valueformat == null || valueformat == '') {
        valueformat = sheetattribs.defaultnontextvalueformat;
      }
      valueformat = sheetobj.valueformats[valueformat - 0];
      if (valueformat == null || valueformat == 'none') {
        valueformat = '';
      }
      if (valueformat == 'formula') {
        if (cell.datatype == 'f') {
          displayvalue = SocialCalc.special_chars('=' + cell.formula) || '&nbsp;';
        } else if (cell.datatype == 'c') {
          displayvalue = SocialCalc.special_chars("'" + cell.formula) || '&nbsp;';
        } else {
          displayvalue = SocialCalc.special_chars("'" + displayvalue) || '&nbsp;';
        }
        return displayvalue;
      } else if (valueformat == 'forcetext') {
        if (cell.datatype == 'f') {
          displayvalue = SocialCalc.special_chars('=' + cell.formula) || '&nbsp;';
        } else if (cell.datatype == 'c') {
          displayvalue = SocialCalc.special_chars(cell.formula) || '&nbsp;';
        } else {
          displayvalue = SocialCalc.special_chars(displayvalue) || '&nbsp;';
        }
        return displayvalue;
      }
      html_display_value = displayvalue;
      displayvalue = SocialCalc.format_number_for_display(
        displayvalue,
        cell.valuetype,
        valueformat,
      );
      html_formated_value = displayvalue;
    } else {
      displayvalue = '&nbsp;';
    }
    if (
      !untrusted &&
      valueinputwidget == 'i' &&
      html_display_value != null &&
      html_formated_value != null
    ) {
      var parameters = sheetobj.ioParameterList[cr];
      var formula_details = SocialCalc.Formula.FunctionList[formula_name];
      if (formula_details) {
        var cell_html = formula_details[5];
        var checkedValue = html_display_value == 0 ? '' : 'checked';
        cell_html = cell_html.replace(/<%=checked%>/g, checkedValue);
        cell_html = cell_html.replace(/<%=formated_value%>/g, html_formated_value);
        cell_html = cell_html.replace(/<%=display_value%>/g, html_display_value);
        var parameterValue;
        if (parameters) {
          for (var index = 0; index < parameters.length; index++) {
            if (parameters[index].type == 'coord') {
              parameterValue = sheetobj.GetAssuredCell(parameters[index].value).datavalue;
            } else {
              parameterValue = parameters[index].value;
            }
            var paramRegExp = new RegExp('<%=parameter' + index + '_value%>', 'g');
            cell_html = cell_html.replace(paramRegExp, parameterValue);
          }
          if (parameters.html) {
            for (var htmlIndex = 0; htmlIndex < parameters.html.length; htmlIndex++) {
              var paramRegExp = new RegExp('<%=html' + htmlIndex + '_value%>', 'g');
              cell_html = cell_html.replace(paramRegExp, parameters.html[htmlIndex]);
            }
          }
          if (parameters.css) {
            cell_html = cell_html.replace(/^(<\w+)(\W)/, "$1 style='" + parameters.css + "'$2");
          }
        }
        return cell_html.replace(/<%=cell_reference%>/g, cr);
      }
      return 'error:Widget HTML missing';
    }
    return displayvalue;
  };
  SC.format_text_for_display = function (
    rawvalue,
    valuetype,
    valueformat,
    sheetobj,
    linkstyle,
    nontextvalueformat,
  ) {
    var valuesubtype, dvsc, dvue;
    var textval;
    var displayvalue;
    var untrusted = Boolean(SocialCalc.Callbacks.untrustedContent);
    valuesubtype = valuetype.substring(1);
    displayvalue = rawvalue;
    if (valueformat == 'none' || valueformat == null) valueformat = '';
    if (!/^(text-|custom|hidden)/.test(valueformat)) valueformat = '';
    if (valueformat == '' || valueformat == 'General') {
      if (valuesubtype == 'h') valueformat = 'text-html';
      if (valuesubtype == 'w' || valuesubtype == 'r') valueformat = 'text-wiki';
      if (valuesubtype == 'l') valueformat = 'text-link';
      if (!valuesubtype) valueformat = 'text-plain';
    }
    if (valueformat == 'text-html') {
      if (untrusted) {
        displayvalue = SocialCalc.EscapeUntrustedHtml(displayvalue);
      }
    } else if (SocialCalc.Callbacks.expand_wiki && valueformat.startsWith('text-wiki')) {
      displayvalue = SocialCalc.Callbacks.expand_wiki(
        displayvalue,
        sheetobj,
        linkstyle,
        valueformat,
      );
      if (untrusted) {
        displayvalue = SocialCalc.EscapeUntrustedHtml(displayvalue);
      }
    } else if (valueformat == 'text-wiki') {
      displayvalue =
        (SocialCalc.Callbacks.expand_markup &&
          SocialCalc.Callbacks.expand_markup(displayvalue, sheetobj, linkstyle)) ||
        SocialCalc.special_chars('wiki-text:' + displayvalue);
      if (untrusted) {
        displayvalue = SocialCalc.EscapeUntrustedHtml(displayvalue);
      }
    } else if (valueformat == 'text-url') {
      dvsc = SocialCalc.special_chars(displayvalue);
      if (untrusted) {
        dvue = SocialCalc.SafeUrlForRender(displayvalue);
        displayvalue = dvue == null ? dvsc : '<a href="' + dvue + '">' + dvsc + '</a>';
      } else {
        dvue = encodeURI(displayvalue);
        displayvalue = '<a href="' + dvue + '">' + dvsc + '</a>';
      }
    } else if (valueformat == 'text-link') {
      displayvalue = SocialCalc.expand_text_link(displayvalue, sheetobj, linkstyle, valueformat);
    } else if (valueformat == 'text-image') {
      if (untrusted) {
        dvue = SocialCalc.SafeUrlForRender(displayvalue);
        displayvalue =
          dvue == null ? SocialCalc.special_chars(displayvalue) : '<img src="' + dvue + '">';
      } else {
        dvue = encodeURI(displayvalue);
        displayvalue = '<img src="' + dvue + '">';
      }
    } else if (valueformat.substring(0, 12) == 'text-custom:') {
      dvsc = SocialCalc.special_chars(displayvalue);
      dvsc = dvsc.replace(/  /g, '&nbsp; ');
      dvsc = dvsc.replace(/\n/g, '<br>');
      var customTemplate = valueformat.substring(12);
      textval = {};
      if (untrusted && typeof SocialCalc.Callbacks.securityPolicy.sanitizeHtml === 'function') {
        textval.r = displayvalue;
        textval.u = encodeURI(displayvalue);
        textval.s = dvsc;
        displayvalue = SocialCalc.EscapeUntrustedHtml(
          customTemplate.replace(/@(r|s|u)/g, function (a, c) {
            return textval[c];
          }),
        );
      } else if (untrusted) {
        textval.r = SocialCalc.EscapeUntrustedHtml(displayvalue);
        textval.u = SocialCalc.SafeUrlForRender(displayvalue) || '';
        textval.s = dvsc;
        displayvalue = customTemplate
          .split(/(@[rsu])/g)
          .map(function (part) {
            return part === '@r' || part === '@s' || part === '@u'
              ? textval[part.charAt(1)]
              : SocialCalc.special_chars(part);
          })
          .join('');
      } else {
        textval.r = displayvalue;
        textval.u = encodeURI(displayvalue);
        textval.s = dvsc;
        displayvalue = customTemplate.replace(/@(r|s|u)/g, function (a, c) {
          return textval[c];
        });
      }
    } else if (valueformat.substring(0, 6) == 'custom') {
      displayvalue = SocialCalc.special_chars(displayvalue);
      displayvalue = displayvalue.replace(/  /g, '&nbsp; ');
      displayvalue = displayvalue.replace(/\n/g, '<br>');
      displayvalue += ' (custom format)';
    } else if (valueformat == 'hidden') {
      displayvalue = '&nbsp;';
    } else if (
      nontextvalueformat != null &&
      nontextvalueformat != '' &&
      sheetobj.valueformats[nontextvalueformat - 0] != 'none' &&
      sheetobj.valueformats[nontextvalueformat - 0] != ''
    ) {
      valueformat = sheetobj.valueformats[nontextvalueformat];
      displayvalue = SocialCalc.format_number_for_display(rawvalue, valuetype, valueformat);
    } else {
      displayvalue = SocialCalc.special_chars(displayvalue);
      displayvalue = displayvalue.replace(/  /g, '&nbsp; ');
      displayvalue = displayvalue.replace(/\n/g, '<br>');
    }
    return displayvalue;
  };
  SC.format_number_for_display = function (rawvalue, valuetype, valueformat) {
    var value, valuesubtype;
    var scc = SocialCalc.Constants;
    value = rawvalue - 0;
    valuesubtype = valuetype.substring(1);
    if (valueformat == 'Auto' || valueformat == '') {
      if (valuesubtype == '%') {
        valueformat = scc.defaultFormatp;
      } else if (valuesubtype == '$') {
        valueformat = scc.defaultFormatc;
      } else if (valuesubtype == 'dt') {
        valueformat = scc.defaultFormatdt;
      } else if (valuesubtype == 'd') {
        valueformat = scc.defaultFormatd;
      } else if (valuesubtype == 't') {
        valueformat = scc.defaultFormatt;
      } else if (valuesubtype == 'l') {
        valueformat = 'logical';
      } else {
        valueformat = 'General';
      }
    }
    if (valueformat == 'logical') {
      return value ? scc.defaultDisplayTRUE : scc.defaultDisplayFALSE;
    }
    if (valueformat == 'hidden') {
      return '&nbsp;';
    }
    return SocialCalc.FormatNumber.formatNumberWithFormat(rawvalue, valueformat, '');
  };
  SC.DetermineValueType = function (rawvalue) {
    var value = rawvalue + '';
    var type = 't';
    var tvalue;
    var matches;
    var year;
    var hour;
    var minute;
    var second;
    var denom;
    var num;
    var intgr;
    var constr;
    tvalue = value.replace(/^\s+/, '');
    tvalue = tvalue.replace(/\s+$/, '');
    if (value.length == 0) {
      type = '';
    } else if (value.match(/^\s+$/)) {
    } else if (tvalue.match(/^[-+]?\d*(?:\.)?\d*(?:[eE][-+]?\d+)?$/)) {
      value = tvalue - 0;
      if (isNaN(value)) {
        value = rawvalue + '';
      } else {
        type = 'n';
      }
    } else if (tvalue.match(/^[-+]?\d*(?:\.)?\d*\s*%$/)) {
      value = (tvalue.slice(0, -1) - 0) / 100;
      type = 'n%';
    } else if (tvalue.match(/^[-+]?\$\s*\d*(?:\.)?\d*\s*$/) && tvalue.match(/\d/)) {
      value = tvalue.replace(/\$/, '') - 0;
      type = 'n$';
    } else if (tvalue.match(/^[-+]?(\d*,\d*)+(?:\.)?\d*$/)) {
      value = tvalue.replace(/,/g, '') - 0;
      type = 'n';
    } else if (tvalue.match(/^[-+]?(\d*,\d*)+(?:\.)?\d*\s*%$/)) {
      value = (tvalue.replace(/[%,]/g, '') - 0) / 100;
      type = 'n%';
    } else if (tvalue.match(/^[-+]?\$\s*(\d*,\d*)+(?:\.)?\d*$/) && tvalue.match(/\d/)) {
      value = tvalue.replace(/[$,]/g, '') - 0;
      type = 'n$';
    } else if ((matches = value.match(/^(\d{1,2})[/-](\d{1,2})[/-](\d{1,4})\s*$/))) {
      year = matches[3] - 0;
      year = year < 1e3 ? year + 2e3 : year;
      value =
        navigator.language.indexOf('fr') === 0
          ? SocialCalc.FormatNumber.convert_date_gregorian_to_julian(
              year,
              matches[2] - 0,
              matches[1] - 0,
            ) - 2415019
          : SocialCalc.FormatNumber.convert_date_gregorian_to_julian(
              year,
              matches[1] - 0,
              matches[2] - 0,
            ) - 2415019;
      type = 'nd';
    } else if ((matches = value.match(/^(\d{4})[/-](\d{1,2})[/-](\d{1,2})\s*$/))) {
      year = matches[1] - 0;
      year = year < 1e3 ? year + 2e3 : year;
      value =
        SocialCalc.FormatNumber.convert_date_gregorian_to_julian(
          year,
          matches[2] - 0,
          matches[3] - 0,
        ) - 2415019;
      type = 'nd';
    } else if (
      (matches = value.match(/^(\d{4})[/-](\d{1,2})[/-](\d{1,2}) (\d{1,2}):(\d{1,2})\s*$/))
    ) {
      year = matches[1] - 0;
      year = year < 1e3 ? year + 2e3 : year;
      hour = matches[4] - 0;
      minute = matches[5] - 0;
      value =
        SocialCalc.FormatNumber.convert_date_gregorian_to_julian(
          year,
          matches[2] - 0,
          matches[3] - 0,
        ) - 2415019;
      type = 'nd';
      if (hour < 24 && minute < 60) {
        value += hour / 24 + minute / (24 * 60);
        type = 'ndt';
      }
    } else if (
      (matches = value.match(
        /^(\d{4})[/-](\d{1,2})[/-](\d{1,2}) (\d{1,2}):(\d{1,2}):(\d{1,2})\s*$/,
      ))
    ) {
      year = matches[1] - 0;
      year = year < 1e3 ? year + 2e3 : year;
      hour = matches[4] - 0;
      minute = matches[5] - 0;
      second = matches[6] - 0;
      value =
        SocialCalc.FormatNumber.convert_date_gregorian_to_julian(
          year,
          matches[2] - 0,
          matches[3] - 0,
        ) - 2415019;
      type = 'nd';
      if (hour < 24 && minute < 60 && second < 60) {
        value += hour / 24 + minute / (24 * 60) + second / (24 * 60 * 60);
        type = 'ndt';
      }
    } else if ((matches = value.match(/^(\d{1,2}):(\d{1,2})\s*$/))) {
      hour = matches[1] - 0;
      minute = matches[2] - 0;
      if (hour < 24 && minute < 60) {
        value = hour / 24 + minute / (24 * 60);
        type = 'nt';
      }
    } else if ((matches = value.match(/^(\d{1,2}):(\d{1,2}):(\d{1,2})\s*$/))) {
      hour = matches[1] - 0;
      minute = matches[2] - 0;
      second = matches[3] - 0;
      if (hour < 24 && minute < 60 && second < 60) {
        value = hour / 24 + minute / (24 * 60) + second / (24 * 60 * 60);
        type = 'nt';
      }
    } else if ((matches = value.match(/^\s*([-+]?\d+) (\d+)\/(\d+)\s*$/))) {
      intgr = matches[1] - 0;
      num = matches[2] - 0;
      denom = matches[3] - 0;
      if (denom && denom > 0) {
        value = intgr + (intgr < 0 ? -num / denom : num / denom);
        type = 'n';
      }
    } else if ((constr = SocialCalc.InputConstants[value.toUpperCase()])) {
      num = constr.indexOf(',');
      value = constr.substring(0, num) - 0;
      type = constr.substring(num + 1);
    } else if (tvalue.length > 8 && tvalue.substring(0, 8).toLowerCase() == 'https://') {
      value = tvalue;
      type = 'tl';
    } else if (tvalue.length > 7 && tvalue.substring(0, 7).toLowerCase() == 'http://') {
      value = tvalue;
      type = 'tl';
    } else if (tvalue.match(/<([A-Z][A-Z0-9]*)\b[^>]*>[\s\S]*?<\/\1>/i)) {
      value = tvalue;
      type = 'th';
    }
    return {
      value,
      type,
    };
  };
  SC.InputConstants = {
    TRUE: '1,nl',
    FALSE: '0,nl',
    '#N/A': '0,e#N/A',
    '#NULL!': '0,e#NULL!',
    '#NUM!': '0,e#NUM!',
    '#DIV/0!': '0,e#DIV/0!',
    '#VALUE!': '0,e#VALUE!',
    '#REF!': '0,e#REF!',
    '#NAME?': '0,e#NAME?',
  };
  SC.default_expand_markup = function (displayvalue, _sheetobj, _linkstyle) {
    var result = displayvalue;
    result = SocialCalc.special_chars(result);
    result = result.replace(/  /g, '&nbsp; ');
    result = result.replace(/\n/g, '<br>');
    return result;
  };
  SC.expand_text_link = function (displayvalue, sheetobj, linkstyle, valueformat) {
    var desc, tb, str;
    var scc = SocialCalc.Constants;
    var url = '';
    var parts = SocialCalc.ParseCellLinkText(displayvalue + '');
    if (parts.desc) {
      desc = SocialCalc.special_chars(parts.desc);
    } else {
      desc = parts.pagename ? scc.defaultPageLinkFormatString : scc.defaultLinkFormatString;
    }
    if (
      displayvalue.length > 7 &&
      displayvalue.substring(0, 7).toLowerCase() == 'http://' &&
      displayvalue.charAt(displayvalue.length - 1) != '>'
    ) {
      desc = desc.substring(7);
    }
    tb = parts.newwin || !linkstyle ? ' target="_blank"' : '';
    if (parts.pagename) {
      if (SocialCalc.Callbacks.MakePageLink) {
        url = SocialCalc.Callbacks.MakePageLink(
          parts.pagename,
          parts.workspacename,
          linkstyle,
          valueformat,
        );
        if (SocialCalc.Callbacks.untrustedContent) {
          var safePageUrl = SocialCalc.SafeUrlForRender(url);
          if (safePageUrl == null) {
            return desc;
          }
          url = safePageUrl;
        }
      }
    } else if (SocialCalc.Callbacks.untrustedContent) {
      var safeUrl = SocialCalc.SafeUrlForRender(parts.url);
      if (safeUrl == null) {
        return desc;
      }
      url = safeUrl;
    } else {
      url = encodeURI(parts.url);
    }
    str = '<a href="' + url + '"' + tb + '>' + desc + '</a>';
    return str;
  };
  SC.ParseCellLinkText = function (str) {
    var result = {
      url: '',
      desc: '',
      newwin: false,
      pagename: '',
      workspace: '',
    };
    var pageform = false;
    var urlend = str.length - 1;
    var descstart = 0;
    var lastlt = str.lastIndexOf('<');
    var lastbrkt = str.lastIndexOf('[');
    var lastbrace = str.lastIndexOf('{');
    var descend = -1;
    var wsend = 0;
    if (
      (str.charAt(urlend) != '>' || lastlt == -1) &&
      (str.charAt(urlend) != ']' || lastbrkt == -1) &&
      (str.charAt(urlend) != '}' ||
        str.charAt(urlend - 1) != ']' ||
        lastbrace == -1 ||
        lastbrkt == -1 ||
        lastbrkt < lastbrace)
    ) {
      urlend++;
      descend = urlend;
    } else {
      if (str.charAt(urlend) == '>') {
        descend = lastlt - 1;
        if (lastlt > 0 && str.charAt(descend) == '<' && str.charAt(urlend - 1) == '>') {
          descend--;
          urlend--;
          result.newwin = true;
        }
      } else if (str.charAt(urlend) == ']') {
        descend = lastbrkt - 1;
        pageform = true;
        if (lastbrkt > 0 && str.charAt(descend) == '[' && str.charAt(urlend - 1) == ']') {
          descend--;
          urlend--;
          result.newwin = true;
        }
      } else {
        descend = lastbrace - 1;
        pageform = true;
        wsend = lastbrkt;
        urlend--;
        if (lastbrkt > 0 && str.charAt(lastbrkt - 1) == '[' && str.charAt(urlend - 1) == ']') {
          wsend = lastbrkt - 1;
          urlend--;
          result.newwin = true;
        }
        if (str.charAt(wsend - 1) == ' ') {
          wsend--;
        }
        result.workspace = str.substring(lastbrace + 1, wsend) || '';
      }
      if (str.charAt(descend) == ' ') {
        descend--;
      }
      if (str.charAt(descstart) == '"' && str.charAt(descend) == '"') {
        descstart++;
        descend--;
      }
    }
    if (pageform) {
      result.pagename = str.substring(lastbrkt + 1, urlend) || '';
    } else {
      result.url = str.substring(lastlt + 1, urlend) || '';
    }
    if (descend >= descstart) {
      result.desc = str.substring(descstart, descend + 1);
    }
    return result;
  };
  SC.ConvertSaveToOtherFormat = function (savestr, outputformat, dorecalc) {
    var sheet, context, clipextents, div, ele, row, col, cr, cell, str;
    var result = '';
    if (outputformat == 'scsave') {
      return savestr;
    }
    if (savestr == '') {
      return '';
    }
    sheet = new SocialCalc.Sheet();
    sheet.ParseSheetSave(savestr);
    if (dorecalc) {
      throw 'SocialCalc.ConvertSaveToOtherFormat: Not doing recalc.';
    }
    if (sheet.copiedfrom) {
      clipextents = SocialCalc.ParseRange(sheet.copiedfrom);
    } else {
      clipextents = {
        cr1: {
          row: 1,
          col: 1,
        },
        cr2: {
          row: sheet.attribs.lastrow,
          col: sheet.attribs.lastcol,
        },
      };
    }
    if (outputformat == 'html') {
      context = new SocialCalc.RenderContext(sheet);
      if (sheet.copiedfrom) {
        context.rowpanes[0] = {
          first: clipextents.cr1.row,
          last: clipextents.cr2.row,
        };
        context.colpanes[0] = {
          first: clipextents.cr1.col,
          last: clipextents.cr2.col,
        };
      }
      div = SocialCalc.document.createElement('div');
      ele = context.RenderSheet(null, context.defaultHTMLlinkstyle);
      div.appendChild(ele);
      result = div.innerHTML;
      return result;
    }
    for (row = clipextents.cr1.row; row <= clipextents.cr2.row; row++) {
      for (col = clipextents.cr1.col; col <= clipextents.cr2.col; col++) {
        cr = SocialCalc.crToCoord(col, row);
        cell = sheet.GetAssuredCell(cr);
        if (cell.errors) {
          str = cell.errors;
        } else {
          str = SocialCalc.FormatCellForExport(sheet, cell, cr);
        }
        if (outputformat == 'csv') {
          if (str.indexOf('"') != -1) {
            str = str.replace(/"/g, '""');
          }
          if (/[, \n"]/.test(str)) {
            str = '"' + str + '"';
          }
          if (col > clipextents.cr1.col) {
            str = ',' + str;
          }
        } else if (outputformat == 'tab') {
          if (str.indexOf('\n') != -1) {
            if (str.indexOf('"') != -1) {
              str = str.replace(/"/g, '""');
            }
            str = '"' + str + '"';
          }
          if (col > clipextents.cr1.col) {
            str = '	' + str;
          }
        }
        result += str;
      }
      result += '\n';
    }
    return result;
  };
  SC.ConvertOtherFormatToSave = function (inputstr, inputformat) {
    var sheet;
    var lines;
    var i;
    var line;
    var value;
    var inquote;
    var j;
    var ch;
    var row;
    var col;
    var cr;
    var maxc;
    var result = '';
    var AddCell = function () {
      col++;
      if (col > maxc) maxc = col;
      cr = SocialCalc.crToCoord(col, row);
      SocialCalc.SetConvertedCell(sheet, cr, value);
      value = '';
    };
    if (inputformat == 'scsave') {
      return inputstr;
    }
    sheet = new SocialCalc.Sheet();
    lines = inputstr.split(/\r\n|\n/);
    maxc = 0;
    if (inputformat == 'csv') {
      row = 0;
      inquote = false;
      for (i = 0; i < lines.length; i++) {
        if (i == lines.length - 1 && lines[i] == '') break;
        if (inquote) {
          value += '\n';
        } else {
          value = '';
          row++;
          col = 0;
        }
        line = lines[i];
        for (j = 0; j < line.length; j++) {
          ch = line.charAt(j);
          if (ch == '"') {
            if (inquote) {
              if (j < line.length - 1 && line.charAt(j + 1) == '"') {
                j++;
                value += '"';
              } else {
                inquote = false;
                if (j == line.length - 1) {
                  AddCell();
                }
              }
            } else {
              inquote = true;
            }
            continue;
          }
          if (ch == ',' && !inquote) {
            AddCell();
          } else {
            value += ch;
          }
          if (j == line.length - 1 && !inquote) {
            AddCell();
          }
        }
      }
      if (maxc > 0) {
        sheet.attribs.lastrow = row;
        sheet.attribs.lastcol = maxc;
        result = sheet.CreateSheetSave('A1:' + SocialCalc.crToCoord(maxc, row));
      }
    }
    if (inputformat == 'tab') {
      row = 0;
      inquote = false;
      for (i = 0; i < lines.length; i++) {
        if (i == lines.length - 1 && lines[i] == '') break;
        if (inquote) {
          value += '\n';
        } else {
          value = '';
          row++;
          col = 0;
        }
        line = lines[i];
        for (j = 0; j < line.length; j++) {
          ch = line.charAt(j);
          if (ch == '"') {
            if (inquote) {
              if (j < line.length - 1) {
                if (line.charAt(j + 1) == '"') {
                  j++;
                  value += '"';
                } else if (line.charAt(j + 1) == '	') {
                  j++;
                  inquote = false;
                  AddCell();
                }
              } else {
                inquote = false;
                AddCell();
              }
              continue;
            }
            if (value == '') {
              inquote = true;
              continue;
            }
          }
          if (ch == '	' && !inquote) {
            AddCell();
          } else {
            value += ch;
          }
          if (j == line.length - 1 && !inquote) {
            AddCell();
          }
        }
      }
      if (maxc > 0) {
        sheet.attribs.lastrow = row;
        sheet.attribs.lastcol = maxc;
        result = sheet.CreateSheetSave('A1:' + SocialCalc.crToCoord(maxc, row));
      }
    }
    return result;
  };
  SC.SetConvertedCell = function (sheet, cr, rawvalue) {
    var cell, value;
    cell = sheet.GetAssuredCell(cr);
    if (typeof rawvalue == 'string' && rawvalue.charAt(0) == '=') {
      cell.datatype = 'f';
      cell.formula = rawvalue.substring(1);
      cell.datavalue = 0;
      cell.valuetype = 'e#N/A';
      delete cell.errors;
      delete cell.displaystring;
      delete cell.parseinfo;
      return;
    }
    value = SocialCalc.DetermineValueType(rawvalue);
    if (value.type == 'n' && value.value == rawvalue) {
      cell.datatype = 'v';
      cell.valuetype = 'n';
      cell.datavalue = value.value;
    } else if (value.type.charAt(0) == 't') {
      cell.datatype = 't';
      cell.valuetype = value.type;
      cell.datavalue = value.value;
    } else {
      cell.datatype = 'c';
      cell.valuetype = value.type;
      cell.datavalue = value.value;
      cell.formula = rawvalue;
    }
  };

  // In-place TypeScript conversion of socialcalctableeditor.js (SocialCalc global script).
  // Ambient API types live in socialcalctableeditor.d.ts (referenced by dist/SocialCalc.d.ts).
  // Vite+ strips types with Oxc before UMD concat — no runtime tax.
  // Typechecked core after removing @ts-nocheck.

  // See js/socialcalctableeditor.d.ts for class / global shapes.

  const TableEditorSC = SocialCalc;
  TableEditorSC.TableEditor = function (context) {
    var scc = TableEditorSC.Constants;
    this.context = context;
    this.toplevel = null;
    this.fullgrid = null;
    this.noEdit = false;
    this.width = null;
    this.tablewidth = null;
    this.height = null;
    this.tableheight = null;
    this.inputBox = null;
    this.inputEcho = null;
    this.verticaltablecontrol = null;
    this.horizontaltablecontrol = null;
    this.logo = null;
    this.cellhandles = null;
    this.timeout = null;
    this.busy = false;
    this.ensureecell = false;
    this.deferredCommands = [];
    this.deferredEmailCommands = [];
    this.gridposition = null;
    this.headposition = null;
    this.firstscrollingrow = null;
    this.firstscrollingrowtop = null;
    this.lastnonscrollingrow = null;
    this.lastvisiblerow = null;
    this.firstscrollingcol = null;
    this.firstscrollingcolleft = null;
    this.lastnonscrollingcol = null;
    this.lastvisiblecol = null;
    this.rowpositions = [];
    this.colpositions = [];
    this.rowheight = [];
    this.colwidth = [];
    this.ecell = null;
    this.state = 'start';
    this.workingvalues = {};
    this.imageprefix = scc.defaultImagePrefix;
    this.idPrefix = scc.defaultTableEditorIDPrefix;
    this.pageUpDnAmount = scc.defaultPageUpDnAmount;
    this.recalcFunction = function (editor) {
      if (editor.context.sheetobj.RecalcSheet)
        editor.context.sheetobj.RecalcSheet(TableEditorSC.EditorSheetStatusCallback, editor);
      return null;
    };
    this.ctrlkeyFunction = function (editor, charname) {
      var ta, cell, position, cmd, sel, cliptext;
      switch (charname) {
        case '[ctrl-a]':
          editor.MoveECell('A1');
          editor.RangeAnchor('A1');
          editor.RangeExtend(
            TableEditorSC.crToCoord(
              editor.context.sheetobj.attribs.lastcol,
              editor.context.sheetobj.attribs.lastrow,
            ),
          );
          return false;
        case '[ctrl-c]':
        case '[ctrl-x]':
          ta = editor.pasteTextarea;
          ta.value = '';
          cell = TableEditorSC.GetEditorCellElement(editor, editor.ecell.row, editor.ecell.col);
          if (cell) {
            position = TableEditorSC.GetElementPosition(cell.element);
            ta.style.left = position.left - 1 + 'px';
            ta.style.top = position.top - 1 + 'px';
          }
          if (editor.range.hasrange) {
            sel =
              TableEditorSC.crToCoord(editor.range.left, editor.range.top) +
              ':' +
              TableEditorSC.crToCoord(editor.range.right, editor.range.bottom);
          } else {
            sel = editor.ecell.coord;
          }
          cliptext = TableEditorSC.ConvertSaveToOtherFormat(
            TableEditorSC.CreateSheetSave(editor.context.sheetobj, sel),
            'tab',
          );
          if (charname == '[ctrl-c]' || editor.noEdit || editor.ECellReadonly()) {
            cmd = 'copy ' + sel + ' formulas';
          } else {
            cmd = 'cut ' + sel + ' formulas';
          }
          editor.EditorScheduleSheetCommands(cmd, true, false);
          ta.style.display = 'block';
          ta.value = cliptext;
          ta.focus();
          ta.select();
          window.setTimeout(function () {
            var ta = editor.pasteTextarea;
            ta.blur();
            ta.style.display = 'none';
            TableEditorSC.KeyboardFocus();
          }, 200);
          return true;
        case '[ctrl-v]':
          if (editor.noEdit || editor.ECellReadonly()) return true;
          ta = editor.pasteTextarea;
          ta.value = '';
          cell = TableEditorSC.GetEditorCellElement(editor, editor.ecell.row, editor.ecell.col);
          if (cell) {
            position = TableEditorSC.GetElementPosition(cell.element);
            ta.style.left = position.left - 1 + 'px';
            ta.style.top = position.top - 1 + 'px';
          }
          ta.style.display = 'block';
          ta.value = '';
          ta.focus();
          window.setTimeout(function () {
            var ta = editor.pasteTextarea;
            var value = ta.value;
            ta.blur();
            ta.style.display = 'none';
            var cmd = '';
            if (editor.pastescclipboard) {
              editor.pastescclipboard = false;
            } else {
              var clipstr = TableEditorSC.ConvertSaveToOtherFormat(
                TableEditorSC.Clipboard.clipboard,
                'tab',
              );
              value = value.replace(/\r\n/g, '\n');
              if (
                value != clipstr &&
                (value.length - clipstr.length != 1 ||
                  value.substring(0, value.length - 1) != clipstr)
              ) {
                cmd =
                  'loadclipboard ' +
                  TableEditorSC.encodeForSave(
                    TableEditorSC.ConvertOtherFormatToSave(value, 'tab'),
                  ) +
                  '\n';
              }
            }
            var cr;
            if (editor.range.hasrange) {
              var clipsheet = new TableEditorSC.Sheet();
              clipsheet.ParseSheetSave(TableEditorSC.Clipboard.clipboard);
              var matches = clipsheet.copiedfrom.match(/(.+):(.+)/);
              if (matches !== null && matches[1] === matches[2]) {
                cr =
                  TableEditorSC.crToCoord(editor.range.left, editor.range.top) +
                  ':' +
                  TableEditorSC.crToCoord(editor.range.right, editor.range.bottom);
              } else {
                cr = TableEditorSC.crToCoord(editor.range.left, editor.range.top);
              }
            } else {
              cr = editor.ecell.coord;
            }
            cmd += 'paste ' + cr + ' formulas';
            editor.EditorScheduleSheetCommands(cmd, true, false);
            TableEditorSC.KeyboardFocus();
          }, 200);
          return true;
        case '[ctrl-z]':
          editor.EditorScheduleSheetCommands('undo', true, false);
          return false;
        case '[ctrl-s]':
          if (!TableEditorSC.Constants.AllowCtrlS) break;
          window.setTimeout(function () {
            var sheet = editor.context.sheetobj;
            var cell = sheet.GetAssuredCell(editor.ecell.coord);
            var ntvf = cell.nontextvalueformat
              ? sheet.valueformats[cell.nontextvalueformat - 0] || ''
              : '';
            var newntvf = window.prompt(
              'Advanced Feature:\n\nCustom Numeric Format or Command',
              ntvf,
            );
            if (newntvf != null) {
              if (newntvf.match(/^cmd:/)) {
                cmd = newntvf.substring(4);
              } else if (newntvf.match(/^edit:/)) {
                cmd = newntvf.substring(5);
                if (TableEditorSC.CtrlSEditor) {
                  TableEditorSC.CtrlSEditor(cmd);
                }
                return;
              } else {
                if (editor.range.hasrange) {
                  sel =
                    TableEditorSC.crToCoord(editor.range.left, editor.range.top) +
                    ':' +
                    TableEditorSC.crToCoord(editor.range.right, editor.range.bottom);
                } else {
                  sel = editor.ecell.coord;
                }
                cmd = 'set ' + sel + ' nontextvalueformat ' + newntvf;
              }
              editor.EditorScheduleSheetCommands(cmd, true, false);
            }
          }, 200);
          return false;
        default:
          break;
      }
      return true;
    };
    context.sheetobj.statuscallback = TableEditorSC.EditorSheetStatusCallback;
    context.sheetobj.statuscallbackparams = this;
    this.StatusCallback = {};
    this.MoveECellCallback = {};
    this.RangeChangeCallback = {};
    this.SettingsCallbacks = {};
    this.ecell = {
      coord: 'A1',
      row: 1,
      col: 1,
    };
    context.highlights[this.ecell.coord] = 'cursor';
    this.range = { hasrange: false };
    this.range2 = { hasrange: false };
  };
  TableEditorSC.TableEditor.prototype.CreateTableEditor = function (width, height) {
    return TableEditorSC.CreateTableEditor(this, width, height);
  };
  TableEditorSC.TableEditor.prototype.ResizeTableEditor = function (width, height) {
    return TableEditorSC.ResizeTableEditor(this, width, height);
  };
  TableEditorSC.TableEditor.prototype.SaveEditorSettings = function () {
    return TableEditorSC.SaveEditorSettings(this);
  };
  TableEditorSC.TableEditor.prototype.LoadEditorSettings = function (str, flags) {
    return TableEditorSC.LoadEditorSettings(this, str, flags);
  };
  TableEditorSC.TableEditor.prototype.EditorRenderSheet = function () {
    TableEditorSC.EditorRenderSheet(this);
  };
  TableEditorSC.TableEditor.prototype.EditorScheduleSheetCommands = function (
    cmdstr,
    saveundo,
    ignorebusy,
  ) {
    TableEditorSC.EditorScheduleSheetCommands(this, cmdstr, saveundo, ignorebusy);
  };
  TableEditorSC.TableEditor.prototype.ScheduleSheetCommands = function (cmdstr, saveundo) {
    this.context.sheetobj.ScheduleSheetCommands(cmdstr, saveundo);
  };
  TableEditorSC.TableEditor.prototype.SheetUndo = function () {
    this.context.sheetobj.SheetUndo();
  };
  TableEditorSC.TableEditor.prototype.SheetRedo = function () {
    this.context.sheetobj.SheetRedo();
  };
  TableEditorSC.TableEditor.prototype.EditorStepSet = function (status, arg) {
    TableEditorSC.EditorStepSet(this, status, arg);
  };
  TableEditorSC.TableEditor.prototype.GetStatuslineString = function (status, arg, params) {
    return TableEditorSC.EditorGetStatuslineString(this, status, arg, params);
  };
  TableEditorSC.TableEditor.prototype.EditorMouseRegister = function () {
    return TableEditorSC.EditorMouseRegister(this);
  };
  TableEditorSC.TableEditor.prototype.EditorMouseUnregister = function () {
    return TableEditorSC.EditorMouseUnregister(this);
  };
  TableEditorSC.TableEditor.prototype.EditorMouseRange = function (coord) {
    return TableEditorSC.EditorMouseRange(this, coord);
  };
  TableEditorSC.TableEditor.prototype.EditorProcessKey = function (ch, e) {
    return TableEditorSC.EditorProcessKey(this, ch, e);
  };
  TableEditorSC.TableEditor.prototype.EditorAddToInput = function (str, prefix) {
    return TableEditorSC.EditorAddToInput(this, str, prefix);
  };
  TableEditorSC.TableEditor.prototype.DisplayCellContents = function () {
    return TableEditorSC.EditorDisplayCellContents(this);
  };
  TableEditorSC.TableEditor.prototype.EditorSaveEdit = function (text) {
    return TableEditorSC.EditorSaveEdit(this, text);
  };
  TableEditorSC.TableEditor.prototype.EditorApplySetCommandsToRange = function (cmdline, type) {
    return TableEditorSC.EditorApplySetCommandsToRange(this, cmdline, type);
  };
  TableEditorSC.TableEditor.prototype.MoveECellWithKey = function (ch) {
    return TableEditorSC.MoveECellWithKey(this, ch);
  };
  TableEditorSC.TableEditor.prototype.MoveECell = function (newcell) {
    if (TableEditorSC._app) return 'A1';
    return TableEditorSC.MoveECell(this, newcell);
  };
  TableEditorSC.TableEditor.prototype.ReplaceCell = function (cell, row, col) {
    TableEditorSC.ReplaceCell(this, cell, row, col);
  };
  TableEditorSC.TableEditor.prototype.UpdateCellCSS = function (cell, row, col) {
    TableEditorSC.UpdateCellCSS(this, cell, row, col);
  };
  TableEditorSC.TableEditor.prototype.SetECellHeaders = function (selected) {
    TableEditorSC.SetECellHeaders(this, selected);
  };
  TableEditorSC.TableEditor.prototype.EnsureECellVisible = function () {
    TableEditorSC.EnsureECellVisible(this);
  };
  TableEditorSC.TableEditor.prototype.ECellReadonly = function (coord) {
    return TableEditorSC.ECellReadonly(this, coord);
  };
  TableEditorSC.TableEditor.prototype.RangeAnchor = function (coord) {
    TableEditorSC.RangeAnchor(this, coord);
  };
  TableEditorSC.TableEditor.prototype.RangeExtend = function (coord) {
    TableEditorSC.RangeExtend(this, coord);
  };
  TableEditorSC.TableEditor.prototype.RangeRemove = function () {
    TableEditorSC.RangeRemove(this);
  };
  TableEditorSC.TableEditor.prototype.Range2Remove = function () {
    TableEditorSC.Range2Remove(this);
  };
  TableEditorSC.TableEditor.prototype.FitToEditTable = function () {
    TableEditorSC.FitToEditTable(this);
  };
  TableEditorSC.TableEditor.prototype.CalculateEditorPositions = function () {
    TableEditorSC.CalculateEditorPositions(this);
  };
  TableEditorSC.TableEditor.prototype.ScheduleRender = function (renderwidgets) {
    if (TableEditorSC._app && renderwidgets == true) this.context.sheetobj.widgetsClean = false;
    TableEditorSC.ScheduleRender(this);
  };
  TableEditorSC.TableEditor.prototype.DoRenderStep = function () {
    TableEditorSC.DoRenderStep(this);
  };
  TableEditorSC.TableEditor.prototype.SchedulePositionCalculations = function () {
    TableEditorSC.SchedulePositionCalculations(this);
  };
  TableEditorSC.TableEditor.prototype.DoPositionCalculations = function () {
    TableEditorSC.DoPositionCalculations(this);
  };
  TableEditorSC.TableEditor.prototype.CalculateRowPositions = function (panenum, positions, sizes) {
    return TableEditorSC.CalculateRowPositions(this, panenum, positions, sizes);
  };
  TableEditorSC.TableEditor.prototype.CalculateColPositions = function (panenum, positions, sizes) {
    return TableEditorSC.CalculateColPositions(this, panenum, positions, sizes);
  };
  TableEditorSC.TableEditor.prototype.ScrollRelative = function (vertical, amount) {
    TableEditorSC.ScrollRelative(this, vertical, amount);
  };
  TableEditorSC.TableEditor.prototype.ScrollRelativeBoth = function (vamount, hamount) {
    TableEditorSC.ScrollRelativeBoth(this, vamount, hamount);
  };
  TableEditorSC.TableEditor.prototype.PageRelative = function (vertical, direction) {
    TableEditorSC.PageRelative(this, vertical, direction);
  };
  TableEditorSC.TableEditor.prototype.LimitLastPanes = function () {
    TableEditorSC.LimitLastPanes(this);
  };
  TableEditorSC.TableEditor.prototype.ScrollTableUpOneRow = function () {
    return TableEditorSC.ScrollTableUpOneRow(this);
  };
  TableEditorSC.TableEditor.prototype.ScrollTableDownOneRow = function () {
    return TableEditorSC.ScrollTableDownOneRow(this);
  };
  TableEditorSC.TableEditor.prototype.ScrollTableLeftOneCol = function () {
    return TableEditorSC.ScrollTableLeftOneCol(this);
  };
  TableEditorSC.TableEditor.prototype.ScrollTableRightOneCol = function () {
    return TableEditorSC.ScrollTableRightOneCol(this);
  };
  TableEditorSC.TableEditor.prototype.StopPropagation = function () {
    return TableEditorSC.StopPropagation(this);
  };
  TableEditorSC.TableEditor.prototype.SetMouseMoveUp = function () {
    return TableEditorSC.SetMouseMoveUp(this);
  };
  TableEditorSC.TableEditor.prototype.RemoveMouseMoveUp = function () {
    return TableEditorSC.RemoveMouseMoveUp(this);
  };
  TableEditorSC.CreateTableEditor = function (editor, width, height) {
    var scc = TableEditorSC.Constants;
    var AssignID = TableEditorSC.AssignID;
    editor.toplevel = SocialCalc.document.createElement('div');
    editor.toplevel.style.position = 'relative';
    AssignID(editor, editor.toplevel, 'toplevel');
    editor.width = width;
    editor.height = height;
    editor.griddiv = SocialCalc.document.createElement('div');
    editor.tablewidth = Math.max(0, width - scc.defaultTableControlThickness);
    editor.tableheight = Math.max(0, height - scc.defaultTableControlThickness);
    editor.griddiv.style.width = editor.tablewidth + 'px';
    editor.griddiv.style.height = editor.tableheight + 'px';
    editor.griddiv.style.overflow = 'hidden';
    editor.griddiv.style.cursor = 'default';
    if (scc.cteGriddivClass) editor.griddiv.className = scc.cteGriddivClass;
    AssignID(editor, editor.griddiv, 'griddiv');
    editor.FitToEditTable();
    editor.EditorRenderSheet();
    editor.griddiv.appendChild(editor.fullgrid);
    editor.verticaltablecontrol = new TableEditorSC.TableControl(editor, true, editor.tableheight);
    editor.verticaltablecontrol.CreateTableControl();
    AssignID(editor, editor.verticaltablecontrol.main, 'tablecontrolv');
    editor.horizontaltablecontrol = new TableEditorSC.TableControl(
      editor,
      false,
      editor.tablewidth,
    );
    editor.horizontaltablecontrol.CreateTableControl();
    AssignID(editor, editor.horizontaltablecontrol.main, 'tablecontrolh');
    var table, tbody, tr, td, ta;
    table = SocialCalc.document.createElement('table');
    editor.layouttable = table;
    table.setAttribute('cellspacing', '0');
    table.setAttribute('cellpadding', '0');
    AssignID(editor, table, 'layouttable');
    tbody = SocialCalc.document.createElement('tbody');
    table.appendChild(tbody);
    tr = SocialCalc.document.createElement('tr');
    tbody.appendChild(tr);
    td = SocialCalc.document.createElement('td');
    td.appendChild(editor.griddiv);
    tr.appendChild(td);
    if (TableEditorSC._app != true) {
      td = SocialCalc.document.createElement('td');
      td.appendChild(editor.verticaltablecontrol.main);
      tr.appendChild(td);
    }
    tr = SocialCalc.document.createElement('tr');
    tbody.appendChild(tr);
    if (TableEditorSC._app != true) {
      td = SocialCalc.document.createElement('td');
      td.appendChild(editor.horizontaltablecontrol.main);
      tr.appendChild(td);
    }
    td = SocialCalc.document.createElement('td');
    if (TableEditorSC._app) {
      td.style.background = 'url(' + editor.imageprefix + 'logo.gif) no-repeat right center';
    } else {
      td.style.background = 'url(' + editor.imageprefix + 'logo.gif) no-repeat center center';
    }
    td.innerHTML =
      "<div style='cursor:pointer;font-size:1px;'><img src='" +
      editor.imageprefix +
      "1x1.gif' border='0' width='18' height='18'></div>";
    tr.appendChild(td);
    editor.logo = td;
    AssignID(editor, editor.logo, 'logo');
    var logoImg = td.firstChild && td.firstChild.firstChild;
    if (logoImg) logoImg.title = 'SocialCalc';
    editor.toplevel.appendChild(editor.layouttable);
    if (!editor.noEdit) {
      editor.inputEcho = new TableEditorSC.InputEcho(editor);
      AssignID(editor, editor.inputEcho.main, 'inputecho');
    }
    editor.cellhandles = new TableEditorSC.CellHandles(editor);
    ta = SocialCalc.document.createElement('textarea');
    TableEditorSC.setStyles(
      ta,
      'display:none;position:absolute;height:1px;width:1px;opacity:0;filter:alpha(opacity=0);',
    );
    ta.value = '';
    editor.pasteTextarea = ta;
    AssignID(editor, editor.pasteTextarea, 'pastetextarea');
    if (navigator.userAgent.match(/Safari\//) && !navigator.userAgent.match(/Chrome\//)) {
      window.removeEventListener('beforepaste', TableEditorSC.SafariPasteFunction, false);
      window.addEventListener('beforepaste', TableEditorSC.SafariPasteFunction, false);
      window.removeEventListener('beforecopy', TableEditorSC.SafariPasteFunction, false);
      window.addEventListener('beforecopy', TableEditorSC.SafariPasteFunction, false);
      window.removeEventListener('beforecut', TableEditorSC.SafariPasteFunction, false);
      window.addEventListener('beforecut', TableEditorSC.SafariPasteFunction, false);
    }
    editor.toplevel.appendChild(editor.pasteTextarea);
    TableEditorSC.MouseWheelRegister(editor.toplevel, {
      WheelMove: TableEditorSC.EditorProcessMouseWheel,
      editor,
    });
    TableEditorSC.KeyboardSetFocus(editor);
    TableEditorSC.EditorSheetStatusCallback(null, 'startup', null, editor);
    return editor.toplevel;
  };
  TableEditorSC.SafariPasteFunction = function (e) {
    e.preventDefault();
  };
  TableEditorSC.ResizeTableEditor = function (editor, width, height) {
    var scc = TableEditorSC.Constants;
    editor.width = width;
    editor.height = height;
    editor.toplevel.style.width = width + 'px';
    editor.toplevel.style.height = height + 'px';
    if (TableEditorSC._app) {
      editor.tablewidth = Math.max(0, width);
    } else {
      editor.tablewidth = Math.max(0, width - scc.defaultTableControlThickness);
    }
    editor.tableheight = Math.max(0, height - scc.defaultTableControlThickness);
    editor.griddiv.style.width = editor.tablewidth + 'px';
    editor.griddiv.style.height = editor.tableheight + 'px';
    editor.verticaltablecontrol.main.style.height = editor.tableheight + 'px';
    editor.horizontaltablecontrol.main.style.width = editor.tablewidth + 'px';
    editor.FitToEditTable();
    editor.ScheduleRender();
    return;
  };
  TableEditorSC.SaveEditorSettings = function (editor) {
    var i, setting;
    var context = editor.context;
    var range = editor.range;
    var result = '';
    result += 'version:1.0\n';
    for (i = 0; i < context.rowpanes.length; i++) {
      result +=
        'rowpane:' + i + ':' + context.rowpanes[i].first + ':' + context.rowpanes[i].last + '\n';
    }
    for (i = 0; i < context.colpanes.length; i++) {
      result +=
        'colpane:' + i + ':' + context.colpanes[i].first + ':' + context.colpanes[i].last + '\n';
    }
    if (editor.ecell) {
      result += 'ecell:' + editor.ecell.coord + '\n';
    }
    if (range.hasrange) {
      result +=
        'range:' +
        range.anchorcoord +
        ':' +
        range.top +
        ':' +
        range.bottom +
        ':' +
        range.left +
        ':' +
        range.right +
        '\n';
    }
    for (setting in editor.SettingsCallbacks) {
      result += editor.SettingsCallbacks[setting].save(editor, setting);
    }
    return result;
  };
  TableEditorSC.LoadEditorSettings = function (editor, str, flags) {
    var lines = str.split(/\r\n|\n/);
    var parts = [];
    var line, i, cr, row, col, coord, setting;
    var context = editor.context;
    var highlights, range;
    context.rowpanes = [
      {
        first: 1,
        last: 1,
      },
    ];
    context.colpanes = [
      {
        first: 1,
        last: 1,
      },
    ];
    editor.ecell = null;
    editor.range = { hasrange: false };
    editor.range2 = { hasrange: false };
    range = editor.range;
    context.highlights = {};
    highlights = context.highlights;
    for (i = 0; i < lines.length; i++) {
      line = lines[i];
      parts = line.split(':');
      setting = parts[0];
      switch (setting) {
        case 'version':
          break;
        case 'rowpane':
          context.rowpanes[parts[1] - 0] = {
            first: parts[2] - 0,
            last: parts[3] - 0,
          };
          break;
        case 'colpane':
          context.colpanes[parts[1] - 0] = {
            first: parts[2] - 0,
            last: parts[3] - 0,
          };
          break;
        case 'ecell':
          editor.ecell = TableEditorSC.coordToCr(parts[1]);
          editor.ecell.coord = parts[1];
          highlights[parts[1]] = 'cursor';
          break;
        case 'range':
          range.hasrange = true;
          range.anchorcoord = parts[1];
          cr = TableEditorSC.coordToCr(range.anchorcoord);
          range.anchorrow = cr.row;
          range.anchorcol = cr.col;
          range.top = parts[2] - 0;
          range.bottom = parts[3] - 0;
          range.left = parts[4] - 0;
          range.right = parts[5] - 0;
          for (row = range.top; row <= range.bottom; row++) {
            for (col = range.left; col <= range.right; col++) {
              coord = TableEditorSC.crToCoord(col, row);
              if (highlights[coord] != 'cursor') {
                highlights[coord] = 'range';
              }
            }
          }
          break;
        default:
          if (editor.SettingsCallbacks[setting]) {
            editor.SettingsCallbacks[setting].load(editor, setting, line, flags);
          }
          break;
      }
    }
    return;
  };
  TableEditorSC.EditorRenderSheet = function (editor) {
    editor.EditorMouseUnregister();
    var sheetobj = editor.context.sheetobj;
    if (sheetobj.reRenderCellList != null && TableEditorSC._app && sheetobj.widgetsClean === true) {
      for (var index in sheetobj.reRenderCellList) {
        var coord = sheetobj.reRenderCellList[index];
        var valuetype = sheetobj.cells[coord].valuetype;
        if (valuetype.charAt(1) != 'i' || valuetype != sheetobj.cells[coord].prevvaluetype) {
          var cr = TableEditorSC.coordToCr(coord);
          var cell = TableEditorSC.GetEditorCellElement(editor, cr.row, cr.col);
          if (cell != null) editor.ReplaceCell(cell, cr.row, cr.col);
        }
      }
      sheetobj.reRenderCellList = [];
    } else {
      editor.fullgrid = editor.context.RenderSheet(editor.fullgrid);
      if (sheetobj.reRenderCellList != null && TableEditorSC._app) {
        sheetobj.widgetsClean = true;
        sheetobj.reRenderCellList = [];
      }
    }
    if (editor.ecell) editor.SetECellHeaders('selected');
    TableEditorSC.AssignID(editor, editor.fullgrid, 'fullgrid');
    if (!TableEditorSC._app) editor.fullgrid.className = 'te_download';
    editor.EditorMouseRegister();
  };
  TableEditorSC.EditorScheduleSheetCommands = function (editor, cmdstr, saveundo, ignorebusy) {
    if (editor.state != 'start' && !ignorebusy) {
      return;
    }
    if (editor.busy && !ignorebusy) {
      editor.deferredCommands.push({
        cmdstr,
        saveundo,
      });
      return;
    }
    var cmdTokens = cmdstr.split(' ');
    switch (cmdTokens[0]) {
      case 'recalc':
      case 'redisplay':
        editor.context.sheetobj.ScheduleSheetCommands(cmdstr, false);
        break;
      case 'undo':
        if (TableEditorSC._app) editor.context.sheetobj.widgetsClean = false;
        editor.SheetUndo();
        break;
      case 'redo':
        if (TableEditorSC._app) editor.context.sheetobj.widgetsClean = false;
        editor.SheetRedo();
        break;
      case 'setemailparameters':
        var emailsSent;
        try {
          emailsSent = TableEditorSC.TriggerIoAction.Email(cmdTokens[1], cmdTokens[2]);
        } finally {
          if (!emailsSent || !emailsSent.length) {
            editor.context.sheetobj.ScheduleSheetCommands('', false);
          }
        }
        break;
      default:
        editor.context.sheetobj.ScheduleSheetCommands(cmdstr, saveundo);
        break;
    }
  };
  TableEditorSC.EditorSheetStatusCallback = function (recalcdata, status, arg, editor) {
    var f, cell, dcmd;
    var sheetobj = editor.context.sheetobj;
    var signalstatus = function (s) {
      for (f in editor.StatusCallback) {
        if (editor.StatusCallback[f].func) {
          editor.StatusCallback[f].func(editor, s, arg, editor.StatusCallback[f].params);
        }
      }
    };
    switch (status) {
      case 'startup':
        break;
      case 'cmdstart':
        editor.busy = true;
        sheetobj.celldisplayneeded = '';
        break;
      case 'cmdextension':
        break;
      case 'cmdend':
        signalstatus(status);
        if (sheetobj.changedrendervalues) {
          editor.context.PrecomputeSheetFontsAndLayouts();
          editor.context.CalculateCellSkipData();
          sheetobj.changedrendervalues = false;
        }
        if (sheetobj.celldisplayneeded && !sheetobj.renderneeded) {
          if (
            sheetobj.cells[sheetobj.celldisplayneeded] &&
            sheetobj.cells[sheetobj.celldisplayneeded].valuetype != 'e#N/A'
          ) {
            var cr = TableEditorSC.coordToCr(sheetobj.celldisplayneeded);
            cell = TableEditorSC.GetEditorCellElement(editor, cr.row, cr.col);
            editor.ReplaceCell(cell, cr.row, cr.col);
          }
        }
        if (editor.deferredCommands.length) {
          dcmd = editor.deferredCommands.shift();
          editor.EditorScheduleSheetCommands(dcmd.cmdstr, dcmd.saveundo, true);
          return;
        }
        if (
          sheetobj.attribs.needsrecalc &&
          (sheetobj.attribs.recalc != 'off' || sheetobj.recalconce) &&
          editor.recalcFunction
        ) {
          editor.FitToEditTable();
          sheetobj.renderneeded = false;
          if (sheetobj.recalconce) delete sheetobj.recalconce;
          editor.recalcFunction(editor);
        } else {
          if (sheetobj.renderneeded) {
            editor.FitToEditTable();
            sheetobj.renderneeded = false;
            editor.ScheduleRender(false);
          } else {
            editor.SchedulePositionCalculations();
          }
        }
        if (sheetobj.hiddencolrow == 'col') {
          if (editor.ecell !== null) {
            var col = editor.ecell.col;
            while (sheetobj.colattribs.hide[TableEditorSC.rcColname(col)] == 'yes') {
              col++;
            }
            var coord = TableEditorSC.crToCoord(col, editor.ecell.row);
            editor.MoveECell(coord);
            sheetobj.hiddencolrow = '';
          }
        }
        if (sheetobj.hiddencolrow == 'row') {
          if (editor.ecell !== null) {
            var row = editor.ecell.row;
            while (sheetobj.rowattribs.hide[row] == 'yes') {
              row++;
            }
            var coord = TableEditorSC.crToCoord(editor.ecell.col, row);
            editor.MoveECell(coord);
            sheetobj.hiddencolrow = '';
          }
        }
        return;
      case 'calcstart':
        editor.busy = true;
        break;
      case 'calccheckdone':
      case 'calcorder':
      case 'calcstep':
      case 'calcloading':
      case 'calcserverfunc':
        break;
      case 'calcfinished':
        signalstatus(status);
        editor.ScheduleRender(false);
        return;
      case 'schedrender':
        editor.busy = true;
        break;
      case 'renderdone':
        break;
      case 'schedposcalc':
        editor.busy = true;
        break;
      case 'doneposcalc':
        if (editor.deferredEmailCommands.length) {
          signalstatus(status);
          var emailcmd = editor.deferredEmailCommands.shift();
          editor.EditorScheduleSheetCommands(emailcmd.cmdstr, emailcmd.saveundo, true);
          return;
        }
        if (editor.deferredCommands.length) {
          signalstatus(status);
          dcmd = editor.deferredCommands.shift();
          editor.EditorScheduleSheetCommands(dcmd.cmdstr, dcmd.saveundo, true);
        } else {
          editor.busy = false;
          signalstatus(status);
          if (editor.state == 'start') editor.DisplayCellContents();
        }
        return;
      case 'emailing':
      case 'confirmemailsent':
        break;
      default:
        (function(){})('Unknown status: ' + status);
        break;
    }
    signalstatus(status);
    return;
  };
  TableEditorSC.EditorGetStatuslineString = function (editor, status, arg, params) {
    var scc = TableEditorSC.Constants;
    var sstr, progress, coord, circ, r, c, cell, sum;
    progress = '';
    switch (status) {
      case 'moveecell':
      case 'rangechange':
      case 'startup':
        break;
      case 'cmdstart':
        params.command = true;
        document.body.style.cursor = 'progress';
        editor.griddiv.style.cursor = 'progress';
        progress = scc.s_statusline_executing;
        break;
      case 'cmdextension':
        progress = 'Command Extension: ' + arg;
        break;
      case 'cmdend':
        params.command = false;
        break;
      case 'schedrender':
        progress = scc.s_statusline_displaying;
        break;
      case 'renderdone':
        progress = ' ';
        break;
      case 'schedposcalc':
        progress = scc.s_statusline_displaying;
        break;
      case 'cmdendnorender':
      case 'doneposcalc':
        document.body.style.cursor = 'default';
        editor.griddiv.style.cursor = 'default';
        if (params.emailing == 'sent') {
          progress = params.emailreponse;
          params.emailreponse = '';
          params.emailing = 'done';
        }
        break;
      case 'calcorder':
        progress =
          scc.s_statusline_ordering + Math.floor((100 * arg.count) / (arg.total || 1)) + '%';
        break;
      case 'calcstep':
        progress =
          scc.s_statusline_calculating + Math.floor((100 * arg.count) / (arg.total || 1)) + '%';
        break;
      case 'calcloading':
        progress =
          scc.s_statusline_calculatingls +
          ': ' +
          (SocialCalc.Callbacks.untrustedContent
            ? SocialCalc.special_chars(arg.sheetname + '')
            : arg.sheetname);
        break;
      case 'calcserverfunc':
        progress =
          scc.s_statusline_calculating +
          Math.floor((100 * arg.count) / (arg.total || 1)) +
          '%, ' +
          scc.s_statusline_doingserverfunc +
          arg.funcname +
          scc.s_statusline_incell +
          arg.coord;
        break;
      case 'calcstart':
        params.calculating = true;
        document.body.style.cursor = 'progress';
        editor.griddiv.style.cursor = 'progress';
        progress = scc.s_statusline_calcstart;
        break;
      case 'calccheckdone':
        break;
      case 'calcfinished':
        params.calculating = false;
        break;
      case 'emailing':
        params.emailing = 'sending';
        params.emailreponse = '';
        break;
      case 'confirmemailsent':
        params.emailing = 'sent';
        if (typeof params.emailreponse === 'undefined') params.emailreponse = '';
        params.emailreponse += arg;
        break;
      default:
        progress = status;
        break;
    }
    if (params.emailing == 'sending') {
      progress += scc.s_statusline_sendemail;
    }
    if (params.emailing == 'sent') {
      progress += params.emailreponse;
    }
    if (!progress && params.calculating) {
      progress = scc.s_statusline_calculating;
    }
    if (
      !params.calculating &&
      !params.command &&
      !progress &&
      editor.range.hasrange &&
      (editor.range.left != editor.range.right || editor.range.top != editor.range.bottom)
    ) {
      sum = 0;
      for (r = editor.range.top; r <= editor.range.bottom; r++) {
        for (c = editor.range.left; c <= editor.range.right; c++) {
          cell = editor.context.sheetobj.cells[TableEditorSC.crToCoord(c, r)];
          if (!cell) continue;
          if (cell.valuetype && cell.valuetype.charAt(0) == 'n') {
            sum += cell.datavalue - 0;
          }
        }
      }
      sum = TableEditorSC.FormatNumber.formatNumberWithFormat(sum, '[,]General', '');
      coord =
        TableEditorSC.crToCoord(editor.range.left, editor.range.top) +
        ':' +
        TableEditorSC.crToCoord(editor.range.right, editor.range.bottom);
      progress =
        coord +
        ' (' +
        (editor.range.right - editor.range.left + 1) +
        'x' +
        (editor.range.bottom - editor.range.top + 1) +
        ') ' +
        scc.s_statusline_sum +
        '=' +
        sum +
        ' ' +
        progress;
    }
    sstr = (editor.ecell || {}).coord + ' &nbsp; ' + progress;
    if (!params.calculating && editor.context.sheetobj.attribs.needsrecalc == 'yes') {
      sstr += ' &nbsp; ' + scc.s_statusline_recalcneeded;
    }
    circ = editor.context.sheetobj.attribs.circularreferencecell;
    if (circ) {
      circ = circ.replace(/\|/, ' referenced by ');
      sstr += ' &nbsp; ' + scc.s_statusline_circref + circ + '</span>';
    }
    sstr += '';
    return sstr;
  };
  TableEditorSC.EditorMouseInfo = {
    registeredElements: [],
    editor: null,
    element: null,
    ignore: false,
    mousedowncoord: '',
    mouselastcoord: '',
    mouseresizecol: '',
    mouseresizeclientx: null,
    mouseresizedisplay: null,
  };
  TableEditorSC.EditorMouseRegister = function (editor) {
    var mouseinfo = TableEditorSC.EditorMouseInfo;
    var element = editor.fullgrid;
    var i;
    for (i = 0; i < mouseinfo.registeredElements.length; i++) {
      if (mouseinfo.registeredElements[i].editor == editor) {
        if (mouseinfo.registeredElements[i].element == element) {
          return;
        }
        break;
      }
    }
    if (i < mouseinfo.registeredElements.length) {
      mouseinfo.registeredElements[i].element = element;
    } else {
      mouseinfo.registeredElements.push({
        element,
        editor,
      });
    }
    element.addEventListener('mousedown', TableEditorSC.ProcessEditorMouseDown, false);
    element.addEventListener('dblclick', TableEditorSC.ProcessEditorDblClick, false);
    mouseinfo.ignore = false;
    return;
  };
  TableEditorSC.EditorMouseUnregister = function (editor) {
    var mouseinfo = TableEditorSC.EditorMouseInfo;
    var i, oldelement;
    for (i = 0; i < mouseinfo.registeredElements.length; i++) {
      if (mouseinfo.registeredElements[i].editor == editor) {
        break;
      }
    }
    if (i < mouseinfo.registeredElements.length) {
      oldelement = mouseinfo.registeredElements[i].element;
      oldelement.removeEventListener('mousedown', TableEditorSC.ProcessEditorMouseDown, false);
      oldelement.removeEventListener('dblclick', TableEditorSC.ProcessEditorDblClick, false);
      mouseinfo.registeredElements.splice(i, 1);
    }
    return;
  };
  TableEditorSC.StopPropagation = function (event) {
    if (event.stopPropagation) event.stopPropagation();
    else event.cancelBubble = true;
    if (event.preventDefault) event.preventDefault();
    else event.returnValue = false;
  };
  TableEditorSC.SetMouseMoveUp = function (move, up, element, event) {
    document.addEventListener('mousemove', move, true);
    document.addEventListener('mouseup', up, true);
    TableEditorSC.StopPropagation(event);
  };
  TableEditorSC.RemoveMouseMoveUp = function (move, up, element, event) {
    TableEditorSC.StopPropagation(event);
    document.removeEventListener('mousemove', move, true);
    document.removeEventListener('mouseup', up, true);
  };
  TableEditorSC.ProcessEditorMouseDown = function (e) {
    var editor, result, coord, range;
    var event = e || window.event;
    var mouseinfo = TableEditorSC.EditorMouseInfo;
    var ele = event.target || event.srcElement;
    var target = ele;
    if (target.nodeType == 3) target = target.parentNode;
    var mobj;
    if (mouseinfo.ignore) return;
    for (mobj = null; !mobj && ele; ele = ele.parentNode) {
      mobj = TableEditorSC.LookupElement(ele, mouseinfo.registeredElements);
    }
    if (!mobj) {
      mouseinfo.editor = null;
      return;
    }
    editor = mobj.editor;
    mouseinfo.element = ele;
    range = editor.range;
    var pos = TableEditorSC.GetElementPositionWithScroll(editor.toplevel);
    var clientX = event.clientX - pos.left;
    var clientY = event.clientY - pos.top;
    result = TableEditorSC.GridMousePosition(editor, clientX, clientY);
    if (!result) return;
    mouseinfo.editor = editor;
    if (result.rowheader) {
      if (result.rowselect) TableEditorSC.ProcessEditorRowselectMouseDown(event, ele, result);
      else TableEditorSC.ProcessEditorRowsizeMouseDown(event, ele, result);
      return;
    }
    if (result.colheader) {
      if (result.colselect) TableEditorSC.ProcessEditorColselectMouseDown(event, ele, result);
      else TableEditorSC.ProcessEditorColsizeMouseDown(event, ele, result);
      return;
    }
    if (!result.coord) return;
    if (!range.hasrange) {
      if (event.shiftKey) editor.RangeAnchor();
    }
    coord = editor.MoveECell(result.coord);
    if (TableEditorSC._app == true) {
      TableEditorSC.CmdGotFocus(true);
      return;
    }
    var clickedCell = editor.context.sheetobj.cells[coord];
    if (clickedCell && clickedCell.valuetype.charAt(1) == 'i') {
      var formula_name = clickedCell.valuetype.substring(2);
      var widget_id = formula_name + '_' + coord;
      if (target && widget_id == target.id)
        TableEditorSC.CmdGotFocus(document.getElementById(widget_id));
      return;
    }
    if (range.hasrange) {
      if (event.shiftKey) editor.RangeExtend();
      else editor.RangeRemove();
    }
    mouseinfo.mousedowncoord = coord;
    mouseinfo.mouselastcoord = coord;
    editor.EditorMouseRange(coord);
    TableEditorSC.KeyboardSetFocus(editor);
    if (editor.state != 'start' && editor.inputBox) editor.inputBox.element.focus();
    TableEditorSC.SetMouseMoveUp(
      TableEditorSC.ProcessEditorMouseMove,
      TableEditorSC.ProcessEditorMouseUp,
      ele,
      event,
    );
    return;
  };
  TableEditorSC.EditorMouseRange = function (editor, coord) {
    var inputtext, wval;
    var range = editor.range;
    switch (editor.state) {
      case 'input':
        inputtext = editor.inputBox.GetText();
        wval = editor.workingvalues;
        if (
          ('(+-*/,:!&<>=^'.indexOf(inputtext.slice(-1)) >= 0 && inputtext.slice(0, 1) == '=') ||
          inputtext == '='
        ) {
          wval.partialexpr = inputtext;
        }
        if (wval.partialexpr) {
          if (coord) {
            if (range.hasrange) {
              editor.inputBox.SetText(
                wval.partialexpr +
                  TableEditorSC.crToCoord(range.left, range.top) +
                  ':' +
                  TableEditorSC.crToCoord(range.right, range.bottom),
              );
            } else {
              editor.inputBox.SetText(wval.partialexpr + coord);
            }
          }
        } else {
          editor.inputBox.Blur();
          editor.inputBox.ShowInputBox(false);
          editor.state = 'start';
          editor.cellhandles.ShowCellHandles(true);
          editor.EditorSaveEdit();
          editor.inputBox.DisplayCellContents(null);
        }
        break;
      case 'inputboxdirect':
        editor.inputBox.Blur();
        editor.inputBox.ShowInputBox(false);
        editor.state = 'start';
        editor.cellhandles.ShowCellHandles(true);
        editor.EditorSaveEdit();
        editor.inputBox.DisplayCellContents(null);
        break;
    }
  };
  TableEditorSC.ProcessEditorMouseMove = function (e) {
    var editor, result;
    var event = e || window.event;
    var mouseinfo = TableEditorSC.EditorMouseInfo;
    editor = mouseinfo.editor;
    if (!editor) return;
    if (mouseinfo.ignore) return;
    var pos = TableEditorSC.GetElementPositionWithScroll(editor.toplevel);
    var clientX = event.clientX - pos.left;
    var clientY = event.clientY - pos.top;
    result = TableEditorSC.GridMousePosition(editor, clientX, clientY);
    if (!result) return;
    if (result && !result.coord) {
      TableEditorSC.SetDragAutoRepeat(editor, result);
      return;
    }
    TableEditorSC.SetDragAutoRepeat(editor, null);
    if (!result.coord) return;
    if (result.coord != mouseinfo.mouselastcoord) {
      if (!event.shiftKey && !editor.range.hasrange) {
        editor.RangeAnchor(mouseinfo.mousedowncoord);
      }
      editor.MoveECell(result.coord);
      editor.RangeExtend();
    }
    mouseinfo.mouselastcoord = result.coord;
    editor.EditorMouseRange(result.coord);
    TableEditorSC.StopPropagation(event);
    return;
  };
  TableEditorSC.ProcessEditorMouseUp = function (e) {
    var editor, element, result;
    var event = e || window.event;
    var mouseinfo = TableEditorSC.EditorMouseInfo;
    editor = mouseinfo.editor;
    if (!editor) return;
    if (mouseinfo.ignore) return;
    element = mouseinfo.element;
    var pos = TableEditorSC.GetElementPositionWithScroll(editor.toplevel);
    var clientX = event.clientX - pos.left;
    var clientY = event.clientY - pos.top;
    result = TableEditorSC.GridMousePosition(editor, clientX, clientY);
    TableEditorSC.SetDragAutoRepeat(editor, null);
    if (!result) return;
    if (!result.coord) result.coord = editor.ecell.coord;
    if (editor.range.hasrange) {
      editor.MoveECell(result.coord);
      editor.RangeExtend();
    } else if (result.coord && result.coord != mouseinfo.mousedowncoord) {
      editor.RangeAnchor(mouseinfo.mousedowncoord);
      editor.MoveECell(result.coord);
      editor.RangeExtend();
    }
    editor.EditorMouseRange(result.coord);
    mouseinfo.editor = null;
    TableEditorSC.RemoveMouseMoveUp(
      TableEditorSC.ProcessEditorMouseMove,
      TableEditorSC.ProcessEditorMouseUp,
      element,
      event,
    );
    return false;
  };
  TableEditorSC.ProcessEditorColsizeMouseDown = function (e, ele, result) {
    var event = e || window.event;
    var mouseinfo = TableEditorSC.EditorMouseInfo;
    var editor = mouseinfo.editor;
    var pos = TableEditorSC.GetElementPositionWithScroll(editor.toplevel);
    var clientX = event.clientX - pos.left;
    mouseinfo.mouseresizecolnum = result.coltoresize;
    mouseinfo.mouseresizecol = TableEditorSC.rcColname(result.coltoresize);
    mouseinfo.mousedownclientx = clientX;
    mouseinfo.mousecoltounhide = result.coltounhide;
    if (result.coltoresize) {
      var sizedisplay = SocialCalc.document.createElement('div');
      mouseinfo.mouseresizedisplay = sizedisplay;
      sizedisplay.style.width = 'auto';
      sizedisplay.style.position = 'absolute';
      sizedisplay.style.zIndex = '100';
      sizedisplay.style.top = editor.headposition.top + 'px';
      sizedisplay.style.left = editor.colpositions[result.coltoresize] + 'px';
      sizedisplay.innerHTML =
        '<table cellpadding="0" cellspacing="0"><tr><td style="height:100px;' +
        'border:1px dashed black;background-color:white;width:' +
        (editor.context.colwidth[mouseinfo.mouseresizecolnum] - 2) +
        'px;">&nbsp;</td>' +
        '<td><div style="font-size:small;color:white;background-color:gray;padding:4px;">' +
        editor.context.colwidth[mouseinfo.mouseresizecolnum] +
        '</div></td></tr></table>';
      var szTarget =
        sizedisplay.firstChild &&
        sizedisplay.firstChild.lastChild &&
        sizedisplay.firstChild.lastChild.firstChild &&
        sizedisplay.firstChild.lastChild.firstChild.childNodes[0];
      if (szTarget) TableEditorSC.setStyles(szTarget, 'filter:alpha(opacity=85);opacity:.85;');
      editor.toplevel.appendChild(sizedisplay);
    }
    TableEditorSC.SetMouseMoveUp(
      TableEditorSC.ProcessEditorColsizeMouseMove,
      TableEditorSC.ProcessEditorColsizeMouseUp,
      editor.toplevel,
      event,
    );
    return;
  };
  TableEditorSC.ProcessEditorColsizeMouseMove = function (e) {
    var event = e || window.event;
    var mouseinfo = TableEditorSC.EditorMouseInfo;
    var editor = mouseinfo.editor;
    if (!editor) return;
    if (mouseinfo.mouseresizecolnum) {
      var pos = TableEditorSC.GetElementPositionWithScroll(editor.toplevel);
      var clientX = event.clientX - pos.left;
      var newsize =
        editor.context.colwidth[mouseinfo.mouseresizecolnum] -
        0 +
        (clientX - mouseinfo.mousedownclientx);
      if (newsize < TableEditorSC.Constants.defaultMinimumColWidth)
        newsize = TableEditorSC.Constants.defaultMinimumColWidth;
      var sizedisplay = mouseinfo.mouseresizedisplay;
      sizedisplay.innerHTML =
        '<table cellpadding="0" cellspacing="0"><tr><td style="height:100px;' +
        'border:1px dashed black;background-color:white;width:' +
        (newsize - 2) +
        'px;">&nbsp;</td>' +
        '<td><div style="font-size:small;color:white;background-color:gray;padding:4px;">' +
        newsize +
        '</div></td></tr></table>';
      TableEditorSC.setStyles(
        sizedisplay.firstChild.lastChild.firstChild.childNodes[0],
        'filter:alpha(opacity=85);opacity:.85;',
      );
    }
    TableEditorSC.StopPropagation(event);
    return;
  };
  TableEditorSC.ProcessEditorColsizeMouseUp = function (e) {
    var event = e || window.event;
    var mouseinfo = TableEditorSC.EditorMouseInfo;
    var editor = mouseinfo.editor;
    if (!editor) return;
    var pos = TableEditorSC.GetElementPositionWithScroll(editor.toplevel);
    var clientX = event.clientX - pos.left;
    TableEditorSC.RemoveMouseMoveUp(
      TableEditorSC.ProcessEditorColsizeMouseMove,
      TableEditorSC.ProcessEditorColsizeMouseUp,
      editor.toplevel,
      event,
    );
    if (mouseinfo.mousecoltounhide) {
      editor.EditorScheduleSheetCommands(
        'set ' + TableEditorSC.rcColname(mouseinfo.mousecoltounhide) + ' hide',
        true,
        false,
      );
    } else if (mouseinfo.mouseresizecolnum) {
      var newsize =
        editor.context.colwidth[mouseinfo.mouseresizecolnum] -
        0 +
        (clientX - mouseinfo.mousedownclientx);
      if (newsize < TableEditorSC.Constants.defaultMinimumColWidth)
        newsize = TableEditorSC.Constants.defaultMinimumColWidth;
      editor.EditorScheduleSheetCommands(
        'set ' + mouseinfo.mouseresizecol + ' width ' + newsize,
        true,
        false,
      );
      if (editor.timeout) window.clearTimeout(editor.timeout);
      editor.timeout = window.setTimeout(TableEditorSC.FinishColRowSize, 1);
    }
    return false;
  };
  TableEditorSC.FinishColRowSize = function () {
    var mouseinfo = TableEditorSC.EditorMouseInfo;
    var editor = mouseinfo.editor;
    if (!editor) return;
    editor.toplevel.removeChild(mouseinfo.mouseresizedisplay);
    mouseinfo.mouseresizedisplay = null;
    mouseinfo.editor = null;
    return;
  };
  TableEditorSC.ProcessEditorRowselectMouseDown = function (e, ele, result) {
    var event = e || window.event;
    var mouseinfo = TableEditorSC.EditorMouseInfo;
    var editor = mouseinfo.editor;
    var sheet = TableEditorSC.GetSpreadsheetControlObject().sheet;
    var coord1 = TableEditorSC.crToCoord(1, result.row);
    var coord2 = TableEditorSC.crToCoord(sheet.LastCol(), result.row);
    var coord3 = TableEditorSC.crToCoord(editor.firstscrollingcol, result.row);
    editor.RangeAnchor(coord1);
    editor.RangeExtend(coord2);
    editor.MoveECell(coord3);
    TableEditorSC.SetMouseMoveUp(
      TableEditorSC.ProcessEditorRowselectMouseMove,
      TableEditorSC.ProcessEditorRowselectMouseUp,
      editor.toplevel,
      event,
    );
  };
  TableEditorSC.ProcessEditorRowselectMouseMove = function (e) {
    var event = e || window.event;
    var mouseinfo = TableEditorSC.EditorMouseInfo;
    var editor = mouseinfo.editor;
    var sheet = TableEditorSC.GetSpreadsheetControlObject().sheet;
    if (!editor) return;
    var pos = TableEditorSC.GetElementPositionWithScroll(editor.toplevel);
    var clientX = event.clientX - pos.left;
    var clientY = event.clientY - pos.top;
    var result = TableEditorSC.GridMousePosition(editor, clientX, clientY);
    var coord2 = TableEditorSC.crToCoord(sheet.LastCol(), result.row);
    var coord3 = TableEditorSC.crToCoord(editor.firstscrollingcol, result.row);
    editor.RangeExtend(coord2);
    editor.MoveECell(coord3);
    return;
  };
  TableEditorSC.ProcessEditorRowselectMouseUp = function (e) {
    var event = e || window.event;
    var mouseinfo = TableEditorSC.EditorMouseInfo;
    var editor = mouseinfo.editor;
    if (!editor) return;
    TableEditorSC.RemoveMouseMoveUp(
      TableEditorSC.ProcessEditorRowselectMouseMove,
      TableEditorSC.ProcessEditorRowselectMouseUp,
      editor.toplevel,
      event,
    );
    return;
  };
  TableEditorSC.ProcessEditorColselectMouseDown = function (e, ele, result) {
    var event = e || window.event;
    var mouseinfo = TableEditorSC.EditorMouseInfo;
    var editor = mouseinfo.editor;
    var sheet = TableEditorSC.GetSpreadsheetControlObject().sheet;
    var coord1 = TableEditorSC.crToCoord(result.col, 1);
    var coord2 = TableEditorSC.crToCoord(result.col, sheet.LastRow());
    var coord3 = TableEditorSC.crToCoord(result.col, editor.firstscrollingrow);
    editor.RangeAnchor(coord1);
    editor.RangeExtend(coord2);
    editor.MoveECell(coord3);
    TableEditorSC.SetMouseMoveUp(
      TableEditorSC.ProcessEditorColselectMouseMove,
      TableEditorSC.ProcessEditorColselectMouseUp,
      editor.toplevel,
      event,
    );
  };
  TableEditorSC.ProcessEditorColselectMouseMove = function (e) {
    var event = e || window.event;
    var mouseinfo = TableEditorSC.EditorMouseInfo;
    var editor = mouseinfo.editor;
    var sheet = TableEditorSC.GetSpreadsheetControlObject().sheet;
    if (!editor) return;
    var pos = TableEditorSC.GetElementPositionWithScroll(editor.toplevel);
    var clientX = event.clientX - pos.left;
    var clientY = event.clientY - pos.top;
    var result = TableEditorSC.GridMousePosition(editor, clientX, clientY);
    var coord2 = TableEditorSC.crToCoord(result.col, sheet.LastRow());
    var coord3 = TableEditorSC.crToCoord(result.col, editor.firstscrollingrow);
    editor.RangeExtend(coord2);
    editor.MoveECell(coord3);
    return;
  };
  TableEditorSC.ProcessEditorColselectMouseUp = function (e) {
    var event = e || window.event;
    var mouseinfo = TableEditorSC.EditorMouseInfo;
    var editor = mouseinfo.editor;
    if (!editor) return;
    TableEditorSC.RemoveMouseMoveUp(
      TableEditorSC.ProcessEditorColselectMouseMove,
      TableEditorSC.ProcessEditorColselectMouseUp,
      editor.toplevel,
      event,
    );
    return;
  };
  TableEditorSC.ProcessEditorRowsizeMouseDown = function (e, ele, result) {
    var event = e || window.event;
    var mouseinfo = TableEditorSC.EditorMouseInfo;
    var editor = mouseinfo.editor;
    var pos = TableEditorSC.GetSpreadsheetControlObject().spreadsheetDiv.firstChild.offsetHeight;
    var clientY = event.clientY - pos;
    mouseinfo.mouseresizerownum = result.rowtoresize;
    mouseinfo.mouseresizerow = result.rowtoresize;
    mouseinfo.mousedownclienty = clientY;
    mouseinfo.mouserowtounhide = result.rowtounhide;
    if (result.rowtoresize) {
      var sizedisplay = SocialCalc.document.createElement('div');
      mouseinfo.mouseresizedisplay = sizedisplay;
      sizedisplay.style.width = editor.context.totalwidth + 'px';
      sizedisplay.style.height = editor.rowpositions[result.rowtoresize] + 'px';
      sizedisplay.style.position = 'absolute';
      sizedisplay.style.zIndex = '100';
      sizedisplay.style.top = editor.rowpositions[result.rowtoresize] + 'px';
      sizedisplay.style.left = editor.headposition.left + 'px';
      sizedisplay.innerHTML =
        '<table cellpadding="0" cellspacing="0"><tr><td style="width:100px' +
        'border:1px dashed black;background-color:white;height:' +
        (editor.context.rowheight[mouseinfo.mouseresizerownum] - 2) +
        'px;">&nbsp;</td>' +
        '<td><div style="font-size:small;color:white;background-color:gray;padding:4px;">' +
        editor.context.rowheight[mouseinfo.mouseresizerownum] +
        '</div></td></tr></table>';
      var szTarget2 =
        sizedisplay.firstChild &&
        sizedisplay.firstChild.lastChild &&
        sizedisplay.firstChild.lastChild.firstChild &&
        sizedisplay.firstChild.lastChild.firstChild.childNodes[0];
      if (szTarget2) TableEditorSC.setStyles(szTarget2, 'filter:alpha(opacity=85);opacity:.5;');
      editor.toplevel.appendChild(sizedisplay);
    }
    TableEditorSC.SetMouseMoveUp(
      TableEditorSC.ProcessEditorRowsizeMouseMove,
      TableEditorSC.ProcessEditorRowsizeMouseUp,
      editor.toplevel,
      event,
    );
    return;
  };
  TableEditorSC.ProcessEditorRowsizeMouseMove = function (e) {
    var event = e || window.event;
    var mouseinfo = TableEditorSC.EditorMouseInfo;
    var editor = mouseinfo.editor;
    if (!editor) return;
    if (mouseinfo.mouseresizerownum) {
      var pos = TableEditorSC.GetSpreadsheetControlObject().spreadsheetDiv.firstChild.offsetHeight;
      var clientY = event.clientY - pos;
      var newsize =
        editor.context.rowheight[mouseinfo.mouseresizerownum] -
        0 +
        (clientY - mouseinfo.mousedownclienty);
      if (newsize < TableEditorSC.Constants.defaultAssumedRowHeight)
        newsize = TableEditorSC.Constants.defaultAssumedRowHeight;
      var sizedisplay = mouseinfo.mouseresizedisplay;
      sizedisplay.innerHTML =
        '<table cellpadding="0" cellspacing="0"><tr><td style="width:100px;' +
        'border:1px dashed black;background-color:white;height:' +
        (newsize - 2) +
        'px;">&nbsp;</td>' +
        '<td><div style="font-size:small;color:white;background-color:gray;padding:4px;">' +
        newsize +
        '</div></td></tr></table>';
      TableEditorSC.setStyles(
        sizedisplay.firstChild.lastChild.firstChild.childNodes[0],
        'filter:alpha(opacity=85);opacity:.5;',
      );
    }
    TableEditorSC.StopPropagation(event);
    return;
  };
  TableEditorSC.ProcessEditorRowsizeMouseUp = function (e) {
    var event = e || window.event;
    var mouseinfo = TableEditorSC.EditorMouseInfo;
    var editor = mouseinfo.editor;
    if (!editor) return;
    var pos = TableEditorSC.GetSpreadsheetControlObject().spreadsheetDiv.firstChild.offsetHeight;
    var clientY = event.clientY - pos;
    TableEditorSC.RemoveMouseMoveUp(
      TableEditorSC.ProcessEditorRowsizeMouseMove,
      TableEditorSC.ProcessEditorRowsizeMouseUp,
      editor.toplevel,
      event,
    );
    if (mouseinfo.mouserowtounhide) {
      editor.EditorScheduleSheetCommands(
        'set ' + mouseinfo.mouserowtounhide + ' hide',
        true,
        false,
      );
    } else if (mouseinfo.mouseresizerownum) {
      var newsize =
        editor.context.rowheight[mouseinfo.mouseresizerownum] -
        0 +
        (clientY - mouseinfo.mousedownclienty);
      if (newsize < TableEditorSC.Constants.defaultAssumedRowHeight)
        newsize = TableEditorSC.Constants.defaultAssumedRowHeight;
      editor.EditorScheduleSheetCommands(
        'set ' + mouseinfo.mouseresizerownum + ' height ' + newsize,
        true,
        false,
      );
      if (editor.timeout) window.clearTimeout(editor.timeout);
      editor.timeout = window.setTimeout(TableEditorSC.FinishColRowSize, 1);
    }
    return false;
  };
  TableEditorSC.AutoRepeatInfo = {
    timer: null,
    mouseinfo: null,
    repeatinterval: 1e3,
    editor: null,
    repeatcallback: null,
  };
  TableEditorSC.SetDragAutoRepeat = function (editor, mouseinfo, callback) {
    var repeatinfo = TableEditorSC.AutoRepeatInfo;
    var coord, direction;
    repeatinfo.repeatcallback = callback;
    if (!mouseinfo) {
      if (repeatinfo.timer) {
        window.clearTimeout(repeatinfo.timer);
        repeatinfo.timer = null;
      }
      repeatinfo.mouseinfo = null;
      return;
    }
    repeatinfo.editor = editor;
    if (repeatinfo.mouseinfo) {
      if (mouseinfo.rowheader || mouseinfo.rowfooter) {
        if (mouseinfo.row != repeatinfo.mouseinfo.row) {
          coord = TableEditorSC.crToCoord(editor.ecell.col, mouseinfo.row);
          if (repeatinfo.repeatcallback) {
            direction =
              mouseinfo.row < repeatinfo.mouseinfo.row
                ? 'left'
                : mouseinfo.row > repeatinfo.mouseinfo.row
                  ? 'right'
                  : '';
            repeatinfo.repeatcallback(coord, direction);
          } else {
            editor.MoveECell(coord);
            editor.MoveECell(coord);
            editor.RangeExtend();
            editor.EditorMouseRange(coord);
          }
        }
      } else if (mouseinfo.colheader || mouseinfo.colfooter) {
        if (mouseinfo.col != repeatinfo.mouseinfo.col) {
          coord = TableEditorSC.crToCoord(mouseinfo.col, editor.ecell.row);
          if (repeatinfo.repeatcallback) {
            direction =
              mouseinfo.row < repeatinfo.mouseinfo.row
                ? 'left'
                : mouseinfo.row > repeatinfo.mouseinfo.row
                  ? 'right'
                  : '';
            repeatinfo.repeatcallback(coord, direction);
          } else {
            editor.MoveECell(coord);
            editor.RangeExtend();
            editor.EditorMouseRange(coord);
          }
        }
      }
    }
    repeatinfo.mouseinfo = mouseinfo;
    if (mouseinfo.distance < 5) repeatinfo.repeatinterval = 333;
    else if (mouseinfo.distance < 10) repeatinfo.repeatinterval = 250;
    else if (mouseinfo.distance < 25) repeatinfo.repeatinterval = 100;
    else if (mouseinfo.distance < 35) repeatinfo.repeatinterval = 75;
    else {
      if (repeatinfo.timer) {
        window.clearTimeout(repeatinfo.timer);
        repeatinfo.timer = null;
      }
      return;
    }
    if (!repeatinfo.timer) {
      repeatinfo.timer = window.setTimeout(TableEditorSC.DragAutoRepeat, repeatinfo.repeatinterval);
    }
    return;
  };
  TableEditorSC.DragAutoRepeat = function () {
    var repeatinfo = TableEditorSC.AutoRepeatInfo;
    var mouseinfo = repeatinfo.mouseinfo;
    var direction, coord, cr;
    if (mouseinfo.rowheader) direction = 'left';
    else if (mouseinfo.rowfooter) direction = 'right';
    else if (mouseinfo.colheader) direction = 'up';
    else if (mouseinfo.colfooter) direction = 'down';
    if (repeatinfo.repeatcallback) {
      cr = TableEditorSC.coordToCr(repeatinfo.editor.ecell.coord);
      if (direction == 'left' && cr.col > 1) cr.col--;
      else if (direction == 'right') cr.col++;
      else if (direction == 'up' && cr.row > 1) cr.row--;
      else if (direction == 'down') cr.row++;
      coord = TableEditorSC.crToCoord(cr.col, cr.row);
      repeatinfo.repeatcallback(coord, direction);
    } else {
      coord = repeatinfo.editor.MoveECellWithKey('[a' + direction + ']shifted');
      if (coord) repeatinfo.editor.EditorMouseRange(coord);
    }
    repeatinfo.timer = window.setTimeout(TableEditorSC.DragAutoRepeat, repeatinfo.repeatinterval);
  };
  TableEditorSC.ProcessEditorDblClick = function (e) {
    var editor, result;
    var event = e || window.event;
    var mouseinfo = TableEditorSC.EditorMouseInfo;
    var ele = event.target || event.srcElement;
    var mobj;
    if (mouseinfo.ignore) return;
    for (mobj = null; !mobj && ele; ele = ele.parentNode) {
      mobj = TableEditorSC.LookupElement(ele, mouseinfo.registeredElements);
    }
    if (!mobj) {
      mouseinfo.editor = null;
      return;
    }
    editor = mobj.editor;
    var pos = TableEditorSC.GetElementPositionWithScroll(editor.toplevel);
    var clientX = event.clientX - pos.left;
    var clientY = event.clientY - pos.top;
    result = TableEditorSC.GridMousePosition(editor, clientX, clientY);
    if (!result || !result.coord) return;
    mouseinfo.editor = editor;
    mouseinfo.element = ele;
    switch (editor.state) {
      case 'start':
        TableEditorSC.EditorOpenCellEdit(editor);
        break;
      case 'input':
        break;
      default:
        break;
    }
    TableEditorSC.StopPropagation(event);
    return;
  };
  TableEditorSC.EditorOpenCellEdit = function (editor) {
    var wval;
    if (!editor.ecell) return true;
    if (!editor.inputBox) return true;
    if (editor.inputBox.element.disabled) return true;
    editor.inputBox.ShowInputBox(true);
    editor.inputBox.Focus();
    editor.inputBox.SetText('');
    editor.inputBox.DisplayCellContents();
    editor.inputBox.Select('end');
    wval = editor.workingvalues;
    wval.partialexpr = '';
    wval.ecoord = editor.ecell.coord;
    wval.erow = editor.ecell.row;
    wval.ecol = editor.ecell.col;
    return;
  };
  TableEditorSC.EditorProcessKey = function (editor, ch, e) {
    var result, coord, inputtext, f;
    var wval = editor.workingvalues;
    var range = editor.range;
    if (typeof ch != 'string') ch = '';
    switch (editor.state) {
      case 'start':
        if (e.shiftKey && ch.substr(0, 2) == '[a') {
          ch = ch + 'shifted';
        }
        if (ch == '[enter]') ch = '[adown]';
        if (ch == '[tab]') ch = e.shiftKey ? '[aleft]' : '[aright]';
        if (ch.substr(0, 2) == '[a' || ch.substr(0, 3) == '[pg' || ch == '[home]') {
          result = editor.MoveECellWithKey(ch);
          return !result;
        }
        if (ch == '[del]' || ch == '[backspace]') {
          if (!editor.noEdit && !editor.ECellReadonly()) {
            editor.EditorApplySetCommandsToRange('empty', '');
          }
          break;
        }
        if (ch == '[esc]') {
          if (range.hasrange) {
            editor.RangeRemove();
            editor.MoveECell(range.anchorcoord);
            for (f in editor.StatusCallback) {
              editor.StatusCallback[f].func(
                editor,
                'specialkey',
                ch,
                editor.StatusCallback[f].params,
              );
            }
          }
          return false;
        }
        if (ch == '[f2]') {
          if (editor.noEdit || editor.ECellReadonly()) return true;
          TableEditorSC.EditorOpenCellEdit(editor);
          editor.state = 'inputboxdirect';
          return false;
        }
        if ((ch.length > 1 && ch.substr(0, 1) == '[') || ch.length == 0) {
          if (editor.ctrlkeyFunction && ch.length > 0) {
            return editor.ctrlkeyFunction(editor, ch);
          } else {
            return true;
          }
        }
        if (!editor.ecell) return true;
        if (!editor.inputBox) return true;
        if (editor.ECellReadonly()) return true;
        editor.inputBox.element.disabled = false;
        editor.state = 'input';
        editor.inputBox.ShowInputBox(true);
        editor.inputBox.Focus();
        editor.inputBox.SetText(ch);
        editor.inputBox.Select('end');
        wval.partialexpr = '';
        wval.ecoord = editor.ecell.coord;
        wval.erow = editor.ecell.row;
        wval.ecol = editor.ecell.col;
        editor.RangeRemove();
        break;
      case 'input':
        inputtext = editor.inputBox.GetText();
        if (editor.inputBox.skipOne) return false;
        if (ch == '[esc]' || ch == '[enter]' || ch == '[tab]' || (ch && ch.substr(0, 2) == '[a')) {
          if (
            ('(+-*/,:!&<>=^'.indexOf(inputtext.slice(-1)) >= 0 && inputtext.slice(0, 1) == '=') ||
            inputtext == '='
          ) {
            wval.partialexpr = inputtext;
          }
          if (wval.partialexpr) {
            if (e.shiftKey && ch.substr(0, 2) == '[a') {
              ch = ch + 'shifted';
            }
            coord = editor.MoveECellWithKey(ch);
            if (coord) {
              if (range.hasrange) {
                editor.inputBox.SetText(
                  wval.partialexpr +
                    TableEditorSC.crToCoord(range.left, range.top) +
                    ':' +
                    TableEditorSC.crToCoord(range.right, range.bottom),
                );
              } else {
                editor.inputBox.SetText(wval.partialexpr + coord);
              }
              return false;
            }
          }
          editor.inputBox.Blur();
          editor.inputBox.ShowInputBox(false);
          editor.state = 'start';
          editor.cellhandles.ShowCellHandles(true);
          if (ch != '[esc]') {
            editor.EditorSaveEdit();
            if (editor.ecell.coord != wval.ecoord) {
              editor.MoveECell(wval.ecoord);
            }
            if (ch == '[enter]') ch = '[adown]';
            if (ch == '[tab]') ch = e.shiftKey ? '[aleft]' : '[aright]';
            if (ch.substr(0, 2) == '[a') {
              editor.MoveECellWithKey(ch);
            }
          } else {
            editor.inputBox.DisplayCellContents();
            editor.RangeRemove();
            editor.MoveECell(wval.ecoord);
          }
          break;
        }
        if (wval.partialexpr && ch == '[backspace]') {
          editor.inputBox.SetText(wval.partialexpr);
          wval.partialexpr = '';
          editor.RangeRemove();
          editor.MoveECell(wval.ecoord);
          editor.inputBox.ShowInputBox(true);
          return false;
        }
        if (ch == '[f2]') {
          editor.state = 'inputboxdirect';
          return false;
        }
        if (range.hasrange) editor.RangeRemove();
        editor.MoveECell(wval.ecoord);
        if (wval.partialexpr) {
          editor.inputBox.ShowInputBox(true);
          wval.partialexpr = '';
        }
        return true;
      case 'inputboxdirect':
        inputtext = editor.inputBox.GetText();
        if (ch == '[esc]' || ch == '[enter]' || ch == '[tab]') {
          editor.inputBox.Blur();
          editor.inputBox.ShowInputBox(false);
          editor.state = 'start';
          editor.cellhandles.ShowCellHandles(true);
          if (ch == '[esc]') {
            editor.inputBox.DisplayCellContents();
          } else {
            editor.EditorSaveEdit();
            if (editor.ecell.coord != wval.ecoord) {
              editor.MoveECell(wval.ecoord);
            }
            if (ch == '[enter]') ch = '[adown]';
            if (ch == '[tab]') ch = e.shiftKey ? '[aleft]' : '[aright]';
            if (ch.substr(0, 2) == '[a') {
              editor.MoveECellWithKey(ch);
            }
          }
          break;
        }
        if (ch == '[f2]') {
          editor.state = 'input';
          return false;
        }
        return true;
      case 'skip-and-start':
        editor.state = 'start';
        editor.cellhandles.ShowCellHandles(true);
        return false;
      default:
        return true;
    }
    return false;
  };
  TableEditorSC.EditorAddToInput = function (editor, str, prefix) {
    var wval = editor.workingvalues;
    if (editor.noEdit || editor.ECellReadonly()) return;
    switch (editor.state) {
      case 'start':
        editor.state = 'input';
        editor.inputBox.ShowInputBox(true);
        editor.inputBox.element.disabled = false;
        editor.inputBox.Focus();
        editor.inputBox.SetText((prefix || '') + str);
        editor.inputBox.Select('end');
        wval.partialexpr = '';
        wval.ecoord = editor.ecell.coord;
        wval.erow = editor.ecell.row;
        wval.ecol = editor.ecell.col;
        editor.RangeRemove();
        break;
      case 'input':
      case 'inputboxdirect':
        editor.inputBox.element.focus();
        if (wval.partialexpr) {
          editor.inputBox.SetText(wval.partialexpr);
          wval.partialexpr = '';
          editor.RangeRemove();
          editor.MoveECell(wval.ecoord);
        }
        editor.inputBox.SetText(editor.inputBox.GetText() + str);
        break;
      default:
        break;
    }
  };
  TableEditorSC.EditorDisplayCellContents = function (editor) {
    if (editor.inputBox) editor.inputBox.DisplayCellContents();
  };
  TableEditorSC.EditorSaveEdit = function (editor, text) {
    var valueinfo, fch, type, value, oldvalue, cmdline;
    var sheetobj = editor.context.sheetobj;
    var wval = editor.workingvalues;
    type = 'text t';
    value = typeof text == 'string' ? text : editor.inputBox.GetText();
    oldvalue = TableEditorSC.GetCellContents(sheetobj, wval.ecoord) + '';
    if (value == oldvalue) {
      return;
    }
    fch = value.charAt(0);
    if (fch == '=' && value.indexOf('\n') == -1) {
      type = 'formula';
      value = value.substring(1);
    } else if (fch == "'") {
      type = 'text t';
      value = value.substring(1);
      valueinfo = TableEditorSC.DetermineValueType(value);
      if (valueinfo.type.charAt(0) == 't') {
        type = 'text ' + valueinfo.type;
      }
    } else if (value.length == 0) {
      type = 'empty';
    } else {
      valueinfo = TableEditorSC.DetermineValueType(value);
      if (valueinfo.type == 'n' && value == valueinfo.value + '') {
        type = 'value n';
      } else if (valueinfo.type.charAt(0) == 't') {
        type = 'text ' + valueinfo.type;
      } else {
        type = 'constant ' + valueinfo.type + ' ' + valueinfo.value;
      }
    }
    if (type.charAt(0) == 't') {
      value = TableEditorSC.encodeForSave(value);
    }
    cmdline = 'set ' + wval.ecoord + ' ' + type + ' ' + value;
    editor.EditorScheduleSheetCommands(cmdline, true, false);
    if (typeof sheetobj.ioEventTree === 'undefined') return;
    if (typeof sheetobj.ioParameterList === 'undefined') return;
    if (typeof sheetobj.ioEventTree[wval.ecoord] !== 'undefined') {
      TableEditorSC.EditedTriggerCell(
        sheetobj.ioEventTree[wval.ecoord],
        wval.ecoord,
        editor,
        sheetobj,
      );
    }
    return;
  };
  TableEditorSC.EditedTriggerCell = function (actionFormulaCells, editedCellRef, editor, sheet) {
    for (var actionCellId in actionFormulaCells) {
      var parameters = sheet.ioParameterList[actionCellId];
      if (typeof parameters === 'undefined') continue;
      switch (parameters.function_name) {
        case 'EMAILONEDIT':
        case 'EMAILONEDITIF':
          var cmdline = 'setemailparameters ' + actionCellId + ' ' + editedCellRef;
          editor.deferredEmailCommands.push({
            cmdstr: cmdline,
            saveundo: false,
          });
          break;
      }
    }
  };
  TableEditorSC.EditorApplySetCommandsToRange = function (editor, cmd) {
    var line, coord;
    var ecell = editor.ecell;
    var range = editor.range;
    if (range.hasrange) {
      coord =
        TableEditorSC.crToCoord(range.left, range.top) +
        ':' +
        TableEditorSC.crToCoord(range.right, range.bottom);
      line = 'set ' + coord + ' ' + cmd;
      editor.EditorScheduleSheetCommands(line, true, false);
    } else {
      line = 'set ' + ecell.coord + ' ' + cmd;
      editor.EditorScheduleSheetCommands(line, true, false);
    }
    editor.DisplayCellContents();
  };
  TableEditorSC.EditorProcessMouseWheel = function (event, delta, mousewheelinfo, wobj) {
    if (wobj.functionobj.editor.busy) return;
    if (delta > 0) {
      wobj.functionobj.editor.ScrollRelative(true, Math.floor(-delta * 1.5));
    }
    if (delta < 0) {
      wobj.functionobj.editor.ScrollRelative(true, Math.ceil(-delta * 1.5));
    }
  };
  TableEditorSC.GridMousePosition = function (editor, clientX, clientY) {
    var row, rowpane, col, colpane, rowtoresize, coltoresize, unhide, pos;
    var result = {};
    for (row = 1; row < editor.rowpositions.length; row++) {
      if (!editor.rowheight[row]) continue;
      if (editor.rowpositions[row] + editor.rowheight[row] > clientY) {
        break;
      }
    }
    for (col = 1; col < editor.colpositions.length; col++) {
      if (!editor.colwidth[col]) continue;
      if (editor.colpositions[col] + editor.colwidth[col] > clientX) {
        break;
      }
    }
    result.row = row;
    result.col = col;
    if (editor.headposition && TableEditorSC._app != true) {
      if (clientX < editor.headposition.left && clientX >= editor.gridposition.left) {
        result.rowheader = true;
        result.distance = editor.headposition.left - clientX;
        result.rowtoresize = false;
        result.rowselect = false;
        for (rowtoresize = 1; rowtoresize < editor.rowpositions.length; rowtoresize++) {
          if (!editor.rowheight[rowtoresize]) continue;
          if (
            editor.rowpositions[rowtoresize] + editor.rowheight[rowtoresize] - 3 <= clientY &&
            editor.rowpositions[rowtoresize] + editor.rowheight[rowtoresize] + 3 >= clientY
          ) {
            result.rowtoresize = rowtoresize;
            break;
          }
        }
        if ((unhide = editor.context.rowunhidetop[row])) {
          pos = TableEditorSC.GetElementPosition(unhide);
          if (
            clientX >= pos.left &&
            clientX < pos.left + unhide.offsetWidth &&
            clientY >= editor.rowpositions[row] + editor.rowheight[row] - unhide.offsetHeight &&
            clientY < editor.rowpositions[row] + editor.rowheight[row]
          ) {
            result.rowtounhide = row + 1;
          }
        }
        if ((unhide = editor.context.rowunhidebottom[row])) {
          pos = TableEditorSC.GetElementPosition(unhide);
          if (
            clientX >= pos.left &&
            clientX < pos.left + unhide.offsetWidth &&
            clientY >= editor.rowpositions[row] &&
            clientY < editor.rowpositions[row] + unhide.offsetHeight
          ) {
            result.rowtounhide = row - 1;
          }
        }
        if (result.rowtounhide == null) {
          for (rowpane = 0; rowpane < editor.context.rowpanes.length; rowpane++) {
            if (
              result.rowtoresize >= editor.context.rowpanes[rowpane].first &&
              result.rowtoresize <= editor.context.rowpanes[rowpane].last
            ) {
              return result;
            }
          }
          result.rowselect = true;
        }
        delete result.rowtoresize;
        return result;
      } else if (clientY < editor.headposition.top && clientY > editor.gridposition.top) {
        result.colheader = true;
        result.distance = editor.headposition.top - clientY;
        result.coltoresize = false;
        result.colselect = false;
        for (coltoresize = 1; coltoresize < editor.colpositions.length; coltoresize++) {
          if (!editor.colwidth[coltoresize]) continue;
          if (
            editor.colpositions[coltoresize] + editor.colwidth[coltoresize] - 3 <= clientX &&
            editor.colpositions[coltoresize] + editor.colwidth[coltoresize] + 3 >= clientX
          ) {
            result.coltoresize = coltoresize;
            break;
          }
        }
        if ((unhide = editor.context.colunhideleft[col])) {
          pos = TableEditorSC.GetElementPosition(unhide);
          if (
            clientX >= pos.left &&
            clientX < pos.left + unhide.offsetWidth &&
            clientY >= pos.top &&
            clientY < pos.top + unhide.offsetHeight
          ) {
            result.coltounhide = col + 1;
          }
        }
        if ((unhide = editor.context.colunhideright[col])) {
          pos = TableEditorSC.GetElementPosition(unhide);
          if (
            clientX >= pos.left &&
            clientX < pos.left + unhide.offsetWidth &&
            clientY >= pos.top &&
            clientY < pos.top + unhide.offsetHeight
          ) {
            result.coltounhide = col - 1;
          }
        }
        if (result.coltounhide == null) {
          for (colpane = 0; colpane < editor.context.colpanes.length; colpane++) {
            if (
              result.coltoresize >= editor.context.colpanes[colpane].first &&
              result.coltoresize <= editor.context.colpanes[colpane].last
            ) {
              return result;
            }
          }
          result.colselect = true;
        }
        delete result.coltoresize;
        return result;
      } else if (clientX >= editor.verticaltablecontrol.controlborder) {
        result.rowfooter = true;
        result.distance = clientX - editor.verticaltablecontrol.controlborder;
        return result;
      } else if (clientY >= editor.horizontaltablecontrol.controlborder) {
        result.colfooter = true;
        result.distance = clientY - editor.horizontaltablecontrol.controlborder;
        return result;
      } else if (clientX < editor.gridposition.left) {
        result.rowheader = true;
        result.distance = editor.headposition.left - clientX;
        return result;
      } else if (clientY <= editor.gridposition.top) {
        result.colheader = true;
        result.distance = editor.headposition.top - clientY;
        return result;
      } else {
        result.coord = TableEditorSC.crToCoord(result.col, result.row);
        if (editor.context.cellskip[result.coord]) {
          result.coord = editor.context.cellskip[result.coord];
        }
        return result;
      }
    }
    return null;
  };
  TableEditorSC.GetEditorCellElement = function (editor, row, col) {
    var headerColOffset = 0;
    var headerRowOffset = 0;
    if (editor.context.showRCHeaders == false) {
      var headerColOffset = -1;
      var headerRowOffset = -1;
    }
    var rowpane, colpane, c, coord;
    var rowindex = 0;
    var colindex = 0;
    for (rowpane = 0; rowpane < editor.context.rowpanes.length; rowpane++) {
      if (
        row >= editor.context.rowpanes[rowpane].first &&
        row <= editor.context.rowpanes[rowpane].last
      ) {
        for (colpane = 0; colpane < editor.context.colpanes.length; colpane++) {
          if (
            col >= editor.context.colpanes[colpane].first &&
            col <= editor.context.colpanes[colpane].last
          ) {
            rowindex += row - editor.context.rowpanes[rowpane].first + 2;
            for (c = editor.context.colpanes[colpane].first; c <= col; c++) {
              coord = editor.context.cellskip[TableEditorSC.crToCoord(c, row)];
              if (!coord || !editor.context.CoordInPane(coord, rowpane, colpane)) colindex++;
            }
            return {
              element:
                editor.griddiv.firstChild.lastChild.childNodes[rowindex + headerRowOffset]
                  .childNodes[colindex + headerColOffset],
              rowpane,
              colpane,
            };
          }
          for (
            c = editor.context.colpanes[colpane].first;
            c <= editor.context.colpanes[colpane].last;
            c++
          ) {
            coord = editor.context.cellskip[TableEditorSC.crToCoord(c, row)];
            if (!coord || !editor.context.CoordInPane(coord, rowpane, colpane)) colindex++;
          }
          colindex += 1;
        }
      }
      rowindex +=
        editor.context.rowpanes[rowpane].last - editor.context.rowpanes[rowpane].first + 1 + 1;
    }
    return null;
  };
  TableEditorSC.MoveECellWithKey = function (editor, ch) {
    var coord, row, col, cell;
    var shifted = false;
    var delta = 1;
    if (!editor.ecell) {
      return null;
    }
    if (ch.slice(-7) == 'shifted') {
      ch = ch.slice(0, -7);
      shifted = true;
    }
    row = editor.ecell.row;
    col = editor.ecell.col;
    cell = editor.context.sheetobj.cells[editor.ecell.coord];
    switch (ch) {
      case '[adown]':
        row += (cell && cell.rowspan) || 1;
        break;
      case '[aup]':
        row--;
        delta = -1;
        break;
      case '[pgdn]':
        if (editor.lastvisiblerow && editor.firstscrollingrow) {
          row += editor.lastvisiblerow - editor.firstscrollingrow + ((cell && cell.rowspan) || 1);
        } else {
          row += editor.pageUpDnAmount - 1 + ((cell && cell.rowspan) || 1);
        }
        break;
      case '[pgup]':
        if (editor.lastvisiblerow && editor.firstscrollingrow) {
          row -= editor.lastvisiblerow - editor.firstscrollingrow + 1;
        } else {
          row -= editor.pageUpDnAmount;
        }
        delta = -1;
        break;
      case '[aright]':
        col += (cell && cell.colspan) || 1;
        break;
      case '[aleft]':
        col--;
        delta = -1;
        break;
      case '[home]':
        row = 1;
        col = 1;
        break;
      default:
        return null;
    }
    if (editor.context.sheetobj.attribs.usermaxcol)
      col = Math.min(editor.context.sheetobj.attribs.usermaxcol, col);
    if (editor.context.sheetobj.attribs.usermaxrow)
      row = Math.min(editor.context.sheetobj.attribs.usermaxrow, row);
    while (editor.context.sheetobj.colattribs.hide[TableEditorSC.rcColname(col)] == 'yes') {
      col += delta;
      if (col < 1) {
        delta = -delta;
        col = 1;
      }
    }
    while (editor.context.sheetobj.rowattribs.hide[row] == 'yes') {
      row += delta;
      if (row < 1) {
        delta = -delta;
        row = 1;
      }
    }
    if (!editor.range.hasrange) {
      if (shifted) editor.RangeAnchor();
    }
    coord = editor.MoveECell(TableEditorSC.crToCoord(col, row));
    if (editor.range.hasrange) {
      if (shifted) editor.RangeExtend();
      else editor.RangeRemove();
    }
    return coord;
  };
  TableEditorSC.MoveECell = function (editor, newcell) {
    var cell, f;
    var highlights = editor.context.highlights;
    var ecell = TableEditorSC.coordToCr(newcell);
    if (
      editor.context.sheetobj.attribs.usermaxcol &&
      ecell.col > editor.context.sheetobj.attribs.usermaxcol
    )
      ecell.col = editor.context.sheetobj.attribs.usermaxcol;
    if (
      editor.context.sheetobj.attribs.usermaxrow &&
      ecell.row > editor.context.sheetobj.attribs.usermaxrow
    )
      ecell.row = editor.context.sheetobj.attribs.usermaxrow;
    newcell = TableEditorSC.crToCoord(ecell.col, ecell.row);
    if (editor.ecell) {
      if (editor.ecell.coord == newcell) return newcell;
      cell = TableEditorSC.GetEditorCellElement(editor, editor.ecell.row, editor.ecell.col);
      delete highlights[editor.ecell.coord];
      if (
        editor.range2.hasrange &&
        editor.ecell.row >= editor.range2.top &&
        editor.ecell.row <= editor.range2.bottom &&
        editor.ecell.col >= editor.range2.left &&
        editor.ecell.col <= editor.range2.right
      ) {
        highlights[editor.ecell.coord] = 'range2';
      }
      editor.UpdateCellCSS(cell, editor.ecell.row, editor.ecell.col);
      editor.SetECellHeaders('');
      if (editor.cellhandles) editor.cellhandles.ShowCellHandles(false);
    }
    newcell = editor.context.cellskip[newcell] || newcell;
    editor.ecell = TableEditorSC.coordToCr(newcell);
    editor.ecell.coord = newcell;
    cell = TableEditorSC.GetEditorCellElement(editor, editor.ecell.row, editor.ecell.col);
    highlights[newcell] = 'cursor';
    for (f in editor.MoveECellCallback) {
      editor.MoveECellCallback[f](editor);
    }
    editor.UpdateCellCSS(cell, editor.ecell.row, editor.ecell.col);
    editor.SetECellHeaders('selected');
    for (f in editor.StatusCallback) {
      editor.StatusCallback[f].func(editor, 'moveecell', newcell, editor.StatusCallback[f].params);
    }
    if (editor.busy) {
      editor.ensureecell = true;
    } else {
      editor.ensureecell = false;
      editor.EnsureECellVisible();
    }
    return newcell;
  };
  TableEditorSC.EnsureECellVisible = function (editor) {
    var vamount = 0;
    var hamount = 0;
    if (editor.ecell.row > editor.lastnonscrollingrow) {
      if (editor.ecell.row < editor.firstscrollingrow) {
        vamount =
          editor.ecell.row -
          editor.firstscrollingrow -
          Math.floor((editor.lastvisiblerow - editor.firstscrollingrow) / 2);
      } else if (editor.ecell.row + 1 > editor.lastvisiblerow) {
        vamount =
          editor.ecell.row -
          editor.lastvisiblerow +
          Math.floor((editor.lastvisiblerow - editor.firstscrollingrow) / 2);
      }
    }
    if (editor.ecell.col > editor.lastnonscrollingcol) {
      if (editor.ecell.col < editor.firstscrollingcol) {
        hamount =
          editor.ecell.col -
          editor.firstscrollingcol -
          Math.floor((editor.lastvisiblecol - editor.firstscrollingcol) / 2);
      } else if (editor.ecell.col + 1 > editor.lastvisiblecol) {
        hamount =
          editor.ecell.col -
          editor.lastvisiblecol +
          Math.floor((editor.lastvisiblecol - editor.firstscrollingcol) / 2);
      }
    }
    if (vamount != 0 || hamount != 0) {
      editor.ScrollRelativeBoth(vamount, hamount);
    } else {
      editor.cellhandles.ShowCellHandles(true);
    }
  };
  TableEditorSC.ReplaceCell = function (editor, cell, row, col) {
    var newelement, a;
    if (!cell) return;
    newelement = editor.context.RenderCell(row, col, cell.rowpane, cell.colpane, true, null);
    if (newelement && cell.element) {
      cell.element.innerHTML = newelement.innerHTML;
      cell.element.style.cssText = '';
      cell.element.className = newelement.className;
      for (a in newelement.style) {
        if (newelement.style[a] != 'cssText') cell.element.style[a] = newelement.style[a];
      }
    }
  };
  TableEditorSC.UpdateCellCSS = function (editor, cell, row, col) {
    var newelement, a;
    if (!cell) return;
    newelement = editor.context.RenderCell(row, col, cell.rowpane, cell.colpane, true, null);
    if (newelement) {
      cell.element.style.cssText = '';
      cell.element.className = newelement.className;
      for (a in newelement.style) {
        if (newelement.style[a] != 'cssText') cell.element.style[a] = newelement.style[a];
      }
    }
  };
  TableEditorSC.SetECellHeaders = function (editor, selected) {
    if (editor.context.showRCHeaders === false) return;
    var ecell = editor.ecell;
    var context = editor.context;
    var rowpane, colpane, first, last;
    var rowindex = 0;
    var colindex = 0;
    var headercell;
    if (!ecell) return;
    while (context.sheetobj.colattribs.hide[TableEditorSC.rcColname(ecell.col)] == 'yes') {
      ecell.col++;
    }
    while (context.sheetobj.rowattribs.hide[ecell.row] == 'yes') {
      ecell.row++;
    }
    ecell.coord = TableEditorSC.crToCoord(ecell.col, ecell.row);
    for (rowpane = 0; rowpane < context.rowpanes.length; rowpane++) {
      first = context.rowpanes[rowpane].first;
      last = context.rowpanes[rowpane].last;
      if (ecell.row >= first && ecell.row <= last) {
        var i = 2 + rowindex + ecell.row - first;
        if (editor.fullgrid !== null && i >= 0) {
          headercell = editor.fullgrid.childNodes[1].childNodes[i].childNodes[0];
          if (headercell) {
            if (context.classnames) headercell.className = context.classnames[selected + 'rowname'];
            if (context.explicitStyles)
              headercell.style.cssText = context.explicitStyles[selected + 'rowname'];
            headercell.style.verticalAlign = 'top';
          }
        }
      }
      rowindex += last - first + 1 + 1;
    }
    for (colpane = 0; colpane < context.colpanes.length; colpane++) {
      first = context.colpanes[colpane].first;
      last = context.colpanes[colpane].last;
      if (ecell.col >= first && ecell.col <= last) {
        var i = 1 + colindex + ecell.col - first;
        if (editor.fullgrid !== null && i >= 0) {
          headercell = editor.fullgrid.childNodes[1].childNodes[1].childNodes[i];
          if (headercell) {
            if (context.classnames) headercell.className = context.classnames[selected + 'colname'];
            if (context.explicitStyles)
              headercell.style.cssText = context.explicitStyles[selected + 'colname'];
          }
        }
      }
      colindex += last - first + 1 + 1;
    }
  };
  TableEditorSC.ECellReadonly = function (editor, ecoord) {
    if (!ecoord && editor.ecell) {
      ecoord = editor.ecell.coord;
    }
    if (!ecoord) return false;
    var cell = editor.context.sheetobj.cells[ecoord];
    return cell && cell.readonly;
  };
  TableEditorSC.RangeAnchor = function (editor, ecoord) {
    if (editor.range.hasrange) {
      editor.RangeRemove();
    }
    editor.RangeExtend(ecoord);
  };
  TableEditorSC.RangeExtend = function (editor, ecoord) {
    var cell, cr, coord, row, col, f;
    var highlights = editor.context.highlights;
    var range = editor.range;
    var range2 = editor.range2;
    var ecell;
    if (ecoord) {
      ecell = TableEditorSC.coordToCr(ecoord);
      ecell.coord = ecoord;
    } else ecell = editor.ecell;
    if (!ecell) return;
    if (!range.hasrange) {
      range.anchorcoord = ecell.coord;
      range.anchorrow = ecell.row;
      range.top = ecell.row;
      range.bottom = ecell.row;
      range.anchorcol = ecell.col;
      range.left = ecell.col;
      range.right = ecell.col;
      range.hasrange = true;
    }
    if (range.anchorrow < ecell.row) {
      range.top = range.anchorrow;
      range.bottom = ecell.row;
    } else {
      range.top = ecell.row;
      range.bottom = range.anchorrow;
    }
    if (range.anchorcol < ecell.col) {
      range.left = range.anchorcol;
      range.right = ecell.col;
    } else {
      range.left = ecell.col;
      range.right = range.anchorcol;
    }
    for (coord in highlights) {
      switch (highlights[coord]) {
        case 'range':
          highlights[coord] = 'unrange';
          break;
        case 'range2':
          highlights[coord] = 'unrange2';
          break;
      }
    }
    for (row = range.top; row <= range.bottom; row++) {
      for (col = range.left; col <= range.right; col++) {
        coord = TableEditorSC.crToCoord(col, row);
        switch (highlights[coord]) {
          case 'unrange':
            highlights[coord] = 'range';
            break;
          case 'cursor':
            break;
          case 'unrange2':
          default:
            highlights[coord] = 'newrange';
            break;
        }
      }
    }
    for (row = range2.top; range2.hasrange && row <= range2.bottom; row++) {
      for (col = range2.left; col <= range2.right; col++) {
        coord = TableEditorSC.crToCoord(col, row);
        switch (highlights[coord]) {
          case 'unrange2':
            highlights[coord] = 'range2';
            break;
          case 'range':
          case 'newrange':
          case 'cursor':
            break;
          default:
            highlights[coord] = 'newrange2';
            break;
        }
      }
    }
    for (coord in highlights) {
      switch (highlights[coord]) {
        case 'unrange':
          delete highlights[coord];
          break;
        case 'newrange':
          highlights[coord] = 'range';
          break;
        case 'newrange2':
          highlights[coord] = 'range2';
          break;
        case 'range':
        case 'range2':
        case 'cursor':
          continue;
      }
      cr = TableEditorSC.coordToCr(coord);
      cell = TableEditorSC.GetEditorCellElement(editor, cr.row, cr.col);
      editor.UpdateCellCSS(cell, cr.row, cr.col);
    }
    for (f in editor.RangeChangeCallback) {
      editor.RangeChangeCallback[f](editor);
    }
    coord = TableEditorSC.crToCoord(editor.range.left, editor.range.top);
    if (editor.range.left != editor.range.right || editor.range.top != editor.range.bottom) {
      coord += ':' + TableEditorSC.crToCoord(editor.range.right, editor.range.bottom);
    }
    for (f in editor.StatusCallback) {
      editor.StatusCallback[f].func(editor, 'rangechange', coord, editor.StatusCallback[f].params);
    }
    return;
  };
  TableEditorSC.RangeRemove = function (editor) {
    var cell, cr, coord, row, col, f;
    var highlights = editor.context.highlights;
    var range = editor.range;
    var range2 = editor.range2;
    if (!range.hasrange && !range2.hasrange) return;
    for (row = range2.top; range2.hasrange && row <= range2.bottom; row++) {
      for (col = range2.left; col <= range2.right; col++) {
        coord = TableEditorSC.crToCoord(col, row);
        switch (highlights[coord]) {
          case 'range':
            highlights[coord] = 'newrange2';
            break;
          case 'range2':
          case 'cursor':
            break;
          default:
            highlights[coord] = 'newrange2';
            break;
        }
      }
    }
    for (coord in highlights) {
      switch (highlights[coord]) {
        case 'range':
          delete highlights[coord];
          break;
        case 'newrange2':
          highlights[coord] = 'range2';
          break;
        case 'cursor':
          continue;
      }
      cr = TableEditorSC.coordToCr(coord);
      cell = TableEditorSC.GetEditorCellElement(editor, cr.row, cr.col);
      editor.UpdateCellCSS(cell, cr.row, cr.col);
    }
    range.hasrange = false;
    for (f in editor.RangeChangeCallback) {
      editor.RangeChangeCallback[f](editor);
    }
    for (f in editor.StatusCallback) {
      editor.StatusCallback[f].func(editor, 'rangechange', '', editor.StatusCallback[f].params);
    }
    return;
  };
  TableEditorSC.Range2Remove = function (editor) {
    var cell, cr, coord;
    var highlights = editor.context.highlights;
    var range2 = editor.range2;
    if (!range2.hasrange) return;
    for (coord in highlights) {
      switch (highlights[coord]) {
        case 'range2':
          delete highlights[coord];
          break;
        case 'range':
        case 'cursor':
          continue;
      }
      cr = TableEditorSC.coordToCr(coord);
      cell = TableEditorSC.GetEditorCellElement(editor, cr.row, cr.col);
      editor.UpdateCellCSS(cell, cr.row, cr.col);
    }
    range2.hasrange = false;
    return;
  };
  TableEditorSC.FitToEditTable = function (editor) {
    var colnum, colname, colwidth, totalwidth, totalrows, rownum, rowpane, needed, colpane;
    var context = editor.context;
    var sheetobj = context.sheetobj;
    totalwidth = context.showRCHeaders ? context.rownamewidth - 0 : 0;
    for (colpane = 0; colpane < context.colpanes.length - 1; colpane++) {
      for (
        colnum = context.colpanes[colpane].first;
        colnum <= context.colpanes[colpane].last;
        colnum++
      ) {
        colname = TableEditorSC.rcColname(colnum);
        if (sheetobj.colattribs.hide[colname] != 'yes') {
          colwidth =
            sheetobj.colattribs.width[colname] ||
            sheetobj.attribs.defaultcolwidth ||
            TableEditorSC.Constants.defaultColWidth;
          if (colwidth == 'blank' || colwidth == 'auto') colwidth = '';
          totalwidth += colwidth && colwidth - 0 > 0 ? colwidth - 0 : 10;
        }
      }
    }
    for (colnum = context.colpanes[colpane].first; colnum <= 1e4; colnum++) {
      colname = TableEditorSC.rcColname(colnum);
      if (sheetobj.colattribs.hide[colname] != 'yes') {
        colwidth =
          sheetobj.colattribs.width[colname] ||
          sheetobj.attribs.defaultcolwidth ||
          TableEditorSC.Constants.defaultColWidth;
        if (colwidth == 'blank' || colwidth == 'auto') colwidth = '';
        totalwidth += colwidth && colwidth - 0 > 0 ? colwidth - 0 : 10;
      }
      if (totalwidth > editor.tablewidth) break;
    }
    context.colpanes[colpane].last = context.sheetobj.attribs.usermaxcol || colnum;
    var firstRow = context.rowpanes[0].first;
    var lastRow = context.sheetobj.attribs.lastrow;
    while (sheetobj.rowattribs.hide[firstRow] == 'yes' && firstRow < lastRow) firstRow++;
    context.rowpanes[0].first = firstRow;
    totalrows = context.showRCHeaders ? 1 : 0;
    for (rowpane = 0; rowpane < context.rowpanes.length - 1; rowpane++) {
      totalrows += context.rowpanes[rowpane].last - context.rowpanes[rowpane].first + 1;
      for (
        rownum = context.rowpanes[rowpane].first;
        rownum <= context.rowpanes[rowpane].last;
        rownum++
      ) {
        if (sheetobj.rowattribs.hide[rownum] == 'yes') {
          totalrows--;
        }
      }
    }
    needed = editor.tableheight - totalrows * context.pixelsPerRow;
    context.rowpanes[rowpane].last =
      context.sheetobj.attribs.usermaxrow ||
      context.rowpanes[rowpane].first + Math.floor(needed / context.pixelsPerRow) + 1;
  };
  TableEditorSC.CalculateEditorPositions = function (editor) {
    var rowpane, colpane, i;
    editor.gridposition = TableEditorSC.GetElementPosition(editor.griddiv);
    var element = editor.griddiv.firstChild.lastChild.childNodes[1].childNodes[0];
    editor.headposition = TableEditorSC.GetElementPosition(element);
    editor.headposition.left += element.offsetWidth;
    editor.headposition.top += element.offsetHeight;
    editor.rowpositions = [];
    for (rowpane = 0; rowpane < editor.context.rowpanes.length; rowpane++) {
      editor.CalculateRowPositions(rowpane, editor.rowpositions, editor.rowheight);
    }
    for (i = 0; i < editor.rowpositions.length; i++) {
      if (editor.rowpositions[i] > editor.gridposition.top + editor.tableheight) break;
    }
    editor.lastvisiblerow = i - 1;
    editor.colpositions = [];
    for (colpane = 0; colpane < editor.context.colpanes.length; colpane++) {
      editor.CalculateColPositions(colpane, editor.colpositions, editor.colwidth);
    }
    for (i = 0; i < editor.colpositions.length; i++) {
      if (editor.colpositions[i] > editor.gridposition.left + editor.tablewidth) break;
    }
    editor.lastvisiblecol = i - 1;
    editor.firstscrollingrow = editor.context.rowpanes[editor.context.rowpanes.length - 1].first;
    while (editor.context.sheetobj.rowattribs.hide[editor.firstscrollingrow] == 'yes') {
      editor.firstscrollingrow++;
    }
    editor.firstscrollingrowtop =
      editor.rowpositions[editor.firstscrollingrow] || editor.headposition.top;
    editor.lastnonscrollingrow =
      editor.context.rowpanes.length - 1 > 0
        ? editor.context.rowpanes[editor.context.rowpanes.length - 2].last
        : 0;
    editor.firstscrollingcol = editor.context.colpanes[editor.context.colpanes.length - 1].first;
    while (
      editor.context.sheetobj.colattribs.hide[TableEditorSC.rcColname(editor.firstscrollingcol)] ==
      'yes'
    ) {
      editor.firstscrollingcol++;
    }
    editor.firstscrollingcolleft =
      editor.colpositions[editor.firstscrollingcol] || editor.headposition.left;
    editor.lastnonscrollingcol =
      editor.context.colpanes.length - 1 > 0
        ? editor.context.colpanes[editor.context.colpanes.length - 2].last
        : 0;
    editor.verticaltablecontrol.ComputeTableControlPositions();
    editor.horizontaltablecontrol.ComputeTableControlPositions();
  };
  TableEditorSC.ScheduleRender = function (editor) {
    if (editor.ignoreRender == true) return;
    if (editor.timeout) window.clearTimeout(editor.timeout);
    TableEditorSC.EditorSheetStatusCallback(null, 'schedrender', null, editor);
    editor.timeout = window.setTimeout(function () {
      TableEditorSC.DoRenderStep(editor);
    }, 1);
  };
  TableEditorSC.DoRenderStep = function (editor) {
    editor.timeout = null;
    editor.EditorRenderSheet();
    TableEditorSC.EditorSheetStatusCallback(null, 'renderdone', null, editor);
    TableEditorSC.EditorSheetStatusCallback(null, 'schedposcalc', null, editor);
    editor.timeout = window.setTimeout(function () {
      TableEditorSC.DoPositionCalculations(editor);
    }, 1);
  };
  TableEditorSC.SchedulePositionCalculations = function (editor) {
    TableEditorSC.EditorSheetStatusCallback(null, 'schedposcalc', null, editor);
    editor.timeout = window.setTimeout(function () {
      TableEditorSC.DoPositionCalculations(editor);
    }, 1);
  };
  TableEditorSC.DoPositionCalculations = function (editor) {
    editor.timeout = null;
    editor.CalculateEditorPositions();
    editor.verticaltablecontrol.PositionTableControlElements();
    editor.horizontaltablecontrol.PositionTableControlElements();
    TableEditorSC.EditorSheetStatusCallback(null, 'doneposcalc', null, editor);
    if (editor.ensureecell && editor.ecell && !editor.deferredCommands.length) {
      editor.ensureecell = false;
      editor.EnsureECellVisible();
    }
    editor.cellhandles.ShowCellHandles(true);
  };
  TableEditorSC.CalculateRowPositions = function (editor, panenum, positions, sizes) {
    var toprow, rowpane, rownum, offset, trowobj;
    var context = editor.context;
    var tbodyobj;
    if (!context.showRCHeaders) return;
    tbodyobj = editor.fullgrid.lastChild;
    toprow = 2;
    for (rowpane = 0; rowpane < panenum; rowpane++) {
      toprow += context.rowpanes[rowpane].last - context.rowpanes[rowpane].first + 2;
    }
    offset = 0;
    for (
      rownum = context.rowpanes[rowpane].first;
      rownum <= context.rowpanes[rowpane].last;
      rownum++
    ) {
      trowobj = tbodyobj.childNodes[toprow + offset];
      offset++;
      if (!trowobj) {
        continue;
      }
      if (!positions[rownum]) {
        positions[rownum] = trowobj.firstChild.offsetTop;
        sizes[rownum] = trowobj.firstChild.offsetHeight;
      }
    }
    return;
  };
  TableEditorSC.CalculateColPositions = function (editor, panenum, positions, sizes) {
    var leftcol, colpane, colnum, offset, trowobj, cellposition;
    var context = editor.context;
    var tbodyobj;
    if (!context.showRCHeaders) return;
    tbodyobj = editor.fullgrid.lastChild;
    leftcol = 1;
    for (colpane = 0; colpane < panenum; colpane++) {
      leftcol += context.colpanes[colpane].last - context.colpanes[colpane].first + 2;
    }
    trowobj = tbodyobj.childNodes[1];
    offset = 0;
    for (
      colnum = context.colpanes[colpane].first;
      colnum <= context.colpanes[colpane].last;
      colnum++
    ) {
      cellposition = TableEditorSC.GetElementPosition(trowobj.childNodes[leftcol + offset]);
      if (!positions[colnum]) {
        positions[colnum] = cellposition.left;
        if (trowobj.childNodes[leftcol + offset]) {
          sizes[colnum] = trowobj.childNodes[leftcol + offset].offsetWidth;
        }
      }
      offset++;
    }
    return;
  };
  TableEditorSC.ScrollRelative = function (editor, vertical, amount) {
    if (vertical) {
      editor.ScrollRelativeBoth(amount, 0);
    } else {
      editor.ScrollRelativeBoth(0, amount);
    }
    return;
  };
  TableEditorSC.ScrollRelativeBoth = function (editor, vamount, hamount) {
    var context = editor.context;
    var dv = vamount > 0 ? 1 : -1,
      dh = hamount > 0 ? 1 : -1;
    var vplen = context.rowpanes.length;
    var vlimit = vplen > 1 ? context.rowpanes[vplen - 2].last + 1 : 1;
    if (context.rowpanes[vplen - 1].first + vamount < vlimit) {
      vamount = -context.rowpanes[vplen - 1].first + vlimit;
    }
    var hplen = context.colpanes.length;
    var hlimit = hplen > 1 ? context.colpanes[hplen - 2].last + 1 : 1;
    if (context.colpanes[hplen - 1].first + hamount < hlimit) {
      hamount = -context.colpanes[hplen - 1].first + hlimit;
    }
    while (
      context.sheetobj.colattribs.hide[
        TableEditorSC.rcColname(context.colpanes[hplen - 1].first + hamount)
      ] == 'yes'
    ) {
      hamount += dh;
      if (hamount < 1) {
        hamount = 0;
        break;
      }
    }
    while (context.sheetobj.rowattribs.hide[context.rowpanes[vplen - 1].first + vamount] == 'yes') {
      vamount += dv;
      if (vamount < 1) {
        vamount = 0;
        break;
      }
    }
    if ((vamount == 1 || vamount == -1) && hamount == 0) {
      if (vamount == 1) {
        editor.ScrollTableUpOneRow();
      } else {
        editor.ScrollTableDownOneRow();
      }
      if (editor.ecell) editor.SetECellHeaders('selected');
      editor.SchedulePositionCalculations();
      return;
    }
    if (vamount != 0 || hamount != 0) {
      context.rowpanes[vplen - 1].first += vamount;
      context.rowpanes[vplen - 1].last += vamount;
      context.colpanes[hplen - 1].first += hamount;
      context.colpanes[hplen - 1].last += hamount;
      editor.LimitLastPanes();
      editor.FitToEditTable();
      editor.ScheduleRender();
    }
  };
  TableEditorSC.PageRelative = function (editor, vertical, direction) {
    var context = editor.context;
    var panes = vertical ? 'rowpanes' : 'colpanes';
    var lastpane = context[panes][context[panes].length - 1];
    var lastvisible = vertical ? 'lastvisiblerow' : 'lastvisiblecol';
    var sizearray = vertical ? editor.rowheight : editor.colwidth;
    var defaultsize = vertical
      ? TableEditorSC.Constants.defaultAssumedRowHeight
      : TableEditorSC.Constants.defaultColWidth;
    var size, newfirst, totalsize, current;
    if (direction > 0) {
      newfirst = editor[lastvisible];
      if (newfirst == lastpane.first) newfirst += 1;
    } else {
      if (vertical) {
        totalsize = editor.tableheight - (editor.firstscrollingrowtop - editor.gridposition.top);
      } else {
        totalsize = editor.tablewidth - (editor.firstscrollingcolleft - editor.gridposition.left);
      }
      totalsize -=
        sizearray[editor[lastvisible]] > 0 ? sizearray[editor[lastvisible]] : defaultsize;
      for (newfirst = lastpane.first - 1; newfirst > 0; newfirst--) {
        size = sizearray[newfirst] > 0 ? sizearray[newfirst] : defaultsize;
        if (totalsize < size) break;
        totalsize -= size;
      }
      current = lastpane.first;
      if (newfirst >= current) newfirst = current - 1;
      if (newfirst < 1) newfirst = 1;
    }
    lastpane.first = newfirst;
    lastpane.last = newfirst + 1;
    editor.LimitLastPanes();
    editor.FitToEditTable();
    editor.ScheduleRender();
  };
  TableEditorSC.LimitLastPanes = function (editor) {
    var context = editor.context;
    var plen;
    plen = context.rowpanes.length;
    if (plen > 1 && context.rowpanes[plen - 1].first <= context.rowpanes[plen - 2].last)
      context.rowpanes[plen - 1].first = context.rowpanes[plen - 2].last + 1;
    if (
      context.sheetobj.attribs.usermaxrow &&
      context.rowpanes[plen - 1].first > context.sheetobj.attribs.usermaxrow
    )
      context.rowpanes[plen - 1].first = context.sheetobj.attribs.usermaxrow;
    plen = context.colpanes.length;
    if (plen > 1 && context.colpanes[plen - 1].first <= context.colpanes[plen - 2].last)
      context.colpanes[plen - 1].first = context.colpanes[plen - 2].last + 1;
    if (
      context.sheetobj.attribs.usermaxcol &&
      context.colpanes[plen - 1].first > context.sheetobj.attribs.usermaxcol
    )
      context.colpanes[plen - 1].first = context.sheetobj.attribs.usermaxcol;
  };
  TableEditorSC.ScrollTableUpOneRow = function (editor) {
    var toprow,
      rowpane,
      rownum,
      colnum,
      colpane,
      cell,
      oldrownum,
      maxrowspan,
      newbottomrow,
      newrow,
      oldchild,
      bottomrownum,
      coord;
    var rowneedsrefresh = {};
    var context = editor.context;
    var sheetobj = context.sheetobj;
    var tableobj = editor.fullgrid;
    var tbodyobj;
    tbodyobj = tableobj.lastChild;
    toprow = context.showRCHeaders ? 2 : 1;
    for (rowpane = 0; rowpane < context.rowpanes.length - 1; rowpane++) {
      toprow += context.rowpanes[rowpane].last - context.rowpanes[rowpane].first + 2;
    }
    if (
      context.sheetobj.attribs.usermaxrow &&
      context.sheetobj.attribs.usermaxrow - context.rowpanes[rowpane].first < 1
    ) {
      return tableobj;
    }
    tbodyobj.removeChild(tbodyobj.childNodes[toprow]);
    context.rowpanes[rowpane].first++;
    context.rowpanes[rowpane].last++;
    editor.FitToEditTable();
    context.CalculateColWidthData();
    if (
      !context.sheetobj.attribs.usermaxrow ||
      context.rowpanes[rowpane].last != context.sheetobj.attribs.usermaxrow
    ) {
      newbottomrow = context.RenderRow(context.rowpanes[rowpane].last, rowpane);
      tbodyobj.appendChild(newbottomrow);
    }
    maxrowspan = 1;
    oldrownum = context.rowpanes[rowpane].first - 1;
    for (colpane = 0; colpane < context.colpanes.length; colpane++) {
      for (
        colnum = context.colpanes[colpane].first;
        colnum <= context.colpanes[colpane].last;
        colnum++
      ) {
        coord = TableEditorSC.crToCoord(colnum, oldrownum);
        if (context.cellskip[coord]) continue;
        cell = sheetobj.cells[coord];
        if (cell && cell.rowspan > maxrowspan) maxrowspan = cell.rowspan;
      }
    }
    if (maxrowspan > 1) {
      for (rownum = 1; rownum < maxrowspan; rownum++) {
        if (rownum + oldrownum >= context.rowpanes[rowpane].last) break;
        newrow = context.RenderRow(rownum + oldrownum, rowpane);
        oldchild = tbodyobj.childNodes[toprow + rownum - 1];
        tbodyobj.replaceChild(newrow, oldchild);
      }
    }
    bottomrownum = context.rowpanes[rowpane].last;
    for (colpane = 0; colpane < context.colpanes.length; colpane++) {
      for (
        colnum = context.colpanes[colpane].first;
        colnum <= context.colpanes[colpane].last;
        colnum++
      ) {
        coord = context.cellskip[TableEditorSC.crToCoord(colnum, bottomrownum)];
        if (!coord) continue;
        rownum = context.coordToCR[coord].row - 0;
        if (rownum == context.rowpanes[rowpane].last || rownum < context.rowpanes[rowpane].first)
          continue;
        cell = sheetobj.cells[coord];
        if (cell && cell.rowspan > 1) rowneedsrefresh[rownum] = true;
      }
    }
    for (rownum in rowneedsrefresh) {
      newrow = context.RenderRow(rownum, rowpane);
      oldchild = tbodyobj.childNodes[toprow + (Number(rownum) - context.rowpanes[rowpane].first)];
      tbodyobj.replaceChild(newrow, oldchild);
    }
    return tableobj;
  };
  TableEditorSC.ScrollTableDownOneRow = function (editor) {
    var toprow,
      rowpane,
      rownum,
      colnum,
      colpane,
      cell,
      newrownum,
      maxrowspan,
      newrow,
      oldchild,
      bottomrownum,
      coord;
    var rowneedsrefresh = {};
    var context = editor.context;
    var sheetobj = context.sheetobj;
    var tableobj = editor.fullgrid;
    var tbodyobj;
    tbodyobj = tableobj.lastChild;
    toprow = context.showRCHeaders ? 2 : 1;
    for (rowpane = 0; rowpane < context.rowpanes.length - 1; rowpane++) {
      toprow += context.rowpanes[rowpane].last - context.rowpanes[rowpane].first + 2;
    }
    if (!context.sheetobj.attribs.usermaxrow) {
      tbodyobj.removeChild(
        tbodyobj.childNodes[
          toprow + (context.rowpanes[rowpane].last - context.rowpanes[rowpane].first)
        ],
      );
    }
    context.rowpanes[rowpane].first--;
    context.rowpanes[rowpane].last--;
    editor.FitToEditTable();
    context.CalculateColWidthData();
    newrow = context.RenderRow(context.rowpanes[rowpane].first, rowpane);
    tbodyobj.insertBefore(newrow, tbodyobj.childNodes[toprow]);
    maxrowspan = 1;
    newrownum = context.rowpanes[rowpane].first;
    for (colpane = 0; colpane < context.colpanes.length; colpane++) {
      for (
        colnum = context.colpanes[colpane].first;
        colnum <= context.colpanes[colpane].last;
        colnum++
      ) {
        coord = TableEditorSC.crToCoord(colnum, newrownum);
        if (context.cellskip[coord]) continue;
        cell = sheetobj.cells[coord];
        if (cell && cell.rowspan > maxrowspan) maxrowspan = cell.rowspan;
      }
    }
    if (maxrowspan > 1) {
      for (rownum = 1; rownum < maxrowspan; rownum++) {
        if (rownum + newrownum > context.rowpanes[rowpane].last) break;
        newrow = context.RenderRow(rownum + newrownum, rowpane);
        oldchild = tbodyobj.childNodes[toprow + rownum];
        tbodyobj.replaceChild(newrow, oldchild);
      }
    }
    bottomrownum = context.rowpanes[rowpane].last;
    for (colpane = 0; colpane < context.colpanes.length; colpane++) {
      for (
        colnum = context.colpanes[colpane].first;
        colnum <= context.colpanes[colpane].last;
        colnum++
      ) {
        coord = TableEditorSC.crToCoord(colnum, bottomrownum);
        cell = sheetobj.cells[coord];
        if (cell && cell.rowspan > 1) {
          rowneedsrefresh[bottomrownum] = true;
          continue;
        }
        coord = context.cellskip[TableEditorSC.crToCoord(colnum, bottomrownum)];
        if (!coord) continue;
        rownum = context.coordToCR[coord].row - 0;
        if (rownum == bottomrownum || rownum < context.rowpanes[rowpane].first) continue;
        cell = sheetobj.cells[coord];
        if (cell && cell.rowspan > 1) rowneedsrefresh[rownum] = true;
      }
    }
    for (rownum in rowneedsrefresh) {
      newrow = context.RenderRow(rownum, rowpane);
      oldchild = tbodyobj.childNodes[toprow + (Number(rownum) - context.rowpanes[rowpane].first)];
      tbodyobj.replaceChild(newrow, oldchild);
    }
    return tableobj;
  };
  TableEditorSC.ScrollTableLeftOneCol = function (editor) {
    return TableEditorSC.ScrollRelative(editor, false, -1);
  };
  TableEditorSC.ScrollTableRightOneCol = function (editor) {
    return TableEditorSC.ScrollRelative(editor, false, 1);
  };
  TableEditorSC.InputBox = function (element, editor) {
    if (!element) return;
    this.element = element;
    this.editor = editor;
    this.inputEcho = null;
    editor.inputBox = this;
    element.onmousedown = TableEditorSC.InputBoxOnMouseDown;
    editor.MoveECellCallback.formulabar = function (e) {
      if (e.state != 'start') return;
      editor.inputBox.DisplayCellContents(e.ecell.coord);
    };
  };
  TableEditorSC.InputBox.prototype.DisplayCellContents = function (coord) {
    TableEditorSC.InputBoxDisplayCellContents(this, coord);
  };
  TableEditorSC.InputBox.prototype.ShowInputBox = function (show) {
    this.editor.inputEcho.ShowInputEcho(show);
  };
  TableEditorSC.InputBox.prototype.GetText = function () {
    return this.element.value;
  };
  TableEditorSC.InputBox.prototype.SetText = function (newtext) {
    if (!this.element) return;
    this.element.value = newtext;
    this.editor.inputEcho.SetText(newtext + '_');
  };
  TableEditorSC.InputBox.prototype.Focus = function () {
    TableEditorSC.InputBoxFocus(this);
  };
  TableEditorSC.InputBox.prototype.Blur = function () {
    return this.element.blur();
  };
  TableEditorSC.InputBox.prototype.Select = function (t) {
    if (!this.element) return;
    switch (t) {
      case 'end':
        if (this.element.selectionStart != undefined) {
          this.element.selectionStart = this.element.value.length;
          this.element.selectionEnd = this.element.value.length;
        }
        break;
    }
  };
  TableEditorSC.InputBoxDisplayCellContents = function (inputbox, coord) {
    var scc = TableEditorSC.Constants;
    if (!inputbox) return;
    if (!coord) {
      if (!inputbox.editor) return;
      if (!inputbox.editor.ecell) return;
      coord = inputbox.editor.ecell.coord;
    }
    var text = TableEditorSC.GetCellContents(inputbox.editor.context.sheetobj, coord);
    if (text.indexOf('\n') != -1) {
      text = scc.s_inputboxdisplaymultilinetext;
      inputbox.element.disabled = true;
    } else if (inputbox.editor.ECellReadonly()) {
      inputbox.element.disabled = true;
    } else {
      inputbox.element.disabled = false;
    }
    inputbox.SetText(text);
  };
  TableEditorSC.InputBoxFocus = function (inputbox) {
    if (!inputbox) return;
    inputbox.element.focus();
    var editor = inputbox.editor;
    editor.state = 'input';
    var wval = editor.workingvalues;
    wval.partialexpr = '';
    wval.ecoord = editor.ecell.coord;
    wval.erow = editor.ecell.row;
    wval.ecol = editor.ecell.col;
  };
  TableEditorSC.InputBoxOnMouseDown = function (_e) {
    var editor = TableEditorSC.Keyboard.focusTable;
    if (!editor) return true;
    var wval = editor.workingvalues;
    switch (editor.state) {
      case 'start':
        editor.state = 'inputboxdirect';
        wval.partialexpr = '';
        wval.ecoord = editor.ecell.coord;
        wval.erow = editor.ecell.row;
        wval.ecol = editor.ecell.col;
        editor.inputEcho.ShowInputEcho(true);
        break;
      case 'input':
        wval.partialexpr = '';
        editor.MoveECell(wval.ecoord);
        editor.state = 'inputboxdirect';
        TableEditorSC.KeyboardFocus();
        break;
      case 'inputboxdirect':
        break;
    }
    return undefined;
  };
  TableEditorSC.InputEcho = function (editor) {
    var scc = TableEditorSC.Constants;
    this.editor = editor;
    this.text = '';
    this.interval = null;
    this.container = null;
    this.main = null;
    this.prompt = null;
    this.hint = null;
    this.functionbox = null;
    this.container = SocialCalc.document.createElement('div');
    TableEditorSC.setStyles(this.container, 'display:none;position:absolute;zIndex:10;');
    this.main = SocialCalc.document.createElement('div');
    if (scc.defaultInputEchoClass) this.main.className = scc.defaultInputEchoClass;
    if (scc.defaultInputEchoStyle) TableEditorSC.setStyles(this.main, scc.defaultInputEchoStyle);
    this.main.innerHTML = '&nbsp;';
    this.hint = SocialCalc.document.createElement('div');
    if (scc.defaultInputEchoHintClass) this.hint.className = scc.defaultInputEchoHintClass;
    if (scc.defaultInputEchoHintStyle)
      TableEditorSC.setStyles(this.hint, scc.defaultInputEchoHintStyle);
    this.hint.innerHTML = '';
    this.container.appendChild(this.hint);
    this.container.appendChild(this.main);
    this.prompt = SocialCalc.document.createElement('div');
    if (scc.defaultInputEchoPromptClass) this.prompt.className = scc.defaultInputEchoPromptClass;
    if (scc.defaultInputEchoPromptStyle)
      TableEditorSC.setStyles(this.prompt, scc.defaultInputEchoPromptStyle);
    this.prompt.innerHTML = '';
    this.container.appendChild(this.prompt);
    TableEditorSC.DragRegister(
      this.main,
      true,
      true,
      {
        MouseDown: TableEditorSC.DragFunctionStart,
        MouseMove: TableEditorSC.DragFunctionPosition,
        MouseUp: TableEditorSC.DragFunctionPosition,
        Disabled: null,
        positionobj: this.container,
      },
      this.editor.toplevel,
    );
    editor.toplevel.appendChild(this.container);
  };
  TableEditorSC.InputEcho.prototype.ShowInputEcho = function (show) {
    return TableEditorSC.ShowInputEcho(this, show);
  };
  TableEditorSC.InputEcho.prototype.SetText = function (str) {
    return TableEditorSC.SetInputEchoText(this, str);
  };
  TableEditorSC.ShowInputEcho = function (inputecho, show) {
    var cell, position;
    var editor = inputecho.editor;
    if (!editor) return;
    if (show) {
      editor.cellhandles.ShowCellHandles(false);
      cell = TableEditorSC.GetEditorCellElement(editor, editor.ecell.row, editor.ecell.col);
      if (cell) {
        position = TableEditorSC.GetElementPosition(cell.element);
        inputecho.container.style.left = position.left - 1 + 'px';
        inputecho.container.style.top = position.top - 1 + 'px';
      }
      inputecho.hint.innerHTML = editor.ecell.coord;
      inputecho.container.style.display = 'block';
      if (inputecho.interval) window.clearInterval(inputecho.interval);
      inputecho.interval = window.setInterval(TableEditorSC.InputEchoHeartbeat, 50);
    } else {
      if (inputecho.interval) window.clearInterval(inputecho.interval);
      inputecho.container.style.display = 'none';
    }
  };
  TableEditorSC.SetInputEchoText = function (inputecho, str) {
    var scc = TableEditorSC.Constants;
    var fname, fstr;
    var newstr = TableEditorSC.special_chars(str);
    newstr = newstr.replace(/\n/g, '<br>');
    if (inputecho.text != newstr) {
      inputecho.main.innerHTML = newstr;
      inputecho.text = newstr;
    }
    var parts = str.match(/.*[+\-*/&^<>=,(]([A-Za-z][A-Za-z][\w.]*?)\([^)]*$/);
    if (str.charAt(0) == '=' && parts) {
      fname = parts[1].toUpperCase();
      if (TableEditorSC.Formula.FunctionList[fname]) {
        TableEditorSC.Formula.FillFunctionInfo();
        fstr = TableEditorSC.special_chars(
          fname + '(' + TableEditorSC.Formula.FunctionArgString(fname) + ')',
        );
      } else {
        fstr = scc.ietUnknownFunction + fname;
      }
      if (inputecho.prompt.innerHTML != fstr) {
        inputecho.prompt.innerHTML = fstr;
        inputecho.prompt.style.display = 'block';
      }
    } else if (inputecho.prompt.style.display != 'none') {
      inputecho.prompt.innerHTML = '';
      inputecho.prompt.style.display = 'none';
    }
  };
  TableEditorSC.InputEchoHeartbeat = function () {
    var editor = TableEditorSC.Keyboard.focusTable;
    if (!editor) return true;
    editor.inputEcho.SetText(editor.inputBox.GetText() + '_');
    return undefined;
  };
  TableEditorSC.InputEchoMouseDown = function (_e) {
    var editor = TableEditorSC.Keyboard.focusTable;
    if (!editor) return true;
    editor.inputBox.element.focus();
    return undefined;
  };
  TableEditorSC.CellHandles = function (editor) {
    if (editor.noEdit) return;
    this.editor = editor;
    this.noCursorSuffix = false;
    this.movedmouse = false;
    this.draghandle = SocialCalc.document.createElement('div');
    TableEditorSC.setStyles(
      this.draghandle,
      'display:none;position:absolute;zIndex:8;border:1px solid white;width:4px;height:4px;fontSize:1px;backgroundColor:#0E93D8;cursor:default;',
    );
    this.draghandle.innerHTML = '&nbsp;';
    editor.toplevel.appendChild(this.draghandle);
    TableEditorSC.AssignID(editor, this.draghandle, 'draghandle');
    var imagetype = 'png';
    this.dragpalette = SocialCalc.document.createElement('div');
    TableEditorSC.setStyles(
      this.dragpalette,
      'display:none;position:absolute;zIndex:8;width:90px;height:90px;fontSize:1px;textAlign:center;cursor:default;' +
        'backgroundImage:url(' +
        TableEditorSC.Constants.defaultImagePrefix +
        'drag-handles.' +
        imagetype +
        ');',
    );
    this.dragpalette.innerHTML = '&nbsp;';
    editor.toplevel.appendChild(this.dragpalette);
    TableEditorSC.AssignID(editor, this.dragpalette, 'dragpalette');
    this.dragtooltip = SocialCalc.document.createElement('div');
    TableEditorSC.setStyles(
      this.dragtooltip,
      'display:none;position:absolute;zIndex:9;border:1px solid black;width:100px;height:auto;fontSize:10px;backgroundColor:#FFFFFF;',
    );
    this.dragtooltip.innerHTML = '&nbsp;';
    editor.toplevel.appendChild(this.dragtooltip);
    TableEditorSC.AssignID(editor, this.dragtooltip, 'dragtooltip');
    this.fillinghandle = SocialCalc.document.createElement('div');
    TableEditorSC.setStyles(
      this.fillinghandle,
      'display:none;position:absolute;zIndex:9;border:1px solid black;width:auto;height:14px;fontSize:10px;backgroundColor:#FFFFFF;',
    );
    this.fillinghandle.innerHTML = '&nbsp;';
    editor.toplevel.appendChild(this.fillinghandle);
    TableEditorSC.AssignID(editor, this.fillinghandle, 'fillinghandle');
    this.draghandle.addEventListener(
      'mousemove',
      TableEditorSC.CellHandlesMouseMoveOnHandle,
      false,
    );
    this.dragpalette.addEventListener('mousedown', TableEditorSC.CellHandlesMouseDown, false);
    this.dragpalette.addEventListener(
      'mousemove',
      TableEditorSC.CellHandlesMouseMoveOnHandle,
      false,
    );
  };
  TableEditorSC.CellHandles.prototype.ShowCellHandles = function (show, moveshow) {
    return TableEditorSC.ShowCellHandles(this, show, moveshow);
  };
  TableEditorSC.ShowCellHandles = function (cellhandles, show, moveshow) {
    var cell;
    var editor = cellhandles.editor;
    var doshow = false;
    var row, col;
    var colinc = 1,
      rowinc = 1;
    if (!editor) return;
    if (!editor.ecell) return;
    showCellHandlesBlock: {
      if (!show) break showCellHandlesBlock;
      row = editor.ecell.row;
      col = editor.ecell.col;
      if (editor.state != 'start') break showCellHandlesBlock;
      if (row >= editor.lastvisiblerow) break showCellHandlesBlock;
      if (col >= editor.lastvisiblecol) break showCellHandlesBlock;
      if (row < editor.firstscrollingrow) break showCellHandlesBlock;
      if (col < editor.firstscrollingcol) break showCellHandlesBlock;
      while (
        editor.context.sheetobj.colattribs.hide[TableEditorSC.rcColname(col + colinc)] == 'yes'
      ) {
        colinc++;
      }
      while (editor.context.sheetobj.rowattribs.hide[row + rowinc] == 'yes') {
        rowinc++;
      }
      cell =
        editor.context.sheetobj.cells[TableEditorSC.crToCoord(col + colinc - 1, row + rowinc - 1)];
      if (typeof cell != 'undefined') {
        colinc += (cell.colspan || 1) - 1;
        rowinc += (cell.rowspan || 1) - 1;
      }
      if (editor.rowpositions[row + rowinc] + 20 > editor.horizontaltablecontrol.controlborder) {
        break showCellHandlesBlock;
      }
      if (editor.rowpositions[row + rowinc] - 10 < editor.headposition.top) {
        break showCellHandlesBlock;
      }
      if (editor.colpositions[col + colinc] + 20 > editor.verticaltablecontrol.controlborder) {
        break showCellHandlesBlock;
      }
      if (editor.colpositions[col + colinc] - 30 < editor.headposition.left) {
        break showCellHandlesBlock;
      }
      cellhandles.draghandle.style.left = editor.colpositions[col + colinc] - 1 + 'px';
      cellhandles.draghandle.style.top = editor.rowpositions[row + rowinc] - 1 + 'px';
      cellhandles.draghandle.style.display = 'block';
      if (moveshow) {
        cellhandles.draghandle.style.display = 'none';
        cellhandles.dragpalette.style.left = editor.colpositions[col + colinc] - 45 + 'px';
        cellhandles.dragpalette.style.top = editor.rowpositions[row + rowinc] - 45 + 'px';
        cellhandles.dragpalette.style.display = 'block';
        cellhandles.dragtooltip.style.left = editor.colpositions[col + colinc] - 45 + 'px';
        cellhandles.dragtooltip.style.top = editor.rowpositions[row + rowinc] + 45 + 'px';
        cellhandles.dragtooltip.style.display = 'none';
      }
      doshow = true;
    }
    if (!doshow) {
      cellhandles.draghandle.style.display = 'none';
    }
    if (!moveshow) {
      cellhandles.dragpalette.style.display = 'none';
      cellhandles.dragtooltip.style.display = 'none';
    }
  };
  TableEditorSC.CellHandlesMouseMoveOnHandle = function (e) {
    var scc = TableEditorSC.Constants;
    var event = e || window.event;
    var target = event.target || event.srcElement;
    var editor = TableEditorSC.Keyboard.focusTable;
    if (!editor) return true;
    var cellhandles = editor.cellhandles;
    if (!cellhandles.editor) return true;
    var pos = TableEditorSC.GetElementPositionWithScroll(editor.toplevel);
    var clientX = event.clientX - pos.left;
    var clientY = event.clientY - pos.top;
    if (!editor.cellhandles.mouseDown) {
      editor.cellhandles.ShowCellHandles(true, true);
      if (target == cellhandles.dragpalette) {
        var whichhandle = TableEditorSC.SegmentDivHit(
          [scc.CH_radius1, scc.CH_radius2],
          editor.cellhandles.dragpalette,
          clientX,
          clientY,
        );
        if (whichhandle == 0) {
          TableEditorSC.CellHandlesHoverTimeout();
          return;
        }
      }
      if (cellhandles.timer) {
        window.clearTimeout(cellhandles.timer);
        cellhandles.timer = null;
      }
      cellhandles.timer = window.setTimeout(TableEditorSC.CellHandlesHoverTimeout, 3e3);
    }
    return;
  };
  TableEditorSC.SegmentDivHit = function (segtable, divWithMouseHit, x, y) {
    var width = divWithMouseHit.offsetWidth;
    var height = divWithMouseHit.offsetHeight;
    var left = divWithMouseHit.offsetLeft;
    var top = divWithMouseHit.offsetTop;
    var v = 0;
    var table = segtable;
    var quadrant = '';
    var len = Math.sqrt(
      Math.pow(x - left - (width / 2 - 0.5), 2) + Math.pow(y - top - (height / 2 - 0.5), 2),
    );
    if (table.length == 2) {
      if (x >= left && x < left + width / 2 && y >= top && y < top + height / 2) {
        if (len <= segtable[0]) v = -1;
        else if (len <= segtable[1]) v = 1;
      }
      if (x >= left + width / 2 && x < left + width && y >= top && y < top + height / 2) {
        if (len <= segtable[0]) v = -2;
        else if (len <= segtable[1]) v = 2;
      }
      if (x >= left + width / 2 && x < left + width && y >= top + height / 2 && y < top + height) {
        if (len <= segtable[0]) v = -3;
        else if (len <= segtable[1]) v = 3;
      }
      if (x >= left && x < left + width / 2 && y >= top + height / 2 && y < top + height) {
        if (len <= segtable[0]) v = -4;
        else if (len <= segtable[1]) v = 4;
      }
      return v;
    }
    while (true) {
      if (x >= left && x < left + width / 2 && y >= top && y < top + height / 2) {
        quadrant += '1';
        v = table[0];
        if (typeof v == 'number') {
          break;
        }
        table = v;
        width = width / 2;
        height = height / 2;
        continue;
      }
      if (x >= left + width / 2 && x < left + width && y >= top && y < top + height / 2) {
        quadrant += '2';
        v = table[1];
        if (typeof v == 'number') {
          break;
        }
        table = v;
        width = width / 2;
        left = left + width;
        height = height / 2;
        continue;
      }
      if (x >= left + width / 2 && x < left + width && y >= top + height / 2 && y < top + height) {
        quadrant += '3';
        v = table[2];
        if (typeof v == 'number') {
          break;
        }
        table = v;
        width = width / 2;
        left = left + width;
        height = height / 2;
        top = top + height;
        continue;
      }
      if (x >= left && x < left + width / 2 && y >= top + height / 2 && y < top + height) {
        quadrant += '4';
        v = table[3];
        if (typeof v == 'number') {
          break;
        }
        table = v;
        width = width / 2;
        height = height / 2;
        top = top + height;
        continue;
      }
      return 0;
    }
    return v;
  };
  TableEditorSC.CellHandlesHoverTimeout = function () {
    var editor = TableEditorSC.Keyboard.focusTable;
    if (!editor) return true;
    var cellhandles = editor.cellhandles;
    if (cellhandles.timer) {
      window.clearTimeout(cellhandles.timer);
      cellhandles.timer = null;
    }
    editor.cellhandles.ShowCellHandles(true, false);
    return undefined;
  };
  TableEditorSC.CellHandlesMouseDown = function (e) {
    var scc = TableEditorSC.Constants;
    var editor, coord, range;
    var event = e || window.event;
    var mouseinfo = TableEditorSC.EditorMouseInfo;
    editor = TableEditorSC.Keyboard.focusTable;
    if (!editor) return true;
    if (editor.busy) return;
    var cellhandles = editor.cellhandles;
    cellhandles.movedmouse = false;
    var pos = TableEditorSC.GetElementPositionWithScroll(editor.toplevel);
    var clientX = event.clientX - pos.left;
    var clientY = event.clientY - pos.top;
    if (cellhandles.timer) {
      window.clearTimeout(cellhandles.timer);
      cellhandles.timer = null;
    }
    cellhandles.dragtooltip.innerHTML = '&nbsp;';
    cellhandles.dragtooltip.style.display = 'none';
    range = editor.range;
    var whichhandle = TableEditorSC.SegmentDivHit(
      [scc.CH_radius1, scc.CH_radius2],
      editor.cellhandles.dragpalette,
      clientX,
      clientY,
    );
    if (whichhandle == 1 || whichhandle == -1 || whichhandle == 0) {
      cellhandles.ShowCellHandles(true, false);
      return;
    }
    mouseinfo.ignore = true;
    if (whichhandle == -3) {
      cellhandles.dragtype = 'Fill';
      cellhandles.noCursorSuffix = false;
    } else if (whichhandle == 3) {
      cellhandles.dragtype = 'FillC';
      cellhandles.noCursorSuffix = false;
    } else if (whichhandle == -2) {
      cellhandles.dragtype = 'Move';
      cellhandles.noCursorSuffix = true;
    } else if (whichhandle == -4) {
      cellhandles.dragtype = 'MoveI';
      cellhandles.noCursorSuffix = false;
    } else if (whichhandle == 2) {
      cellhandles.dragtype = 'MoveC';
      cellhandles.noCursorSuffix = true;
    } else if (whichhandle == 4) {
      cellhandles.dragtype = 'MoveIC';
      cellhandles.noCursorSuffix = false;
    }
    cellhandles.filltype = null;
    switch (cellhandles.dragtype) {
      case 'Fill':
      case 'FillC':
        if (!range.hasrange) {
          editor.RangeAnchor();
        }
        editor.range2.top = editor.range.top;
        editor.range2.right = editor.range.right;
        editor.range2.bottom = editor.range.bottom;
        editor.range2.left = editor.range.left;
        editor.range2.hasrange = true;
        break;
      case 'Move':
      case 'MoveI':
      case 'MoveC':
      case 'MoveIC':
        if (!range.hasrange) {
          editor.RangeAnchor();
        }
        editor.range2.top = editor.range.top;
        editor.range2.right = editor.range.right;
        editor.range2.bottom = editor.range.bottom;
        editor.range2.left = editor.range.left;
        editor.range2.hasrange = true;
        editor.RangeRemove();
        break;
      default:
        return;
    }
    cellhandles.fillinghandle.style.left = clientX + 'px';
    cellhandles.fillinghandle.style.top = clientY - 17 + 'px';
    cellhandles.fillinghandle.innerHTML =
      scc.s_CHindicatorOperationLookup[cellhandles.dragtype] +
      (scc.s_CHindicatorDirectionLookup[editor.cellhandles.filltype] || '');
    cellhandles.fillinghandle.style.display = 'block';
    cellhandles.ShowCellHandles(true, false);
    cellhandles.mouseDown = true;
    mouseinfo.editor = editor;
    coord = editor.ecell.coord;
    cellhandles.startingcoord = coord;
    cellhandles.startingX = clientX;
    cellhandles.startingY = clientY;
    mouseinfo.mouselastcoord = coord;
    TableEditorSC.KeyboardSetFocus(editor);
    document.addEventListener('mousemove', TableEditorSC.CellHandlesMouseMove, true);
    document.addEventListener('mouseup', TableEditorSC.CellHandlesMouseUp, true);
    TableEditorSC.StopPropagation(event);
    return;
  };
  TableEditorSC.CellHandlesMouseMove = function (e) {
    var scc = TableEditorSC.Constants;
    var editor, result;
    var crstart, crend, c, r;
    var event = e || window.event;
    var mouseinfo = TableEditorSC.EditorMouseInfo;
    editor = mouseinfo.editor;
    if (!editor) return;
    var cellhandles = editor.cellhandles;
    var pos = TableEditorSC.GetElementPositionWithScroll(editor.toplevel);
    var clientX = event.clientX - pos.left;
    var clientY = event.clientY - pos.top;
    result = TableEditorSC.GridMousePosition(editor, clientX, clientY);
    if (!result) return;
    if (result && !result.coord) {
      TableEditorSC.SetDragAutoRepeat(editor, result, TableEditorSC.CellHandlesDragAutoRepeat);
      return;
    }
    TableEditorSC.SetDragAutoRepeat(editor, null);
    if (!result.coord) return;
    crstart = TableEditorSC.coordToCr(editor.cellhandles.startingcoord);
    crend = TableEditorSC.coordToCr(result.coord);
    cellhandles.movedmouse = true;
    switch (cellhandles.dragtype) {
      case 'Fill':
      case 'FillC':
        if (result.coord == cellhandles.startingcoord) {
          cellhandles.filltype = null;
          cellhandles.startingX = clientX;
          cellhandles.startingY = clientY;
        } else if (cellhandles.filltype) {
          if (cellhandles.filltype == 'Down') {
            crend.col = crstart.col;
            if (crend.row < crstart.row) crend.row = crstart.row;
          } else {
            crend.row = crstart.row;
            if (crend.col < crstart.col) crend.col = crstart.col;
          }
        } else {
          if (Math.abs(clientY - cellhandles.startingY) > 10) cellhandles.filltype = 'Down';
          else if (Math.abs(clientX - cellhandles.startingX) > 10) cellhandles.filltype = 'Right';
          crend.col = crstart.col;
          crend.row = crstart.row;
        }
        result.coord = TableEditorSC.crToCoord(crend.col, crend.row);
        if (result.coord != mouseinfo.mouselastcoord) {
          editor.MoveECell(result.coord);
          editor.RangeExtend();
        }
        break;
      case 'Move':
      case 'MoveC':
        if (result.coord != mouseinfo.mouselastcoord) {
          editor.MoveECell(result.coord);
          c = editor.range2.right - editor.range2.left + result.col;
          r = editor.range2.bottom - editor.range2.top + result.row;
          editor.RangeAnchor(TableEditorSC.crToCoord(c, r));
          editor.RangeExtend();
        }
        break;
      case 'MoveI':
      case 'MoveIC':
        if (result.coord == cellhandles.startingcoord) {
          cellhandles.filltype = null;
          cellhandles.startingX = clientX;
          cellhandles.startingY = clientY;
        } else if (cellhandles.filltype) {
          if (cellhandles.filltype == 'Vertical') {
            crend.col = editor.range2.left;
            if (crend.row >= editor.range2.top && crend.row <= editor.range2.bottom + 1)
              crend.row = editor.range2.bottom + 2;
          } else {
            crend.row = editor.range2.top;
            if (crend.col >= editor.range2.left && crend.col <= editor.range2.right + 1)
              crend.col = editor.range2.right + 2;
          }
        } else {
          if (Math.abs(clientY - cellhandles.startingY) > 10) cellhandles.filltype = 'Vertical';
          else if (Math.abs(clientX - cellhandles.startingX) > 10)
            cellhandles.filltype = 'Horizontal';
          crend.col = crstart.col;
          crend.row = crstart.row;
        }
        result.coord = TableEditorSC.crToCoord(crend.col, crend.row);
        if (result.coord != mouseinfo.mouselastcoord) {
          editor.MoveECell(result.coord);
          if (!cellhandles.filltype) editor.RangeRemove();
          else {
            c = editor.range2.right - editor.range2.left + crend.col;
            r = editor.range2.bottom - editor.range2.top + crend.row;
            editor.RangeAnchor(TableEditorSC.crToCoord(c, r));
            editor.RangeExtend();
          }
        }
        break;
    }
    cellhandles.fillinghandle.style.left = clientX + 'px';
    cellhandles.fillinghandle.style.top = clientY - 17 + 'px';
    cellhandles.fillinghandle.innerHTML =
      scc.s_CHindicatorOperationLookup[cellhandles.dragtype] +
      (scc.s_CHindicatorDirectionLookup[editor.cellhandles.filltype] || '');
    cellhandles.fillinghandle.style.display = 'block';
    mouseinfo.mouselastcoord = result.coord;
    TableEditorSC.StopPropagation(event);
    return;
  };
  TableEditorSC.CellHandlesDragAutoRepeat = function (coord, direction) {
    var mouseinfo = TableEditorSC.EditorMouseInfo;
    var editor = mouseinfo.editor;
    if (!editor) return;
    var cellhandles = editor.cellhandles;
    var crstart = TableEditorSC.coordToCr(editor.cellhandles.startingcoord);
    var crend = TableEditorSC.coordToCr(coord);
    var newcoord, c, r;
    var vscroll = 0;
    var hscroll = 0;
    if (direction == 'left') hscroll = -1;
    else if (direction == 'right') hscroll = 1;
    else if (direction == 'up') vscroll = -1;
    else if (direction == 'down') vscroll = 1;
    editor.ScrollRelativeBoth(vscroll, hscroll);
    switch (cellhandles.dragtype) {
      case 'Fill':
      case 'FillC':
        if (cellhandles.filltype) {
          if (cellhandles.filltype == 'Down') {
            crend.col = crstart.col;
            if (crend.row < crstart.row) crend.row = crstart.row;
          } else {
            crend.row = crstart.row;
            if (crend.col < crstart.col) crend.col = crstart.col;
          }
        } else {
          crend.col = crstart.col;
          crend.row = crstart.row;
        }
        newcoord = TableEditorSC.crToCoord(crend.col, crend.row);
        if (newcoord != mouseinfo.mouselastcoord) {
          editor.MoveECell(coord);
          editor.RangeExtend();
        }
        break;
      case 'Move':
      case 'MoveC':
        if (coord != mouseinfo.mouselastcoord) {
          editor.MoveECell(coord);
          c = editor.range2.right - editor.range2.left + editor.ecell.col;
          r = editor.range2.bottom - editor.range2.top + editor.ecell.row;
          editor.RangeAnchor(TableEditorSC.crToCoord(c, r));
          editor.RangeExtend();
        }
        break;
      case 'MoveI':
      case 'MoveIC':
        if (cellhandles.filltype) {
          if (cellhandles.filltype == 'Vertical') {
            crend.col = editor.range2.left;
            if (crend.row >= editor.range2.top && crend.row <= editor.range2.bottom + 1)
              crend.row = editor.range2.bottom + 2;
          } else {
            crend.row = editor.range2.top;
            if (crend.col >= editor.range2.left && crend.col <= editor.range2.right + 1)
              crend.col = editor.range2.right + 2;
          }
        } else {
          crend.col = crstart.col;
          crend.row = crstart.row;
        }
        newcoord = TableEditorSC.crToCoord(crend.col, crend.row);
        if (newcoord != mouseinfo.mouselastcoord) {
          editor.MoveECell(newcoord);
          c = editor.range2.right - editor.range2.left + crend.col;
          r = editor.range2.bottom - editor.range2.top + crend.row;
          editor.RangeAnchor(TableEditorSC.crToCoord(c, r));
          editor.RangeExtend();
        }
        break;
    }
    mouseinfo.mouselastcoord = newcoord;
  };
  TableEditorSC.CellHandlesMouseUp = function (e) {
    var editor, result, cstr, cmdtype, cmdtype2;
    var crstart, crend;
    var sizec, sizer;
    var event = e || window.event;
    var mouseinfo = TableEditorSC.EditorMouseInfo;
    editor = mouseinfo.editor;
    if (!editor) return;
    var cellhandles = editor.cellhandles;
    mouseinfo.ignore = false;
    var pos = TableEditorSC.GetElementPositionWithScroll(editor.toplevel);
    var clientX = event.clientX - pos.left;
    var clientY = event.clientY - pos.top;
    result = TableEditorSC.GridMousePosition(editor, clientX, clientY);
    TableEditorSC.SetDragAutoRepeat(editor, null);
    cellhandles.mouseDown = false;
    cellhandles.noCursorSuffix = false;
    cellhandles.fillinghandle.style.display = 'none';
    if (!result) result = {};
    if (!result.coord) result.coord = editor.ecell.coord;
    switch (cellhandles.dragtype) {
      case 'Fill':
      case 'Move':
      case 'MoveI':
        cmdtype2 = ' all';
        break;
      case 'FillC':
      case 'MoveC':
      case 'MoveIC':
        cmdtype2 = ' formulas';
        break;
    }
    if (!cellhandles.movedmouse) {
      cellhandles.dragtype = 'Nothing';
    }
    switch (cellhandles.dragtype) {
      case 'Nothing':
        editor.Range2Remove();
        editor.RangeRemove();
        break;
      case 'Fill':
      case 'FillC':
        crstart = TableEditorSC.coordToCr(cellhandles.startingcoord);
        crend = TableEditorSC.coordToCr(result.coord);
        if (cellhandles.filltype) {
          if (cellhandles.filltype == 'Down') {
            crend.col = crstart.col;
          } else {
            crend.row = crstart.row;
          }
        }
        result.coord = TableEditorSC.crToCoord(crend.col, crend.row);
        editor.MoveECell(result.coord);
        editor.RangeExtend();
        if (editor.cellhandles.filltype == 'Right') {
          cmdtype = 'right';
        } else {
          cmdtype = 'down';
        }
        cstr =
          'fill' +
          cmdtype +
          ' ' +
          TableEditorSC.crToCoord(editor.range.left, editor.range.top) +
          ':' +
          TableEditorSC.crToCoord(editor.range.right, editor.range.bottom) +
          cmdtype2;
        editor.EditorScheduleSheetCommands(cstr, true, false);
        break;
      case 'Move':
      case 'MoveC':
        editor.context.cursorsuffix = '';
        cstr =
          'movepaste ' +
          TableEditorSC.crToCoord(editor.range2.left, editor.range2.top) +
          ':' +
          TableEditorSC.crToCoord(editor.range2.right, editor.range2.bottom) +
          ' ' +
          editor.ecell.coord +
          cmdtype2;
        editor.EditorScheduleSheetCommands(cstr, true, false);
        editor.Range2Remove();
        break;
      case 'MoveI':
      case 'MoveIC':
        editor.context.cursorsuffix = '';
        sizec = editor.range2.right - editor.range2.left;
        sizer = editor.range2.bottom - editor.range2.top;
        cstr =
          'moveinsert ' +
          TableEditorSC.crToCoord(editor.range2.left, editor.range2.top) +
          ':' +
          TableEditorSC.crToCoord(editor.range2.right, editor.range2.bottom) +
          ' ' +
          editor.ecell.coord +
          cmdtype2;
        editor.EditorScheduleSheetCommands(cstr, true, false);
        editor.Range2Remove();
        editor.RangeRemove();
        editor.RangeAnchor(
          TableEditorSC.crToCoord(editor.ecell.col + sizec, editor.ecell.row + sizer),
        );
        editor.RangeExtend();
        break;
    }
    TableEditorSC.RemoveMouseMoveUp(
      TableEditorSC.CellHandlesMouseMove,
      TableEditorSC.CellHandlesMouseUp,
      cellhandles.draghandle,
      event,
    );
    mouseinfo.editor = null;
    return false;
  };
  TableEditorSC.TableControl = function (editor, vertical, size) {
    var scc = TableEditorSC.Constants;
    this.editor = editor;
    this.vertical = vertical;
    this.size = size;
    this.main = null;
    this.endcap = null;
    this.paneslider = null;
    this.lessbutton = null;
    this.morebutton = null;
    this.scrollarea = null;
    this.thumb = null;
    this.controlborder = null;
    this.endcapstart = null;
    this.panesliderstart = null;
    this.lessbuttonstart = null;
    this.morebuttonstart = null;
    this.scrollareastart = null;
    this.scrollareaend = null;
    this.scrollareasize = null;
    this.thumbpos = null;
    this.controlthickness = scc.defaultTableControlThickness;
    this.sliderthickness = scc.defaultTCSliderThickness;
    this.buttonthickness = scc.defaultTCButtonThickness;
    this.thumbthickness = scc.defaultTCThumbThickness;
    this.minscrollingpanesize =
      this.buttonthickness + this.buttonthickness + this.thumbthickness + 20;
  };
  TableEditorSC.TableControl.prototype.CreateTableControl = function () {
    return TableEditorSC.CreateTableControl(this);
  };
  TableEditorSC.TableControl.prototype.PositionTableControlElements = function () {
    TableEditorSC.PositionTableControlElements(this);
  };
  TableEditorSC.TableControl.prototype.ComputeTableControlPositions = function () {
    TableEditorSC.ComputeTableControlPositions(this);
  };
  TableEditorSC.CreateTableControl = function (control) {
    var s;
    var functions;
    var params;
    var AssignID = TableEditorSC.AssignID;
    var setStyles = TableEditorSC.setStyles;
    var scc = TableEditorSC.Constants;
    var imageprefix = control.editor.imageprefix;
    var vh = control.vertical ? 'v' : 'h';
    var SCLoc = TableEditorSC.LocalizeString;
    control.main = SocialCalc.document.createElement('div');
    s = control.main.style;
    s.height = (control.vertical ? control.size : control.controlthickness) + 'px';
    s.width = (control.vertical ? control.controlthickness : control.size) + 'px';
    s.zIndex = 0;
    setStyles(control.main, scc.TCmainStyle);
    s.backgroundImage = 'url(' + imageprefix + 'main-' + vh + '.gif)';
    if (scc.TCmainClass) control.main.className = scc.TCmainClass;
    control.main.style.display = 'none';
    control.endcap = SocialCalc.document.createElement('div');
    s = control.endcap.style;
    s.height = control.controlthickness + 'px';
    s.width = control.controlthickness + 'px';
    s.zIndex = 1;
    s.overflow = 'hidden';
    s.position = 'absolute';
    setStyles(control.endcap, scc.TCendcapStyle);
    s.backgroundImage = 'url(' + imageprefix + 'endcap-' + vh + '.gif)';
    if (scc.TCendcapClass) control.endcap.className = scc.TCendcapClass;
    AssignID(control.editor, control.endcap, 'endcap' + vh);
    control.main.appendChild(control.endcap);
    control.paneslider = SocialCalc.document.createElement('div');
    s = control.paneslider.style;
    s.height = (control.vertical ? control.sliderthickness : control.controlthickness) + 'px';
    s.overflow = 'hidden';
    s.width = (control.vertical ? control.controlthickness : control.sliderthickness) + 'px';
    s.position = 'absolute';
    s[control.vertical ? 'top' : 'left'] = '4px';
    s.zIndex = 3;
    s.backgroundImage = 'url(' + imageprefix + 'paneslider-' + vh + '.gif)';
    if (scc.TCpanesliderClass) control.paneslider.className = scc.TCpanesliderClass;
    AssignID(control.editor, control.paneslider, 'paneslider' + vh);
    control.paneslider.title = SCLoc(
      control.vertical ? 'Drag to lock pane horizontally' : 'Drag to lock pane vertically',
    );
    functions = {
      MouseDown: TableEditorSC.TCPSDragFunctionStart,
      MouseMove: TableEditorSC.TCPSDragFunctionMove,
      MouseUp: TableEditorSC.TCPSDragFunctionStop,
      Disabled: function () {
        return control.editor.busy;
      },
    };
    functions.control = control;
    if (TableEditorSC._app != true)
      TableEditorSC.DragRegister(
        control.paneslider,
        control.vertical,
        !control.vertical,
        functions,
        control.editor.toplevel,
      );
    control.main.appendChild(control.paneslider);
    control.lessbutton = SocialCalc.document.createElement('div');
    s = control.lessbutton.style;
    s.height = (control.vertical ? control.buttonthickness : control.controlthickness) + 'px';
    s.width = (control.vertical ? control.controlthickness : control.buttonthickness) + 'px';
    s.zIndex = 2;
    s.overflow = 'hidden';
    s.position = 'absolute';
    setStyles(control.lessbutton, scc.TClessbuttonStyle);
    s.backgroundImage = 'url(' + imageprefix + 'less-' + vh + 'n.gif)';
    if (scc.TClessbuttonClass) control.lessbutton.className = scc.TClessbuttonClass;
    AssignID(control.editor, control.lessbutton, 'lessbutton' + vh);
    params = {
      repeatwait: scc.TClessbuttonRepeatWait,
      repeatinterval: scc.TClessbuttonRepeatInterval,
      normalstyle: 'backgroundImage:url(' + imageprefix + 'less-' + vh + 'n.gif);',
      downstyle: 'backgroundImage:url(' + imageprefix + 'less-' + vh + 'd.gif);',
      hoverstyle: 'backgroundImage:url(' + imageprefix + 'less-' + vh + 'h.gif);',
    };
    functions = {
      MouseDown: function () {
        if (!control.editor.busy) control.editor.ScrollRelative(control.vertical, -1);
      },
      Repeat: function () {
        if (!control.editor.busy) control.editor.ScrollRelative(control.vertical, -1);
      },
      Disabled: function () {
        return control.editor.busy;
      },
    };
    TableEditorSC.ButtonRegister(control.editor, control.lessbutton, params, functions);
    control.main.appendChild(control.lessbutton);
    control.morebutton = SocialCalc.document.createElement('div');
    s = control.morebutton.style;
    s.height = (control.vertical ? control.buttonthickness : control.controlthickness) + 'px';
    s.width = (control.vertical ? control.controlthickness : control.buttonthickness) + 'px';
    s.zIndex = 2;
    s.overflow = 'hidden';
    s.position = 'absolute';
    setStyles(control.morebutton, scc.TCmorebuttonStyle);
    s.backgroundImage = 'url(' + imageprefix + 'more-' + vh + 'n.gif)';
    if (scc.TCmorebuttonClass) control.morebutton.className = scc.TCmorebuttonClass;
    AssignID(control.editor, control.morebutton, 'morebutton' + vh);
    params = {
      repeatwait: scc.TCmorebuttonRepeatWait,
      repeatinterval: scc.TCmorebuttonRepeatInterval,
      normalstyle: 'backgroundImage:url(' + imageprefix + 'more-' + vh + 'n.gif);',
      downstyle: 'backgroundImage:url(' + imageprefix + 'more-' + vh + 'd.gif);',
      hoverstyle: 'backgroundImage:url(' + imageprefix + 'more-' + vh + 'h.gif);',
    };
    functions = {
      MouseDown: function () {
        if (!control.editor.busy) control.editor.ScrollRelative(control.vertical, +1);
      },
      Repeat: function () {
        if (!control.editor.busy) control.editor.ScrollRelative(control.vertical, +1);
      },
      Disabled: function () {
        return control.editor.busy;
      },
    };
    TableEditorSC.ButtonRegister(control.editor, control.morebutton, params, functions);
    control.main.appendChild(control.morebutton);
    control.scrollarea = SocialCalc.document.createElement('div');
    s = control.scrollarea.style;
    s.height = control.controlthickness + 'px';
    s.width = control.controlthickness + 'px';
    s.zIndex = 1;
    s.overflow = 'hidden';
    s.position = 'absolute';
    setStyles(control.scrollarea, scc.TCscrollareaStyle);
    s.backgroundImage = 'url(' + imageprefix + 'scrollarea-' + vh + '.gif)';
    if (scc.TCscrollareaClass) control.scrollarea.className = scc.TCscrollareaClass;
    AssignID(control.editor, control.scrollarea, 'scrollarea' + vh);
    params = {
      repeatwait: scc.TCscrollareaRepeatWait,
      repeatinterval: scc.TCscrollareaRepeatWait,
    };
    functions = {
      MouseDown: TableEditorSC.ScrollAreaClick,
      Repeat: TableEditorSC.ScrollAreaClick,
      Disabled: function () {
        return control.editor.busy;
      },
    };
    functions.control = control;
    TableEditorSC.ButtonRegister(control.editor, control.scrollarea, params, functions);
    control.main.appendChild(control.scrollarea);
    control.thumb = SocialCalc.document.createElement('div');
    s = control.thumb.style;
    s.height = (control.vertical ? control.thumbthickness : control.controlthickness) + 'px';
    s.width = (control.vertical ? control.controlthickness : control.thumbthickness) + 'px';
    s.zIndex = 2;
    s.overflow = 'hidden';
    s.position = 'absolute';
    setStyles(control.thumb, scc.TCthumbStyle);
    control.thumb.style.backgroundImage = 'url(' + imageprefix + 'thumb-' + vh + 'n.gif)';
    if (scc.TCthumbClass) control.thumb.className = scc.TCthumbClass;
    AssignID(control.editor, control.thumb, 'thumb' + vh);
    functions = {
      MouseDown: TableEditorSC.TCTDragFunctionStart,
      MouseMove: TableEditorSC.TCTDragFunctionMove,
      MouseUp: TableEditorSC.TCTDragFunctionStop,
      Disabled: function () {
        return control.editor.busy;
      },
    };
    functions.control = control;
    if (TableEditorSC._app != true)
      TableEditorSC.DragRegister(
        control.thumb,
        control.vertical,
        !control.vertical,
        functions,
        control.editor.toplevel,
      );
    params = {
      normalstyle: 'backgroundImage:url(' + imageprefix + 'thumb-' + vh + 'n.gif)',
      name: 'Thumb',
      downstyle: 'backgroundImage:url(' + imageprefix + 'thumb-' + vh + 'd.gif)',
      hoverstyle: 'backgroundImage:url(' + imageprefix + 'thumb-' + vh + 'h.gif)',
    };
    TableEditorSC.ButtonRegister(control.editor, control.thumb, params, null);
    control.main.appendChild(control.thumb);
    return control.main;
  };
  TableEditorSC.ScrollAreaClick = function (e, buttoninfo, bobj) {
    var control = bobj.functionobj.control;
    var pos = TableEditorSC.GetElementPositionWithScroll(control.editor.toplevel);
    var clickpos = control.vertical ? buttoninfo.clientY - pos.top : buttoninfo.clientX - pos.left;
    if (control.editor.busy) {
      return;
    }
    control.editor.PageRelative(control.vertical, clickpos > control.thumbpos ? 1 : -1);
    return;
  };
  TableEditorSC.PositionTableControlElements = function (control) {
    var border, realend, thumbpos;
    var editor = control.editor;
    if (control.vertical) {
      border = control.controlborder + 'px';
      control.endcap.style.top = control.endcapstart + 'px';
      control.endcap.style.left = border;
      control.paneslider.style.top = control.panesliderstart + 'px';
      control.paneslider.style.left = border;
      control.lessbutton.style.top = control.lessbuttonstart + 'px';
      control.lessbutton.style.left = border;
      control.morebutton.style.top = control.morebuttonstart + 'px';
      control.morebutton.style.left = border;
      control.scrollarea.style.top = control.scrollareastart + 'px';
      control.scrollarea.style.left = border;
      control.scrollarea.style.height = control.scrollareasize + 'px';
      realend = Math.max(editor.context.sheetobj.attribs.lastrow, editor.firstscrollingrow + 1);
      thumbpos =
        ((editor.firstscrollingrow - (editor.lastnonscrollingrow + 1)) *
          (control.scrollareasize - 3 * control.thumbthickness)) /
          (realend - (editor.lastnonscrollingrow + 1)) +
        control.scrollareastart -
        1;
      thumbpos = Math.floor(thumbpos);
      control.thumb.style.top = thumbpos + 'px';
      control.thumb.style.left = border;
    } else {
      border = control.controlborder + 'px';
      control.endcap.style.left = control.endcapstart + 'px';
      control.endcap.style.top = border;
      control.paneslider.style.left = control.panesliderstart + 'px';
      control.paneslider.style.top = border;
      control.lessbutton.style.left = control.lessbuttonstart + 'px';
      control.lessbutton.style.top = border;
      control.morebutton.style.left = control.morebuttonstart + 'px';
      control.morebutton.style.top = border;
      control.scrollarea.style.left = control.scrollareastart + 'px';
      control.scrollarea.style.top = border;
      control.scrollarea.style.width = control.scrollareasize + 'px';
      realend = Math.max(editor.context.sheetobj.attribs.lastcol, editor.firstscrollingcol + 1);
      thumbpos =
        ((editor.firstscrollingcol - (editor.lastnonscrollingcol + 1)) *
          (control.scrollareasize - control.thumbthickness)) /
          (realend - editor.lastnonscrollingcol) +
        control.scrollareastart -
        1;
      thumbpos = Math.floor(thumbpos);
      control.thumb.style.left = thumbpos + 'px';
      control.thumb.style.top = border;
    }
    control.thumbpos = thumbpos;
    control.main.style.display = 'block';
  };
  TableEditorSC.ComputeTableControlPositions = function (control) {
    var editor = control.editor;
    if (!editor.gridposition || !editor.headposition)
      throw "Can't compute table control positions before editor positions";
    if (control.vertical) {
      control.controlborder = editor.gridposition.left + editor.tablewidth;
      control.endcapstart = editor.gridposition.top;
      control.panesliderstart = editor.firstscrollingrowtop - control.sliderthickness;
      control.lessbuttonstart = editor.firstscrollingrowtop - 1;
      control.morebuttonstart =
        editor.gridposition.top + editor.tableheight - control.buttonthickness;
      control.scrollareastart = editor.firstscrollingrowtop - 1 + control.buttonthickness;
      control.scrollareaend = control.morebuttonstart - 1;
      control.scrollareasize = control.scrollareaend - control.scrollareastart + 1;
    } else {
      control.controlborder = editor.gridposition.top + editor.tableheight;
      control.endcapstart = editor.gridposition.left;
      control.panesliderstart = editor.firstscrollingcolleft - control.sliderthickness;
      control.lessbuttonstart = editor.firstscrollingcolleft - 1;
      control.morebuttonstart =
        editor.gridposition.left + editor.tablewidth - control.buttonthickness;
      control.scrollareastart = editor.firstscrollingcolleft - 1 + control.buttonthickness;
      control.scrollareaend = control.morebuttonstart - 1;
      control.scrollareasize = control.scrollareaend - control.scrollareastart + 1;
    }
  };
  TableEditorSC.TCPSDragFunctionStart = function (event, draginfo, dobj) {
    var editor = dobj.functionobj.control.editor;
    var scc = TableEditorSC.Constants;
    TableEditorSC.DragFunctionStart(event, draginfo, dobj);
    draginfo.trackingline = SocialCalc.document.createElement('div');
    draginfo.trackingline.style.height = dobj.vertical
      ? scc.TCPStrackinglineThickness
      : editor.tableheight - (editor.headposition.top - editor.gridposition.top) + 'px';
    draginfo.trackingline.style.width = dobj.vertical
      ? editor.tablewidth - (editor.headposition.left - editor.gridposition.left) + 'px'
      : scc.TCPStrackinglineThickness;
    draginfo.trackingline.style.backgroundImage =
      'url(' + editor.imageprefix + 'trackingline-' + (dobj.vertical ? 'v' : 'h') + '.gif)';
    if (scc.TCPStrackinglineClass) draginfo.trackingline.className = scc.TCPStrackinglineClass;
    TableEditorSC.setStyles(draginfo.trackingline, scc.TCPStrackinglineStyle);
    if (dobj.vertical) {
      var row = TableEditorSC.Lookup(
        draginfo.clientY + dobj.functionobj.control.sliderthickness,
        editor.rowpositions,
      );
      draginfo.trackingline.style.top =
        (editor.rowpositions[row] || editor.headposition.top) + 'px';
      draginfo.trackingline.style.left = editor.headposition.left + 'px';
      draginfo.trackingline.id = 'trackingline-vertical';
      if (editor.context.rowpanes.length - 1) {
        editor.context.SetRowPaneFirstLast(
          1,
          editor.context.rowpanes[0].last + 1,
          editor.context.rowpanes[0].last + 1,
        );
        editor.FitToEditTable();
        editor.ScheduleRender();
      }
    } else {
      var col = TableEditorSC.Lookup(
        draginfo.clientX + dobj.functionobj.control.sliderthickness,
        editor.colpositions,
      );
      draginfo.trackingline.style.top = editor.headposition.top + 'px';
      draginfo.trackingline.style.left =
        (editor.colpositions[col] || editor.headposition.left) + 'px';
      draginfo.trackingline.id = 'trackingline-horizon';
      if (editor.context.colpanes.length - 1) {
        editor.context.SetColPaneFirstLast(
          1,
          editor.context.colpanes[0].last + 1,
          editor.context.colpanes[0].last + 1,
        );
        editor.FitToEditTable();
        editor.ScheduleRender();
      }
    }
    editor.griddiv.appendChild(draginfo.trackingline);
  };
  TableEditorSC.TCPSDragFunctionMove = function (event, draginfo, dobj) {
    var row, col, max, min;
    var control = dobj.functionobj.control;
    var sliderthickness = control.sliderthickness;
    var editor = control.editor;
    if (dobj.vertical) {
      max = control.morebuttonstart - control.minscrollingpanesize - draginfo.offsetY;
      if (draginfo.clientY > max) draginfo.clientY = max;
      min = editor.headposition.top - sliderthickness - draginfo.offsetY;
      if (draginfo.clientY < min) draginfo.clientY = min;
      row = TableEditorSC.Lookup(draginfo.clientY + sliderthickness, editor.rowpositions);
      while (editor.context.sheetobj.rowattribs.hide[row] == 'yes') {
        row++;
      }
      draginfo.trackingline.style.top =
        (editor.rowpositions[row] || editor.headposition.top) + 'px';
    } else {
      max = control.morebuttonstart - control.minscrollingpanesize - draginfo.offsetX;
      if (draginfo.clientX > max) draginfo.clientX = max;
      min = editor.headposition.left - sliderthickness - draginfo.offsetX;
      if (draginfo.clientX < min) draginfo.clientX = min;
      col = TableEditorSC.Lookup(draginfo.clientX + sliderthickness, editor.colpositions);
      while (editor.context.sheetobj.colattribs.hide[TableEditorSC.rcColname(col)] == 'yes') {
        col++;
      }
      draginfo.trackingline.style.left =
        (editor.colpositions[col] || editor.headposition.left) + 'px';
    }
    TableEditorSC.DragFunctionPosition(event, draginfo, dobj);
  };
  TableEditorSC.TCPSDragFunctionStop = function (event, draginfo, dobj) {
    var row, col, max, min;
    var control = dobj.functionobj.control;
    var sliderthickness = control.sliderthickness;
    var editor = control.editor;
    if (dobj.vertical) {
      max = control.morebuttonstart - control.minscrollingpanesize - draginfo.offsetY;
      if (draginfo.clientY > max) draginfo.clientY = max;
      min = editor.headposition.top - sliderthickness - draginfo.offsetY;
      if (draginfo.clientY < min) draginfo.clientY = min;
      row = TableEditorSC.Lookup(draginfo.clientY + sliderthickness, editor.rowpositions);
      if (row > editor.context.sheetobj.attribs.lastrow)
        row = editor.context.sheetobj.attribs.lastrow;
      while (editor.context.sheetobj.rowattribs.hide[row] == 'yes') {
        row++;
      }
      editor.EditorScheduleSheetCommands('pane row ' + row, true, false);
    } else {
      max = control.morebuttonstart - control.minscrollingpanesize - draginfo.offsetX;
      if (draginfo.clientX > max) draginfo.clientX = max;
      min = editor.headposition.left - sliderthickness - draginfo.offsetX;
      if (draginfo.clientX < min) draginfo.clientX = min;
      col = TableEditorSC.Lookup(draginfo.clientX + sliderthickness, editor.colpositions);
      if (col > editor.context.sheetobj.attribs.lastcol)
        col = editor.context.sheetobj.attribs.lastcol;
      while (editor.context.sheetobj.colattribs.hide[TableEditorSC.rcColname(col)] == 'yes') {
        col++;
      }
      editor.EditorScheduleSheetCommands('pane col ' + col, true, false);
    }
  };
  TableEditorSC.TCTDragFunctionStart = function (event, draginfo, dobj) {
    var control = dobj.functionobj.control;
    var editor = control.editor;
    var scc = TableEditorSC.Constants;
    TableEditorSC.DragFunctionStart(event, draginfo, dobj);
    if (draginfo.thumbstatus) {
      if (draginfo.thumbstatus.rowmsgele) draginfo.thumbstatus.rowmsgele = null;
      if (draginfo.thumbstatus.rowpreviewele) draginfo.thumbstatus.rowpreviewele = null;
      editor.toplevel.removeChild(draginfo.thumbstatus);
      draginfo.thumbstatus = null;
    }
    draginfo.thumbstatus = SocialCalc.document.createElement('div');
    if (dobj.vertical) {
      if (scc.TCTDFSthumbstatusvClass) draginfo.thumbstatus.className = scc.TCTDFSthumbstatusvClass;
      TableEditorSC.setStyles(draginfo.thumbstatus, scc.TCTDFSthumbstatusvStyle);
      draginfo.thumbstatus.style.top = draginfo.clientY + scc.TCTDFStopOffsetv + 'px';
      draginfo.thumbstatus.style.left = control.controlborder - 10 - editor.tablewidth / 2 + 'px';
      draginfo.thumbstatus.style.width = editor.tablewidth / 2 + 'px';
      draginfo.thumbcontext = new TableEditorSC.RenderContext(editor.context.sheetobj);
      draginfo.thumbcontext.showGrid = true;
      draginfo.thumbcontext.rowpanes = [
        {
          first: 1,
          last: 1,
        },
      ];
      var pane = editor.context.colpanes[editor.context.colpanes.length - 1];
      draginfo.thumbcontext.colpanes = [
        {
          first: pane.first,
          last: pane.last,
        },
      ];
      draginfo.thumbstatus.innerHTML =
        '<table cellspacing="0" cellpadding="0"><tr><td valign="top" style="' +
        scc.TCTDFSthumbstatusrownumStyle +
        '" class="' +
        scc.TCTDFSthumbstatusrownumClass +
        '"><div>msg</div></td><td valign="top"><div style="overflow:hidden;">preview</div></td></tr></table>';
      draginfo.thumbstatus.rowmsgele =
        draginfo.thumbstatus.firstChild.firstChild.firstChild.firstChild.firstChild;
      draginfo.thumbstatus.rowpreviewele =
        draginfo.thumbstatus.firstChild.firstChild.firstChild.childNodes[1].firstChild;
      editor.toplevel.appendChild(draginfo.thumbstatus);
      TableEditorSC.TCTDragFunctionRowSetStatus(draginfo, editor, editor.firstscrollingrow || 1);
    } else {
      if (scc.TCTDFSthumbstatushClass) draginfo.thumbstatus.className = scc.TCTDFSthumbstatushClass;
      TableEditorSC.setStyles(draginfo.thumbstatus, scc.TCTDFSthumbstatushStyle);
      draginfo.thumbstatus.style.top = control.controlborder + scc.TCTDFStopOffseth + 'px';
      draginfo.thumbstatus.style.left = draginfo.clientX + scc.TCTDFSleftOffseth + 'px';
      editor.toplevel.appendChild(draginfo.thumbstatus);
      draginfo.thumbstatus.innerHTML =
        scc.s_TCTDFthumbstatusPrefixh + TableEditorSC.rcColname(editor.firstscrollingcol);
    }
  };
  TableEditorSC.TCTDragFunctionRowSetStatus = function (draginfo, editor, row) {
    var scc = TableEditorSC.Constants;
    var msg = scc.s_TCTDFthumbstatusPrefixv + row + ' ';
    draginfo.thumbstatus.rowmsgele.innerHTML = msg;
    draginfo.thumbcontext.rowpanes = [
      {
        first: row,
        last: row,
      },
    ];
    draginfo.thumbrowshown = row;
    draginfo.thumbcontext.RenderSheet(draginfo.thumbstatus.rowpreviewele.firstChild, {
      type: 'html',
    });
  };
  TableEditorSC.TCTDragFunctionMove = function (event, draginfo, dobj) {
    var first, msg;
    var control = dobj.functionobj.control;
    var editor = control.editor;
    var scc = TableEditorSC.Constants;
    if (dobj.vertical) {
      if (draginfo.clientY > control.scrollareaend - draginfo.offsetY - control.thumbthickness + 2)
        draginfo.clientY = control.scrollareaend - draginfo.offsetY - control.thumbthickness + 2;
      if (draginfo.clientY < control.scrollareastart - draginfo.offsetY - 1)
        draginfo.clientY = control.scrollareastart - draginfo.offsetY - 1;
      draginfo.thumbstatus.style.top = draginfo.clientY + 'px';
      first =
        ((draginfo.clientY + draginfo.offsetY - control.scrollareastart + 1) /
          (control.scrollareasize - control.thumbthickness)) *
          (editor.context.sheetobj.attribs.lastrow - editor.lastnonscrollingrow) +
        editor.lastnonscrollingrow +
        1;
      first = Math.floor(first);
      if (first <= editor.lastnonscrollingrow) first = editor.lastnonscrollingrow + 1;
      if (first > editor.context.sheetobj.attribs.lastrow)
        first = editor.context.sheetobj.attribs.lastrow;
      if (first != draginfo.thumbrowshown) {
        TableEditorSC.TCTDragFunctionRowSetStatus(draginfo, editor, first);
      }
    } else {
      if (draginfo.clientX > control.scrollareaend - draginfo.offsetX - control.thumbthickness + 2)
        draginfo.clientX = control.scrollareaend - draginfo.offsetX - control.thumbthickness + 2;
      if (draginfo.clientX < control.scrollareastart - draginfo.offsetX - 1)
        draginfo.clientX = control.scrollareastart - draginfo.offsetX - 1;
      draginfo.thumbstatus.style.left = draginfo.clientX + 'px';
      first =
        ((draginfo.clientX + draginfo.offsetX - control.scrollareastart + 1) /
          (control.scrollareasize - control.thumbthickness)) *
          (editor.context.sheetobj.attribs.lastcol - editor.lastnonscrollingcol) +
        editor.lastnonscrollingcol +
        1;
      first = Math.floor(first);
      if (first <= editor.lastnonscrollingcol) first = editor.lastnonscrollingcol + 1;
      if (first > editor.context.sheetobj.attribs.lastcol)
        first = editor.context.sheetobj.attribs.lastcol;
      msg = scc.s_TCTDFthumbstatusPrefixh + TableEditorSC.rcColname(first);
      draginfo.thumbstatus.innerHTML = msg;
    }
    TableEditorSC.DragFunctionPosition(event, draginfo, dobj);
  };
  TableEditorSC.TCTDragFunctionStop = function (event, draginfo, dobj) {
    var first;
    var control = dobj.functionobj.control;
    var editor = control.editor;
    if (dobj.vertical) {
      first =
        ((draginfo.clientY + draginfo.offsetY - control.scrollareastart + 1) /
          (control.scrollareasize - control.thumbthickness)) *
          (editor.context.sheetobj.attribs.lastrow - editor.lastnonscrollingrow) +
        editor.lastnonscrollingrow +
        1;
      first = Math.floor(first);
      if (first <= editor.lastnonscrollingrow) first = editor.lastnonscrollingrow + 1;
      if (first > editor.context.sheetobj.attribs.lastrow)
        first = editor.context.sheetobj.attribs.lastrow;
      editor.context.SetRowPaneFirstLast(editor.context.rowpanes.length - 1, first, first + 1);
    } else {
      first =
        ((draginfo.clientX + draginfo.offsetX - control.scrollareastart + 1) /
          (control.scrollareasize - control.thumbthickness)) *
          (editor.context.sheetobj.attribs.lastcol - editor.lastnonscrollingcol) +
        editor.lastnonscrollingcol +
        1;
      first = Math.floor(first);
      if (first <= editor.lastnonscrollingcol) first = editor.lastnonscrollingcol + 1;
      if (first > editor.context.sheetobj.attribs.lastcol)
        first = editor.context.sheetobj.attribs.lastcol;
      editor.context.SetColPaneFirstLast(editor.context.colpanes.length - 1, first, first + 1);
    }
    editor.FitToEditTable();
    if (draginfo.thumbstatus.rowmsgele) draginfo.thumbstatus.rowmsgele = null;
    if (draginfo.thumbstatus.rowpreviewele) draginfo.thumbstatus.rowpreviewele = null;
    editor.toplevel.removeChild(draginfo.thumbstatus);
    draginfo.thumbstatus = null;
    editor.ScheduleRender();
  };
  TableEditorSC.DragInfo = {
    registeredElements: [],
    draggingElement: null,
    startX: 0,
    startY: 0,
    startZ: 0,
    clientX: 0,
    clientY: 0,
    offsetX: 0,
    offsetY: 0,
    relativeOffset: {
      left: 0,
      top: 0,
    },
  };
  TableEditorSC.DragRegister = function (element, vertical, horizontal, functionobj, parent) {
    var draginfo = TableEditorSC.DragInfo;
    if (!functionobj) {
      functionobj = {
        MouseDown: TableEditorSC.DragFunctionStart,
        MouseMove: TableEditorSC.DragFunctionPosition,
        MouseUp: TableEditorSC.DragFunctionPosition,
        Disabled: null,
      };
    }
    draginfo.registeredElements.push({
      element,
      vertical,
      horizontal,
      functionobj,
      parent,
    });
    element.addEventListener('mousedown', TableEditorSC.DragMouseDown, false);
  };
  TableEditorSC.DragUnregister = function (element) {
    var draginfo = TableEditorSC.DragInfo;
    var i;
    if (!element) return;
    for (i = 0; i < draginfo.registeredElements.length; i++) {
      if (draginfo.registeredElements[i].element == element) {
        draginfo.registeredElements.splice(i, 1);
        element.removeEventListener('mousedown', TableEditorSC.DragMouseDown, false);
        return;
      }
    }
    return;
  };
  TableEditorSC.DragMouseDown = function (event) {
    var e = event || window.event;
    var draginfo = TableEditorSC.DragInfo;
    var dobj = TableEditorSC.LookupElement(e.target || e.srcElement, draginfo.registeredElements);
    if (!dobj) return;
    if (dobj && dobj.functionobj && dobj.functionobj.Disabled) {
      if (dobj.functionobj.Disabled(e, draginfo, dobj)) {
        return;
      }
    }
    draginfo.draggingElement = dobj;
    if (dobj.parent) {
      draginfo.relativeOffset = TableEditorSC.GetElementPositionWithScroll(dobj.parent);
    }
    draginfo.clientX = e.clientX - draginfo.relativeOffset.left;
    draginfo.clientY = e.clientY - draginfo.relativeOffset.top;
    draginfo.startX = draginfo.clientX;
    draginfo.startY = draginfo.clientY;
    draginfo.startZ = dobj.element.style.zIndex;
    draginfo.offsetX = 0;
    draginfo.offsetY = 0;
    dobj.element.style.zIndex = '100';
    TableEditorSC.SetMouseMoveUp(
      TableEditorSC.DragMouseMove,
      TableEditorSC.DragMouseUp,
      dobj.element,
      e,
    );
    if (dobj && dobj.functionobj && dobj.functionobj.MouseDown)
      dobj.functionobj.MouseDown(e, draginfo, dobj);
    return false;
  };
  TableEditorSC.DragMouseMove = function (event) {
    var e = event || window.event;
    var draginfo = TableEditorSC.DragInfo;
    var dobj = draginfo.draggingElement;
    draginfo.clientX = e.clientX - draginfo.relativeOffset.left;
    draginfo.clientY = e.clientY - draginfo.relativeOffset.top;
    TableEditorSC.StopPropagation(e);
    if (dobj && dobj.functionobj && dobj.functionobj.MouseMove)
      dobj.functionobj.MouseMove(e, draginfo, dobj);
    return false;
  };
  TableEditorSC.DragMouseUp = function (event) {
    var e = event || window.event;
    var draginfo = TableEditorSC.DragInfo;
    var dobj = draginfo.draggingElement;
    draginfo.clientX = e.clientX - draginfo.relativeOffset.left;
    draginfo.clientY = e.clientY - draginfo.relativeOffset.top;
    dobj.element.style.zIndex = draginfo.startZ;
    if (dobj && dobj.functionobj && dobj.functionobj.MouseUp)
      dobj.functionobj.MouseUp(e, draginfo, dobj);
    TableEditorSC.RemoveMouseMoveUp(
      TableEditorSC.DragMouseMove,
      TableEditorSC.DragMouseUp,
      dobj.element,
      e,
    );
    draginfo.draggingElement = null;
    return false;
  };
  TableEditorSC.DragFunctionStart = function (event, draginfo, dobj) {
    var element = dobj.functionobj.positionobj || dobj.element;
    draginfo.offsetY = parseInt(element.style.top) - draginfo.clientY;
    draginfo.offsetX = parseInt(element.style.left) - draginfo.clientX;
  };
  TableEditorSC.DragFunctionPosition = function (event, draginfo, dobj) {
    var element = dobj.functionobj.positionobj || dobj.element;
    if (dobj.vertical) element.style.top = draginfo.clientY + draginfo.offsetY + 'px';
    if (dobj.horizontal) element.style.left = draginfo.clientX + draginfo.offsetX + 'px';
  };
  TableEditorSC.ButtonInfo = {
    registeredElements: [],
    buttonElement: null,
    doingHover: false,
    buttonDown: false,
    timer: null,
    relativeOffset: null,
    clientX: 0,
    clientY: 0,
  };
  TableEditorSC.ButtonRegister = function (editor, element, paramobj, functionobj) {
    var buttoninfo = TableEditorSC.ButtonInfo;
    if (!paramobj) paramobj = {};
    buttoninfo.registeredElements.push({
      name: paramobj.name,
      element,
      editor,
      normalstyle: paramobj.normalstyle,
      hoverstyle: paramobj.hoverstyle,
      downstyle: paramobj.downstyle,
      repeatwait: paramobj.repeatwait,
      repeatinterval: paramobj.repeatinterval,
      functionobj,
    });
    element.addEventListener('mousedown', TableEditorSC.ButtonMouseDown, false);
    element.addEventListener('mouseover', TableEditorSC.ButtonMouseOver, false);
    element.addEventListener('mouseout', TableEditorSC.ButtonMouseOut, false);
    return;
  };
  TableEditorSC.ButtonMouseOver = function (event) {
    var e = event || window.event;
    var buttoninfo = TableEditorSC.ButtonInfo;
    var bobj = TableEditorSC.LookupElement(e.target || e.srcElement, buttoninfo.registeredElements);
    if (!bobj) return;
    if (buttoninfo.buttonDown) {
      if (buttoninfo.buttonElement == bobj) {
        buttoninfo.doingHover = true;
      }
      return;
    }
    if (buttoninfo.buttonElement && buttoninfo.buttonElement != bobj && buttoninfo.doingHover) {
      TableEditorSC.setStyles(
        buttoninfo.buttonElement.element,
        buttoninfo.buttonElement.normalstyle,
      );
    }
    buttoninfo.buttonElement = bobj;
    buttoninfo.doingHover = true;
    TableEditorSC.setStyles(bobj.element, bobj.hoverstyle);
    if (bobj && bobj.functionobj && bobj.functionobj.MouseOver)
      bobj.functionobj.MouseOver(e, buttoninfo, bobj);
    return;
  };
  TableEditorSC.ButtonMouseOut = function (event) {
    var e = event || window.event;
    var buttoninfo = TableEditorSC.ButtonInfo;
    if (buttoninfo.buttonDown) {
      buttoninfo.doingHover = false;
      return;
    }
    var bobj = TableEditorSC.LookupElement(e.target || e.srcElement, buttoninfo.registeredElements);
    if (buttoninfo.doingHover) {
      if (buttoninfo.buttonElement)
        TableEditorSC.setStyles(
          buttoninfo.buttonElement.element,
          buttoninfo.buttonElement.normalstyle,
        );
      buttoninfo.buttonElement = null;
      buttoninfo.doingHover = false;
    }
    if (bobj && bobj.functionobj && bobj.functionobj.MouseOut)
      bobj.functionobj.MouseOut(e, buttoninfo, bobj);
    return;
  };
  TableEditorSC.ButtonMouseDown = function (event) {
    var e = event || window.event;
    var buttoninfo = TableEditorSC.ButtonInfo;
    var bobj = TableEditorSC.LookupElement(e.target || e.srcElement, buttoninfo.registeredElements);
    if (!bobj) return;
    if (bobj && bobj.functionobj && bobj.functionobj.Disabled) {
      if (bobj.functionobj.Disabled(e, buttoninfo, bobj)) {
        return;
      }
    }
    buttoninfo.buttonElement = bobj;
    buttoninfo.buttonDown = true;
    TableEditorSC.setStyles(bobj.element, buttoninfo.buttonElement.downstyle);
    document.addEventListener('mouseup', TableEditorSC.ButtonMouseUp, true);
    TableEditorSC.StopPropagation(e);
    buttoninfo.relativeOffset = TableEditorSC.GetElementPositionWithScroll(bobj.editor.toplevel);
    buttoninfo.clientX = e.clientX - buttoninfo.relativeOffset.left;
    buttoninfo.clientY = e.clientY - buttoninfo.relativeOffset.top;
    if (bobj && bobj.functionobj && bobj.functionobj.MouseDown)
      bobj.functionobj.MouseDown(e, buttoninfo, bobj);
    if (bobj.repeatwait) {
      buttoninfo.timer = window.setTimeout(TableEditorSC.ButtonRepeat, bobj.repeatwait);
    }
    return;
  };
  TableEditorSC.ButtonMouseUp = function (event) {
    var e = event || window.event;
    var buttoninfo = TableEditorSC.ButtonInfo;
    var bobj = buttoninfo.buttonElement;
    if (buttoninfo.timer) {
      window.clearTimeout(buttoninfo.timer);
      buttoninfo.timer = null;
    }
    if (!buttoninfo.buttonDown) return;
    TableEditorSC.StopPropagation(e);
    document.removeEventListener('mouseup', TableEditorSC.ButtonMouseUp, true);
    if (buttoninfo.buttonElement.downstyle) {
      if (buttoninfo.doingHover)
        TableEditorSC.setStyles(bobj.element, buttoninfo.buttonElement.hoverstyle);
      else TableEditorSC.setStyles(bobj.element, buttoninfo.buttonElement.normalstyle);
    }
    buttoninfo.buttonDown = false;
    if (bobj && bobj.functionobj && bobj.functionobj.MouseUp)
      bobj.functionobj.MouseUp(e, buttoninfo, bobj);
  };
  TableEditorSC.ButtonRepeat = function () {
    var buttoninfo = TableEditorSC.ButtonInfo;
    var bobj = buttoninfo.buttonElement;
    if (!bobj) return;
    if (bobj && bobj.functionobj && bobj.functionobj.Repeat)
      bobj.functionobj.Repeat(null, buttoninfo, bobj);
    buttoninfo.timer = window.setTimeout(TableEditorSC.ButtonRepeat, bobj.repeatinterval || 100);
  };
  TableEditorSC.MouseWheelInfo = { registeredElements: [] };
  TableEditorSC.MouseWheelRegister = function (element, functionobj) {
    var mousewheelinfo = TableEditorSC.MouseWheelInfo;
    mousewheelinfo.registeredElements.push({
      element,
      functionobj,
    });
    element.addEventListener('DOMMouseScroll', TableEditorSC.ProcessMouseWheel, false);
    element.addEventListener('mousewheel', TableEditorSC.ProcessMouseWheel, false);
    return;
  };
  TableEditorSC.ProcessMouseWheel = function (e) {
    var event = e || window.event;
    var delta;
    if (TableEditorSC.Keyboard.passThru) return;
    var mousewheelinfo = TableEditorSC.MouseWheelInfo;
    var ele = event.target || event.srcElement;
    var wobj;
    for (wobj = null; !wobj && ele; ele = ele.parentNode) {
      wobj = TableEditorSC.LookupElement(ele, mousewheelinfo.registeredElements);
    }
    if (!wobj) return;
    if (event.wheelDelta) {
      delta = event.wheelDelta / 120;
    } else delta = -event.detail / 3;
    if (!delta) delta = 0;
    if (wobj.functionobj && wobj.functionobj.WheelMove)
      wobj.functionobj.WheelMove(event, delta, mousewheelinfo, wobj);
    if (event.preventDefault) event.preventDefault();
    event.returnValue = false;
  };
  TableEditorSC.keyboardTables = {
    specialKeysCommon: {
      8: '[backspace]',
      9: '[tab]',
      13: '[enter]',
      25: '[tab]',
      27: '[esc]',
      33: '[pgup]',
      34: '[pgdn]',
      35: '[end]',
      36: '[home]',
      37: '[aleft]',
      38: '[aup]',
      39: '[aright]',
      40: '[adown]',
      45: '[ins]',
      46: '[del]',
      113: '[f2]',
    },
    controlKeysIE: {
      65: '[ctrl-a]',
      67: '[ctrl-c]',
      83: '[ctrl-s]',
      86: '[ctrl-v]',
      88: '[ctrl-x]',
      90: '[ctrl-z]',
    },
    specialKeysSafari: {
      8: '[backspace]',
      9: '[tab]',
      13: '[enter]',
      25: '[tab]',
      27: '[esc]',
      63232: '[aup]',
      63233: '[adown]',
      63234: '[aleft]',
      63235: '[aright]',
      63272: '[del]',
      63273: '[home]',
      63275: '[end]',
      63276: '[pgup]',
      63277: '[pgdn]',
      63237: '[f2]',
    },
    controlKeysSafari: {
      97: '[ctrl-a]',
      99: '[ctrl-c]',
      115: '[ctrl-s]',
      118: '[ctrl-v]',
      120: '[ctrl-x]',
      122: '[ctrl-z]',
    },
    ignoreKeysSafari: {
      63236: '[f1]',
      63238: '[f3]',
      63239: '[f4]',
      63240: '[f5]',
      63241: '[f6]',
      63242: '[f7]',
      63243: '[f8]',
      63244: '[f9]',
      63245: '[f10]',
      63246: '[f11]',
      63247: '[f12]',
      63289: '[numlock]',
    },
    specialKeysFirefox: {
      8: '[backspace]',
      9: '[tab]',
      13: '[enter]',
      25: '[tab]',
      27: '[esc]',
      33: '[pgup]',
      34: '[pgdn]',
      35: '[end]',
      36: '[home]',
      37: '[aleft]',
      38: '[aup]',
      39: '[aright]',
      40: '[adown]',
      45: '[ins]',
      46: '[del]',
      113: '[f2]',
    },
    controlKeysFirefox: {
      97: '[ctrl-a]',
      99: '[ctrl-c]',
      115: '[ctrl-s]',
      118: '[ctrl-v]',
      120: '[ctrl-x]',
      122: '[ctrl-z]',
    },
  };
  TableEditorSC.Keyboard = {
    areListener: false,
    focusTable: null,
    passThru: null,
    didProcessKey: false,
    statusFromProcessKey: false,
    repeatingKeyPress: false,
    chForProcessKey: '',
  };
  TableEditorSC.KeyboardSetFocus = function (editor) {
    TableEditorSC.Keyboard.focusTable = editor;
    if (!TableEditorSC.Keyboard.areListener) {
      document.onkeydown = TableEditorSC.ProcessKeyDown;
      document.onkeypress = TableEditorSC.ProcessKeyPress;
      TableEditorSC.Keyboard.areListener = true;
    }
    if (TableEditorSC.Keyboard.passThru) {
      if (TableEditorSC.Keyboard.passThru.blur) {
        TableEditorSC.Keyboard.passThru.blur();
      }
      TableEditorSC.Keyboard.passThru = null;
    }
    window.focus();
  };
  TableEditorSC.KeyboardFocus = function () {
    TableEditorSC.Keyboard.passThru = null;
    window.focus();
  };
  TableEditorSC.ProcessKeyDown = function (e) {
    var kt = TableEditorSC.keyboardTables;
    kt.didProcessKey = false;
    kt.statusFromProcessKey = false;
    kt.repeatingKeyPress = false;
    var ch = '';
    var status = true;
    if (TableEditorSC._app) return;
    if (TableEditorSC.Keyboard.passThru) return;
    e = e || window.event;
    if (e.which == undefined) {
      ch = kt.specialKeysCommon[e.keyCode];
      if (!ch) {
        if (e.ctrlKey) {
          ch = kt.controlKeysIE[e.keyCode];
        }
        if (!ch) return true;
      }
      status = TableEditorSC.ProcessKey(ch, e);
      if (!status) {
        if (e.preventDefault) e.preventDefault();
        e.returnValue = false;
      }
    } else {
      ch = kt.specialKeysCommon[e.keyCode];
      if (!ch) {
        if (e.ctrlKey || e.metaKey) {
          ch = kt.controlKeysIE[e.keyCode];
        }
        if (!ch) return true;
      }
      status = TableEditorSC.ProcessKey(ch, e);
      kt.didProcessKey = true;
      kt.statusFromProcessKey = status;
      kt.chForProcessKey = ch;
    }
    return status;
  };
  TableEditorSC.ProcessKeyPress = function (e) {
    var kt = TableEditorSC.keyboardTables;
    var ch = '';
    e = e || window.event;
    if (TableEditorSC._app) return;
    if (TableEditorSC.Keyboard.passThru) return;
    if (kt.didProcessKey) {
      if (kt.repeatingKeyPress) {
        return TableEditorSC.ProcessKey(kt.chForProcessKey, e);
      } else {
        kt.repeatingKeyPress = true;
        return kt.statusFromProcessKey;
      }
    }
    if (e.which == undefined) {
      ch = String.fromCharCode(e.keyCode);
    } else {
      if (!e.which) return false;
      if (e.keyCode == e.charCode) {
        ch = kt.specialKeysSafari[e.keyCode];
        if (!ch) {
          if (kt.ignoreKeysSafari[e.keyCode]) return true;
          if (e.metaKey) {
            ch = kt.controlKeysSafari[e.keyCode];
          } else {
            ch = String.fromCharCode(e.which);
          }
        }
      } else {
        if (kt.specialKeysFirefox[e.keyCode]) {
          return true;
        }
        ch = String.fromCharCode(e.which);
        if (e.ctrlKey || e.metaKey) {
          ch = kt.controlKeysFirefox[e.which];
        }
      }
    }
    var status = TableEditorSC.ProcessKey(ch, e);
    if (!status) {
      if (e.preventDefault) e.preventDefault();
      e.returnValue = false;
    }
    return status;
  };
  TableEditorSC.ProcessKey = function (ch, e) {
    var ft = TableEditorSC.Keyboard.focusTable;
    if (!ft) return true;
    return ft.EditorProcessKey(ch, e);
  };

  // In-place TypeScript conversion of the SocialCalc number-formatting module.
  // Ambient API types live in formatnumber2.d.ts (referenced by dist/SocialCalc.d.ts).
  // Vite+ strips types with Oxc before UMD concat — no runtime tax.
  //
  /*
// SocialCalc Number Formatting Library
//
// Part of the SocialCalc package.
//
// (c) Copyright 2008 Socialtext, Inc.
// All Rights Reserved.
//
// The contents of this file are subject to the Artistic License 2.0; you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at http://socialcalc.org/licenses/al-20/.
//
// Some of the other files in the SocialCalc package are licensed under
// different licenses. Please note the licenses of the modules you use.
//
// Code History:
//
// Initially coded by Dan Bricklin of Software Garden, Inc., for Socialtext, Inc.
// Based in part on the SocialCalc 1.1.0 code written in Perl.
// The SocialCalc 1.1.0 code was:
//    Portions (c) Copyright 2005, 2006, 2007 Software Garden, Inc.
//    All Rights Reserved.
//    Portions (c) Copyright 2007 Socialtext, Inc.
//    All Rights Reserved.
// The Perl SocialCalc started as modifications to the wikiCalc(R) program, version 1.0.
// wikiCalc 1.0 was written by Software Garden, Inc.
// Unless otherwise specified, referring to "SocialCalc" in comments refers to this
// JavaScript version of the code, not the SocialCalc Perl code.
//
*/

  // Redundant `if (!SocialCalc)` guard removed — SocialCalc is defined by
  // socialcalcconstants.js earlier in the concatenated UMD bundle.

  // Implementation-only mutable view of FormatNumber.
  // Public formatnumber2.d.ts keeps `const` members for consumers; this module
  // still performs the legacy one-time progressive assignment at load.

  SocialCalc.FormatNumber = {};
  const FormatNumberMut = SocialCalc.FormatNumber;
  FormatNumberMut.format_definitions = {};
  FormatNumberMut.separatorchar = ',';
  FormatNumberMut.decimalchar = '.';
  FormatNumberMut.daynames = [
    'Sunday',
    'Monday',
    'Tuesday',
    'Wednesday',
    'Thursday',
    'Friday',
    'Saturday',
  ];
  FormatNumberMut.daynames3 = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
  FormatNumberMut.monthnames3 = [
    'Jan',
    'Feb',
    'Mar',
    'Apr',
    'May',
    'Jun',
    'Jul',
    'Aug',
    'Sep',
    'Oct',
    'Nov',
    'Dec',
  ];
  FormatNumberMut.monthnames = [
    'January',
    'February',
    'March',
    'April',
    'May',
    'June',
    'July',
    'August',
    'September',
    'October',
    'November',
    'December',
  ];
  FormatNumberMut.allowedcolors = {
    BLACK: '#000000',
    BLUE: '#0000FF',
    CYAN: '#00FFFF',
    GREEN: '#00FF00',
    MAGENTA: '#FF00FF',
    RED: '#FF0000',
    WHITE: '#FFFFFF',
    YELLOW: '#FFFF00',
  };
  FormatNumberMut.alloweddates = {
    H: 'h]',
    M: 'm]',
    MM: 'mm]',
    S: 's]',
    SS: 'ss]',
  };
  FormatNumberMut.commands = {
    copy: 1,
    color: 2,
    integer_placeholder: 3,
    fraction_placeholder: 4,
    decimal: 5,
    currency: 6,
    general: 7,
    separator: 8,
    date: 9,
    comparison: 10,
    section: 11,
    style: 12,
  };
  FormatNumberMut.datevalues = {
    julian_offset: 2415019,
    seconds_in_a_day: 24 * 60 * 60,
    seconds_in_an_hour: 60 * 60,
  };
  FormatNumberMut.formatNumberWithFormat = function (rawvalue, format_string, currency_char) {
    var scc = SocialCalc.Constants;
    var scfn = SocialCalc.FormatNumber;
    var op = 0,
      operandstr = '',
      fromend = 0,
      cval = 0,
      operandstrlc = '';
    var startval = 0,
      estartval = 0;
    var hrs = 0,
      mins = 0,
      secs = 0,
      ehrs = 0,
      emins = 0,
      esecs = 0;
    var ampmstr;
    var ymd = {
      year: 0,
      month: 0,
      day: 0,
    };
    var minOK = 0,
      mspos = 0;
    var result = '';
    var thisformat;
    var section = 0,
      gotcomparison = 0,
      compop = '',
      compval = 0,
      cpos = 0,
      oppos = 0;
    var sectioninfo;
    var i = 0,
      decimalscale = 0,
      scaledvalue = 0,
      strvalue = '',
      strparts = null,
      integervalue = '',
      fractionvalue = '';
    var integerdigits2 = 0,
      integerpos = 0,
      fractionpos = 0,
      textcolor = '',
      textstyle = '',
      separatorchar = '',
      decimalchar = '';
    var value = 0;
    if (typeof rawvalue == 'string' && !rawvalue.length) return '';
    value = Number(rawvalue);
    if (!isFinite(value)) {
      if (typeof rawvalue == 'string') {
        return scfn.formatTextWithFormat(rawvalue, format_string);
      } else {
        return 'NaN';
      }
    }
    rawvalue = value;
    var negativevalue = value < 0 ? 1 : 0;
    if (negativevalue) value = -value;
    var zerovalue = value == 0 ? 1 : 0;
    currency_char = currency_char || scc.FormatNumber_defaultCurrency;
    scfn.parse_format_string(scfn.format_definitions, format_string);
    thisformat = scfn.format_definitions[format_string];
    if (!thisformat) throw 'Format not parsed error!';
    section = thisformat.sectioninfo.length - 1;
    if (thisformat.hascomparison) {
      section = 0;
      gotcomparison = 0;
      for (cpos = 0; ; cpos++) {
        op = thisformat.operators[cpos];
        operandstr = thisformat.operands[cpos];
        if (!op) {
          if (gotcomparison) {
            format_string = 'General';
            scfn.parse_format_string(scfn.format_definitions, format_string);
            thisformat = scfn.format_definitions[format_string];
            section = 0;
          }
          break;
        }
        if (op == scfn.commands.section) {
          if (!gotcomparison) {
            break;
          }
          gotcomparison = 0;
          section++;
          continue;
        }
        if (op == scfn.commands.comparison) {
          i = operandstr.indexOf(':');
          compop = operandstr.substring(0, i);
          compval = Number(operandstr.substring(i + 1));
          if (
            (compop == '<' && rawvalue < compval) ||
            (compop == '<=' && rawvalue <= compval) ||
            (compop == '=' && rawvalue == compval) ||
            (compop == '<>' && rawvalue != compval) ||
            (compop == '>=' && rawvalue >= compval) ||
            (compop == '>' && rawvalue > compval)
          ) {
            break;
          }
          gotcomparison = 1;
        }
      }
    } else if (section > 0) {
      if (section == 1) {
        if (negativevalue) {
          negativevalue = 0;
          section = 1;
        } else {
          section = 0;
        }
      } else if (section == 2 || section == 3) {
        if (negativevalue) {
          negativevalue = 0;
          section = 1;
        } else if (zerovalue) {
          section = 2;
        } else {
          section = 0;
        }
      }
    }
    sectioninfo = thisformat.sectioninfo[section];
    var sectionCommas = sectioninfo.commas || 0;
    var sectionPercent = sectioninfo.percent || 0;
    var sectionFractionDigits = sectioninfo.fractiondigits || 0;
    var sectionIntegerDigits = sectioninfo.integerdigits || 0;
    var sectionStart = sectioninfo.sectionstart || 0;
    if (sectionCommas > 0) {
      for (i = 0; i < sectionCommas; i++) {
        value /= 1e3;
      }
    }
    if (sectionPercent > 0) {
      for (i = 0; i < sectionPercent; i++) {
        value *= 100;
      }
    }
    decimalscale = 1;
    for (i = 0; i < sectionFractionDigits; i++) {
      decimalscale *= 10;
    }
    scaledvalue = Math.floor(value * decimalscale + 0.5);
    scaledvalue = scaledvalue / decimalscale;
    if (!isFinite(scaledvalue)) return 'NaN';
    strvalue = scaledvalue + '';
    if (scaledvalue == 0 && (sectioninfo.fractiondigits || sectioninfo.integerdigits)) {
      negativevalue = 0;
    }
    if (strvalue.indexOf('e') >= 0) {
      if (sectionPercent > 0) {
        var scipct = (negativevalue ? '-' : '') + strvalue;
        for (i = 0; i < sectionPercent; i++) {
          scipct += '%';
        }
        return scipct;
      }
      return rawvalue + '';
    }
    strparts = strvalue.match(/^\+{0,1}(\d*)(?:\.(\d*)){0,1}$/);
    integervalue = strparts[1];
    if (!integervalue || integervalue == '0') integervalue = '';
    fractionvalue = strparts[2];
    if (!fractionvalue) fractionvalue = '';
    if (sectioninfo.hasdate) {
      if (rawvalue < 0) {
        return '??-???-??&nbsp;??:??:??';
      }
      startval = (rawvalue - Math.floor(rawvalue)) * scfn.datevalues.seconds_in_a_day;
      estartval = rawvalue * scfn.datevalues.seconds_in_a_day;
      hrs = Math.floor(startval / scfn.datevalues.seconds_in_an_hour);
      ehrs = Math.floor(estartval / scfn.datevalues.seconds_in_an_hour);
      startval = startval - hrs * scfn.datevalues.seconds_in_an_hour;
      mins = Math.floor(startval / 60);
      emins = Math.floor(estartval / 60);
      secs = startval - mins * 60;
      decimalscale = 1;
      for (i = 0; i < sectionFractionDigits; i++) {
        decimalscale *= 10;
      }
      secs = Math.floor(secs * decimalscale + 0.5);
      secs = secs / decimalscale;
      esecs = Math.floor(estartval * decimalscale + 0.5);
      esecs = esecs / decimalscale;
      if (secs >= 60) {
        secs = 0;
        mins++;
        emins++;
        if (mins >= 60) {
          mins = 0;
          hrs++;
          ehrs++;
          if (hrs >= 24) {
            hrs = 0;
            rawvalue++;
          }
        }
      }
      fractionvalue = secs - Math.floor(secs) + '';
      fractionvalue = fractionvalue.substring(2);
      ymd = SocialCalc.FormatNumber.convert_date_julian_to_gregorian(
        Math.floor(rawvalue + scfn.datevalues.julian_offset),
      );
      minOK = 0;
      mspos = sectionStart;
      for (; ; mspos++) {
        op = thisformat.operators[mspos];
        operandstr = thisformat.operands[mspos];
        if (!op) break;
        if (op == scfn.commands.section) break;
        if (op == scfn.commands.date) {
          if (
            (operandstr.toLowerCase() == 'am/pm' || operandstr.toLowerCase() == 'a/p') &&
            !ampmstr
          ) {
            if (hrs >= 12) {
              hrs -= 12;
              ampmstr =
                operandstr.toLowerCase() == 'a/p' ? scc.s_FormatNumber_pm1 : scc.s_FormatNumber_pm;
            } else {
              ampmstr =
                operandstr.toLowerCase() == 'a/p' ? scc.s_FormatNumber_am1 : scc.s_FormatNumber_am;
            }
            if (operandstr.indexOf(ampmstr) < 0) ampmstr = ampmstr.toLowerCase();
          }
          if (minOK && (operandstr == 'm' || operandstr == 'mm')) {
            thisformat.operands[mspos] += 'in';
          }
          if (operandstr.charAt(0) == 'h') {
            minOK = 1;
          } else {
            minOK = 0;
          }
        } else if (op != scfn.commands.copy) {
          minOK = 0;
        }
      }
      minOK = 0;
      for (--mspos; ; mspos--) {
        op = thisformat.operators[mspos];
        operandstr = thisformat.operands[mspos];
        if (!op) break;
        if (op == scfn.commands.section) break;
        if (op == scfn.commands.date) {
          if (minOK && (operandstr == 'm' || operandstr == 'mm')) {
            thisformat.operands[mspos] += 'in';
          }
          if (operandstr == 'ss') {
            minOK = 1;
          } else {
            minOK = 0;
          }
        } else if (op != scfn.commands.copy) {
          minOK = 0;
        }
      }
    }
    integerdigits2 = 0;
    integerpos = 0;
    fractionpos = 0;
    textcolor = '';
    textstyle = '';
    separatorchar = scc.FormatNumber_separatorchar;
    if (separatorchar.indexOf(' ') >= 0) separatorchar = separatorchar.replace(/ /g, '&nbsp;');
    decimalchar = scc.FormatNumber_decimalchar;
    if (decimalchar.indexOf(' ') >= 0) decimalchar = decimalchar.replace(/ /g, '&nbsp;');
    oppos = sectionStart;
    while ((op = thisformat.operators[oppos])) {
      operandstr = thisformat.operands[oppos++];
      if (op == scfn.commands.copy) {
        result += operandstr;
      } else if (op == scfn.commands.color) {
        textcolor = operandstr;
      } else if (op == scfn.commands.style) {
        textstyle = operandstr;
      } else if (op == scfn.commands.integer_placeholder) {
        if (negativevalue) {
          result += '-';
          negativevalue = 0;
        }
        integerdigits2++;
        if (integerdigits2 == 1) {
          if (integervalue.length > sectionIntegerDigits) {
            for (; integerpos < integervalue.length - sectionIntegerDigits; integerpos++) {
              result += integervalue.charAt(integerpos);
              if (sectioninfo.thousandssep) {
                fromend = integervalue.length - integerpos - 1;
                if (fromend > 2 && fromend % 3 == 0) {
                  result += separatorchar;
                }
              }
            }
          }
        }
        if (
          integervalue.length < sectionIntegerDigits &&
          integerdigits2 <= sectionIntegerDigits - integervalue.length
        ) {
          if (operandstr == '0' || operandstr == '?') {
            result += operandstr == '0' ? '0' : '&nbsp;';
            if (sectioninfo.thousandssep) {
              fromend = sectionIntegerDigits - integerdigits2;
              if (fromend > 2 && fromend % 3 == 0) {
                result += separatorchar;
              }
            }
          }
        } else {
          result += integervalue.charAt(integerpos);
          if (sectioninfo.thousandssep) {
            fromend = integervalue.length - integerpos - 1;
            if (fromend > 2 && fromend % 3 == 0) {
              result += separatorchar;
            }
          }
          integerpos++;
        }
      } else if (op == scfn.commands.fraction_placeholder) {
        if (fractionpos >= fractionvalue.length) {
          if (operandstr == '0' || operandstr == '?') {
            result += operandstr == '0' ? '0' : '&nbsp;';
          }
        } else {
          result += fractionvalue.charAt(fractionpos);
        }
        fractionpos++;
      } else if (op == scfn.commands.decimal) {
        if (negativevalue) {
          result += '-';
          negativevalue = 0;
        }
        result += decimalchar;
      } else if (op == scfn.commands.currency) {
        if (negativevalue) {
          result += '-';
          negativevalue = 0;
        }
        result += (operandstr === '$' ? currency_char : operandstr) || operandstr;
      } else if (op == scfn.commands.general) {
        if (value != 0) {
          var factor = Math.floor(Math.LOG10E * Math.log(value));
          factor = Math.pow(10, 13 - factor);
          value = Math.floor(factor * value + 0.5) / factor;
          if (!isFinite(value)) return 'NaN';
        }
        if (negativevalue) {
          result += '-';
        }
        strvalue = value + '';
        if (strvalue.indexOf('e') >= 0) {
          result += strvalue;
          continue;
        }
        strparts = strvalue.match(/^\+{0,1}(\d*)(?:\.(\d*)){0,1}$/);
        integervalue = strparts[1];
        if (!integervalue || integervalue == '0') integervalue = '';
        fractionvalue = strparts[2];
        if (!fractionvalue) fractionvalue = '';
        integerpos = 0;
        fractionpos = 0;
        if (integervalue.length) {
          for (; integerpos < integervalue.length; integerpos++) {
            result += integervalue.charAt(integerpos);
            if (sectioninfo.thousandssep) {
              fromend = integervalue.length - integerpos - 1;
              if (fromend > 2 && fromend % 3 == 0) {
                result += separatorchar;
              }
            }
          }
        } else {
          result += '0';
        }
        if (fractionvalue.length) {
          result += decimalchar;
          for (; fractionpos < fractionvalue.length; fractionpos++) {
            result += fractionvalue.charAt(fractionpos);
          }
        }
      } else if (op == scfn.commands.date) {
        operandstrlc = operandstr.toLowerCase();
        if (operandstrlc == 'y' || operandstrlc == 'yy') {
          result += (ymd.year + '').substring(2);
        } else if (operandstrlc == 'yyyy') {
          result += ymd.year + '';
        } else if (operandstrlc == 'd') {
          result += ymd.day + '';
        } else if (operandstrlc == 'dd') {
          cval = 1e3 + ymd.day;
          result += (cval + '').substr(2);
        } else if (operandstrlc == 'ddd') {
          cval = Math.floor(rawvalue + 6) % 7;
          result += scc.s_FormatNumber_daynames3[cval];
        } else if (operandstrlc == 'dddd') {
          cval = Math.floor(rawvalue + 6) % 7;
          result += scc.s_FormatNumber_daynames[cval];
        } else if (operandstrlc == 'm') {
          result += ymd.month + '';
        } else if (operandstrlc == 'mm') {
          cval = 1e3 + ymd.month;
          result += (cval + '').substr(2);
        } else if (operandstrlc == 'mmm') {
          result += scc.s_FormatNumber_monthnames3[ymd.month - 1];
        } else if (operandstrlc == 'mmmm') {
          result += scc.s_FormatNumber_monthnames[ymd.month - 1];
        } else if (operandstrlc == 'mmmmm') {
          result += scc.s_FormatNumber_monthnames[ymd.month - 1].charAt(0);
        } else if (operandstrlc == 'h') {
          result += hrs + '';
        } else if (operandstrlc == 'h]') {
          result += ehrs + '';
        } else if (operandstrlc == 'mmin') {
          cval = 1e3 + mins + '';
          result += String(cval).substr(2);
        } else if (operandstrlc == 'mm]') {
          if (emins < 100) {
            cval = 1e3 + emins + '';
            result += String(cval).substr(2);
          } else {
            result += emins + '';
          }
        } else if (operandstrlc == 'min') {
          result += mins + '';
        } else if (operandstrlc == 'm]') {
          result += emins + '';
        } else if (operandstrlc == 'hh') {
          cval = 1e3 + hrs + '';
          result += String(cval).substr(2);
        } else if (operandstrlc == 's') {
          cval = Math.floor(secs);
          result += cval + '';
        } else if (operandstrlc == 'ss') {
          cval = 1e3 + Math.floor(secs) + '';
          result += String(cval).substr(2);
        } else if (operandstrlc == 'am/pm' || operandstrlc == 'a/p') {
          result += ampmstr || '';
        } else if (operandstrlc == 'ss]') {
          if (esecs < 100) {
            cval = 1e3 + Math.floor(esecs) + '';
            result += String(cval).substr(2);
          } else {
            cval = Math.floor(esecs);
            result += cval + '';
          }
        }
      } else if (op == scfn.commands.section) break;
      else if (op == scfn.commands.comparison) continue;
      else result += '!! Parse error !!';
    }
    if (textcolor) {
      result = '<span style="color:' + textcolor + ';">' + result + '</span>';
    }
    if (textstyle) {
      result = '<span style="' + textstyle + ';">' + result + '</span>';
    }
    return result;
  };
  FormatNumberMut.formatTextWithFormat = function (rawvalue, format_string) {
    var scfn = SocialCalc.FormatNumber;
    var value = rawvalue + '';
    var result = '';
    var section;
    var sectioninfo;
    var op, oppos;
    var operandstr;
    var thisformat;
    var textcolor = '';
    var textstyle = '';
    scfn.parse_format_string(scfn.format_definitions, format_string);
    thisformat = scfn.format_definitions[format_string];
    if (!thisformat) throw 'Format not parsed error!';
    section = thisformat.sectioninfo.length - 1;
    if (section == 0) {
      section = 0;
    } else if (section == 3) {
      section = 3;
    } else {
      return value;
    }
    sectioninfo = thisformat.sectioninfo[section];
    oppos = sectioninfo.sectionstart || 0;
    while ((op = thisformat.operators[oppos])) {
      operandstr = thisformat.operands[oppos++];
      if (op == scfn.commands.copy) {
        if (operandstr == '@') {
          result += value;
        } else {
          result += operandstr.replace(/ /g, '&nbsp;');
        }
      } else if (op == scfn.commands.color) {
        textcolor = operandstr;
      } else if (op == scfn.commands.style) {
        textstyle = operandstr;
      }
    }
    if (textcolor) {
      result = '<span style="color:' + textcolor + ';">' + result + '</span>';
    }
    if (textstyle) {
      result = '<span style="' + textstyle + ';">' + result + '</span>';
    }
    return result;
  };
  FormatNumberMut.parse_format_string = function (format_defs, format_string) {
    var scfn = SocialCalc.FormatNumber;
    var thisformat;
    var section;
    var sectioninfo;
    var integerpart = 1;
    var lastwasinteger;
    var lastwasslash;
    var lastwasasterisk;
    var lastwasunderscore;
    var inquote,
      quotestr = '';
    var inbracket,
      bracketstr = '',
      bracketdata;
    var ingeneral;
    var ampmstr, part;
    var indate;
    var chpos;
    var ch;
    if (format_defs[format_string]) return;
    thisformat = {
      operators: [],
      operands: [],
      sectioninfo: [{}],
    };
    format_defs[format_string] = thisformat;
    section = 0;
    sectioninfo = thisformat.sectioninfo[section];
    sectioninfo.sectionstart = 0;
    sectioninfo.integerdigits = 0;
    sectioninfo.fractiondigits = 0;
    sectioninfo.commas = 0;
    sectioninfo.percent = 0;
    for (chpos = 0; chpos < format_string.length; chpos++) {
      ch = format_string.charAt(chpos);
      if (inquote) {
        if (ch == '"') {
          inquote = 0;
          thisformat.operators.push(scfn.commands.copy);
          thisformat.operands.push(quotestr);
          continue;
        }
        quotestr += ch;
        continue;
      }
      if (inbracket) {
        if (ch == ']') {
          inbracket = 0;
          bracketdata = SocialCalc.FormatNumber.parse_format_bracket(bracketstr);
          if (bracketdata.operator == scfn.commands.separator) {
            sectioninfo.thousandssep = 1;
            continue;
          }
          if (bracketdata.operator == scfn.commands.date) {
            sectioninfo.hasdate = 1;
          }
          if (bracketdata.operator == scfn.commands.comparison) {
            thisformat.hascomparison = 1;
          }
          thisformat.operators.push(bracketdata.operator);
          thisformat.operands.push(bracketdata.operand);
          continue;
        }
        bracketstr += ch;
        continue;
      }
      if (lastwasslash) {
        thisformat.operators.push(scfn.commands.copy);
        thisformat.operands.push(ch);
        lastwasslash = false;
        continue;
      }
      if (lastwasasterisk) {
        thisformat.operators.push(scfn.commands.copy);
        thisformat.operands.push(ch + ch + ch + ch + ch);
        lastwasasterisk = false;
        continue;
      }
      if (lastwasunderscore) {
        thisformat.operators.push(scfn.commands.copy);
        thisformat.operands.push('&nbsp;');
        lastwasunderscore = false;
        continue;
      }
      if (ingeneral) {
        if ('general'.charAt(ingeneral) == ch.toLowerCase()) {
          ingeneral++;
          if (ingeneral == 7) {
            thisformat.operators.push(scfn.commands.general);
            thisformat.operands.push(ch);
            ingeneral = 0;
          }
          continue;
        }
        ingeneral = 0;
      }
      if (indate) {
        if (indate.charAt(0) == ch) {
          indate += ch;
          continue;
        }
        thisformat.operators.push(scfn.commands.date);
        thisformat.operands.push(indate);
        sectioninfo.hasdate = 1;
        indate = '';
      }
      if (ampmstr) {
        ampmstr += ch;
        part = ampmstr.toLowerCase();
        if (part == 'am/pm' || part == 'a/p') {
          thisformat.operators.push(scfn.commands.date);
          thisformat.operands.push(ampmstr);
          ampmstr = '';
        }
        continue;
      }
      if (ch == '#' || ch == '0' || ch == '?') {
        if (integerpart) {
          sectioninfo.integerdigits++;
          if (sectioninfo.commas) {
            sectioninfo.thousandssep = 1;
            sectioninfo.commas = 0;
          }
          lastwasinteger = 1;
          thisformat.operators.push(scfn.commands.integer_placeholder);
          thisformat.operands.push(ch);
        } else {
          sectioninfo.fractiondigits++;
          lastwasinteger = 1;
          thisformat.operators.push(scfn.commands.fraction_placeholder);
          thisformat.operands.push(ch);
        }
      } else if (ch == '.') {
        lastwasinteger = 0;
        thisformat.operators.push(scfn.commands.decimal);
        thisformat.operands.push(ch);
        integerpart = 0;
      } else if (ch == '$') {
        lastwasinteger = 0;
        thisformat.operators.push(scfn.commands.currency);
        thisformat.operands.push('$');
      } else if (ch == ',') {
        if (lastwasinteger) {
          sectioninfo.commas++;
        } else {
          thisformat.operators.push(scfn.commands.copy);
          thisformat.operands.push(ch);
        }
      } else if (ch == '%') {
        lastwasinteger = 0;
        sectioninfo.percent++;
        thisformat.operators.push(scfn.commands.copy);
        thisformat.operands.push(ch);
      } else if (ch == '"') {
        lastwasinteger = 0;
        inquote = 1;
        quotestr = '';
      } else if (ch == '[') {
        lastwasinteger = 0;
        inbracket = 1;
        bracketstr = '';
      } else if (ch == '\\') {
        lastwasslash = 1;
        lastwasinteger = 0;
      } else if (ch == '*') {
        lastwasasterisk = 1;
        lastwasinteger = 0;
      } else if (ch == '_') {
        lastwasunderscore = 1;
        lastwasinteger = 0;
      } else if (ch == ';') {
        section++;
        thisformat.sectioninfo[section] = {};
        sectioninfo = thisformat.sectioninfo[section];
        sectioninfo.sectionstart = 1 + thisformat.operators.length;
        sectioninfo.integerdigits = 0;
        sectioninfo.fractiondigits = 0;
        sectioninfo.commas = 0;
        sectioninfo.percent = 0;
        integerpart = 1;
        lastwasinteger = 0;
        thisformat.operators.push(scfn.commands.section);
        thisformat.operands.push(ch);
      } else if (ch.toLowerCase() == 'g') {
        ingeneral = 1;
        lastwasinteger = 0;
      } else if (ch.toLowerCase() == 'a') {
        ampmstr = ch;
        lastwasinteger = 0;
      } else if ('dmyhHs'.indexOf(ch) >= 0) {
        indate = ch;
      } else {
        lastwasinteger = 0;
        thisformat.operators.push(scfn.commands.copy);
        thisformat.operands.push(ch);
      }
    }
    if (indate) {
      thisformat.operators.push(scfn.commands.date);
      thisformat.operands.push(indate);
      sectioninfo.hasdate = 1;
    }
    return;
  };
  FormatNumberMut.parse_format_bracket = function (bracketstr) {
    var scfn = SocialCalc.FormatNumber;
    var scc = SocialCalc.Constants;
    var bracketdata = {
      operator: 0,
      operand: '',
    };
    var parts;
    if (bracketstr.charAt(0) == '$') {
      bracketdata.operator = scfn.commands.currency;
      parts = bracketstr.match(/^\$(.+?)(-.+?){0,1}$/);
      if (parts) {
        bracketdata.operand = parts[1];
      } else {
        bracketdata.operand = bracketstr.substring(1) || scc.FormatNumber_defaultCurrency || '$';
      }
    } else if (bracketstr == '?$') {
      bracketdata.operator = scfn.commands.currency;
      bracketdata.operand = '[?$]';
    } else if (scfn.allowedcolors[bracketstr.toUpperCase()]) {
      bracketdata.operator = scfn.commands.color;
      bracketdata.operand = scfn.allowedcolors[bracketstr.toUpperCase()];
    } else if ((parts = bracketstr.match(/^style=([^"]*)$/))) {
      bracketdata.operator = scfn.commands.style;
      bracketdata.operand = parts[1];
    } else if (bracketstr == ',') {
      bracketdata.operator = scfn.commands.separator;
      bracketdata.operand = bracketstr;
    } else if (scfn.alloweddates[bracketstr.toUpperCase()]) {
      bracketdata.operator = scfn.commands.date;
      bracketdata.operand = scfn.alloweddates[bracketstr.toUpperCase()];
    } else if ((parts = bracketstr.match(/^[<>=]/))) {
      parts = bracketstr.match(/^([<>=]+)(.+)$/);
      bracketdata.operator = scfn.commands.comparison;
      bracketdata.operand = parts ? parts[1] + ':' + parts[2] : bracketstr;
    } else {
      bracketdata.operator = scfn.commands.copy;
      bracketdata.operand = '[' + bracketstr + ']';
    }
    return bracketdata;
  };
  FormatNumberMut.convert_date_gregorian_to_julian = function (year, month, day) {
    var juliandate;
    juliandate =
      day -
      32075 +
      SocialCalc.intFunc((1461 * (year + 4800 + SocialCalc.intFunc((month - 14) / 12))) / 4);
    juliandate += SocialCalc.intFunc(
      (367 * (month - 2 - SocialCalc.intFunc((month - 14) / 12) * 12)) / 12,
    );
    juliandate =
      juliandate -
      SocialCalc.intFunc(
        (3 * SocialCalc.intFunc((year + 4900 + SocialCalc.intFunc((month - 14) / 12)) / 100)) / 4,
      );
    return juliandate;
  };
  FormatNumberMut.convert_date_julian_to_gregorian = function (juliandate) {
    var L, N, I, J, K;
    L = juliandate + 68569;
    N = Math.floor((4 * L) / 146097);
    L = L - Math.floor((146097 * N + 3) / 4);
    I = Math.floor((4e3 * (L + 1)) / 1461001);
    L = L - Math.floor((1461 * I) / 4) + 31;
    J = Math.floor((80 * L) / 2447);
    K = L - Math.floor((2447 * J) / 80);
    L = Math.floor(J / 11);
    J = J + 2 - 12 * L;
    I = 100 * (N - 49) + I + L;
    return {
      year: I,
      month: J,
      day: K,
    };
  };
  SocialCalc.intFunc = function (n) {
    if (n < 0) {
      return -Math.floor(-n);
    } else {
      return Math.floor(n);
    }
  };

  // In-place TypeScript conversion of formula1.js (SocialCalc global script).
  // Ambient API types live in formula1.d.ts (referenced by dist/SocialCalc.d.ts).
  // Intermediate: typechecked core after removing @ts-nocheck.
  // Opts this module into strict TypeScript checking; sibling .d.ts provides types.
  //
  /*
// SocialCalc Spreadsheet Formula Library
//
// Part of the SocialCalc package
//
// (c) Copyright 2008 Socialtext, Inc.
// All Rights Reserved.
//
// The contents of this file are subject to the Artistic License 2.0; you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at http://socialcalc.org/licenses/al-20/.
//
// Some of the other files in the SocialCalc package are licensed under
// different licenses. Please note the licenses of the modules you use.
//
// Code History:
//
// Initially coded by Dan Bricklin of Software Garden, Inc., for Socialtext, Inc.
// Based in part on the SocialCalc 1.1.0 code written in Perl.
// The SocialCalc 1.1.0 code was:
//    Portions (c) Copyright 2005, 2006, 2007 Software Garden, Inc.
//    All Rights Reserved.
//    Portions (c) Copyright 2007 Socialtext, Inc.
//    All Rights Reserved.
// The Perl SocialCalc started as modifications to the wikiCalc(R) program, version 1.0.
// wikiCalc 1.0 was written by Software Garden, Inc.
// Unless otherwise specified, referring to "SocialCalc" in comments refers to this
// JavaScript version of the code, not the SocialCalc Perl code.
//
*/

  // Redundant `if (!SocialCalc)` guard removed — SocialCalc bag is created by
  // module-wrapper-top.js inside the UMD factory.

  // Implementation-only mutable views for progressive init of ambient Formula API.
  // Reads still go through SocialCalc.Formula (typed ambient); writes use FormulaMut.

  SocialCalc.Formula = {};
  const FormulaMut = SocialCalc.Formula;
  SocialCalc.TriggerIoAction = {};
  const TriggerIoMut = SocialCalc.TriggerIoAction;
  FormulaMut.ParseState = {
    num: 1,
    alpha: 2,
    coord: 3,
    string: 4,
    stringquote: 5,
    numexp1: 6,
    numexp2: 7,
    alphanumeric: 8,
    specialvalue: 9,
  };
  FormulaMut.TokenType = {
    num: 1,
    coord: 2,
    op: 3,
    name: 4,
    error: 5,
    string: 6,
    space: 7,
  };
  FormulaMut.CharClass = {
    num: 1,
    numstart: 2,
    op: 3,
    eof: 4,
    alpha: 5,
    incoord: 6,
    error: 7,
    quote: 8,
    space: 9,
    specialstart: 10,
  };
  FormulaMut.CharClassTable = {
    ' ': 9,
    '!': 3,
    '"': 8,
    "'": 8,
    '#': 10,
    $: 6,
    '%': 3,
    '&': 3,
    '(': 3,
    ')': 3,
    '*': 3,
    '+': 3,
    ',': 3,
    '-': 3,
    '.': 2,
    '/': 3,
    0: 1,
    1: 1,
    2: 1,
    3: 1,
    4: 1,
    5: 1,
    6: 1,
    7: 1,
    8: 1,
    9: 1,
    ':': 3,
    '<': 3,
    '=': 3,
    '>': 3,
    A: 5,
    B: 5,
    C: 5,
    D: 5,
    E: 5,
    F: 5,
    G: 5,
    H: 5,
    I: 5,
    J: 5,
    K: 5,
    L: 5,
    M: 5,
    N: 5,
    O: 5,
    P: 5,
    Q: 5,
    R: 5,
    S: 5,
    T: 5,
    U: 5,
    V: 5,
    W: 5,
    X: 5,
    Y: 5,
    Z: 5,
    '^': 3,
    _: 5,
    a: 5,
    b: 5,
    c: 5,
    d: 5,
    e: 5,
    f: 5,
    g: 5,
    h: 5,
    i: 5,
    j: 5,
    k: 5,
    l: 5,
    m: 5,
    n: 5,
    o: 5,
    p: 5,
    q: 5,
    r: 5,
    s: 5,
    t: 5,
    u: 5,
    v: 5,
    w: 5,
    x: 5,
    y: 5,
    z: 5,
  };
  FormulaMut.UpperCaseTable = {
    a: 'A',
    b: 'B',
    c: 'C',
    d: 'D',
    e: 'E',
    f: 'F',
    g: 'G',
    h: 'H',
    i: 'I',
    j: 'J',
    k: 'K',
    l: 'L',
    m: 'M',
    n: 'N',
    o: 'O',
    p: 'P',
    q: 'Q',
    r: 'R',
    s: 'S',
    t: 'T',
    u: 'U',
    v: 'V',
    w: 'W',
    x: 'X',
    y: 'Y',
    z: 'Z',
    A: 'A',
    B: 'B',
    C: 'C',
    D: 'D',
    E: 'E',
    F: 'F',
    G: 'G',
    H: 'H',
    I: 'I',
    J: 'J',
    K: 'K',
    L: 'L',
    M: 'M',
    N: 'N',
    O: 'O',
    P: 'P',
    Q: 'Q',
    R: 'R',
    S: 'S',
    T: 'T',
    U: 'U',
    V: 'V',
    W: 'W',
    X: 'X',
    Y: 'Y',
    Z: 'Z',
  };
  FormulaMut.SpecialConstants = {
    '#NULL!': '0,e#NULL!',
    '#NUM!': '0,e#NUM!',
    '#DIV/0!': '0,e#DIV/0!',
    '#VALUE!': '0,e#VALUE!',
    '#REF!': '0,e#REF!',
    '#NAME?': '0,e#NAME?',
    '#N/A': '0,e#N/A',
  };
  FormulaMut.TokenPrecedence = {
    '!': 1,
    ':': 2,
    ',': 2,
    M: -3,
    P: -3,
    '%': 4,
    '^': 5,
    '*': 6,
    '/': 6,
    '+': 7,
    '-': 7,
    '&': 8,
    '<': 9,
    '>': 9,
    '=': 9,
    G: 9,
    L: 9,
    N: 9,
  };
  FormulaMut.TokenOpExpansion = {
    G: '>=',
    L: '<=',
    M: '-',
    N: '<>',
    P: '+',
  };
  FormulaMut.TypeLookupTable = {
    unaryminus: {
      'n*': '|n*:1|',
      'e*': '|e*:1|',
      't*': '|t*:e#VALUE!|',
      b: '|b:n|',
    },
    unaryplus: {
      'n*': '|n*:1|',
      'e*': '|e*:1|',
      't*': '|t*:e#VALUE!|',
      b: '|b:n|',
    },
    unarypercent: {
      'n*': '|n:n%|n*:n|',
      'e*': '|e*:1|',
      't*': '|t*:e#VALUE!|',
      b: '|b:n|',
    },
    plus: {
      'n%': '|n%:n%|nd:n|nt:n|ndt:n|n$:n|n:n|n*:n|b:n|e*:2|t*:e#VALUE!|',
      nd: '|n%:n|nd:nd|nt:ndt|ndt:ndt|n$:n|n:nd|n*:n|b:n|e*:2|t*:e#VALUE!|',
      nt: '|n%:n|nd:ndt|nt:nt|ndt:ndt|n$:n|n:nt|n*:n|b:n|e*:2|t*:e#VALUE!|',
      ndt: '|n%:n|nd:ndt|nt:ndt|ndt:ndt|n$:n|n:ndt|n*:n|b:n|e*:2|t*:e#VALUE!|',
      n$: '|n%:n|nd:n|nt:n|ndt:n|n$:n$|n:n$|n*:n|b:n|e*:2|t*:e#VALUE!|',
      nl: '|n%:n|nd:n|nt:n|ndt:n|n$:n|n:n|n*:n|b:n|e*:2|t*:e#VALUE!|',
      n: '|n%:n|nd:nd|nt:nt|ndt:ndt|n$:n$|n:n|n*:n|b:n|e*:2|t*:e#VALUE!|',
      b: '|n%:n%|nd:nd|nt:nt|ndt:ndt|n$:n$|n:n|n*:n|b:n|e*:2|t*:e#VALUE!|',
      't*': '|ni:1|n*:e#VALUE!|t*:e#VALUE!|b:e#VALUE!|e*:2|',
      'n*': '|ni:1|e*:2|',
      'e*': '|e*:1|n*:1|t*:1|b:1|',
    },
    concat: {
      t: '|t:t|th:th|tw:tw|tl:t|tr:tr|t*:2|e*:2|',
      th: '|t:th|th:th|tw:t|tl:th|tr:t|t*:t|e*:2|',
      tw: '|t:tw|th:t|tw:tw|tl:tw|tr:tw|t*:t|e*:2|',
      tl: '|t:tl|th:th|tw:tw|tl:tl|tr:tr|t*:t|e*:2|',
      't*': '|t*:t|e*:2|',
      'e*': '|e*:1|n*:1|t*:1|',
    },
    oneargnumeric: {
      'n*': '|n*:n|',
      'e*': '|e*:1|',
      't*': '|t*:e#VALUE!|',
      b: '|b:n|',
    },
    twoargnumeric: {
      'n*': '|n*:n|t*:e#VALUE!|e*:2|',
      'e*': '|e*:1|n*:1|t*:1|',
      't*': '|t*:e#VALUE!|n*:e#VALUE!|e*:2|',
    },
    propagateerror: {
      'n*': '|n*:2|e*:2|',
      'e*': '|e*:2|',
      't*': '|t*:2|e*:2|',
      b: '|b:2|e*:2|',
    },
  };
  FormulaMut.evaluate_parsed_formula = function (parseinfo, sheet, allowrangereturn) {
    var result;
    var scf = SocialCalc.Formula;
    var revpolish;
    revpolish = scf.ConvertInfixToPolish(parseinfo);
    result = scf.EvaluatePolish(parseinfo, revpolish, sheet, allowrangereturn);
    return result;
  };
  if (typeof SocialCalc.debug_log === 'undefined') SocialCalc.debug_log = [];
  SocialCalc.DebugLog = function (_logObject) {};
  FormulaMut.EvaluatePolish = function (parseinfo, revpolish, sheet, allowrangereturn) {
    var scf = SocialCalc.Formula;
    var scc = SocialCalc.Constants;
    var tokentype = scf.TokenType;
    var lookup_result_type = scf.LookupResultType;
    var typelookup = scf.TypeLookupTable;
    var operand_as_number = scf.OperandAsNumber;
    var operand_as_text = scf.OperandAsText;
    var operand_value_and_type = scf.OperandValueAndType;
    var operands_as_coord_on_sheet = scf.OperandsAsCoordOnSheet;
    var format_number_for_display =
      SocialCalc.format_number_for_display ||
      function (v) {
        return v + '';
      };
    var errortext = '';
    var function_start = -1;
    var missingOperandError = {
      value: '',
      type: 'e#VALUE!',
      error: scc.s_parseerrmissingoperand,
    };
    var operand = [];
    var PushOperand = function (t, v) {
      operand.push({
        type: t,
        value: v,
      });
    };
    var i,
      rii,
      prii,
      ttype,
      ttext,
      value,
      value1,
      value2,
      tostype,
      tostype2,
      resulttype,
      valuetype,
      cond,
      vmatch,
      smatch;
    if (!parseinfo.length || !(revpolish instanceof Array)) {
      return {
        value: '',
        type: 'e#VALUE!',
        error: typeof revpolish == 'string' ? revpolish : '',
      };
    }
    var parameterInfo = [];
    for (i = 0; i < revpolish.length; i++) {
      rii = revpolish[i];
      if (rii == function_start) {
        PushOperand('start', 0);
        continue;
      }
      prii = parseinfo[rii];
      ttype = prii.type;
      ttext = prii.text;
      parameterInfo.push(prii);
      if (ttype == tokentype.num) {
        PushOperand('n', ttext - 0);
      } else if (ttype == tokentype.coord) {
        PushOperand('coord', ttext);
      } else if (ttype == tokentype.string) {
        PushOperand('t', ttext);
      } else if (ttype == tokentype.op) {
        if (operand.length <= 0) {
          return missingOperandError;
        }
        if (ttext == 'M') {
          value1 = operand_as_number(sheet, operand);
          resulttype = lookup_result_type(value1.type, value1.type, typelookup.unaryminus);
          PushOperand(resulttype, -value1.value);
        } else if (ttext == 'P') {
          value1 = operand_as_number(sheet, operand);
          resulttype = lookup_result_type(value1.type, value1.type, typelookup.unaryplus);
          PushOperand(resulttype, value1.value);
        } else if (ttext == '%') {
          value1 = operand_as_number(sheet, operand);
          resulttype = lookup_result_type(value1.type, value1.type, typelookup.unarypercent);
          PushOperand(resulttype, 0.01 * value1.value);
        } else if (ttext == '&') {
          if (operand.length <= 1) {
            return missingOperandError;
          }
          value2 = operand_as_text(sheet, operand);
          value1 = operand_as_text(sheet, operand);
          resulttype = lookup_result_type(value1.type, value2.type, typelookup.concat);
          PushOperand(resulttype, value1.value + value2.value);
        } else if (ttext == ':') {
          if (operand.length <= 1) {
            return missingOperandError;
          }
          value1 = scf.OperandsAsRangeOnSheet(sheet, operand);
          PushOperand(value1.type, value1.value);
        } else if (ttext == '!') {
          if (operand.length <= 1) {
            return missingOperandError;
          }
          value1 = operands_as_coord_on_sheet(sheet, operand);
          if (value1.error) {
            errortext = errortext || value1.error;
          }
          PushOperand(value1.type, value1.value);
        } else if (
          ttext == '<' ||
          ttext == 'L' ||
          ttext == '=' ||
          ttext == 'G' ||
          ttext == '>' ||
          ttext == 'N'
        ) {
          if (operand.length <= 1) {
            errortext = scc.s_parseerrmissingoperand;
            break;
          }
          value2 = operand_value_and_type(sheet, operand);
          value1 = operand_value_and_type(sheet, operand);
          if (value1.type.charAt(0) == 'n' && value2.type.charAt(0) == 'n') {
            cond = 0;
            if (ttext == '<') {
              cond = value1.value < value2.value ? 1 : 0;
            } else if (ttext == 'L') {
              cond = value1.value <= value2.value ? 1 : 0;
            } else if (ttext == '=') {
              cond = value1.value == value2.value ? 1 : 0;
            } else if (ttext == 'G') {
              cond = value1.value >= value2.value ? 1 : 0;
            } else if (ttext == '>') {
              cond = value1.value > value2.value ? 1 : 0;
            } else if (ttext == 'N') {
              cond = value1.value != value2.value ? 1 : 0;
            }
            PushOperand('nl', cond);
          } else if (value1.type.charAt(0) == 'e') {
            PushOperand(value1.type, 0);
          } else if (value2.type.charAt(0) == 'e') {
            PushOperand(value2.type, 0);
          } else {
            tostype = value1.type.charAt(0);
            tostype2 = value2.type.charAt(0);
            if (tostype == 'n') {
              value1.value = format_number_for_display(value1.value, 'n', '');
            } else if (tostype == 'b') {
              value1.value = '';
            }
            if (tostype2 == 'n') {
              value2.value = format_number_for_display(value2.value, 'n', '');
            } else if (tostype2 == 'b') {
              value2.value = '';
            }
            cond = 0;
            value1.value = value1.value.toLowerCase();
            value2.value = value2.value.toLowerCase();
            if (ttext == '<') {
              cond = value1.value < value2.value ? 1 : 0;
            } else if (ttext == 'L') {
              cond = value1.value <= value2.value ? 1 : 0;
            } else if (ttext == '=') {
              cond = value1.value == value2.value ? 1 : 0;
            } else if (ttext == 'G') {
              cond = value1.value >= value2.value ? 1 : 0;
            } else if (ttext == '>') {
              cond = value1.value > value2.value ? 1 : 0;
            } else if (ttext == 'N') {
              cond = value1.value != value2.value ? 1 : 0;
            }
            PushOperand('nl', cond);
          }
        } else {
          if (operand.length <= 1) {
            errortext = scc.s_parseerrmissingoperand;
            break;
          }
          if (ttext == '+') {
            value2 = operand_value_and_type(sheet, operand);
            value1 = operand_value_and_type(sheet, operand);
            resulttype = lookup_result_type(value1.type, value2.type, typelookup.plus);
            PushOperand(resulttype, value1.value + value2.value);
          } else {
            value2 = operand_as_number(sheet, operand);
            value1 = operand_as_number(sheet, operand);
          }
          if (ttext == '-') {
            resulttype = lookup_result_type(value1.type, value2.type, typelookup.plus);
            PushOperand(resulttype, value1.value - value2.value);
          } else if (ttext == '*') {
            resulttype = lookup_result_type(value1.type, value2.type, typelookup.plus);
            PushOperand(resulttype, value1.value * value2.value);
          } else if (ttext == '/') {
            resulttype = lookup_result_type(value1.type, value2.type, typelookup.plus);
            if (resulttype.charAt(0) == 'e') {
              PushOperand(resulttype, 0);
            } else if (value2.value != 0) {
              PushOperand(resulttype, value1.value / value2.value);
            } else {
              PushOperand('e#DIV/0!', 0);
            }
          } else if (ttext == '^') {
            resulttype = lookup_result_type(value1.type, value2.type, typelookup.plus);
            if (resulttype.charAt(0) == 'e') {
              PushOperand(resulttype, 0);
            } else {
              value1.value = Math.pow(value1.value, value2.value);
              value1.type = 'n';
              if (isNaN(value1.value)) {
                value1.value = 0;
                value1.type = 'e#NUM!';
              }
              PushOperand(value1.type, value1.value);
            }
          }
        }
      } else if (ttype == tokentype.name) {
        if (
          (rii > 0 && parseinfo[rii - 1].type == tokentype.op && parseinfo[rii - 1].text == ':') ||
          (rii + 1 < parseinfo.length &&
            parseinfo[rii + 1].type == tokentype.op &&
            parseinfo[rii + 1].text == ':')
        ) {
          if (operand.length && operand[operand.length - 1].type == 'start') {
            operand.pop();
          }
          PushOperand('name', ttext);
        } else {
          errortext = scf.CalculateFunction(ttext, operand, sheet, parseinfo.coord);
          if (errortext) break;
        }
      } else {
        errortext = scc.s_InternalError + 'Unknown token ' + ttype + ' (' + ttext + '). ';
        break;
      }
    }
    value = operand[0] ? operand[0].value : '';
    tostype = operand[0] ? operand[0].type : '';
    if (tostype == 'name') {
      value1 = SocialCalc.Formula.LookupName(sheet, value);
      value = value1.value;
      tostype = value1.type;
      errortext = errortext || value1.error;
    }
    if (tostype == 'coord') {
      value1 = operand_value_and_type(sheet, operand);
      value = value1.value;
      tostype = value1.type;
      if (tostype == 'b') {
        tostype = 'n';
        value = 0;
      }
    }
    if (operand.length > 1 && !errortext) {
      errortext += scc.s_parseerrerrorinformula;
    }
    valuetype = tostype;
    if (tostype.charAt(0) == 'e') {
      errortext = errortext || tostype.substring(1) || scc.s_calcerrerrorvalueinformula;
    } else if (tostype == 'range') {
      vmatch = value.match(/^(.*)\|(.*)\|/);
      smatch = vmatch[1].indexOf('!');
      if (smatch >= 0) {
        vmatch[1] =
          vmatch[1].substring(smatch + 1) + '!' + vmatch[1].substring(0, smatch).toUpperCase();
      } else {
        vmatch[1] = vmatch[1].toUpperCase();
      }
      value = vmatch[1] + ':' + vmatch[2].toUpperCase();
      if (!allowrangereturn) {
        errortext = scc.s_formularangeresult + ' ' + value;
      }
    }
    if (errortext && valuetype.charAt(0) != 'e') {
      value = errortext;
      valuetype = 'e';
    }
    if (valuetype.charAt(0) == 'n') {
      const numericIsNaN = isNaN(value);
      if (numericIsNaN || !isFinite(value)) {
        value = 0;
        valuetype = 'e#NUM!';
        errortext = numericIsNaN ? scc.s_calcerrnumericnan : scc.s_calcerrnumericoverflow;
      }
    }
    return {
      value,
      type: valuetype,
      error: errortext,
    };
  };
  FormulaMut.LookupName = function (sheet, name, isEnd) {
    var pos, specialc, parseinfo;
    var names = sheet.names;
    var value = {};
    var startedwalk = false;
    if (names[name.toUpperCase()]) {
      value.value = names[name.toUpperCase()].definition;
      if (value.value.charAt(0) == '=') {
        if (!sheet.checknamecirc) {
          sheet.checknamecirc = {};
          startedwalk = true;
        } else {
          if (sheet.checknamecirc[name]) {
            value.type = 'e#NAME?';
            value.error = SocialCalc.Constants.s_circularnameref + ' "' + name + '".';
            return value;
          }
        }
        sheet.checknamecirc[name] = true;
        parseinfo = SocialCalc.Formula.ParseFormulaIntoTokens(value.value.substring(1));
        value = SocialCalc.Formula.evaluate_parsed_formula(parseinfo, sheet, 1);
        delete sheet.checknamecirc[name];
        if (startedwalk) {
          delete sheet.checknamecirc;
        }
        if (value.type != 'range') return value;
      }
      pos = value.value.indexOf(':');
      if (pos != -1) {
        value.type = 'range';
        value.value = value.value.substring(0, pos) + '|' + value.value.substring(pos + 1) + '|';
        value.value = value.value.toUpperCase();
      } else {
        value.type = 'coord';
        value.value = value.value.toUpperCase();
      }
      return value;
    } else if ((specialc = SocialCalc.Formula.SpecialConstants[name.toUpperCase()])) {
      pos = specialc.indexOf(',');
      value.value = specialc.substring(0, pos) - 0;
      value.type = specialc.substring(pos + 1);
      return value;
    } else if (/^[a-zA-Z][a-zA-Z]?$/.test(name)) {
      value.type = 'coord';
      value.value = name.toUpperCase() + (isEnd ? sheet.attribs.lastrow : 1);
      return value;
    } else {
      value.value = '';
      value.type = 'e#NAME?';
      value.error = SocialCalc.Constants.s_calcerrunknownname + ' "' + name + '"';
      return value;
    }
  };
  FormulaMut.StepThroughRangeDown = function (operand, rangevalue) {
    var value1, value2, sequence, pos1, pos2, sheet1, rp, c, r, count;
    var scf = SocialCalc.Formula;
    pos1 = rangevalue.indexOf('|');
    pos2 = rangevalue.indexOf('|', pos1 + 1);
    value1 = rangevalue.substring(0, pos1);
    value2 = rangevalue.substring(pos1 + 1, pos2);
    sequence = rangevalue.substring(pos2 + 1) - 0;
    pos1 = value1.indexOf('!');
    if (pos1 != -1) {
      sheet1 = value1.substring(pos1);
      value1 = value1.substring(0, pos1);
    } else {
      sheet1 = '';
    }
    rp = scf.OrderRangeParts(value1, value2);
    if (rp.c1 < 1 || rp.c2 < 1 || rp.c1 > 702 || rp.c2 > 702 || rp.r1 < 1 || rp.r2 < 1) {
      return {
        value: 0,
        type: 'e#REF!',
      };
    }
    count = 0;
    for (r = rp.r1; r <= rp.r2; r++) {
      for (c = rp.c1; c <= rp.c2; c++) {
        count++;
        if (count > sequence) {
          if (r != rp.r2 || c != rp.c2) {
            scf.PushOperand(operand, 'range', value1 + sheet1 + '|' + value2 + '|' + count);
          }
          return {
            value: SocialCalc.crToCoord(c, r) + sheet1,
            type: 'coord',
          };
        }
      }
    }
    return undefined;
  };
  FormulaMut.DecodeRangeParts = function (sheetdata, range) {
    var value1, value2, pos1, pos2, sheet1, coordsheetdata, rp;
    var scf = SocialCalc.Formula;
    pos1 = range.indexOf('|');
    pos2 = range.indexOf('|', pos1 + 1);
    value1 = range.substring(0, pos1);
    value2 = range.substring(pos1 + 1, pos2);
    pos1 = value1.indexOf('!');
    if (pos1 != -1) {
      sheet1 = value1.substring(pos1 + 1);
      value1 = value1.substring(0, pos1);
    } else {
      sheet1 = '';
    }
    coordsheetdata = sheetdata;
    if (sheet1) {
      coordsheetdata = scf.FindInSheetCache(sheet1);
      if (coordsheetdata == null) {
        return null;
      }
    }
    rp = scf.OrderRangeParts(value1, value2);
    if (rp.c1 < 1 || rp.c2 < 1 || rp.c1 > 702 || rp.c2 > 702 || rp.r1 < 1 || rp.r2 < 1) {
      return null;
    }
    return {
      sheetdata: coordsheetdata,
      sheetname: sheet1,
      col1num: rp.c1,
      ncols: rp.c2 - rp.c1 + 1,
      row1num: rp.r1,
      nrows: rp.r2 - rp.r1 + 1,
    };
  };
  if (!SocialCalc.Formula.FunctionList) {
    FormulaMut.FunctionList = {};
  }
  FormulaMut.FunctionClasses = null;
  FormulaMut.FunctionArgDefs = {};
  FormulaMut.StoreIoEventFormula = function (
    function_name,
    coord,
    operand_reverse,
    sheet,
    io_parameters,
  ) {
    var operand = [];
    SocialCalc.Formula.Clone(operand, operand_reverse);
    operand.reverse();
    if (operand.length == 0) return;
    if (typeof sheet.ioEventTree === 'undefined') sheet.ioEventTree = {};
    if (typeof sheet.ioParameterList === 'undefined') sheet.ioParameterList = {};
    if (typeof sheet.ioTimeTriggerList === 'undefined') sheet.ioTimeTriggerList = {};
    if (typeof sheet.radioGroupList === 'undefined') sheet.radioGroupList = {};
    if (typeof sheet.ioParameterList[coord] === 'undefined') sheet.ioParameterList[coord] = {};
    sheet.ioParameterList[coord] = operand;
    sheet.ioParameterList[coord].function_name = function_name;
    if (io_parameters == 'TimeTrigger') {
      var PushTriggerTime = function (list, coordA1, sheetData) {
        var cell = sheetData.cells[coordA1];
        if (typeof cell !== 'undefined' && cell.valuetype.charAt(0) == 'n') {
          list.push(cell.datavalue);
        }
      };
      var triggerTimeCellId = SocialCalc.Formula.PlainCoord(operand[0].value);
      var currentTriggerTimeList = [];
      if (operand[0].type == 'range') {
        let rangeinfo = SocialCalc.Formula.DecodeRangeParts(sheet, triggerTimeCellId);
        for (var i = 0; i < rangeinfo.ncols; i++) {
          for (var j = 0; j < rangeinfo.nrows; j++) {
            var cellCoord = SocialCalc.crToCoord(rangeinfo.col1num + i, rangeinfo.row1num + j);
            PushTriggerTime(currentTriggerTimeList, cellCoord, sheet);
          }
        }
      }
      if (operand[0].type == 'coord') {
        PushTriggerTime(currentTriggerTimeList, triggerTimeCellId, sheet);
      }
      if (operand[0].type.charAt(0) == 'n') {
        currentTriggerTimeList.push(operand[0].value);
      }
      if (
        typeof sheet.ioTimeTriggerList[coord] === 'undefined' ||
        SocialCalc.Formula.ArrayValuesEqual(
          sheet.ioTimeTriggerList[coord],
          currentTriggerTimeList,
        ) == false
      ) {
        sheet.ioTimeTriggerList[coord] = currentTriggerTimeList;
        var timeZoneOffsetMins = new Date().getTimezoneOffset();
        var start_1_1_1970 = 25569;
        var triggerUTCTimeList = [];
        for (var index = 0; index < currentTriggerTimeList.length; ++index)
          triggerUTCTimeList[index] = Math.floor(
            (currentTriggerTimeList[index] - start_1_1_1970) * 24 * 60 + timeZoneOffsetMins,
          );
        sheet.ScheduleSheetCommands(
          'settimetrigger ' + coord + ' ' + triggerUTCTimeList.toString(),
        );
      }
    }
    if (
      io_parameters == 'EventTree' &&
      (operand[0].type == 'coord' || operand[0].type == 'range')
    ) {
      var triggerCellId = operand[0].value.replace(/\$/g, '');
      var PushTriggerCord = function (list, index, v) {
        if (typeof list[index] === 'undefined') list[index] = {};
        list[index][v] = v;
      };
      if (operand[0].type == 'range') {
        var rangeinfo = SocialCalc.Formula.DecodeRangeParts(sheet, triggerCellId);
        for (var i = 0; i < rangeinfo.ncols; i++) {
          for (var j = 0; j < rangeinfo.nrows; j++) {
            var cellcr = SocialCalc.crToCoord(rangeinfo.col1num + i, rangeinfo.row1num + j);
            PushTriggerCord(sheet.ioEventTree, cellcr, coord);
          }
        }
      }
      if (operand[0].type == 'coord') {
        PushTriggerCord(sheet.ioEventTree, triggerCellId, coord);
      }
    }
    if (io_parameters == 'Input') {
      var formDataViewer = null;
      if (SocialCalc.CurrentSpreadsheetControlObject != null) {
        formDataViewer = SocialCalc.CurrentSpreadsheetControlObject.formDataViewer;
      } else if (SocialCalc.CurrentSpreadsheetViewerObject != null) {
        formDataViewer = SocialCalc.CurrentSpreadsheetViewerObject.formDataViewer;
      }
      if (formDataViewer == null) {
        return;
      }
      if (formDataViewer != null && formDataViewer.loaded == true) {
        if (formDataViewer.formFields == null) SocialCalc.Formula.LoadFormFields();
        var formFieldName = (function_name + coord).toLowerCase();
        var sheetCmd = null;
        if (formDataViewer.formFields[formFieldName] == null) {
          var col = (formDataViewer.formFields[formFieldName] =
            formDataViewer.formFieldsLength++ + 2);
          var headerCoord = SocialCalc.crToCoord(col, 1);
          sheetCmd =
            'set ' +
            headerCoord +
            ' text t ' +
            SocialCalc.encodeForSave(function_name.toLowerCase() + coord);
        }
        if (operand[0].type.charAt(0) == 't' || operand[0].type.charAt(0) == 'n') {
          var valueCoord = SocialCalc.crToCoord(formDataViewer.formFields[formFieldName], 2);
          if (
            formDataViewer.sheet.cells[valueCoord] == null ||
            formDataViewer.sheet.cells[valueCoord].datavalue != operand[0].value
          ) {
            var sheetCmdTwo =
              'set ' + valueCoord + ' text t ' + SocialCalc.encodeForSave(operand[0].value);
            if (sheetCmd != null) sheetCmd = sheetCmd + '\n' + sheetCmdTwo;
            else sheetCmd = sheetCmdTwo;
          }
        }
        if (sheetCmd != null) formDataViewer.sheet.ScheduleSheetCommands(sheetCmd, false);
      }
    }
  };
  FormulaMut.Clone = function (destination, source) {
    for (var property in source) {
      if (
        typeof source[property] === 'object' &&
        source[property] !== null &&
        destination[property]
      ) {
        SocialCalc.Formula.Clone(destination[property], source[property]);
      } else {
        destination[property] = source[property];
      }
    }
  };
  FormulaMut.LoadFormFields = function () {
    var formDataViewer =
      SocialCalc.CurrentSpreadsheetControlObject != null
        ? SocialCalc.CurrentSpreadsheetControlObject.formDataViewer
        : SocialCalc.CurrentSpreadsheetViewerObject.formDataViewer;
    formDataViewer.formFields = {};
    if (formDataViewer.sheet.cells.A1 == null) {
      formDataViewer.sheet.ScheduleSheetCommands(
        'set A1 text t ' + SocialCalc.encodeForSave('FieldName:'),
        false,
      );
    }
    if (formDataViewer.sheet.cells.A2 == null) {
      formDataViewer.sheet.ScheduleSheetCommands(
        'set A2 text t ' + SocialCalc.encodeForSave('Pending:'),
        false,
      );
    }
    var col = 2;
    while (true) {
      var coord = SocialCalc.crToCoord(col, 1);
      var cell = formDataViewer.sheet.cells[coord];
      if (!cell) break;
      formDataViewer.formFields[cell.datavalue.toLowerCase()] = col;
      col++;
    }
    formDataViewer.formFieldsLength = col - 2;
  };
  FormulaMut.CalculateFunction = function (fname, operand, sheet, coord) {
    var fobj, ffunc, argnum, ttext;
    var foperand;
    var scf = SocialCalc.Formula;
    var errortext = '';
    fobj = scf.FunctionList[fname];
    if (fobj) {
      foperand = [];
      ffunc = fobj[0];
      argnum = fobj[1];
      scf.CopyFunctionArgs(operand, foperand);
      if (foperand.length === 0 && sheet.names && sheet.names[fname.toUpperCase()]) {
        scf.PushOperand(operand, 'name', fname);
        return '';
      }
      if (fobj[6] && fobj[6] != '') {
        SocialCalc.DebugLog('action:' + fname);
        scf.StoreIoEventFormula(fname, coord, foperand, sheet, fobj[6]);
      }
      if (argnum != 100) {
        if (argnum < 0) {
          if (foperand.length < -argnum) {
            errortext = scf.FunctionArgsError(fname, operand);
            return errortext;
          }
        } else {
          if (foperand.length != argnum) {
            errortext = scf.FunctionArgsError(fname, operand);
            return errortext;
          }
        }
      }
      errortext = ffunc(fname, operand, foperand, sheet, coord);
    } else {
      ttext = fname;
      if (operand.length && operand[operand.length - 1].type == 'start') {
        operand.pop();
        scf.PushOperand(operand, 'name', ttext);
      } else {
        errortext = SocialCalc.Constants.s_sheetfuncunknownfunction + ' ' + ttext + '. ';
      }
    }
    return errortext;
  };
  FormulaMut.FunctionArgsError = function (fname, operand) {
    var errortext = SocialCalc.Constants.s_calcerrincorrectargstofunction + ' ' + fname + '. ';
    SocialCalc.Formula.PushOperand(operand, 'e#VALUE!', errortext);
    return errortext;
  };
  FormulaMut.FunctionSpecificError = function (fname, operand, errortype, errortext) {
    SocialCalc.Formula.PushOperand(operand, errortype, errortext);
    return errortext;
  };
  FormulaMut.CheckForErrorValue = function (operand, v) {
    if (v.type.charAt(0) == 'e') {
      operand.push(v);
      return true;
    } else {
      return false;
    }
  };
  FormulaMut.FillFunctionInfo = function () {
    var scf = SocialCalc.Formula;
    var scc = SocialCalc.Constants;
    var fname, f, classes, cname, i;
    if (scf.FunctionClasses) {
      return;
    }
    for (fname in scf.FunctionList) {
      f = scf.FunctionList[fname];
      if (f[2]) {
        scf.FunctionArgDefs[f[2]] = scc['s_farg_' + f[2]] || '';
      }
      if (!f[3]) {
        if (scc['s_fdef_' + fname]) {
          scf.FunctionList[fname][3] = scc['s_fdef_' + fname];
        }
      }
    }
    scf.FunctionClasses = {};
    for (i = 0; i < scc.function_classlist.length; i++) {
      cname = scc.function_classlist[i];
      scf.FunctionClasses[cname] = {
        name: scc['s_fclass_' + cname],
        items: [],
      };
    }
    for (fname in scf.FunctionList) {
      f = scf.FunctionList[fname];
      classes = f[4] ? f[4].split(',') : [];
      classes.push('all');
      for (i = 0; i < classes.length; i++) {
        cname = classes[i];
        scf.FunctionClasses[cname].items.push(fname);
      }
    }
    for (cname in scf.FunctionClasses) {
      scf.FunctionClasses[cname].items.sort();
    }
  };
  FormulaMut.FunctionArgString = function (fname) {
    var scf = SocialCalc.Formula;
    var fdata = scf.FunctionList[fname];
    var nargs, i, str;
    var adef = fdata[2];
    if (!adef) {
      nargs = fdata[1];
      if (nargs == 0) {
        adef = ' ';
      } else if (nargs > 0) {
        str = 'v1';
        for (i = 2; i <= nargs; i++) {
          str += ', v' + i;
        }
        return str;
      } else {
        str = 'v1';
        for (i = 2; i < -nargs; i++) {
          str += ', v' + i;
        }
        return str + ', ...';
      }
    }
    str = scf.FunctionArgDefs[adef] || adef;
    return str;
  };
  FormulaMut.SeriesFunctions = function (fname, operand, foperand, sheet) {
    var value1, t, v1;
    var scf = SocialCalc.Formula;
    var operand_value_and_type = scf.OperandValueAndType;
    var lookup_result_type = scf.LookupResultType;
    var typelookupplus = scf.TypeLookupTable.plus;
    var PushOperand = function (t, v) {
      operand.push({
        type: t,
        value: v,
      });
    };
    var concat = '';
    var sum = 0;
    var resulttypesum = '';
    var count = 0;
    var counta = 0;
    var countblank = 0;
    var product = 1;
    var maxval;
    var minval;
    var mk = 0,
      sk = 0,
      mk1 = 0,
      sk1 = 0;
    while (foperand.length > 0) {
      value1 = operand_value_and_type(sheet, foperand);
      t = value1.type.charAt(0);
      if (t == 'n') count += 1;
      if (t != 'b') counta += 1;
      if (t == 'b') countblank += 1;
      if (t != 'e' && t != 'b') concat = concat + value1.value;
      if (t == 'n') {
        v1 = value1.value - 0;
        sum += v1;
        product *= v1;
        maxval = maxval != undefined ? (v1 > maxval ? v1 : maxval) : v1;
        minval = minval != undefined ? (v1 < minval ? v1 : minval) : v1;
        if (count == 1) {
          mk1 = v1;
          sk1 = 0;
        } else {
          mk = mk1 + (v1 - mk1) / count;
          sk = sk1 + (v1 - mk1) * (v1 - mk);
          sk1 = sk;
          mk1 = mk;
        }
        resulttypesum = lookup_result_type(
          value1.type,
          resulttypesum || value1.type,
          typelookupplus,
        );
      } else if (t == 'e' && resulttypesum.charAt(0) != 'e') {
        resulttypesum = value1.type;
      }
    }
    resulttypesum = resulttypesum || 'n';
    switch (fname) {
      case 'CONCAT':
      case 'CONCATENATE':
        PushOperand('t', concat);
        break;
      case 'SUM':
        PushOperand(resulttypesum, sum);
        break;
      case 'PRODUCT':
        PushOperand(resulttypesum, product);
        break;
      case 'MIN':
        PushOperand(resulttypesum, minval || 0);
        break;
      case 'MAX':
        PushOperand(resulttypesum, maxval || 0);
        break;
      case 'COUNT':
        PushOperand('n', count);
        break;
      case 'COUNTA':
        PushOperand('n', counta);
        break;
      case 'COUNTBLANK':
        PushOperand('n', countblank);
        break;
      case 'AVERAGE':
        if (count > 0) {
          PushOperand(resulttypesum, sum / count);
        } else {
          PushOperand('e#DIV/0!', 0);
        }
        break;
      case 'STDEV':
        if (count > 1) {
          PushOperand(resulttypesum, Math.sqrt(sk / (count - 1)));
        } else {
          PushOperand('e#DIV/0!', 0);
        }
        break;
      case 'STDEVP':
        if (count > 1) {
          PushOperand(resulttypesum, Math.sqrt(sk / count));
        } else {
          PushOperand('e#DIV/0!', 0);
        }
        break;
      case 'VAR':
        if (count > 1) {
          PushOperand(resulttypesum, sk / (count - 1));
        } else {
          PushOperand('e#DIV/0!', 0);
        }
        break;
      case 'VARP':
        if (count > 1) {
          PushOperand(resulttypesum, sk / count);
        } else {
          PushOperand('e#DIV/0!', 0);
        }
        break;
    }
    return null;
  };
  SocialCalc.Formula.FunctionList['AVERAGE'] = [
    SocialCalc.Formula.SeriesFunctions,
    -1,
    'vn',
    null,
    'stat',
  ];
  SocialCalc.Formula.FunctionList['CONCAT'] = [
    SocialCalc.Formula.SeriesFunctions,
    -1,
    'vn',
    null,
    'text',
  ];
  SocialCalc.Formula.FunctionList['CONCATENATE'] = [
    SocialCalc.Formula.SeriesFunctions,
    -1,
    'vn',
    null,
    'text',
  ];
  SocialCalc.Formula.FunctionList['COUNT'] = [
    SocialCalc.Formula.SeriesFunctions,
    -1,
    'vn',
    null,
    'stat',
  ];
  SocialCalc.Formula.FunctionList['COUNTA'] = [
    SocialCalc.Formula.SeriesFunctions,
    -1,
    'vn',
    null,
    'stat',
  ];
  SocialCalc.Formula.FunctionList['COUNTBLANK'] = [
    SocialCalc.Formula.SeriesFunctions,
    -1,
    'vn',
    null,
    'stat',
  ];
  SocialCalc.Formula.FunctionList['MAX'] = [
    SocialCalc.Formula.SeriesFunctions,
    -1,
    'vn',
    null,
    'stat',
  ];
  SocialCalc.Formula.FunctionList['MIN'] = [
    SocialCalc.Formula.SeriesFunctions,
    -1,
    'vn',
    null,
    'stat',
  ];
  SocialCalc.Formula.FunctionList['PRODUCT'] = [
    SocialCalc.Formula.SeriesFunctions,
    -1,
    'vn',
    null,
    'stat',
  ];
  SocialCalc.Formula.FunctionList['STDEV'] = [
    SocialCalc.Formula.SeriesFunctions,
    -1,
    'vn',
    null,
    'stat',
  ];
  SocialCalc.Formula.FunctionList['STDEVP'] = [
    SocialCalc.Formula.SeriesFunctions,
    -1,
    'vn',
    null,
    'stat',
  ];
  SocialCalc.Formula.FunctionList['SUM'] = [
    SocialCalc.Formula.SeriesFunctions,
    -1,
    'vn',
    null,
    'stat',
  ];
  SocialCalc.Formula.FunctionList['VAR'] = [
    SocialCalc.Formula.SeriesFunctions,
    -1,
    'vn',
    null,
    'stat',
  ];
  SocialCalc.Formula.FunctionList['VARP'] = [
    SocialCalc.Formula.SeriesFunctions,
    -1,
    'vn',
    null,
    'stat',
  ];
  FormulaMut.SumProductFunction = function (fname, operand, foperand, sheet) {
    var range,
      sum = 0;
    var products = [];
    var scf = SocialCalc.Formula;
    var ncols = 0,
      nrows = 0;
    var rangeinfo, i, j, k, cellcr, cell, value;
    var PushOperand = function (t, v) {
      operand.push({
        type: t,
        value: v,
      });
    };
    while (foperand.length > 0) {
      range = scf.TopOfStackValueAndType(sheet, foperand);
      if (range.type != 'range') {
        PushOperand('e#VALUE!', 0);
        return;
      }
      rangeinfo = scf.DecodeRangeParts(sheet, range.value);
      if (!rangeinfo) {
        PushOperand('e#REF!', 0);
        return;
      }
      if (!ncols) ncols = rangeinfo.ncols;
      else if (ncols != rangeinfo.ncols) {
        PushOperand('e#VALUE!', 0);
        return;
      }
      if (!nrows) nrows = rangeinfo.nrows;
      else if (nrows != rangeinfo.nrows) {
        PushOperand('e#VALUE!', 0);
        return;
      }
      for (i = 0; i < rangeinfo.ncols; i++) {
        for (j = 0; j < rangeinfo.nrows; j++) {
          k = i * rangeinfo.nrows + j;
          cellcr = SocialCalc.crToCoord(rangeinfo.col1num + i, rangeinfo.row1num + j);
          cell = rangeinfo.sheetdata.GetAssuredCell(cellcr);
          value = cell.valuetype == 'n' ? cell.datavalue : 0;
          products[k] = (typeof products[k] !== 'undefined' ? products[k] : 1) * value;
        }
      }
    }
    for (i = 0; i < products.length; i++) {
      sum += products[i];
    }
    PushOperand('n', sum);
    return;
  };
  SocialCalc.Formula.FunctionList['SUMPRODUCT'] = [
    SocialCalc.Formula.SumProductFunction,
    -1,
    'rangen',
    '',
    'stat',
  ];
  FormulaMut.DSeriesFunctions = function (fname, operand, foperand, sheet) {
    var cr, dbrange, fieldname, criteriarange;
    var fieldasnum, targetcol, i, j, k, cell, criteriafieldnums, criterianum;
    var testok, criteriacr, criteria, testcol, testcr;
    var t, v1;
    var scf = SocialCalc.Formula;
    var lookup_result_type = scf.LookupResultType;
    var typelookupplus = scf.TypeLookupTable.plus;
    var PushOperand = function (t, v) {
      operand.push({
        type: t,
        value: v,
      });
    };
    var value1 = {};
    var sum = 0;
    var resulttypesum = '';
    var count = 0;
    var counta = 0;
    var countblank = 0;
    var countmatches = 0;
    var product = 1;
    var maxval;
    var minval;
    var mk = 0,
      sk = 0,
      mk1 = 0,
      sk1 = 0;
    dbrange = scf.TopOfStackValueAndType(sheet, foperand);
    fieldname = scf.OperandValueAndType(sheet, foperand);
    criteriarange = scf.TopOfStackValueAndType(sheet, foperand);
    var dbinfo, criteriainfo;
    if (dbrange.type != 'range' || criteriarange.type != 'range') {
      return scf.FunctionArgsError(fname, operand);
    }
    dbinfo = scf.DecodeRangeParts(sheet, dbrange.value);
    criteriainfo = scf.DecodeRangeParts(sheet, criteriarange.value);
    fieldasnum = scf.FieldToColnum(
      dbinfo.sheetdata,
      dbinfo.col1num,
      dbinfo.ncols,
      dbinfo.row1num,
      fieldname.value,
      fieldname.type,
    );
    if (fieldasnum <= 0) {
      PushOperand('e#VALUE!', 0);
      return;
    }
    targetcol = dbinfo.col1num + fieldasnum - 1;
    criteriafieldnums = [];
    for (i = 0; i < criteriainfo.ncols; i++) {
      cell = criteriainfo.sheetdata.GetAssuredCell(
        SocialCalc.crToCoord(criteriainfo.col1num + i, criteriainfo.row1num),
      );
      criterianum = scf.FieldToColnum(
        dbinfo.sheetdata,
        dbinfo.col1num,
        dbinfo.ncols,
        dbinfo.row1num,
        cell.datavalue,
        cell.valuetype,
      );
      if (criterianum <= 0) {
        PushOperand('e#VALUE!', 0);
        return;
      }
      criteriafieldnums.push(dbinfo.col1num + criterianum - 1);
    }
    for (i = 1; i < dbinfo.nrows; i++) {
      testok = false;
      CRITERIAROW: for (j = 1; j < criteriainfo.nrows; j++) {
        for (k = 0; k < criteriainfo.ncols; k++) {
          criteriacr = SocialCalc.crToCoord(criteriainfo.col1num + k, criteriainfo.row1num + j);
          cell = criteriainfo.sheetdata.GetAssuredCell(criteriacr);
          criteria = cell.datavalue;
          if (typeof criteria == 'string' && criteria.length == 0) continue;
          testcol = criteriafieldnums[k];
          testcr = SocialCalc.crToCoord(testcol, dbinfo.row1num + i);
          cell = dbinfo.sheetdata.GetAssuredCell(testcr);
          if (!scf.TestCriteria(cell.datavalue, cell.valuetype || 'b', criteria)) {
            continue CRITERIAROW;
          }
        }
        testok = true;
        break CRITERIAROW;
      }
      if (!testok) {
        continue;
      }
      cr = SocialCalc.crToCoord(targetcol, dbinfo.row1num + i);
      cell = dbinfo.sheetdata.GetAssuredCell(cr);
      value1.value = cell.datavalue;
      value1.type = cell.valuetype;
      t = value1.type.charAt(0);
      countmatches += 1;
      if (t == 'n') count += 1;
      if (t != 'b') counta += 1;
      if (t == 'b') countblank += 1;
      if (t == 'n') {
        v1 = value1.value - 0;
        sum += v1;
        product *= v1;
        maxval = maxval != undefined ? (v1 > maxval ? v1 : maxval) : v1;
        minval = minval != undefined ? (v1 < minval ? v1 : minval) : v1;
        if (count == 1) {
          mk1 = v1;
          sk1 = 0;
        } else {
          mk = mk1 + (v1 - mk1) / count;
          sk = sk1 + (v1 - mk1) * (v1 - mk);
          sk1 = sk;
          mk1 = mk;
        }
        resulttypesum = lookup_result_type(
          value1.type,
          resulttypesum || value1.type,
          typelookupplus,
        );
      } else if (t == 'e' && resulttypesum.charAt(0) != 'e') {
        resulttypesum = value1.type;
      }
    }
    resulttypesum = resulttypesum || 'n';
    switch (fname) {
      case 'DSUM':
        PushOperand(resulttypesum, sum);
        break;
      case 'DPRODUCT':
        PushOperand(resulttypesum, count == 0 ? 0 : product);
        break;
      case 'DMIN':
        PushOperand(resulttypesum, minval || 0);
        break;
      case 'DMAX':
        PushOperand(resulttypesum, maxval || 0);
        break;
      case 'DCOUNT':
        PushOperand('n', count);
        break;
      case 'DCOUNTA':
        PushOperand('n', counta);
        break;
      case 'DAVERAGE':
        if (count > 0) {
          PushOperand(resulttypesum, sum / count);
        } else {
          PushOperand('e#DIV/0!', 0);
        }
        break;
      case 'DSTDEV':
        if (count > 1) {
          PushOperand(resulttypesum, Math.sqrt(sk / (count - 1)));
        } else {
          PushOperand('e#DIV/0!', 0);
        }
        break;
      case 'DSTDEVP':
        if (count > 1) {
          PushOperand(resulttypesum, Math.sqrt(sk / count));
        } else {
          PushOperand('e#DIV/0!', 0);
        }
        break;
      case 'DVAR':
        if (count > 1) {
          PushOperand(resulttypesum, sk / (count - 1));
        } else {
          PushOperand('e#DIV/0!', 0);
        }
        break;
      case 'DVARP':
        if (count > 1) {
          PushOperand(resulttypesum, sk / count);
        } else {
          PushOperand('e#DIV/0!', 0);
        }
        break;
      case 'DGET':
        if (countmatches == 1) {
          PushOperand(value1.type, value1.value);
        } else if (countmatches == 0) {
          PushOperand('e#VALUE!', 0);
        } else {
          PushOperand('e#NUM!', 0);
        }
        break;
    }
    return;
  };
  SocialCalc.Formula.FunctionList['DAVERAGE'] = [
    SocialCalc.Formula.DSeriesFunctions,
    3,
    'dfunc',
    '',
    'stat',
  ];
  SocialCalc.Formula.FunctionList['DCOUNT'] = [
    SocialCalc.Formula.DSeriesFunctions,
    3,
    'dfunc',
    '',
    'stat',
  ];
  SocialCalc.Formula.FunctionList['DCOUNTA'] = [
    SocialCalc.Formula.DSeriesFunctions,
    3,
    'dfunc',
    '',
    'stat',
  ];
  SocialCalc.Formula.FunctionList['DGET'] = [
    SocialCalc.Formula.DSeriesFunctions,
    3,
    'dfunc',
    '',
    'stat',
  ];
  SocialCalc.Formula.FunctionList['DMAX'] = [
    SocialCalc.Formula.DSeriesFunctions,
    3,
    'dfunc',
    '',
    'stat',
  ];
  SocialCalc.Formula.FunctionList['DMIN'] = [
    SocialCalc.Formula.DSeriesFunctions,
    3,
    'dfunc',
    '',
    'stat',
  ];
  SocialCalc.Formula.FunctionList['DPRODUCT'] = [
    SocialCalc.Formula.DSeriesFunctions,
    3,
    'dfunc',
    '',
    'stat',
  ];
  SocialCalc.Formula.FunctionList['DSTDEV'] = [
    SocialCalc.Formula.DSeriesFunctions,
    3,
    'dfunc',
    '',
    'stat',
  ];
  SocialCalc.Formula.FunctionList['DSTDEVP'] = [
    SocialCalc.Formula.DSeriesFunctions,
    3,
    'dfunc',
    '',
    'stat',
  ];
  SocialCalc.Formula.FunctionList['DSUM'] = [
    SocialCalc.Formula.DSeriesFunctions,
    3,
    'dfunc',
    '',
    'stat',
  ];
  SocialCalc.Formula.FunctionList['DVAR'] = [
    SocialCalc.Formula.DSeriesFunctions,
    3,
    'dfunc',
    '',
    'stat',
  ];
  SocialCalc.Formula.FunctionList['DVARP'] = [
    SocialCalc.Formula.DSeriesFunctions,
    3,
    'dfunc',
    '',
    'stat',
  ];
  FormulaMut.FieldToColnum = function (sheet, col1num, ncols, row1num, fieldname, fieldtype) {
    var colnum, cell, value;
    if (fieldtype.charAt(0) == 'n') {
      colnum = fieldname - 0;
      if (colnum <= 0 || colnum > ncols) {
        return 0;
      }
      return Math.floor(colnum);
    }
    if (fieldtype.charAt(0) != 't') {
      return 0;
    }
    fieldname = fieldname ? fieldname.toLowerCase() : '';
    for (colnum = 0; colnum < ncols; colnum++) {
      cell = sheet.GetAssuredCell(SocialCalc.crToCoord(col1num + colnum, row1num));
      value = cell.datavalue;
      value = (value + '').toLowerCase();
      if (value == fieldname) {
        return colnum + 1;
      }
    }
    return 0;
  };
  FormulaMut.LookupFunctions = function (fname, operand, foperand, sheet) {
    var lookupvalue, range, rangelookup, rangeinfo;
    var offsetvalue = 0;
    var c = 0,
      r = 0,
      csave = 0,
      rsave = 0;
    var cincr, rincr;
    var previousOK, cell, value, valuetype, cr;
    var scf = SocialCalc.Formula;
    var operand_value_and_type = scf.OperandValueAndType;
    var PushOperand = function (t, v) {
      operand.push({
        type: t,
        value: v,
      });
    };
    lookupvalue = operand_value_and_type(sheet, foperand);
    if (lookupvalue.type.charAt(0) == 'e') {
      PushOperand(lookupvalue.type, 0);
      return;
    }
    if (typeof lookupvalue.value == 'string') {
      lookupvalue.value = lookupvalue.value.toLowerCase();
    }
    range = scf.TopOfStackValueAndType(sheet, foperand);
    rangelookup = 1;
    if (fname == 'MATCH') {
      if (foperand.length) {
        rangelookup = scf.OperandAsNumber(sheet, foperand);
        if (rangelookup.type.charAt(0) != 'n') {
          PushOperand('e#VALUE!', 0);
          return;
        }
        if (foperand.length) {
          scf.FunctionArgsError(fname, operand);
          return 0;
        }
        rangelookup = rangelookup.value - 0;
      }
    } else {
      offsetvalue = scf.OperandAsNumber(sheet, foperand);
      if (offsetvalue.type.charAt(0) != 'n') {
        PushOperand('e#VALUE!', 0);
        return;
      }
      offsetvalue = Math.floor(offsetvalue.value);
      if (foperand.length) {
        rangelookup = scf.OperandAsNumber(sheet, foperand);
        if (rangelookup.type.charAt(0) != 'n') {
          PushOperand('e#VALUE!', 0);
          return;
        }
        if (foperand.length) {
          scf.FunctionArgsError(fname, operand);
          return 0;
        }
        rangelookup = rangelookup.value ? 1 : 0;
      }
    }
    lookupvalue.type = lookupvalue.type.charAt(0);
    if (lookupvalue.type == 'n') {
      lookupvalue.value = lookupvalue.value - 0;
    }
    if (range.type != 'range') {
      scf.FunctionArgsError(fname, operand);
      return 0;
    }
    rangeinfo = scf.DecodeRangeParts(sheet, range.value, range.type);
    if (!rangeinfo) {
      PushOperand('e#REF!', 0);
      return;
    }
    c = 0;
    r = 0;
    cincr = 0;
    rincr = 0;
    if (fname == 'HLOOKUP') {
      cincr = 1;
      if (offsetvalue > rangeinfo.nrows) {
        PushOperand('e#REF!', 0);
        return;
      }
    } else if (fname == 'VLOOKUP') {
      rincr = 1;
      if (offsetvalue > rangeinfo.ncols) {
        PushOperand('e#REF!', 0);
        return;
      }
    } else {
      if (rangeinfo.ncols > 1) {
        if (rangeinfo.nrows > 1) {
          PushOperand('e#N/A', 0);
          return;
        }
        cincr = 1;
      } else {
        rincr = 1;
      }
    }
    if (offsetvalue < 1 && fname != 'MATCH') {
      PushOperand('e#VALUE!', 0);
      return 0;
    }
    for (;;) {
      cr = SocialCalc.crToCoord(rangeinfo.col1num + c, rangeinfo.row1num + r);
      cell = rangeinfo.sheetdata.GetAssuredCell(cr);
      value = cell.datavalue;
      valuetype = cell.valuetype ? cell.valuetype.charAt(0) : 'b';
      if (valuetype == 'n') {
        value = value - 0;
      }
      if (rangelookup) {
        if (lookupvalue.type == 'n' && valuetype == 'n') {
          if (lookupvalue.value == value) {
            break;
          }
          if (
            (rangelookup > 0 && lookupvalue.value > value) ||
            (rangelookup < 0 && lookupvalue.value < value)
          ) {
            previousOK = 1;
            csave = c;
            rsave = r;
          } else if (previousOK) {
            previousOK = 2;
            break;
          }
        } else if (lookupvalue.type == 't' && valuetype == 't') {
          value = typeof value == 'string' ? value.toLowerCase() : '';
          if (lookupvalue.value == value) {
            break;
          }
          if (
            (rangelookup > 0 && lookupvalue.value > value) ||
            (rangelookup < 0 && lookupvalue.value < value)
          ) {
            previousOK = 1;
            csave = c;
            rsave = r;
          } else if (previousOK) {
            previousOK = 2;
            break;
          }
        }
      } else {
        if (lookupvalue.type == 'n' && valuetype == 'n') {
          if (lookupvalue.value == value) {
            break;
          }
        } else if (lookupvalue.type == 't' && valuetype == 't') {
          value = typeof value == 'string' ? value.toLowerCase() : '';
          if (lookupvalue.value == value) {
            break;
          }
        }
      }
      r += rincr;
      c += cincr;
      if (r >= rangeinfo.nrows || c >= rangeinfo.ncols) {
        if (previousOK) {
          previousOK = 2;
          break;
        }
        PushOperand('e#N/A', 0);
        return;
      }
    }
    if (previousOK == 2) {
      r = rsave;
      c = csave;
    }
    if (fname == 'MATCH') {
      value = c + r + 1;
      valuetype = 'n';
    } else {
      cr = SocialCalc.crToCoord(
        rangeinfo.col1num + c + (fname == 'VLOOKUP' ? offsetvalue - 1 : 0),
        rangeinfo.row1num + r + (fname == 'HLOOKUP' ? offsetvalue - 1 : 0),
      );
      cell = rangeinfo.sheetdata.GetAssuredCell(cr);
      value = cell.datavalue;
      valuetype = cell.valuetype;
    }
    PushOperand(valuetype, value);
    return;
  };
  SocialCalc.Formula.FunctionList['HLOOKUP'] = [
    SocialCalc.Formula.LookupFunctions,
    -3,
    'hlookup',
    '',
    'lookup',
  ];
  SocialCalc.Formula.FunctionList['MATCH'] = [
    SocialCalc.Formula.LookupFunctions,
    -2,
    'match',
    '',
    'lookup',
  ];
  SocialCalc.Formula.FunctionList['VLOOKUP'] = [
    SocialCalc.Formula.LookupFunctions,
    -3,
    'vlookup',
    '',
    'lookup',
  ];
  FormulaMut.IndexFunction = function (fname, operand, foperand, sheet) {
    var range, sheetname, indexinfo, rowindex, colindex, result, resulttype;
    var scf = SocialCalc.Formula;
    var PushOperand = function (t, v) {
      operand.push({
        type: t,
        value: v,
      });
    };
    range = scf.TopOfStackValueAndType(sheet, foperand);
    if (range.type != 'range') {
      scf.FunctionArgsError(fname, operand);
      return 0;
    }
    indexinfo = scf.DecodeRangeParts(sheet, range.value, range.type);
    if (indexinfo.sheetname) {
      sheetname = '!' + indexinfo.sheetname;
    } else {
      sheetname = '';
    }
    rowindex = { value: 0 };
    colindex = { value: 0 };
    if (foperand.length) {
      rowindex = scf.OperandAsNumber(sheet, foperand);
      if (rowindex.type.charAt(0) != 'n' || rowindex.value < 0) {
        PushOperand('e#VALUE!', 0);
        return;
      }
      if (foperand.length) {
        colindex = scf.OperandAsNumber(sheet, foperand);
        if (colindex.type.charAt(0) != 'n' || colindex.value < 0) {
          PushOperand('e#VALUE!', 0);
          return;
        }
        if (foperand.length) {
          scf.FunctionArgsError(fname, operand);
          return 0;
        }
      } else {
        if (indexinfo.nrows == 1) {
          colindex.value = rowindex.value;
          rowindex.value = 0;
        }
      }
    }
    if (rowindex.value > indexinfo.nrows || colindex.value > indexinfo.ncols) {
      PushOperand('e#REF!', 0);
      return;
    }
    if (rowindex.value == 0) {
      if (colindex.value == 0) {
        if (indexinfo.nrows == 1 && indexinfo.ncols == 1) {
          result = SocialCalc.crToCoord(indexinfo.col1num, indexinfo.row1num) + sheetname;
          resulttype = 'coord';
        } else {
          result =
            SocialCalc.crToCoord(indexinfo.col1num, indexinfo.row1num) +
            sheetname +
            '|' +
            SocialCalc.crToCoord(
              indexinfo.col1num + indexinfo.ncols - 1,
              indexinfo.row1num + indexinfo.nrows - 1,
            ) +
            '|';
          resulttype = 'range';
        }
      } else {
        if (indexinfo.nrows == 1) {
          result =
            SocialCalc.crToCoord(indexinfo.col1num + colindex.value - 1, indexinfo.row1num) +
            sheetname;
          resulttype = 'coord';
        } else {
          result =
            SocialCalc.crToCoord(indexinfo.col1num + colindex.value - 1, indexinfo.row1num) +
            sheetname +
            '|' +
            SocialCalc.crToCoord(
              indexinfo.col1num + colindex.value - 1,
              indexinfo.row1num + indexinfo.nrows - 1,
            ) +
            '|';
          resulttype = 'range';
        }
      }
    } else {
      if (colindex.value == 0) {
        if (indexinfo.ncols == 1) {
          result =
            SocialCalc.crToCoord(indexinfo.col1num, indexinfo.row1num + rowindex.value - 1) +
            sheetname;
          resulttype = 'coord';
        } else {
          result =
            SocialCalc.crToCoord(indexinfo.col1num, indexinfo.row1num + rowindex.value - 1) +
            sheetname +
            '|' +
            SocialCalc.crToCoord(
              indexinfo.col1num + indexinfo.ncols - 1,
              indexinfo.row1num + rowindex.value - 1,
            ) +
            '|';
          resulttype = 'range';
        }
      } else {
        result =
          SocialCalc.crToCoord(
            indexinfo.col1num + colindex.value - 1,
            indexinfo.row1num + rowindex.value - 1,
          ) + sheetname;
        resulttype = 'coord';
      }
    }
    PushOperand(resulttype, result);
    return;
  };
  SocialCalc.Formula.FunctionList['INDEX'] = [
    SocialCalc.Formula.IndexFunction,
    -1,
    'index',
    '',
    'lookup',
  ];
  FormulaMut.CountifSumifFunctions = function (fname, operand, foperand, sheet) {
    var range, criteria, sumrange, f2operand, value1, value2;
    var sum = 0;
    var resulttypesum = '';
    var count = 0;
    var scf = SocialCalc.Formula;
    var operand_value_and_type = scf.OperandValueAndType;
    var lookup_result_type = scf.LookupResultType;
    var typelookupplus = scf.TypeLookupTable.plus;
    var PushOperand = function (t, v) {
      operand.push({
        type: t,
        value: v,
      });
    };
    range = scf.TopOfStackValueAndType(sheet, foperand);
    criteria = scf.OperandAsText(sheet, foperand);
    if (fname == 'SUMIF') {
      if (foperand.length == 1) {
        sumrange = scf.TopOfStackValueAndType(sheet, foperand);
      } else if (foperand.length == 0) {
        sumrange = {
          value: range.value,
          type: range.type,
        };
      } else {
        scf.FunctionArgsError(fname, operand);
        return 0;
      }
    } else {
      sumrange = {
        value: range.value,
        type: range.type,
      };
    }
    if (criteria.type.charAt(0) == 'e') criteria.value = null;
    if (range.type != 'coord' && range.type != 'range') {
      scf.FunctionArgsError(fname, operand);
      return 0;
    }
    if (fname == 'SUMIF' && sumrange.type != 'coord' && sumrange.type != 'range') {
      scf.FunctionArgsError(fname, operand);
      return 0;
    }
    foperand.push(range);
    f2operand = [];
    f2operand.push(sumrange);
    while (foperand.length) {
      value1 = operand_value_and_type(sheet, foperand);
      value2 = operand_value_and_type(sheet, f2operand);
      if (!scf.TestCriteria(value1.value, value1.type, criteria.value)) {
        continue;
      }
      count += 1;
      if (value2.type.charAt(0) == 'n') {
        sum += value2.value - 0;
        resulttypesum = lookup_result_type(
          value2.type,
          resulttypesum || value2.type,
          typelookupplus,
        );
      } else if (value2.type.charAt(0) == 'e' && resulttypesum.charAt(0) != 'e') {
        resulttypesum = value2.type;
      }
    }
    resulttypesum = resulttypesum || 'n';
    if (fname == 'SUMIF') {
      PushOperand(resulttypesum, sum);
    } else if (fname == 'COUNTIF') {
      PushOperand('n', count);
    }
    return;
  };
  SocialCalc.Formula.FunctionList['COUNTIF'] = [
    SocialCalc.Formula.CountifSumifFunctions,
    2,
    'rangec',
    '',
    'stat',
  ];
  SocialCalc.Formula.FunctionList['SUMIF'] = [
    SocialCalc.Formula.CountifSumifFunctions,
    -2,
    'sumif',
    '',
    'stat',
  ];
  FormulaMut.SumifsFunction = function (fname, operand, foperand, sheet) {
    var range, criteria, sumrange, f2operand, value1, value2;
    var sum = 0;
    var resulttypesum = '';
    var scf = SocialCalc.Formula;
    var operand_value_and_type = scf.OperandValueAndType;
    var lookup_result_type = scf.LookupResultType;
    var typelookupplus = scf.TypeLookupTable.plus;
    var PushOperand = function (t, v) {
      operand.push({
        type: t,
        value: v,
      });
    };
    sumrange = scf.TopOfStackValueAndType(sheet, foperand);
    if (sumrange.type != 'coord' && sumrange.type != 'range') {
      scf.FunctionArgsError(fname, operand);
      return 0;
    }
    var ranges = [],
      criterias = [];
    while (foperand.length) {
      range = scf.TopOfStackValueAndType(sheet, foperand);
      criteria = scf.OperandAsText(sheet, foperand);
      if (criteria.type.charAt(0) == 'e') criteria.value = null;
      if (range.type != 'coord' && range.type != 'range') {
        scf.FunctionArgsError(fname, operand);
        return 0;
      }
      ranges.push([range]);
      criterias.push(criteria);
    }
    f2operand = [];
    f2operand.push(sumrange);
    while (f2operand.length) {
      value2 = operand_value_and_type(sheet, f2operand);
      var all_good = true;
      for (var i = 0; i < ranges.length; i++) {
        value1 = operand_value_and_type(sheet, ranges[i]);
        if (!all_good) {
          continue;
        }
        if (!scf.TestCriteria(value1.value, value1.type, criterias[i].value)) {
          all_good = false;
        }
      }
      if (!all_good) {
        continue;
      }
      if (value2.type.charAt(0) == 'n') {
        sum += value2.value - 0;
        resulttypesum = lookup_result_type(
          value2.type,
          resulttypesum || value2.type,
          typelookupplus,
        );
      } else if (value2.type.charAt(0) == 'e' && resulttypesum.charAt(0) != 'e') {
        resulttypesum = value2.type;
      }
    }
    resulttypesum = resulttypesum || 'n';
    PushOperand(resulttypesum, sum);
    return;
  };
  SocialCalc.Formula.FunctionList['SUMIFS'] = [
    SocialCalc.Formula.SumifsFunction,
    -3,
    'sum_range, criteria_range1, criteria1, [criteria_range2, criteria2, ... criteria_range_n, criteria_n]',
    '',
    'stat',
  ];
  FormulaMut.IfFunction = function (fname, operand, foperand, sheet) {
    var cond, t;
    var scf = SocialCalc.Formula;
    cond = SocialCalc.Formula.OperandValueAndType(sheet, foperand);
    t = cond.type.charAt(0);
    if (t == 'e') {
      operand.push({
        type: cond.type,
        value: 0,
      });
      return;
    }
    if (t != 'n' && t != 'b') {
      operand.push({
        type: 'e#VALUE!',
        value: 0,
      });
      return;
    }
    var op1, op2;
    op1 = foperand.pop();
    if (foperand.length == 1) {
      op2 = foperand.pop();
    } else if (foperand.length == 0) {
      op2 = {
        type: 'n',
        value: 0,
      };
    } else {
      scf.FunctionArgsError(fname, operand);
      return;
    }
    operand.push(cond.value ? op1 : op2);
  };
  SocialCalc.Formula.FunctionList['IF'] = [SocialCalc.Formula.IfFunction, -2, 'iffunc', '', 'test'];
  FormulaMut.DateFunction = function (fname, operand, foperand, sheet) {
    var scf = SocialCalc.Formula;
    var result = 0;
    var year = scf.OperandAsNumber(sheet, foperand);
    var month = scf.OperandAsNumber(sheet, foperand);
    var day = scf.OperandAsNumber(sheet, foperand);
    var resulttype = scf.LookupResultType(year.type, month.type, scf.TypeLookupTable.twoargnumeric);
    resulttype = scf.LookupResultType(resulttype, day.type, scf.TypeLookupTable.twoargnumeric);
    if (resulttype.charAt(0) == 'n') {
      result =
        SocialCalc.FormatNumber.convert_date_gregorian_to_julian(
          Math.floor(year.value),
          Math.floor(month.value),
          Math.floor(day.value),
        ) - SocialCalc.FormatNumber.datevalues.julian_offset;
      resulttype = 'nd';
    }
    scf.PushOperand(operand, resulttype, result);
    return;
  };
  SocialCalc.Formula.FunctionList['DATE'] = [
    SocialCalc.Formula.DateFunction,
    3,
    'date',
    '',
    'datetime',
  ];
  FormulaMut.TimeFunction = function (fname, operand, foperand, sheet) {
    var scf = SocialCalc.Formula;
    var result = 0;
    var hours = scf.OperandAsNumber(sheet, foperand);
    var minutes = scf.OperandAsNumber(sheet, foperand);
    var seconds = scf.OperandAsNumber(sheet, foperand);
    var resulttype = scf.LookupResultType(
      hours.type,
      minutes.type,
      scf.TypeLookupTable.twoargnumeric,
    );
    resulttype = scf.LookupResultType(resulttype, seconds.type, scf.TypeLookupTable.twoargnumeric);
    if (resulttype.charAt(0) == 'n') {
      result = (hours.value * 60 * 60 + minutes.value * 60 + seconds.value) / (24 * 60 * 60);
      resulttype = 'nt';
    }
    scf.PushOperand(operand, resulttype, result);
    return;
  };
  SocialCalc.Formula.FunctionList['TIME'] = [
    SocialCalc.Formula.TimeFunction,
    3,
    'hms',
    '',
    'datetime',
  ];
  FormulaMut.DMYFunctions = function (fname, operand, foperand, sheet) {
    var ymd, dtype, doffset;
    var scf = SocialCalc.Formula;
    var result = 0;
    var datevalue = scf.OperandAsNumber(sheet, foperand);
    var resulttype = scf.LookupResultType(
      datevalue.type,
      datevalue.type,
      scf.TypeLookupTable.oneargnumeric,
    );
    if (resulttype.charAt(0) == 'n') {
      ymd = SocialCalc.FormatNumber.convert_date_julian_to_gregorian(
        Math.floor(datevalue.value + SocialCalc.FormatNumber.datevalues.julian_offset),
      );
      switch (fname) {
        case 'DAY':
          result = ymd.day;
          break;
        case 'MONTH':
          result = ymd.month;
          break;
        case 'YEAR':
          result = ymd.year;
          break;
        case 'WEEKDAY':
          dtype = { value: 1 };
          if (foperand.length) {
            dtype = scf.OperandAsNumber(sheet, foperand);
            if (dtype.type.charAt(0) != 'n' || dtype.value < 1 || dtype.value > 3) {
              scf.PushOperand(operand, 'e#VALUE!', 0);
              return;
            }
            if (foperand.length) {
              scf.FunctionArgsError(fname, operand);
              return;
            }
          }
          doffset = 6;
          if (dtype.value > 1) {
            doffset -= 1;
          }
          result = (Math.floor(datevalue.value + doffset) % 7) + (dtype.value < 3 ? 1 : 0);
          break;
      }
    }
    scf.PushOperand(operand, resulttype, result);
    return;
  };
  SocialCalc.Formula.FunctionList['DAY'] = [
    SocialCalc.Formula.DMYFunctions,
    1,
    'v',
    '',
    'datetime',
  ];
  SocialCalc.Formula.FunctionList['MONTH'] = [
    SocialCalc.Formula.DMYFunctions,
    1,
    'v',
    '',
    'datetime',
  ];
  SocialCalc.Formula.FunctionList['YEAR'] = [
    SocialCalc.Formula.DMYFunctions,
    1,
    'v',
    '',
    'datetime',
  ];
  SocialCalc.Formula.FunctionList['WEEKDAY'] = [
    SocialCalc.Formula.DMYFunctions,
    -1,
    'weekday',
    '',
    'datetime',
  ];
  FormulaMut.HMSFunctions = function (fname, operand, foperand, sheet) {
    var hours, minutes, seconds, fraction;
    var scf = SocialCalc.Formula;
    var result = 0;
    var datetime = scf.OperandAsNumber(sheet, foperand);
    var resulttype = scf.LookupResultType(
      datetime.type,
      datetime.type,
      scf.TypeLookupTable.oneargnumeric,
    );
    if (resulttype.charAt(0) == 'n') {
      if (datetime.value < 0) {
        scf.PushOperand(operand, 'e#NUM!', 0);
        return;
      }
      fraction = datetime.value - Math.floor(datetime.value);
      fraction *= 24;
      hours = Math.floor(fraction);
      fraction -= Math.floor(fraction);
      fraction *= 60;
      minutes = Math.floor(fraction);
      fraction -= Math.floor(fraction);
      fraction *= 60;
      seconds = Math.floor(fraction + (datetime.value >= 0 ? 0.5 : -0.5));
      if (fname == 'HOUR') {
        result = hours;
      } else if (fname == 'MINUTE') {
        result = minutes;
      } else if (fname == 'SECOND') {
        result = seconds;
      }
    }
    scf.PushOperand(operand, resulttype, result);
    return;
  };
  SocialCalc.Formula.FunctionList['HOUR'] = [
    SocialCalc.Formula.HMSFunctions,
    1,
    'v',
    '',
    'datetime',
  ];
  SocialCalc.Formula.FunctionList['MINUTE'] = [
    SocialCalc.Formula.HMSFunctions,
    1,
    'v',
    '',
    'datetime',
  ];
  SocialCalc.Formula.FunctionList['SECOND'] = [
    SocialCalc.Formula.HMSFunctions,
    1,
    'v',
    '',
    'datetime',
  ];
  FormulaMut.ExactFunction = function (fname, operand, foperand, sheet) {
    var scf = SocialCalc.Formula;
    var result = 0;
    var resulttype = 'nl';
    var value1 = scf.OperandValueAndType(sheet, foperand);
    var v1type = value1.type.charAt(0);
    var value2 = scf.OperandValueAndType(sheet, foperand);
    var v2type = value2.type.charAt(0);
    if (v1type == 't') {
      if (v2type == 't') {
        result = value1.value == value2.value ? 1 : 0;
      } else if (v2type == 'b') {
        result = value1.value.length ? 0 : 1;
      } else if (v2type == 'n') {
        result = value1.value == value2.value + '' ? 1 : 0;
      } else if (v2type == 'e') {
        result = value2.value;
        resulttype = value2.type;
      }
    } else if (v1type == 'n') {
      if (v2type == 'n') {
        result = value1.value - 0 == value2.value - 0 ? 1 : 0;
      } else if (v2type == 'b') {
        result = 0;
      } else if (v2type == 't') {
        result = value1.value + '' == value2.value ? 1 : 0;
      } else if (v2type == 'e') {
        result = value2.value;
        resulttype = value2.type;
      }
    } else if (v1type == 'b') {
      if (v2type == 't') {
        result = value2.value.length ? 0 : 1;
      } else if (v2type == 'b') {
        result = 1;
      } else if (v2type == 'n') {
        result = 0;
      } else if (v2type == 'e') {
        result = value2.value;
        resulttype = value2.type;
      }
    } else if (v1type == 'e') {
      result = value1.value;
      resulttype = value1.type;
    }
    scf.PushOperand(operand, resulttype, result);
    return;
  };
  SocialCalc.Formula.FunctionList['EXACT'] = [SocialCalc.Formula.ExactFunction, 2, '', '', 'text'];
  FormulaMut.ArgList = {
    FIND: [1, 1, 0],
    LEFT: [1, 0],
    LEN: [1],
    LOWER: [1],
    MID: [1, 0, 0],
    PROPER: [1],
    REPLACE: [1, 0, 0, 1],
    REPT: [1, 0],
    RIGHT: [1, 0],
    SUBSTITUTE: [1, 1, 1, 0],
    TRIM: [1],
    HEXCODE: [1],
    UPPER: [1],
  };
  FormulaMut.StringFunctions = function (fname, operand, foperand, sheet) {
    var i, value, offset, len, start, count;
    var fulltext, oldtext, newtext, which, oldpos, pos;
    var scf = SocialCalc.Formula;
    var result = 0;
    var resulttype = 'e#VALUE!';
    var numargs = foperand.length;
    var argdef = scf.ArgList[fname];
    var operand_value = [];
    var operand_type = [];
    for (i = 1; i <= numargs; i++) {
      if (i > argdef.length) {
        scf.FunctionArgsError(fname, operand);
        return;
      }
      if (argdef[i - 1] == 0) value = scf.OperandAsNumber(sheet, foperand);
      else value = scf.OperandAsText(sheet, foperand);
      operand_value[i] = value.value;
      operand_type[i] = value.type;
      if (value.type.charAt(0) == 'e') {
        scf.PushOperand(operand, value.type, result);
        return;
      }
    }
    switch (fname) {
      case 'FIND':
        offset = operand_type[3] ? operand_value[3] - 1 : 0;
        if (offset < 0) {
          result = 'Start is before string';
        } else {
          result = operand_value[2].indexOf(operand_value[1], offset);
          if (result >= 0) {
            result += 1;
            resulttype = 'n';
          } else {
            result = 'Not found';
          }
        }
        break;
      case 'LEFT':
        len = operand_type[2] ? operand_value[2] - 0 : 1;
        if (len < 0) {
          result = 'Negative length';
        } else {
          result = operand_value[1].substring(0, len);
          resulttype = 't';
        }
        break;
      case 'LEN':
        result = operand_value[1].length;
        resulttype = 'n';
        break;
      case 'LOWER':
        result = operand_value[1].toLowerCase();
        resulttype = 't';
        break;
      case 'MID':
        start = operand_value[2] - 0;
        len = operand_value[3] - 0;
        if (len < 1 || start < 1) {
          result = 'Bad arguments';
        } else {
          result = operand_value[1].substring(start - 1, start + len - 1);
          resulttype = 't';
        }
        break;
      case 'PROPER':
        result = operand_value[1].replace(/\b\w+\b/g, function (word) {
          return word.substring(0, 1).toUpperCase() + word.substring(1);
        });
        resulttype = 't';
        break;
      case 'REPLACE':
        start = operand_value[2] - 0;
        len = operand_value[3] - 0;
        if (len < 0 || start < 1) {
          result = 'Bad arguments';
        } else {
          result =
            operand_value[1].substring(0, start - 1) +
            operand_value[4] +
            operand_value[1].substring(start - 1 + len);
          resulttype = 't';
        }
        break;
      case 'REPT':
        count = operand_value[2] - 0;
        if (count < 0) {
          result = 'Negative count';
        } else {
          result = '';
          for (; count > 0; count--) {
            result += operand_value[1];
          }
          resulttype = 't';
        }
        break;
      case 'RIGHT':
        len = operand_type[2] ? operand_value[2] - 0 : 1;
        if (len < 0) {
          result = 'Negative length';
        } else {
          result = operand_value[1].slice(-len);
          resulttype = 't';
        }
        break;
      case 'SUBSTITUTE':
        fulltext = operand_value[1];
        oldtext = operand_value[2];
        newtext = operand_value[3];
        if (operand_value[4] != null) {
          which = operand_value[4] - 0;
          if (which <= 0) {
            result = 'Non-positive instance number';
            break;
          }
        } else {
          which = 0;
        }
        count = 0;
        oldpos = 0;
        result = '';
        while (true) {
          pos = fulltext.indexOf(oldtext, oldpos);
          if (pos >= 0) {
            count++;
            result += fulltext.substring(oldpos, pos);
            if (which == 0) {
              result += newtext;
            } else if (which == count) {
              result += newtext + fulltext.substring(pos + oldtext.length);
              break;
            } else {
              result += oldtext;
            }
            oldpos = pos + oldtext.length;
          } else {
            result += fulltext.substring(oldpos);
            break;
          }
        }
        resulttype = 't';
        break;
      case 'TRIM':
        result = operand_value[1];
        result = result.replace(/^ */, '');
        result = result.replace(/ *$/, '');
        result = result.replace(/ +/g, ' ');
        resulttype = 't';
        break;
      case 'HEXCODE':
        result = String(operand_value[1]);
        var code = result.charCodeAt(0);
        if (55296 <= code && code <= 56319) {
          var next = result.charCodeAt(1);
          if (56320 <= next && next <= 57343) {
            code = (code - 55296) * 1024 + (next - 56320) + 65536;
          }
        }
        result = code.toString(16).toUpperCase();
        resulttype = 't';
        break;
      case 'UPPER':
        result = operand_value[1].toUpperCase();
        resulttype = 't';
        break;
    }
    scf.PushOperand(operand, resulttype, result);
    return;
  };
  SocialCalc.Formula.FunctionList['FIND'] = [
    SocialCalc.Formula.StringFunctions,
    -2,
    'find',
    '',
    'text',
  ];
  SocialCalc.Formula.FunctionList['LEFT'] = [
    SocialCalc.Formula.StringFunctions,
    -2,
    'tc',
    '',
    'text',
  ];
  SocialCalc.Formula.FunctionList['LEN'] = [
    SocialCalc.Formula.StringFunctions,
    1,
    'txt',
    '',
    'text',
  ];
  SocialCalc.Formula.FunctionList['LOWER'] = [
    SocialCalc.Formula.StringFunctions,
    1,
    'txt',
    '',
    'text',
  ];
  SocialCalc.Formula.FunctionList['MID'] = [
    SocialCalc.Formula.StringFunctions,
    3,
    'mid',
    '',
    'text',
  ];
  SocialCalc.Formula.FunctionList['PROPER'] = [
    SocialCalc.Formula.StringFunctions,
    1,
    'v',
    '',
    'text',
  ];
  SocialCalc.Formula.FunctionList['REPLACE'] = [
    SocialCalc.Formula.StringFunctions,
    4,
    'replace',
    '',
    'text',
  ];
  SocialCalc.Formula.FunctionList['REPT'] = [
    SocialCalc.Formula.StringFunctions,
    2,
    'tc',
    '',
    'text',
  ];
  SocialCalc.Formula.FunctionList['RIGHT'] = [
    SocialCalc.Formula.StringFunctions,
    -1,
    'tc',
    '',
    'text',
  ];
  SocialCalc.Formula.FunctionList['SUBSTITUTE'] = [
    SocialCalc.Formula.StringFunctions,
    -3,
    'subs',
    '',
    'text',
  ];
  SocialCalc.Formula.FunctionList['TRIM'] = [
    SocialCalc.Formula.StringFunctions,
    1,
    'v',
    '',
    'text',
  ];
  SocialCalc.Formula.FunctionList['HEXCODE'] = [
    SocialCalc.Formula.StringFunctions,
    1,
    'v',
    '',
    'text',
  ];
  SocialCalc.Formula.FunctionList['UPPER'] = [
    SocialCalc.Formula.StringFunctions,
    1,
    'v',
    '',
    'text',
  ];
  FormulaMut.IsFunctions = function (fname, operand, foperand, sheet) {
    var scf = SocialCalc.Formula;
    var result = 0;
    var resulttype = 'nl';
    var value = scf.OperandValueAndType(sheet, foperand);
    var t = value.type.charAt(0);
    switch (fname) {
      case 'ISBLANK':
        result = value.type == 'b' ? 1 : 0;
        break;
      case 'ISERR':
        result = t == 'e' ? (value.type == 'e#N/A' ? 0 : 1) : 0;
        break;
      case 'ISERROR':
        result = t == 'e' ? 1 : 0;
        break;
      case 'ISLOGICAL':
        result = value.type == 'nl' ? 1 : 0;
        break;
      case 'ISNA':
        result = value.type == 'e#N/A' ? 1 : 0;
        break;
      case 'ISNONTEXT':
        result = t == 't' ? 0 : 1;
        break;
      case 'ISNUMBER':
        result = t == 'n' ? 1 : 0;
        break;
      case 'ISTEXT':
        result = t == 't' ? 1 : 0;
        break;
    }
    scf.PushOperand(operand, resulttype, result);
    return;
  };
  SocialCalc.Formula.FunctionList['ISBLANK'] = [SocialCalc.Formula.IsFunctions, 1, 'v', '', 'test'];
  SocialCalc.Formula.FunctionList['ISERR'] = [SocialCalc.Formula.IsFunctions, 1, 'v', '', 'test'];
  SocialCalc.Formula.FunctionList['ISERROR'] = [SocialCalc.Formula.IsFunctions, 1, 'v', '', 'test'];
  SocialCalc.Formula.FunctionList['ISLOGICAL'] = [
    SocialCalc.Formula.IsFunctions,
    1,
    'v',
    '',
    'test',
  ];
  SocialCalc.Formula.FunctionList['ISNA'] = [SocialCalc.Formula.IsFunctions, 1, 'v', '', 'test'];
  SocialCalc.Formula.FunctionList['ISNONTEXT'] = [
    SocialCalc.Formula.IsFunctions,
    1,
    'v',
    '',
    'test',
  ];
  SocialCalc.Formula.FunctionList['ISNUMBER'] = [
    SocialCalc.Formula.IsFunctions,
    1,
    'v',
    '',
    'test',
  ];
  SocialCalc.Formula.FunctionList['ISTEXT'] = [SocialCalc.Formula.IsFunctions, 1, 'v', '', 'test'];
  FormulaMut.NTVFunctions = function (fname, operand, foperand, sheet) {
    var scf = SocialCalc.Formula;
    var result = 0;
    var resulttype = 'e#VALUE!';
    var value = scf.OperandValueAndType(sheet, foperand);
    var t = value.type.charAt(0);
    switch (fname) {
      case 'N':
        result = t == 'n' ? value.value - 0 : 0;
        resulttype = 'n';
        break;
      case 'T':
        result = t == 't' ? value.value + '' : '';
        resulttype = 't';
        break;
      case 'VALUE':
        if (t == 'n' || t == 'b') {
          result = value.value || 0;
          resulttype = 'n';
        } else if (t == 't') {
          value = SocialCalc.DetermineValueType(value.value);
          if (value.type.charAt(0) != 'n') {
            result = 0;
            resulttype = 'e#VALUE!';
          } else {
            result = value.value - 0;
            resulttype = 'n';
          }
        }
        break;
    }
    if (t == 'e') {
      resulttype = value.type;
    }
    scf.PushOperand(operand, resulttype, result);
    return;
  };
  SocialCalc.Formula.FunctionList['N'] = [SocialCalc.Formula.NTVFunctions, 1, 'v', '', 'math'];
  SocialCalc.Formula.FunctionList['T'] = [SocialCalc.Formula.NTVFunctions, 1, 'v', '', 'text'];
  SocialCalc.Formula.FunctionList['VALUE'] = [SocialCalc.Formula.NTVFunctions, 1, 'v', '', 'text'];
  FormulaMut.Math1Functions = function (fname, operand, foperand, sheet) {
    var v1, value, f;
    var result = {};
    var scf = SocialCalc.Formula;
    v1 = scf.OperandAsNumber(sheet, foperand);
    value = v1.value;
    result.type = scf.LookupResultType(v1.type, v1.type, scf.TypeLookupTable.oneargnumeric);
    if (result.type == 'n') {
      switch (fname) {
        case 'ABS':
          value = Math.abs(value);
          break;
        case 'ACOS':
          if (value >= -1 && value <= 1) {
            value = Math.acos(value);
          } else {
            result.type = 'e#NUM!';
          }
          break;
        case 'ASIN':
          if (value >= -1 && value <= 1) {
            value = Math.asin(value);
          } else {
            result.type = 'e#NUM!';
          }
          break;
        case 'ATAN':
          value = Math.atan(value);
          break;
        case 'COS':
          value = Math.cos(value);
          break;
        case 'DEGREES':
          value = (value * 180) / Math.PI;
          break;
        case 'EVEN':
          value = value < 0 ? -value : value;
          if (value != Math.floor(value)) {
            value = Math.floor(value + 1) + (Math.floor(value + 1) % 2);
          } else {
            value = value + (value % 2);
          }
          if (v1.value < 0) value = -value;
          break;
        case 'EXP':
          value = Math.exp(value);
          break;
        case 'FACT':
          f = 1;
          value = Math.floor(value);
          for (; value > 0; value--) {
            f *= value;
          }
          value = f;
          break;
        case 'INT':
          value = Math.floor(value);
          break;
        case 'LN':
          if (value <= 0) {
            result.type = 'e#NUM!';
            result.error = SocialCalc.Constants.s_sheetfunclnarg;
          }
          value = Math.log(value);
          break;
        case 'LOG10':
          if (value <= 0) {
            result.type = 'e#NUM!';
            result.error = SocialCalc.Constants.s_sheetfunclog10arg;
          }
          value = Math.log(value) / Math.log(10);
          break;
        case 'ODD':
          value = value < 0 ? -value : value;
          if (value != Math.floor(value)) {
            value = Math.floor(value + 1) + (1 - (Math.floor(value + 1) % 2));
          } else {
            value = value + (1 - (value % 2));
          }
          if (v1.value < 0) value = -value;
          break;
        case 'RADIANS':
          value = (value * Math.PI) / 180;
          break;
        case 'SIN':
          value = Math.sin(value);
          break;
        case 'SQRT':
          if (value >= 0) {
            value = Math.sqrt(value);
          } else {
            result.type = 'e#NUM!';
          }
          break;
        case 'TAN':
          if (Math.cos(value) != 0) {
            value = Math.tan(value);
          } else {
            result.type = 'e#NUM!';
          }
          break;
      }
    }
    result.value = value;
    operand.push(result);
    return null;
  };
  SocialCalc.Formula.FunctionList['ABS'] = [SocialCalc.Formula.Math1Functions, 1, 'v', '', 'math'];
  SocialCalc.Formula.FunctionList['ACOS'] = [SocialCalc.Formula.Math1Functions, 1, 'v', '', 'math'];
  SocialCalc.Formula.FunctionList['ASIN'] = [SocialCalc.Formula.Math1Functions, 1, 'v', '', 'math'];
  SocialCalc.Formula.FunctionList['ATAN'] = [SocialCalc.Formula.Math1Functions, 1, 'v', '', 'math'];
  SocialCalc.Formula.FunctionList['COS'] = [SocialCalc.Formula.Math1Functions, 1, 'v', '', 'math'];
  SocialCalc.Formula.FunctionList['DEGREES'] = [
    SocialCalc.Formula.Math1Functions,
    1,
    'v',
    '',
    'math',
  ];
  SocialCalc.Formula.FunctionList['EVEN'] = [SocialCalc.Formula.Math1Functions, 1, 'v', '', 'math'];
  SocialCalc.Formula.FunctionList['EXP'] = [SocialCalc.Formula.Math1Functions, 1, 'v', '', 'math'];
  SocialCalc.Formula.FunctionList['FACT'] = [SocialCalc.Formula.Math1Functions, 1, 'v', '', 'math'];
  SocialCalc.Formula.FunctionList['INT'] = [SocialCalc.Formula.Math1Functions, 1, 'v', '', 'math'];
  SocialCalc.Formula.FunctionList['LN'] = [SocialCalc.Formula.Math1Functions, 1, 'v', '', 'math'];
  SocialCalc.Formula.FunctionList['LOG10'] = [
    SocialCalc.Formula.Math1Functions,
    1,
    'v',
    '',
    'math',
  ];
  SocialCalc.Formula.FunctionList['ODD'] = [SocialCalc.Formula.Math1Functions, 1, 'v', '', 'math'];
  SocialCalc.Formula.FunctionList['RADIANS'] = [
    SocialCalc.Formula.Math1Functions,
    1,
    'v',
    '',
    'math',
  ];
  SocialCalc.Formula.FunctionList['SIN'] = [SocialCalc.Formula.Math1Functions, 1, 'v', '', 'math'];
  SocialCalc.Formula.FunctionList['SQRT'] = [SocialCalc.Formula.Math1Functions, 1, 'v', '', 'math'];
  SocialCalc.Formula.FunctionList['TAN'] = [SocialCalc.Formula.Math1Functions, 1, 'v', '', 'math'];
  FormulaMut.Math2Functions = function (fname, operand, foperand, sheet) {
    var xval, yval, quotient, decimalscale, i;
    var result = {};
    var scf = SocialCalc.Formula;
    xval = scf.OperandAsNumber(sheet, foperand);
    yval = scf.OperandAsNumber(sheet, foperand);
    result.type = scf.LookupResultType(xval.type, yval.type, scf.TypeLookupTable.twoargnumeric);
    if (result.type == 'n') {
      switch (fname) {
        case 'ATAN2':
          if (xval.value == 0 && yval.value == 0) {
            result.type = 'e#DIV/0!';
          } else {
            result.value = Math.atan2(yval.value, xval.value);
          }
          break;
        case 'POWER':
          result.value = Math.pow(xval.value, yval.value);
          if (isNaN(result.value)) {
            result.value = 0;
            result.type = 'e#NUM!';
          }
          break;
        case 'MOD':
          if (yval.value == 0) {
            result.type = 'e#DIV/0!';
          } else {
            quotient = xval.value / yval.value;
            quotient = Math.floor(quotient);
            result.value = xval.value - quotient * yval.value;
          }
          break;
        case 'TRUNC':
          decimalscale = 1;
          if (yval.value >= 0) {
            yval.value = Math.floor(yval.value);
            for (i = 0; i < yval.value; i++) {
              decimalscale *= 10;
            }
            result.value = Math.floor(Math.abs(xval.value) * decimalscale) / decimalscale;
          } else if (yval.value < 0) {
            yval.value = Math.floor(-yval.value);
            for (i = 0; i < yval.value; i++) {
              decimalscale *= 10;
            }
            result.value = Math.floor(Math.abs(xval.value) / decimalscale) * decimalscale;
          }
          if (xval.value < 0) {
            result.value = -result.value;
          }
      }
    }
    operand.push(result);
    return null;
  };
  SocialCalc.Formula.FunctionList['ATAN2'] = [
    SocialCalc.Formula.Math2Functions,
    2,
    'xy',
    '',
    'math',
  ];
  SocialCalc.Formula.FunctionList['MOD'] = [SocialCalc.Formula.Math2Functions, 2, '', '', 'math'];
  SocialCalc.Formula.FunctionList['POWER'] = [SocialCalc.Formula.Math2Functions, 2, '', '', 'math'];
  SocialCalc.Formula.FunctionList['TRUNC'] = [
    SocialCalc.Formula.Math2Functions,
    2,
    'valpre',
    '',
    'math',
  ];
  FormulaMut.LogFunction = function (fname, operand, foperand, sheet) {
    var value, value2;
    var result = {};
    var scf = SocialCalc.Formula;
    result.value = 0;
    value = scf.OperandAsNumber(sheet, foperand);
    result.type = scf.LookupResultType(value.type, value.type, scf.TypeLookupTable.oneargnumeric);
    if (foperand.length == 1) {
      value2 = scf.OperandAsNumber(sheet, foperand);
      if (value2.type.charAt(0) != 'n' || value2.value <= 0) {
        scf.FunctionSpecificError(
          fname,
          operand,
          'e#NUM!',
          SocialCalc.Constants.s_sheetfunclogsecondarg,
        );
        return 0;
      }
    } else if (foperand.length != 0) {
      scf.FunctionArgsError(fname, operand);
      return 0;
    } else {
      value2 = {
        value: Math.E,
        type: 'n',
      };
    }
    if (result.type == 'n') {
      if (value.value <= 0) {
        scf.FunctionSpecificError(
          fname,
          operand,
          'e#NUM!',
          SocialCalc.Constants.s_sheetfunclogfirstarg,
        );
        return 0;
      }
      result.value = Math.log(value.value) / Math.log(value2.value);
    }
    operand.push(result);
    return;
  };
  SocialCalc.Formula.FunctionList['LOG'] = [SocialCalc.Formula.LogFunction, -1, 'log', '', 'math'];
  FormulaMut.RoundFunction = function (fname, operand, foperand, sheet) {
    var value2, decimalscale, scaledvalue, i;
    var scf = SocialCalc.Formula;
    var result = 0;
    var resulttype = 'e#VALUE!';
    var value = scf.OperandValueAndType(sheet, foperand);
    resulttype = scf.LookupResultType(value.type, value.type, scf.TypeLookupTable.oneargnumeric);
    if (foperand.length == 1) {
      value2 = scf.OperandValueAndType(sheet, foperand);
      if (value2.type.charAt(0) != 'n') {
        scf.FunctionSpecificError(
          fname,
          operand,
          'e#NUM!',
          SocialCalc.Constants.s_sheetfuncroundsecondarg,
        );
        return 0;
      }
    } else if (foperand.length != 0) {
      scf.FunctionArgsError(fname, operand);
      return 0;
    } else {
      value2 = {
        value: 0,
        type: 'n',
      };
    }
    if (resulttype == 'n') {
      value2.value = value2.value - 0;
      if (value2.value == 0) {
        result = Math.round(value.value);
      } else if (value2.value > 0) {
        decimalscale = 1;
        value2.value = Math.floor(value2.value);
        for (i = 0; i < value2.value; i++) {
          decimalscale *= 10;
        }
        scaledvalue = Math.round(value.value * decimalscale);
        result = scaledvalue / decimalscale;
      } else if (value2.value < 0) {
        decimalscale = 1;
        value2.value = Math.floor(-value2.value);
        for (i = 0; i < value2.value; i++) {
          decimalscale *= 10;
        }
        scaledvalue = Math.round(value.value / decimalscale);
        result = scaledvalue * decimalscale;
      }
    }
    scf.PushOperand(operand, resulttype, result);
    return;
  };
  SocialCalc.Formula.FunctionList['ROUND'] = [
    SocialCalc.Formula.RoundFunction,
    -1,
    'vp',
    '',
    'math',
  ];
  FormulaMut.CeilingFloorFunctions = function (fname, operand, foperand, sheet) {
    var scf = SocialCalc.Formula;
    var val, sig, t;
    var PushOperand = function (t, v) {
      operand.push({
        type: t,
        value: v,
      });
    };
    val = scf.OperandValueAndType(sheet, foperand);
    t = val.type.charAt(0);
    if (t != 'n') {
      PushOperand('e#VALUE!', 0);
      return;
    }
    if (val.value == 0) {
      PushOperand('n', 0);
      return;
    }
    if (foperand.length == 1) {
      sig = scf.OperandValueAndType(sheet, foperand);
      t = sig.type.charAt(0);
      if (t != 'n') {
        PushOperand('e#VALUE!', 0);
        return;
      }
    } else if (foperand.length == 0) {
      sig = {
        type: 'n',
        value: val.value > 0 ? 1 : -1,
      };
    } else {
      PushOperand('e#VALUE!', 0);
      return;
    }
    if (sig.value == 0) {
      PushOperand('n', 0);
      return;
    }
    if (sig.value * val.value < 0) {
      PushOperand('e#NUM!', 0);
      return;
    }
    switch (fname) {
      case 'CEILING':
        PushOperand('n', Math.ceil(val.value / sig.value) * sig.value);
        break;
      case 'FLOOR':
        PushOperand('n', Math.floor(val.value / sig.value) * sig.value);
        break;
    }
    return;
  };
  SocialCalc.Formula.FunctionList['CEILING'] = [
    SocialCalc.Formula.CeilingFloorFunctions,
    -1,
    'vsig',
    '',
    'math',
  ];
  SocialCalc.Formula.FunctionList['FLOOR'] = [
    SocialCalc.Formula.CeilingFloorFunctions,
    -1,
    'vsig',
    '',
    'math',
  ];
  FormulaMut.AndOrFunctions = function (fname, operand, foperand, sheet) {
    var value1, result;
    var scf = SocialCalc.Formula;
    var resulttype = '';
    if (fname == 'AND') {
      result = 1;
    } else if (fname == 'OR') {
      result = 0;
    }
    while (foperand.length) {
      value1 = scf.OperandValueAndType(sheet, foperand);
      if (value1.type.charAt(0) == 'n') {
        value1.value = value1.value - 0;
        if (fname == 'AND') {
          result = value1.value != 0 ? result : 0;
        } else if (fname == 'OR') {
          result = value1.value != 0 ? 1 : result;
        }
        resulttype = scf.LookupResultType(
          value1.type,
          resulttype || 'nl',
          scf.TypeLookupTable.propagateerror,
        );
      } else if (value1.type.charAt(0) == 'e' && resulttype.charAt(0) != 'e') {
        resulttype = value1.type;
      }
    }
    if (resulttype.length < 1) {
      resulttype = 'e#VALUE!';
      result = 0;
    }
    scf.PushOperand(operand, resulttype, result);
    return;
  };
  SocialCalc.Formula.FunctionList['AND'] = [
    SocialCalc.Formula.AndOrFunctions,
    -1,
    'vn',
    '',
    'test',
  ];
  SocialCalc.Formula.FunctionList['OR'] = [SocialCalc.Formula.AndOrFunctions, -1, 'vn', '', 'test'];
  FormulaMut.NotFunction = function (fname, operand, foperand, sheet) {
    var result = 0;
    var scf = SocialCalc.Formula;
    var value = scf.OperandValueAndType(sheet, foperand);
    var resulttype = scf.LookupResultType(
      value.type,
      value.type,
      scf.TypeLookupTable.propagateerror,
    );
    if (value.type.charAt(0) == 'n' || value.type == 'b') {
      result = value.value - 0 != 0 ? 0 : 1;
      resulttype = 'nl';
    } else if (value.type.charAt(0) == 't') {
      resulttype = 'e#VALUE!';
    }
    scf.PushOperand(operand, resulttype, result);
    return;
  };
  SocialCalc.Formula.FunctionList['NOT'] = [SocialCalc.Formula.NotFunction, 1, 'v', '', 'test'];
  FormulaMut.ChooseFunction = function (fname, operand, foperand, sheet) {
    var resulttype, count, value1;
    var result = 0;
    var scf = SocialCalc.Formula;
    var cindex = scf.OperandAsNumber(sheet, foperand);
    if (cindex.type.charAt(0) != 'n') {
      cindex.value = 0;
    }
    cindex.value = Math.floor(cindex.value);
    count = 0;
    while (foperand.length) {
      value1 = scf.TopOfStackValueAndType(sheet, foperand);
      count += 1;
      if (cindex.value == count) {
        result = value1.value;
        resulttype = value1.type;
        break;
      }
    }
    if (resulttype) {
      scf.PushOperand(operand, resulttype, result);
    } else {
      scf.PushOperand(operand, 'e#VALUE!', 0);
    }
    return;
  };
  SocialCalc.Formula.FunctionList['CHOOSE'] = [
    SocialCalc.Formula.ChooseFunction,
    -2,
    'choose',
    '',
    'lookup',
  ];
  FormulaMut.ColumnsRowsFunctions = function (fname, operand, foperand, sheet) {
    var resulttype, rangeinfo;
    var result = 0;
    var scf = SocialCalc.Formula;
    var value1 = scf.TopOfStackValueAndType(sheet, foperand);
    if (value1.type == 'coord') {
      result = 1;
      resulttype = 'n';
    } else if (value1.type == 'range') {
      rangeinfo = scf.DecodeRangeParts(sheet, value1.value);
      if (!rangeinfo) {
        result = 0;
        resulttype = 'e#REF!';
      } else if (fname == 'COLUMNS') {
        result = rangeinfo.ncols;
        resulttype = 'n';
      } else if (fname == 'ROWS') {
        result = rangeinfo.nrows;
        resulttype = 'n';
      } else {
        result = 0;
        resulttype = 'n';
      }
    } else {
      result = 0;
      resulttype = 'e#VALUE!';
    }
    scf.PushOperand(operand, resulttype, result);
    return;
  };
  SocialCalc.Formula.FunctionList['COLUMNS'] = [
    SocialCalc.Formula.ColumnsRowsFunctions,
    1,
    'range',
    '',
    'lookup',
  ];
  SocialCalc.Formula.FunctionList['ROWS'] = [
    SocialCalc.Formula.ColumnsRowsFunctions,
    1,
    'range',
    '',
    'lookup',
  ];
  FormulaMut.ZeroArgFunctions = function (fname, operand, _foperand, _sheet) {
    var startval, tzoffset, start_1_1_1970, seconds_in_a_day, nowdays;
    var result = {
      value: 0,
      type: '',
    };
    switch (fname) {
      case 'FALSE':
        result.type = 'nl';
        result.value = 0;
        break;
      case 'NA':
        result.type = 'e#N/A';
        break;
      case 'NOW':
        startval = new Date();
        tzoffset = startval.getTimezoneOffset();
        startval = startval.getTime() / 1e3;
        start_1_1_1970 = 25569;
        seconds_in_a_day = 24 * 60 * 60;
        nowdays = start_1_1_1970 + startval / seconds_in_a_day - tzoffset / (24 * 60);
        result.value = nowdays;
        result.type = 'ndt';
        SocialCalc.Formula.FreshnessInfo.volatile.NOW = true;
        break;
      case 'PI':
        result.type = 'n';
        result.value = Math.PI;
        break;
      case 'TODAY':
        startval = new Date();
        tzoffset = startval.getTimezoneOffset();
        startval = startval.getTime() / 1e3;
        start_1_1_1970 = 25569;
        seconds_in_a_day = 24 * 60 * 60;
        nowdays = start_1_1_1970 + startval / seconds_in_a_day - tzoffset / (24 * 60);
        result.value = Math.floor(nowdays);
        result.type = 'nd';
        SocialCalc.Formula.FreshnessInfo.volatile.TODAY = true;
        break;
      case 'TRUE':
        result.type = 'nl';
        result.value = 1;
        break;
      case 'RAND':
        result.type = 'n';
        result.value = Math.random();
        SocialCalc.Formula.FreshnessInfo.volatile.RAND = true;
        break;
    }
    operand.push(result);
    return null;
  };
  SocialCalc.Formula.FunctionList['FALSE'] = [
    SocialCalc.Formula.ZeroArgFunctions,
    0,
    '',
    '',
    'test',
  ];
  SocialCalc.Formula.FunctionList['NA'] = [SocialCalc.Formula.ZeroArgFunctions, 0, '', '', 'test'];
  SocialCalc.Formula.FunctionList['NOW'] = [
    SocialCalc.Formula.ZeroArgFunctions,
    0,
    '',
    '',
    'datetime',
  ];
  SocialCalc.Formula.FunctionList['RAND'] = [
    SocialCalc.Formula.ZeroArgFunctions,
    0,
    '',
    '',
    'math',
  ];
  SocialCalc.Formula.FunctionList['PI'] = [SocialCalc.Formula.ZeroArgFunctions, 0, '', '', 'math'];
  SocialCalc.Formula.FunctionList['TODAY'] = [
    SocialCalc.Formula.ZeroArgFunctions,
    0,
    '',
    '',
    'datetime',
  ];
  SocialCalc.Formula.FunctionList['TRUE'] = [
    SocialCalc.Formula.ZeroArgFunctions,
    0,
    '',
    '',
    'test',
  ];
  FormulaMut.DDBFunction = function (fname, operand, foperand, sheet) {
    var method, depreciation, accumulateddepreciation, i;
    var scf = SocialCalc.Formula;
    var cost = scf.OperandAsNumber(sheet, foperand);
    var salvage = scf.OperandAsNumber(sheet, foperand);
    var lifetime = scf.OperandAsNumber(sheet, foperand);
    var period = scf.OperandAsNumber(sheet, foperand);
    if (scf.CheckForErrorValue(operand, cost)) return;
    if (scf.CheckForErrorValue(operand, salvage)) return;
    if (scf.CheckForErrorValue(operand, lifetime)) return;
    if (scf.CheckForErrorValue(operand, period)) return;
    if (lifetime.value < 1) {
      scf.FunctionSpecificError(fname, operand, 'e#NUM!', SocialCalc.Constants.s_sheetfuncddblife);
      return 0;
    }
    method = {
      value: 2,
      type: 'n',
    };
    if (foperand.length > 0) {
      method = scf.OperandAsNumber(sheet, foperand);
    }
    if (foperand.length != 0) {
      scf.FunctionArgsError(fname, operand);
      return 0;
    }
    if (scf.CheckForErrorValue(operand, method)) return;
    depreciation = 0;
    accumulateddepreciation = 0;
    for (i = 1; i <= period.value - 0 && i <= lifetime.value; i++) {
      depreciation = (cost.value - accumulateddepreciation) * (method.value / lifetime.value);
      if (cost.value - accumulateddepreciation - depreciation < salvage.value) {
        depreciation = cost.value - accumulateddepreciation - salvage.value;
      }
      accumulateddepreciation += depreciation;
    }
    scf.PushOperand(operand, 'n$', depreciation);
    return;
  };
  SocialCalc.Formula.FunctionList['DDB'] = [
    SocialCalc.Formula.DDBFunction,
    -4,
    'ddb',
    '',
    'financial',
  ];
  FormulaMut.SLNFunction = function (fname, operand, foperand, sheet) {
    var depreciation;
    var scf = SocialCalc.Formula;
    var cost = scf.OperandAsNumber(sheet, foperand);
    var salvage = scf.OperandAsNumber(sheet, foperand);
    var lifetime = scf.OperandAsNumber(sheet, foperand);
    if (scf.CheckForErrorValue(operand, cost)) return;
    if (scf.CheckForErrorValue(operand, salvage)) return;
    if (scf.CheckForErrorValue(operand, lifetime)) return;
    if (lifetime.value < 1) {
      scf.FunctionSpecificError(fname, operand, 'e#NUM!', SocialCalc.Constants.s_sheetfuncslnlife);
      return 0;
    }
    depreciation = (cost.value - salvage.value) / lifetime.value;
    scf.PushOperand(operand, 'n$', depreciation);
    return;
  };
  SocialCalc.Formula.FunctionList['SLN'] = [
    SocialCalc.Formula.SLNFunction,
    3,
    'csl',
    '',
    'financial',
  ];
  FormulaMut.SYDFunction = function (fname, operand, foperand, sheet) {
    var depreciation, sumperiods;
    var scf = SocialCalc.Formula;
    var cost = scf.OperandAsNumber(sheet, foperand);
    var salvage = scf.OperandAsNumber(sheet, foperand);
    var lifetime = scf.OperandAsNumber(sheet, foperand);
    var period = scf.OperandAsNumber(sheet, foperand);
    if (scf.CheckForErrorValue(operand, cost)) return;
    if (scf.CheckForErrorValue(operand, salvage)) return;
    if (scf.CheckForErrorValue(operand, lifetime)) return;
    if (scf.CheckForErrorValue(operand, period)) return;
    if (lifetime.value < 1 || period.value <= 0) {
      scf.PushOperand(operand, 'e#NUM!', 0);
      return 0;
    }
    sumperiods = ((lifetime.value + 1) * lifetime.value) / 2;
    depreciation =
      ((cost.value - salvage.value) * (lifetime.value - period.value + 1)) / sumperiods;
    scf.PushOperand(operand, 'n$', depreciation);
    return;
  };
  SocialCalc.Formula.FunctionList['SYD'] = [
    SocialCalc.Formula.SYDFunction,
    4,
    'cslp',
    '',
    'financial',
  ];
  FormulaMut.InterestFunctions = function (fname, operand, foperand, sheet) {
    var resulttype, result, dval, eval_, fval;
    var pv, fv, rate, n, payment, paytype, guess, part1, part2, part3, part4, part5;
    var olddelta, maxloop, tries, delta, epsilon, oldrate, m;
    var scf = SocialCalc.Formula;
    var aval = scf.OperandAsNumber(sheet, foperand);
    var bval = scf.OperandAsNumber(sheet, foperand);
    var cval = scf.OperandAsNumber(sheet, foperand);
    resulttype = scf.LookupResultType(aval.type, bval.type, scf.TypeLookupTable.twoargnumeric);
    resulttype = scf.LookupResultType(resulttype, cval.type, scf.TypeLookupTable.twoargnumeric);
    if (foperand.length) {
      dval = scf.OperandAsNumber(sheet, foperand);
      resulttype = scf.LookupResultType(resulttype, dval.type, scf.TypeLookupTable.twoargnumeric);
      if (foperand.length) {
        eval_ = scf.OperandAsNumber(sheet, foperand);
        resulttype = scf.LookupResultType(
          resulttype,
          eval_.type,
          scf.TypeLookupTable.twoargnumeric,
        );
        if (foperand.length) {
          if (fname != 'RATE') {
            scf.FunctionArgsError(fname, operand);
            return 0;
          }
          fval = scf.OperandAsNumber(sheet, foperand);
          resulttype = scf.LookupResultType(
            resulttype,
            fval.type,
            scf.TypeLookupTable.twoargnumeric,
          );
        }
      }
    }
    if (resulttype == 'n') {
      switch (fname) {
        case 'FV':
          rate = aval.value;
          n = bval.value;
          payment = cval.value;
          pv = dval != null ? dval.value : 0;
          paytype = eval_ != null ? (eval_.value ? 1 : 0) : 0;
          if (rate == 0) {
            fv = -pv - payment * n;
          } else {
            fv = -(
              pv * Math.pow(1 + rate, n) +
              (payment * (1 + rate * paytype) * (Math.pow(1 + rate, n) - 1)) / rate
            );
          }
          result = fv;
          resulttype = 'n$';
          break;
        case 'NPER':
          rate = aval.value;
          payment = bval.value;
          pv = cval.value;
          fv = dval != null ? dval.value : 0;
          paytype = eval_ != null ? (eval_.value ? 1 : 0) : 0;
          if (rate == 0) {
            if (payment == 0) {
              scf.PushOperand(operand, 'e#NUM!', 0);
              return;
            }
            n = (pv + fv) / -payment;
          } else {
            part1 = (payment * (1 + rate * paytype)) / rate;
            part2 = pv + part1;
            if (part2 == 0 || rate <= -1) {
              scf.PushOperand(operand, 'e#NUM!', 0);
              return;
            }
            part3 = (part1 - fv) / part2;
            if (part3 <= 0) {
              scf.PushOperand(operand, 'e#NUM!', 0);
              return;
            }
            part4 = Math.log(part3);
            part5 = Math.log(1 + rate);
            n = part4 / part5;
          }
          result = n;
          resulttype = 'n';
          break;
        case 'PMT':
          rate = aval.value;
          n = bval.value;
          pv = cval.value;
          fv = dval != null ? dval.value : 0;
          paytype = eval_ != null ? (eval_.value ? 1 : 0) : 0;
          if (n == 0) {
            scf.PushOperand(operand, 'e#NUM!', 0);
            return;
          } else if (rate == 0) {
            payment = (fv - pv) / n;
          } else {
            payment =
              (0 - fv - pv * Math.pow(1 + rate, n)) /
              (((1 + rate * paytype) * (Math.pow(1 + rate, n) - 1)) / rate);
          }
          result = payment;
          resulttype = 'n$';
          break;
        case 'PV':
          rate = aval.value;
          n = bval.value;
          payment = cval.value;
          fv = dval != null ? dval.value : 0;
          paytype = eval_ != null ? (eval_.value ? 1 : 0) : 0;
          if (rate == -1) {
            scf.PushOperand(operand, 'e#DIV/0!', 0);
            return;
          } else if (rate == 0) {
            pv = -fv - payment * n;
          } else {
            pv =
              (-fv - (payment * (1 + rate * paytype) * (Math.pow(1 + rate, n) - 1)) / rate) /
              Math.pow(1 + rate, n);
          }
          result = pv;
          resulttype = 'n$';
          break;
        case 'RATE':
          n = aval.value;
          payment = bval.value;
          pv = cval.value;
          fv = dval != null ? dval.value : 0;
          paytype = eval_ != null ? (eval_.value ? 1 : 0) : 0;
          guess = fval != null ? fval.value : 0.1;
          maxloop = 100;
          tries = 0;
          delta = 1;
          epsilon = 1e-7;
          rate = guess || 1e-8;
          while ((delta >= 0 ? delta : -delta) > epsilon && rate != oldrate) {
            delta =
              fv +
              pv * Math.pow(1 + rate, n) +
              (payment * (1 + rate * paytype) * (Math.pow(1 + rate, n) - 1)) / rate;
            if (olddelta != null) {
              m = (delta - olddelta) / (rate - oldrate) || 0.001;
              oldrate = rate;
              rate = rate - delta / m;
              olddelta = delta;
            } else {
              oldrate = rate;
              rate = 1.1 * rate;
              olddelta = delta;
            }
            tries++;
            if (tries >= maxloop) {
              scf.PushOperand(operand, 'e#NUM!', 0);
              return;
            }
          }
          result = rate;
          resulttype = 'n%';
          break;
      }
    }
    scf.PushOperand(operand, resulttype, result);
    return;
  };
  SocialCalc.Formula.FunctionList['FV'] = [
    SocialCalc.Formula.InterestFunctions,
    -3,
    'fv',
    '',
    'financial',
  ];
  SocialCalc.Formula.FunctionList['NPER'] = [
    SocialCalc.Formula.InterestFunctions,
    -3,
    'nper',
    '',
    'financial',
  ];
  SocialCalc.Formula.FunctionList['PMT'] = [
    SocialCalc.Formula.InterestFunctions,
    -3,
    'pmt',
    '',
    'financial',
  ];
  SocialCalc.Formula.FunctionList['PV'] = [
    SocialCalc.Formula.InterestFunctions,
    -3,
    'pv',
    '',
    'financial',
  ];
  SocialCalc.Formula.FunctionList['RATE'] = [
    SocialCalc.Formula.InterestFunctions,
    -3,
    'rate',
    '',
    'financial',
  ];
  FormulaMut.NPVFunction = function (fname, operand, foperand, sheet) {
    var resulttypenpv, rate, sum, factor, value1;
    var scf = SocialCalc.Formula;
    rate = scf.OperandAsNumber(sheet, foperand);
    if (scf.CheckForErrorValue(operand, rate)) return;
    sum = 0;
    resulttypenpv = 'n';
    factor = 1;
    while (foperand.length) {
      value1 = scf.OperandValueAndType(sheet, foperand);
      if (value1.type.charAt(0) == 'e' && resulttypenpv.charAt(0) != 'e') {
        resulttypenpv = value1.type;
        break;
      }
      if (
        value1.type.charAt(0) == 'n' ||
        value1.type.charAt(0) == 'b' ||
        value1.type.charAt(0) == 't'
      ) {
        factor *= 1 + rate.value;
        if (factor == 0) {
          scf.PushOperand(operand, 'e#DIV/0!', 0);
          return;
        }
        var cash = value1.type.charAt(0) == 'n' ? value1.value - 0 : 0;
        sum += cash / factor;
        if (value1.type.charAt(0) == 'n') {
          resulttypenpv = scf.LookupResultType(
            value1.type,
            resulttypenpv || value1.type,
            scf.TypeLookupTable.plus,
          );
        }
      }
    }
    if (resulttypenpv.charAt(0) == 'n') {
      resulttypenpv = 'n$';
    }
    scf.PushOperand(operand, resulttypenpv, sum);
    return;
  };
  SocialCalc.Formula.FunctionList['NPV'] = [
    SocialCalc.Formula.NPVFunction,
    -2,
    'npv',
    '',
    'financial',
  ];
  FormulaMut.IRRFunction = function (fname, operand, foperand, sheet) {
    var value1, guess, oldsum, maxloop, tries, epsilon, rate, oldrate, m, sum, factor, i;
    var rangeoperand = [];
    var cashflows = [];
    var hasNumericCashflow = false;
    var scf = SocialCalc.Formula;
    rangeoperand.push(foperand.pop());
    while (rangeoperand.length) {
      value1 = scf.OperandValueAndType(sheet, rangeoperand);
      if (value1.type.charAt(0) == 'n') {
        cashflows.push(value1.value);
        hasNumericCashflow = true;
      } else if (value1.type.charAt(0) == 'b' || value1.type.charAt(0) == 't') {
        cashflows.push(0);
      } else if (value1.type.charAt(0) == 'e') {
        scf.PushOperand(operand, 'e#VALUE!', 0);
        return;
      }
    }
    if (!cashflows.length || !hasNumericCashflow) {
      scf.PushOperand(operand, 'e#NUM!', 0);
      return;
    }
    guess = { value: 0 };
    if (foperand.length) {
      guess = scf.OperandAsNumber(sheet, foperand);
      if (guess.type.charAt(0) != 'n' && guess.type.charAt(0) != 'b') {
        scf.PushOperand(operand, 'e#VALUE!', 0);
        return;
      }
      if (foperand.length) {
        scf.FunctionArgsError(fname, operand);
        return;
      }
    }
    guess.value = guess.value || 0.1;
    maxloop = 20;
    tries = 0;
    epsilon = 1e-7;
    rate = guess.value;
    sum = 1;
    while ((sum >= 0 ? sum : -sum) > epsilon && rate != oldrate) {
      sum = 0;
      factor = 1;
      for (i = 0; i < cashflows.length; i++) {
        factor *= 1 + rate;
        if (factor == 0) {
          scf.PushOperand(operand, 'e#DIV/0!', 0);
          return;
        }
        sum += cashflows[i] / factor;
      }
      if (oldsum != null) {
        m = (sum - oldsum) / (rate - oldrate);
        oldrate = rate;
        rate = rate - sum / m;
        oldsum = sum;
      } else {
        oldrate = rate;
        rate = 1.1 * rate;
        oldsum = sum;
      }
      tries++;
      if (tries >= maxloop) {
        scf.PushOperand(operand, 'e#NUM!', 0);
        return;
      }
    }
    scf.PushOperand(operand, 'n%', rate);
    return;
  };
  SocialCalc.Formula.FunctionList['IRR'] = [
    SocialCalc.Formula.IRRFunction,
    -1,
    'irr',
    '',
    'financial',
  ];
  FormulaMut.IoFunctions = function (fname, operand, foperand, sheet, coord) {
    var argList = {
      BUTTON: [2],
      IMAGEBUTTON: [2],
      EMAIL: [14, 14, 14, 14],
      EMAILIF: [13, 14, 14, 14, 14],
      EMAILONEDIT: [14, 14, 14, 14, 14],
      EMAILAT: [13, 14, 14, 14, 14],
      EMAILONEDITIF: [14, 13, 14, 14, 14, 14],
      EMAILATIF: [13, 13, 14, 14, 14, 14],
      SUBMIT: [2],
      TEXTBOX: [2],
      AUTOCOMPLETE: [2, 14],
      SELECT: [2, 14, 1, 1],
      CHECKBOX: [3],
      RADIOBUTTON: [2, 2],
      COPYVALUE: [4, 12, 15],
      COPYFORMULA: [4, 12, 12],
      INSERT: [4, 8, -12, -15],
      DELETEIF: [4, 7, 8],
      COMMAND: [4, 14],
      COMMANDIF: [4, 13, 14],
      PANEL: [15, -12],
      SPLASH: [12],
      STYLE: [6],
    };
    var i;
    var parameter;
    var scf = SocialCalc.Formula;
    var result = 0;
    var resulttype = 'e#VALUE!';
    var numargs = foperand.length;
    var argdef = argList[fname];
    var operand_value = [];
    var operand_type = [];
    var repeatCount = -1;
    var repeatIndex = 0;
    var wantedType;
    for (i = 1; i <= numargs; i++) {
      if (repeatCount != -1) {
        wantedType = Math.abs(argdef[repeatIndex + ((i - 1 - repeatIndex) % repeatCount)]);
      } else {
        if (i > argdef.length) {
          scf.FunctionArgsError(fname, operand);
          return;
        }
        wantedType = argdef[i - 1];
        if (wantedType < 0) {
          repeatCount = argdef.length - (i - 1);
          repeatIndex = i - 1;
          wantedType = Math.abs(wantedType);
        }
      }
      if (wantedType == 1) {
        parameter = scf.OperandAsNumber(sheet, foperand);
      } else if (wantedType == 2) {
        parameter = scf.OperandAsText(sheet, foperand);
      } else if (wantedType == 3) {
        parameter = scf.OperandValueAndType(sheet, foperand);
      } else {
        var parameterType = foperand[foperand.length - 1].type;
        var validParameterType = false;
        if (
          (parameterType.charAt(0) == 'n' && (wantedType & 1) != 0) ||
          (parameterType.charAt(0) == 't' && (wantedType & 2) != 0)
        ) {
          validParameterType = true;
          parameter = scf.OperandValueAndType(sheet, foperand);
        }
        if (parameterType == 'coord' && (wantedType & 4) != 0) {
          validParameterType = true;
          parameter = scf.OperandAsCoord(sheet, foperand);
          parameter.value = SocialCalc.Formula.PlainCoord(parameter.value);
        }
        if (parameterType == 'range' && (wantedType & 8) != 0) {
          validParameterType = true;
          parameter = scf.OperandAsRange(sheet, foperand);
          parameter.value = SocialCalc.Formula.PlainCoord(parameter.value);
        }
        if (validParameterType == false) {
          scf.FunctionArgsError(fname, operand);
          return;
        }
      }
      operand_value[i] = parameter.value;
      operand_type[i] = parameter.type;
      if (parameter.type.charAt(0) == 'e') {
        scf.PushOperand(operand, parameter.type, result);
        return;
      }
    }
    switch (fname) {
      case 'STYLE':
        var parameters = sheet.ioParameterList[coord];
        if (parameters) {
          var css = SocialCalc.Formula.getStandardizedList(sheet, {
            value: operand_value[1],
            type: operand_type[1],
          });
          if (css.length > 0) {
            parameters.css = css[0];
            parameters.cssParameter =
              operand_type[1] == 't' ? '"' + operand_value[1] + '"' : operand_value[1];
          }
          result = '';
          resulttype = 'ni';
        }
        break;
      case 'SELECT':
        var parameters = sheet.ioParameterList[coord];
        var optionSource = SocialCalc.Formula.getStandardizedList(sheet, parameters[1]);
        parameters.html = [];
        parameters.html[0] = operand_value[4] == true ? 'multiple' : '';
        parameters.html[1] = operand_value[3] ? '' + operand_value[3] : '1';
        if (optionSource.length > 0) {
          var options = '<option>' + optionSource.join('</option><option>') + '</option>';
          var optionRegExp = new RegExp('<option>' + operand_value[1], '');
          parameters.html[2] = options.replace(
            optionRegExp,
            '<option selected>' + operand_value[1],
          );
        }
        result = operand_value[1];
        resulttype = 'ti' + fname;
        break;
      case 'SUBMIT':
        result = 'Submit';
        if (numargs > 0) result = operand_value[1];
        resulttype = 'ti' + fname;
        break;
      case 'BUTTON':
      case 'IMAGEBUTTON':
      case 'TEXTBOX':
      case 'AUTOCOMPLETE':
        if (numargs > 0) result = operand_value[1];
        resulttype = 'ti' + fname;
        break;
      case 'EMAIL':
      case 'EMAILIF':
        resulttype = 'ti' + fname;
        result = 'Send';
        break;
      case 'EMAILONEDIT':
      case 'EMAILONEDITIF':
      case 'EMAILAT':
      case 'EMAILATIF':
        resulttype = 'ti' + fname;
        result = 'Send Now';
        break;
      case 'CHECKBOX':
      case 'RADIOBUTTON':
        if (operand_type[1].charAt(0) == 't') {
          result = operand_value[1].toUpperCase() == 'TRUE' ? 1 : 0;
        } else {
          result = operand_value[1] == 0 ? 0 : 1;
        }
        resulttype = 'ni' + fname;
        break;
      case 'COPYVALUE':
      case 'COPYFORMULA':
      case 'INSERT':
      case 'DELETEIF':
      case 'COMMAND':
      case 'COMMANDIF':
        var cell = sheet.cells[operand_value[1]];
        if (typeof cell === 'undefined') break;
        result = cell.datavalue;
        result = String(result).split('/');
        result = result[result.length - 1];
        resulttype = 't';
        break;
      case 'PANEL':
      case 'SPLASH':
        var showindices = [];
        var firstPanelIndex = 2;
        if (fname == 'SPLASH') {
          result = 'SPLASH:' + operand_value[1];
          resulttype = 't';
          if (sheet.splashdone == true) break;
          sheet.splashdone = true;
          firstPanelIndex = 1;
          showindices = [0];
        } else {
          showindices = SocialCalc.Formula.getStandardizedList(sheet, {
            value: operand_value[1],
            type: operand_type[1],
          });
          result = fname + ':' + showindices;
        }
        resulttype = 't';
        if (SocialCalc._app) {
          var showrows = [],
            showcols = [];
          for (
            var parameterIndex = firstPanelIndex;
            parameterIndex < operand_value.length;
            ++parameterIndex
          ) {
            var wanted = parameterIndex - 1;
            var showPanelFound = showindices.some(function (v) {
              return v == wanted;
            });
            if (!showPanelFound) continue;
            var panelCoordData = SocialCalc.Formula.getStandardizedCoords(sheet, {
              value: operand_value[parameterIndex],
              type: operand_type[parameterIndex],
            });
            for (var i = 0; i < panelCoordData.ncols; i++) {
              showcols[panelCoordData.col1num + i] = true;
            }
            for (var j = 0; j < panelCoordData.nrows; j++) {
              showrows[panelCoordData.row1num + j] = true;
            }
          }
          var spreadsheet = window.spreadsheet;
          if (spreadsheet == null) spreadsheet = window.ss;
          var forceRender = false;
          var lastShowDimension = 0;
          var showGridDimension = function (sheet, lastIndex, sheetHideList, showList, getIndexOf) {
            for (var arrayIndex = 1; arrayIndex <= lastIndex; arrayIndex++) {
              var sheetHideIndex = getIndexOf(arrayIndex);
              if (typeof sheetHideList[sheetHideIndex] == 'undefined') {
                if (showList[arrayIndex] !== true) {
                  sheetHideList[sheetHideIndex] = 'yes';
                  forceRender = true;
                } else {
                  lastShowDimension = arrayIndex;
                }
              } else {
                if (showList[arrayIndex] === true) {
                  delete sheetHideList[sheetHideIndex];
                  forceRender = true;
                  lastShowDimension = arrayIndex;
                }
              }
            }
          };
          var getRowIndex = function (row) {
            return row;
          };
          showGridDimension(
            sheet,
            sheet.attribs.lastrow,
            sheet.rowattribs.hide,
            showrows,
            getRowIndex,
          );
          lastShowDimension = 0;
          showGridDimension(
            sheet,
            sheet.attribs.lastcol,
            sheet.colattribs.hide,
            showcols,
            SocialCalc.rcColname,
          );
          sheet.attribs.usermaxcol = lastShowDimension;
          if (forceRender) {
            sheet.renderneeded = true;
            sheet.widgetsClean = false;
            spreadsheet.editor.context.rowpanes[0].first = 1;
            spreadsheet.editor.context.CalculateColWidthData();
            spreadsheet.width = spreadsheet.editor.context.totalwidth;
            spreadsheet.height = 2500;
            spreadsheet.editor.ResizeTableEditor(spreadsheet.editor.context.totalwidth, 2500);
          }
        }
    }
    scf.PushOperand(operand, resulttype, result);
    return;
  };
  SocialCalc.Formula.FunctionList['BUTTON'] = [
    SocialCalc.Formula.IoFunctions,
    1,
    'label',
    '',
    'gui',
    "<button type='button' onclick=\"SocialCalc.TriggerIoAction.Button('<%=cell_reference%>');\"><%=formated_value%></button>",
    'ParameterList',
  ];
  SocialCalc.Formula.FunctionList['IMAGEBUTTON'] = [
    SocialCalc.Formula.IoFunctions,
    1,
    'imageurl',
    '',
    'gui',
    "<input type='image' src='<%=display_value%>' alt='Submit' onclick=\"SocialCalc.TriggerIoAction.Button('<%=cell_reference%>');\">",
    'ParameterList',
  ];
  SocialCalc.Formula.FunctionList['EMAIL'] = [
    SocialCalc.Formula.IoFunctions,
    -3,
    'to_range subject_range, body_range',
    '',
    'action',
    "<button type='button' onclick=\"SocialCalc.TriggerIoAction.Email('<%=cell_reference%>');\"><%=formated_value%></button>",
    'ParameterList',
  ];
  SocialCalc.Formula.FunctionList['EMAILIF'] = [
    SocialCalc.Formula.IoFunctions,
    -4,
    'condition_range, to_range subject_range, body_range',
    '',
    'action',
    "<button type='button' onclick=\"SocialCalc.TriggerIoAction.Email('<%=cell_reference%>');\"><%=formated_value%></button>",
    'ParameterList',
  ];
  SocialCalc.Formula.FunctionList['EMAILONEDIT'] = [
    SocialCalc.Formula.IoFunctions,
    -4,
    'editRange, to_range subject_range, body_range',
    '',
    'action',
    "<button type='button' onclick=\"SocialCalc.TriggerIoAction.Email('<%=cell_reference%>');\"><%=formated_value%></button>",
    'EventTree',
  ];
  SocialCalc.Formula.FunctionList['EMAILAT'] = [
    SocialCalc.Formula.IoFunctions,
    -4,
    'datetime_value, to_range subject_range, body_range',
    '',
    'action',
    "<button type='button' onclick=\"SocialCalc.TriggerIoAction.Email('<%=cell_reference%>');\"><%=formated_value%></button>",
    'TimeTrigger',
  ];
  SocialCalc.Formula.FunctionList['EMAILONEDITIF'] = [
    SocialCalc.Formula.IoFunctions,
    -5,
    'editRange, condition, to_range subject_range, body_range',
    '',
    'action',
    "<button type='button' onclick=\"SocialCalc.TriggerIoAction.Email('<%=cell_reference%>');\"><%=formated_value%></button>",
    'EventTree',
  ];
  SocialCalc.Formula.FunctionList['EMAILATIF'] = [
    SocialCalc.Formula.IoFunctions,
    -5,
    'datetime_value, condition, to_range subject_range, body_range',
    '',
    'action',
    "<button type='button' onclick=\"SocialCalc.TriggerIoAction.Email('<%=cell_reference%>');\"><%=formated_value%></button>",
    'TimeTrigger',
  ];
  SocialCalc.Formula.FunctionList['SUBMIT'] = [
    SocialCalc.Formula.IoFunctions,
    100,
    '[label]',
    '',
    'action',
    "<button type='button' onclick=\"SocialCalc.TriggerIoAction.Submit('<%=cell_reference%>');\"><%=formated_value%></button>",
    'ParameterList',
  ];
  SocialCalc.Formula.FunctionList['TEXTBOX'] = [
    SocialCalc.Formula.IoFunctions,
    1,
    'value',
    '',
    'gui',
    "<input type='text' id='TEXTBOX_<%=cell_reference%>' onblur='SocialCalc.CmdGotFocus(null);' oninput=\"SocialCalc.TriggerIoAction.TextBox('<%=cell_reference%>')\" value='<%=display_value%>' >",
    'Input',
  ];
  SocialCalc.Formula.FunctionList['AUTOCOMPLETE'] = [
    SocialCalc.Formula.IoFunctions,
    2,
    'value, range or csv_text',
    '',
    'gui',
    "<input type='text' id='AUTOCOMPLETE_<%=cell_reference%>' onfocus=\"SocialCalc.TriggerIoAction.AddAutocomplete('<%=cell_reference%>');\" onblur='SocialCalc.CmdGotFocus(null);' value='<%=display_value%>' >",
    'Input',
  ];
  SocialCalc.Formula.FunctionList['SELECT'] = [
    SocialCalc.Formula.IoFunctions,
    -2,
    'value, range or csv_text [,size]',
    '',
    'gui',
    "<select size='<%=html1_value%>' id='SELECT_<%=cell_reference%>' onchange=\"SocialCalc.TriggerIoAction.SelectList('<%=cell_reference%>')\" <%=html0_value%>><%=html2_value%></select>",
    'Input',
  ];
  SocialCalc.Formula.FunctionList['CHECKBOX'] = [
    SocialCalc.Formula.IoFunctions,
    1,
    'value',
    '',
    'gui',
    "<input type='checkbox' id='CHECKBOX_<%=cell_reference%>' <%=checked%> onblur='SocialCalc.CmdGotFocus(null);' onchange=\"SocialCalc.TriggerIoAction.CheckBox('<%=cell_reference%>')\" >",
    'Input',
  ];
  SocialCalc.Formula.FunctionList['RADIOBUTTON'] = [
    SocialCalc.Formula.IoFunctions,
    2,
    'value, groupname',
    '',
    'gui',
    "<input type='radio' value='<%=cell_reference%>' id='RADIOBUTTON_<%=cell_reference%>' <%=checked%> name='<%=parameter1_value%>' onblur=\"SocialCalc.CmdGotFocus(null);\" onclick=\"SocialCalc.TriggerIoAction.RadioButton('<%=parameter1_value%>');\" >",
    'Input',
  ];
  SocialCalc.Formula.FunctionList['COPYVALUE'] = [
    SocialCalc.Formula.IoFunctions,
    3,
    'trigger_cell, destinationCell, value_or_range',
    '',
    'action',
    '',
    'EventTree',
  ];
  SocialCalc.Formula.FunctionList['COPYFORMULA'] = [
    SocialCalc.Formula.IoFunctions,
    3,
    'trigger_cell, destinationCell, formula_range',
    '',
    'action',
    '',
    'EventTree',
  ];
  SocialCalc.Formula.FunctionList['INSERT'] = [
    SocialCalc.Formula.IoFunctions,
    -2,
    'trigger_cell, destination_range [,formula_range,value_or_range,formula_range, ...]',
    '',
    'action',
    '',
    'EventTree',
  ];
  SocialCalc.Formula.FunctionList['DELETEIF'] = [
    SocialCalc.Formula.IoFunctions,
    -1,
    'trigger_cell, criteria , test_range',
    '',
    'action',
    '',
    'EventTree',
  ];
  SocialCalc.Formula.FunctionList['COMMAND'] = [
    SocialCalc.Formula.IoFunctions,
    -1,
    'trigger_cell, commands',
    '',
    'action',
    '',
    'EventTree',
  ];
  SocialCalc.Formula.FunctionList['COMMANDIF'] = [
    SocialCalc.Formula.IoFunctions,
    -1,
    'trigger_cell, conditions, commands',
    '',
    'action',
    '',
    'EventTree',
  ];
  SocialCalc.Formula.FunctionList['PANEL'] = [
    SocialCalc.Formula.IoFunctions,
    -1,
    'showindices_range_or_csv, panel1_range [, panel2_range , ...]',
    '',
    'gui',
    '',
  ];
  SocialCalc.Formula.FunctionList['SPLASH'] = [
    SocialCalc.Formula.IoFunctions,
    -1,
    'splash_panel_range',
    '',
    'gui',
    '',
  ];
  SocialCalc.Formula.FunctionList['STYLE'] = [
    SocialCalc.Formula.IoFunctions,
    -1,
    'css',
    '',
    'gui',
    '',
  ];
  TriggerIoMut.AddAutocomplete = function (triggerCellId) {
    var spreadsheet = window.spreadsheet;
    if (spreadsheet == null) spreadsheet = window.ss;
    var sheet = spreadsheet.sheet;
    var parameters = sheet.ioParameterList[triggerCellId];
    if (typeof parameters === 'undefined') return;
    var autocompleteSource = SocialCalc.Formula.getStandardizedList(sheet, parameters[1]);
    $.ui.autocomplete.filter = function (array, term) {
      var matcher = new RegExp('\\b' + $.ui.autocomplete.escapeRegex(term), 'i');
      return $.grep(array, function (value) {
        return matcher.test(value.label || value.value || value);
      });
    };
    $('#AUTOCOMPLETE_' + triggerCellId).autocomplete({
      source: autocompleteSource,
      minLength: 1,
      autoFocus: true,
      select: function (event, ui) {
        $(this).val(ui.item.label);
        SocialCalc.TriggerIoAction.AutoComplete(triggerCellId);
      },
      change: function (event, ui) {
        if (ui.item === null) {
          $(this).val('');
        }
        SocialCalc.TriggerIoAction.AutoComplete(triggerCellId);
      },
    });
  };
  TriggerIoMut.Button = function (triggerCellId) {
    var spreadsheet = window.spreadsheet;
    if (spreadsheet == null) spreadsheet = window.ss;
    var sheet = spreadsheet.sheet;
    if (typeof sheet.ioEventTree === 'undefined') return;
    if (typeof sheet.ioParameterList === 'undefined') return;
    if (sheet.ioEventTree[triggerCellId] === 'undefined') return;
    for (var actionCellId in sheet.ioEventTree[triggerCellId]) {
      var parameters = sheet.ioParameterList[actionCellId];
      var conditionsParameter = null;
      switch (parameters.function_name) {
        case 'COPYVALUE':
          var parameterdata = SocialCalc.Formula.getStandardizedValues(sheet, parameters[2]);
          var destcr = SocialCalc.coordToCr(parameters[1].value);
          var sheetCommandList = SocialCalc.TriggerIoAction.CopyValueToRange(parameterdata, destcr);
          spreadsheet.editor.EditorScheduleSheetCommands(sheetCommandList, true, false);
          break;
        case 'COPYFORMULA':
          var parameterdata = SocialCalc.Formula.getStandardizedValues(sheet, parameters[2]);
          var destcr = SocialCalc.coordToCr(parameters[1].value);
          sheetCommandList = SocialCalc.TriggerIoAction.CopyFormulaToRange(parameterdata, destcr);
          spreadsheet.editor.EditorScheduleSheetCommands(sheetCommandList, true, false);
          break;
        case 'INSERT':
          var parameterdata = SocialCalc.Formula.getStandardizedValues(sheet, parameters[1]);
          if (parameterdata.type != 'range') break;
          var insertCommand;
          var colOffset = 0,
            rowOffset = 0;
          if (parameterdata.ncols > 1) {
            colOffset = 1;
            rowOffset = 0;
            insertCommand = 'insertcol';
          }
          if (parameterdata.nrows > 1) {
            colOffset = 0;
            rowOffset = 1;
            insertCommand = 'insertrow';
          }
          var insertcellCoord = parameterdata.cellcoord[colOffset][rowOffset];
          sheetCommandList = insertCommand + ' ' + insertcellCoord;
          var destcr = SocialCalc.coordToCr(insertcellCoord);
          var sourceDataIndex = 2;
          while (sourceDataIndex < parameters.length) {
            if (
              parameters[sourceDataIndex].type == 'range' ||
              parameters[sourceDataIndex].type == 'coord' ||
              sourceDataIndex % 2 == 1
            ) {
              var copyCellRange = SocialCalc.Formula.getStandardizedValues(
                sheet,
                parameters[sourceDataIndex],
              );
              if (sourceDataIndex % 2 == 0) {
                sheetCommandList =
                  sheetCommandList +
                  '\n' +
                  SocialCalc.TriggerIoAction.CopyFormulaToRange(copyCellRange, destcr);
              } else {
                sheetCommandList =
                  sheetCommandList +
                  '\n' +
                  SocialCalc.TriggerIoAction.CopyValueToRange(copyCellRange, destcr);
              }
              if (parameterdata.nrows > 1) destcr.col += copyCellRange.ncols;
              if (parameterdata.ncols > 1) destcr.row += copyCellRange.nrows;
            }
            sourceDataIndex++;
          }
          spreadsheet.editor.EditorScheduleSheetCommands(sheetCommandList, true, false);
          break;
        case 'DELETEIF':
          var criteriaParameter = SocialCalc.Formula.getStandardizedValues(sheet, parameters[1]);
          var testRangeParameter = SocialCalc.Formula.getStandardizedValues(sheet, parameters[2]);
          sheetCommandList = '';
          var colOffset = 0,
            rowOffset = 0,
            deleteCommand;
          if (testRangeParameter.ncols > 1 && testRangeParameter.nrows > 1) return;
          if (testRangeParameter.ncols > 1) {
            colOffset = 1;
            rowOffset = 0;
            deleteCommand = 'deletecol';
          }
          if (testRangeParameter.nrows > 1) {
            colOffset = 0;
            rowOffset = 1;
            deleteCommand = 'deleterow';
          }
          var criteriaValue = criteriaParameter.celldata[0][0].datavalue;
          for (var i = testRangeParameter.ncols - colOffset - 1; i >= colOffset; i--) {
            for (var j = testRangeParameter.nrows - rowOffset - 1; j >= rowOffset; j--) {
              var cell = testRangeParameter.celldata[i][j];
              if (
                SocialCalc.Formula.TestCriteria(cell.datavalue, cell.valuetype, criteriaValue) ==
                true
              ) {
                if (sheetCommandList != '') sheetCommandList = sheetCommandList + '\n';
                sheetCommandList =
                  sheetCommandList + deleteCommand + ' ' + testRangeParameter.cellcoord[i][j];
              }
            }
          }
          if (sheetCommandList != '')
            spreadsheet.editor.EditorScheduleSheetCommands(sheetCommandList, true, false);
          break;
        case 'COMMANDIF':
          conditionsParameter = SocialCalc.Formula.getStandardizedValues(sheet, parameters[1]);
        case 'COMMAND':
          var commandsParameter;
          sheetCommandList = '';
          if (conditionsParameter != null) {
            commandsParameter = SocialCalc.Formula.getStandardizedValues(sheet, parameters[2]);
            if (
              conditionsParameter.ncols != commandsParameter.ncols ||
              conditionsParameter.nrows != commandsParameter.nrows
            )
              break;
          } else {
            commandsParameter = SocialCalc.Formula.getStandardizedValues(sheet, parameters[1]);
          }
          for (var i = 0; i < commandsParameter.ncols; i++) {
            for (var j = 0; j < commandsParameter.nrows; j++) {
              if (conditionsParameter != null) {
                var conditionCell = conditionsParameter.celldata[i][j];
                if (conditionCell.datavalue == false) continue;
              }
              if (sheetCommandList != '') sheetCommandList = sheetCommandList + '\n';
              var cellCommand = commandsParameter.celldata[i][j];
              sheetCommandList = sheetCommandList + cellCommand.datavalue.toString().trim();
            }
          }
          if (sheetCommandList != '')
            spreadsheet.editor.EditorScheduleSheetCommands(sheetCommandList, true, false);
          break;
      }
    }
  };
  TriggerIoMut.CopyFormulaToRange = function (formulaData, destcr) {
    var sheetCommandList = '';
    var sheetCommand;
    for (var i = 0; i < formulaData.ncols; i++) {
      for (var j = 0; j < formulaData.nrows; j++) {
        var cell = formulaData.celldata[i][j];
        var destCellCoord = SocialCalc.crToCoord(destcr.col + i, destcr.row + j);
        if (i != 0 || j != 0) sheetCommandList = sheetCommandList + '\n';
        if (typeof cell !== 'undefined' && cell.valuetype != 'b') {
          var cellDataType = cell.datatype;
          var cellValueType = cell.valuetype;
          var cellDataValue = cell.datavalue;
          var cellFormula = cell.formula;
          if (cellDataType == 'f') {
            cellFormula = SocialCalc.OffsetFormulaCoords(
              cellFormula,
              destcr.col - formulaData.col1num,
              destcr.row - formulaData.row1num,
            );
            cellDataValue = '';
            cellValueType = '';
          } else {
            if (cellDataType != 'c') cellFormula = '';
          }
          sheetCommand =
            'set ' +
            destCellCoord +
            ' ' +
            SocialCalc.Constants.cellDataType[cellDataType] +
            ' ' +
            cellValueType +
            ' ' +
            SocialCalc.encodeForSave(cellDataValue) +
            ' ' +
            cellFormula;
        } else {
          sheetCommand = 'set ' + destCellCoord + ' empty';
        }
        sheetCommandList += sheetCommand.trim();
      }
    }
    return sheetCommandList;
  };
  TriggerIoMut.CopyValueToRange = function (sourceData, destcr) {
    var sheetCommandList = '';
    var sheetCommand;
    for (var i = 0; i < sourceData.ncols; i++) {
      for (var j = 0; j < sourceData.nrows; j++) {
        var cell = sourceData.celldata[i][j];
        var destCellCoord = SocialCalc.crToCoord(destcr.col + i, destcr.row + j);
        if (i != 0 || j != 0) sheetCommandList = sheetCommandList + '\n';
        if (typeof cell !== 'undefined' && cell.valuetype != 'b') {
          var cellDataType = cell.datatype;
          var cellValueType = cell.valuetype;
          var cellDataValue = cell.datavalue;
          var cellFormula = cell.formula;
          if (cellDataType == 'f') {
            cellFormula = '';
            cellDataType = cellValueType;
            if (cellValueType != 'n' && cellValueType.charAt(0) != 't') {
              cellDataType = 'c';
              cellFormula = cell.displaystring;
            }
            if (cellValueType.charAt(0) == 't') cellDataType = 't';
          } else {
            if (cellDataType != 'c') cellFormula = '';
          }
          sheetCommand =
            'set ' +
            destCellCoord +
            ' ' +
            SocialCalc.Constants.cellDataType[cellDataType] +
            ' ' +
            cellValueType +
            ' ' +
            SocialCalc.encodeForSave(cellDataValue) +
            ' ' +
            cellFormula;
        } else {
          sheetCommand = 'set ' + destCellCoord + ' empty';
        }
        sheetCommandList += sheetCommand.trim();
      }
    }
    return sheetCommandList;
  };
  TriggerIoMut.Email = function (emailFormulaCellId, optionalTriggerCellId) {
    optionalTriggerCellId =
      typeof optionalTriggerCellId !== 'undefined' ? optionalTriggerCellId : null;
    var scf = SocialCalc.Formula;
    var spreadsheet = window.spreadsheet;
    if (spreadsheet == null) spreadsheet = window.ss;
    var sheet = spreadsheet.sheet;
    var cell = sheet.cells[emailFormulaCellId];
    if (typeof sheet.ioParameterList === 'undefined') return;
    var parameters = sheet.ioParameterList[emailFormulaCellId];
    if (typeof parameters === 'undefined') return;
    var parameterValues = [];
    var parameterCellRefs = [];
    var maxRangeSize = 1;
    for (var index = 0; index < parameters.length; index++) {
      if (parameters[index].type.charAt(0) == 't') {
        parameterValues[index] = [String(parameters[index].value).replace(/ /g, '%20')];
      }
      if (parameters[index].type == 'coord') {
        parameterValues[index] = [
          String(sheet.GetAssuredCell(parameters[index].value).datavalue).replace(/ /g, '%20'),
        ];
      }
      if (parameters[index].type == 'range') {
        var rangeinfo = scf.DecodeRangeParts(sheet, parameters[index].value);
        parameterValues[index] = [];
        parameterCellRefs[index] = [];
        var rangeSizeCounter = 0;
        for (var i = 0; i < rangeinfo.ncols; i++) {
          for (var j = 0; j < rangeinfo.nrows; j++) {
            var cellcr = SocialCalc.crToCoord(rangeinfo.col1num + i, rangeinfo.row1num + j);
            var cell = rangeinfo.sheetdata.GetAssuredCell(cellcr);
            parameterValues[index].push(cell.datavalue.toString().replace(/ /g, '%20'));
            parameterCellRefs[index].push(cellcr);
            rangeSizeCounter++;
          }
        }
        if (rangeSizeCounter > maxRangeSize) maxRangeSize = rangeSizeCounter;
      }
    }
    var conditionIndex = -1;
    var toAddressParamOffset = 0;
    switch (parameters.function_name) {
      case 'EMAILIF':
        conditionIndex = 0;
        toAddressParamOffset = 1;
        break;
      case 'EMAILAT':
      case 'EMAILONEDIT':
        toAddressParamOffset = 1;
        break;
      case 'EMAILONEDITIF':
      case 'EMAILATIF':
        conditionIndex = 1;
        toAddressParamOffset = 2;
        break;
      case 'EMAIL':
        break;
    }
    switch (parameters.function_name) {
      case 'EMAILONEDIT':
      case 'EMAILONEDITIF':
        if (
          optionalTriggerCellId &&
          parameters[0].type == 'coord' &&
          parameters[0].value == optionalTriggerCellId
        )
          optionalTriggerCellId = null;
        break;
      default:
        optionalTriggerCellId = null;
    }
    var setStatusBarMessage = false;
    var emailContentsList = [];
    for (var rangeIndex = maxRangeSize - 1; rangeIndex > -1; rangeIndex--) {
      if (conditionIndex != -1) {
        var conditionRangeIndex =
          rangeIndex >= parameterValues[conditionIndex].length ? 0 : rangeIndex;
        if (parameterValues[conditionIndex][conditionRangeIndex] == false) continue;
      }
      if (optionalTriggerCellId && optionalTriggerCellId != parameterCellRefs[0][rangeIndex])
        continue;
      var toaddressRangeIndex =
        rangeIndex >= parameterValues[toAddressParamOffset].length ? 0 : rangeIndex;
      var subjectsRangeIndex =
        rangeIndex >= parameterValues[toAddressParamOffset + 1].length ? 0 : rangeIndex;
      var bodyRangeIndex =
        rangeIndex >= parameterValues[toAddressParamOffset + 2].length ? 0 : rangeIndex;
      var emailContents =
        parameterValues[toAddressParamOffset][toaddressRangeIndex] +
        ' ' +
        parameterValues[toAddressParamOffset + 1][subjectsRangeIndex] +
        ' ' +
        parameterValues[toAddressParamOffset + 2][bodyRangeIndex];
      setStatusBarMessage = true;
      sheet.ScheduleSheetCommands('sendemail ' + emailContents, false);
      emailContentsList.push([
        parameterValues[toAddressParamOffset][toaddressRangeIndex],
        parameterValues[toAddressParamOffset + 1][subjectsRangeIndex],
        parameterValues[toAddressParamOffset + 2][bodyRangeIndex],
      ]);
    }
    if (setStatusBarMessage)
      SocialCalc.EditorSheetStatusCallback(null, 'emailing', null, spreadsheet.editor);
    return emailContentsList;
  };
  TriggerIoMut.Submit = function (_triggerCellId) {
    var formDataViewer =
      SocialCalc.CurrentSpreadsheetControlObject != null
        ? SocialCalc.CurrentSpreadsheetControlObject.formDataViewer
        : SocialCalc.CurrentSpreadsheetViewerObject.formDataViewer;
    if (formDataViewer != null && formDataViewer.loaded == true) {
      var spreadsheet = window.spreadsheet;
      if (spreadsheet == null) spreadsheet = window.ss;
      var sheet = spreadsheet.sheet;
      var date = new Date();
      var formDataValues =
        '' +
        date.getFullYear() +
        '-' +
        (date.getMonth() + 1) +
        '-' +
        date.getDate() +
        ' ' +
        date.getHours() +
        ':' +
        date.getMinutes() +
        ':' +
        date.getSeconds();
      for (var colIndex = 2; colIndex <= formDataViewer.formFieldsLength + 1; colIndex++) {
        var valueCoord = SocialCalc.crToCoord(colIndex, 2);
        formDataValues += '\r' + formDataViewer.sheet.cells[valueCoord].datavalue;
      }
      sheet.ScheduleSheetCommands('submitform \r' + formDataValues, false);
    }
  };
  TriggerIoMut.SelectList = function (selectListCellId) {
    var getHTMLselectListCellValue = function (selectListWidget) {
      return selectListWidget.value;
    };
    var function_name = 'SELECT';
    SocialCalc.TriggerIoAction.updateInputWidgetFormula(
      function_name,
      selectListCellId,
      getHTMLselectListCellValue,
    );
  };
  TriggerIoMut.AutoComplete = function (autoCompleteCellId) {
    var getHTMLAutoCompleteCellValue = function (autoCompleteWidget) {
      return autoCompleteWidget.value;
    };
    var function_name = 'AUTOCOMPLETE';
    SocialCalc.TriggerIoAction.updateInputWidgetFormula(
      function_name,
      autoCompleteCellId,
      getHTMLAutoCompleteCellValue,
    );
  };
  TriggerIoMut.TextBox = function (textBoxCellId) {
    var getHTMLTextBoxCellValue = function (textBoxWidget) {
      return textBoxWidget.value;
    };
    var function_name = 'TEXTBOX';
    SocialCalc.TriggerIoAction.updateInputWidgetFormula(
      function_name,
      textBoxCellId,
      getHTMLTextBoxCellValue,
    );
  };
  TriggerIoMut.CheckBox = function (checkBoxCellId) {
    var getHTMLCheckBoxCellValue = function (checkBoxWidget) {
      return checkBoxWidget.checked ? 'TRUE' : 'FALSE';
    };
    var function_name = 'CHECKBOX';
    SocialCalc.TriggerIoAction.updateInputWidgetFormula(
      function_name,
      checkBoxCellId,
      getHTMLCheckBoxCellValue,
    );
  };
  TriggerIoMut.RadioButton = function (radioButtonGroupName) {
    var getHTMLRadioButtonValue = function (radioButtonWidget) {
      return radioButtonWidget.checked ? 'TRUE' : 'FALSE';
    };
    var function_name = 'RADIOBUTTON';
    $('input[name="' + radioButtonGroupName + '"]').each(function () {
      SocialCalc.TriggerIoAction.updateInputWidgetFormula(
        function_name,
        $(this)
          .attr('id')
          .replace(/RADIOBUTTON_/, ''),
        getHTMLRadioButtonValue,
      );
    });
  };
  TriggerIoMut.updateInputWidgetFormula = function (
    function_name,
    widgetCellId,
    getHTMLWidgetCellValue,
  ) {
    var spreadsheet = window.spreadsheet;
    if (spreadsheet == null) spreadsheet = window.ss;
    var sheet = spreadsheet.sheet;
    var parameters = sheet.ioParameterList[widgetCellId];
    if (typeof parameters === 'undefined') return;
    var cell_widget = document.getElementById(function_name + '_' + widgetCellId);
    var inputValue = getHTMLWidgetCellValue(cell_widget);
    inputValue = SocialCalc.encodeForSave(inputValue);
    var sheetCommand =
      'set ' + widgetCellId + ' formula ' + function_name + '("' + inputValue + '"';
    for (var paramIndex = 1; paramIndex < parameters.length; paramIndex++) {
      if (parameters[paramIndex].type.charAt(0) == 'n') {
        sheetCommand += ',' + parameters[paramIndex].value;
      }
      if (parameters[paramIndex].type.charAt(0) == 't') {
        sheetCommand += ',"' + parameters[paramIndex].value + '"';
      }
      if (parameters[paramIndex].type == 'range') {
        sheetCommand +=
          ',' +
          parameters[paramIndex].value
            .toString()
            .replace(/(\$?[A-Z]+\$?[0-9]+)([!]?)([^|]*)[|](\$?[A-Z]+\$?[0-9]+)[|]/i, '$3$2$1:$4');
      }
      if (parameters[paramIndex].type == 'coord') {
        sheetCommand += ',' + parameters[paramIndex].value;
      }
    }
    sheetCommand += ')';
    if (parameters.cssParameter) {
      sheetCommand += '+style(' + parameters.cssParameter + ')';
    }
    spreadsheet.editor.EditorScheduleSheetCommands(sheetCommand, true, false);
    SocialCalc.TriggerIoAction.UpdateFormDataSheet(function_name, widgetCellId, inputValue);
  };
  TriggerIoMut.UpdateFormDataSheet = function (function_name, formCellId, inputValue) {
    var formDataViewer =
      SocialCalc.CurrentSpreadsheetControlObject != null
        ? SocialCalc.CurrentSpreadsheetControlObject.formDataViewer
        : SocialCalc.CurrentSpreadsheetViewerObject.formDataViewer;
    if (formDataViewer == null) return;
    var formFieldName = (function_name + formCellId).toLowerCase();
    if (formDataViewer.formFields[formFieldName] != null) {
      var valueCoord = SocialCalc.crToCoord(formDataViewer.formFields[formFieldName], 2);
      formDataViewer.sheet.ScheduleSheetCommands(
        'set ' + valueCoord + ' text t ' + inputValue,
        false,
      );
    }
  };
  FormulaMut.getStandardizedValues = function (sheet, parameterData) {
    return SocialCalc.Formula.getStandardizedParameter(sheet, parameterData, true, true);
  };
  FormulaMut.getStandardizedCoords = function (sheet, parameterData) {
    return SocialCalc.Formula.getStandardizedParameter(sheet, parameterData, true, false);
  };
  FormulaMut.getStandardizedList = function (sheet, listParameter) {
    var listValues = [];
    var parameterdata = SocialCalc.Formula.getStandardizedValues(sheet, listParameter);
    if (parameterdata.ncols == 1 && parameterdata.nrows == 1) {
      listValues = String(parameterdata.celldata[0][0].datavalue).split(',');
    } else {
      for (var i = 0; i < parameterdata.ncols; i++) {
        for (var j = 0; j < parameterdata.nrows; j++) {
          var cell = parameterdata.celldata[i][j];
          listValues.push(cell.datavalue.toString());
        }
      }
    }
    return listValues;
  };
  FormulaMut.getStandardizedParameter = function (
    sheet,
    parameterData,
    includeCellCoord,
    includeCellData,
  ) {
    var result = {
      type: parameterData.type,
      value: parameterData.value,
    };
    if (includeCellData) result.celldata = [];
    if (parameterData.type != 'coord' && parameterData.type != 'range') {
      result.ncols = 1;
      result.nrows = 1;
      result.col1num = 1;
      result.row1num = 1;
      if (includeCellCoord) {
        result.cellcoord = null;
      }
      if (includeCellData) {
        result.celldata[0] = [];
        var constantDatatype =
          parameterData.type == 'n' ? 'v' : parameterData.type == 't' ? parameterData.type : 'c';
        result.celldata[0][0] = {
          coord: null,
          datatype: constantDatatype,
          valuetype: parameterData.type,
          datavalue: parameterData.value,
        };
      }
    } else {
      var scf = SocialCalc.Formula;
      var sourcerangeinfo = null;
      if (parameterData.type == 'coord') {
        var sourceCoord = SocialCalc.Formula.PlainCoord(parameterData.value);
        sourcerangeinfo = scf.DecodeRangeParts(sheet, sourceCoord + '|' + sourceCoord + '|');
      }
      if (parameterData.type == 'range') {
        sourcerangeinfo = scf.DecodeRangeParts(sheet, parameterData.value);
      }
      if (includeCellCoord) result.cellcoord = [];
      for (var i = 0; i < sourcerangeinfo.ncols; i++) {
        for (var j = 0; j < sourcerangeinfo.nrows; j++) {
          var cellcoord = SocialCalc.crToCoord(
            sourcerangeinfo.col1num + i,
            sourcerangeinfo.row1num + j,
          );
          if (includeCellCoord) {
            if (typeof result.cellcoord[i] === 'undefined') result.cellcoord[i] = [];
            result.cellcoord[i][j] = cellcoord;
          }
          if (includeCellData) {
            if (typeof result.celldata[i] === 'undefined') result.celldata[i] = [];
            var cell = sourcerangeinfo.sheetdata.GetAssuredCell(cellcoord);
            result.celldata[i][j] = cell;
          }
        }
      }
      result.ncols = sourcerangeinfo.ncols;
      result.nrows = sourcerangeinfo.nrows;
      result.col1num = sourcerangeinfo.col1num;
      result.row1num = sourcerangeinfo.row1num;
    }
    return result;
  };
  FormulaMut.SheetCache = {
    sheets: {},
    waitingForLoading: null,
    constants: {
      asloaded: 0,
      recalcing: 1,
      recalcdone: 2,
    },
    loadsheet: null,
  };
  FormulaMut.FindInSheetCache = function (sheetname) {
    var sfsc = SocialCalc.Formula.SheetCache;
    var nsheetname = SocialCalc.Formula.NormalizeSheetName(sheetname);
    if (sfsc.sheets[nsheetname]) {
      return sfsc.sheets[nsheetname].sheet;
    }
    if (sfsc.waitingForLoading) {
      return null;
    }
    if (sfsc.loadsheet) {
      (function(){})('Using SocialCalc.Formula.SheetCache.loadsheet - deprecated');
      return SocialCalc.Formula.AddSheetToCache(nsheetname, sfsc.loadsheet(nsheetname));
    }
    sfsc.waitingForLoading = nsheetname;
    return null;
  };
  FormulaMut.AddSheetToCache = function (sheetname, str, live) {
    var newsheet = null;
    var sfsc = SocialCalc.Formula.SheetCache;
    var sfscc = sfsc.constants;
    var newsheetname = SocialCalc.Formula.NormalizeSheetName(sheetname);
    if (str) {
      newsheet = new SocialCalc.Sheet();
      newsheet.ParseSheetSave(str);
    }
    sfsc.sheets[newsheetname] = {
      sheet: newsheet,
      recalcstate: sfscc.asloaded,
      name: newsheetname,
    };
    SocialCalc.Formula.FreshnessInfo.sheets[newsheetname] =
      typeof live == 'undefined' || live === false;
    return newsheet;
  };
  FormulaMut.NormalizeSheetName = function (sheetname) {
    if (SocialCalc.Callbacks.NormalizeSheetName) {
      return SocialCalc.Callbacks.NormalizeSheetName(sheetname);
    } else {
      return sheetname.toLowerCase();
    }
  };
  FormulaMut.RemoteFunctionInfo = { waitingForServer: null };
  FormulaMut.FreshnessInfo = {
    sheets: {},
    volatile: {},
    recalc_completed: false,
  };
  FormulaMut.FreshnessInfoReset = function () {
    var scffi = SocialCalc.Formula.FreshnessInfo;
    var scfsc = SocialCalc.Formula.SheetCache;
    for (var sheet in scffi.sheets) {
      if (scffi.sheets[sheet] === false) {
        delete scfsc.sheets[sheet];
      }
    }
    scffi.sheets = {};
    scffi.volatile = {};
    scffi.recalc_completed = false;
  };
  FormulaMut.PlainCoord = function (coord) {
    if (coord.indexOf('$') == -1) return coord;
    return coord.replace(/\$/g, '');
  };
  FormulaMut.OrderRangeParts = function (coord1, coord2) {
    var cr1, cr2;
    var result = {};
    cr1 = SocialCalc.coordToCr(coord1);
    cr2 = SocialCalc.coordToCr(coord2);
    if (cr1.col > cr2.col) {
      result.c1 = cr2.col;
      result.c2 = cr1.col;
    } else {
      result.c1 = cr1.col;
      result.c2 = cr2.col;
    }
    if (cr1.row > cr2.row) {
      result.r1 = cr2.row;
      result.r2 = cr1.row;
    } else {
      result.r1 = cr1.row;
      result.r2 = cr2.row;
    }
    return result;
  };
  FormulaMut.TestCriteria = function (value, type, criteria) {
    var comparitor, basestring, basevalue, cond, testvalue;
    if (criteria == null) {
      return false;
    }
    criteria = criteria + '';
    if (
      criteria.substring(0, 2) == '>=' ||
      criteria.substring(0, 2) == '<=' ||
      criteria.substring(0, 2) == '<>'
    ) {
      comparitor = criteria.substring(0, 2);
      basestring = criteria.substring(2);
    } else {
      comparitor = criteria.charAt(0);
      if (comparitor == '=' || comparitor == '<' || comparitor == '>') {
        basestring = criteria.substring(1);
      } else {
        if (criteria.search(/([^~]\*|^\*)/) != -1 || criteria.search(/([^~]\?|^\?)/) != -1) {
          comparitor = 'regex';
          if (criteria == '*') {
            basestring = '.+';
          } else {
            basestring = criteria.split('').reverse().join('');
            basestring = basestring
              .replace(/\?(?=[^~])|\?$/g, '?.')
              .replace(/\?~/g, '?\\')
              .replace(/\*(?=[^~])|\*$/g, '*.')
              .replace(/\*~/, '*\\');
            basestring = basestring.split('').reverse().join('');
          }
          basestring = '^' + basestring + '$';
        } else {
          comparitor = 'none';
          basestring = criteria;
        }
      }
    }
    basevalue = SocialCalc.DetermineValueType(basestring);
    if (!basevalue.type) {
      if (comparitor == 'none') {
        return false;
      }
      if (type.charAt(0) == 'b' && comparitor == '=') return true;
      return false;
    }
    cond = false;
    if (basevalue.type.charAt(0) == 'n' && type.charAt(0) == 't') {
      testvalue = SocialCalc.DetermineValueType(value);
      if (testvalue.type.charAt(0) == 'n') {
        value = testvalue.value;
        type = testvalue.type;
      }
    }
    if (type.charAt(0) == 'n' && basevalue.type.charAt(0) == 'n') {
      value = value - 0;
      basevalue.value = basevalue.value - 0;
      switch (comparitor) {
        case '<':
          cond = value < basevalue.value;
          break;
        case '<=':
          cond = value <= basevalue.value;
          break;
        case '=':
        case 'none':
          cond = value == basevalue.value;
          break;
        case '>':
          cond = value > basevalue.value;
          break;
        case '>=':
          cond = value >= basevalue.value;
          break;
        case '<>':
          cond = value != basevalue.value;
          break;
      }
    } else if (type.charAt(0) == 'e') {
      cond = false;
    } else if (basevalue.type.charAt(0) == 'e') {
      cond = false;
    } else {
      if (type.charAt(0) == 'n') {
        value = SocialCalc.format_number_for_display(value, 'n', '');
      }
      if (basevalue.type.charAt(0) == 'n') {
        return false;
      }
      value = value ? value.toLowerCase() : '';
      basevalue.value = basevalue.value ? basevalue.value.toLowerCase() : '';
      switch (comparitor) {
        case '<':
          cond = value < basevalue.value;
          break;
        case '<=':
          cond = value <= basevalue.value;
          break;
        case '=':
          cond = value == basevalue.value;
          break;
        case 'none':
          cond = value.substring(0, basevalue.value.length) == basevalue.value;
          break;
        case '>':
          cond = value > basevalue.value;
          break;
        case '>=':
          cond = value >= basevalue.value;
          break;
        case '<>':
          cond = value != basevalue.value;
          break;
        case 'regex':
          try {
            cond = value.search(new RegExp(basevalue.value)) != -1;
          } catch {
            cond = false;
          }
          break;
      }
    }
    return cond;
  };

  // Pure formula parse / token / type helpers.
  // Shipping source extracted from formula1 for full typecheck + LemmaScript.
  // Concatenated after formula1 (Formula bag + token tables) and before formula-ref.
  // Fully typechecked — no @ts-nocheck.
  //
  /*
// SocialCalc Formula Parse Helpers
// Part of the SocialCalc package.
// (c) Copyright 2008 Socialtext, Inc.
// Artistic License 2.0: http://socialcalc.org/licenses/al-20/
//
*/

  // Formula object and token tables are created in formula1.ts. Assign pure
  // methods onto the same bag via a named mutable view typed against the ambient API.

  const FormulaParseMut = SocialCalc.Formula;
  FormulaParseMut.ParsePushToken = function (parseinfo, ttext, ttype, topcode) {
    parseinfo.push({
      text: ttext,
      type: ttype,
      opcode: topcode,
    });
  };
  FormulaParseMut.ParseFormulaIntoTokens = function (line) {
    var i, ch, cclass, last_token, last_token_type, last_token_text, t;
    var scf = SocialCalc.Formula;
    var scc = SocialCalc.Constants;
    var parsestate = scf.ParseState;
    var tokentype = scf.TokenType;
    var charclass = scf.CharClass;
    var charclasstable = scf.CharClassTable;
    var uppercasetable = scf.UpperCaseTable;
    var pushtoken = scf.ParsePushToken;
    var coordregex = /^\$?[A-Z]{1,2}\$?[1-9]\d*$/i;
    var parseinfo = [];
    var str = '';
    var state = 0;
    var haddecimal = false;
    var twochrop = '';
    for (i = 0; i <= line.length; i++) {
      if (i < line.length) {
        ch = line.charAt(i);
        cclass = charclasstable[ch];
      } else {
        ch = '';
        cclass = charclass.eof;
      }
      if (state == parsestate.num) {
        if (cclass == charclass.num) {
          str += ch;
        } else if (cclass == charclass.numstart && !haddecimal) {
          haddecimal = true;
          str += ch;
        } else if (ch == 'E' || ch == 'e') {
          str += ch;
          haddecimal = false;
          state = parsestate.numexp1;
        } else {
          pushtoken(parseinfo, str, tokentype.num, 0);
          haddecimal = false;
          state = 0;
        }
      }
      if (state == parsestate.numexp1) {
        if (cclass == parsestate.num) {
          state = parsestate.numexp2;
        } else if ((ch == '+' || ch == '-') && uppercasetable[str.charAt(str.length - 1)] == 'E') {
          str += ch;
        } else if (ch == 'E' || ch == 'e') {
        } else {
          pushtoken(parseinfo, scc.s_parseerrexponent, tokentype.error, 0);
          state = 0;
        }
      }
      if (state == parsestate.numexp2) {
        if (cclass == charclass.num) {
          str += ch;
        } else {
          pushtoken(parseinfo, str, tokentype.num, 0);
          state = 0;
        }
      }
      if (state == parsestate.alpha) {
        if (cclass == charclass.num) {
          state = parsestate.coord;
        } else if (cclass == charclass.alpha || ch == '.') {
          str += ch;
        } else if (cclass == charclass.incoord) {
          state = parsestate.coord;
        } else if (
          cclass == charclass.op ||
          cclass == charclass.numstart ||
          cclass == charclass.space ||
          cclass == charclass.eof
        ) {
          pushtoken(parseinfo, str.toUpperCase(), tokentype.name, 0);
          state = 0;
        } else {
          pushtoken(parseinfo, scc.s_parseerrchar, tokentype.error, 0);
          state = 0;
        }
      }
      if (state == parsestate.coord) {
        if (cclass == charclass.num) {
          str += ch;
        } else if (cclass == charclass.incoord) {
          str += ch;
        } else if (cclass == charclass.alpha) {
          state = parsestate.alphanumeric;
        } else if (
          cclass == charclass.op ||
          cclass == charclass.numstart ||
          cclass == charclass.eof ||
          cclass == charclass.space
        ) {
          if (coordregex.test(str)) {
            t = tokentype.coord;
          } else {
            t = tokentype.name;
          }
          pushtoken(parseinfo, str.toUpperCase(), t, 0);
          state = 0;
        } else {
          pushtoken(parseinfo, scc.s_parseerrchar, tokentype.error, 0);
          state = 0;
        }
      }
      if (state == parsestate.alphanumeric) {
        if (cclass == charclass.num || cclass == charclass.alpha) {
          str += ch;
        } else if (
          cclass == charclass.op ||
          cclass == charclass.numstart ||
          cclass == charclass.space ||
          cclass == charclass.eof
        ) {
          pushtoken(parseinfo, str.toUpperCase(), tokentype.name, 0);
          state = 0;
        } else {
          pushtoken(parseinfo, scc.s_parseerrchar, tokentype.error, 0);
          state = 0;
        }
      }
      if (state == parsestate.string) {
        if (cclass == charclass.quote) {
          state = parsestate.stringquote;
        } else if (cclass == charclass.eof) {
          pushtoken(parseinfo, scc.s_parseerrstring, tokentype.error, 0);
          state = 0;
        } else {
          str += ch;
        }
      } else if (state == parsestate.stringquote) {
        if (cclass == charclass.quote) {
          str += ch;
          state = parsestate.string;
        } else {
          pushtoken(parseinfo, str, tokentype.string, 0);
          state = 0;
        }
      } else if (state == parsestate.specialvalue) {
        if (
          str.charAt(str.length - 1) == '!' ||
          (scf.SpecialConstants && scf.SpecialConstants[str.toUpperCase()])
        ) {
          pushtoken(parseinfo, str.toUpperCase(), tokentype.name, 0);
          state = 0;
        } else if (cclass == charclass.eof) {
          pushtoken(parseinfo, scc.s_parseerrspecialvalue, tokentype.error, 0);
          state = 0;
        } else {
          str += ch;
        }
      }
      if (state == 0) {
        if (cclass == charclass.num) {
          str = ch;
          state = parsestate.num;
        } else if (cclass == charclass.numstart) {
          str = ch;
          haddecimal = true;
          state = parsestate.num;
        } else if (cclass == charclass.alpha || cclass == charclass.incoord) {
          str = ch;
          state = parsestate.alpha;
        } else if (cclass == charclass.specialstart) {
          str = ch;
          state = parsestate.specialvalue;
        } else if (cclass == charclass.op) {
          str = ch;
          if (parseinfo.length > 0) {
            last_token = parseinfo[parseinfo.length - 1];
            last_token_type = last_token.type;
            last_token_text = last_token.text;
            if (last_token_type == charclass.op) {
              twochrop = last_token_text + str;
              if (twochrop == '<=' || twochrop == '>=' || twochrop == '<>') {
                str = last_token_text + str;
                parseinfo.pop();
                last_token = parseinfo[parseinfo.length - 1];
                last_token_type = last_token.type;
                last_token_text = last_token.text;
              }
            }
          } else {
            last_token_type = charclass.eof;
            last_token_text = 'EOF';
          }
          t = tokentype.op;
          if (
            parseinfo.length == 0 ||
            (last_token_type == charclass.op && last_token_text != ')' && last_token_text != '%')
          ) {
            if (str == '-') {
              str = 'M';
              ch = 'M';
            } else if (str == '+') {
              str = 'P';
              ch = 'P';
            } else if (str == ')' && last_token_text == '(') {
            } else if (str != '(') {
              t = tokentype.error;
              str = scc.s_parseerrtwoops;
            }
          } else if (str.length > 1) {
            if (str == '>=') {
              str = 'G';
              ch = 'G';
            } else if (str == '<=') {
              str = 'L';
              ch = 'L';
            } else {
              str = 'N';
              ch = 'N';
            }
          }
          pushtoken(parseinfo, str, t, ch);
          state = 0;
        } else if (cclass == charclass.quote) {
          str = '';
          state = parsestate.string;
        } else if (cclass == charclass.space) {
        } else if (cclass == charclass.eof) {
        } else {
          pushtoken(parseinfo, scc.s_parseerrchar, tokentype.error, 0);
        }
      }
    }
    return parseinfo;
  };
  FormulaParseMut.ConvertInfixToPolish = function (parseinfo) {
    var scf = SocialCalc.Formula;
    var scc = SocialCalc.Constants;
    var tokentype = scf.TokenType;
    var token_precedence = scf.TokenPrecedence;
    var revpolish = [];
    var parsestack = [];
    var errortext = '';
    var function_start = -1;
    var i, pii, ttype, ttext, tprecedence, tstackprecedence;
    for (i = 0; i < parseinfo.length; i++) {
      pii = parseinfo[i];
      ttype = pii.type;
      ttext = pii.text;
      if (ttype == tokentype.num || ttype == tokentype.coord || ttype == tokentype.string) {
        revpolish.push(i);
      } else if (ttype == tokentype.name) {
        parsestack.push(i);
        revpolish.push(function_start);
      } else if (ttype == tokentype.space) {
        continue;
      } else if (ttext == ',') {
        while (parsestack.length && parseinfo[parsestack[parsestack.length - 1]].text != '(') {
          revpolish.push(parsestack.pop());
        }
        if (parsestack.length == 0) {
          errortext = scc.s_parseerrmissingopenparen;
          break;
        }
      } else if (ttext == '(') {
        parsestack.push(i);
      } else if (ttext == ')') {
        while (parsestack.length && parseinfo[parsestack[parsestack.length - 1]].text != '(') {
          revpolish.push(parsestack.pop());
        }
        if (parsestack.length == 0) {
          errortext = scc.s_parseerrcloseparennoopen;
          break;
        }
        parsestack.pop();
        if (
          parsestack.length &&
          parseinfo[parsestack[parsestack.length - 1]].type == tokentype.name
        ) {
          revpolish.push(parsestack.pop());
        }
      } else if (ttype == tokentype.op) {
        if (
          parsestack.length &&
          parseinfo[parsestack[parsestack.length - 1]].type == tokentype.name
        ) {
          revpolish.push(parsestack.pop());
        }
        while (
          parsestack.length &&
          parseinfo[parsestack[parsestack.length - 1]].type == tokentype.op &&
          parseinfo[parsestack[parsestack.length - 1]].text != '('
        ) {
          tprecedence = token_precedence[pii.opcode];
          tstackprecedence = token_precedence[parseinfo[parsestack[parsestack.length - 1]].opcode];
          if (tprecedence >= 0 && tprecedence < tstackprecedence) break;
          if (tprecedence < 0) {
            tprecedence = -tprecedence;
            if (tstackprecedence < 0) tstackprecedence = -tstackprecedence;
            if (tprecedence <= tstackprecedence) break;
          }
          revpolish.push(parsestack.pop());
        }
        parsestack.push(i);
      } else if (ttype == tokentype.error) {
        errortext = ttext;
        break;
      }
    }
    while (parsestack.length > 0) {
      if (parseinfo[parsestack[parsestack.length - 1]].text == '(') {
        errortext = scc.s_parseerrmissingcloseparen;
        break;
      }
      revpolish.push(parsestack.pop());
    }
    if (errortext) {
      return errortext;
    }
    return revpolish;
  };
  FormulaParseMut.LookupResultType = function (type1, type2, typelookup) {
    var pos1, pos2, result;
    var table1 = typelookup[type1];
    if (!table1) {
      table1 = typelookup[type1.charAt(0) + '*'];
      if (!table1) {
        return 'e#VALUE! (internal error, missing LookupResultType ' + type1.charAt(0) + '*)';
      }
    }
    pos1 = table1.indexOf('|' + type2 + ':');
    if (pos1 >= 0) {
      pos2 = table1.indexOf('|', pos1 + 1);
      if (pos2 < 0) return 'e#VALUE! (internal error, incorrect LookupResultType ' + table1 + ')';
      result = table1.substring(pos1 + type2.length + 2, pos2);
      if (result == '1') return type1;
      if (result == '2') return type2;
      return result;
    }
    pos1 = table1.indexOf('|' + type2.charAt(0) + '*:');
    if (pos1 >= 0) {
      pos2 = table1.indexOf('|', pos1 + 1);
      if (pos2 < 0) return 'e#VALUE! (internal error, incorrect LookupResultType ' + table1 + ')';
      result = table1.substring(pos1 + 4, pos2);
      if (result == '1') return type1;
      if (result == '2') return type2;
      return result;
    }
    return 'e#VALUE!';
  };
  FormulaParseMut.ArrayValuesEqual = function (a, b) {
    var i = a.length;
    if (i != b.length) return false;
    while (i--) {
      if (a[i] !== b[i]) return false;
    }
    return true;
  };
  FormulaParseMut.PushOperand = function (operand, t, v) {
    operand.push({
      type: t,
      value: v,
    });
  };
  FormulaParseMut.CopyFunctionArgs = function (operand, foperand) {
    while (operand.length > 0 && operand[operand.length - 1].type != 'start') {
      foperand.push(operand.pop());
    }
    operand.pop();
    return;
  };

  // Pure formula operand-stack helpers.
  // Shipping source extracted from formula1 for full typecheck + LemmaScript.
  // Concatenated after formula-parse (token helpers) and before formula-ref.
  // Fully typechecked — no @ts-nocheck.
  //
  /*
// SocialCalc Formula Operand Helpers
// Part of the SocialCalc package.
// (c) Copyright 2008 Socialtext, Inc.
// Artistic License 2.0: http://socialcalc.org/licenses/al-20/
//
*/

  // Methods assign onto SocialCalc.Formula created in formula1.ts.

  const FormulaOperandMut = SocialCalc.Formula;
  FormulaOperandMut.TopOfStackValueAndType = function (sheet, operand) {
    var scf = SocialCalc.Formula;
    var result = {
      type: '',
      value: '',
    };
    var stacklen = operand.length;
    if (!stacklen) {
      result.error = SocialCalc.Constants.s_InternalError + 'no operand on stack';
      return result;
    }
    result.value = operand[stacklen - 1].value;
    result.type = operand[stacklen - 1].type;
    operand.pop();
    if (result.type == 'name') {
      result = scf.LookupName(sheet, result.value);
    }
    return result;
  };
  FormulaOperandMut.OperandAsNumber = function (sheet, operand) {
    var t, valueinfo;
    var operandinfo = SocialCalc.Formula.OperandValueAndType(sheet, operand);
    t = operandinfo.type.charAt(0);
    if (t == 'n') {
      operandinfo.value = operandinfo.value - 0;
    } else if (t == 'b') {
      operandinfo.type = 'n';
      operandinfo.value = 0;
    } else if (t == 'e') {
      operandinfo.value = 0;
    } else {
      valueinfo = SocialCalc.DetermineValueType
        ? SocialCalc.DetermineValueType(operandinfo.value)
        : {
            value: operandinfo.value - 0,
            type: 'n',
          };
      if (valueinfo.type.charAt(0) == 'n') {
        operandinfo.value = valueinfo.value - 0;
        operandinfo.type = valueinfo.type;
      } else {
        operandinfo.value = 0;
        operandinfo.type = valueinfo.type.charAt(0) == 'e' ? valueinfo.type : 'e#VALUE!';
      }
    }
    return operandinfo;
  };
  FormulaOperandMut.OperandAsText = function (sheet, operand) {
    var t;
    var operandinfo = SocialCalc.Formula.OperandValueAndType(sheet, operand);
    t = operandinfo.type.charAt(0);
    if (t == 't') {
    } else if (t == 'n') {
      operandinfo.value = SocialCalc.format_number_for_display
        ? SocialCalc.format_number_for_display(operandinfo.value, operandinfo.type, '')
        : (operandinfo.value = operandinfo.value + '');
      operandinfo.type = 't';
    } else if (t == 'b') {
      operandinfo.value = '';
      operandinfo.type = 't';
    } else if (t == 'e') {
      operandinfo.value = '';
    } else {
      operandinfo.value = operandinfo.value + '';
      operandinfo.type = 't';
    }
    return operandinfo;
  };
  FormulaOperandMut.OperandValueAndType = function (sheet, operand) {
    var cellvtype, cell, pos, coordsheet;
    var scf = SocialCalc.Formula;
    var result = {
      type: '',
      value: '',
    };
    var stacklen = operand.length;
    if (!stacklen) {
      result.error = SocialCalc.Constants.s_InternalError + 'no operand on stack';
      return result;
    }
    result.value = operand[stacklen - 1].value;
    result.type = operand[stacklen - 1].type;
    operand.pop();
    if (result.type == 'name') {
      result = scf.LookupName(sheet, result.value);
    }
    if (result.type == 'range') {
      result = scf.StepThroughRangeDown(operand, result.value);
    }
    if (result.type == 'coord') {
      coordsheet = sheet;
      const coordText = result.value;
      pos = coordText.indexOf('!');
      if (pos != -1) {
        coordsheet = scf.FindInSheetCache(coordText.substring(pos + 1));
        if (coordsheet == null) {
          result.type = 'e#REF!';
          result.error =
            SocialCalc.Constants.s_sheetunavailable + ' ' + coordText.substring(pos + 1);
          result.value = 0;
          return result;
        }
        result.value = coordText.substring(0, pos);
      }
      cell = coordsheet.cells[SocialCalc.Formula.PlainCoord(result.value)];
      if (cell) {
        cellvtype = cell.valuetype;
        result.value = cell.datavalue;
      } else {
        cellvtype = 'b';
      }
      result.type = cellvtype || 'b';
      if (result.type == 'b') {
        result.value = 0;
      }
    }
    return result;
  };
  FormulaOperandMut.OperandAsCoord = function (sheet, operand) {
    return SocialCalc.Formula.OperandAsType(sheet, operand, 'coord');
  };
  FormulaOperandMut.OperandAsRange = function (sheet, operand) {
    return SocialCalc.Formula.OperandAsType(sheet, operand, 'range');
  };
  FormulaOperandMut.OperandAsType = function (sheet, operand, operandtype) {
    var result = {
      type: '',
      value: '',
    };
    var stacklen = operand.length;
    if (!stacklen) {
      result.type = 'e#REF!';
      result.value = 0;
      result.error = SocialCalc.Constants.s_InternalError + 'no operand on stack';
      return result;
    }
    result.value = operand[stacklen - 1].value;
    result.type = operand[stacklen - 1].type;
    operand.pop();
    if (result.type == 'name') {
      result = SocialCalc.Formula.LookupName(sheet, result.value);
    }
    if (result.type == operandtype) {
      return result;
    } else {
      result.value = SocialCalc.Constants.s_calcerrcellrefmissing;
      result.type = 'e#REF!';
      return result;
    }
  };
  FormulaOperandMut.OperandsAsCoordOnSheet = function (sheet, operand) {
    var sheetname, othersheet, pos1, pos2;
    var value1 = {
      type: '',
      value: '',
    };
    var result = {
      type: '',
      value: '',
    };
    var scf = SocialCalc.Formula;
    var stacklen = operand.length;
    value1.value = operand[stacklen - 1].value;
    value1.type = operand[stacklen - 1].type;
    operand.pop();
    sheetname = scf.OperandAsSheetName(sheet, operand);
    if (sheetname.type.charAt(0) == 'e' || sheetname.error) {
      if (sheetname.type.charAt(0) == 'e') {
        result.type = sheetname.type;
        result.value = sheetname.value;
      } else {
        result.type = 'e#REF!';
        result.value = 0;
      }
      if (sheetname.error) {
        result.error = sheetname.error;
      }
      return result;
    }
    othersheet = scf.FindInSheetCache(sheetname.value);
    if (othersheet == null) {
      result.type = 'e#REF!';
      result.value = 0;
      result.error = SocialCalc.Constants.s_sheetunavailable + ' ' + sheetname.value;
      return result;
    }
    if (value1.type == 'name') {
      value1 = scf.LookupName(othersheet, value1.value);
    }
    result.type = value1.type;
    if (value1.type == 'coord') {
      result.value = value1.value + '!' + sheetname.value;
    } else if (value1.type == 'range') {
      const rv = value1.value;
      pos1 = rv.indexOf('|');
      pos2 = rv.indexOf('|', pos1 + 1);
      result.value =
        rv.substring(0, pos1) + '!' + sheetname.value + '|' + rv.substring(pos1 + 1, pos2) + '|';
    } else if (value1.type.charAt(0) == 'e') {
      result.value = value1.value;
    } else {
      result.error = SocialCalc.Constants.s_calcerrcellrefmissing;
      result.type = 'e#REF!';
      result.value = 0;
    }
    return result;
  };
  FormulaOperandMut.OperandsAsRangeOnSheet = function (sheet, operand) {
    var value1;
    var othersheet, pos1, pos2;
    var value2 = {
      type: '',
      value: '',
    };
    var scf = SocialCalc.Formula;
    var scc = SocialCalc.Constants;
    var stacklen = operand.length;
    value2.value = operand[stacklen - 1].value;
    value2.type = operand[stacklen - 1].type;
    operand.pop();
    value1 = scf.OperandAsCoord(sheet, operand);
    if (value1.type != 'coord') {
      return {
        value: 0,
        type: 'e#REF!',
      };
    }
    othersheet = sheet;
    const leftCoord = value1.value;
    pos1 = leftCoord.indexOf('!');
    if (pos1 != -1) {
      pos2 = leftCoord.indexOf('|', pos1 + 1);
      if (pos2 < 0) pos2 = leftCoord.length;
      othersheet = scf.FindInSheetCache(leftCoord.substring(pos1 + 1, pos2));
      if (othersheet == null) {
        return {
          value: 0,
          type: 'e#REF!',
          error: scc.s_sheetunavailable + ' ' + leftCoord.substring(pos1 + 1, pos2),
        };
      }
    }
    if (value2.type == 'name') {
      value2 = scf.LookupName(othersheet, value2.value, 'end');
    }
    if (value2.type == 'coord') {
      return {
        value: value1.value + '|' + value2.value + '|',
        type: 'range',
      };
    } else {
      return {
        value: scc.s_calcerrcellrefmissing,
        type: 'e#REF!',
      };
    }
  };
  FormulaOperandMut.OperandAsSheetName = function (sheet, operand) {
    var nvalue, cell;
    var result = {
      type: '',
      value: '',
    };
    var stacklen = operand.length;
    result.value = operand[stacklen - 1].value;
    result.type = operand[stacklen - 1].type;
    operand.pop();
    if (result.type == 'name') {
      nvalue = SocialCalc.Formula.LookupName(sheet, result.value);
      if (nvalue.type == 'e#NAME?' && nvalue.value === '') {
        return result;
      }
      result.value = nvalue.value;
      result.type = nvalue.type;
      if (nvalue.error) {
        result.error = nvalue.error;
      }
    }
    if (result.type.charAt(0) == 'e') {
      return result;
    }
    if (result.type == 'coord') {
      cell = sheet.cells[SocialCalc.Formula.PlainCoord(result.value)];
      if (cell) {
        result.value = cell.datavalue;
        result.type = cell.valuetype;
      } else {
        result.value = '';
        result.type = 'b';
      }
    }
    if (result.type.charAt(0) == 't') {
      return result;
    } else {
      result.value = '';
      result.error = SocialCalc.Constants.s_calcerrsheetnamemissing;
      return result;
    }
  };

  // In-place TypeScript module: pure formula-reference rewrite + A1 coord algebra.
  // Shipping source (not a parallel oracle). Concatenated after formula1.js so
  // SocialCalc.Formula.ParseFormulaIntoTokens exists at call time.
  // Fully typechecked — no @ts-nocheck.
  // Ambient API surface remains in socialcalc-3.d.ts for consumers.
  //
  /*
// SocialCalc Formula Reference Rewrite Helpers
//
// Part of the SocialCalc package.
// Extracted from socialcalc-3 for typechecked pure-core work and LemmaScript.
//
// (c) Copyright 2008 Socialtext, Inc.
// All Rights Reserved.
//
// The contents of this file are subject to the Artistic License 2.0; you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at http://socialcalc.org/licenses/al-20/.
//
*/

  // Runtime root is created by module-wrapper-top.js. Ambient declare namespace is
  // types-only; progressive assignment of these members uses a named mutable view
  // so we never redeclare `var SocialCalc` (which collapses the namespace in tsc).

  const FormulaRefRoot = SocialCalc;
  FormulaRefRoot.rcColname = function (c) {
    if (c > 702) c = 702;
    if (c < 1) c = 1;
    const collow = ((c - 1) % 26) + 65;
    const colhigh = Math.floor((c - 1) / 26);
    if (colhigh) {
      return String.fromCharCode(colhigh + 64) + String.fromCharCode(collow);
    }
    return String.fromCharCode(collow);
  };
  FormulaRefRoot.letters = [
    'A',
    'B',
    'C',
    'D',
    'E',
    'F',
    'G',
    'H',
    'I',
    'J',
    'K',
    'L',
    'M',
    'N',
    'O',
    'P',
    'Q',
    'R',
    'S',
    'T',
    'U',
    'V',
    'W',
    'X',
    'Y',
    'Z',
  ];
  FormulaRefRoot.crToCoord = function (c, r) {
    if (c < 1) c = 1;
    if (c > 702) c = 702;
    if (r < 1) r = 1;
    const collow = (c - 1) % 26;
    const colhigh = Math.floor((c - 1) / 26);
    if (colhigh) {
      return FormulaRefRoot.letters[colhigh - 1] + FormulaRefRoot.letters[collow] + r;
    }
    return FormulaRefRoot.letters[collow] + r;
  };
  FormulaRefRoot.coordToCol = {};
  FormulaRefRoot.coordToRow = {};
  FormulaRefRoot.coordToCr = function (cr) {
    const cachedRow = FormulaRefRoot.coordToRow[cr];
    if (cachedRow) {
      return {
        row: cachedRow,
        col: FormulaRefRoot.coordToCol[cr],
      };
    }
    let c = 0;
    let r = 0;
    for (let i = 0; i < cr.length; i++) {
      const ch = cr.charCodeAt(i);
      if (ch === 36) {
      } else if (ch <= 57) {
        r = 10 * r + ch - 48;
      } else if (ch >= 97) {
        c = 26 * c + ch - 96;
      } else if (ch >= 65) {
        c = 26 * c + ch - 64;
      }
    }
    if (c > 702) c = 0;
    FormulaRefRoot.coordToCol[cr] = c;
    FormulaRefRoot.coordToRow[cr] = r;
    return {
      row: r,
      col: c,
    };
  };
  FormulaRefRoot.ParseRange = function (range) {
    if (!range) range = 'A1:A1';
    range = range.toUpperCase();
    const pos = range.indexOf(':');
    if (pos >= 0) {
      const crA = range.substring(0, pos);
      const a = FormulaRefRoot.coordToCr(crA);
      const crB = range.substring(pos + 1);
      const b = FormulaRefRoot.coordToCr(crB);
      return {
        cr1: {
          row: a.row,
          col: a.col,
          coord: crA,
        },
        cr2: {
          row: b.row,
          col: b.col,
          coord: crB,
        },
      };
    }
    const p0 = FormulaRefRoot.coordToCr(range);
    return {
      cr1: {
        row: p0.row,
        col: p0.col,
        coord: range,
      },
      cr2: {
        row: p0.row,
        col: p0.col,
        coord: range,
      },
    };
  };
  function quoteFormulaString(text) {
    const escaped = text.replace(/'/g, "''").replace(/"/g, '""');
    if (text.indexOf("'") >= 0) {
      return "'" + escaped + "'";
    }
    return '"' + escaped + '"';
  }
  FormulaRefRoot.OffsetFormulaCoords = function (formula, coloffset, rowoffset, band) {
    const scf = SocialCalc.Formula;
    const tokentype = scf.TokenType;
    const token_op = tokentype.op;
    const token_string = tokentype.string;
    const token_coord = tokentype.coord;
    const tokenOpExpansion = scf.TokenOpExpansion;
    const parseinfo = scf.ParseFormulaIntoTokens(formula);
    let updatedformula = '';
    let sheetref = false;
    for (let i = 0; i < parseinfo.length; i++) {
      const ttype = parseinfo[i].type;
      const ttext = parseinfo[i].text;
      if (ttype === token_op) {
        if (ttext === '!') {
          sheetref = true;
        } else if (ttext !== ':') {
          sheetref = false;
        }
      }
      if (ttype === token_coord) {
        let newcr = '';
        const cr = FormulaRefRoot.coordToCr(ttext);
        const absCol = ttext.charAt(0) === '$';
        const absRow = ttext.indexOf('$', 1) !== -1;
        let shiftCol = !absCol;
        let shiftRow = !absRow;
        if (band && sheetref) {
          shiftCol = false;
          shiftRow = false;
        } else if (band) {
          if (
            band.startCol != null &&
            band.endCol != null &&
            (cr.col < band.startCol || cr.col > band.endCol)
          ) {
            shiftCol = false;
            shiftRow = false;
          }
          if (
            band.startRow != null &&
            band.endRow != null &&
            (cr.row < band.startRow || cr.row > band.endRow)
          ) {
            shiftCol = false;
            shiftRow = false;
          }
        }
        if (shiftCol) {
          cr.col += coloffset;
        }
        if (absCol) {
          newcr += '$';
        }
        newcr += FormulaRefRoot.rcColname(cr.col);
        if (absRow) {
          newcr += '$';
        }
        if (shiftRow) {
          cr.row += rowoffset;
        }
        newcr += cr.row;
        if (cr.row < 1 || cr.col < 1 || cr.col > 702) {
          newcr = '#REF!';
        }
        updatedformula += newcr;
      } else if (ttype === token_string) {
        updatedformula += quoteFormulaString(ttext);
      } else if (ttype === token_op) {
        updatedformula += tokenOpExpansion[ttext] || ttext;
      } else {
        updatedformula += ttext;
      }
    }
    return updatedformula;
  };
  FormulaRefRoot.AdjustFormulaCoords = function (formula, col, coloffset, row, rowoffset) {
    const scf = SocialCalc.Formula;
    const tokentype = scf.TokenType;
    const token_op = tokentype.op;
    const token_string = tokentype.string;
    const token_coord = tokentype.coord;
    const tokenOpExpansion = scf.TokenOpExpansion;
    const parseinfo = scf.ParseFormulaIntoTokens(formula);
    let updatedformula = '';
    let sheetref = false;
    for (let i = 0; i < parseinfo.length; i++) {
      let ttype = parseinfo[i].type;
      let ttext = parseinfo[i].text;
      if (ttype === token_op) {
        if (ttext === '!') {
          sheetref = true;
        } else if (ttext !== ':') {
          sheetref = false;
        }
        ttext = tokenOpExpansion[ttext] || ttext;
      }
      if (ttype === token_coord) {
        const cr = FormulaRefRoot.coordToCr(ttext);
        if (
          (coloffset < 0 && cr.col >= col && cr.col < col - coloffset) ||
          (rowoffset < 0 && cr.row >= row && cr.row < row - rowoffset)
        ) {
          if (!sheetref) {
            cr.col = 0;
            cr.row = 0;
          }
        }
        if (!sheetref) {
          if (cr.col >= col) {
            cr.col += coloffset;
          }
          if (cr.row >= row) {
            cr.row += rowoffset;
          }
        }
        let newcr;
        if (ttext.charAt(0) === '$') {
          newcr = '$' + FormulaRefRoot.rcColname(cr.col);
        } else {
          newcr = FormulaRefRoot.rcColname(cr.col);
        }
        if (ttext.indexOf('$', 1) !== -1) {
          newcr += '$' + cr.row;
        } else {
          newcr += cr.row;
        }
        if (cr.row < 1 || cr.col < 1 || cr.col > 702) {
          newcr = '#REF!';
        }
        ttext = newcr;
      } else if (ttype === token_string) {
        ttext = quoteFormulaString(ttext);
      }
      updatedformula += ttext;
    }
    return updatedformula;
  };
  FormulaRefRoot.ReplaceFormulaCoords = function (formula, movedto) {
    const scf = SocialCalc.Formula;
    const tokentype = scf.TokenType;
    const token_op = tokentype.op;
    const token_string = tokentype.string;
    const token_coord = tokentype.coord;
    const tokenOpExpansion = scf.TokenOpExpansion;
    const parseinfo = scf.ParseFormulaIntoTokens(formula);
    let updatedformula = '';
    let sheetref = false;
    for (let i = 0; i < parseinfo.length; i++) {
      let ttype = parseinfo[i].type;
      let ttext = parseinfo[i].text;
      if (ttype === token_op) {
        if (ttext === '!') {
          sheetref = true;
        } else if (ttext !== ':') {
          sheetref = false;
        }
        ttext = tokenOpExpansion[ttext] || ttext;
      }
      if (ttype === token_coord) {
        const cr0 = FormulaRefRoot.coordToCr(ttext);
        const coord = FormulaRefRoot.crToCoord(cr0.col, cr0.row);
        const moved = movedto[coord];
        if (moved && !sheetref) {
          const cr = FormulaRefRoot.coordToCr(moved);
          let newcr;
          if (ttext.charAt(0) === '$') {
            newcr = '$' + FormulaRefRoot.rcColname(cr.col);
          } else {
            newcr = FormulaRefRoot.rcColname(cr.col);
          }
          if (ttext.indexOf('$', 1) !== -1) {
            newcr += '$' + cr.row;
          } else {
            newcr += cr.row;
          }
          ttext = newcr;
        }
      } else if (ttype === token_string) {
        ttext = quoteFormulaString(ttext);
      }
      updatedformula += ttext;
    }
    return updatedformula;
  };

  // Opt this module into TypeScript strict checking via the r2scout config.
  // In-place TypeScript conversion of socialcalcpopup.js (SocialCalc global script).
  // Ambient API types live in socialcalcpopup.d.ts.
  // Typechecked global-script module (no @ts-nocheck).
  /*
// The module of the SocialCalc package for the optional popup menus in socialcalcspreadsheetcontrol.js
//
// (c) Copyright 2009 Socialtext, Inc.
// All Rights Reserved.
//
// The contents of this file are subject to the Artistic License 2.0; you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at http://socialcalc.org/licenses/al-20/.
//
// Some of the other files in the SocialCalc package are licensed under
// different licenses. Please note the licenses of the modules you use.
//
// Code History:
//
// Initially coded by Dan Bricklin of Software Garden, Inc., for Socialtext, Inc.
//
*/

  // Module-load guards removed — in the concatenated UMD bundle, SocialCalc
  // is always defined by the time this file runs.

  // Implementation-only mutable view for progressive Popup bag init.

  SocialCalc.Popup = {};
  const PopupMut = SocialCalc.Popup;
  PopupMut.Types = {};
  PopupMut.Controls = {};
  PopupMut.Current = {};
  SocialCalc.Popup.LocalizeString = function (str) {
    return str;
  };
  PopupMut.Create = function (type, id, attribs) {
    var pt = SocialCalc.Popup.Types[type];
    if (pt && pt.Create) {
      pt.Create(type, id, attribs);
    }
    PopupMut.imagePrefix = SocialCalc.Constants.defaultImagePrefix;
  };
  PopupMut.SetValue = function (id, value) {
    var sp = SocialCalc.Popup;
    var spt = sp.Types;
    var spc = sp.Controls;
    if (!spc[id]) {
      (function(){})('Unknown control ' + id);
      return;
    }
    var type = spc[id].type;
    var pt = spt[type];
    var spcdata = spc[id].data;
    if (pt && pt.Create) {
      pt.SetValue(type, id, value);
      if (spcdata.attribs && spcdata.attribs.changedcallback) {
        spcdata.attribs.changedcallback(spcdata.attribs, id, value);
      }
    }
  };
  PopupMut.SetDisabled = function (id, disabled) {
    var sp = SocialCalc.Popup;
    var spt = sp.Types;
    var spc = sp.Controls;
    if (!spc[id]) {
      (function(){})('Unknown control ' + id);
      return;
    }
    var type = spc[id].type;
    var pt = spt[type];
    if (pt && pt.Create) {
      if (sp.Current.id && id == sp.Current.id) {
        pt.Hide(type, sp.Current.id);
        sp.Current.id = null;
      }
      pt.SetDisabled(type, id, disabled);
    }
  };
  PopupMut.GetValue = function (id) {
    var sp = SocialCalc.Popup;
    var spt = sp.Types;
    var spc = sp.Controls;
    if (!spc[id]) {
      (function(){})('Unknown control ' + id);
      return;
    }
    var type = spc[id].type;
    var pt = spt[type];
    if (pt && pt.Create) {
      return pt.GetValue(type, id);
    }
    return null;
  };
  PopupMut.Initialize = function (id, data) {
    var sp = SocialCalc.Popup;
    var spt = sp.Types;
    var spc = sp.Controls;
    if (!spc[id]) {
      (function(){})('Unknown control ' + id);
      return;
    }
    var type = spc[id].type;
    var pt = spt[type];
    if (pt && pt.Initialize) {
      pt.Initialize(type, id, data);
    }
  };
  PopupMut.Reset = function (type) {
    var sp = SocialCalc.Popup;
    var spt = sp.Types;
    if (spt[type] && spt[type].Reset) spt[type].Reset(type);
  };
  PopupMut.CClick = function (id) {
    var sp = SocialCalc.Popup;
    var spt = sp.Types;
    var spc = sp.Controls;
    if (!spc[id]) {
      (function(){})('Unknown control ' + id);
      return;
    }
    if (spc[id].data && spc[id].data.disabled) return;
    var type = spc[id].type;
    var pt = spt[type];
    if (sp.Current.id) {
      spt[spc[sp.Current.id].type].Hide(type, sp.Current.id);
      if (id == sp.Current.id) {
        sp.Current.id = null;
        return;
      }
    }
    if (pt && pt.Show) {
      pt.Show(type, id);
    }
    sp.Current.id = id;
  };
  PopupMut.Close = function () {
    var sp = SocialCalc.Popup;
    if (!sp.Current.id) return;
    sp.CClick(sp.Current.id);
  };
  PopupMut.Cancel = function () {
    var sp = SocialCalc.Popup;
    var spt = sp.Types;
    var spc = sp.Controls;
    if (!sp.Current.id) return;
    var type = spc[sp.Current.id].type;
    var pt = spt[type];
    pt.Cancel(type, sp.Current.id);
    sp.Current.id = null;
  };
  PopupMut.CreatePopupDiv = function (id, attribs) {
    var pos;
    var sp = SocialCalc.Popup;
    var spc = sp.Controls;
    var spcdata = spc[id].data;
    var main = SocialCalc.document.createElement('div');
    main.style.position = 'absolute';
    pos = SocialCalc.GetElementPosition(spcdata.mainele);
    main.style.top = pos.top + spcdata.mainele.offsetHeight + 'px';
    main.style.left = pos.left + 'px';
    main.style.zIndex = String(100);
    main.style.backgroundColor = '#FFF';
    main.style.border = '1px solid black';
    if (attribs.width) {
      main.style.width = attribs.width;
    }
    spcdata.mainele.appendChild(main);
    if (attribs.title) {
      main.innerHTML =
        '<table cellspacing="0" cellpadding="0" style="border-bottom:1px solid black;"><tr>' +
        '<td style="font-size:10px;cursor:default;width:100%;background-color:#999;color:#FFF;">' +
        attribs.title +
        '</td>' +
        '<td style="font-size:10px;cursor:default;color:#666;" onclick="SocialCalc.Popup.Cancel();">&nbsp;X&nbsp;</td></tr></table>';
      if (attribs.moveable) {
        var tableEle = main.firstChild;
        var tbodyEle = tableEle.firstChild;
        var trEle = tbodyEle.firstChild;
        var tdEle = trEle.firstChild;
        spcdata.dragregistered = tdEle;
        SocialCalc.DragRegister(
          spcdata.dragregistered,
          true,
          true,
          {
            MouseDown: SocialCalc.DragFunctionStart,
            MouseMove: SocialCalc.DragFunctionPosition,
            MouseUp: SocialCalc.DragFunctionPosition,
            Disabled: null,
            positionobj: main,
          },
          spcdata.mainele,
        );
      }
    }
    return main;
  };
  PopupMut.EnsurePosition = function (id, container) {
    var sp = SocialCalc.Popup;
    var spc = sp.Controls;
    var spcdata = spc[id].data;
    var main = spcdata.mainele.firstChild;
    if (!main) {
      (function(){})('No main popup element firstChild.');
      return;
    }
    var popup = spcdata.popupele;
    function GetLayoutValues(ele) {
      var r = SocialCalc.GetElementPosition(ele);
      r.height = ele.offsetHeight;
      r.width = ele.offsetWidth;
      r.bottom = r.top + r.height;
      r.right = r.left + r.width;
      return r;
    }
    var p = GetLayoutValues(popup);
    var c = GetLayoutValues(container);
    var m = GetLayoutValues(main);
    if (m.bottom + p.height < c.bottom && m.left + p.width < c.right) {
      popup.style.top = m.bottom + 'px';
      popup.style.left = m.left + 'px';
    } else if (m.top - p.height > c.top && m.left + p.width < c.right) {
      popup.style.top = m.top - p.height + 'px';
      popup.style.left = m.left + 'px';
    } else if (m.bottom + p.height < c.bottom && m.right - p.width > c.left) {
      popup.style.top = m.bottom + 'px';
      popup.style.left = m.right - p.width + 'px';
    } else if (m.top - p.height > c.top && m.right - p.width > c.left) {
      popup.style.top = m.top - p.height + 'px';
      popup.style.left = m.right - p.width + 'px';
    } else if (m.bottom + p.height < c.bottom && p.width < c.width) {
      popup.style.top = m.bottom + 'px';
      popup.style.left = c.left + Math.floor((c.width - p.width) / 2) + 'px';
    } else if (m.top - p.height > c.top && p.width < c.width) {
      popup.style.top = m.top - p.height + 'px';
      popup.style.left = c.left + Math.floor((c.width - p.width) / 2) + 'px';
    } else if (p.height < c.height && m.right + p.width < c.right) {
      popup.style.top = c.top + Math.floor((c.height - p.height) / 2) + 'px';
      popup.style.left = m.right + 'px';
    } else if (p.height < c.height && m.left - p.width > c.left) {
      popup.style.top = c.top + Math.floor((c.height - p.height) / 2) + 'px';
      popup.style.left = m.left - p.width + 'px';
    } else {
    }
  };
  PopupMut.DestroyPopupDiv = function (ele, dragregistered) {
    if (!ele) return;
    ele.innerHTML = '';
    SocialCalc.DragUnregister(dragregistered);
    if (ele.parentNode) {
      ele.parentNode.removeChild(ele);
    }
  };
  function PopupClamp255(v) {
    if (!Number.isFinite(v) || v < 0) return 0;
    if (v > 255) return 255;
    return Math.trunc(v);
  }
  PopupMut.RGBToHex = function (val) {
    var sp = SocialCalc.Popup;
    if (val == '') {
      return '000000';
    }
    var rgbvals = val.match(/(-?\d+)\D+(-?\d+)\D+(-?\d+)/);
    if (rgbvals) {
      return (
        sp.ToHex(Number(rgbvals[1])) + sp.ToHex(Number(rgbvals[2])) + sp.ToHex(Number(rgbvals[3]))
      );
    } else {
      return '000000';
    }
  };
  PopupMut.HexDigits = '0123456789ABCDEF';
  PopupMut.ToHex = function (num) {
    var sp = SocialCalc.Popup;
    var v = PopupClamp255(num);
    var first = Math.floor(v / 16);
    var second = v % 16;
    return sp.HexDigits.charAt(first) + sp.HexDigits.charAt(second);
  };
  PopupMut.FromHex = function (str) {
    var sp = SocialCalc.Popup;
    var first = sp.HexDigits.indexOf(str.charAt(0).toUpperCase());
    var second = sp.HexDigits.indexOf(str.charAt(1).toUpperCase());
    return (first >= 0 ? first : 0) * 16 + (second >= 0 ? second : 0);
  };
  PopupMut.HexToRGB = function (val) {
    var sp = SocialCalc.Popup;
    return (
      'rgb(' +
      sp.FromHex(val.substring(1, 3)) +
      ',' +
      sp.FromHex(val.substring(3, 5)) +
      ',' +
      sp.FromHex(val.substring(5, 7)) +
      ')'
    );
  };
  PopupMut.makeRGB = function (r, g, b) {
    return 'rgb(' + PopupClamp255(r) + ',' + PopupClamp255(g) + ',' + PopupClamp255(b) + ')';
  };
  PopupMut.splitRGB = function (rgb) {
    var parts = rgb.match(/(-?\d+)\D+(-?\d+)\D+(-?\d+)\D/);
    if (!parts) {
      return {
        r: 0,
        g: 0,
        b: 0,
      };
    }
    return {
      r: PopupClamp255(Number(parts[1])),
      g: PopupClamp255(Number(parts[2])),
      b: PopupClamp255(Number(parts[3])),
    };
  };
  PopupMut.Types.List = {};
  SocialCalc.Popup.Types.List.Create = function (type, id, attribs) {
    var sp = SocialCalc.Popup;
    var spc = sp.Controls;
    var spcid = {
      type,
      value: '',
      display: '',
      data: {},
    };
    spc[id] = spcid;
    var spcdata = spcid.data;
    spcdata.attribs = attribs || {};
    var ele = document.getElementById(id);
    if (!ele) {
      (function(){})('Missing element ' + id);
      return;
    }
    spcdata.mainele = ele;
    ele.innerHTML =
      '<input style="cursor:pointer;width:100px;font-size:smaller;" onfocus="this.blur();" onclick="SocialCalc.Popup.CClick(\'' +
      id +
      '\');" value="">';
    spcdata.options = [];
  };
  SocialCalc.Popup.Types.List.SetValue = function (type, id, value) {
    var i, o;
    var sp = SocialCalc.Popup;
    var spc = sp.Controls;
    var spcdata = spc[id].data;
    spcdata.value = value;
    spcdata.custom = false;
    for (i = 0; i < spcdata.options.length; i++) {
      o = spcdata.options[i];
      if (o.a) {
        if (o.a.skip || o.a.custom || o.a.cancel) {
          continue;
        }
      }
      if (o.v == spcdata.value) {
        spcdata.display = o.o;
        break;
      }
    }
    if (i == spcdata.options.length) {
      spcdata.display = 'Custom';
      spcdata.custom = true;
    }
    if (spcdata.mainele && spcdata.mainele.firstChild) {
      spcdata.mainele.firstChild.value = spcdata.display;
    }
  };
  SocialCalc.Popup.Types.List.SetDisabled = function (type, id, disabled) {
    var sp = SocialCalc.Popup;
    var spc = sp.Controls;
    var spcdata = spc[id].data;
    spcdata.disabled = disabled;
    if (spcdata.mainele && spcdata.mainele.firstChild) {
      spcdata.mainele.firstChild.disabled = disabled;
    }
  };
  SocialCalc.Popup.Types.List.GetValue = function (type, id) {
    var sp = SocialCalc.Popup;
    var spc = sp.Controls;
    var spcdata = spc[id].data;
    return spcdata.value;
  };
  SocialCalc.Popup.Types.List.Initialize = function (type, id, data) {
    var a;
    var sp = SocialCalc.Popup;
    var spc = sp.Controls;
    var spcdata = spc[id].data;
    for (a in data.attribs) {
      spcdata.attribs[a] = data.attribs[a];
    }
    spcdata.options = data ? data.options : [];
    if (data.value) {
      sp.SetValue(id, data.value);
    }
  };
  SocialCalc.Popup.Types.List.Reset = function (type) {
    var sp = SocialCalc.Popup;
    var spt = sp.Types;
    var spc = sp.Controls;
    if (sp.Current.id && spc[sp.Current.id].type == type) {
      spt[type].Hide(type, sp.Current.id);
      sp.Current.id = null;
    }
  };
  SocialCalc.Popup.Types.List.Show = function (type, id) {
    var ele;
    var sp = SocialCalc.Popup;
    var spc = sp.Controls;
    var spcdata = spc[id].data;
    var str = '';
    spcdata.popupele = sp.CreatePopupDiv(id, spcdata.attribs);
    if (spcdata.custom) {
      str = SocialCalc.Popup.Types.List.MakeCustom(type, id);
      ele = SocialCalc.document.createElement('div');
      ele.innerHTML =
        '<div style="cursor:default;padding:4px;background-color:#CCC;">' + str + '</div>';
      var outerDiv = ele.firstChild;
      var innerDiv = outerDiv.firstChild;
      spcdata.customele = innerDiv.childNodes[1];
      spcdata.listdiv = null;
      spcdata.contentele = ele;
    } else {
      str = SocialCalc.Popup.Types.List.MakeList(type, id);
      ele = SocialCalc.document.createElement('div');
      ele.innerHTML = '<div style="cursor:default;padding:4px;">' + str + '</div>';
      spcdata.customele = null;
      spcdata.listdiv = ele.firstChild;
      spcdata.contentele = ele;
    }
    if (spcdata.mainele && spcdata.mainele.firstChild) {
      spcdata.mainele.firstChild.disabled = true;
    }
    spcdata.popupele.appendChild(ele);
    if (spcdata.attribs.ensureWithin) {
      SocialCalc.Popup.EnsurePosition(id, spcdata.attribs.ensureWithin);
    }
  };
  SocialCalc.Popup.Types.List.MakeList = function (type, id) {
    var i, o, bg;
    var sp = SocialCalc.Popup;
    var spc = sp.Controls;
    var spcdata = spc[id].data;
    var str = '<table cellspacing="0" cellpadding="0"><tr>';
    var td = '<td style="vertical-align:top;">';
    str += td;
    spcdata.ncols = 1;
    for (i = 0; i < spcdata.options.length; i++) {
      o = spcdata.options[i];
      if (o.a) {
        if (o.a.newcol) {
          str += '</td>' + td + '&nbsp;&nbsp;&nbsp;&nbsp;' + '</td>' + td;
          spcdata.ncols += 1;
          continue;
        }
        if (o.a.skip) {
          str += '<div style="font-size:x-small;white-space:nowrap;">' + o.o + '</div>';
          continue;
        }
      }
      if (o.v == spcdata.value && !(o.a && (o.a.custom || o.a.cancel))) {
        bg = 'background-color:#DDF;';
      } else {
        bg = '';
      }
      str +=
        '<div style="font-size:x-small;white-space:nowrap;' +
        bg +
        '" onclick="SocialCalc.Popup.Types.List.ItemClicked(\'' +
        id +
        "','" +
        i +
        '\');" onmousemove="SocialCalc.Popup.Types.List.MouseMove(\'' +
        id +
        '\',this);">' +
        o.o +
        '</div>';
    }
    str += '</td></tr></table>';
    return str;
  };
  SocialCalc.Popup.Types.List.MakeCustom = function (type, id) {
    var SPLoc = SocialCalc.Popup.LocalizeString;
    var sp = SocialCalc.Popup;
    var spc = sp.Controls;
    var spcdata = spc[id].data;
    var style = 'style="font-size:smaller;"';
    var str = '';
    var val = spcdata.value;
    val = SocialCalc.special_chars(val);
    str =
      '<div style="white-space:nowrap;"><br>' +
      '<input id="customvalue" value="' +
      val +
      '"><br><br>' +
      '<input ' +
      style +
      ' type="button" value="' +
      SPLoc('OK') +
      '" onclick="SocialCalc.Popup.Types.List.CustomOK(\'' +
      id +
      '\');return false;">' +
      '<input ' +
      style +
      ' type="button" value="' +
      SPLoc('List') +
      '" onclick="SocialCalc.Popup.Types.List.CustomToList(\'' +
      id +
      '\');">' +
      '<input ' +
      style +
      ' type="button" value="' +
      SPLoc('Cancel') +
      '" onclick="SocialCalc.Popup.Close();">' +
      '<br></div>';
    return str;
  };
  SocialCalc.Popup.Types.List.ItemClicked = function (id, num) {
    var oele, str, nele;
    var sp = SocialCalc.Popup;
    var spc = sp.Controls;
    var spcdata = spc[id].data;
    var a = spcdata.options[num].a;
    if (a && a.custom) {
      oele = spcdata.contentele;
      str = SocialCalc.Popup.Types.List.MakeCustom('List', id);
      nele = SocialCalc.document.createElement('div');
      nele.innerHTML =
        '<div style="cursor:default;padding:4px;background-color:#CCC;">' + str + '</div>';
      var outerDivIC = nele.firstChild;
      var innerDivIC = outerDivIC.firstChild;
      spcdata.customele = innerDivIC.childNodes[1];
      spcdata.listdiv = null;
      spcdata.contentele = nele;
      spcdata.popupele.replaceChild(nele, oele);
      if (spcdata.attribs.ensureWithin) {
        SocialCalc.Popup.EnsurePosition(id, spcdata.attribs.ensureWithin);
      }
      return;
    }
    if (a && a.cancel) {
      SocialCalc.Popup.Close();
      return;
    }
    SocialCalc.Popup.SetValue(id, spcdata.options[num].v);
    SocialCalc.Popup.Close();
  };
  SocialCalc.Popup.Types.List.CustomToList = function (id) {
    var oele, str, nele;
    var sp = SocialCalc.Popup;
    var spc = sp.Controls;
    var spcdata = spc[id].data;
    oele = spcdata.contentele;
    str = SocialCalc.Popup.Types.List.MakeList('List', id);
    nele = SocialCalc.document.createElement('div');
    nele.innerHTML = '<div style="cursor:default;padding:4px;">' + str + '</div>';
    spcdata.customele = null;
    spcdata.listdiv = nele.firstChild;
    spcdata.contentele = nele;
    spcdata.popupele.replaceChild(nele, oele);
    if (spcdata.attribs.ensureWithin) {
      SocialCalc.Popup.EnsurePosition(id, spcdata.attribs.ensureWithin);
    }
  };
  SocialCalc.Popup.Types.List.CustomOK = function (id) {
    var sp = SocialCalc.Popup;
    var spc = sp.Controls;
    var spcdata = spc[id].data;
    SocialCalc.Popup.SetValue(id, spcdata.customele.value);
    SocialCalc.Popup.Close();
  };
  SocialCalc.Popup.Types.List.MouseMove = function (id, ele) {
    var col, i;
    var sp = SocialCalc.Popup;
    var spc = sp.Controls;
    var spcdata = spc[id].data;
    var list = spcdata.listdiv;
    if (!list) return;
    var rowele = list.firstChild.firstChild.firstChild;
    for (col = 0; col < spcdata.ncols; col++) {
      for (i = 0; i < rowele.childNodes[col * 2].childNodes.length; i++) {
        rowele.childNodes[col * 2].childNodes[i].style.backgroundColor = '#FFF';
      }
    }
    ele.style.backgroundColor = '#DDF';
  };
  SocialCalc.Popup.Types.List.Hide = function (type, id) {
    var sp = SocialCalc.Popup;
    var spc = sp.Controls;
    var spcdata = spc[id].data;
    sp.DestroyPopupDiv(spcdata.popupele, spcdata.dragregistered);
    spcdata.popupele = null;
    if (spcdata.mainele && spcdata.mainele.firstChild) {
      spcdata.mainele.firstChild.disabled = false;
    }
  };
  SocialCalc.Popup.Types.List.Cancel = function (type, id) {
    SocialCalc.Popup.Types.List.Hide(type, id);
  };
  PopupMut.Types.ColorChooser = {};
  SocialCalc.Popup.Types.ColorChooser.Create = function (type, id, attribs) {
    var sp = SocialCalc.Popup;
    var spc = sp.Controls;
    var spcid = {
      type,
      value: '',
      display: '',
      data: {},
    };
    spc[id] = spcid;
    var spcdata = spcid.data;
    spcdata.attribs = attribs || {};
    var spca = spcdata.attribs;
    spcdata.value = '';
    var ele = document.getElementById(id);
    if (!ele) {
      (function(){})('Missing element ' + id);
      return;
    }
    spcdata.mainele = ele;
    ele.innerHTML =
      '<div style="cursor:pointer;border:1px solid black;vertical-align:top;width:' +
      (spca.sampleWidth || '15px') +
      ';height:' +
      (spca.sampleHeight || '15px') +
      ';" onclick="SocialCalc.Popup.Types.ColorChooser.ControlClicked(\'' +
      id +
      '\');">&nbsp;</div>';
  };
  SocialCalc.Popup.Types.ColorChooser.SetValue = function (type, id, value) {
    var img, pos;
    var sp = SocialCalc.Popup;
    var spc = sp.Controls;
    var spcdata = spc[id].data;
    var spca = spcdata.attribs;
    spcdata.value = value;
    spcdata.custom = false;
    if (spcdata.mainele && spcdata.mainele.firstChild) {
      if (spcdata.value) {
        spcdata.mainele.firstChild.style.backgroundColor = spcdata.value;
        if (spca.backgroundImage) {
          img = 'url(' + sp.imagePrefix + spca.backgroundImage + ')';
        } else {
          img = '';
        }
        pos = 'center center';
      } else {
        spcdata.mainele.firstChild.style.backgroundColor = '#FFF';
        if (spca.backgroundImageDefault) {
          img = 'url(' + sp.imagePrefix + spca.backgroundImageDefault + ')';
          pos = 'center center';
        } else {
          img = 'url(' + sp.imagePrefix + 'defaultcolor.gif)';
          pos = 'left top';
        }
      }
      spcdata.mainele.firstChild.style.backgroundPosition = pos;
      spcdata.mainele.firstChild.style.backgroundImage = img;
    }
  };
  SocialCalc.Popup.Types.ColorChooser.SetDisabled = function (type, id, disabled) {
    var img, pos;
    var sp = SocialCalc.Popup;
    var spc = sp.Controls;
    var spcdata = spc[id].data;
    var spca = spcdata.attribs;
    spcdata.disabled = disabled;
    if (spcdata.mainele && spcdata.mainele.firstChild) {
      if (disabled) {
        spcdata.mainele.firstChild.style.backgroundColor = '#DDD';
        if (spca.backgroundImageDisabled) {
          img = 'url(' + sp.imagePrefix + spca.backgroundImageDisabled + ')';
          pos = 'center center';
        } else {
          img = 'url(' + sp.imagePrefix + 'defaultcolor.gif)';
          pos = 'left top';
        }
        spcdata.mainele.firstChild.style.backgroundPosition = pos;
        spcdata.mainele.firstChild.style.backgroundImage = img;
      } else {
        sp.SetValue(id, spcdata.value);
      }
    }
  };
  SocialCalc.Popup.Types.ColorChooser.GetValue = function (type, id) {
    var sp = SocialCalc.Popup;
    var spc = sp.Controls;
    var spcdata = spc[id].data;
    return spcdata.value;
  };
  SocialCalc.Popup.Types.ColorChooser.Initialize = function (type, id, data) {
    var a;
    var sp = SocialCalc.Popup;
    var spc = sp.Controls;
    var spcdata = spc[id].data;
    for (a in data.attribs) {
      spcdata.attribs[a] = data.attribs[a];
    }
    if (data.value) {
      sp.SetValue(id, data.value);
    }
  };
  SocialCalc.Popup.Types.ColorChooser.Reset = function (type) {
    var sp = SocialCalc.Popup;
    var spt = sp.Types;
    var spc = sp.Controls;
    if (sp.Current.id && spc[sp.Current.id].type == type) {
      spt[type].Hide(type, sp.Current.id);
      sp.Current.id = null;
    }
  };
  SocialCalc.Popup.Types.ColorChooser.Show = function (type, id) {
    var ele, mainele;
    var sp = SocialCalc.Popup;
    var spc = sp.Controls;
    var spcdata = spc[id].data;
    var str = '';
    spcdata.oldvalue = spcdata.value;
    spcdata.popupele = sp.CreatePopupDiv(id, spcdata.attribs);
    if (spcdata.custom) {
      str = SocialCalc.Popup.Types.ColorChooser.MakeCustom(type, id);
      ele = SocialCalc.document.createElement('div');
      ele.innerHTML =
        '<div style="cursor:default;padding:4px;background-color:#CCC;">' + str + '</div>';
      var ccOuter = ele.firstChild;
      var ccInner = ccOuter.firstChild;
      spcdata.customele = ccInner.childNodes[2];
      spcdata.contentele = ele;
    } else {
      mainele = SocialCalc.Popup.Types.ColorChooser.CreateGrid(type, id);
      ele = SocialCalc.document.createElement('div');
      ele.style.padding = '3px';
      ele.style.backgroundColor = '#CCC';
      ele.appendChild(mainele);
      spcdata.customele = null;
      spcdata.contentele = ele;
    }
    spcdata.popupele.appendChild(ele);
    if (spcdata.attribs.ensureWithin) {
      SocialCalc.Popup.EnsurePosition(id, spcdata.attribs.ensureWithin);
    }
  };
  SocialCalc.Popup.Types.ColorChooser.MakeCustom = function (type, id) {
    var sp = SocialCalc.Popup;
    var spc = sp.Controls;
    var spcdata = spc[id].data;
    var SPLoc = sp.LocalizeString;
    var style = 'style="font-size:smaller;"';
    var str = '';
    str =
      '<div style="white-space:nowrap;"><br>' +
      '#<input id="customvalue" style="width:75px;" value="' +
      spcdata.value +
      '"><br><br>' +
      '<input ' +
      style +
      ' type="button" value="' +
      SPLoc('OK') +
      '" onclick="SocialCalc.Popup.Types.ColorChooser.CustomOK(\'' +
      id +
      '\');return false;">' +
      '<input ' +
      style +
      ' type="button" value="' +
      SPLoc('Grid') +
      '" onclick="SocialCalc.Popup.Types.ColorChooser.CustomToGrid(\'' +
      id +
      '\');">' +
      '<br></div>';
    return str;
  };
  SocialCalc.Popup.Types.ColorChooser.ItemClicked = function (_id, _num) {
    SocialCalc.Popup.Close();
  };
  SocialCalc.Popup.Types.ColorChooser.CustomToList = function (_id) {};
  SocialCalc.Popup.Types.ColorChooser.Hide = function (type, id) {
    var sp = SocialCalc.Popup;
    var spc = sp.Controls;
    var spcdata = spc[id].data;
    sp.DestroyPopupDiv(spcdata.popupele, spcdata.dragregistered);
    spcdata.popupele = null;
  };
  SocialCalc.Popup.Types.ColorChooser.Cancel = function (type, id) {
    var sp = SocialCalc.Popup;
    var spc = sp.Controls;
    var spcdata = spc[id].data;
    sp.SetValue(id, spcdata.oldvalue);
    SocialCalc.Popup.Types.ColorChooser.Hide(type, id);
  };
  SocialCalc.Popup.Types.ColorChooser.CreateGrid = function (type, id) {
    var ele, row, rowele, col, g;
    var sp = SocialCalc.Popup;
    var spt = sp.Types;
    var spc = sp.Controls;
    var SPLoc = sp.LocalizeString;
    var spcdata = spc[id].data;
    spcdata.grid = {};
    var grid = spcdata.grid;
    var mainele = SocialCalc.document.createElement('div');
    var tele = SocialCalc.document.createElement('table');
    tele.cellSpacing = '0';
    tele.cellPadding = '0';
    tele.style.width = '100px';
    grid.table = tele;
    ele = SocialCalc.document.createElement('tbody');
    grid.table.appendChild(ele);
    grid.tbody = ele;
    for (row = 0; row < 16; row++) {
      rowele = SocialCalc.document.createElement('tr');
      for (col = 0; col < 5; col++) {
        var gEntry = {};
        g = gEntry;
        grid[row + ',' + col] = g;
        ele = SocialCalc.document.createElement('td');
        ele.style.fontSize = '1px';
        ele.innerHTML = '&nbsp;';
        ele.style.height = '10px';
        if (col <= 1) {
          ele.style.width = '17px';
          ele.style.borderRight = '3px solid white';
        } else {
          ele.style.width = '20px';
          ele.style.backgroundRepeat = 'no-repeat';
        }
        rowele.appendChild(ele);
        g.ele = ele;
      }
      grid.tbody.appendChild(rowele);
    }
    mainele.appendChild(grid.table);
    ele = SocialCalc.document.createElement('div');
    ele.style.marginTop = '3px';
    ele.innerHTML =
      '<table cellspacing="0" cellpadding="0"><tr>' +
      '<td style="width:17px;background-color:#FFF;background-image:url(' +
      sp.imagePrefix +
      'defaultcolor.gif);height:16px;font-size:10px;cursor:pointer;" title="' +
      SPLoc('Default') +
      '">&nbsp;</td>' +
      '<td style="width:23px;height:16px;font-size:10px;text-align:center;cursor:pointer;" title="' +
      SPLoc('Custom') +
      '">#</td>' +
      '<td style="width:60px;height:16px;font-size:10px;text-align:center;cursor:pointer;">' +
      SPLoc('OK') +
      '</td>' +
      '</tr></table>';
    var cgTable = ele.firstChild;
    var cgTbody = cgTable.firstChild;
    var cgTr = cgTbody.firstChild;
    grid.defaultbox = cgTr.childNodes[0];
    grid.defaultbox.onclick = (e) => spt.ColorChooser.DefaultClicked(e);
    grid.custom = cgTr.childNodes[1];
    grid.custom.onclick = (e) => spt.ColorChooser.CustomClicked(e);
    grid.msg = cgTr.childNodes[2];
    grid.msg.onclick = (e) => spt.ColorChooser.CloseOK(e);
    mainele.appendChild(ele);
    grid.table.onmousedown = (e) => spt.ColorChooser.GridMouseDown(e);
    spt.ColorChooser.DetermineColors(id);
    spt.ColorChooser.SetColors(id);
    return mainele;
  };
  SocialCalc.Popup.Types.ColorChooser.gridToG = function (grid, row, col) {
    return grid[row + ',' + col];
  };
  SocialCalc.Popup.Types.ColorChooser.DetermineColors = function (id) {
    var sp = SocialCalc.Popup;
    var spt = sp.Types;
    var sptc = spt.ColorChooser;
    var spc = sp.Controls;
    var spcdata = spc[id].data;
    var grid = spcdata.grid;
    var col, row;
    var rgb = sp.splitRGB(spcdata.value);
    col = 2;
    row = 16 - Math.floor((rgb.r + 16) / 16);
    grid['selectedrow' + col] = row;
    for (row = 0; row < 16; row++) {
      sptc.gridToG(grid, row, col).rgb = sp.makeRGB(17 * (15 - row), 0, 0);
    }
    col = 3;
    row = 16 - Math.floor((rgb.g + 16) / 16);
    grid['selectedrow' + col] = row;
    for (row = 0; row < 16; row++) {
      sptc.gridToG(grid, row, col).rgb = sp.makeRGB(0, 17 * (15 - row), 0);
    }
    col = 4;
    row = 16 - Math.floor((rgb.b + 16) / 16);
    grid['selectedrow' + col] = row;
    for (row = 0; row < 16; row++) {
      sptc.gridToG(grid, row, col).rgb = sp.makeRGB(0, 0, 17 * (15 - row));
    }
    col = 1;
    for (row = 0; row < 16; row++) {
      sptc.gridToG(grid, row, col).rgb = sp.makeRGB(
        17 * (15 - row),
        17 * (15 - row),
        17 * (15 - row),
      );
    }
    col = 0;
    var steps = [0, 68, 153, 204, 255];
    var commonrgb = [
      '400',
      '310',
      '420',
      '440',
      '442',
      '340',
      '040',
      '042',
      '032',
      '044',
      '024',
      '004',
      '204',
      '314',
      '402',
      '414',
    ];
    var x;
    for (row = 0; row < 16; row++) {
      x = commonrgb[row];
      sptc.gridToG(grid, row, col).rgb =
        'rgb(' +
        steps[Number(x.charAt(0))] +
        ',' +
        steps[Number(x.charAt(1))] +
        ',' +
        steps[Number(x.charAt(2))] +
        ')';
    }
  };
  SocialCalc.Popup.Types.ColorChooser.SetColors = function (id) {
    var row, col, g, rgb;
    var sp = SocialCalc.Popup;
    var spt = sp.Types;
    var sptc = spt.ColorChooser;
    var spc = sp.Controls;
    var spcdata = spc[id].data;
    var grid = spcdata.grid;
    for (row = 0; row < 16; row++) {
      for (col = 0; col < 5; col++) {
        g = sptc.gridToG(grid, row, col);
        g.ele.style.backgroundColor = g.rgb;
        g.ele.title = sp.RGBToHex(g.rgb);
        if (grid['selectedrow' + col] == row) {
          g.ele.style.backgroundImage = 'url(' + sp.imagePrefix + 'chooserarrow.gif)';
        } else {
          g.ele.style.backgroundImage = '';
        }
      }
    }
    sp.SetValue(id, spcdata.value);
    grid.msg.style.backgroundColor = spcdata.value;
    rgb = sp.splitRGB(spcdata.value || 'rgb(255,255,255)');
    if (rgb.r + rgb.g + rgb.b < 220) {
      grid.msg.style.color = '#FFF';
    } else {
      grid.msg.style.color = '#000';
    }
    if (!spcdata.value) {
      grid.msg.style.backgroundColor = '#FFF';
      grid.msg.style.backgroundImage = 'url(' + sp.imagePrefix + 'defaultcolor.gif)';
      grid.msg.title = 'Default';
    } else {
      grid.msg.style.backgroundImage = '';
      grid.msg.title = sp.RGBToHex(spcdata.value);
    }
  };
  SocialCalc.Popup.Types.ColorChooser.GridMouseDown = function (e) {
    var event = e || window.event;
    var sp = SocialCalc.Popup;
    var spt = sp.Types;
    var sptc = spt.ColorChooser;
    var spc = sp.Controls;
    var id = sp.Current.id;
    if (!id) return;
    var spcdata = spc[id].data;
    var grid = spcdata.grid;
    switch (event.type) {
      case 'mousedown':
        grid.mousedown = true;
        break;
      case 'mouseup':
        grid.mousedown = false;
        break;
      case 'mousemove':
        if (!grid.mousedown) {
          return;
        }
        break;
    }
    var pos = SocialCalc.GetElementPositionWithScroll(spcdata.mainele);
    var clientX = event.clientX - pos.left;
    var clientY = event.clientY - pos.top;
    var gpos = SocialCalc.GetElementPositionWithScroll(grid.table);
    gpos.left -= pos.left;
    gpos.top -= pos.top;
    var row = Math.floor((clientY - gpos.top - 2) / 10);
    row = row < 0 ? 0 : row;
    var col = Math.floor((clientX - gpos.left) / 20);
    row = row > 15 ? 15 : row;
    col = col < 0 ? 0 : col > 4 ? 4 : col;
    var color = sptc.gridToG(grid, row, col).ele.style.backgroundColor;
    var newrgb = sp.splitRGB(color);
    var oldrgb = sp.splitRGB(spcdata.value);
    switch (col) {
      case 2:
        spcdata.value = sp.makeRGB(newrgb.r, oldrgb.g, oldrgb.b);
        break;
      case 3:
        spcdata.value = sp.makeRGB(oldrgb.r, newrgb.g, oldrgb.b);
        break;
      case 4:
        spcdata.value = sp.makeRGB(oldrgb.r, oldrgb.g, newrgb.b);
        break;
      case 0:
      case 1:
        spcdata.value = color;
    }
    sptc.DetermineColors(id);
    sptc.SetColors(id);
  };
  SocialCalc.Popup.Types.ColorChooser.ControlClicked = function (id) {
    var sp = SocialCalc.Popup;
    var spt = sp.Types;
    var sptc = spt.ColorChooser;
    var cid = sp.Current.id;
    if (!cid || id != cid) {
      sp.CClick(id);
      return;
    }
    sptc.CloseOK();
  };
  SocialCalc.Popup.Types.ColorChooser.DefaultClicked = function (_e) {
    var sp = SocialCalc.Popup;
    var spc = sp.Controls;
    var id = sp.Current.id;
    if (!id) return;
    var spcdata = spc[id].data;
    spcdata.value = '';
    SocialCalc.Popup.SetValue(id, spcdata.value);
    SocialCalc.Popup.Close();
  };
  SocialCalc.Popup.Types.ColorChooser.CustomClicked = function (_e) {
    var sp = SocialCalc.Popup;
    var spc = sp.Controls;
    var id = sp.Current.id;
    if (!id) return;
    var spcdata = spc[id].data;
    var oele, str, nele;
    oele = spcdata.contentele;
    str = SocialCalc.Popup.Types.ColorChooser.MakeCustom('ColorChooser', id);
    nele = SocialCalc.document.createElement('div');
    nele.innerHTML =
      '<div style="cursor:default;padding:4px;background-color:#CCC;">' + str + '</div>';
    var ccOuter2 = nele.firstChild;
    var ccInner2 = ccOuter2.firstChild;
    spcdata.customele = ccInner2.childNodes[2];
    spcdata.contentele = nele;
    spcdata.popupele.replaceChild(nele, oele);
    spcdata.customele.value = sp.RGBToHex(spcdata.value);
    if (spcdata.attribs.ensureWithin) {
      SocialCalc.Popup.EnsurePosition(id, spcdata.attribs.ensureWithin);
    }
  };
  SocialCalc.Popup.Types.ColorChooser.CustomToGrid = function (id) {
    var oele, nele;
    var sp = SocialCalc.Popup;
    var spc = sp.Controls;
    var spcdata = spc[id].data;
    SocialCalc.Popup.SetValue(id, sp.HexToRGB('#' + spcdata.customele.value));
    var oele, mainele, nele;
    oele = spcdata.contentele;
    mainele = SocialCalc.Popup.Types.ColorChooser.CreateGrid('ColorChooser', id);
    nele = SocialCalc.document.createElement('div');
    nele.style.padding = '3px';
    nele.style.backgroundColor = '#CCC';
    nele.appendChild(mainele);
    spcdata.customele = null;
    spcdata.contentele = nele;
    spcdata.popupele.replaceChild(nele, oele);
    if (spcdata.attribs.ensureWithin) {
      SocialCalc.Popup.EnsurePosition(id, spcdata.attribs.ensureWithin);
    }
  };
  SocialCalc.Popup.Types.ColorChooser.CustomOK = function (id) {
    var sp = SocialCalc.Popup;
    var spc = sp.Controls;
    var spcdata = spc[id].data;
    SocialCalc.Popup.SetValue(id, sp.HexToRGB('#' + spcdata.customele.value));
    SocialCalc.Popup.Close();
  };
  SocialCalc.Popup.Types.ColorChooser.CloseOK = function (_e) {
    var sp = SocialCalc.Popup;
    var spc = sp.Controls;
    var id = sp.Current.id;
    if (!id) return;
    var spcdata = spc[id].data;
    SocialCalc.Popup.SetValue(id, spcdata.value);
    SocialCalc.Popup.Close();
  };

  // In-place TypeScript conversion of socialcalcspreadsheetcontrol.js (SocialCalc global script).
  // Ambient API types live in socialcalcspreadsheetcontrol.d.ts (referenced by dist/SocialCalc.d.ts).
  // Vite+ strips types with Oxc before UMD concat — no runtime tax.
  // Typechecked core after removing @ts-nocheck.

  // Opt-in to TypeScript strict checking (noImplicitAny, strictNullChecks) via r2scout config.
  //
  // SocialCalcSpreadsheetControl
  //
  /*
// The code module of the SocialCalc package that lets you embed a spreadsheet
// control with toolbar, etc., into a web page.
//
// (c) Copyright 2008, 2009, 2010 Socialtext, Inc.
// All Rights Reserved.
//


/*

LEGAL NOTICES REQUIRED BY THE COMMON PUBLIC ATTRIBUTION LICENSE:

EXHIBIT A. Common Public Attribution License Version 1.0.

The contents of this file are subject to the Common Public Attribution License Version 1.0 (the
"License"); you may not use this file except in compliance with the License. You may obtain a copy
of the License at http://socialcalc.org. The License is based on the Mozilla Public License Version 1.1 but
Sections 14 and 15 have been added to cover use of software over a computer network and provide for
limited attribution for the Original Developer. In addition, Exhibit A has been modified to be
consistent with Exhibit B.

Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
KIND, either express or implied. See the License for the specific language governing rights and
limitations under the License.

The Original Code is SocialCalc JavaScript SpreadsheetControl.

The Original Developer is the Initial Developer.

The Initial Developer of the Original Code is Socialtext, Inc. All portions of the code written by
Socialtext, Inc., are Copyright (c) Socialtext, Inc. All Rights Reserved.

Contributor: Dan Bricklin.


EXHIBIT B. Attribution Information

When the SpreadsheetControl is producing and/or controlling the display the Graphic Image must be
displayed on the screen visible to the user in a manner comparable to that in the
Original Code. The Attribution Phrase must be displayed as a "tooltip" or "hover-text" for
that image. The image must be linked to the Attribution URL so as to access that page
when clicked. If the user interface includes a prominent "about" display which includes
factual prominent attribution in a form similar to that in the "about" display included
with the Original Code, including Socialtext copyright notices and URLs, then the image
need not be linked to the Attribution URL but the "tool-tip" is still required.

Attribution Copyright Notice:

 Copyright (C) 2010 Socialtext, Inc.
 All Rights Reserved.

Attribution Phrase (not exceeding 10 words): SocialCalc

Attribution URL: http://www.socialcalc.org/

Graphic Image: The contents of the sc-logo.gif file in the Original Code or
a suitable replacement from http://www.socialcalc.org/licenses specified as
being for SocialCalc.

Display of Attribution Information is required in Larger Works which are defined
in the CPAL as a work which combines Covered Code or portions thereof with code
not governed by the terms of the CPAL.

*/

  // Mutable progressive-init bridges for ambient nested namespaces.

  SocialCalc.OtherSaveParts = {};
  const SpreadsheetControlSC = SocialCalc;
  SpreadsheetControlSC.CurrentSpreadsheetControlObject = null;
  SpreadsheetControlSC.SpreadsheetControl = function (idPrefix) {
    var scc = SocialCalc.Constants;
    this.parentNode = null;
    this.spreadsheetDiv = null;
    this.requestedHeight = 0;
    this.requestedWidth = 0;
    this.requestedSpaceBelow = 0;
    this.height = 0;
    this.width = 0;
    this.viewheight = 0;
    this.tabs = [];
    this.tabnums = {};
    this.tabreplacements = {};
    this.currentTab = -1;
    this.views = {};
    this.spreadsheetDiv = null;
    this.editorDiv = null;
    this.sortrange = '';
    this.moverange = '';
    this.idPrefix = idPrefix || 'SocialCalc-';
    this.multipartBoundary = 'SocialCalcSpreadsheetControlSave';
    this.imagePrefix = scc.defaultImagePrefix;
    this.toolbarbackground = scc.SCToolbarbackground;
    this.tabbackground = scc.SCTabbackground;
    this.tabselectedCSS = scc.SCTabselectedCSS;
    this.tabplainCSS = scc.SCTabplainCSS;
    this.toolbartext = scc.SCToolbartext;
    this.formulabarheight = scc.SCFormulabarheight;
    this.statuslineheight = scc.SCStatuslineheight;
    this.statuslineCSS = scc.SCStatuslineCSS;
    this.ExportCallback = null;
    if (typeof SocialCalc.debug_log === 'undefined') SocialCalc.debug_log = [];
    this.sheet = new SocialCalc.Sheet();
    this.context = new SocialCalc.RenderContext(this.sheet);
    this.context.showGrid = true;
    this.context.showRCHeaders = true;
    this.editor = new SocialCalc.TableEditor(this.context);
    this.editor.StatusCallback.statusline = {
      func: SocialCalc.SpreadsheetControlStatuslineCallback,
      params: {
        statuslineid: this.idPrefix + 'statusline',
        recalcid1: this.idPrefix + 'divider_recalc',
        recalcid2: this.idPrefix + 'button_recalc',
      },
    };
    SpreadsheetControlSC.CurrentSpreadsheetControlObject = this;
    this.editor.MoveECellCallback.movefrom = function (editor) {
      var spreadsheet = SocialCalc.GetSpreadsheetControlObject();
      spreadsheet.context.cursorsuffix = '';
      if (editor.range2.hasrange && !editor.cellhandles.noCursorSuffix) {
        if (
          editor.ecell.row == editor.range2.top &&
          (editor.ecell.col < editor.range2.left || editor.ecell.col > editor.range2.right + 1)
        ) {
          spreadsheet.context.cursorsuffix = 'insertleft';
        }
        if (
          editor.ecell.col == editor.range2.left &&
          (editor.ecell.row < editor.range2.top || editor.ecell.row > editor.range2.bottom + 1)
        ) {
          spreadsheet.context.cursorsuffix = 'insertup';
        }
      }
    };
    this.formulabuttons = {
      formulafunctions: {
        image: 'insertformula.png',
        tooltip: 'Functions',
        command: SpreadsheetControlSC.SpreadsheetControl.DoFunctionList,
      },
      multilineinput: {
        image: 'listbox.png',
        tooltip: 'Multi-line Input Box',
        command: SpreadsheetControlSC.SpreadsheetControl.DoMultiline,
      },
      link: {
        image: 'inserthyperlink.png',
        tooltip: 'Link Input Box',
        command: SpreadsheetControlSC.SpreadsheetControl.DoLink,
      },
      sum: {
        image: 'autosum.png',
        tooltip: 'Auto Sum',
        command: SpreadsheetControlSC.SpreadsheetControl.DoSum,
      },
    };
    this.findbuttons = {
      last: {
        image: 'upsearch.png',
        tooltip: 'Find Previous',
        command: SpreadsheetControlSC.SpreadsheetControl.SearchUp,
      },
      next: {
        image: 'downsearch.png',
        tooltip: 'Find Next',
        command: SpreadsheetControlSC.SpreadsheetControl.SearchDown,
      },
    };
    this.tabnums.edit = this.tabs.length;
    this.tabs.push({
      name: 'edit',
      text: 'Edit',
      html:
        ' <div id="%id.edittools" style="padding:10px 0px 0px 0px;">' +
        '&nbsp;<img id="%id.button_undo" src="%img.undo.png" style="vertical-align:bottom;">' +
        ' <img id="%id.button_redo" src="%img.redo.png" style="vertical-align:bottom;">' +
        ' &nbsp;<img src="%img.divider1.png" style="vertical-align:bottom;">&nbsp; ' +
        '<img id="%id.button_copy" src="%img.copy.png" style="vertical-align:bottom;">' +
        ' <img id="%id.button_cut" src="%img.cut.png" style="vertical-align:bottom;">' +
        ' <img id="%id.button_paste" src="%img.paste.png" style="vertical-align:bottom;">' +
        ' &nbsp;<img src="%img.divider1.png" style="vertical-align:bottom;">&nbsp; ' +
        '<img id="%id.button_delete" src="%img.delete.png" style="vertical-align:bottom;">' +
        ' <img id="%id.button_pasteformats" src="%img.formatpaintbrush.png" style="vertical-align:bottom;">' +
        ' &nbsp;<img src="%img.divider1.png" style="vertical-align:bottom;">&nbsp; ' +
        ' <span id="%id.locktools"><img id="%id.button_lock" src="%img.lock.png" style="vertical-align:bottom;">' +
        ' <img id="%id.button_unlock" src="%img.unlock.png" style="vertical-align:bottom;">' +
        ' &nbsp;<img src="%img.divider1.png" style="vertical-align:bottom;">&nbsp;</span> ' +
        '<img id="%id.button_filldown" src="%img.filldown.png" style="vertical-align:bottom;">' +
        ' <img id="%id.button_fillright" src="%img.fillright.png" style="vertical-align:bottom;">' +
        ' &nbsp;<img src="%img.divider1.png" style="vertical-align:bottom;">&nbsp; ' +
        '<img id="%id.button_movefrom" src="%img.movefromoff.gif" style="vertical-align:bottom;">' +
        ' <img id="%id.button_movepaste" src="%img.movepasteoff.gif" style="vertical-align:bottom;">' +
        ' <img id="%id.button_moveinsert" src="%img.moveinsertoff.gif" style="vertical-align:bottom;">' +
        ' &nbsp;<img src="%img.divider1.png" style="vertical-align:bottom;">&nbsp; ' +
        '<img id="%id.button_alignleft" src="%img.alignleft.png" style="vertical-align:bottom;">' +
        ' <img id="%id.button_aligncenter" src="%img.aligncenter.png" style="vertical-align:bottom;">' +
        ' <img id="%id.button_alignright" src="%img.alignright.png" style="vertical-align:bottom;">' +
        ' &nbsp;<img src="%img.divider1.png" style="vertical-align:bottom;">&nbsp; ' +
        '<img id="%id.button_borderon" src="%img.borderson.png" style="vertical-align:bottom;"> ' +
        ' <img id="%id.button_borderoff" src="%img.bordersoff.png" style="vertical-align:bottom;"> ' +
        ' <img id="%id.button_swapcolors" src="%img.swapcolors.png" style="vertical-align:bottom;"> ' +
        ' &nbsp;<img src="%img.divider1.png" style="vertical-align:bottom;">&nbsp; ' +
        '<img id="%id.button_merge" src="%img.mergecells.png" style="vertical-align:bottom;"> ' +
        ' &nbsp;<img src="%img.divider1.png" style="vertical-align:bottom;">&nbsp; ' +
        '<img id="%id.button_insertrow" src="%img.insertrows.png" style="vertical-align:bottom;"> ' +
        ' <img id="%id.button_insertcol" src="%img.insertcolumns.png" style="vertical-align:bottom;"> ' +
        ' <img id="%id.button_deleterow" src="%img.deleterows.png" style="vertical-align:bottom;"> ' +
        ' <img id="%id.button_deletecol" src="%img.deletecolumns.png" style="vertical-align:bottom;"> ' +
        ' <img id="%id.button_hiderow" src="%img.hiderow.png" style="vertical-align:bottom;"> ' +
        ' <img id="%id.button_hidecol" src="%img.hidecol.png" style="vertical-align:bottom;"> ' +
        ' &nbsp;<img id="%id.divider_recalc" src="%img.divider1.png" style="vertical-align:bottom;">&nbsp; ' +
        '<img id="%id.button_recalc" src="%img.recalc.png" style="vertical-align:bottom;"> ' +
        ' </div>',
      oncreate: null,
      onclick: null,
    });
    this.tabnums.settings = this.tabs.length;
    this.tabs.push({
      name: 'settings',
      text: 'Format',
      html:
        '<div id="%id.settingstools" style="display:none;">' +
        ' <div id="%id.sheetsettingstoolbar" style="display:none;">' +
        '  <table cellspacing="0" cellpadding="0"><tr><td>' +
        '   <div style="%tbt.">%loc!SHEET SETTINGS!:</div>' +
        '   </td></tr><tr><td>' +
        '   <input id="%id.settings-savesheet" type="button" value="%loc!Save!" onclick="SocialCalc.SettingsControlSave(\'sheet\');">' +
        '   <input type="button" value="%loc!Cancel!" onclick="SocialCalc.SettingsControlSave(\'cancel\');">' +
        '   <input type="button" value="%loc!Show Cell Settings!" onclick="SocialCalc.SpreadsheetControlSettingsSwitch(\'cell\');return false;">' +
        '   </td></tr></table>' +
        ' </div>' +
        ' <div id="%id.cellsettingstoolbar" style="display:none;">' +
        '  <table cellspacing="0" cellpadding="0"><tr><td>' +
        '   <div style="%tbt.">%loc!CELL SETTINGS!: <span id="%id.settingsecell">&nbsp;</span></div>' +
        '   </td></tr><tr><td>' +
        '  <input id="%id.settings-savecell" type="button" value="%loc!Save!" onclick="SocialCalc.SettingsControlSave(\'cell\');">' +
        '  <input type="button" value="%loc!Cancel!" onclick="SocialCalc.SettingsControlSave(\'cancel\');">' +
        '  <input type="button" value="%loc!Show Sheet Settings!" onclick="SocialCalc.SpreadsheetControlSettingsSwitch(\'sheet\');return false;">' +
        '  </td></tr></table>' +
        ' </div>' +
        '</div>',
      view: 'settings',
      onclick: function (s, _t) {
        SpreadsheetControlSC.SettingsControls.idPrefix = s.idPrefix;
        SocialCalc.SettingControlReset();
        var sheetattribs = s.sheet.EncodeSheetAttributes();
        var cellattribs = s.sheet.EncodeCellAttributes(s.editor.ecell.coord);
        SocialCalc.SettingsControlLoadPanel(s.views.settings.values.sheetspanel, sheetattribs);
        SocialCalc.SettingsControlLoadPanel(s.views.settings.values.cellspanel, cellattribs);
        document.getElementById(s.idPrefix + 'settingsecell').innerHTML = s.editor.ecell.coord;
        SocialCalc.SpreadsheetControlSettingsSwitch('cell');
        s.views.settings.element.style.height = s.viewheight + 'px';
        s.views.settings.element.firstChild.style.height = s.viewheight + 'px';
        var range;
        if (s.editor.range.hasrange) {
          range =
            SocialCalc.crToCoord(s.editor.range.left, s.editor.range.top) +
            ':' +
            SocialCalc.crToCoord(s.editor.range.right, s.editor.range.bottom);
        } else {
          range = s.editor.ecell.coord;
        }
        document.getElementById(s.idPrefix + 'settings-savecell').value =
          SocialCalc.LocalizeString('Save to') + ': ' + range;
      },
      onclickFocus: true,
    });
    this.views['settings'] = {
      name: 'settings',
      values: {},
      oncreate: function (s, viewobj) {
        var scc = SocialCalc.Constants;
        viewobj.values.sheetspanel = {
          colorchooser: { id: s.idPrefix + 'scolorchooser' },
          formatnumber: {
            setting: 'numberformat',
            type: 'PopupList',
            id: s.idPrefix + 'formatnumber',
            initialdata: scc.SCFormatNumberFormats,
          },
          formattext: {
            setting: 'textformat',
            type: 'PopupList',
            id: s.idPrefix + 'formattext',
            initialdata: scc.SCFormatTextFormats,
          },
          fontfamily: {
            setting: 'fontfamily',
            type: 'PopupList',
            id: s.idPrefix + 'fontfamily',
            initialdata: scc.SCFormatFontfamilies,
          },
          fontlook: {
            setting: 'fontlook',
            type: 'PopupList',
            id: s.idPrefix + 'fontlook',
            initialdata: scc.SCFormatFontlook,
          },
          fontsize: {
            setting: 'fontsize',
            type: 'PopupList',
            id: s.idPrefix + 'fontsize',
            initialdata: scc.SCFormatFontsizes,
          },
          textalignhoriz: {
            setting: 'textalignhoriz',
            type: 'PopupList',
            id: s.idPrefix + 'textalignhoriz',
            initialdata: scc.SCFormatTextAlignhoriz,
          },
          numberalignhoriz: {
            setting: 'numberalignhoriz',
            type: 'PopupList',
            id: s.idPrefix + 'numberalignhoriz',
            initialdata: scc.SCFormatNumberAlignhoriz,
          },
          alignvert: {
            setting: 'alignvert',
            type: 'PopupList',
            id: s.idPrefix + 'alignvert',
            initialdata: scc.SCFormatAlignVertical,
          },
          textcolor: {
            setting: 'textcolor',
            type: 'ColorChooser',
            id: s.idPrefix + 'textcolor',
          },
          bgcolor: {
            setting: 'bgcolor',
            type: 'ColorChooser',
            id: s.idPrefix + 'bgcolor',
          },
          padtop: {
            setting: 'padtop',
            type: 'PopupList',
            id: s.idPrefix + 'padtop',
            initialdata: scc.SCFormatPadsizes,
          },
          padright: {
            setting: 'padright',
            type: 'PopupList',
            id: s.idPrefix + 'padright',
            initialdata: scc.SCFormatPadsizes,
          },
          padbottom: {
            setting: 'padbottom',
            type: 'PopupList',
            id: s.idPrefix + 'padbottom',
            initialdata: scc.SCFormatPadsizes,
          },
          padleft: {
            setting: 'padleft',
            type: 'PopupList',
            id: s.idPrefix + 'padleft',
            initialdata: scc.SCFormatPadsizes,
          },
          colwidth: {
            setting: 'colwidth',
            type: 'PopupList',
            id: s.idPrefix + 'colwidth',
            initialdata: scc.SCFormatColwidth,
          },
          recalc: {
            setting: 'recalc',
            type: 'PopupList',
            id: s.idPrefix + 'recalc',
            initialdata: scc.SCFormatRecalc,
          },
          usermaxcol: {
            setting: 'usermaxcol',
            type: 'PopupList',
            id: s.idPrefix + 'usermaxcol',
            initialdata: scc.SCFormatUserMaxCol,
          },
          usermaxrow: {
            setting: 'usermaxrow',
            type: 'PopupList',
            id: s.idPrefix + 'usermaxrow',
            initialdata: scc.SCFormatUserMaxRow,
          },
        };
        viewobj.values.cellspanel = {
          name: 'cell',
          colorchooser: { id: s.idPrefix + 'scolorchooser' },
          cformatnumber: {
            setting: 'numberformat',
            type: 'PopupList',
            id: s.idPrefix + 'cformatnumber',
            initialdata: scc.SCFormatNumberFormats,
          },
          cformattext: {
            setting: 'textformat',
            type: 'PopupList',
            id: s.idPrefix + 'cformattext',
            initialdata: scc.SCFormatTextFormats,
          },
          cfontfamily: {
            setting: 'fontfamily',
            type: 'PopupList',
            id: s.idPrefix + 'cfontfamily',
            initialdata: scc.SCFormatFontfamilies,
          },
          cfontlook: {
            setting: 'fontlook',
            type: 'PopupList',
            id: s.idPrefix + 'cfontlook',
            initialdata: scc.SCFormatFontlook,
          },
          cfontsize: {
            setting: 'fontsize',
            type: 'PopupList',
            id: s.idPrefix + 'cfontsize',
            initialdata: scc.SCFormatFontsizes,
          },
          calignhoriz: {
            setting: 'alignhoriz',
            type: 'PopupList',
            id: s.idPrefix + 'calignhoriz',
            initialdata: scc.SCFormatTextAlignhoriz,
          },
          calignvert: {
            setting: 'alignvert',
            type: 'PopupList',
            id: s.idPrefix + 'calignvert',
            initialdata: scc.SCFormatAlignVertical,
          },
          ctextcolor: {
            setting: 'textcolor',
            type: 'ColorChooser',
            id: s.idPrefix + 'ctextcolor',
          },
          cbgcolor: {
            setting: 'bgcolor',
            type: 'ColorChooser',
            id: s.idPrefix + 'cbgcolor',
          },
          cbt: {
            setting: 'bt',
            type: 'BorderSide',
            id: s.idPrefix + 'cbt',
          },
          cbr: {
            setting: 'br',
            type: 'BorderSide',
            id: s.idPrefix + 'cbr',
          },
          cbb: {
            setting: 'bb',
            type: 'BorderSide',
            id: s.idPrefix + 'cbb',
          },
          cbl: {
            setting: 'bl',
            type: 'BorderSide',
            id: s.idPrefix + 'cbl',
          },
          cpadtop: {
            setting: 'padtop',
            type: 'PopupList',
            id: s.idPrefix + 'cpadtop',
            initialdata: scc.SCFormatPadsizes,
          },
          cpadright: {
            setting: 'padright',
            type: 'PopupList',
            id: s.idPrefix + 'cpadright',
            initialdata: scc.SCFormatPadsizes,
          },
          cpadbottom: {
            setting: 'padbottom',
            type: 'PopupList',
            id: s.idPrefix + 'cpadbottom',
            initialdata: scc.SCFormatPadsizes,
          },
          cpadleft: {
            setting: 'padleft',
            type: 'PopupList',
            id: s.idPrefix + 'cpadleft',
            initialdata: scc.SCFormatPadsizes,
          },
        };
        SocialCalc.SettingsControlInitializePanel(viewobj.values.sheetspanel);
        SocialCalc.SettingsControlInitializePanel(viewobj.values.cellspanel);
      },
      replacements: {
        itemtitle: {
          regex: /%itemtitle\./g,
          replacement:
            'style="padding:12px 10px 0px 10px;font-weight:bold;text-align:right;vertical-align:top;font-size:small;"',
        },
        sectiontitle: {
          regex: /%sectiontitle\./g,
          replacement:
            'style="padding:16px 10px 0px 0px;font-weight:bold;vertical-align:top;font-size:small;color:#C00;"',
        },
        parttitle: {
          regex: /%parttitle\./g,
          replacement: 'style="font-weight:bold;font-size:x-small;padding:0px 0px 3px 0px;"',
        },
        itembody: {
          regex: /%itembody\./g,
          replacement: 'style="padding:12px 0px 0px 0px;vertical-align:top;font-size:small;"',
        },
        bodypart: {
          regex: /%bodypart\./g,
          replacement: 'style="padding:0px 10px 0px 0px;font-size:small;vertical-align:top;"',
        },
      },
      divStyle: 'border:1px solid black;overflow:auto;',
      html:
        '<div id="%id.scolorchooser" style="display:none;position:absolute;z-index:20;"></div>' +
        '<table cellspacing="0" cellpadding="0">' +
        ' <tr><td style="vertical-align:top;">' +
        '<table id="%id.sheetsettingstable" style="display:none;" cellspacing="0" cellpadding="0">' +
        '<tr>' +
        ' <td %itemtitle.><br>%loc!Default Format!:</td>' +
        ' <td %itembody.>' +
        '   <table cellspacing="0" cellpadding="0"><tr>' +
        '    <td %bodypart.>' +
        '     <div %parttitle.>%loc!Number!</div>' +
        '     <span id="%id.formatnumber"></span>' +
        '    </td>' +
        '    <td %bodypart.>' +
        '     <div %parttitle.>%loc!Text!</div>' +
        '     <span id="%id.formattext"></span>' +
        '    </td>' +
        '   </tr></table>' +
        ' </td>' +
        '</tr>' +
        '<tr>' +
        ' <td %itemtitle.><br>%loc!Default Alignment!:</td>' +
        ' <td %itembody.>' +
        '   <table cellspacing="0" cellpadding="0"><tr>' +
        '    <td %bodypart.>' +
        '     <div %parttitle.>%loc!Text Horizontal!</div>' +
        '     <span id="%id.textalignhoriz"></span>' +
        '    </td>' +
        '    <td %bodypart.>' +
        '     <div %parttitle.>%loc!Number Horizontal!</div>' +
        '     <span id="%id.numberalignhoriz"></span>' +
        '    </td>' +
        '    <td %bodypart.>' +
        '     <div %parttitle.>%loc!Vertical!</div>' +
        '     <span id="%id.alignvert"></span>' +
        '    </td>' +
        '   </tr></table>' +
        ' </td>' +
        '</tr>' +
        '<tr>' +
        ' <td %itemtitle.><br>%loc!Default Font!:</td>' +
        ' <td %itembody.>' +
        '   <table cellspacing="0" cellpadding="0"><tr>' +
        '    <td %bodypart.>' +
        '     <div %parttitle.>%loc!Family!</div>' +
        '     <span id="%id.fontfamily"></span>' +
        '    </td>' +
        '    <td %bodypart.>' +
        '     <div %parttitle.>%loc!Bold &amp; Italics!</div>' +
        '     <span id="%id.fontlook"></span>' +
        '    </td>' +
        '    <td %bodypart.>' +
        '     <div %parttitle.>%loc!Size!</div>' +
        '     <span id="%id.fontsize"></span>' +
        '    </td>' +
        '    <td %bodypart.>' +
        '     <div %parttitle.>%loc!Color!</div>' +
        '     <div id="%id.textcolor"></div>' +
        '    </td>' +
        '    <td %bodypart.>' +
        '     <div %parttitle.>%loc!Background!</div>' +
        '     <div id="%id.bgcolor"></div>' +
        '    </td>' +
        '   </tr></table>' +
        ' </td>' +
        '</tr>' +
        '<tr>' +
        ' <td %itemtitle.><br>%loc!Default Padding!:</td>' +
        ' <td %itembody.>' +
        '   <table cellspacing="0" cellpadding="0"><tr>' +
        '    <td %bodypart.>' +
        '     <div %parttitle.>%loc!Top!</div>' +
        '     <span id="%id.padtop"></span>' +
        '    </td>' +
        '    <td %bodypart.>' +
        '     <div %parttitle.>%loc!Right!</div>' +
        '     <span id="%id.padright"></span>' +
        '    </td>' +
        '    <td %bodypart.>' +
        '     <div %parttitle.>%loc!Bottom!</div>' +
        '     <span id="%id.padbottom"></span>' +
        '    </td>' +
        '    <td %bodypart.>' +
        '     <div %parttitle.>%loc!Left!</div>' +
        '     <span id="%id.padleft"></span>' +
        '    </td>' +
        '   </tr></table>' +
        ' </td>' +
        '</tr>' +
        '<tr>' +
        ' <td %itemtitle.><br>%loc!Default Column Width!:</td>' +
        ' <td %itembody.>' +
        '   <table cellspacing="0" cellpadding="0"><tr>' +
        '    <td %bodypart.>' +
        '     <div %parttitle.>&nbsp;</div>' +
        '     <span id="%id.colwidth"></span>' +
        '    </td>' +
        '   </tr></table>' +
        ' </td>' +
        '</tr>' +
        '<tr>' +
        ' <td %itemtitle.><br>%loc!Recalculation!:</td>' +
        ' <td %itembody.>' +
        '   <table cellspacing="0" cellpadding="0"><tr>' +
        '    <td %bodypart.>' +
        '     <div %parttitle.>&nbsp;</div>' +
        '     <span id="%id.recalc"></span>' +
        '    </td>' +
        '   </tr></table>' +
        ' </td>' +
        '</tr>' +
        '<tr>' +
        ' <td %itemtitle.><br>%loc!Dimensions!:</td>' +
        ' <td %itembody.>' +
        '   <table cellspacing="0" cellpadding="0"><tr>' +
        '    <td %bodypart.>' +
        '     <div %parttitle.>%loc!Columns!</div>' +
        '     <span id="%id.usermaxcol"></span>' +
        '    </td>' +
        '    <td %bodypart.>' +
        '     <div %parttitle.>%loc!Rows!</div>' +
        '     <span id="%id.usermaxrow"></span>' +
        '    </td>' +
        '   </tr></table>' +
        ' </td>' +
        '</tr>' +
        '</table>' +
        '<table id="%id.cellsettingstable" cellspacing="0" cellpadding="0">' +
        '<tr>' +
        ' <td %itemtitle.><br>%loc!Format!:</td>' +
        ' <td %itembody.>' +
        '   <table cellspacing="0" cellpadding="0"><tr>' +
        '    <td %bodypart.>' +
        '     <div %parttitle.>%loc!Number!</div>' +
        '     <span id="%id.cformatnumber"></span>' +
        '    </td>' +
        '    <td %bodypart.>' +
        '     <div %parttitle.>%loc!Text!</div>' +
        '     <span id="%id.cformattext"></span>' +
        '    </td>' +
        '   </tr></table>' +
        ' </td>' +
        '</tr>' +
        '<tr>' +
        ' <td %itemtitle.><br>%loc!Alignment!:</td>' +
        ' <td %itembody.>' +
        '   <table cellspacing="0" cellpadding="0"><tr>' +
        '    <td %bodypart.>' +
        '     <div %parttitle.>%loc!Horizontal!</div>' +
        '     <span id="%id.calignhoriz"></span>' +
        '    </td>' +
        '    <td %bodypart.>' +
        '     <div %parttitle.>%loc!Vertical!</div>' +
        '     <span id="%id.calignvert"></span>' +
        '    </td>' +
        '   </tr></table>' +
        ' </td>' +
        '</tr>' +
        '<tr>' +
        ' <td %itemtitle.><br>%loc!Font!:</td>' +
        ' <td %itembody.>' +
        '   <table cellspacing="0" cellpadding="0"><tr>' +
        '    <td %bodypart.>' +
        '     <div %parttitle.>%loc!Family!</div>' +
        '     <span id="%id.cfontfamily"></span>' +
        '    </td>' +
        '    <td %bodypart.>' +
        '     <div %parttitle.>%loc!Bold &amp; Italics!</div>' +
        '     <span id="%id.cfontlook"></span>' +
        '    </td>' +
        '    <td %bodypart.>' +
        '     <div %parttitle.>%loc!Size!</div>' +
        '     <span id="%id.cfontsize"></span>' +
        '    </td>' +
        '    <td %bodypart.>' +
        '     <div %parttitle.>%loc!Color!</div>' +
        '     <div id="%id.ctextcolor"></div>' +
        '    </td>' +
        '    <td %bodypart.>' +
        '     <div %parttitle.>%loc!Background!</div>' +
        '     <div id="%id.cbgcolor"></div>' +
        '    </td>' +
        '   </tr></table>' +
        ' </td>' +
        '</tr>' +
        '<tr>' +
        ' <td %itemtitle.><br>%loc!Borders!:</td>' +
        ' <td %itembody.>' +
        '   <table cellspacing="0" cellpadding="0">' +
        '    <tr><td %bodypart. colspan="3"><div %parttitle.>%loc!Top Border!</div></td>' +
        '     <td %bodypart. colspan="3"><div %parttitle.>%loc!Right Border!</div></td>' +
        '     <td %bodypart. colspan="3"><div %parttitle.>%loc!Bottom Border!</div></td>' +
        '     <td %bodypart. colspan="3"><div %parttitle.>%loc!Left Border!</div></td>' +
        '    </tr><tr>' +
        '    <td %bodypart.>' +
        '     <input id="%id.cbt-onoff-bcb" onclick="SocialCalc.SettingsControlOnchangeBorder(this);" type="checkbox">' +
        '    </td>' +
        '    <td %bodypart.>' +
        '     <div id="%id.cbt-color"></div>' +
        '    </td>' +
        '    <td>&nbsp;&nbsp;&nbsp;&nbsp;</td>' +
        '    <td %bodypart.>' +
        '     <input id="%id.cbr-onoff-bcb" onclick="SocialCalc.SettingsControlOnchangeBorder(this);" type="checkbox">' +
        '    </td>' +
        '    <td %bodypart.>' +
        '     <div id="%id.cbr-color"></div>' +
        '    </td>' +
        '    <td>&nbsp;&nbsp;&nbsp;&nbsp;</td>' +
        '    <td %bodypart.>' +
        '     <input id="%id.cbb-onoff-bcb" onclick="SocialCalc.SettingsControlOnchangeBorder(this);" type="checkbox">' +
        '    </td>' +
        '    <td %bodypart.>' +
        '     <div id="%id.cbb-color"></div>' +
        '    </td>' +
        '    <td>&nbsp;&nbsp;&nbsp;&nbsp;</td>' +
        '    <td %bodypart.>' +
        '     <input id="%id.cbl-onoff-bcb" onclick="SocialCalc.SettingsControlOnchangeBorder(this);" type="checkbox">' +
        '    </td>' +
        '    <td %bodypart.>' +
        '     <div id="%id.cbl-color"></div>' +
        '    </td>' +
        '    <td>&nbsp;&nbsp;&nbsp;&nbsp;</td>' +
        '   </tr></table>' +
        ' </td>' +
        '</tr>' +
        '<tr>' +
        ' <td %itemtitle.><br>%loc!Padding!:</td>' +
        ' <td %itembody.>' +
        '   <table cellspacing="0" cellpadding="0"><tr>' +
        '    <td %bodypart.>' +
        '     <div %parttitle.>%loc!Top!</div>' +
        '     <span id="%id.cpadtop"></span>' +
        '    </td>' +
        '    <td %bodypart.>' +
        '     <div %parttitle.>%loc!Right!</div>' +
        '     <span id="%id.cpadright"></span>' +
        '    </td>' +
        '    <td %bodypart.>' +
        '     <div %parttitle.>%loc!Bottom!</div>' +
        '     <span id="%id.cpadbottom"></span>' +
        '    </td>' +
        '    <td %bodypart.>' +
        '     <div %parttitle.>%loc!Left!</div>' +
        '     <span id="%id.cpadleft"></span>' +
        '    </td>' +
        '   </tr></table>' +
        ' </td>' +
        '</tr>' +
        '</table>' +
        ' </td><td style="vertical-align:top;padding:12px 0px 0px 12px;">' +
        '  <div style="width:100px;height:100px;overflow:hidden;border:1px solid black;background-color:#EEE;padding:6px;">' +
        '   <table cellspacing="0" cellpadding="0"><tr>' +
        '    <td id="sample-text" style="height:100px;width:100px;"><div>%loc!This is a<br>sample!</div><div>-1234.5</div></td>' +
        '   </tr></table>' +
        '  </div>' +
        ' </td></tr></table>' +
        '<br>',
    };
    this.tabnums.sort = this.tabs.length;
    this.tabs.push({
      name: 'sort',
      text: 'Sort',
      html:
        ' <div id="%id.sorttools" style="display:none;">' +
        '  <table cellspacing="0" cellpadding="0"><tr>' +
        '   <td style="vertical-align:top;padding-right:4px;width:160px;">' +
        '    <div style="%tbt.">%loc!Set Cells To Sort!</div>' +
        '    <select id="%id.sortlist" size="1" onfocus="%s.CmdGotFocus(this);"><option selected>[select range]</option><option value="all">Sort All</option></select>' +
        '    <input type="button" value="%loc!OK!" onclick="%s.DoCmd(this, \'ok-setsort\');" style="font-size:x-small;">' +
        '   </td>' +
        '   <td style="vertical-align:middle;padding-right:16px;width:100px;text-align:right;">' +
        '    <div style="%tbt.">&nbsp;</div>' +
        '    <input type="button" id="%id.sortbutton" value="%loc!Sort Cells! A1:A1" onclick="%s.DoCmd(this, \'dosort\');" style="visibility:hidden;">' +
        '   </td>' +
        '   <td style="vertical-align:top;padding-right:16px;">' +
        '    <table cellspacing="0" cellpadding="0"><tr>' +
        '     <td style="vertical-align:top;">' +
        '      <div style="%tbt.">%loc!Major Sort!</div>' +
        '      <select id="%id.majorsort" size="1" onfocus="%s.CmdGotFocus(this);"></select>' +
        '     </td><td>' +
        '      <input type="radio" name="majorsort" id="%id.majorsortup" value="up" checked><span style="font-size:x-small;color:#555753;">%loc!Up!</span><br>' +
        '      <input type="radio" name="majorsort" id="%id.majorsortdown" value="down"><span style="font-size:x-small;color:#555753;">%loc!Down!</span>' +
        '     </td>' +
        '    </tr></table>' +
        '   </td>' +
        '   <td style="vertical-align:top;padding-right:16px;">' +
        '    <table cellspacing="0" cellpadding="0"><tr>' +
        '     <td style="vertical-align:top;">' +
        '      <div style="%tbt.">%loc!Minor Sort!</div>' +
        '      <select id="%id.minorsort" size="1" onfocus="%s.CmdGotFocus(this);"></select>' +
        '     </td><td>' +
        '      <input type="radio" name="minorsort" id="%id.minorsortup" value="up" checked><span style="font-size:x-small;color:#555753;">%loc!Up!</span><br>' +
        '      <input type="radio" name="minorsort" id="%id.minorsortdown" value="down"><span style="font-size:x-small;color:#555753;">%loc!Down!</span>' +
        '     </td>' +
        '    </tr></table>' +
        '   </td>' +
        '   <td style="vertical-align:top;padding-right:16px;">' +
        '    <table cellspacing="0" cellpadding="0"><tr>' +
        '     <td style="vertical-align:top;">' +
        '      <div style="%tbt.">%loc!Last Sort!</div>' +
        '      <select id="%id.lastsort" size="1" onfocus="%s.CmdGotFocus(this);"></select>' +
        '     </td><td>' +
        '      <input type="radio" name="lastsort" id="%id.lastsortup" value="up" checked><span style="font-size:x-small;color:#555753;">%loc!Up!</span><br>' +
        '      <input type="radio" name="lastsort" id="%id.lastsortdown" value="down"><span style="font-size:x-small;color:#555753;">%loc!Down!</span>' +
        '     </td>' +
        '    </tr></table>' +
        '   </td>' +
        '  </tr></table>' +
        ' </div>',
      onclick: SocialCalc.SpreadsheetControlSortOnclick,
    });
    this.editor.SettingsCallbacks.sort = {
      save: SocialCalc.SpreadsheetControlSortSave,
      load: SocialCalc.SpreadsheetControlSortLoad,
    };
    this.tabnums.audit = this.tabs.length;
    this.tabs.push({
      name: 'audit',
      text: 'Audit',
      html:
        '<div id="%id.audittools" style="display:none;">' +
        ' <div style="%tbt.">&nbsp;</div>' +
        '</div>',
      view: 'audit',
      onclick: function (s, _t) {
        var SCLoc = SocialCalc.LocalizeString;
        var i, j;
        var str =
          '<table cellspacing="0" cellpadding="0" style="margin-bottom:10px;"><tr><td style="font-size:small;padding:6px;"><b>' +
          SCLoc('Audit Trail This Session') +
          ':</b><br><br>';
        var stack = s.sheet.changes.stack;
        var tos = s.sheet.changes.tos;
        for (i = 0; i < stack.length; i++) {
          if (i == tos + 1)
            str +=
              '<br></td></tr><tr><td style="font-size:small;background-color:#EEE;padding:6px;">' +
              SCLoc('UNDONE STEPS') +
              ':<br>';
          for (j = 0; j < stack[i].command.length; j++) {
            str += SocialCalc.special_chars(stack[i].command[j]) + '<br>';
          }
        }
        var ObjToSource = function (o) {
          if (typeof o == 'string') return o;
          if (!o) return 'null';
          if (typeof o == 'object') {
            if (!ObjToSource.check) ObjToSource.check = new Array();
            for (var i = 0, k2 = ObjToSource.check.length; i < k2; ++i) {
              if (ObjToSource.check[i] == o) {
                return '{}';
              }
            }
            ObjToSource.check.push(o);
          }
          var k = '',
            na = typeof o.length == 'undefined' ? 1 : 0,
            str = '';
          for (var p in o) {
            if (na) k = "'" + p + "':";
            if (typeof o[p] == 'string') str += k + "'" + o[p] + "',";
            else if (typeof o[p] == 'object') str += k + ObjToSource(o[p]) + ',';
            else str += k + o[p] + ',';
          }
          if (typeof o == 'object') ObjToSource.check.pop();
          if (na) return '{' + str.slice(0, -1) + '}';
          else return '[' + str.slice(0, -1) + ']';
        };
        if (typeof SocialCalc.debug_log != 'undefined') {
          for (const item of SocialCalc.debug_log) {
            str += ObjToSource(item) + '<br>';
          }
        }
        s.views.audit.element.innerHTML = str + '</td></tr></table>';
        SocialCalc.CmdGotFocus(true);
      },
      onclickFocus: true,
    });
    this.views['audit'] = {
      name: 'audit',
      divStyle: 'border:1px solid black;overflow:auto;',
      html: 'Audit Trail',
    };
    this.tabnums.comment = this.tabs.length;
    this.tabs.push({
      name: 'comment',
      text: 'Comment',
      html:
        '<div id="%id.commenttools" style="display:none;">' +
        '<table cellspacing="0" cellpadding="0"><tr><td>' +
        '<textarea id="%id.commenttext" style="font-size:small;height:32px;width:600px;overflow:auto;" onfocus="%s.CmdGotFocus(this);"></textarea>' +
        '</td><td style="vertical-align:top;">' +
        '&nbsp;<input type="button" value="%loc!Save!" onclick="%s.SpreadsheetControlCommentSet();" style="font-size:x-small;">' +
        '</td></tr></table>' +
        '</div>',
      view: 'sheet',
      onclick: SocialCalc.SpreadsheetControlCommentOnclick,
      onunclick: SocialCalc.SpreadsheetControlCommentOnunclick,
    });
    this.tabnums.names = this.tabs.length;
    this.tabs.push({
      name: 'names',
      text: 'Names',
      html:
        '<div id="%id.namestools" style="display:none;">' +
        '  <table cellspacing="0" cellpadding="0"><tr>' +
        '   <td style="vertical-align:top;padding-right:24px;">' +
        '    <div style="%tbt.">%loc!Existing Names!</div>' +
        '    <select id="%id.nameslist" size="1" onchange="%s.SpreadsheetControlNamesChangedName();" onfocus="%s.CmdGotFocus(this);"><option selected>[New]</option></select>' +
        '   </td>' +
        '   <td style="vertical-align:top;padding-right:6px;">' +
        '    <div style="%tbt.">%loc!Name!</div>' +
        '    <input type="text" id="%id.namesname" style="font-size:x-small;width:75px;" onfocus="%s.CmdGotFocus(this);">' +
        '   </td>' +
        '   <td style="vertical-align:top;padding-right:6px;">' +
        '    <div style="%tbt.">%loc!Description!</div>' +
        '    <input type="text" id="%id.namesdesc" style="font-size:x-small;width:150px;" onfocus="%s.CmdGotFocus(this);">' +
        '   </td>' +
        '   <td style="vertical-align:top;padding-right:6px;">' +
        '    <div style="%tbt.">%loc!Value!</div>' +
        '    <input type="text" id="%id.namesvalue" width="16" style="font-size:x-small;width:100px;" onfocus="%s.CmdGotFocus(this);">' +
        '   </td>' +
        '   <td style="vertical-align:top;padding-right:12px;width:100px;">' +
        '    <div style="%tbt.">%loc!Set Value To!</div>' +
        '    <input type="button" id="%id.namesrangeproposal" value="A1" onclick="%s.SpreadsheetControlNamesSetValue();" style="font-size:x-small;">' +
        '   </td>' +
        '   <td style="vertical-align:top;padding-right:6px;">' +
        '    <div style="%tbt.">&nbsp;</div>' +
        '    <input type="button" value="%loc!Save!" onclick="%s.SpreadsheetControlNamesSave();" style="font-size:x-small;">' +
        '    <input type="button" value="%loc!Delete!" onclick="%s.SpreadsheetControlNamesDelete()" style="font-size:x-small;">' +
        '   </td>' +
        '  </tr></table>' +
        '</div>',
      view: 'sheet',
      onclick: SocialCalc.SpreadsheetControlNamesOnclick,
      onunclick: SocialCalc.SpreadsheetControlNamesOnunclick,
    });
    this.tabnums.clipboard = this.tabs.length;
    this.tabs.push({
      name: 'clipboard',
      text: 'Clipboard',
      html:
        '<div id="%id.clipboardtools" style="display:none;">' +
        '  <table cellspacing="0" cellpadding="0"><tr>' +
        '   <td style="vertical-align:top;padding-right:24px;">' +
        '    <div style="%tbt.">' +
        '     &nbsp;' +
        '    </div>' +
        '   </td>' +
        '  </tr></table>' +
        '</div>',
      view: 'clipboard',
      onclick: SocialCalc.SpreadsheetControlClipboardOnclick,
      onclickFocus: 'clipboardtext',
    });
    this.views['clipboard'] = {
      name: 'clipboard',
      divStyle: 'overflow:auto;',
      html:
        ' <div style="font-size:x-small;padding:5px 0px 10px 0px;">' +
        '  <b>%loc!Display Clipboard in!:</b>' +
        '  <input type="radio" id="%id.clipboardformat-tab" name="%id.clipboardformat" checked onclick="%s.SpreadsheetControlClipboardFormat(\'tab\');"> %loc!Tab-delimited format! &nbsp;' +
        '  <input type="radio" id="%id.clipboardformat-csv" name="%id.clipboardformat" onclick="%s.SpreadsheetControlClipboardFormat(\'csv\');"> %loc!CSV format! &nbsp;' +
        '  <input type="radio" id="%id.clipboardformat-scsave" name="%id.clipboardformat" onclick="%s.SpreadsheetControlClipboardFormat(\'scsave\');"> %loc!SocialCalc-save format!' +
        ' </div>' +
        ' <input type="button" value="%loc!Load SocialCalc Clipboard With This!" style="font-size:x-small;" onclick="%s.SpreadsheetControlClipboardLoad();">&nbsp; ' +
        ' <input type="button" value="%loc!Clear SocialCalc Clipboard!" style="font-size:x-small;" onclick="%s.SpreadsheetControlClipboardClear();">&nbsp; ' +
        ' <br>' +
        ' <textarea id="%id.clipboardtext" style="font-size:small;height:350px;width:800px;overflow:auto;" onfocus="%s.CmdGotFocus(this);"></textarea>',
    };
    return;
  };
  SpreadsheetControlSC.SpreadsheetControl.prototype.InitializeSpreadsheetControl = function (
    node,
    height,
    width,
    spacebelow,
  ) {
    return SocialCalc.InitializeSpreadsheetControl(this, node, height, width, spacebelow);
  };
  SpreadsheetControlSC.SpreadsheetControl.prototype.DoOnResize = function () {
    return SocialCalc.DoOnResize(this);
  };
  SpreadsheetControlSC.SpreadsheetControl.prototype.SizeSSDiv = function () {
    return SocialCalc.SizeSSDiv(this);
  };
  SpreadsheetControlSC.SpreadsheetControl.prototype.ExecuteCommand = function (combostr, sstr) {
    return SocialCalc.SpreadsheetControlExecuteCommand(this, combostr, sstr);
  };
  SpreadsheetControlSC.SpreadsheetControl.prototype.CreateSheetHTML = function () {
    return SocialCalc.SpreadsheetControlCreateSheetHTML(this);
  };
  SpreadsheetControlSC.SpreadsheetControl.prototype.CreateSpreadsheetSave = function (otherparts) {
    return SocialCalc.SpreadsheetControlCreateSpreadsheetSave(this, otherparts);
  };
  SpreadsheetControlSC.SpreadsheetControl.prototype.DecodeSpreadsheetSave = function (str) {
    return SocialCalc.SpreadsheetControlDecodeSpreadsheetSave(this, str);
  };
  SpreadsheetControlSC.SpreadsheetControl.prototype.CreateCellHTML = function (coord) {
    return SocialCalc.SpreadsheetControlCreateCellHTML(this, coord);
  };
  SpreadsheetControlSC.SpreadsheetControl.prototype.CreateCellHTMLSave = function (range) {
    return SocialCalc.SpreadsheetControlCreateCellHTMLSave(this, range);
  };
  SpreadsheetControlSC.SpreadsheetControl.prototype.ParseSheetSave = function (str) {
    return this.sheet.ParseSheetSave(str);
  };
  SpreadsheetControlSC.SpreadsheetControl.prototype.CreateSheetSave = function () {
    return this.sheet.CreateSheetSave();
  };
  SpreadsheetControlSC.InitializeSpreadsheetControl = function (
    spreadsheet,
    node,
    height,
    width,
    spacebelow,
  ) {
    var scc = SocialCalc.Constants;
    var SCLoc = SocialCalc.LocalizeString;
    var SCLocSS = SocialCalc.LocalizeSubstrings;
    var html, child, i, vname, v, style, button, bele;
    var tabs = spreadsheet.tabs;
    var views = spreadsheet.views;
    spreadsheet.requestedHeight = height;
    spreadsheet.requestedWidth = width;
    spreadsheet.requestedSpaceBelow = spacebelow;
    if (typeof node == 'string') node = document.getElementById(node);
    if (node == null) {
      (function(){})('SocialCalc.SpreadsheetControl not given parent node.');
      return;
    }
    spreadsheet.parentNode = node;
    spreadsheet.spreadsheetDiv = SocialCalc.document.createElement('div');
    spreadsheet.SizeSSDiv();
    for (child = node.firstChild; child != null; child = node.firstChild) {
      node.removeChild(child);
    }
    html = '<div>';
    html +=
      '<div style="' +
      spreadsheet.tabbackground +
      '">' +
      '<table cellpadding="0" cellspacing="0"><tr>';
    for (i = 0; i < tabs.length; i++) {
      html +=
        '  <td id="%id.' +
        tabs[i].name +
        'tab" style="' +
        (i == 0 ? spreadsheet.tabselectedCSS : spreadsheet.tabplainCSS) +
        '" onclick="%s.SetTab(this);">' +
        SCLoc(tabs[i].text) +
        '</td>';
    }
    html += ' </tr></table></div>';
    html += '<div style="' + spreadsheet.toolbarbackground + 'padding:12px 10px 10px 4px;">';
    for (i = 0; i < tabs.length; i++) {
      html += tabs[i].html;
    }
    html += '</div>';
    html += '</div>';
    spreadsheet.currentTab = 0;
    for (style in spreadsheet.tabreplacements) {
      html = html.replace(
        spreadsheet.tabreplacements[style].regex,
        spreadsheet.tabreplacements[style].replacement,
      );
    }
    html = html.replace(/%s\./g, 'SocialCalc.');
    html = html.replace(/%id\./g, spreadsheet.idPrefix);
    html = html.replace(/%tbt\./g, spreadsheet.toolbartext);
    html = html.replace(/%img\./g, spreadsheet.imagePrefix);
    html = SCLocSS(html);
    spreadsheet.spreadsheetDiv.innerHTML = html;
    node.appendChild(spreadsheet.spreadsheetDiv);
    spreadsheet.Buttons = {
      button_undo: {
        tooltip: 'Undo',
        command: 'undo',
      },
      button_redo: {
        tooltip: 'Redo',
        command: 'redo',
      },
      button_copy: {
        tooltip: 'Copy',
        command: 'copy',
      },
      button_cut: {
        tooltip: 'Cut',
        command: 'cut',
      },
      button_paste: {
        tooltip: 'Paste',
        command: 'paste',
      },
      button_pasteformats: {
        tooltip: 'Paste Formats',
        command: 'pasteformats',
      },
      button_lock: {
        tooltip: 'Lock Cell',
        command: 'lock',
      },
      button_unlock: {
        tooltip: 'Unlock Cell',
        command: 'unlock',
      },
      button_delete: {
        tooltip: 'Delete Cell Contents',
        command: 'delete',
      },
      button_filldown: {
        tooltip: 'Fill Down',
        command: 'filldown',
      },
      button_fillright: {
        tooltip: 'Fill Right',
        command: 'fillright',
      },
      button_movefrom: {
        tooltip: 'Set/Clear Move From',
        command: 'movefrom',
      },
      button_movepaste: {
        tooltip: 'Move Paste',
        command: 'movepaste',
      },
      button_moveinsert: {
        tooltip: 'Move Insert',
        command: 'moveinsert',
      },
      button_alignleft: {
        tooltip: 'Align Left',
        command: 'align-left',
      },
      button_aligncenter: {
        tooltip: 'Align Center',
        command: 'align-center',
      },
      button_alignright: {
        tooltip: 'Align Right',
        command: 'align-right',
      },
      button_borderon: {
        tooltip: 'Borders On',
        command: 'borderon',
      },
      button_borderoff: {
        tooltip: 'Borders Off',
        command: 'borderoff',
      },
      button_swapcolors: {
        tooltip: 'Swap Colors',
        command: 'swapcolors',
      },
      button_merge: {
        tooltip: 'Merge/Unmerge Cells',
        command: 'merge',
      },
      button_insertrow: {
        tooltip: 'Insert Row Before',
        command: 'insertrow',
      },
      button_insertcol: {
        tooltip: 'Insert Column Before',
        command: 'insertcol',
      },
      button_deleterow: {
        tooltip: 'Delete Row',
        command: 'deleterow',
      },
      button_deletecol: {
        tooltip: 'Delete Column',
        command: 'deletecol',
      },
      button_hiderow: {
        tooltip: 'Hide Row',
        command: 'hiderow',
      },
      button_hidecol: {
        tooltip: 'Hide Column',
        command: 'hidecol',
      },
      button_recalc: {
        tooltip: 'Recalculate',
        command: 'recalc',
      },
    };
    for (button in spreadsheet.Buttons) {
      bele = document.getElementById(spreadsheet.idPrefix + button);
      if (!bele) {
        (function(){})('Button ' + (spreadsheet.idPrefix + button) + ' missing');
        continue;
      }
      bele.style.border = '1px solid ' + scc.ISCButtonBorderNormal;
      bele.title = SCLoc(spreadsheet.Buttons[button].tooltip);
      SocialCalc.ButtonRegister(
        spreadsheet.editor,
        bele,
        {
          normalstyle:
            'border:1px solid ' +
            scc.ISCButtonBorderNormal +
            ';background-color:' +
            scc.ISCButtonNormalBackground +
            ';',
          hoverstyle:
            'border:1px solid ' +
            scc.ISCButtonBorderHover +
            ';background-color:' +
            scc.ISCButtonHoverBackground +
            ';',
          downstyle:
            'border:1px solid ' +
            scc.ISCButtonBorderDown +
            ';background-color:' +
            scc.ISCButtonDownBackground +
            ';',
        },
        {
          MouseDown: SocialCalc.DoButtonCmd,
          command: spreadsheet.Buttons[button].command,
        },
      );
    }
    spreadsheet.formulabarDiv = SocialCalc.document.createElement('div');
    spreadsheet.formulabarDiv.innerHTML = '<input type="text" size="60" value="">&nbsp;';
    spreadsheet.spreadsheetDiv.appendChild(spreadsheet.formulabarDiv);
    new SocialCalc.InputBox(spreadsheet.formulabarDiv.firstChild, spreadsheet.editor);
    for (button in spreadsheet.formulabuttons) {
      bele = SocialCalc.document.createElement('img');
      bele.id = spreadsheet.idPrefix + button;
      bele.src =
        (spreadsheet.formulabuttons[button].skipImagePrefix ? '' : spreadsheet.imagePrefix) +
        spreadsheet.formulabuttons[button].image;
      bele.style.verticalAlign = 'middle';
      bele.style.border = '1px solid #FFF';
      bele.style.marginLeft = '4px';
      bele.title = SCLoc(spreadsheet.formulabuttons[button].tooltip);
      SocialCalc.ButtonRegister(
        spreadsheet.editor,
        bele,
        {
          normalstyle: 'border:1px solid #FFF;backgroundColor:#FFF;',
          hoverstyle: 'border:1px solid #CCC;backgroundColor:#FFF;',
          downstyle: 'border:1px solid #000;backgroundColor:#FFF;',
        },
        {
          MouseDown: spreadsheet.formulabuttons[button].command,
          Disabled: function () {
            return spreadsheet.editor.ECellReadonly();
          },
        },
      );
      spreadsheet.formulabarDiv.appendChild(bele);
    }
    var input = $("<input id='searchbarinput' value='' placeholder='Search sheet…'>");
    var searchBar = $("<span id='searchbar'></span>");
    searchBar.append("<div id='searchstatus'></div>");
    searchBar.append(input);
    for (button in spreadsheet.findbuttons) {
      bele = SocialCalc.document.createElement('img');
      bele.id = spreadsheet.idPrefix + button;
      bele.src = spreadsheet.imagePrefix + spreadsheet.findbuttons[button].image;
      bele.style.verticalAlign = 'middle';
      bele.style.border = '1px solid #FFF';
      bele.title = SCLoc(spreadsheet.findbuttons[button].tooltip);
      SocialCalc.ButtonRegister(
        spreadsheet.editor,
        bele,
        {
          normalstyle: 'border:1px solid #FFF;backgroundColor:#FFF;',
          hoverstyle: 'border:1px solid #CCC;backgroundColor:#FFF;',
          downstyle: 'border:1px solid #000;backgroundColor:#FFF;',
        },
        {
          MouseDown: spreadsheet.findbuttons[button].command,
          Disabled: function () {
            return false;
          },
        },
      );
      searchBar[0].appendChild(bele);
    }
    input.on('input', SpreadsheetControlSC.SpreadsheetControl.FindInSheet);
    input.on('focus', function () {
      SpreadsheetControlSC.Keyboard.passThru = true;
    });
    input.on('blur', function () {
      SpreadsheetControlSC.Keyboard.passThru = false;
    });
    input.keyup(function (e) {
      if (e.keyCode == 13) {
        if (e.shiftKey) {
          SpreadsheetControlSC.SpreadsheetControl.SearchUp();
        } else {
          SpreadsheetControlSC.SpreadsheetControl.SearchDown();
        }
      }
    });
    spreadsheet.formulabarDiv.appendChild(searchBar[0]);
    for (i = 0; i < tabs.length; i++) {
      if (tabs[i].oncreate) {
        tabs[i].oncreate(spreadsheet, tabs[i].name);
      }
    }
    SocialCalc.CalculateSheetNonViewHeight(spreadsheet);
    spreadsheet.viewheight = spreadsheet.height - spreadsheet.nonviewheight;
    spreadsheet.editorDiv = spreadsheet.editor.CreateTableEditor(
      spreadsheet.width,
      spreadsheet.viewheight,
    );
    var appViewDiv = SocialCalc.document.createElement('div');
    appViewDiv.id = 'te_appView';
    appViewDiv.appendChild(spreadsheet.editorDiv);
    spreadsheet.editorDiv = appViewDiv;
    var formDataDiv = SocialCalc.document.createElement('div');
    formDataDiv.id = 'te_formData';
    formDataDiv.style.display = 'none';
    spreadsheet.editorDiv.appendChild(formDataDiv);
    spreadsheet.spreadsheetDiv.appendChild(spreadsheet.editorDiv);
    spreadsheet.formDataViewer = new SocialCalc.SpreadsheetViewer('te_FormData-');
    spreadsheet.formDataViewer.InitializeSpreadsheetViewer(formDataDiv.id, 180, 0, 200);
    spreadsheet.formDataViewer.editor.ignoreRender = true;
    for (vname in views) {
      html = views[vname].html;
      for (style in views[vname].replacements) {
        html = html.replace(
          views[vname].replacements[style].regex,
          views[vname].replacements[style].replacement,
        );
      }
      html = html.replace(/%s\./g, 'SocialCalc.');
      html = html.replace(/%id\./g, spreadsheet.idPrefix);
      html = html.replace(/%tbt\./g, spreadsheet.toolbartext);
      html = html.replace(/%img\./g, spreadsheet.imagePrefix);
      v = SocialCalc.document.createElement('div');
      SocialCalc.setStyles(v, views[vname].divStyle);
      v.style.display = 'none';
      v.style.width = spreadsheet.width + 'px';
      v.style.height = spreadsheet.viewheight + 'px';
      v.id = spreadsheet.idPrefix + views[vname].name + 'view';
      html = SCLocSS(html);
      v.innerHTML = html;
      spreadsheet.spreadsheetDiv.appendChild(v);
      views[vname].element = v;
      if (views[vname].oncreate) {
        views[vname].oncreate(spreadsheet, views[vname]);
      }
    }
    views.sheet = {
      name: 'sheet',
      element: spreadsheet.editorDiv,
    };
    spreadsheet.statuslineDiv = SocialCalc.document.createElement('div');
    spreadsheet.statuslineDiv.style.cssText = spreadsheet.statuslineCSS;
    spreadsheet.statuslineDiv.style.height =
      spreadsheet.statuslineheight -
      (spreadsheet.statuslineDiv.style.paddingTop.slice(0, -2) - 0) -
      (spreadsheet.statuslineDiv.style.paddingBottom.slice(0, -2) - 0) +
      'px';
    spreadsheet.statuslineDiv.id = spreadsheet.idPrefix + 'statusline';
    spreadsheet.spreadsheetDiv.appendChild(spreadsheet.statuslineDiv);
    spreadsheet.spreadsheetDiv.addEventListener(
      'mousedown',
      function () {
        SocialCalc.SetSpreadsheetControlObject(spreadsheet);
      },
      false,
    );
    spreadsheet.spreadsheetDiv.addEventListener(
      'mouseover',
      function () {
        SocialCalc.SetSpreadsheetControlObject(spreadsheet);
      },
      false,
    );
    return;
  };
  SpreadsheetControlSC.CalculateSheetNonViewHeight = function (spreadsheet) {
    spreadsheet.nonviewheight = spreadsheet.statuslineheight;
    for (var nodeIndex = 0; nodeIndex < spreadsheet.spreadsheetDiv.childNodes.length; nodeIndex++) {
      if (spreadsheet.spreadsheetDiv.childNodes[nodeIndex].id == 'SocialCalc-statusline') continue;
      spreadsheet.nonviewheight += spreadsheet.spreadsheetDiv.childNodes[nodeIndex].offsetHeight;
    }
  };
  SpreadsheetControlSC.GetSpreadsheetControlObject = function () {
    var csco =
      SocialCalc.CurrentSpreadsheetControlObject != null
        ? SocialCalc.CurrentSpreadsheetControlObject
        : SocialCalc.CurrentSpreadsheetViewerObject;
    if (csco) return csco;
  };
  SpreadsheetControlSC.SetSpreadsheetControlObject = function (spreadsheet) {
    SpreadsheetControlSC.CurrentSpreadsheetControlObject = spreadsheet;
    if (SpreadsheetControlSC.Keyboard.focusTable && spreadsheet) {
      SpreadsheetControlSC.Keyboard.focusTable = spreadsheet.editor;
    }
  };
  SpreadsheetControlSC.SetTab = function (obj) {
    var newtab;
    var tname;
    var newtabnum = 0;
    var newview;
    var i;
    var vname;
    var ele;
    var menutabs = {};
    var tools = {};
    var spreadsheet = SocialCalc.GetSpreadsheetControlObject();
    var tabs = spreadsheet.tabs;
    var views = spreadsheet.views;
    if (typeof obj == 'string') {
      newtab = obj;
    } else {
      newtab = obj.id.slice(spreadsheet.idPrefix.length, -3);
    }
    if (
      spreadsheet.editor.busy &&
      (!tabs[spreadsheet.currentTab].view || tabs[spreadsheet.currentTab].view == 'sheet')
    ) {
      for (i = 0; i < tabs.length; i++) {
        if (tabs[i].name == newtab && tabs[i].view && tabs[i].view != 'sheet') {
          return;
        }
      }
    }
    if (spreadsheet.tabs[spreadsheet.currentTab].onunclick) {
      spreadsheet.tabs[spreadsheet.currentTab].onunclick(
        spreadsheet,
        spreadsheet.tabs[spreadsheet.currentTab].name,
      );
    }
    for (i = 0; i < tabs.length; i++) {
      tname = tabs[i].name;
      menutabs[tname] = document.getElementById(spreadsheet.idPrefix + tname + 'tab');
      tools[tname] = document.getElementById(spreadsheet.idPrefix + tname + 'tools');
      if (tname == newtab) {
        newtabnum = i;
        tools[tname].style.display = 'block';
        menutabs[tname].style.cssText = spreadsheet.tabselectedCSS;
      } else {
        tools[tname].style.display = 'none';
        menutabs[tname].style.cssText = spreadsheet.tabplainCSS;
      }
    }
    spreadsheet.currentTab = newtabnum;
    if (tabs[newtabnum].onclick) {
      tabs[newtabnum].onclick(spreadsheet, newtab);
    }
    for (vname in views) {
      if ((!tabs[newtabnum].view && vname == 'sheet') || tabs[newtabnum].view == vname) {
        views[vname].element.style.display = 'block';
        newview = vname;
      } else {
        views[vname].element.style.display = 'none';
      }
    }
    if (tabs[newtabnum].onclickFocus) {
      ele = tabs[newtabnum].onclickFocus;
      if (typeof ele == 'string') {
        ele = document.getElementById(spreadsheet.idPrefix + ele);
        ele.focus();
      }
      SocialCalc.CmdGotFocus(ele);
    } else {
      SocialCalc.KeyboardFocus();
    }
    if (views[newview].needsresize && views[newview].onresize) {
      views[newview].needsresize = false;
      views[newview].onresize(spreadsheet, views[newview]);
    }
    if (newview == 'sheet') {
      spreadsheet.statuslineDiv.style.display = 'block';
      spreadsheet.editor.ScheduleRender();
    } else {
      spreadsheet.statuslineDiv.style.display = 'none';
    }
    return;
  };
  SpreadsheetControlSC.SpreadsheetControlStatuslineCallback = function (
    editor,
    status,
    arg,
    params,
  ) {
    var rele1, rele2;
    var ele = document.getElementById(params.statuslineid);
    if (ele) {
      ele.innerHTML = editor.GetStatuslineString(status, arg, params);
    }
    switch (status) {
      case 'cmdendnorender':
      case 'calcfinished':
      case 'doneposcalc':
        rele1 = document.getElementById(params.recalcid1);
        rele2 = document.getElementById(params.recalcid2);
        if (!rele1 || !rele2) break;
        if (editor.context.sheetobj.attribs.needsrecalc == 'yes') {
          rele1.style.display = 'inline';
          rele2.style.display = 'inline';
        } else {
          rele1.style.display = 'none';
          rele2.style.display = 'none';
        }
        break;
      default:
        break;
    }
  };
  SpreadsheetControlSC.UpdateSortRangeProposal = function (editor) {
    var ele = document.getElementById(
      SocialCalc.GetSpreadsheetControlObject().idPrefix + 'sortlist',
    );
    if (editor.range.hasrange) {
      ele.options[0].text =
        SocialCalc.crToCoord(editor.range.left, editor.range.top) +
        ':' +
        SocialCalc.crToCoord(editor.range.right, editor.range.bottom);
    } else {
      ele.options[0].text = SocialCalc.LocalizeString('[select range]');
    }
  };
  SpreadsheetControlSC.LoadColumnChoosers = function (spreadsheet) {
    var SCLoc = SocialCalc.LocalizeString;
    var sortrange, nrange, rparts, col, colname, sele, oldindex;
    if (spreadsheet.sortrange && spreadsheet.sortrange.indexOf(':') == -1) {
      nrange = SocialCalc.Formula.LookupName(spreadsheet.sheet, spreadsheet.sortrange);
      if (nrange.type == 'range') {
        rparts = nrange.value.match(/^(.*)\|(.*)\|$/);
        sortrange = rparts[1] + ':' + rparts[2];
      } else {
        sortrange = 'A1:A1';
      }
    } else {
      sortrange = spreadsheet.sortrange;
    }
    var range = SocialCalc.ParseRange(sortrange);
    sele = document.getElementById(spreadsheet.idPrefix + 'majorsort');
    oldindex = sele.selectedIndex;
    sele.options.length = 0;
    sele.options[sele.options.length] = new Option(SCLoc('[None]'), '');
    for (col = range.cr1.col; col <= range.cr2.col; col++) {
      colname = SocialCalc.rcColname(col);
      sele.options[sele.options.length] = new Option(SCLoc('Column ') + colname, colname);
    }
    sele.selectedIndex =
      oldindex > 1 && oldindex <= range.cr2.col - range.cr1.col + 1 ? oldindex : 1;
    sele = document.getElementById(spreadsheet.idPrefix + 'minorsort');
    oldindex = sele.selectedIndex;
    sele.options.length = 0;
    sele.options[sele.options.length] = new Option(SCLoc('[None]'), '');
    for (col = range.cr1.col; col <= range.cr2.col; col++) {
      colname = SocialCalc.rcColname(col);
      sele.options[sele.options.length] = new Option(colname, colname);
    }
    sele.selectedIndex =
      oldindex > 0 && oldindex <= range.cr2.col - range.cr1.col + 1 ? oldindex : 0;
    sele = document.getElementById(spreadsheet.idPrefix + 'lastsort');
    oldindex = sele.selectedIndex;
    sele.options.length = 0;
    sele.options[sele.options.length] = new Option(SCLoc('[None]'), '');
    for (col = range.cr1.col; col <= range.cr2.col; col++) {
      colname = SocialCalc.rcColname(col);
      sele.options[sele.options.length] = new Option(colname, colname);
    }
    sele.selectedIndex =
      oldindex > 0 && oldindex <= range.cr2.col - range.cr1.col + 1 ? oldindex : 0;
  };
  SpreadsheetControlSC.DoButtonCmd = function (e, buttoninfo, bobj) {
    SocialCalc.DoCmd(bobj.element, bobj.functionobj.command);
  };
  SpreadsheetControlSC.DoCmd = function (obj, which) {
    var combostr,
      sstr,
      cl,
      i,
      clele,
      slist,
      slistele,
      str,
      sele,
      rele,
      lele,
      ele,
      sortrange,
      nrange,
      rparts;
    var sheet, cell, color, bgcolor, defaultcolor, defaultbgcolor;
    var spreadsheet = SocialCalc.GetSpreadsheetControlObject();
    var editor = spreadsheet.editor;
    switch (which) {
      case 'undo':
        spreadsheet.ExecuteCommand('undo', '');
        break;
      case 'redo':
        spreadsheet.ExecuteCommand('redo', '');
        break;
      case 'fill-rowcolstuff':
      case 'fill-text':
        cl = which.substring(5);
        clele = document.getElementById(spreadsheet.idPrefix + cl + 'list');
        clele.length = 0;
        for (i = 0; i < SocialCalc.SpreadsheetCmdTable[cl].length; i++) {
          clele.options[i] = new Option(SocialCalc.SpreadsheetCmdTable[cl][i].t);
        }
        which = 'changed-' + cl;
      case 'changed-rowcolstuff':
      case 'changed-text':
        cl = which.substring(8);
        clele = document.getElementById(spreadsheet.idPrefix + cl + 'list');
        slist =
          SocialCalc.SpreadsheetCmdTable.slists[
            SocialCalc.SpreadsheetCmdTable[cl][clele.selectedIndex].s
          ];
        slistele = document.getElementById(spreadsheet.idPrefix + cl + 'slist');
        slistele.length = 0;
        for (i = 0; i < (slist.length || 0); i++) {
          slistele.options[i] = new Option(slist[i].t, slist[i].s);
        }
        return;
      case 'ok-rowcolstuff':
      case 'ok-text':
        cl = which.substring(3);
        clele = document.getElementById(spreadsheet.idPrefix + cl + 'list');
        slistele = document.getElementById(spreadsheet.idPrefix + cl + 'slist');
        combostr = SocialCalc.SpreadsheetCmdTable[cl][clele.selectedIndex].c;
        sstr = slistele[slistele.selectedIndex].value;
        SocialCalc.SpreadsheetControlExecuteCommand(obj, combostr, sstr);
        break;
      case 'ok-setsort':
        lele = document.getElementById(spreadsheet.idPrefix + 'sortlist');
        if (lele.selectedIndex == 0) {
          if (editor.range.hasrange) {
            spreadsheet.sortrange =
              SocialCalc.crToCoord(editor.range.left, editor.range.top) +
              ':' +
              SocialCalc.crToCoord(editor.range.right, editor.range.bottom);
          } else {
            spreadsheet.sortrange = editor.ecell.coord + ':' + editor.ecell.coord;
          }
        } else {
          var val = lele.options[lele.selectedIndex].value;
          if (val == 'all') {
            var cells = spreadsheet.sheet.cells;
            var min_col = -1,
              max_col = -1,
              min_row = -1,
              max_row = -1;
            for (var cell_id in cells) {
              var cr = SocialCalc.coordToCr(cell_id);
              if (min_row == -1 || cr.row < min_row) {
                min_row = cr.row;
              }
              if (min_col == -1 || cr.col < min_col) {
                min_col = cr.col;
              }
              if (max_row == -1 || cr.row > max_row) {
                max_row = cr.row;
              }
              if (max_col == -1 || cr.col > max_col) {
                max_col = cr.col;
              }
            }
            spreadsheet.sortrange =
              SocialCalc.crToCoord(min_col, min_row) + ':' + SocialCalc.crToCoord(max_col, max_row);
            lele.options[lele.selectedIndex].text += ' (' + spreadsheet.sortrange + ')';
          } else {
            spreadsheet.sortrange = lele.options[lele.selectedIndex].value;
          }
        }
        ele = document.getElementById(spreadsheet.idPrefix + 'sortbutton');
        ele.value = SocialCalc.LocalizeString('Sort ') + spreadsheet.sortrange;
        ele.style.visibility = 'visible';
        SocialCalc.LoadColumnChoosers(spreadsheet);
        if (obj && obj.blur) obj.blur();
        SocialCalc.KeyboardFocus();
        return;
      case 'dosort':
        if (spreadsheet.sortrange && spreadsheet.sortrange.indexOf(':') == -1) {
          nrange = SocialCalc.Formula.LookupName(spreadsheet.sheet, spreadsheet.sortrange);
          if (nrange.type != 'range') return;
          rparts = nrange.value.match(/^(.*)\|(.*)\|$/);
          sortrange = rparts[1] + ':' + rparts[2];
        } else {
          sortrange = spreadsheet.sortrange;
        }
        if (sortrange == 'A1:A1') return;
        str = 'sort ' + sortrange + ' ';
        sele = document.getElementById(spreadsheet.idPrefix + 'majorsort');
        rele = document.getElementById(spreadsheet.idPrefix + 'majorsortup');
        str += sele.options[sele.selectedIndex].value + (rele.checked ? ' up' : ' down');
        sele = document.getElementById(spreadsheet.idPrefix + 'minorsort');
        if (sele.selectedIndex > 0) {
          rele = document.getElementById(spreadsheet.idPrefix + 'minorsortup');
          str += ' ' + sele.options[sele.selectedIndex].value + (rele.checked ? ' up' : ' down');
        }
        sele = document.getElementById(spreadsheet.idPrefix + 'lastsort');
        if (sele.selectedIndex > 0) {
          rele = document.getElementById(spreadsheet.idPrefix + 'lastsortup');
          str += ' ' + sele.options[sele.selectedIndex].value + (rele.checked ? ' up' : ' down');
        }
        spreadsheet.ExecuteCommand(str, '');
        break;
      case 'merge':
        combostr = SocialCalc.SpreadsheetCmdLookup[which] || '';
        sstr = SocialCalc.SpreadsheetCmdSLookup[which] || '';
        spreadsheet.ExecuteCommand(combostr, sstr);
        if (editor.range.hasrange) {
          editor.MoveECell(SocialCalc.crToCoord(editor.range.left, editor.range.top));
          editor.RangeRemove();
        }
        break;
      case 'movefrom':
        if (editor.range2.hasrange) {
          spreadsheet.context.cursorsuffix = '';
          editor.Range2Remove();
          spreadsheet.ExecuteCommand('redisplay', '');
        } else if (editor.range.hasrange) {
          editor.range2.top = editor.range.top;
          editor.range2.right = editor.range.right;
          editor.range2.bottom = editor.range.bottom;
          editor.range2.left = editor.range.left;
          editor.range2.hasrange = true;
          editor.MoveECell(SocialCalc.crToCoord(editor.range.left, editor.range.top));
        } else {
          editor.range2.top = editor.ecell.row;
          editor.range2.right = editor.ecell.col;
          editor.range2.bottom = editor.ecell.row;
          editor.range2.left = editor.ecell.col;
          editor.range2.hasrange = true;
        }
        str = editor.range2.hasrange ? '' : 'off';
        ele = document.getElementById(spreadsheet.idPrefix + 'button_movefrom');
        ele.src = spreadsheet.imagePrefix + 'movefrom' + str + '.gif';
        ele = document.getElementById(spreadsheet.idPrefix + 'button_movepaste');
        ele.src = spreadsheet.imagePrefix + 'movepaste' + str + '.gif';
        ele = document.getElementById(spreadsheet.idPrefix + 'button_moveinsert');
        ele.src = spreadsheet.imagePrefix + 'moveinsert' + str + '.gif';
        if (editor.range2.hasrange) editor.RangeRemove();
        break;
      case 'movepaste':
      case 'moveinsert':
        if (editor.range2.hasrange) {
          spreadsheet.context.cursorsuffix = '';
          combostr =
            which +
            ' ' +
            SocialCalc.crToCoord(editor.range2.left, editor.range2.top) +
            ':' +
            SocialCalc.crToCoord(editor.range2.right, editor.range2.bottom) +
            ' ' +
            editor.ecell.coord;
          spreadsheet.ExecuteCommand(combostr, '');
          editor.Range2Remove();
          ele = document.getElementById(spreadsheet.idPrefix + 'button_movefrom');
          ele.src = spreadsheet.imagePrefix + 'movefromoff.gif';
          ele = document.getElementById(spreadsheet.idPrefix + 'button_movepaste');
          ele.src = spreadsheet.imagePrefix + 'movepasteoff.gif';
          ele = document.getElementById(spreadsheet.idPrefix + 'button_moveinsert');
          ele.src = spreadsheet.imagePrefix + 'moveinsertoff.gif';
        }
        break;
      case 'swapcolors':
        sheet = spreadsheet.sheet;
        cell = sheet.GetAssuredCell(editor.ecell.coord);
        defaultcolor = sheet.attribs.defaultcolor
          ? sheet.colors[sheet.attribs.defaultcolor]
          : 'rgb(0,0,0)';
        defaultbgcolor = sheet.attribs.defaultbgcolor
          ? sheet.colors[sheet.attribs.defaultbgcolor]
          : 'rgb(255,255,255)';
        color = cell.color ? sheet.colors[cell.color] : defaultcolor;
        if (color == defaultbgcolor) color = '';
        bgcolor = cell.bgcolor ? sheet.colors[cell.bgcolor] : defaultbgcolor;
        if (bgcolor == defaultcolor) bgcolor = '';
        spreadsheet.ExecuteCommand('set %C color ' + bgcolor + '%Nset %C bgcolor ' + color, '');
        break;
      default:
        combostr = SocialCalc.SpreadsheetCmdLookup[which] || '';
        sstr = SocialCalc.SpreadsheetCmdSLookup[which] || '';
        spreadsheet.ExecuteCommand(combostr, sstr);
        break;
    }
    if (obj && obj.blur) obj.blur();
    SocialCalc.KeyboardFocus();
  };
  SpreadsheetControlSC.SpreadsheetCmdLookup = {
    copy: 'copy %C all',
    cut: 'cut %C all',
    paste: 'paste %C all',
    pasteformats: 'paste %C formats',
    lock: 'set %C readonly yes',
    unlock: 'set %C readonly no',
    delete: 'erase %C formulas',
    filldown: 'filldown %C all',
    fillright: 'fillright %C all',
    erase: 'erase %C all',
    borderon: 'set %C bt %S%Nset %C br %S%Nset %C bb %S%Nset %C bl %S',
    borderoff: 'set %C bt %S%Nset %C br %S%Nset %C bb %S%Nset %C bl %S',
    merge: 'merge %C',
    unmerge: 'unmerge %C',
    'align-left': 'set %C cellformat left',
    'align-center': 'set %C cellformat center',
    'align-right': 'set %C cellformat right',
    'align-default': 'set %C cellformat',
    insertrow: 'insertrow %C',
    insertcol: 'insertcol %C',
    deleterow: 'deleterow %C',
    deletecol: 'deletecol %C',
    hiderow: 'set %H hide yes',
    hidecol: 'set %W hide yes',
    undo: 'undo',
    redo: 'redo',
    recalc: 'recalc',
  };
  SpreadsheetControlSC.SpreadsheetCmdSLookup = {
    borderon: '1px solid rgb(0,0,0)',
    borderoff: '',
  };
  SpreadsheetControlSC.SpreadsheetControlExecuteCommand = function (obj, combostr, sstr) {
    var spreadsheet = SocialCalc.GetSpreadsheetControlObject();
    var eobj = spreadsheet.editor;
    var str = {};
    str.P = '%';
    str.N = '\n';
    if (eobj.range.hasrange) {
      str.R =
        SocialCalc.crToCoord(eobj.range.left, eobj.range.top) +
        ':' +
        SocialCalc.crToCoord(eobj.range.right, eobj.range.bottom);
      str.C = str.R;
      str.W = SocialCalc.rcColname(eobj.range.left) + ':' + SocialCalc.rcColname(eobj.range.right);
      str.H = eobj.range.top + ':' + eobj.range.bottom;
    } else if (eobj.ecell) {
      str.C = eobj.ecell.coord;
      str.R = eobj.ecell.coord + ':' + eobj.ecell.coord;
      str.W = SocialCalc.rcColname(SocialCalc.coordToCr(eobj.ecell.coord).col);
      str.H = SocialCalc.coordToCr(eobj.ecell.coord).row;
    } else {
      str.C = 'A1';
      str.R = 'A1:A1';
      str.W = SocialCalc.rcColname(SocialCalc.coordToCr('A1').col);
      str.H = SocialCalc.coordToCr('A1').row;
    }
    str.S = sstr;
    combostr = combostr.replace(/%C/g, str.C);
    combostr = combostr.replace(/%R/g, str.R);
    combostr = combostr.replace(/%N/g, str.N);
    combostr = combostr.replace(/%S/g, str.S);
    combostr = combostr.replace(/%W/g, str.W);
    combostr = combostr.replace(/%H/g, str.H);
    combostr = combostr.replace(/%P/g, str.P);
    eobj.EditorScheduleSheetCommands(combostr, true, false);
  };
  SpreadsheetControlSC.SpreadsheetControlCreateSheetHTML = function (spreadsheet) {
    var context, div, ele;
    var result = '';
    context = new SocialCalc.RenderContext(spreadsheet.sheet);
    div = SocialCalc.document.createElement('div');
    ele = context.RenderSheet(null, { type: 'html' });
    div.appendChild(ele);
    result = div.innerHTML;
    return result;
  };
  SpreadsheetControlSC.SpreadsheetControlCreateCellHTML = function (spreadsheet, coord, linkstyle) {
    var result = '';
    var cell = spreadsheet.sheet.cells[coord];
    if (!cell) return '';
    if (cell.displaystring == undefined) {
      result = SocialCalc.FormatValueForDisplay(
        spreadsheet.sheet,
        cell.datavalue,
        coord,
        linkstyle || spreadsheet.context.defaultHTMLlinkstyle,
      );
    } else {
      result = cell.displaystring;
    }
    if (result == '&nbsp;') result = '';
    return result;
  };
  SpreadsheetControlSC.SpreadsheetControlCreateCellHTMLSave = function (
    spreadsheet,
    range,
    linkstyle,
  ) {
    var cr1, cr2, row, col, coord, cell, cellHTML;
    var result = [];
    var prange;
    if (range) {
      prange = SocialCalc.ParseRange(range);
    } else {
      prange = {
        cr1: {
          row: 1,
          col: 1,
        },
        cr2: {
          row: spreadsheet.sheet.attribs.lastrow,
          col: spreadsheet.sheet.attribs.lastcol,
        },
      };
    }
    cr1 = prange.cr1;
    cr2 = prange.cr2;
    result.push('version:1.0');
    for (row = cr1.row; row <= cr2.row; row++) {
      for (col = cr1.col; col <= cr2.col; col++) {
        coord = SocialCalc.crToCoord(col, row);
        cell = spreadsheet.sheet.cells[coord];
        if (!cell) continue;
        if (cell.displaystring == undefined) {
          cellHTML = SocialCalc.FormatValueForDisplay(
            spreadsheet.sheet,
            cell.datavalue,
            coord,
            linkstyle || spreadsheet.context.defaultHTMLlinkstyle,
          );
        } else {
          cellHTML = cell.displaystring;
        }
        if (cellHTML == '&nbsp;') continue;
        result.push(coord + ':' + SocialCalc.encodeForSave(cellHTML));
      }
    }
    result.push('');
    return result.join('\n');
  };
  SpreadsheetControlSC.SpreadsheetControl.DoFunctionList = function () {
    var i, str, ele;
    var scf = SocialCalc.Formula;
    var scc = SocialCalc.Constants;
    var fcl = scc.function_classlist;
    var spreadsheet = SocialCalc.GetSpreadsheetControlObject();
    var idp = spreadsheet.idPrefix + 'function';
    ele = document.getElementById(idp + 'dialog');
    if (ele) return;
    scf.FillFunctionInfo();
    str =
      '<table><tr><td><span style="font-size:x-small;font-weight:bold">%loc!Category!</span><br>' +
      '<select id="' +
      idp +
      'class" size="' +
      fcl.length +
      '" style="width:120px;" onchange="SocialCalc.SpreadsheetControl.FunctionClassChosen(this.options[this.selectedIndex].value);">';
    for (i = 0; i < fcl.length; i++) {
      str +=
        '<option value="' +
        fcl[i] +
        '"' +
        (i == 0 ? ' selected>' : '>') +
        SocialCalc.special_chars(scf.FunctionClasses[fcl[i]].name) +
        '</option>';
    }
    str +=
      '</select></td><td>&nbsp;&nbsp;</td><td id="' +
      idp +
      'list"><span style="font-size:x-small;font-weight:bold">%loc!Functions!</span><br>' +
      '<select id="' +
      idp +
      'name" size="' +
      fcl.length +
      '" style="width:240px;" ' +
      'onchange="SocialCalc.SpreadsheetControl.FunctionChosen(this.options[this.selectedIndex].value);" ondblclick="SocialCalc.SpreadsheetControl.DoFunctionPaste();">';
    str += SpreadsheetControlSC.SpreadsheetControl.GetFunctionNamesStr('all');
    str +=
      '</td></tr><tr><td colspan="3">' +
      '<div id="' +
      idp +
      'desc" style="width:380px;height:80px;overflow:auto;font-size:x-small;">' +
      SpreadsheetControlSC.SpreadsheetControl.GetFunctionInfoStr(
        scf.FunctionClasses[fcl[0]].items[0],
      ) +
      '</div>' +
      '<div style="width:380px;text-align:right;padding-top:6px;font-size:small;">' +
      '<input type="button" value="%loc!Paste!" style="font-size:smaller;" onclick="SocialCalc.SpreadsheetControl.DoFunctionPaste();">&nbsp;' +
      '<input type="button" value="%loc!Cancel!" style="font-size:smaller;" onclick="SocialCalc.SpreadsheetControl.HideFunctions();"></div>' +
      '</td></tr></table>';
    var main = SocialCalc.document.createElement('div');
    main.id = idp + 'dialog';
    main.style.position = 'absolute';
    var vp = SocialCalc.GetViewportInfo();
    var pos = SocialCalc.GetElementPositionWithScroll(spreadsheet.spreadsheetDiv);
    main.style.top = vp.height / 3 - pos.top + 'px';
    main.style.left = vp.width / 3 - pos.left + 'px';
    main.style.zIndex = '100';
    main.style.backgroundColor = '#FFF';
    main.style.border = '1px solid black';
    main.style.width = '400px';
    str =
      '<table cellspacing="0" cellpadding="0" style="border-bottom:1px solid black;"><tr>' +
      '<td style="font-size:10px;cursor:default;width:100%;background-color:#999;color:#FFF;">' +
      '&nbsp;%loc!Function List!' +
      '</td>' +
      '<td style="font-size:10px;cursor:default;color:#666;" onclick="SocialCalc.SpreadsheetControl.HideFunctions();">&nbsp;X&nbsp;</td></tr></table>' +
      '<div style="background-color:#DDD;">' +
      str +
      '</div>';
    str = SocialCalc.LocalizeSubstrings(str);
    main.innerHTML = str;
    SocialCalc.DragRegister(
      main.firstChild.firstChild.firstChild,
      true,
      true,
      {
        MouseDown: SocialCalc.DragFunctionStart,
        MouseMove: SocialCalc.DragFunctionPosition,
        MouseUp: SocialCalc.DragFunctionPosition,
        Disabled: null,
        positionobj: main,
      },
      spreadsheet.spreadsheetDiv,
    );
    spreadsheet.spreadsheetDiv.appendChild(main);
    ele = document.getElementById(idp + 'name');
    ele.focus();
    SocialCalc.CmdGotFocus(ele);
  };
  SpreadsheetControlSC.SpreadsheetControl.GetFunctionNamesStr = function (cname) {
    var i, f;
    var scf = SocialCalc.Formula;
    var str = '';
    f = scf.FunctionClasses[cname || 'all'];
    for (i = 0; i < f.items.length; i++) {
      str +=
        '<option value="' +
        f.items[i] +
        '"' +
        (i == 0 ? ' selected>' : '>') +
        f.items[i] +
        '</option>';
    }
    return str;
  };
  SpreadsheetControlSC.SpreadsheetControl.FillFunctionNames = function (cname, ele) {
    var i, f;
    var scf = SocialCalc.Formula;
    ele.length = 0;
    f = scf.FunctionClasses[cname];
    for (i = 0; i < f.items.length; i++) {
      ele.options[i] = new Option(f.items[i], f.items[i]);
      if (i == 0) {
        ele.options[i].selected = true;
      }
    }
  };
  SpreadsheetControlSC.SpreadsheetControl.GetFunctionInfoStr = function (fname) {
    var scf = SocialCalc.Formula;
    var f = scf.FunctionList[fname];
    var scsc = SocialCalc.special_chars;
    var str = '<b>' + fname + '(' + scsc(scf.FunctionArgString(fname)) + ')</b><br>';
    str += scsc(f[3]);
    return str;
  };
  SpreadsheetControlSC.SpreadsheetControl.FunctionClassChosen = function (cname) {
    var spreadsheet = SocialCalc.GetSpreadsheetControlObject();
    var idp = spreadsheet.idPrefix + 'function';
    var scf = SocialCalc.Formula;
    SpreadsheetControlSC.SpreadsheetControl.FillFunctionNames(
      cname,
      document.getElementById(idp + 'name'),
    );
    SpreadsheetControlSC.SpreadsheetControl.FunctionChosen(scf.FunctionClasses[cname].items[0]);
  };
  SpreadsheetControlSC.SpreadsheetControl.FunctionChosen = function (fname) {
    var spreadsheet = SocialCalc.GetSpreadsheetControlObject();
    var idp = spreadsheet.idPrefix + 'function';
    document.getElementById(idp + 'desc').innerHTML =
      SpreadsheetControlSC.SpreadsheetControl.GetFunctionInfoStr(fname);
  };
  SpreadsheetControlSC.SpreadsheetControl.HideFunctions = function () {
    var spreadsheet = SocialCalc.GetSpreadsheetControlObject();
    var ele = document.getElementById(spreadsheet.idPrefix + 'functiondialog');
    ele.innerHTML = '';
    SocialCalc.DragUnregister(ele);
    SocialCalc.KeyboardFocus();
    if (ele.parentNode) {
      ele.parentNode.removeChild(ele);
    }
  };
  SpreadsheetControlSC.SpreadsheetControl.DoFunctionPaste = function () {
    var spreadsheet = SocialCalc.GetSpreadsheetControlObject();
    var editor = spreadsheet.editor;
    var ele = document.getElementById(spreadsheet.idPrefix + 'functionname');
    var mele = document.getElementById(spreadsheet.idPrefix + 'multilinetextarea');
    var text = ele.value + '(';
    SpreadsheetControlSC.SpreadsheetControl.HideFunctions();
    if (mele) {
      mele.value += text;
      mele.focus();
      SocialCalc.CmdGotFocus(mele);
    } else {
      editor.EditorAddToInput(text, '=');
    }
  };
  SpreadsheetControlSC.SpreadsheetControl.DoMultiline = function () {
    var SCLocSS = SocialCalc.LocalizeSubstrings;
    var str, ele, text;
    var spreadsheet = SocialCalc.GetSpreadsheetControlObject();
    var editor = spreadsheet.editor;
    var wval = editor.workingvalues;
    var spreadsheet = SocialCalc.GetSpreadsheetControlObject();
    var idp = spreadsheet.idPrefix + 'multiline';
    ele = document.getElementById(idp + 'dialog');
    if (ele) return;
    switch (editor.state) {
      case 'start':
        wval.ecoord = editor.ecell.coord;
        wval.erow = editor.ecell.row;
        wval.ecol = editor.ecell.col;
        editor.RangeRemove();
        text = SocialCalc.GetCellContents(editor.context.sheetobj, wval.ecoord);
        break;
      case 'input':
      case 'inputboxdirect':
        text = editor.inputBox.GetText();
        break;
    }
    editor.inputBox.element.disabled = true;
    text = SocialCalc.special_chars(text);
    str =
      '<textarea id="' +
      idp +
      'textarea" style="width:380px;height:120px;margin:10px 0px 0px 6px;">' +
      text +
      '</textarea>' +
      '<div style="width:380px;text-align:right;padding:6px 0px 4px 6px;font-size:small;">' +
      SCLocSS(
        '<input type="button" value="%loc!Set Cell Contents!" style="font-size:smaller;" onclick="SocialCalc.SpreadsheetControl.DoMultilinePaste();">&nbsp;' +
          '<input type="button" value="%loc!Clear!" style="font-size:smaller;" onclick="SocialCalc.SpreadsheetControl.DoMultilineClear();">&nbsp;' +
          '<input type="button" value="%loc!Cancel!" style="font-size:smaller;" onclick="SocialCalc.SpreadsheetControl.HideMultiline();"></div>' +
          '</div>',
      );
    var main = SocialCalc.document.createElement('div');
    main.id = idp + 'dialog';
    main.style.position = 'absolute';
    var vp = SocialCalc.GetViewportInfo();
    var pos = SocialCalc.GetElementPositionWithScroll(spreadsheet.spreadsheetDiv);
    main.style.top = vp.height / 3 - pos.top + 'px';
    main.style.left = vp.width / 3 - pos.left + 'px';
    main.style.zIndex = '100';
    main.style.backgroundColor = '#FFF';
    main.style.border = '1px solid black';
    main.style.width = '400px';
    main.innerHTML =
      '<table cellspacing="0" cellpadding="0" style="border-bottom:1px solid black;"><tr>' +
      '<td style="font-size:10px;cursor:default;width:100%;background-color:#999;color:#FFF;">' +
      SCLocSS('&nbsp;%loc!Multi-line Input Box!') +
      '</td>' +
      '<td style="font-size:10px;cursor:default;color:#666;" onclick="SocialCalc.SpreadsheetControl.HideMultiline();">&nbsp;X&nbsp;</td></tr></table>' +
      '<div style="background-color:#DDD;">' +
      str +
      '</div>';
    SocialCalc.DragRegister(
      main.firstChild.firstChild.firstChild,
      true,
      true,
      {
        MouseDown: SocialCalc.DragFunctionStart,
        MouseMove: SocialCalc.DragFunctionPosition,
        MouseUp: SocialCalc.DragFunctionPosition,
        Disabled: null,
        positionobj: main,
      },
      spreadsheet.spreadsheetDiv,
    );
    spreadsheet.spreadsheetDiv.appendChild(main);
    ele = document.getElementById(idp + 'textarea');
    ele.focus();
    SocialCalc.CmdGotFocus(ele);
  };
  SpreadsheetControlSC.SpreadsheetControl.HideMultiline = function () {
    var spreadsheet = SocialCalc.GetSpreadsheetControlObject();
    var editor = spreadsheet.editor;
    var ele = document.getElementById(spreadsheet.idPrefix + 'multilinedialog');
    ele.innerHTML = '';
    SocialCalc.DragUnregister(ele);
    SocialCalc.KeyboardFocus();
    if (ele.parentNode) {
      ele.parentNode.removeChild(ele);
    }
    switch (editor.state) {
      case 'start':
        editor.inputBox.DisplayCellContents(null);
        break;
      case 'input':
      case 'inputboxdirect':
        editor.inputBox.element.disabled = false;
        editor.inputBox.Focus();
        break;
    }
  };
  SpreadsheetControlSC.SpreadsheetControl.DoMultilineClear = function () {
    var spreadsheet = SocialCalc.GetSpreadsheetControlObject();
    var ele = document.getElementById(spreadsheet.idPrefix + 'multilinetextarea');
    ele.value = '';
    ele.focus();
  };
  SpreadsheetControlSC.SpreadsheetControl.DoMultilinePaste = function () {
    var spreadsheet = SocialCalc.GetSpreadsheetControlObject();
    var editor = spreadsheet.editor;
    var wval = editor.workingvalues;
    var ele = document.getElementById(spreadsheet.idPrefix + 'multilinetextarea');
    var text = ele.value;
    SpreadsheetControlSC.SpreadsheetControl.HideMultiline();
    switch (editor.state) {
      case 'start':
        wval.partialexpr = '';
        wval.ecoord = editor.ecell.coord;
        wval.erow = editor.ecell.row;
        wval.ecol = editor.ecell.col;
        break;
      case 'input':
      case 'inputboxdirect':
        editor.inputBox.Blur();
        editor.inputBox.ShowInputBox(false);
        editor.state = 'start';
        break;
    }
    editor.EditorSaveEdit(text);
  };
  SpreadsheetControlSC.SpreadsheetControl.DoLink = function () {
    var SCLoc = SocialCalc.LocalizeString;
    var str, ele, text, cell, setformat, popup;
    var spreadsheet = SocialCalc.GetSpreadsheetControlObject();
    var editor = spreadsheet.editor;
    var wval = editor.workingvalues;
    var spreadsheet = SocialCalc.GetSpreadsheetControlObject();
    var idp = spreadsheet.idPrefix + 'link';
    ele = document.getElementById(idp + 'dialog');
    if (ele) return;
    switch (editor.state) {
      case 'start':
        wval.ecoord = editor.ecell.coord;
        wval.erow = editor.ecell.row;
        wval.ecol = editor.ecell.col;
        editor.RangeRemove();
        text = SocialCalc.GetCellContents(editor.context.sheetobj, wval.ecoord);
        break;
      case 'input':
      case 'inputboxdirect':
        text = editor.inputBox.GetText();
        break;
    }
    editor.inputBox.element.disabled = true;
    if (text.charAt(0) == "'") {
      text = text.slice(1);
    }
    var parts = SocialCalc.ParseCellLinkText(text);
    text = SocialCalc.special_chars(text);
    cell = spreadsheet.sheet.cells[editor.ecell.coord];
    setformat = !cell || !cell.textvalueformat ? ' checked' : '';
    popup = parts.newwin ? ' checked' : '';
    str =
      '<div style="padding:6px 0px 4px 6px;">' +
      '<span style="font-size:smaller;">' +
      SCLoc('Description') +
      '</span><br>' +
      '<input type="text" id="' +
      idp +
      'desc" style="width:380px;" value="' +
      SocialCalc.special_chars(parts.desc) +
      '"><br>' +
      '<span style="font-size:smaller;">' +
      SCLoc('URL') +
      '</span><br>' +
      '<input type="text" id="' +
      idp +
      'url" style="width:380px;" value="' +
      SocialCalc.special_chars(parts.url) +
      '"><br>';
    if (SocialCalc.Callbacks.MakePageLink) {
      str +=
        '<span style="font-size:smaller;">' +
        SCLoc('Page Name') +
        '</span><br>' +
        '<input type="text" id="' +
        idp +
        'pagename" style="width:380px;" value="' +
        SocialCalc.special_chars(parts.pagename) +
        '"><br>' +
        '<span style="font-size:smaller;">' +
        SCLoc('Workspace') +
        '</span><br>' +
        '<input type="text" id="' +
        idp +
        'workspace" style="width:380px;" value="' +
        SocialCalc.special_chars(parts.workspace) +
        '"><br>';
    }
    str += SocialCalc.LocalizeSubstrings(
      '<input type="checkbox" id="' +
        idp +
        'format"' +
        setformat +
        '>&nbsp;' +
        '<span style="font-size:smaller;">%loc!Set to Link format!</span><br>' +
        '<input type="checkbox" id="' +
        idp +
        'popup"' +
        popup +
        '>&nbsp;' +
        '<span style="font-size:smaller;">%loc!Show in new browser window!</span>' +
        '</div>' +
        '<div style="width:380px;text-align:right;padding:6px 0px 4px 6px;font-size:small;">' +
        '<input type="button" value="%loc!Set Cell Contents!" style="font-size:smaller;" onclick="SocialCalc.SpreadsheetControl.DoLinkPaste();">&nbsp;' +
        '<input type="button" value="%loc!Clear!" style="font-size:smaller;" onclick="SocialCalc.SpreadsheetControl.DoLinkClear();">&nbsp;' +
        '<input type="button" value="%loc!Cancel!" style="font-size:smaller;" onclick="SocialCalc.SpreadsheetControl.HideLink();"></div>' +
        '</div>',
    );
    var main = SocialCalc.document.createElement('div');
    main.id = idp + 'dialog';
    main.style.position = 'absolute';
    var vp = SocialCalc.GetViewportInfo();
    var pos = SocialCalc.GetElementPositionWithScroll(spreadsheet.spreadsheetDiv);
    main.style.top = vp.height / 3 - pos.top + 'px';
    main.style.left = vp.width / 3 - pos.left + 'px';
    main.style.zIndex = '100';
    main.style.backgroundColor = '#FFF';
    main.style.border = '1px solid black';
    main.style.width = '400px';
    main.innerHTML =
      '<table cellspacing="0" cellpadding="0" style="border-bottom:1px solid black;"><tr>' +
      '<td style="font-size:10px;cursor:default;width:100%;background-color:#999;color:#FFF;">' +
      '&nbsp;' +
      SCLoc('Link Input Box') +
      '</td>' +
      '<td style="font-size:10px;cursor:default;color:#666;" onclick="SocialCalc.SpreadsheetControl.HideLink();">&nbsp;X&nbsp;</td></tr></table>' +
      '<div style="background-color:#DDD;">' +
      str +
      '</div>';
    SocialCalc.DragRegister(
      main.firstChild.firstChild.firstChild,
      true,
      true,
      {
        MouseDown: SocialCalc.DragFunctionStart,
        MouseMove: SocialCalc.DragFunctionPosition,
        MouseUp: SocialCalc.DragFunctionPosition,
        Disabled: null,
        positionobj: main,
      },
      spreadsheet.spreadsheetDiv,
    );
    spreadsheet.spreadsheetDiv.appendChild(main);
    ele = document.getElementById(idp + 'url');
    ele.focus();
    SocialCalc.CmdGotFocus(ele);
  };
  SpreadsheetControlSC.SpreadsheetControl.HideLink = function () {
    var spreadsheet = SocialCalc.GetSpreadsheetControlObject();
    var editor = spreadsheet.editor;
    var ele = document.getElementById(spreadsheet.idPrefix + 'linkdialog');
    ele.innerHTML = '';
    SocialCalc.DragUnregister(ele);
    SocialCalc.KeyboardFocus();
    if (ele.parentNode) {
      ele.parentNode.removeChild(ele);
    }
    switch (editor.state) {
      case 'start':
        editor.inputBox.DisplayCellContents(null);
        break;
      case 'input':
      case 'inputboxdirect':
        editor.inputBox.element.disabled = false;
        editor.inputBox.Focus();
        break;
    }
  };
  SpreadsheetControlSC.SpreadsheetControl.DoLinkClear = function () {
    var spreadsheet = SocialCalc.GetSpreadsheetControlObject();
    document.getElementById(spreadsheet.idPrefix + 'linkdesc').value = '';
    document.getElementById(spreadsheet.idPrefix + 'linkpagename').value = '';
    document.getElementById(spreadsheet.idPrefix + 'linkworkspace').value = '';
    var ele = document.getElementById(spreadsheet.idPrefix + 'linkurl');
    ele.value = '';
    ele.focus();
  };
  SpreadsheetControlSC.SpreadsheetControl.DoLinkPaste = function () {
    var spreadsheet = SocialCalc.GetSpreadsheetControlObject();
    var editor = spreadsheet.editor;
    var wval = editor.workingvalues;
    var descele = document.getElementById(spreadsheet.idPrefix + 'linkdesc');
    var urlele = document.getElementById(spreadsheet.idPrefix + 'linkurl');
    var pagenameele = document.getElementById(spreadsheet.idPrefix + 'linkpagename');
    var workspaceele = document.getElementById(spreadsheet.idPrefix + 'linkworkspace');
    var formatele = document.getElementById(spreadsheet.idPrefix + 'linkformat');
    var popupele = document.getElementById(spreadsheet.idPrefix + 'linkpopup');
    var text = '';
    var ltsym, gtsym, obsym, cbsym;
    if (popupele.checked) {
      ltsym = '<<';
      gtsym = '>>';
      obsym = '[[';
      cbsym = ']]';
    } else {
      ltsym = '<';
      gtsym = '>';
      obsym = '[';
      cbsym = ']';
    }
    if (pagenameele && pagenameele.value) {
      if (workspaceele.value) {
        text = descele.value + '{' + workspaceele.value + obsym + pagenameele.value + cbsym + '}';
      } else {
        text = descele.value + obsym + pagenameele.value + cbsym;
      }
    } else {
      text = descele.value + ltsym + urlele.value + gtsym;
    }
    SpreadsheetControlSC.SpreadsheetControl.HideLink();
    switch (editor.state) {
      case 'start':
        wval.partialexpr = '';
        wval.ecoord = editor.ecell.coord;
        wval.erow = editor.ecell.row;
        wval.ecol = editor.ecell.col;
        break;
      case 'input':
      case 'inputboxdirect':
        editor.inputBox.Blur();
        editor.inputBox.ShowInputBox(false);
        editor.state = 'start';
        break;
    }
    if (formatele.checked) {
      SocialCalc.SpreadsheetControlExecuteCommand(null, 'set %C textvalueformat text-link', '');
    }
    editor.EditorSaveEdit(text);
  };
  SpreadsheetControlSC.SpreadsheetControl.DoSum = function () {
    var cmd, cell, row, col, sel, cr, foundvalue;
    var spreadsheet = SocialCalc.GetSpreadsheetControlObject();
    var editor = spreadsheet.editor;
    var sheet = editor.context.sheetobj;
    if (editor.range.hasrange) {
      sel =
        SocialCalc.crToCoord(editor.range.left, editor.range.top) +
        ':' +
        SocialCalc.crToCoord(editor.range.right, editor.range.bottom);
      cmd =
        'set ' +
        SocialCalc.crToCoord(editor.range.right, editor.range.bottom + 1) +
        ' formula sum(' +
        sel +
        ')';
    } else {
      row = editor.ecell.row - 1;
      col = editor.ecell.col;
      if (row <= 1) {
        cmd = 'set ' + editor.ecell.coord + ' constant e#REF! 0 #REF!';
      } else {
        foundvalue = false;
        while (row > 0) {
          cr = SocialCalc.crToCoord(col, row);
          cell = sheet.GetAssuredCell(cr);
          if (!cell.datatype || cell.datatype == 't') {
            if (foundvalue) {
              row++;
              break;
            }
          } else {
            foundvalue = true;
          }
          row--;
        }
        cmd =
          'set ' +
          editor.ecell.coord +
          ' formula sum(' +
          SocialCalc.crToCoord(col, row) +
          ':' +
          SocialCalc.crToCoord(col, editor.ecell.row - 1) +
          ')';
      }
    }
    editor.EditorScheduleSheetCommands(cmd, true, false);
  };
  SpreadsheetControlSC.SpreadsheetControl.FindInSheet = function () {
    var searchstatus = $('#searchstatus');
    var spreadsheet = SocialCalc.GetSpreadsheetControlObject();
    if (!this.value.length) {
      searchstatus.text('');
      spreadsheet.sheet.search_cells = [];
      spreadsheet.sheet.selected_search_cell = undefined;
      return;
    }
    var cells = spreadsheet.sheet.cells;
    var regex = new RegExp(this.value, 'im');
    var cell, cellvalue;
    var search_cells = [];
    for (var cell_id in cells) {
      cell = cells[cell_id];
      var cr = SocialCalc.coordToCr(cell_id);
      if (
        spreadsheet.sheet.rowattribs.hide[cr.row] === 'yes' ||
        spreadsheet.sheet.colattribs.hide[SocialCalc.rcColname(cr.col)] === 'yes'
      ) {
        continue;
      }
      if (cell.datatype === 'c') {
        cellvalue = cell.displaystring;
      } else {
        cellvalue = String(cell.datavalue);
      }
      if (cellvalue !== undefined && cellvalue.match(regex)) {
        search_cells.push(cell_id);
      }
    }
    spreadsheet.sheet.search_cells = search_cells;
    if (search_cells.length) {
      spreadsheet.sheet.selected_search_cell = 0;
      spreadsheet.editor.MoveECell(search_cells[0]);
      searchstatus.text('1 of ' + search_cells.length);
    } else {
      spreadsheet.sheet.selected_search_cell = undefined;
      searchstatus.text('No Matches');
    }
  };
  SpreadsheetControlSC.SpreadsheetControl.SearchSheet = function (direction) {
    var spreadsheet = SocialCalc.GetSpreadsheetControlObject();
    var sheet = spreadsheet.sheet;
    var cells = sheet.search_cells;
    if (!cells.length) {
      return;
    }
    var selected_cell = sheet.selected_search_cell;
    if (selected_cell === (direction === 0 ? 0 : cells.length - 1)) {
      selected_cell = direction === 0 ? cells.length - 1 : 0;
    } else {
      selected_cell += direction === 0 ? -1 : 1;
    }
    var new_cell = cells[selected_cell];
    sheet.selected_search_cell = selected_cell;
    spreadsheet.editor.MoveECell(new_cell);
    document.getElementById('searchstatus').textContent =
      String(selected_cell + 1) + ' of ' + cells.length;
  };
  SpreadsheetControlSC.SpreadsheetControl.SearchUp = function () {
    SpreadsheetControlSC.SpreadsheetControl.SearchSheet(0);
  };
  SpreadsheetControlSC.SpreadsheetControl.SearchDown = function () {
    SpreadsheetControlSC.SpreadsheetControl.SearchSheet(1);
  };
  SpreadsheetControlSC.SpreadsheetControlSortOnclick = function (s, _t) {
    var name, i;
    var namelist = [];
    var nl = document.getElementById(s.idPrefix + 'sortlist');
    SocialCalc.LoadColumnChoosers(s);
    s.editor.RangeChangeCallback.sort = SocialCalc.UpdateSortRangeProposal;
    for (name in s.sheet.names) {
      namelist.push(name);
    }
    namelist.sort();
    nl.length = 0;
    nl.options[0] = new Option(SocialCalc.LocalizeString('[select range]'));
    nl.options[1] = new Option(SocialCalc.LocalizeString('Sort All'), 'all');
    var n_options = nl.options.length;
    for (i = 0; i < namelist.length; i++) {
      name = namelist[i];
      nl.options[i + n_options] = new Option(name, name);
      if (name == s.sortrange) {
        nl.options[i + n_options].selected = true;
      }
    }
    if (s.sortrange == '') {
      nl.options[0].selected = true;
    }
    SocialCalc.UpdateSortRangeProposal(s.editor);
    SocialCalc.KeyboardFocus();
    return;
  };
  SpreadsheetControlSC.SpreadsheetControlSortSave = function (_editor, _setting) {
    var spreadsheet = SocialCalc.GetSpreadsheetControlObject();
    var str, sele, rele;
    str = 'sort:' + SocialCalc.encodeForSave(spreadsheet.sortrange) + ':';
    sele = document.getElementById(spreadsheet.idPrefix + 'majorsort');
    rele = document.getElementById(spreadsheet.idPrefix + 'majorsortup');
    str += sele.selectedIndex + (rele.checked ? ':up' : ':down');
    sele = document.getElementById(spreadsheet.idPrefix + 'minorsort');
    if (sele.selectedIndex > 0) {
      rele = document.getElementById(spreadsheet.idPrefix + 'minorsortup');
      str += ':' + sele.selectedIndex + (rele.checked ? ':up' : ':down');
    } else {
      str += '::';
    }
    sele = document.getElementById(spreadsheet.idPrefix + 'lastsort');
    if (sele.selectedIndex > 0) {
      rele = document.getElementById(spreadsheet.idPrefix + 'lastsortup');
      str += ':' + sele.selectedIndex + (rele.checked ? ':up' : ':down');
    } else {
      str += '::';
    }
    return str + '\n';
  };
  SpreadsheetControlSC.SpreadsheetControlSortLoad = function (_editor, _setting, line, _flags) {
    var parts;
    var ele;
    var sele;
    var spreadsheet = SocialCalc.GetSpreadsheetControlObject();
    parts = line.split(':');
    spreadsheet.sortrange = SocialCalc.decodeFromSave(parts[1]);
    ele = document.getElementById(spreadsheet.idPrefix + 'sortbutton');
    if (spreadsheet.sortrange) {
      ele.value = SocialCalc.LocalizeString('Sort ') + spreadsheet.sortrange;
      ele.style.visibility = 'visible';
    } else {
      ele.style.visibility = 'hidden';
    }
    SocialCalc.LoadColumnChoosers(spreadsheet);
    sele = document.getElementById(spreadsheet.idPrefix + 'majorsort');
    sele.selectedIndex = parts[2] - 0;
    document.getElementById(spreadsheet.idPrefix + 'majorsort' + parts[3]).checked = true;
    sele = document.getElementById(spreadsheet.idPrefix + 'minorsort');
    if (parts[4]) {
      sele.selectedIndex = parts[4] - 0;
      document.getElementById(spreadsheet.idPrefix + 'minorsort' + parts[5]).checked = true;
    } else {
      sele.selectedIndex = 0;
      document.getElementById(spreadsheet.idPrefix + 'minorsortup').checked = true;
    }
    sele = document.getElementById(spreadsheet.idPrefix + 'lastsort');
    if (parts[6]) {
      sele.selectedIndex = parts[6] - 0;
      document.getElementById(spreadsheet.idPrefix + 'lastsort' + parts[7]).checked = true;
    } else {
      sele.selectedIndex = 0;
      document.getElementById(spreadsheet.idPrefix + 'lastsortup').checked = true;
    }
    return true;
  };
  SpreadsheetControlSC.SpreadsheetControlCommentOnclick = function (s, t) {
    s.editor.MoveECellCallback.comment = SocialCalc.SpreadsheetControlCommentMoveECell;
    SocialCalc.SpreadsheetControlCommentDisplay(s, t);
    SocialCalc.KeyboardFocus();
    return;
  };
  SpreadsheetControlSC.SpreadsheetControlCommentDisplay = function (s, _t) {
    var c = '';
    if (s.editor.ecell && s.editor.ecell.coord && s.sheet.cells[s.editor.ecell.coord]) {
      c = s.sheet.cells[s.editor.ecell.coord].comment || '';
    }
    document.getElementById(s.idPrefix + 'commenttext').value = c;
  };
  SpreadsheetControlSC.SpreadsheetControlCommentMoveECell = function (_editor) {
    SocialCalc.SpreadsheetControlCommentDisplay(
      SocialCalc.GetSpreadsheetControlObject(),
      'comment',
    );
  };
  SpreadsheetControlSC.SpreadsheetControlCommentSet = function () {
    var s = SocialCalc.GetSpreadsheetControlObject();
    s.ExecuteCommand(
      'set %C comment ' +
        SocialCalc.encodeForSave(document.getElementById(s.idPrefix + 'commenttext').value),
    );
    var cell = SocialCalc.GetEditorCellElement(s.editor, s.editor.ecell.row, s.editor.ecell.col);
    if (!s.editor.ECellReadonly()) {
      cell.element.title = document.getElementById(s.idPrefix + 'commenttext').value;
      s.editor.UpdateCellCSS(cell, s.editor.ecell.row, s.editor.ecell.col);
    }
    SocialCalc.KeyboardFocus();
  };
  SpreadsheetControlSC.SpreadsheetControlCommentOnunclick = function (s, _t) {
    delete s.editor.MoveECellCallback.comment;
  };
  SpreadsheetControlSC.SpreadsheetControlNamesOnclick = function (s, _t) {
    document.getElementById(s.idPrefix + 'namesname').value = '';
    document.getElementById(s.idPrefix + 'namesdesc').value = '';
    document.getElementById(s.idPrefix + 'namesvalue').value = '';
    s.editor.RangeChangeCallback.names = SocialCalc.SpreadsheetControlNamesRangeChange;
    s.editor.MoveECellCallback.names = SocialCalc.SpreadsheetControlNamesRangeChange;
    SocialCalc.SpreadsheetControlNamesRangeChange(s.editor);
    SocialCalc.SpreadsheetControlNamesFillNameList();
    SocialCalc.SpreadsheetControlNamesChangedName();
  };
  SpreadsheetControlSC.SpreadsheetControlNamesFillNameList = function () {
    var SCLoc = SocialCalc.LocalizeString;
    var name, i;
    var namelist = [];
    var s = SocialCalc.GetSpreadsheetControlObject();
    var nl = document.getElementById(s.idPrefix + 'nameslist');
    var currentname = document
      .getElementById(s.idPrefix + 'namesname')
      .value.toUpperCase()
      .replace(/[^A-Z0-9_.]/g, '');
    for (name in s.sheet.names) {
      namelist.push(name);
    }
    namelist.sort();
    nl.length = 0;
    if (namelist.length > 0) {
      nl.options[0] = new Option(SCLoc('[New]'));
    } else {
      nl.options[0] = new Option(SCLoc('[None]'));
    }
    for (i = 0; i < namelist.length; i++) {
      name = namelist[i];
      nl.options[i + 1] = new Option(name, name);
      if (name == currentname) {
        nl.options[i + 1].selected = true;
      }
    }
    if (currentname == '') {
      nl.options[0].selected = true;
    }
  };
  SpreadsheetControlSC.SpreadsheetControlNamesChangedName = function () {
    var s = SocialCalc.GetSpreadsheetControlObject();
    var nl = document.getElementById(s.idPrefix + 'nameslist');
    var name = nl.options[nl.selectedIndex].value;
    if (s.sheet.names[name]) {
      document.getElementById(s.idPrefix + 'namesname').value = name;
      document.getElementById(s.idPrefix + 'namesdesc').value = s.sheet.names[name].desc || '';
      document.getElementById(s.idPrefix + 'namesvalue').value =
        s.sheet.names[name].definition || '';
    } else {
      document.getElementById(s.idPrefix + 'namesname').value = '';
      document.getElementById(s.idPrefix + 'namesdesc').value = '';
      document.getElementById(s.idPrefix + 'namesvalue').value = '';
    }
  };
  SpreadsheetControlSC.SpreadsheetControlNamesRangeChange = function (editor) {
    var s = SocialCalc.GetSpreadsheetControlObject();
    var ele = document.getElementById(s.idPrefix + 'namesrangeproposal');
    if (editor.range.hasrange) {
      ele.value =
        SocialCalc.crToCoord(editor.range.left, editor.range.top) +
        ':' +
        SocialCalc.crToCoord(editor.range.right, editor.range.bottom);
    } else {
      ele.value = editor.ecell.coord;
    }
  };
  SpreadsheetControlSC.SpreadsheetControlNamesOnunclick = function (s, _t) {
    delete s.editor.RangeChangeCallback.names;
    delete s.editor.MoveECellCallback.names;
  };
  SpreadsheetControlSC.SpreadsheetControlNamesSetValue = function () {
    var s = SocialCalc.GetSpreadsheetControlObject();
    document.getElementById(s.idPrefix + 'namesvalue').value = document.getElementById(
      s.idPrefix + 'namesrangeproposal',
    ).value;
    SocialCalc.KeyboardFocus();
  };
  SpreadsheetControlSC.SpreadsheetControlNamesSave = function () {
    var s = SocialCalc.GetSpreadsheetControlObject();
    var name = document.getElementById(s.idPrefix + 'namesname').value;
    SocialCalc.SetTab(s.tabs[0].name);
    SocialCalc.KeyboardFocus();
    if (name != '') {
      s.ExecuteCommand(
        'name define ' +
          name +
          ' ' +
          document.getElementById(s.idPrefix + 'namesvalue').value +
          '\n' +
          'name desc ' +
          name +
          ' ' +
          document.getElementById(s.idPrefix + 'namesdesc').value,
      );
    }
  };
  SpreadsheetControlSC.SpreadsheetControlNamesDelete = function () {
    var s = SocialCalc.GetSpreadsheetControlObject();
    var name = document.getElementById(s.idPrefix + 'namesname').value;
    SocialCalc.SetTab(s.tabs[0].name);
    SocialCalc.KeyboardFocus();
    if (name != '') {
      s.ExecuteCommand('name delete ' + name);
    }
    SocialCalc.KeyboardFocus();
  };
  SpreadsheetControlSC.SpreadsheetControlClipboardOnclick = function (s, _t) {
    s = SocialCalc.GetSpreadsheetControlObject();
    var clipele = document.getElementById(s.idPrefix + 'clipboardtext');
    document.getElementById(s.idPrefix + 'clipboardformat-tab').checked = true;
    try {
      clipele.value = SocialCalc.ConvertSaveToOtherFormat(
        SpreadsheetControlSC.Clipboard.clipboard,
        'tab',
      );
    } catch (err) {
      console.error(err);
    }
    return;
  };
  SpreadsheetControlSC.SpreadsheetControlClipboardFormat = function (which) {
    var s = SocialCalc.GetSpreadsheetControlObject();
    var clipele = document.getElementById(s.idPrefix + 'clipboardtext');
    clipele.value = SocialCalc.ConvertSaveToOtherFormat(
      SpreadsheetControlSC.Clipboard.clipboard,
      which,
    );
  };
  SpreadsheetControlSC.SpreadsheetControlClipboardLoad = function () {
    var s = SocialCalc.GetSpreadsheetControlObject();
    var savetype = 'tab';
    SocialCalc.SetTab(s.tabs[0].name);
    SocialCalc.KeyboardFocus();
    if (document.getElementById(s.idPrefix + 'clipboardformat-csv').checked) {
      savetype = 'csv';
    } else if (document.getElementById(s.idPrefix + 'clipboardformat-scsave').checked) {
      savetype = 'scsave';
    }
    s.editor.pastescclipboard = true;
    s.editor.EditorScheduleSheetCommands(
      'loadclipboard ' +
        SocialCalc.encodeForSave(
          SocialCalc.ConvertOtherFormatToSave(
            document.getElementById(s.idPrefix + 'clipboardtext').value,
            savetype,
          ),
        ),
      true,
      false,
    );
  };
  SpreadsheetControlSC.SpreadsheetControlClipboardClear = function () {
    var s = SocialCalc.GetSpreadsheetControlObject();
    var clipele = document.getElementById(s.idPrefix + 'clipboardtext');
    clipele.value = '';
    s.editor.EditorScheduleSheetCommands('clearclipboard', true, false);
    clipele.focus();
  };
  SpreadsheetControlSC.SpreadsheetControlClipboardExport = function () {
    var s = SocialCalc.GetSpreadsheetControlObject();
    if (s.ExportCallback) {
      s.ExportCallback(s);
    }
    SocialCalc.SetTab(s.tabs[0].name);
    SocialCalc.KeyboardFocus();
  };
  SpreadsheetControlSC.SpreadsheetControlSettingsSwitch = function (target) {
    SocialCalc.SettingControlReset();
    var s = SocialCalc.GetSpreadsheetControlObject();
    var sheettable = document.getElementById(s.idPrefix + 'sheetsettingstable');
    var celltable = document.getElementById(s.idPrefix + 'cellsettingstable');
    var sheettoolbar = document.getElementById(s.idPrefix + 'sheetsettingstoolbar');
    var celltoolbar = document.getElementById(s.idPrefix + 'cellsettingstoolbar');
    if (target == 'sheet') {
      sheettable.style.display = 'block';
      celltable.style.display = 'none';
      sheettoolbar.style.display = 'block';
      celltoolbar.style.display = 'none';
      SocialCalc.SettingsControlSetCurrentPanel(s.views.settings.values.sheetspanel);
    } else {
      sheettable.style.display = 'none';
      celltable.style.display = 'block';
      sheettoolbar.style.display = 'none';
      celltoolbar.style.display = 'block';
      SocialCalc.SettingsControlSetCurrentPanel(s.views.settings.values.cellspanel);
    }
  };
  SpreadsheetControlSC.SettingsControlSave = function (target) {
    var range, cmdstr;
    var s = SocialCalc.GetSpreadsheetControlObject();
    var sc = SocialCalc.SettingsControls;
    var panelobj = sc.CurrentPanel;
    var attribs = SocialCalc.SettingsControlUnloadPanel(panelobj);
    SocialCalc.SetTab(s.tabs[0].name);
    SocialCalc.KeyboardFocus();
    if (target == 'sheet') {
      cmdstr = s.sheet.DecodeSheetAttributes(attribs);
    } else if (target == 'cell') {
      if (s.editor.range.hasrange) {
        range =
          SocialCalc.crToCoord(s.editor.range.left, s.editor.range.top) +
          ':' +
          SocialCalc.crToCoord(s.editor.range.right, s.editor.range.bottom);
      }
      cmdstr = s.sheet.DecodeCellAttributes(s.editor.ecell.coord, attribs, range);
    } else {
    }
    if (cmdstr) {
      s.editor.EditorScheduleSheetCommands(cmdstr, true, false);
    }
  };
  SpreadsheetControlSC.SpreadsheetControlCreateSpreadsheetSave = function (
    spreadsheet,
    otherparts,
  ) {
    var result;
    var otherpartsstr = '';
    var otherpartsnames = '';
    var partname, extranl;
    if (otherparts) {
      for (partname in otherparts) {
        if (otherparts[partname].charAt(otherparts[partname].length - 1) != '\n') {
          extranl = '\n';
        } else {
          extranl = '';
        }
        otherpartsstr +=
          '--' +
          spreadsheet.multipartBoundary +
          '\nContent-type: text/plain; charset=UTF-8\n\n' +
          otherparts[partname] +
          extranl;
        otherpartsnames += 'part:' + partname + '\n';
      }
    }
    result =
      'socialcalc:version:1.0\n' +
      'MIME-Version: 1.0\nContent-Type: multipart/mixed; boundary=' +
      spreadsheet.multipartBoundary +
      '\n' +
      '--' +
      spreadsheet.multipartBoundary +
      '\nContent-type: text/plain; charset=UTF-8\n\n' +
      '# SocialCalc Spreadsheet Control Save\nversion:1.0\npart:sheet\npart:edit\npart:audit\n' +
      otherpartsnames +
      '--' +
      spreadsheet.multipartBoundary +
      '\nContent-type: text/plain; charset=UTF-8\n\n' +
      spreadsheet.CreateSheetSave() +
      '--' +
      spreadsheet.multipartBoundary +
      '\nContent-type: text/plain; charset=UTF-8\n\n' +
      spreadsheet.editor.SaveEditorSettings() +
      '--' +
      spreadsheet.multipartBoundary +
      '\nContent-type: text/plain; charset=UTF-8\n\n' +
      spreadsheet.sheet.CreateAuditString() +
      otherpartsstr +
      '--' +
      spreadsheet.multipartBoundary +
      '--\n';
    return result;
  };
  SpreadsheetControlSC.SpreadsheetControlDecodeSpreadsheetSave = function (spreadsheet, str) {
    var pos1,
      mpregex,
      searchinfo,
      boundary,
      boundaryregex,
      blanklineregex,
      start,
      ending,
      lines,
      i,
      line,
      p,
      pnum;
    var parts = {};
    var partlist = [];
    pos1 = str.search(/^MIME-Version:\s1\.0/im);
    if (pos1 < 0) return parts;
    mpregex = /^Content-Type:\s*multipart\/mixed;\s*boundary=(\S+)/gim;
    mpregex.lastIndex = pos1;
    searchinfo = mpregex.exec(str);
    if (!searchinfo || mpregex.lastIndex <= 0) return parts;
    boundary = searchinfo[1];
    boundaryregex = new RegExp('^--' + boundary + '(?:\r\n|\n)', 'mg');
    boundaryregex.lastIndex = mpregex.lastIndex;
    searchinfo = boundaryregex.exec(str);
    blanklineregex = /(?:\r\n|\n)(?:\r\n|\n)/gm;
    blanklineregex.lastIndex = boundaryregex.lastIndex;
    searchinfo = blanklineregex.exec(str);
    if (!searchinfo) return parts;
    start = blanklineregex.lastIndex;
    boundaryregex.lastIndex = start;
    searchinfo = boundaryregex.exec(str);
    if (!searchinfo) return parts;
    ending = searchinfo.index;
    lines = str.substring(start, ending).split(/\r\n|\n/);
    for (i = 0; i < lines.length; i++) {
      line = lines[i];
      p = line.split(':');
      switch (p[0]) {
        case 'version':
          break;
        case 'part':
          partlist.push(p[1]);
          break;
      }
    }
    for (pnum = 0; pnum < partlist.length; pnum++) {
      blanklineregex.lastIndex = ending;
      searchinfo = blanklineregex.exec(str);
      if (!searchinfo) return parts;
      start = blanklineregex.lastIndex;
      if (pnum == partlist.length - 1) {
        boundaryregex = new RegExp('^--' + boundary + '--$', 'mg');
      }
      boundaryregex.lastIndex = start;
      searchinfo = boundaryregex.exec(str);
      if (!searchinfo) return parts;
      ending = searchinfo.index;
      parts[partlist[pnum]] = {
        start,
        end: ending,
      };
    }
    return parts;
  };
  SpreadsheetControlSC.SettingsControls = {
    Controls: {},
    CurrentPanel: null,
  };
  SpreadsheetControlSC.SettingsControlSetCurrentPanel = function (panelobj) {
    SpreadsheetControlSC.SettingsControls.CurrentPanel = panelobj;
    SpreadsheetControlSC.SettingsControls.PopupChangeCallback({ panelobj }, '', null);
  };
  SpreadsheetControlSC.SettingsControlInitializePanel = function (panelobj) {
    var ctrlname, ctrl;
    var sc = SocialCalc.SettingsControls;
    for (ctrlname in panelobj) {
      if (ctrlname == 'name') continue;
      ctrl = sc.Controls[panelobj[ctrlname].type];
      if (ctrl && ctrl.Initialize) ctrl.Initialize(panelobj, ctrlname);
    }
  };
  SpreadsheetControlSC.SettingsControlLoadPanel = function (panelobj, attribs) {
    var ctrlname, ctrl;
    var sc = SocialCalc.SettingsControls;
    for (ctrlname in panelobj) {
      if (ctrlname == 'name') continue;
      ctrl = sc.Controls[panelobj[ctrlname].type];
      if (ctrl && ctrl.SetValue)
        ctrl.SetValue(panelobj, ctrlname, attribs[panelobj[ctrlname].setting]);
    }
  };
  SpreadsheetControlSC.SettingsControlUnloadPanel = function (panelobj) {
    var ctrlname, ctrl;
    var sc = SocialCalc.SettingsControls;
    var attribs = {};
    for (ctrlname in panelobj) {
      if (ctrlname == 'name') continue;
      ctrl = sc.Controls[panelobj[ctrlname].type];
      if (ctrl && ctrl.GetValue)
        attribs[panelobj[ctrlname].setting] = ctrl.GetValue(panelobj, ctrlname);
    }
    return attribs;
  };
  SpreadsheetControlSC.SettingsControls.PopupChangeCallback = function (attribs, _id, _value) {
    var sc = SocialCalc.Constants;
    var ele = document.getElementById('sample-text');
    if (!ele || !attribs || !attribs.panelobj) return;
    var idPrefix = SocialCalc.CurrentSpreadsheetControlObject.idPrefix;
    var c = attribs.panelobj.name == 'cell' ? 'c' : '';
    var v, a, parts, str1, str2;
    parts =
      sc.defaultCellLayout.match(/^padding.(\S+) (\S+) (\S+) (\S+).vertical.align.(\S+);$/) || [];
    var cv = {
      color: ['textcolor'],
      backgroundColor: ['bgcolor', '#FFF'],
      fontSize: ['fontsize', sc.defaultCellFontSize],
      fontFamily: ['fontfamily'],
      paddingTop: ['padtop', parts[1]],
      paddingRight: ['padright', parts[2]],
      paddingBottom: ['padbottom', parts[3]],
      paddingLeft: ['padleft', parts[4]],
      verticalAlign: ['alignvert', parts[5]],
    };
    for (a in cv) {
      v = SpreadsheetControlSC.Popup.GetValue(idPrefix + c + cv[a][0]) || cv[a][1] || '';
      ele.style[a] = v;
    }
    if (c == 'c') {
      cv = {
        borderTop: 'cbt',
        borderRight: 'cbr',
        borderBottom: 'cbb',
        borderLeft: 'cbl',
      };
      for (a in cv) {
        v = SpreadsheetControlSC.SettingsControls.BorderSideGetValue(attribs.panelobj, cv[a]);
        ele.style[a] = v ? v.val || '' : '';
      }
      v = SpreadsheetControlSC.Popup.GetValue(idPrefix + 'calignhoriz');
      ele.style.textAlign = v || 'left';
      ele.childNodes[1].style.textAlign = v || 'right';
    } else {
      ele.style.border = '';
      v = SpreadsheetControlSC.Popup.GetValue(idPrefix + 'textalignhoriz');
      ele.style.textAlign = v || 'left';
      v = SpreadsheetControlSC.Popup.GetValue(idPrefix + 'numberalignhoriz');
      ele.childNodes[1].style.textAlign = v || 'right';
    }
    v = SpreadsheetControlSC.Popup.GetValue(idPrefix + c + 'fontlook');
    parts = v ? v.match(/^(\S+) (\S+)$/) || [] : [];
    ele.style.fontStyle = parts[1] || '';
    ele.style.fontWeight = parts[2] || '';
    v = SpreadsheetControlSC.Popup.GetValue(idPrefix + c + 'formatnumber') || 'General';
    str1 = SocialCalc.FormatNumber.formatNumberWithFormat(9.8765, v, '');
    str2 = SocialCalc.FormatNumber.formatNumberWithFormat(-1234.5, v, '');
    if (str2 != '??-???-??&nbsp;??:??:??') {
      str1 += '<br>' + str2;
    }
    ele.childNodes[1].innerHTML = str1;
  };
  SpreadsheetControlSC.SettingsControls.PopupListSetValue = function (panelobj, ctrlname, value) {
    if (!value) {
      (function(){})(ctrlname + ' no value');
      return;
    }
    var sp = SocialCalc.Popup;
    if (!value.def) {
      sp.SetValue(panelobj[ctrlname].id, value.val);
    } else {
      sp.SetValue(panelobj[ctrlname].id, '');
    }
  };
  SpreadsheetControlSC.SettingsControls.PopupListGetValue = function (panelobj, ctrlname) {
    var ctl = panelobj[ctrlname];
    if (!ctl) return null;
    var value = SpreadsheetControlSC.Popup.GetValue(ctl.id);
    if (value) {
      return {
        def: false,
        val: value,
      };
    } else {
      return {
        def: true,
        val: 0,
      };
    }
  };
  SpreadsheetControlSC.SettingsControls.PopupListInitialize = function (panelobj, ctrlname) {
    var i, val, pos, otext;
    var sc = SocialCalc.SettingsControls;
    var initialdata =
      panelobj[ctrlname].initialdata || sc.Controls[panelobj[ctrlname].type].InitialData || '';
    initialdata = SocialCalc.LocalizeSubstrings(initialdata);
    var optionvals = initialdata.split(/\|/);
    var options = [];
    for (i = 0; i < optionvals.length; i++) {
      val = optionvals[i];
      pos = val.indexOf(':');
      otext = val.substring(0, pos);
      if (otext.indexOf('\\') != -1) {
        otext = otext.replace(/\\c/g, ':');
        otext = otext.replace(/\\b/g, '\\');
      }
      otext = SocialCalc.special_chars(otext);
      if (otext == '[custom]') {
        options[i] = {
          o: SocialCalc.Constants.s_PopupListCustom,
          v: val.substring(pos + 1),
          a: { custom: true },
        };
      } else if (otext == '[cancel]') {
        options[i] = {
          o: SocialCalc.Constants.s_PopupListCancel,
          v: '',
          a: { cancel: true },
        };
      } else if (otext == '[break]') {
        options[i] = {
          o: '-----',
          v: '',
          a: { skip: true },
        };
      } else if (otext == '[newcol]') {
        options[i] = {
          o: '',
          v: '',
          a: { newcol: true },
        };
      } else {
        options[i] = {
          o: otext,
          v: val.substring(pos + 1),
        };
      }
    }
    SpreadsheetControlSC.Popup.Create('List', panelobj[ctrlname].id, {});
    SpreadsheetControlSC.Popup.Initialize(panelobj[ctrlname].id, {
      options,
      attribs: {
        changedcallback: SpreadsheetControlSC.SettingsControls.PopupChangeCallback,
        panelobj,
      },
    });
  };
  SpreadsheetControlSC.SettingsControls.PopupListReset = function (_ctrlname) {
    SpreadsheetControlSC.Popup.Reset('List');
  };
  SpreadsheetControlSC.SettingsControls.Controls.PopupList = {
    SetValue: SpreadsheetControlSC.SettingsControls.PopupListSetValue,
    GetValue: SpreadsheetControlSC.SettingsControls.PopupListGetValue,
    Initialize: SpreadsheetControlSC.SettingsControls.PopupListInitialize,
    OnReset: SpreadsheetControlSC.SettingsControls.PopupListReset,
    ChangedCallback: null,
  };
  SpreadsheetControlSC.SettingsControls.ColorChooserSetValue = function (
    panelobj,
    ctrlname,
    value,
  ) {
    if (!value) {
      (function(){})(ctrlname + ' no value');
      return;
    }
    var sp = SocialCalc.Popup;
    if (!value.def) {
      sp.SetValue(panelobj[ctrlname].id, value.val);
    } else {
      sp.SetValue(panelobj[ctrlname].id, '');
    }
  };
  SpreadsheetControlSC.SettingsControls.ColorChooserGetValue = function (panelobj, ctrlname) {
    var value = SpreadsheetControlSC.Popup.GetValue(panelobj[ctrlname].id);
    if (value) {
      return {
        def: false,
        val: value,
      };
    } else {
      return {
        def: true,
        val: 0,
      };
    }
  };
  SpreadsheetControlSC.SettingsControls.ColorChooserInitialize = function (panelobj, ctrlname) {
    SpreadsheetControlSC.Popup.Create('ColorChooser', panelobj[ctrlname].id, {});
    SpreadsheetControlSC.Popup.Initialize(panelobj[ctrlname].id, {
      attribs: {
        title: '&nbsp;',
        moveable: true,
        width: '106px',
        changedcallback: SpreadsheetControlSC.SettingsControls.PopupChangeCallback,
        panelobj,
      },
    });
  };
  SpreadsheetControlSC.SettingsControls.ColorChooserReset = function (_ctrlname) {
    SpreadsheetControlSC.Popup.Reset('ColorChooser');
  };
  SpreadsheetControlSC.SettingsControls.Controls.ColorChooser = {
    SetValue: SpreadsheetControlSC.SettingsControls.ColorChooserSetValue,
    GetValue: SpreadsheetControlSC.SettingsControls.ColorChooserGetValue,
    Initialize: SpreadsheetControlSC.SettingsControls.ColorChooserInitialize,
    OnReset: SpreadsheetControlSC.SettingsControls.ColorChooserReset,
    ChangedCallback: null,
  };
  SpreadsheetControlSC.SettingsControls.BorderSideSetValue = function (panelobj, ctrlname, value) {
    var ele, idname, parts;
    var idstart = panelobj[ctrlname].id;
    if (!value) {
      (function(){})(ctrlname + ' no value');
      return;
    }
    ele = document.getElementById(idstart + '-onoff-bcb');
    if (!ele) return;
    if (value.val) {
      ele.checked = true;
      ele.value = value.val;
      parts = value.val.match(/(\S+)\s+(\S+)\s+(\S.+)/);
      idname = idstart + '-color';
      SpreadsheetControlSC.Popup.SetValue(idname, parts[3]);
      SpreadsheetControlSC.Popup.SetDisabled(idname, false);
    } else {
      ele.checked = false;
      ele.value = value.val;
      idname = idstart + '-color';
      SpreadsheetControlSC.Popup.SetValue(idname, '');
      SpreadsheetControlSC.Popup.SetDisabled(idname, true);
    }
  };
  SpreadsheetControlSC.SettingsControls.BorderSideGetValue = function (panelobj, ctrlname) {
    var ele, value;
    var idstart = panelobj[ctrlname].id;
    ele = document.getElementById(idstart + '-onoff-bcb');
    if (!ele) return;
    if (ele.checked) {
      value = SpreadsheetControlSC.Popup.GetValue(idstart + '-color');
      value = '1px solid ' + (value || 'rgb(0,0,0)');
      return {
        def: false,
        val: value,
      };
    } else {
      return {
        def: false,
        val: '',
      };
    }
  };
  SpreadsheetControlSC.SettingsControls.BorderSideInitialize = function (panelobj, ctrlname) {
    var idstart = panelobj[ctrlname].id;
    SpreadsheetControlSC.Popup.Create('ColorChooser', idstart + '-color', {});
    SpreadsheetControlSC.Popup.Initialize(idstart + '-color', {
      attribs: {
        title: '&nbsp;',
        width: '106px',
        moveable: true,
        changedcallback: SpreadsheetControlSC.SettingsControls.PopupChangeCallback,
        panelobj,
      },
    });
  };
  SpreadsheetControlSC.SettingsControlOnchangeBorder = function (ele) {
    var sc = SocialCalc.SettingsControls;
    var panelobj = sc.CurrentPanel;
    var nameparts = ele.id.match(/(^.*-)(\w+)-(\w+)-(\w+)$/);
    if (!nameparts) return;
    var ctrlname = nameparts[2];
    var ctrlidsuffix = nameparts[4];
    var ctrltype = panelobj[ctrlname].type;
    switch (ctrlidsuffix) {
      case 'bcb':
        if (ele.checked) {
          sc.Controls[ctrltype].SetValue(sc.CurrentPanel, ctrlname, {
            def: false,
            val: ele.value || '1px solid rgb(0,0,0)',
          });
        } else {
          sc.Controls[ctrltype].SetValue(sc.CurrentPanel, ctrlname, {
            def: false,
            val: '',
          });
        }
        break;
    }
  };
  SpreadsheetControlSC.SettingsControls.Controls.BorderSide = {
    SetValue: SpreadsheetControlSC.SettingsControls.BorderSideSetValue,
    GetValue: SpreadsheetControlSC.SettingsControls.BorderSideGetValue,
    OnClick: SpreadsheetControlSC.SettingsControls.ColorComboOnClick,
    Initialize: SpreadsheetControlSC.SettingsControls.BorderSideInitialize,
    InitialData: {
      thickness: '1 pixel:1px',
      style: 'Solid:solid',
    },
    ChangedCallback: null,
  };
  SpreadsheetControlSC.SettingControlReset = function () {
    var sc = SocialCalc.SettingsControls;
    var ctrlname;
    for (ctrlname in sc.Controls) {
      if (sc.Controls[ctrlname].OnReset) sc.Controls[ctrlname].OnReset(ctrlname);
    }
  };
  SpreadsheetControlSC.CtrlSEditor = function (whichpart) {
    var strtoedit, partname;
    if (whichpart.length > 0) {
      strtoedit = SocialCalc.special_chars(SocialCalc.OtherSaveParts[whichpart] || '');
    } else {
      strtoedit = 'Listing of Parts\n';
      for (partname in SocialCalc.OtherSaveParts) {
        strtoedit += SocialCalc.special_chars(
          '\nPart: ' + partname + '\n=====\n' + SocialCalc.OtherSaveParts[partname] + '\n',
        );
      }
    }
    var editbox = SocialCalc.document.createElement('div');
    editbox.style.cssText =
      'position:absolute;z-index:500;width:300px;height:300px;left:100px;top:200px;border:1px solid black;background-color:#EEE;text-align:center;';
    editbox.id = 'socialcalc-editbox';
    editbox.innerHTML =
      whichpart +
      '<br><br><textarea id="socialcalc-editbox-textarea" style="width:250px;height:200px;">' +
      strtoedit +
      '</textarea><br><br><input type=button ' +
      "onclick=\"SocialCalc.CtrlSEditorDone ('socialcalc-editbox', '" +
      whichpart +
      '\');" value="OK">';
    document.body.appendChild(editbox);
    var ebta = document.getElementById('socialcalc-editbox-textarea');
    ebta.focus();
    SocialCalc.CmdGotFocus(ebta);
  };
  SpreadsheetControlSC.CtrlSEditorDone = function (idprefix, whichpart) {
    var edittextarea = document.getElementById(idprefix + '-textarea');
    var text = edittextarea.value;
    if (whichpart.length > 0) {
      if (text.length > 0) {
        SocialCalc.OtherSaveParts[whichpart] = text;
      } else {
        delete SocialCalc.OtherSaveParts[whichpart];
      }
    }
    var editbox = document.getElementById(idprefix);
    SocialCalc.KeyboardFocus();
    editbox.parentNode.removeChild(editbox);
  };

  // In-place TypeScript conversion of socialcalcviewer.js (SocialCalc global script).
  // Ambient API types live in socialcalcviewer.d.ts (referenced by dist/SocialCalc.d.ts).
  // Vite+ strips types with Oxc before UMD concat — no runtime tax.
  // Typechecked global-script module (no @ts-nocheck).
  //
  // SocialCalcViewer
  //
  /*
// The code module of the SocialCalc package that lets you embed a spreadsheet viewer
// with an optional simple toolbar into a web page.
//
// (c) Copyright 2008, 2009, 2010 Socialtext, Inc.
// All Rights Reserved.
//
*/

  // Mutable progressive-init bridges for ambient nested namespaces.

  SocialCalc.LocalizeStringList = {};
  SocialCalc.CurrentSpreadsheetViewerObject = null;
  const SpreadsheetViewerCtor = function (idPrefix) {
    var scc = SocialCalc.Constants;
    this.parentNode = null;
    this.spreadsheetDiv = null;
    this.requestedHeight = 0;
    this.requestedWidth = 0;
    this.requestedSpaceBelow = 0;
    this.height = 0;
    this.width = 0;
    this.viewheight = 0;
    this.spreadsheetDiv = null;
    this.editorDiv = null;
    this.sortrange = '';
    this.idPrefix = idPrefix || 'SocialCalc-';
    this.imagePrefix = scc.defaultImagePrefix;
    this.statuslineheight = scc.SVStatuslineheight;
    this.statuslineCSS = scc.SVStatuslineCSS;
    this.sheet = new SocialCalc.Sheet();
    this.context = new SocialCalc.RenderContext(this.sheet);
    if (SocialCalc._app == true || SocialCalc._view == true)
      scc.defaultImagePrefix = this.imagePrefix = '../' + this.imagePrefix;
    if (SocialCalc._app == true) {
      this.context.showGrid = false;
      this.context.showRCHeaders = false;
      this.context.highlightTypes.range.style = '';
      this.context.sheetobj.cells['A1'] = new SocialCalc.Cell('A1');
      this.context.sheetobj.cells['A1'].displaystring = 'Loading ... ';
    } else {
      this.context.showGrid = true;
      this.context.showRCHeaders = true;
    }
    this.editor = new SocialCalc.TableEditor(this.context);
    this.editor.noEdit = true;
    this.editor.StatusCallback.statusline = {
      func: SocialCalc.SpreadsheetViewerStatuslineCallback,
      params: {},
    };
    this.hasStatusLine = true;
    this.statuslineHTML =
      '<table cellspacing="0" cellpadding="0"><tr><td width="100%" style="overflow:hidden;">{status}</td><td>&nbsp;</td></tr></table>';
    this.statuslineFull = true;
    this.noRecalc = true;
    this.repeatingMacroTimer = null;
    this.repeatingMacroInterval = 60;
    this.repeatingMacroCommands = '';
    SocialCalc.CurrentSpreadsheetViewerObject = this;
    return;
  };
  SocialCalc.SpreadsheetViewer = SpreadsheetViewerCtor;
  SocialCalc.SpreadsheetViewer.prototype.InitializeSpreadsheetViewer = function (
    node,
    height,
    width,
    spacebelow,
  ) {
    return SocialCalc.InitializeSpreadsheetViewer(this, node, height, width, spacebelow);
  };
  SocialCalc.SpreadsheetViewer.prototype.LoadSave = function (str) {
    return SocialCalc.SpreadsheetViewerLoadSave(this, str);
  };
  SocialCalc.SpreadsheetViewer.prototype.DoOnResize = function () {
    return SocialCalc.DoOnResize(this);
  };
  SocialCalc.SpreadsheetViewer.prototype.SizeSSDiv = function () {
    return SocialCalc.SizeSSDiv(this);
  };
  SocialCalc.SpreadsheetViewer.prototype.DecodeSpreadsheetSave = function (str) {
    return SocialCalc.SpreadsheetViewerDecodeSpreadsheetSave(this, str);
  };
  SocialCalc.SpreadsheetViewer.prototype.ParseSheetSave = function (str) {
    return this.sheet.ParseSheetSave(str);
  };
  SocialCalc.InitializeSpreadsheetViewer = function (spreadsheet, node, height, width, spacebelow) {
    var child;
    spreadsheet.requestedHeight = height || 0;
    spreadsheet.requestedWidth = width || 0;
    spreadsheet.requestedSpaceBelow = spacebelow || 0;
    var nodeEl = typeof node == 'string' ? document.getElementById(node) : node;
    if (nodeEl == null) {
      (function(){})('SocialCalc.SpreadsheetControl not given parent node.');
      return;
    }
    spreadsheet.parentNode = nodeEl;
    spreadsheet.spreadsheetDiv = SocialCalc.document.createElement('div');
    spreadsheet.SizeSSDiv();
    for (child = nodeEl.firstChild; child != null; child = nodeEl.firstChild) {
      nodeEl.removeChild(child);
    }
    nodeEl.appendChild(spreadsheet.spreadsheetDiv);
    spreadsheet.nonviewheight = spreadsheet.hasStatusLine ? spreadsheet.statuslineheight : 0;
    spreadsheet.viewheight = spreadsheet.height - spreadsheet.nonviewheight;
    spreadsheet.editorDiv = spreadsheet.editor.CreateTableEditor(
      spreadsheet.width,
      spreadsheet.viewheight,
    );
    if (spreadsheet.spreadsheetDiv && spreadsheet.editorDiv) {
      spreadsheet.spreadsheetDiv.appendChild(spreadsheet.editorDiv);
    }
    if (spreadsheet.hasStatusLine) {
      spreadsheet.statuslineDiv = SocialCalc.document.createElement('div');
      spreadsheet.statuslineDiv.style.cssText = spreadsheet.statuslineCSS;
      spreadsheet.statuslineDiv.style.height =
        spreadsheet.statuslineheight -
        (Number(spreadsheet.statuslineDiv.style.paddingTop.slice(0, -2)) || 0) -
        (Number(spreadsheet.statuslineDiv.style.paddingBottom.slice(0, -2)) || 0) +
        'px';
      spreadsheet.statuslineDiv.id = spreadsheet.idPrefix + 'statusline';
      spreadsheet.spreadsheetDiv.appendChild(spreadsheet.statuslineDiv);
      spreadsheet.editor.StatusCallback.statusline = {
        func: SocialCalc.SpreadsheetViewerStatuslineCallback,
        params: { spreadsheetobj: spreadsheet },
      };
    }
    if (SocialCalc._app == true) {
      spreadsheet.formDataViewer = new SocialCalc.SpreadsheetViewer('te_FormData-');
      spreadsheet.formDataViewer.sheet.statuscallback = null;
      SocialCalc.CurrentSpreadsheetViewerObject = spreadsheet;
    }
    return;
  };
  SocialCalc.SpreadsheetViewerLoadSave = function (spreadsheet, savestr) {
    var rmstr, pos, t, t2;
    var parts = spreadsheet.DecodeSpreadsheetSave(savestr);
    if (parts) {
      if (parts.sheet) {
        spreadsheet.sheet.ResetSheet();
        spreadsheet.sheet.ParseSheetSave(savestr.substring(parts.sheet.start, parts.sheet.end));
      }
      if (parts.edit) {
        spreadsheet.editor.LoadEditorSettings(savestr.substring(parts.edit.start, parts.edit.end));
      }
      if (parts.startupmacro) {
        spreadsheet.editor.EditorScheduleSheetCommands(
          savestr.substring(parts.startupmacro.start, parts.startupmacro.end),
          false,
          true,
        );
      }
      if (parts.repeatingmacro) {
        rmstr = savestr.substring(parts.repeatingmacro.start, parts.repeatingmacro.end);
        rmstr = rmstr.replace('\r', '');
        pos = rmstr.indexOf('\n');
        if (pos > 0) {
          t = rmstr.substring(0, pos) - 0;
          t2 = t;
          spreadsheet.repeatingMacroInterval = t;
          spreadsheet.repeatingMacroCommands = rmstr.substring(pos + 1);
          if (t2 > 0) {
            spreadsheet.repeatingMacroTimer = window.setTimeout(
              SocialCalc.SpreadsheetViewerDoRepeatingMacro,
              spreadsheet.repeatingMacroInterval * 1e3,
            );
          }
        }
      }
    }
    if (spreadsheet.editor.context.sheetobj.attribs.recalc == 'off' || spreadsheet.noRecalc) {
      spreadsheet.editor.ScheduleRender();
    } else {
      spreadsheet.editor.EditorScheduleSheetCommands('recalc');
    }
  };
  SocialCalc.SpreadsheetViewerDoRepeatingMacro = function () {
    var spreadsheet = SocialCalc.GetSpreadsheetViewerObject();
    var editor = spreadsheet.editor;
    spreadsheet.repeatingMacroTimer = null;
    SocialCalc.SheetCommandInfo.CmdExtensionCallbacks.repeatmacro = {
      func: SocialCalc.SpreadsheetViewerRepeatMacroCommand,
      data: null,
    };
    editor.EditorScheduleSheetCommands(spreadsheet.repeatingMacroCommands);
  };
  SocialCalc.SpreadsheetViewerRepeatMacroCommand = function (name, data, sheet, cmd, _saveundo) {
    var spreadsheet = SocialCalc.GetSpreadsheetViewerObject();
    var rest = cmd.RestOfString();
    var t = rest - 0;
    if (!(t > 0)) t = spreadsheet.repeatingMacroInterval;
    spreadsheet.repeatingMacroInterval = t;
    spreadsheet.repeatingMacroTimer = window.setTimeout(
      SocialCalc.SpreadsheetViewerDoRepeatingMacro,
      spreadsheet.repeatingMacroInterval * 1e3,
    );
  };
  SocialCalc.SpreadsheetViewerStopRepeatingMacro = function () {
    var spreadsheet = SocialCalc.GetSpreadsheetViewerObject();
    if (spreadsheet.repeatingMacroTimer) {
      window.clearTimeout(spreadsheet.repeatingMacroTimer);
      spreadsheet.repeatingMacroTimer = null;
    }
  };
  SocialCalc.SpreadsheetViewerDoButtonCmd = function (e, buttoninfo, bobj) {
    var obj = bobj.element;
    var which = bobj.functionobj.command;
    var spreadsheet = SocialCalc.GetSpreadsheetViewerObject();
    var editor = spreadsheet.editor;
    switch (which) {
      case 'recalc':
        editor.EditorScheduleSheetCommands('recalc');
        break;
      default:
        break;
    }
    if (obj && obj.blur) obj.blur();
    SocialCalc.KeyboardFocus();
  };
  SocialCalc.LocalizeString = function (str) {
    var cstr = SocialCalc.LocalizeStringList[str];
    if (!cstr) {
      cstr =
        SocialCalc.Constants[
          's_loc_' + str.toLowerCase().replace(/\s/g, '_').replace(/\W/g, 'X')
        ] || str;
      SocialCalc.LocalizeStringList[str] = cstr;
    }
    return cstr;
  };
  SocialCalc.LocalizeSubstrings = function (str) {
    var SCLoc = SocialCalc.LocalizeString;
    return str.replace(/%(loc|ssc)!(.*?)!/g, function (a, t, c) {
      if (t == 'ssc') {
        return SocialCalc.Constants[c] || (function(){})('Missing constant: ' + c);
      } else {
        return SCLoc(c);
      }
    });
  };
  SocialCalc.GetSpreadsheetViewerObject = function () {
    var csvo = SocialCalc.CurrentSpreadsheetViewerObject;
    if (csvo) return csvo;
    throw 'No current SpreadsheetViewer object.';
  };
  function SocialCalc_DoOnResize_Viewer(spreadsheet) {
    var v;
    var vname;
    var views = spreadsheet.views || {};
    var needresize = spreadsheet.SizeSSDiv();
    if (!needresize) return;
    for (vname in views) {
      v = views[vname].element;
      v.style.width = spreadsheet.width + 'px';
      v.style.height = spreadsheet.height - spreadsheet.nonviewheight + 'px';
    }
    if (SocialCalc._app) return;
    spreadsheet.editor.ResizeTableEditor(
      spreadsheet.width,
      spreadsheet.height - spreadsheet.nonviewheight,
    );
  }
  function SocialCalc_SizeSSDiv_Viewer(spreadsheet) {
    var sizes;
    var pos;
    var resized, nodestyle, newval;
    var fudgefactorX = 10;
    var fudgefactorY = 10;
    resized = false;
    if (!spreadsheet.parentNode || !spreadsheet.spreadsheetDiv) {
      return false;
    }
    sizes = SocialCalc.GetViewportInfo();
    const basePos = SocialCalc.GetElementPosition(spreadsheet.parentNode);
    pos = {
      left: basePos.left,
      top: basePos.top,
      right: 0,
      bottom: 0,
    };
    nodestyle = spreadsheet.parentNode.style;
    if (nodestyle.marginTop) {
      pos.top += Number(nodestyle.marginTop.slice(0, -2)) || 0;
    }
    if (nodestyle.marginBottom) {
      pos.bottom += Number(nodestyle.marginBottom.slice(0, -2)) || 0;
    }
    if (nodestyle.marginLeft) {
      pos.left += Number(nodestyle.marginLeft.slice(0, -2)) || 0;
    }
    if (nodestyle.marginRight) {
      pos.right += Number(nodestyle.marginRight.slice(0, -2)) || 0;
    }
    newval =
      spreadsheet.requestedHeight ||
      sizes.height - (pos.top + pos.bottom + fudgefactorY) - (spreadsheet.requestedSpaceBelow || 0);
    if (spreadsheet.height != newval) {
      spreadsheet.height = newval;
      spreadsheet.spreadsheetDiv.style.height = newval + 'px';
      resized = true;
    }
    newval =
      spreadsheet.requestedWidth || sizes.width - (pos.left + pos.right + fudgefactorX) || 700;
    if (spreadsheet.width != newval) {
      spreadsheet.width = newval;
      spreadsheet.spreadsheetDiv.style.width = newval + 'px';
      resized = true;
    }
    spreadsheet.spreadsheetDiv.style.position = 'relative';
    return resized;
  }
  SocialCalc.DoOnResize = SocialCalc_DoOnResize_Viewer;
  SocialCalc.SizeSSDiv = SocialCalc_SizeSSDiv_Viewer;
  SocialCalc.SpreadsheetViewerStatuslineCallback = function (editor, status, arg, params) {
    var spreadsheet = params.spreadsheetobj;
    var slstr = '';
    if (spreadsheet && spreadsheet.statuslineDiv) {
      if (spreadsheet.statuslineFull) {
        slstr = editor.GetStatuslineString(status, arg, params);
      } else {
        slstr = editor.ecell ? editor.ecell.coord : '';
      }
      slstr = spreadsheet.statuslineHTML.replace(/\{status\}/, slstr);
      spreadsheet.statuslineDiv.innerHTML = slstr;
    }
    switch (status) {
      case 'cmdendnorender':
      case 'calcfinished':
      case 'doneposcalc':
        break;
      default:
        break;
    }
  };
  SocialCalc.CmdGotFocus = function (obj) {
    SocialCalc.Keyboard.passThru = obj;
  };
  SocialCalc.SpreadsheetViewerCreateSheetHTML = function (spreadsheet) {
    var context, div, ele;
    var result = '';
    context = new SocialCalc.RenderContext(spreadsheet.sheet);
    div = SocialCalc.document.createElement('div');
    ele = context.RenderSheet(null, { type: 'html' });
    div.appendChild(ele);
    result = div.innerHTML;
    return result;
  };
  SocialCalc.SpreadsheetViewerDecodeSpreadsheetSave = function (spreadsheet, str) {
    var pos1,
      mpregex,
      searchinfo,
      boundary,
      boundaryregex,
      blanklineregex,
      start,
      ending,
      lines,
      i,
      p,
      pnum,
      line;
    var parts = {};
    var partlist = [];
    var hasreturnonly = /[^\n]\r[^\n]/;
    if (hasreturnonly.test(str)) {
      str = str.replace(/([^\n])\r([^\n])/g, '$1\r\n$2');
    }
    pos1 = str.search(/^MIME-Version:\s1\.0/im);
    if (pos1 < 0) return parts;
    mpregex = /^Content-Type:\s*multipart\/mixed;\s*boundary=(\S+)/gim;
    mpregex.lastIndex = pos1;
    searchinfo = mpregex.exec(str);
    if (!searchinfo || mpregex.lastIndex <= 0) return parts;
    boundary = searchinfo[1];
    boundaryregex = new RegExp('^--' + boundary + '(?:\r\n|\n)', 'mg');
    boundaryregex.lastIndex = mpregex.lastIndex;
    searchinfo = boundaryregex.exec(str);
    blanklineregex = /(?:\r\n|\n)(?:\r\n|\n)/gm;
    blanklineregex.lastIndex = boundaryregex.lastIndex;
    searchinfo = blanklineregex.exec(str);
    if (!searchinfo) return parts;
    start = blanklineregex.lastIndex;
    boundaryregex.lastIndex = start;
    searchinfo = boundaryregex.exec(str);
    if (!searchinfo) return parts;
    ending = searchinfo.index;
    lines = str.substring(start, ending).split(/\r\n|\n/);
    for (i = 0; i < lines.length; i++) {
      line = lines[i];
      p = line.split(':');
      switch (p[0]) {
        case 'version':
          break;
        case 'part':
          partlist.push(p[1]);
          break;
      }
    }
    for (pnum = 0; pnum < partlist.length; pnum++) {
      blanklineregex.lastIndex = ending;
      searchinfo = blanklineregex.exec(str);
      if (!searchinfo) return parts;
      start = blanklineregex.lastIndex;
      if (pnum == partlist.length - 1) {
        boundaryregex = new RegExp('^--' + boundary + '--$', 'mg');
      }
      boundaryregex.lastIndex = start;
      searchinfo = boundaryregex.exec(str);
      if (!searchinfo) return parts;
      ending = searchinfo.index;
      parts[partlist[pnum]] = {
        start,
        end: ending,
      };
    }
    return parts;
  };

  // Closing half of a UMD IIFE. Inlined in build.ts — not a standalone module.

  // Leading `;` defuses an ASI trap: the preceding file may end with a
  // function expression and no trailing semicolon, so a bare `(` would be
  // parsed as a call applied to that function expression.
  (function () {
    // DOM-free safety net. Full implementations live in
    // socialcalctableeditor.ts / socialcalcspreadsheetcontrol.ts and touch
    // `document`. Each wrapper checks at call time so tests that install a
    // DOM shim *after* bundle load still reach the real methods — the old
    // load-time replacement permanently stomped them once the bundle had
    // been loaded in a non-DOM context.
    var fallbacks = {
      GetEditorCellElement: function () {},
      ReplaceCell: function () {},
      EditorRenderSheet: function () {},
      SpreadsheetControlSortSave: function () {
        return '';
      },
      SpreadsheetControlStatuslineCallback: function () {},
      DoPositionCalculations: function (editor) {
        SocialCalc.EditorSheetStatusCallback(null, 'doneposcalc', null, editor);
      },
    };
    for (var name in fallbacks) {
      if (!Object.prototype.hasOwnProperty.call(fallbacks, name)) continue;
      (function (name, fallback) {
        var real = SocialCalc[name];
        SocialCalc[name] = function () {
          var fn = typeof document !== 'undefined' && real ? real : fallback;
          return fn.apply(this, arguments);
        };
      })(name, fallbacks[name]);
    }
  })();

  // Just return a value to define the module export.
  return SocialCalc;
});


    SocialCalc = module.exports;
  }).call(host);

  SocialCalc.document = SocialCalc.document || {};
  SocialCalc.document.createElement = function (tag) { return new ShimNode(tag); };

  return SocialCalc;
}
