├── README.md ├── bower.json ├── build.js ├── examples ├── cell.lookup.html ├── cell.value.behavior.html ├── cells.celltype.html ├── cells.hidden.html ├── cells.merged.html ├── charts.html ├── custom.functions.html ├── dates.html ├── dts.test.html ├── empty.html ├── events.html ├── extream.lazy.load.sheet.test.html ├── financial.html ├── formula.torture.test.html ├── globalize.html ├── information.html ├── inputs.get.html ├── inputs.html ├── juggling.types.html ├── killall.html ├── lazy.load.sheet.test.html ├── lazy.load.test.html ├── logical.html ├── math.html ├── mild.stress.test.html ├── moderate.stress.test.html ├── mortgage.estimator.html ├── not.editable.html ├── readonly.html ├── schedule.html ├── sort.html ├── statistical.html ├── string.html ├── toggle.state.html └── tsv.tester.html ├── images ├── fork_me.png └── svg │ ├── a2.svg │ ├── bold13.svg │ ├── centered5.svg │ ├── color6.svg │ ├── eye48.svg │ ├── fill1.svg │ ├── font.svg │ ├── full40.svg │ ├── human90.svg │ ├── italic3.svg │ ├── justify9.svg │ ├── left181.svg │ ├── link23.svg │ ├── logo.svg │ ├── minus11.svg │ ├── object10.svg │ ├── object11.svg │ ├── object8.svg │ ├── paint2.svg │ ├── paint45.svg │ ├── paint57.svg │ ├── plus7.svg │ ├── printer70.svg │ ├── right183.svg │ ├── strikethrough.svg │ ├── two258.svg │ ├── two266.svg │ ├── underline1.svg │ └── zoom19.svg ├── index.html ├── legacy-tests ├── cells.adding.js ├── cells.deleting.js ├── cells.disappearing_dependency.js ├── cells.formula_financial.js ├── cells.formula_shifting.js ├── cells.hidden.js ├── cells.highlighting.js ├── cells.inputs.js ├── cells.merging.js ├── cells.types.js ├── cells.value_behaviour.js ├── empty.html ├── formula.functions.js ├── formula.js ├── index.html ├── loader.json.js └── sheet.instantiation.js ├── menu.html ├── menu2.html ├── package.json ├── parser ├── formula │ ├── README.md │ ├── formula.jison │ ├── formula.js │ └── formula.php └── tsv │ ├── tsv.jison │ └── tsv.js ├── src ├── WickedGrid.js ├── WickedGrid │ ├── ActionUI.js │ ├── Cell.js │ ├── CellContextMenu.js │ ├── CellHandler.js │ ├── CellRange.js │ ├── CellTypeHandlers.js │ ├── ColumnContextMenu.js │ ├── ColumnFreezer.js │ ├── Event │ │ ├── cell.js │ │ ├── document.js │ │ └── formula.js │ ├── Fullscreen.js │ ├── Functions.js │ ├── Highlighter.js │ ├── JSONStreamer.js │ ├── Loader │ │ ├── HTML.js │ │ ├── JSON.js │ │ └── XML.js │ ├── Plugin │ │ ├── advanced.js │ │ └── finance.js │ ├── RowContextMenu.js │ ├── RowFreezer.js │ ├── SpreadsheetUI.js │ ├── Theme.js │ ├── Undo.js │ ├── autoFiller.js │ ├── cl.js │ ├── columnMenu.js │ ├── columnResizer.js │ ├── customTab.js │ ├── defaults.js │ ├── enclosure.js │ ├── formulaEditor.js │ ├── header.js │ ├── inPlaceEdit.js │ ├── menu.js │ ├── rowResizer.js │ ├── sheetUI.js │ ├── spreadsheetAdder.js │ ├── statics.js │ ├── tab.js │ ├── tabs.js │ ├── thread.js │ ├── threadScope.js │ ├── ui.js │ └── utilities.js ├── environmentCorrection.js └── jQuery.WickedGrid.js ├── test ├── mocha.opts ├── specs │ └── WickedGrid.js └── test.js ├── utilities ├── tableify.js ├── trace.html └── trace.js ├── wickedgrid.css ├── wickedgrid.js ├── wickedgrid.json └── wickedgrid.min.js /README.md: -------------------------------------------------------------------------------- 1 | [![Stories in Ready](https://badge.waffle.io/spreadsheets/jquery.sheet.png?label=ready&title=Ready)](https://waffle.io/spreadsheets/WickedGrid) 2 | WickedGrid 3 | ============ 4 | 5 | [![Join the chat at https://gitter.im/Spreadsheets/WickedGrid](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Spreadsheets/WickedGrid?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 6 | 7 | #faster than native spreadsheets?... coming soon :) 8 | 9 | the ajax spreadsheet 10 | 11 | [Demo](http://spreadsheets.github.io/WickedGrid) 12 | 13 | [3.1 Documentation](http://visop-dev.com/doc/js3/index.html) 14 | 15 | [![tip for next commit](http://prime4commit.com/projects/174.svg)](http://prime4commit.com/projects/174) 16 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wicked-grid", 3 | "version": "master", 4 | "homepage": "https://github.com/Spreadsheets/WickedGrid", 5 | "authors": [ 6 | "Robert Plummer " 7 | ], 8 | "description": "The Ajax Spreadsheet", 9 | "main": "jquery.sheet.js", 10 | "keywords": [ 11 | "spreadsheet", 12 | "sheet", 13 | "jquery", 14 | "javascript", 15 | "excel", 16 | "csv", 17 | "json", 18 | "xml", 19 | "cloud", 20 | "spreadsheets" 21 | ], 22 | "license": "MIT", 23 | "ignore": [ 24 | "**/.*", 25 | "node_modules", 26 | "bower_components", 27 | "test", 28 | "tests" 29 | ], 30 | "dependencies": { 31 | "jquery": "~2.1.4", 32 | "jquery-nearest": "~1.3.0", 33 | "globalize": "~0.1.1", 34 | "zeroclipboard": "~2.1.6", 35 | "raphael": "~2.1.2", 36 | "graphael": "master", 37 | "jquery-elastic": "https://github.com/janjarfalk/jquery-elastic/releases/tag/1.6.10", 38 | "really-simple-color-picker": "https://github.com/laktek/really-simple-color-picker.git", 39 | "Javascript-Undo-Manager": "*", 40 | "ThemeSwitcher": "https://github.com/josephbulger/ThemeSwitcher.git", 41 | "jquery-ui": "~1.11.4", 42 | "MouseWheel": "https://github.com/robertleeplummerjr/MouseWheel.git", 43 | "bootstrap": "~3.2.0", 44 | "thaw.js": "~1.2.0", 45 | "testify.js": "*", 46 | "operative": "~0.4.0", 47 | "megatable.js": "*", 48 | "infiniscroll.js": "*", 49 | "font-awesome": "~4.5.0" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /build.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | , execSync = require('child_process').execSync 3 | , uglify = require('uglify-js') 4 | 5 | //directories of the JS files 6 | , inDir = './src/' 7 | , outDir = '' 8 | 9 | //these are the paths to the final combined files that you want to have in the end 10 | , temp = '' 11 | , result = '' 12 | , wrapper = 'WickedGrid' 13 | , combinedFile = outDir + 'wickedgrid' 14 | , combinedFileMin = outDir + 'wickedgrid.min' 15 | 16 | , files = [ 17 | //non-constructors next 18 | { 19 | WickedGrid: [ 20 | //statics off of WickedGrid 21 | 'statics', 22 | 23 | 'autoFiller', 24 | 'cl', 25 | 'columnMenu', 26 | 'columnResizer', 27 | 'customTab', 28 | 'defaults', 29 | 'enclosure', 30 | 'formulaEditor', 31 | 'header', 32 | 'inPlaceEdit', 33 | 'menu', 34 | 'rowResizer', 35 | 'sheetUI', 36 | 'spreadsheetAdder', 37 | 'tab', 38 | 'tabs', 39 | 'thread', 40 | 'ui', 41 | 'utilities', 42 | 43 | //children namespaces next 44 | { 45 | Event: ['cell', 'document', 'formula'] 46 | }, 47 | { 48 | Loader: ['HTML', 'JSON', 'XML'] 49 | }, 50 | 51 | //constructors next 52 | 'ActionUI', 53 | 'Cell', 54 | 'CellContextMenu', 55 | 'CellHandler', 56 | 'CellRange', 57 | 'CellTypeHandlers', 58 | 'ColumnContextMenu', 59 | 'ColumnFreezer', 60 | 'Functions', 61 | 'Highlighter', 62 | 'SpreadsheetUI', 63 | 'RowContextMenu', 64 | 'RowFreezer', 65 | 'Theme', 66 | 'Undo' 67 | ] 68 | }, 69 | 70 | //jQuery plugin 71 | 'jQuery.WickedGrid', 72 | 73 | //environment correction last 74 | 'environmentCorrection' 75 | ] 76 | , parserFiles = [ 77 | 'parser/formula/formula', 78 | 'parser/tsv/tsv' 79 | ] 80 | ; 81 | 82 | function pathify(path, cb, prefix) { 83 | prefix = prefix || ''; 84 | if (path.constructor === Array) { 85 | path.forEach(function(file) { 86 | pathify(file, cb, prefix); 87 | }); 88 | } else if (typeof path === 'object') { 89 | for (var p in path) { 90 | if (path.hasOwnProperty(p)) { 91 | pathify(path[p], cb, prefix + (prefix ? '/' : '') + p + '/'); 92 | } 93 | } 94 | } else if (typeof path === 'string') { 95 | cb(prefix + path); 96 | } 97 | } 98 | 99 | //run through the JS files 100 | pathify(files, function(file) { 101 | temp += fs.readFileSync(inDir + file + '.js', 'utf8').toString() + '\n'; 102 | }); 103 | 104 | result = fs.readFileSync(inDir + wrapper + '.js', 'utf8').toString() 105 | .replace('\'CODE_HERE\'', function() { 106 | return temp; 107 | }); 108 | 109 | //run through the parser files 110 | parserFiles.forEach(function(file) { 111 | result += fs.readFileSync(file + '.js', 'utf8').toString(); 112 | }); 113 | 114 | fs.writeFileSync(combinedFile + '.js', result); 115 | 116 | //compress it 117 | 118 | //add the file to the git base 119 | execSync('git add ' + combinedFile + '.js'); 120 | 121 | 122 | var tiny = uglify.minify([combinedFile + '.js'], { 123 | compress: { 124 | dead_code: true, 125 | global_defs: { 126 | DEBUG: false 127 | } 128 | } 129 | }); 130 | 131 | 132 | fs.writeFileSync(combinedFileMin + '.js', tiny.code); 133 | execSync('git add ' + combinedFileMin + '.js'); 134 | process.exit(0); 135 | -------------------------------------------------------------------------------- /examples/cell.value.behavior.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | jQuery.sheet - The Ajax Spreadsheet 10 | 11 | 12 | 13 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 60 | 64 | 65 | 66 | 67 | 68 |
FunctionExecutionDesired ResultCorrect?Notes
Bracket Handling<b>b</b>
Concat with formatDollar Amount: 200For now concatenation makes formatting cells somewhat difficult, so for the time being the formatting is lost on concatenation. Same as example below.
Concat operator with formatDollar Amount: 200
Line BreaksTest 61 | 62 | 63 | Test
69 |
70 | 71 | -------------------------------------------------------------------------------- /examples/cells.celltype.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | jQuery.sheet - The Ajax Spreadsheet 10 | 11 | 12 | 13 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
Cell TypeFormatted Result
None9999
Number9999
Date1/1/2001
Number100
Percent.2344
48 |
49 | 50 | -------------------------------------------------------------------------------- /examples/cells.hidden.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | jQuery.sheet - The Ajax Spreadsheet 10 | 11 | 12 | 13 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 |
62 |
63 | -------------------------------------------------------------------------------- /examples/cells.merged.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | jQuery.sheet - The Ajax Spreadsheet 10 | 11 | 12 | 13 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 |
Merged
59 |
60 | -------------------------------------------------------------------------------- /examples/charts.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | jQuery.sheet - The Ajax Spreadsheet 10 | 11 | 12 | 13 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 |
Chart TypeExampleChartSample DataSample LegendsSample Years
Simple Vertical Bar Chart"=BARCHART(D2:D8)"1Nov2006
Simple Horizontal Bar Chart"=HBARCHART(D2:D8)"2Dec2007
Simple Line Chart"=LINECHART(D2:D8, F2:F6)"5Mar2008
Simple Pie Chart"=PIECHART(D2:D8)"7May2009
7.5June2010
8July2011
8August2012
98 |
99 | 100 | -------------------------------------------------------------------------------- /examples/custom.functions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 25 | 26 | 27 |
28 | 29 | 30 | 31 | 32 |
33 |
34 | 35 | 36 | -------------------------------------------------------------------------------- /examples/empty.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 18 | 19 | 20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /examples/events.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 85 | 86 | 87 | 88 | 89 | 98 | 99 | 100 |
90 |
91 | 92 | 93 | 94 | 95 |
96 |
97 |
101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /examples/extream.lazy.load.sheet.test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | jQuery.sheet - The Ajax Spreadsheet DTS Lazy Loading 10 | 19 | 20 | 21 | 22 | 90 | 91 | 92 |
93 | 94 | 95 | -------------------------------------------------------------------------------- /examples/financial.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | jQuery.sheet - The Ajax Spreadsheet 10 | 11 | 12 | 13 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 |
FunctionExecutionDesired ResultCorrect?Notes
FV12298.46381980343
IPMT-292.4471299093658
IPMT-70.96865039555856
IPMT-208.33333333333334
NPER59.67386567429457
NPER60.08212285376166
NPV1188.4434123352207
PMT-1037.0320893591604
PV-5555.605845933733
PV-9252.072719338317
RATE0.015130843902309656
110 |
111 | 112 | -------------------------------------------------------------------------------- /examples/formula.torture.test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | jQuery.sheet - The Ajax Spreadsheet 10 | 11 | 12 | 13 | 75 | 76 | 77 |
78 | 79 | -------------------------------------------------------------------------------- /examples/globalize.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | jQuery.sheet - The Ajax Spreadsheet 10 | 11 | 12 | 13 | 33 | 34 | 35 | Language: 36 |
37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 |
Format TypeExecutionDesired ResultCorrect?Notes
DATE
NUMBER
62 |
63 | 64 | -------------------------------------------------------------------------------- /examples/information.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | jQuery.sheet - The Ajax Spreadsheet 10 | 11 | 12 | 13 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 |
FunctionExecutionDesired ResultCorrect?Notes
ISNUMBER
ISNUMBER
47 |
48 | 49 | -------------------------------------------------------------------------------- /examples/inputs.get.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | jQuery.sheet - The Ajax Spreadsheet 10 | 11 | 12 | 13 | 47 | 48 | 49 |
50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 |
Input NameValue
59 | 60 | -------------------------------------------------------------------------------- /examples/inputs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | jQuery.sheet - The Ajax Spreadsheet 10 | 11 | 12 | 13 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 |
Inputs are for capturing fixed data, such as a drop down list (DROPDOWN), or a checkbox (CHECKBOX)
Input TypeExampleData NumberData String
Select List"=DROPDOWN(D3:D10)"434Lorem
Radio List"=RADIO(E3:E10)"Donec-20Proin
Checkbox"=CHECKBOX(E3)"true123Aliquam
Get Select List Value"=C3"4123Quisque
Get Radio List Value"=C4"Donec4Aliquam
Get Checkbox Value"=C5"Lorem534456Vivamus
3Etiam
1Donec
103 |
104 | 105 | -------------------------------------------------------------------------------- /examples/killall.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | jQuery.sheet - The Ajax Spreadsheet 10 | 11 | 12 | 13 | 38 | 39 | 40 |
41 | 42 |
43 | 44 | -------------------------------------------------------------------------------- /examples/logical.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | jQuery.sheet - The Ajax Spreadsheet 10 | 11 | 12 | 13 | 14 | 15 | 40 | 41 | 42 |
43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 |
FunctionExecutionDesired ResultCorrect?Notes
AND
AND
FALSE
TRUE
IF
IF
NOT
NOT
OR
OR
ISBLANK
ISBLANK
ISBLANK
ISBLANK
ISBLANK
159 |
160 | 161 | -------------------------------------------------------------------------------- /examples/moderate.stress.test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | jQuery.sheet - The Ajax Spreadsheet 10 | 11 | 12 | 33 | 34 | 35 |
36 | 37 | -------------------------------------------------------------------------------- /examples/schedule.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 54 | 55 | 56 |
57 | 58 | 59 | -------------------------------------------------------------------------------- /examples/statistical.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | jQuery.sheet - The Ajax Spreadsheet 10 | 11 | 12 | 13 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 |
FunctionExecutionDesired ResultCorrect?Notes
AVERAGE858
MAX9999
MIN1
STDEV2.3334523779156067
61 |
62 | 63 | -------------------------------------------------------------------------------- /examples/toggle.state.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | jQuery.sheet - The Ajax Spreadsheet 10 | 11 | 12 | 13 | 29 | 30 | 31 | 32 |
33 | 34 | -------------------------------------------------------------------------------- /examples/tsv.tester.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 |
TestResult
79 | 80 | 90 | 91 | -------------------------------------------------------------------------------- /images/fork_me.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spreadsheets/WickedGrid/7450a6fe386eb2e3765d499a22b9e3b7c37da7d5/images/fork_me.png -------------------------------------------------------------------------------- /images/svg/a2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /images/svg/bold13.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /images/svg/centered5.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /images/svg/color6.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 9 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /images/svg/eye48.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 15 | 16 | -------------------------------------------------------------------------------- /images/svg/fill1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 14 | 15 | -------------------------------------------------------------------------------- /images/svg/font.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 21 | 22 | -------------------------------------------------------------------------------- /images/svg/full40.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /images/svg/human90.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 16 | 17 | -------------------------------------------------------------------------------- /images/svg/italic3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /images/svg/justify9.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /images/svg/left181.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /images/svg/link23.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /images/svg/minus11.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 8 | 9 | -------------------------------------------------------------------------------- /images/svg/object10.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 8 | 9 | -------------------------------------------------------------------------------- /images/svg/object11.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 8 | 9 | -------------------------------------------------------------------------------- /images/svg/object8.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /images/svg/paint2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 17 | 18 | -------------------------------------------------------------------------------- /images/svg/paint45.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 14 | 15 | -------------------------------------------------------------------------------- /images/svg/paint57.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 21 | 22 | -------------------------------------------------------------------------------- /images/svg/plus7.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /images/svg/printer70.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /images/svg/right183.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /images/svg/strikethrough.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 12 | 13 | -------------------------------------------------------------------------------- /images/svg/two258.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 9 | 10 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /images/svg/two266.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /images/svg/underline1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /images/svg/zoom19.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 12 | 13 | -------------------------------------------------------------------------------- /legacy-tests/cells.adding.js: -------------------------------------------------------------------------------- 1 | var rowHeadersCount = '12345678910111213141516171819202122232425262728293031323334353637383940', 2 | ColumnHeadersCount = 'ABCDEFGHIJKLMNOPQRSTUVWXYZAAABACADAEAFAGAHAI' 3 | 4 | 5 | tf.test('Behaviour Testing: Adding Rows @ Start', function(tf) { 6 | var div = $('#sheet') 7 | .append(tableify('original')), 8 | td, 9 | headers, 10 | wg = setup(div[0]), 11 | cell = wg.getCell(0, 0, 0); 12 | wg.addRow(); 13 | 14 | td = div.find('table.wg-table td:contains("original")'); 15 | 16 | headers = div.find('table.wg-table tr:not(:first) th'); 17 | 18 | tf.assertEquals(td[0].parentNode.rowIndex, 2, 'original @ expected location'); 19 | tf.assertEquals(td[0].parentNode.previousSibling.rowIndex, 1, 'added @ expected location'); 20 | tf.assertEquals(headers.text(), rowHeadersCount, 'headers are correct'); 21 | wg.kill(); 22 | }); 23 | 24 | tf.test('Behaviour Testing: Adding Rows @ After 0', function(tf) { 25 | var div = $('#sheet') 26 | .html(tableify('original')), 27 | wg = setup(div[0]), 28 | td, 29 | headers; 30 | 31 | wg.addRow(); 32 | wg.addRow(true, 0); 33 | 34 | td = div.find('table.wg-table td:contains("original")'); 35 | 36 | headers = div.find('table.wg-table tr:not(:first) th'); 37 | 38 | tf.assertEquals(td[0].parentNode.rowIndex, 3, 'original @ expected location'); 39 | tf.assertEquals(td[0].parentNode.previousSibling.rowIndex, 2, 'added @ expected location'); 40 | tf.assertEquals(headers.text(), rowHeadersCount, 'headers are correct'); 41 | wg.kill(); 42 | }); 43 | 44 | tf.test('Behaviour Testing: Adding Rows @ Before 3', function(tf) { 45 | var div = $('#sheet') 46 | .html(tableify('original1\n\ 47 | original2' 48 | )), 49 | wg = setup(div[0]), 50 | td, 51 | headers; 52 | 53 | wg.addRow(true, 0); 54 | 55 | td = div.find('table.wg-table td:contains("original1")'); 56 | 57 | headers = div.find('table.wg-table tr:not(:first) th'); 58 | 59 | tf.assertEquals(td[0].parentNode.rowIndex, 1, 'original @ expected location'); 60 | tf.assertEquals(td.parent().next().children().filter('td').html(), '', 'added @ expected location'); 61 | tf.assertEquals(headers.text(), rowHeadersCount, 'headers are correct'); 62 | wg.kill(); 63 | }); 64 | 65 | tf.test('Behaviour Testing: Adding Columns @ Start', function(tf) { 66 | var div = $('#sheet') 67 | .html(tableify('original')), 68 | wg = setup(div[0]), 69 | td, 70 | headers; 71 | 72 | wg.addColumn(); 73 | 74 | td = div.find('table.wg-table td:contains("original")'); 75 | 76 | headers = div.find('table.wg-table tr:first th'); 77 | 78 | tf.assertEquals(td[0].cellIndex, 2, 'original @ expected location'); 79 | tf.assertEquals(td[0].previousSibling.cellIndex, 1, 'added @ expected location'); 80 | tf.assertEquals(headers.text(), ColumnHeadersCount, 'headers are correct'); 81 | wg.kill(); 82 | 83 | }); 84 | 85 | 86 | tf.test('Behaviour Testing: Adding Columns @ After 1', function(tf) { 87 | var div = $('#sheet') 88 | .html(tableify('original')), 89 | wg = setup(div[0]), 90 | td, 91 | headers; 92 | 93 | wg.addColumn(true, 0); 94 | 95 | td = div.find('table.wg-table td:contains("original")'); 96 | 97 | headers = div.find('table.wg-table tr:first th'); 98 | 99 | tf.assertEquals(td[0].cellIndex, 1, 'original @ expected location'); 100 | tf.assertNotEquals(td[0].nextSibling, null, 'added @ expected location'); 101 | tf.assertEquals(headers.text(), ColumnHeadersCount, 'headers are correct'); 102 | wg.kill(); 103 | 104 | }); 105 | 106 | tf.test('Behaviour Testing: Adding Columns @ Before 2', function(tf) { 107 | var div = $('#sheet') 108 | .html(tableify('original1\toriginal2')), 109 | wg = setup(div[0]), 110 | td, 111 | headers; 112 | 113 | wg.addColumn(true, 0); 114 | 115 | td = div.find('table.wg-table td:contains("original1")'); 116 | 117 | headers = div.find('table.wg-table tr:first th'); 118 | 119 | tf.assertEquals(td[0].cellIndex, 1, 'original @ expected location'); 120 | tf.assertEquals(td.next().html(), '', 'added @ expected location'); 121 | tf.assertEquals(headers.text(), ColumnHeadersCount, 'headers are correct'); 122 | wg.kill(); 123 | }); -------------------------------------------------------------------------------- /legacy-tests/cells.deleting.js: -------------------------------------------------------------------------------- 1 | tf.test('Behaviour Testing: Deleting Row @ Beginning', function(tf) { 2 | var div = $('
') 3 | .append(tableify('original1\toriginal2\toriginal3\n\ 4 | original4\toriginal5\toriginal6\n\ 5 | original7\toriginal8\toriginal9')) 6 | .sheet(), 7 | jS = div.getSheet(), 8 | td; 9 | 10 | jS.deleteRow(1); 11 | 12 | td = div.find('table.jS td:contains("original1")'); 13 | 14 | tf.assertEquals(td.length, 0, 'correct row deleted'); 15 | tf.assertEquals(div.find('table.jS td').length, 2 * 3, 'cell count is correct'); 16 | div.getSheet().kill(); 17 | }); 18 | 19 | tf.test('Behaviour Testing: Deleting Row @ Middle', function(tf) { 20 | var div = $('
') 21 | .append(tableify('original1\toriginal2\toriginal3\n\ 22 | original4\toriginal5\toriginal6\n\ 23 | original7\toriginal8\toriginal9')) 24 | .sheet(), 25 | jS = div.getSheet(), 26 | td; 27 | 28 | jS.deleteRow(2); 29 | 30 | td = div.find('table.jS td:contains("original4")'); 31 | 32 | tf.assertEquals(td.length, 0, 'correct row deleted'); 33 | tf.assertEquals(div.find('table.jS td').length, 2 * 3, 'cell count is correct'); 34 | div.getSheet().kill(); 35 | }); 36 | 37 | tf.test('Behaviour Testing: Deleting Row @ End', function(tf) { 38 | var div = $('
') 39 | .append(tableify('original1\toriginal2\toriginal3\n\ 40 | original4\toriginal5\toriginal6\n\ 41 | original7\toriginal8\toriginal9')) 42 | .sheet(), 43 | jS = div.getSheet(), 44 | td; 45 | 46 | jS.deleteRow(3); 47 | 48 | td = div.find('table.jS td:contains("original9")'); 49 | 50 | tf.assertEquals(td.length, 0, 'correct row deleted'); 51 | tf.assertEquals(div.find('table.jS td').length, 2 * 3, 'cell count is correct'); 52 | div.getSheet().kill(); 53 | }); 54 | 55 | tf.test('Behaviour Testing: Deleting Column @ Beginning', function(tf) { 56 | var div = $('
') 57 | .append(tableify('original1\toriginal2\toriginal3\n\ 58 | original4\toriginal5\toriginal6\n\ 59 | original7\toriginal8\toriginal9')) 60 | .sheet(), 61 | jS = div.getSheet(), 62 | td; 63 | 64 | jS.deleteColumn(1); 65 | 66 | td = div.find('table.jS td:contains("original1")'); 67 | 68 | tf.assertEquals(td.length, 0, 'correct column deleted'); 69 | tf.assertEquals(div.find('table.jS td').length, 2 * 3, 'cell count is correct'); 70 | div.getSheet().kill(); 71 | 72 | }); 73 | 74 | tf.test('Behaviour Testing: Deleting Column @ Middle', function(tf) { 75 | var div = $('
') 76 | .append(tableify('original1\toriginal2\toriginal3\n\ 77 | original4\toriginal5\toriginal6\n\ 78 | original7\toriginal8\toriginal9')) 79 | .sheet(), 80 | jS = div.getSheet(), 81 | td; 82 | 83 | jS.deleteColumn(2); 84 | 85 | td = div.find('table.jS td:contains("original5")'); 86 | 87 | tf.assertEquals(td.length, 0, 'correct column deleted'); 88 | tf.assertEquals(div.find('table.jS td').length, 2 * 3, 'cell count is correct'); 89 | div.getSheet().kill(); 90 | 91 | }); 92 | 93 | tf.test('Behaviour Testing: Deleting Column @ End', function(tf) { 94 | var div = $('
') 95 | .append(tableify('original1\toriginal2\toriginal3\n\ 96 | original4\toriginal5\toriginal6\n\ 97 | original7\toriginal8\toriginal9')) 98 | .sheet(), 99 | jS = div.getSheet(), 100 | td; 101 | 102 | jS.deleteColumn(3); 103 | 104 | td = div.find('table.jS td:contains("original3")'); 105 | tf.assertEquals(td.length, 0, 'correct column deleted'); 106 | tf.assertEquals(div.find('table.jS td').length, 2 * 3, 'cell count is correct'); 107 | div.getSheet().kill(); 108 | }); -------------------------------------------------------------------------------- /legacy-tests/cells.disappearing_dependency.js: -------------------------------------------------------------------------------- 1 | tf.test('Disappearing Dependency: Column 3 Disappears', function(tf) { 2 | var div = $('
') 3 | .append(tableify('=C1\t \t \n\ 4 | =C2\t \t \n\ 5 | =C3\t \t \n')) 6 | .sheet(), 7 | jS = div.getSheet(), 8 | td; 9 | 10 | jS.deleteColumn(3); 11 | 12 | tf.assertEquals(jS.getCell(0,1,1).formula, '#REF!1', 'Formula shifted correctly'); 13 | tf.assertEquals(jS.getCell(0,2,1).formula, '#REF!2', 'Formula shifted correctly'); 14 | tf.assertEquals(jS.getCell(0,3,1).formula, '#REF!3', 'Formula shifted correctly'); 15 | tf.assertEquals(div.find('table.jS td').length, 2 * 3, 'cell count is correct'); 16 | div.getSheet().kill(); 17 | }); 18 | 19 | tf.test('Disappearing Dependency: Row 3 Disappears', function(tf) { 20 | var div = $('
') 21 | .append(tableify('=A3\t=B3\t=C3\n\ 22 | \t \t \n\ 23 | \t \t \n')) 24 | .sheet(), 25 | jS = div.getSheet(), 26 | td; 27 | 28 | jS.deleteRow(3); 29 | 30 | tf.assertEquals(jS.getCell(0,1,1).formula, 'A#REF!', 'Formula shifted correctly'); 31 | tf.assertEquals(jS.getCell(0,1,2).formula, 'B#REF!', 'Formula shifted correctly'); 32 | tf.assertEquals(jS.getCell(0,1,3).formula, 'C#REF!', 'Formula shifted correctly'); 33 | tf.assertEquals(div.find('table.jS td').length, 2 * 3, 'cell count is correct'); 34 | div.getSheet().kill(); 35 | }); 36 | 37 | tf.test('Disappearing Dependency: Column & Row (C3 Disappears)', function(tf) { 38 | var div = $('
') 39 | .append(tableify('=C3\t \t \n\ 40 | \t \t \n\ 41 | \t \t \n')) 42 | .sheet(), 43 | jS = div.getSheet(), 44 | td; 45 | 46 | jS.deleteRow(3); 47 | jS.deleteColumn(3); 48 | 49 | tf.assertEquals(jS.getCell(0,1,1).formula, '#REF!#REF!', 'Formula shifted correctly'); 50 | tf.assertEquals(div.find('table.jS td').length, 2 * 2, 'cell count is correct'); 51 | div.getSheet().kill(); 52 | }); -------------------------------------------------------------------------------- /legacy-tests/cells.formula_financial.js: -------------------------------------------------------------------------------- 1 | tf.test('Formula Financial: FV', function() { 2 | $('#sheet').html(""); 3 | var table = tableify('=FV(7.5%/12, 2*12, -250, -5000, 1)'), 4 | div = $('#sheet') 5 | .append(table), 6 | td, 7 | headers, 8 | wg = setup(div[0]), 9 | cell = wg.getCell(0, 0, 0); 10 | 11 | cell.updateValue(); 12 | 13 | tf.assertEquals(cell.value.valueOf().toFixed(2), "12298.46", 'Correct value'); 14 | wg.kill(); 15 | }); -------------------------------------------------------------------------------- /legacy-tests/cells.highlighting.js: -------------------------------------------------------------------------------- 1 | 2 | tf.test('Behaviour Testing: Highlighting Rows', function(tf) { 3 | $('#sheet').html(""); 4 | var div = $('#sheet') 5 | .append(tableify('original1\t\toriginal2')), 6 | td, 7 | headers, 8 | wg = setup(div[0]), 9 | cell = wg.getCell(0, 0, 0); 10 | 11 | td = div.find('table.wg-table td:contains("original")'); 12 | $(td[0]).mousedown(); 13 | tf.assertEquals(td[0].className, ' ui-state-highlight', 'original1 highlighted correctly'); 14 | $(td[0].nextSibling).mousedown(); 15 | tf.assertEquals(td[0].nextSibling.className, ' ui-state-highlight', 'original2 highlighted correctly'); 16 | wg.kill(); 17 | }); 18 | 19 | tf.test('Behaviour Testing: Highlighting Rows after deleting column', function(tf) { 20 | $('#sheet').html(""); 21 | var div = $('#sheet') 22 | .append(tableify('original1\t\toriginal2')), 23 | td, 24 | headers, 25 | wg = setup(div[0]), 26 | cell = wg.getCell(0, 0, 0); 27 | wg.deleteColumn(1); 28 | 29 | td = div.find('table.wg-table td:contains("original1")'); 30 | $(td[0]).mousedown(); 31 | tf.assertEquals(td[0].className, ' ui-state-highlight', 'original1 @ expected location'); 32 | $(td[0].nextSibling).mousedown(); 33 | tf.assertEquals(td[0].nextSibling.className, ' ui-state-highlight', 'added @ expected location'); 34 | wg.kill(); 35 | }); -------------------------------------------------------------------------------- /legacy-tests/cells.inputs.js: -------------------------------------------------------------------------------- 1 | tf.test('Cells Inputs: Dropdown Values from Cells', function() { 2 | var loader = new Sheet.JSONLoader([{ 3 | rows: [ 4 | {columns: [ 5 | {formula: 'DROPDOWN(A2:J2)', value: 9}, 6 | {formula: 'DROPDOWN(A3:J3)', value: 'd'} 7 | ]}, 8 | {columns: [ 9 | {value: 1}, 10 | {value: 2}, 11 | {value: 3}, 12 | {value: 4}, 13 | {value: 5}, 14 | {value: 6}, 15 | {value: 7}, 16 | {value: 8}, 17 | {value: 9}, 18 | {value: 10} 19 | ]}, 20 | {columns: [ 21 | {value: 'a'}, 22 | {value: 'b'}, 23 | {value: 'c'}, 24 | {value: 'd'}, 25 | {value: 'e'}, 26 | {value: 'f'}, 27 | {value: 'g'}, 28 | {value: 'h'}, 29 | {value: 'i'}, 30 | {value: 'j'} 31 | ]} 32 | ] 33 | }]), 34 | div = $('
') 35 | .appendTo('body') 36 | .sheet({ 37 | loader: loader, 38 | minSize: { 39 | rows: 25, 40 | cols: 5 41 | } 42 | }), 43 | jS = div.getSheet(), 44 | cell1 = jS.getCell(0,1,1), 45 | cell2 = jS.getCell(0,1,2); 46 | 47 | //ensure values have been resolved 48 | cell1.updateValue(); 49 | cell2.updateValue(); 50 | 51 | tf.assertSame(cell1.value.valueOf(), '9', 'Value is correct'); 52 | tf.assertSame(cell1.value.html.nodeName, 'SELECT', 'Node is correct for input type'); 53 | tf.assertSame(cell1.value.html.selectedIndex, 8, 'Correct index selected'); 54 | 55 | tf.assertSame(cell2.value.valueOf(), 'd', 'Value is correct'); 56 | tf.assertSame(cell2.value.html.nodeName, 'SELECT', 'Node is correct for input type'); 57 | tf.assertSame(cell2.value.html.selectedIndex, 3, 'Correct index selected'); 58 | 59 | div.detach(); 60 | div.getSheet().kill(); 61 | }); 62 | 63 | tf.test('Cells Inputs: Dropdown Values from Literal', function() { 64 | var loader = new Sheet.JSONLoader([{ 65 | rows: [ 66 | {columns: [ 67 | {formula: 'DROPDOWN(1,2,3,4,5,6,7,8,9,10)', value: 9}, 68 | {formula: 'DROPDOWN("a","b","c","d","e","f","g","h","i","j")', value: 'd'} 69 | ]} 70 | ] 71 | }]), 72 | div = $('
') 73 | .appendTo('body') 74 | .sheet({ 75 | loader: loader, 76 | minSize: { 77 | rows: 25, 78 | cols: 5 79 | } 80 | }), 81 | jS = div.getSheet(), 82 | cell1 = jS.getCell(0,1,1), 83 | cell2 = jS.getCell(0,1,2); 84 | 85 | //ensure values have been resolved 86 | cell1.updateValue(); 87 | cell2.updateValue(); 88 | 89 | tf.assertSame(cell1.value.valueOf(), '9', 'Value is correct'); 90 | tf.assertSame(cell1.value.html.nodeName, 'SELECT', 'Node is correct for input type'); 91 | tf.assertSame(cell1.value.html.selectedIndex, 8, 'Correct index selected'); 92 | 93 | tf.assertSame(cell2.value.valueOf(), 'd', 'Value is correct'); 94 | tf.assertSame(cell2.value.html.nodeName, 'SELECT', 'Node is correct for input type'); 95 | tf.assertSame(cell2.value.html.selectedIndex, 3, 'Correct index selected'); 96 | 97 | div.detach(); 98 | div.getSheet().kill(); 99 | }); 100 | 101 | 102 | tf.test('Cells Inputs: Checkbox Value from Cell', function() { 103 | var loader = new Sheet.JSONLoader([{ 104 | rows: [ 105 | {columns: [ 106 | {formula: 'CHECKBOX(A2)', value: 1}, 107 | {formula: 'CHECKBOX(A3)', value: ''} 108 | ]}, 109 | {columns: [ 110 | {value: 1} 111 | ]}, 112 | {columns: [ 113 | {value: 'a'} 114 | ]} 115 | ] 116 | }]), 117 | div = $('
') 118 | .appendTo('body') 119 | .sheet({ 120 | loader: loader, 121 | minSize: { 122 | rows: 25, 123 | cols: 5 124 | } 125 | }), 126 | jS = div.getSheet(), 127 | cell1 = jS.getCell(0,1,1), 128 | cell2 = jS.getCell(0,1,2); 129 | 130 | //ensure values have been resolved 131 | cell1.updateValue(); 132 | cell2.updateValue(); 133 | 134 | tf.assertSame(cell1.value.valueOf(), '1', 'Value is correct'); 135 | tf.assertSame(cell1.value.html.firstChild.nodeName, 'INPUT', 'Node is correct for input type'); 136 | tf.assertSame(cell1.value.html.firstChild.getAttribute('type'), 'checkbox', 'Node is correct for input type'); 137 | tf.assertSame(cell1.value.html.firstChild.checked, true, 'Node (input) is checked'); 138 | 139 | tf.assertSame(cell2.value.valueOf(), '', 'Value is correct'); 140 | tf.assertSame(cell2.value.html.firstChild.nodeName, 'INPUT', 'Node is correct for input type'); 141 | tf.assertSame(cell2.value.html.firstChild.getAttribute('type'), 'checkbox', 'Node is correct for input type'); 142 | tf.assertSame(cell2.value.html.firstChild.checked, false, 'Node (input) is not checked'); 143 | 144 | div.detach(); 145 | div.getSheet().kill(); 146 | }); -------------------------------------------------------------------------------- /legacy-tests/cells.merging.js: -------------------------------------------------------------------------------- 1 | tf.test('Cells Merging: Rows Standard', function() { 2 | 3 | }); -------------------------------------------------------------------------------- /legacy-tests/cells.types.js: -------------------------------------------------------------------------------- 1 | tf.test('Cell Types: None', function() { 2 | var div = $('
') 3 | .append('
100
') 4 | .sheet(), 5 | jS = div.getSheet(), 6 | td; 7 | 8 | td = div.find('table.jS td'); 9 | 10 | tf.assertEquals(td.html(), '100'); 11 | div.getSheet().kill(); 12 | }); 13 | 14 | 15 | tf.test('Cell Types: Currency & rounding', function() { 16 | var div = $('
') 17 | .append('
100.123
') 18 | .sheet(), 19 | jS = div.getSheet(), 20 | td; 21 | 22 | td = div.find('table.jS td'); 23 | 24 | tf.assertEquals(td.html(), '$100.12'); 25 | tf.assertEquals(td[0]._cell.value, 100.123); 26 | div.getSheet().kill(); 27 | }); 28 | 29 | 30 | tf.test('Cell Types: Currency', function() { 31 | var div = $('
') 32 | .append('
100
') 33 | .sheet(), 34 | jS = div.getSheet(), 35 | td; 36 | 37 | td = div.find('table.jS td'); 38 | 39 | tf.assertEquals(td.html(), '$100.00'); 40 | tf.assertEquals(td[0]._cell.value, 100); 41 | div.getSheet().kill(); 42 | }); 43 | 44 | 45 | tf.test('Cell Types: Date', function() { 46 | var div = $('
') 47 | .append('
1/1/2020
') 48 | .sheet(), 49 | jS = div.getSheet(), 50 | td = div.find('table.jS td'), 51 | cell = td[0]._cell, 52 | val; 53 | 54 | cell.updateValue(function(_val) { 55 | val = _val; 56 | }); 57 | 58 | tf.assertEquals(val.toString(), Globalize.parseDate('1/1/2020').toString(), 'valueOverride is correct'); 59 | tf.assertEquals(cell.value, '1/1/2020', 'value is correct'); 60 | div.getSheet().kill(); 61 | }); 62 | 63 | 64 | tf.test('Cell Types: Number', function() { 65 | var div = $('
') 66 | .append('
100
') 67 | .sheet(), 68 | jS = div.getSheet(), 69 | td; 70 | 71 | td = div.find('table.jS td'); 72 | 73 | tf.assertSame(td[0]._cell.value.valueOf(), 100); 74 | div.getSheet().kill(); 75 | }); 76 | 77 | 78 | tf.test('Cell Types: Percent', function() { 79 | var div = $('
') 80 | .append('
.1
') 81 | .sheet(), 82 | jS = div.getSheet(), 83 | td; 84 | 85 | td = div.find('table.jS td'); 86 | 87 | tf.assertEquals(td.html(), '10.00 %', 'html is properly displayed'); 88 | tf.assertEquals(td[0]._cell.value.valueOf(), 0.1, 'value remains a number'); 89 | div.getSheet().kill(); 90 | }); 91 | 92 | tf.test('Cell Types: Percent Addition', function() { 93 | var div = $('
') 94 | .append('
.1
') 95 | .sheet(), 96 | jS = div.getSheet(), 97 | td, 98 | cell = jS.getCell(0,1,1); 99 | 100 | cell.updateValue(); 101 | 102 | tf.assertEquals(cell.td.innerHTML, '1.1', 'html is properly displayed'); 103 | tf.assertEquals(cell.value.valueOf(), 1.1, 'value remains a number'); 104 | div.getSheet().kill(); 105 | }); -------------------------------------------------------------------------------- /legacy-tests/cells.value_behaviour.js: -------------------------------------------------------------------------------- 1 | tf.test('Value Behaviour: Angle Brackets', function() { 2 | var table = $(tableify('="test"')), 3 | div = $('
') 4 | .append(table) 5 | .sheet(), 6 | jS = div.getSheet(), 7 | td; 8 | 9 | td = div.find('table.jS td'); 10 | 11 | tf.assertEquals(td[0].innerHTML, '<b>test</b>', 'greater than, less than'); 12 | div.getSheet().kill(); 13 | }); 14 | 15 | tf.test('Value Behaviour: Line Breaks', function() { 16 | var div = $('
') 17 | .append('
') 21 | .sheet(), 22 | jS = div.getSheet(), 23 | td; 24 | 25 | td = div.find('table.jS td'); 26 | 27 | tf.assertEquals(td.find('br').length, 3, 'expected number of br elements'); 28 | div.getSheet().kill(); 29 | }); 30 | 31 | 32 | tf.test('Value Behaviour: Parenthesis', function() { 33 | var div = $('
') 34 | .append(tableify('=IF((((100 + 100) + 100) + 100) > 100, TRUE, FALSE)\t\n\ 35 | 100\t')) 36 | .sheet(), 37 | jS = div.getSheet(), 38 | td, 39 | cellValue; 40 | 41 | td = div.find('table.jS td'); 42 | td[0]._cell.updateValue(function(_cellValue) { 43 | cellValue = _cellValue; 44 | }); 45 | tf.assertEquals(cellValue.valueOf(), true); 46 | div.getSheet().kill(); 47 | }); 48 | 49 | 50 | tf.test('Value Behaviour: html non-transference', function() { 51 | var div = $('
') 52 | .append('
8
') 53 | .sheet(), 54 | jS = div.getSheet(), 55 | td; 56 | 57 | td = div.find('table.jS td'); 58 | 59 | tf.assertEquals(td.eq(1)[0].innerHTML, '8', 'html stayed where it was supposed to'); 60 | div.getSheet().kill(); 61 | }); -------------------------------------------------------------------------------- /legacy-tests/empty.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ({{pass}}|{{fail}}) {{title}} · Testify Suite 5 | 6 | 7 | 8 | 9 | 16 | 17 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /legacy-tests/formula.functions.js: -------------------------------------------------------------------------------- 1 | tf.test('Formula: Math - ABS', function() { 2 | $('#sheet').html(""); 3 | var div = $('#sheet') 4 | .append(tableify('=ABS(-500)')), 5 | wg = setup(div[0]), 6 | td = div.find('table.wg-table td:first'); 7 | 8 | tf.assertEquals(td.html(), 500, td.attr('data-formula')); 9 | wg.kill(); 10 | }); 11 | 12 | tf.test('Formula: Transpose', function() { 13 | $('#sheet').html(""); 14 | var table1 = tableify("=TRANSPOSE(SHEET2!A2:A10)\t\t\t\t\t\t\t\t\t\n\ 15 | =TRANSPOSE(A1:I1)\t\t\t\t\t\t\t\t\t\n\ 16 | \t\t\t\t\t\t\t\t\t\t\n\ 17 | \t\t\t\t\t\t\t\t\t\t\n\ 18 | \t\t\t\t\t\t\t\t\t\t\n\ 19 | \t\t\t\t\t\t\t\t\t\t\n\ 20 | \t\t\t\t\t\t\t\t\t\t\n\ 21 | \t\t\t\t\t\t\t\t\t\t\n\ 22 | \t\t\t\t\t\t\t\t\t\t\n\ 23 | \t\t\t\t\t\t\t\t\t\t"), 24 | table2 = tableify("Density Viscosity Temperature\n\ 25 | 0.457 3.55 500\n\ 26 | 0.525 3.25 400\n\ 27 | 0.616 2.93 300\n\ 28 | 0.675 2.75 250\n\ 29 | 0.746 2.57 200\n\ 30 | 0.835 2.38 150\n\ 31 | 0.946 2.17 100\n\ 32 | 1.09 1.95 50\n\ 33 | 1.29 1.71 0"), 34 | div = $('#sheet') 35 | .append(table1) 36 | .append(table2), 37 | wg = setup(div[0]), 38 | cell = wg.getCell(0, 0, 0); 39 | 40 | cell.updateValue(); 41 | 42 | tf.assertEquals(cell.value.valueOf(), 0.457, 'value = ' + cell.value.valueOf() + ', should = ' + 0.457); 43 | 44 | wg.kill(); 45 | }); 46 | 47 | tf.test('Formula: SUM and MAX', function() { 48 | $('#sheet').html(""); 49 | var table1 = tableify("Density Temperature\n\ 50 | 0.1 10\n\ 51 | 0.2 10\n\ 52 | 0.3 10\n\ 53 | 0.4 10\n\ 54 | 0.5 10\n\ 55 | 0.6 10\n\ 56 | 0.7 10\n\ 57 | =SUM(A1:A8) =MAX(A1:A8)"), 58 | div = $('#sheet') 59 | .append(table1) 60 | wg = setup(div[0]), 61 | cell = wg.getCell(0, 8, 0), 62 | cell1 = wg.getCell(0, 8, 1); 63 | 64 | cell.updateValue(); 65 | cell1.updateValue(); 66 | 67 | tf.assertEquals(cell.value.valueOf(), 2.8, 'value = ' + cell.value.valueOf() + ', should = ' + 2.8); 68 | tf.assertEquals(cell1.value.valueOf(), 0.7, 'value = ' + cell1.value.valueOf() + ', should = ' + 0.7); 69 | 70 | wg.kill(); 71 | }); 72 | 73 | tf.test('Formula: Updatation', function() { 74 | $('#sheet').html(""); 75 | var table1 = tableify("Density Temperature\n\ 76 | 0.1 10\n\ 77 | 0.2 10\n\ 78 | 0.3 10\n\ 79 | 0.4 10\n\ 80 | 0.5 10\n\ 81 | 0.6 10\n\ 82 | 0.7 10\n\ 83 | =SUM(A1:A8) =MAX(A1:A8)"), 84 | div = $('#sheet') 85 | .append(table1) 86 | wg = setup(div[0]), 87 | cell = wg.getCell(0, 8, 0), 88 | cell1 = wg.getCell(0, 8, 1), 89 | cell2 = wg.getCell(0, 5, 0), 90 | cell3 = wg.getCell(0, 2, 0); 91 | 92 | cell.updateValue(); 93 | cell1.updateValue(); 94 | 95 | tf.assertEquals(cell.value.valueOf(), 2.8, 'value = ' + cell.value.valueOf() + ', should = ' + 2.8); 96 | tf.assertEquals(cell1.value.valueOf(), 0.7, 'value = ' + cell1.value.valueOf() + ', should = ' + 0.7); 97 | 98 | // update values 99 | $(cell2.td).mousedown().dblclick(); 100 | $('textarea')[1].value = 0.9; 101 | $("textarea.wg-in-place-edit").select().trigger({type: 'keydown', which: 13, keyCode: 13}); 102 | 103 | cell2.updateValue(); 104 | tf.assertEquals(cell.value.valueOf(), 3.2, 'value = ' + cell.value.valueOf() + ', should = ' + 3.2); 105 | tf.assertEquals(cell1.value.valueOf(), 0.9, 'value = ' + cell1.value.valueOf() + ', should = ' + 0.9); 106 | 107 | $(cell3.td).mousedown().dblclick(); 108 | $('textarea')[1].value = 0.5; 109 | $("textarea.wg-in-place-edit").select().trigger({type: 'keydown', which: 13, keyCode: 13}); 110 | 111 | cell3.updateValue(); 112 | tf.assertEquals(cell.value.valueOf(), 3.5, 'value = ' + cell.value.valueOf() + ', should = ' + 3.5); 113 | tf.assertEquals(cell1.value.valueOf(), 0.9, 'value = ' + cell1.value.valueOf() + ', should = ' + 0.9); 114 | 115 | wg.kill(); 116 | }); -------------------------------------------------------------------------------- /legacy-tests/formula.js: -------------------------------------------------------------------------------- 1 | tf.test('Formula: Dependencies', function() { 2 | var table = tableify('=ABS(-500)\t=A1+A2\t=B1\n\ 3 | 100\t0\t0\n'), 4 | tableHtml = $(table).html(), 5 | div = $('
') 6 | .append(table) 7 | .sheet({ 8 | initCalcRows: 2, 9 | initCalcCols: 3 10 | }), 11 | jS = div.getSheet(), 12 | A1 = jS.spreadsheets[0][1][1], B1 = jS.spreadsheets[0][1][2], C1 = jS.spreadsheets[0][1][3], 13 | A2 = jS.spreadsheets[0][2][1], B2 = jS.spreadsheets[0][2][2], C2 = jS.spreadsheets[0][2][3]; 14 | 15 | tf.assertEquals(A1.dependencies[0], B1, 'A1 is a dependency of B1'); 16 | tf.assertEquals(B1.dependencies[0], C1, 'C1 is a dependency of B1'); 17 | tf.assertEquals(B1.dependencies[0], C1, 'B1 is a dependency of C1'); 18 | div.getSheet().kill(); 19 | }); 20 | 21 | 22 | tf.test('Formula: Proper number parsing', function() { 23 | var table = tableify('=SUM(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)'), 24 | div = $('
') 25 | .append(table) 26 | .sheet(), 27 | jS = div.getSheet(), 28 | A1 = jS.getCell(0, 1, 1); 29 | 30 | A1.updateValue(); 31 | 32 | tf.assertEquals(A1.value.valueOf(), 20, 'Correct value'); 33 | div.getSheet().kill(); 34 | }); 35 | 36 | 37 | tf.test('Formula: Proper cell parsing', function() { 38 | var table = tableify('=SUM(B1)\t185.50'), 39 | div = $('
') 40 | .append(table) 41 | .sheet(), 42 | jS = div.getSheet(), 43 | A1 = jS.getCell(0, 1, 1); 44 | 45 | A1.updateValue(); 46 | 47 | tf.assertEquals(A1.value.valueOf(), 185.50, 'Correct value'); 48 | div.getSheet().kill(); 49 | }); 50 | 51 | 52 | tf.test('Formula: Proper cell & value parsing', function() { 53 | var table = tableify('=SUM(B1, 200)\t185.50'), 54 | div = $('
') 55 | .append(table) 56 | .sheet(), 57 | jS = div.getSheet(), 58 | A1 = jS.getCell(0, 1, 1); 59 | 60 | A1.updateValue(); 61 | 62 | tf.assertEquals(A1.value.valueOf(), 385.50, 'Correct value'); 63 | div.getSheet().kill(); 64 | }); 65 | 66 | tf.test('Formula: Dependencies from JSON', function() { 67 | var spreadsheets = [{ 68 | title: '1', 69 | rows: [{ 70 | columns: [{},{formula:'A1 + 1'}] 71 | }] 72 | },{ 73 | title: '2', 74 | rows: [{ 75 | columns: [{},{formula:'Sheet1!A1 + 33'}] 76 | }] 77 | },{ 78 | title: '3', 79 | rows: [{ 80 | columns: [{},{formula:'Sheet1!A1 + 66'}] 81 | }] 82 | }], 83 | loader = new Sheet.JSONLoader(spreadsheets), 84 | div = $('
') 85 | .sheet({ 86 | loader: loader 87 | }), 88 | jS = div.getSheet(), 89 | sheet1B1 = jS.getCell(0, 1, 2), 90 | sheet2B1 = jS.getCell(1, 1, 2), 91 | sheet3B1 = jS.getCell(2, 1, 2); 92 | 93 | sheet1B1.updateValue(); 94 | sheet2B1.updateValue(); 95 | sheet3B1.updateValue(); 96 | 97 | tf.assertSame(sheet1B1.value.valueOf(), 1, 'sheet1B1 value is correct'); 98 | tf.assertSame(sheet1B1.loadedFrom.cache, 1, 'sheet1B1 cache is correct'); 99 | tf.assertSame(sheet2B1.value.valueOf(), 33, 'sheet2B1 value is correct'); 100 | tf.assertSame(sheet2B1.loadedFrom.cache, 33, 'sheet2B1 cache is correct'); 101 | tf.assertSame(sheet3B1.value.valueOf(), 66, 'sheet3B1 value is correct'); 102 | tf.assertSame(sheet3B1.loadedFrom.cache, 66, 'sheet3B1 cache is correct'); 103 | div.getSheet().kill(); 104 | }); 105 | -------------------------------------------------------------------------------- /legacy-tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ({{pass}}|{{fail}}) {{title}} · Testify Suite 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 32 | 33 | 34 | 35 | 36 | 37 | 49 | 50 | 51 | 52 |
53 |
54 |
55 | 56 | 57 | 106 | 107 | -------------------------------------------------------------------------------- /legacy-tests/sheet.instantiation.js: -------------------------------------------------------------------------------- 1 | tf.test('Sheet Instantiation: Menu position', function() { 2 | var 3 | menuLeft = $('left'), 4 | menuRight = $('right'), 5 | div = '
\ 6 | \ 7 | \ 8 | \ 9 | \ 10 | \ 11 | \ 12 | \ 13 | \ 14 | \ 15 | \ 16 | \ 17 | \ 18 | \ 19 | \ 20 | \ 21 | \ 22 | \ 23 | \ 24 | \ 25 | \ 26 | \ 27 | \ 28 | \ 29 | \ 30 | \ 31 | \ 32 | \ 33 | \ 34 | \ 35 | \ 36 | \ 37 | \ 38 | \ 39 | \ 40 | \ 41 | \ 42 |
12345
678910
1112131415
1617181920
2122232425
\ 43 |
', 44 | $div = $(div) 45 | .sheet({ 46 | menuLeft: menuLeft, 47 | menuRight: menuRight 48 | }), 49 | jS = $div.getSheet(); 50 | 51 | jS.kill(); 52 | 53 | jS = $div 54 | .html(div) 55 | .sheet({ 56 | menuLeft: menuLeft, 57 | menuRight: menuRight 58 | }) 59 | .getSheet(); 60 | 61 | tf.assertEquals(jS.obj.menuLeft()[0], menuLeft[0], 'Menu is same'); 62 | tf.assertEquals($div[0].firstChild.firstChild.firstChild.firstChild.firstChild, menuLeft[0], 'Menu is in right location'); 63 | 64 | jS.kill(); 65 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wickedgrid", 3 | "version": "4.0.0a", 4 | "description": "a wicked fast spreadsheet editor for the web", 5 | "main": "wickedgrid.js", 6 | "directories": { 7 | "example": "examples", 8 | "test": "test" 9 | }, 10 | "scripts": { 11 | "test": "./node_modules/.bin/mocha --reporter spec", 12 | "build": "node build.js", 13 | "docs": "node build.js && rm -rf docs && node ./node_modules/documentation/bin/documentation.js build wickedgrid.js --github --output docs --format html" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/Spreadsheets/WickedGrid.git" 18 | }, 19 | "keywords": [ 20 | "spreadsheet", 21 | "grid", 22 | "fast", 23 | "ajax", 24 | "json", 25 | "csv" 26 | ], 27 | "author": "Robert Lee Plummer Jr. ", 28 | "license": "MIT", 29 | "bugs": { 30 | "url": "https://github.com/Spreadsheets/WickedGrid/issues" 31 | }, 32 | "homepage": "https://github.com/Spreadsheets/WickedGrid#readme", 33 | "devDependencies": { 34 | "mocha": "^2.5.3", 35 | "uglify-js": "^2.6.4" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /parser/formula/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spreadsheets/WickedGrid/7450a6fe386eb2e3765d499a22b9e3b7c37da7d5/parser/formula/README.md -------------------------------------------------------------------------------- /src/WickedGrid/CellContextMenu.js: -------------------------------------------------------------------------------- 1 | WickedGrid.CellContextMenu = (function() { 2 | function CellContextMenu(wickedGrid, menu) { 3 | this.wickedGrid = wickedGrid; 4 | this.menu = menu; 5 | } 6 | 7 | CellContextMenu.prototype = { 8 | show: function(x, y) { 9 | var wickedGrid = this.wickedGrid, 10 | menu = this.menu, 11 | style = menu.style; 12 | 13 | style.left = (x - 5) + 'px'; 14 | style.top = (y - 5) + 'px'; 15 | 16 | wickedGrid.hideMenus(); 17 | wickedGrid.pane().appendChild(menu); 18 | return this; 19 | }, 20 | hide: function() { 21 | if (this.menu.parentNode === null) return this; 22 | 23 | this.menu.parentNode.removeChild(this.menu); 24 | 25 | return this; 26 | } 27 | }; 28 | 29 | return CellContextMenu; 30 | })(); -------------------------------------------------------------------------------- /src/WickedGrid/CellRange.js: -------------------------------------------------------------------------------- 1 | WickedGrid.CellRange = (function() { 2 | function Constructor(cells) { 3 | this.cells = cells || []; 4 | } 5 | 6 | Constructor.prototype = { 7 | clone: function() { 8 | var clones = [], 9 | cells = this.cells, 10 | max = cells.length, 11 | cell, 12 | clone; 13 | 14 | for(var i = 0; i < max;i++) { 15 | cell = cells[i]; 16 | 17 | clone = cell.clone(); 18 | 19 | clones.push(clone); 20 | } 21 | 22 | return new Constructor(clones); 23 | }, 24 | type: Constructor, 25 | typeName: 'WickedGrid.CellRange' 26 | }; 27 | 28 | return Constructor; 29 | })(); -------------------------------------------------------------------------------- /src/WickedGrid/CellTypeHandlers.js: -------------------------------------------------------------------------------- 1 | WickedGrid.cellTypeHandlers = { 2 | percent: function (cell, value) { 3 | //https://stackoverflow.com/questions/2652319/how-do-you-check-that-a-number-is-nan-in-javascript/16988441#16988441 4 | //NaN !== NaN 5 | if (value !== value) return 0; 6 | var num = (isNaN(value) ? Globalize.parseFloat(value) : value * 1), 7 | result; 8 | 9 | if (!isNaN(num)) {//success 10 | result = new Number(num); 11 | result.html = Globalize.format(num, 'p'); 12 | return result; 13 | } 14 | 15 | return value; 16 | }, 17 | date: function (cell, value) { 18 | if (value !== value) return 0; 19 | var date = Globalize.parseDate(value); 20 | if (date === null) { 21 | return value; 22 | } else { 23 | cell.valueOverride = date; 24 | cell.html = Globalize.format(date, 'd'); 25 | return value; 26 | } 27 | }, 28 | time: function (cell, value) { 29 | if (value !== value) return 0; 30 | var date = Globalize.parseDate(value); 31 | if (date === null) { 32 | return value; 33 | } else { 34 | date.html = Globalize.format(date, 't'); 35 | return date; 36 | } 37 | }, 38 | currency: function (cell, value) { 39 | if (value !== value) return 0; 40 | var num = (isNaN(value) ? Globalize.parseFloat(value) : value * 1), 41 | result; 42 | 43 | if (!isNaN(num)) {//success 44 | result = new Number(num); 45 | result.html = Globalize.format(num, 'c'); 46 | return result; 47 | } 48 | 49 | return value; 50 | }, 51 | number: function (cell, value) { 52 | if (value !== value) return 0; 53 | var radix, result; 54 | 55 | if (!CellTypeHandlers.endOfNumber) { 56 | radix = Globalize.culture().numberFormat['.']; 57 | CellTypeHandlers.endOfNumber = new RegExp("([" + (radix == '.' ? "\." : radix) + "])([0-9]*?[1-9]+)?(0)*$"); 58 | } 59 | 60 | if (!isNaN(value)) {//success 61 | result = new Number(value); 62 | result.html = Globalize.format(value + '', "n10") 63 | .replace(CellTypeHandlers.endOfNumber, function (orig, radix, num) { 64 | return (num ? radix : '') + (num || ''); 65 | }); 66 | return result; 67 | } 68 | 69 | return value; 70 | } 71 | }; -------------------------------------------------------------------------------- /src/WickedGrid/ColumnContextMenu.js: -------------------------------------------------------------------------------- 1 | WickedGrid.ColumnContextMenu = (function() { 2 | function ColumnContextMenu(wickedGrid, menu) { 3 | this.wickedGrid = wickedGrid; 4 | this.menu = menu; 5 | } 6 | 7 | ColumnContextMenu.prototype = { 8 | kill: function() { 9 | if (this.menu.parentNode !== null) { 10 | this.menu.parentNode.removeChild(this.menu); 11 | } 12 | 13 | return this; 14 | }, 15 | show: function(x, y) { 16 | this.wickedGrid.hideMenus(); 17 | 18 | var wickedGrid = this.wickedGrid, 19 | menu = this.menu, 20 | style = menu.style; 21 | 22 | style.left = (x - 5) + 'px'; 23 | style.top = (y - 5) + 'px'; 24 | 25 | wickedGrid.pane().appendChild(menu); 26 | return this; 27 | }, 28 | hide: function() { 29 | if (this.menu.parentNode === null) return this; 30 | this.menu.parentNode.removeChild(this.menu); 31 | return this; 32 | } 33 | }; 34 | return ColumnContextMenu; 35 | })(); -------------------------------------------------------------------------------- /src/WickedGrid/ColumnFreezer.js: -------------------------------------------------------------------------------- 1 | //Creates the draggable objects for freezing cells 2 | WickedGrid.columnFreezer = function(wickedGrid) { 3 | if (!wickedGrid.settings.freezableCells) return false; 4 | if (wickedGrid.isBusy()) return false; 5 | 6 | var pane = wickedGrid.pane(), 7 | actionUI = pane.actionUI, 8 | tBody = pane.tBody, 9 | frozenAt = actionUI.frozenAt, 10 | scrolledArea = actionUI.scrolledArea; 11 | 12 | if (!(scrolledArea.col <= (frozenAt.col + 1))) { 13 | return false; 14 | } 15 | 16 | wickedGrid.barHelper().remove(); 17 | 18 | var highlighter, 19 | bar = tBody.children[0].children[frozenAt.col + 1], 20 | paneRectangle = pane.getBoundingClientRect(), 21 | paneTop = paneRectangle.top + document.body.scrollTop, 22 | paneLeft = paneRectangle.left + document.body.scrollLeft, 23 | handle = document.createElement('div'), 24 | $handle = pane.freezeHandleTop = $(handle) 25 | .appendTo(pane) 26 | .addClass(wickedGrid.theme.columnFreezeHandle + ' ' + wickedGrid.cl.barHelper + ' ' + wickedGrid.cl.columnFreezeHandle) 27 | .height(bar.clientHeight - 1) 28 | .css('left', (bar.offsetLeft - handle.clientWidth) + 'px') 29 | .attr('title', wickedGrid.msg.dragToFreezeCol); 30 | 31 | wickedGrid.controls.bar.helper[wickedGrid.i] = wickedGrid.barHelper().add(handle); 32 | wickedGrid.controls.bar.x.handleFreeze[wickedGrid.i] = $handle; 33 | 34 | wickedGrid.draggable($handle, { 35 | axis:'x', 36 | start:function () { 37 | wickedGrid.setBusy(true); 38 | 39 | highlighter = $(document.createElement('div')) 40 | .css('position', 'absolute') 41 | .addClass(wickedGrid.theme.barFreezeIndicator + ' ' + wickedGrid.cl.barHelper) 42 | .height(bar.clientHeight - 1) 43 | .fadeTo(0,0.33) 44 | .appendTo(pane); 45 | }, 46 | drag:function() { 47 | var target = $handle.nearest(bar.parentNode.children).prev(); 48 | if (target.length > 0 && typeof target.position === 'function') { 49 | highlighter.width(target.position().left + target.width()); 50 | } 51 | }, 52 | stop:function (e, ui) { 53 | highlighter.remove(); 54 | wickedGrid.setBusy(false); 55 | wickedGrid.setDirty(true); 56 | var target = $.nearest($handle, bar.parentNode.children); 57 | 58 | wickedGrid.barHelper().remove(); 59 | scrolledArea.col = actionUI.frozenAt.col = Math.max(wickedGrid.getTdLocation(target[0]).col - 1, 0); 60 | wickedGrid.autoFillerHide(); 61 | }, 62 | containment:[paneLeft, paneTop, paneLeft + pane.clientWidth - window.scrollBarSize.width, paneTop] 63 | }); 64 | 65 | return true; 66 | }; -------------------------------------------------------------------------------- /src/WickedGrid/Event/formula.js: -------------------------------------------------------------------------------- 1 | WickedGrid.event.Formula = (function() { 2 | function Formula(wickedGrid) { 3 | this.wickedGrid = wickedGrid; 4 | } 5 | 6 | Formula.prototype = { 7 | /** 8 | * 9 | * @param {Object} e jQuery event 10 | * @returns {*}.evt.formula 11 | */ 12 | keydown:function (e) { 13 | e = e || window.event; 14 | var wickedGrid = this.wickedGrid; 15 | if (wickedGrid.readOnly[wickedGrid.i]) return false; 16 | if (wickedGrid.cellLast === null) return false; 17 | if (wickedGrid.cellLast.rowIndex < 0 || wickedGrid.cellLast.columnIndex < 0) return false; 18 | 19 | wickedGrid.trigger('sheetFormulaKeydown', [false]); 20 | 21 | switch (e.keyCode) { 22 | case key.C: 23 | if (e.ctrlKey) { 24 | return wickedGrid.documentEvents.copy(e); 25 | } 26 | case key.X: 27 | if (e.ctrlKey) { 28 | return wickedGrid.documentEvents.cut(e); 29 | } 30 | case key.Y: 31 | if (e.ctrlKey) { 32 | wickedGrid.documentEvents.redo(e); 33 | return false; 34 | } 35 | break; 36 | case key.Z: 37 | if (e.ctrlKey) { 38 | wickedGrid.documentEvents.undo(e); 39 | return false; 40 | } 41 | break; 42 | case key.ESCAPE: 43 | wickedGrid.cellEvents.editAbandon(); 44 | return true; 45 | break; 46 | case key.ENTER: 47 | wickedGrid.cellEvents.setActiveFromKeyCode(e, true); 48 | return false; 49 | break; 50 | case key.UNKNOWN: 51 | return false; 52 | } 53 | 54 | wickedGrid.cellLast.isEdit = true; 55 | }, 56 | 57 | /** 58 | * Helper for events 59 | * @param {Boolean} ifTrue 60 | * @param e {Object} jQuery event 61 | * @returns {*}.evt.keydownHandler 62 | */ 63 | If:function (ifTrue, e) { 64 | if (ifTrue) { 65 | $(this.wickedGrid.tdActive()).dblclick(); 66 | return true; 67 | } 68 | return false; 69 | } 70 | }; 71 | 72 | return Formula; 73 | })(); -------------------------------------------------------------------------------- /src/WickedGrid/Fullscreen.js: -------------------------------------------------------------------------------- 1 | WickedGrid.FullScreen = (function() { 2 | var $body = $('body'), 3 | $window = $(window); 4 | 5 | function Fullscreen(wickedGrid) { 6 | wickedGrid.cellEvents.done(); 7 | this.wickedGrid = wickedGrid; 8 | this.active = false; 9 | this.container = document.createElement('div'); 10 | } 11 | 12 | Fullscreen.prototype = { 13 | toggle: function() { 14 | if (this.active) { 15 | this.deactivate(); 16 | } else { 17 | this.activate(); 18 | } 19 | }, 20 | activate: function() { 21 | var wickedGrid = this.wickedGrid, 22 | pane = wickedGrid.pane(), 23 | element = wickedGrid.settings.element, 24 | events = $._data(element[0], 'events'), 25 | container = this.container; 26 | 27 | $body.addClass('body-no-scroll'); 28 | 29 | container.className = wickedGrid.cl.fullScreen + ' ' + wickedGrid.theme.fullScreen + ' ' + wickedGrid.cl.element; 30 | 31 | $(container) 32 | .append(element.children()) 33 | .appendTo($body); 34 | 35 | $window 36 | .bind('resize', function() { 37 | $window.trigger('wg-resize'); 38 | }) 39 | .bind('wg-resize', function () { 40 | container 41 | .width(window.innerWidth) 42 | .height(window.innerHeight); 43 | 44 | wickedGrid.sheetSyncSize(); 45 | if (pane.inPlaceEdit) { 46 | pane.inPlaceEdit.goToTd(); 47 | } 48 | }) 49 | .trigger('wg-resize'); 50 | 51 | element.trigger('sheetFullScreen', [true]); 52 | 53 | for (var event in events) { 54 | if (!events.hasOwnProperty(event)) continue; 55 | 56 | for (var i = 0; i < events[event].length; i++) { 57 | element.bind(event, events[event][i].handler); 58 | } 59 | } 60 | }, 61 | deactivate: function() { 62 | var wickedGrid = this.wickedGrid, 63 | pane = this.pane(), 64 | element = wickedGrid.settings.element, 65 | container = this.container; 66 | 67 | $window.unbind('wg-resize'); 68 | $body.removeClass('body-no-scroll'); 69 | 70 | element.prepend(container.children); 71 | container.detach(); 72 | 73 | wickedGrid.sheetSyncSize(); 74 | if (pane.inPlaceEdit) { 75 | pane.inPlaceEdit.goToTd(); 76 | } 77 | 78 | $window.unbind('resize wg-resize'); 79 | 80 | wickedGrid.trigger('sheetFullScreen', [false]); 81 | } 82 | }; 83 | 84 | return Fullscreen; 85 | })(); -------------------------------------------------------------------------------- /src/WickedGrid/Highlighter.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Creates the scrolling system used by each spreadsheet 4 | */ 5 | WickedGrid.Highlighter = (function() { 6 | var Highlighter = function(cellCssClass, barCssClass, tabsCssClass, callBack) { 7 | this.cellCssClass = cellCssClass.split(/[\s]/); 8 | this.barCssClass = barCssClass.split(/[\s]/); 9 | this.tabsCssClass = tabsCssClass.split(/[\s]/); 10 | this.callBack = callBack; 11 | 12 | this.last = []; 13 | this.lastTop = $([]); 14 | this.lastLeft = $([]); 15 | this.lastTab = $([]); 16 | this.startRowIndex = 0; 17 | this.startColumnIndex = 0; 18 | this.endRowIndex = 0; 19 | this.endColumnIndex = 0; 20 | }; 21 | 22 | Highlighter.prototype = { 23 | /** 24 | * 25 | * @param {WickedGrid.Cell} cell 26 | */ 27 | cell: function(cell) { 28 | this.cellCssClass.forEach(function(_class) { 29 | cell.addClass(_class); 30 | }); 31 | 32 | this.last.push(cell); 33 | return this; 34 | }, 35 | 36 | off: function() { 37 | while (this.last.length > 0) { 38 | var last = this.last.pop(); 39 | switch (last.type) { 40 | case WickedGrid.Cell: 41 | this.cellCssClass.forEach(function(_class) { 42 | last.removeClass(_class); 43 | }); 44 | break; 45 | default: 46 | this.cellCssClass.forEach(function(_class) { 47 | last.removeClass(_class); 48 | }); 49 | } 50 | } 51 | return this; 52 | }, 53 | set: function (objs) { 54 | if (objs.parentNode !== undefined) { 55 | objs = [objs]; 56 | } 57 | 58 | var i, 59 | obj, 60 | lastHighlighted = this.last; 61 | 62 | //_obj is the old selected items 63 | if (lastHighlighted && lastHighlighted.length > 0) { 64 | i = lastHighlighted.length - 1; 65 | do { 66 | lastHighlighted[i].isHighlighted = false; 67 | } while (i-- > 0); 68 | } 69 | 70 | if (objs.length > 0) { 71 | i = objs.length - 1; 72 | do { 73 | obj = objs[i]; 74 | if (!obj.isHighlighted) { 75 | obj.isHighlighted = true; 76 | this.cellCssClass.forEach(function(_class) { 77 | if (!obj.className.match(_class)) { 78 | obj.className += ' ' + _class; 79 | } 80 | }); 81 | } 82 | } while (i-- > 0); 83 | } 84 | 85 | this.clear(lastHighlighted); 86 | this.last = objs; 87 | 88 | this.callBack(); 89 | return this; 90 | }, 91 | 92 | /** 93 | * Detects if there is a cell highlighted 94 | * @returns {Boolean} 95 | */ 96 | is:function () { 97 | return this.last.length > 0; 98 | }, 99 | 100 | /** 101 | * Clears highlighted cells 102 | * @param {Object} [obj] 103 | */ 104 | clear:function (obj) { 105 | if (this.is()) { 106 | obj = obj || this.last; 107 | 108 | if (obj && obj.length) { 109 | var i = obj.length - 1; 110 | do { 111 | if (!obj[i].isHighlighted) { 112 | this.cellCssClass.forEach(function(_class) { 113 | obj[i].className = obj[i].className.replace(_class, ''); 114 | }); 115 | obj[i].isHighlighted = false; 116 | } 117 | } while (i-- > 0); 118 | } 119 | } 120 | 121 | this.last = $([]); 122 | 123 | return this; 124 | }, 125 | 126 | 127 | /** 128 | * Sets a bar to be active 129 | * @param {String} direction left or top 130 | * @param {HTMLElement} td index of bar 131 | */ 132 | setBar:function (direction, td) { 133 | var self = this; 134 | switch (direction) { 135 | case 'top': 136 | this.barCssClass.forEach(function(_class) { 137 | self.lastTop 138 | .removeClass(_class); 139 | self.lastTop = $(td).addClass(_class); 140 | }); 141 | break; 142 | case 'left': 143 | this.barCssClass.forEach(function(_class) { 144 | self.lastLeft 145 | .removeClass(_class); 146 | self.lastLeft = $(td).addClass(_class); 147 | }); 148 | break; 149 | } 150 | 151 | return this; 152 | }, 153 | 154 | /** 155 | * Clears bars from being active 156 | */ 157 | clearBar:function () { 158 | var self = this; 159 | this.barCssClass.forEach(function(_class) { 160 | self.lastTop 161 | .removeClass(_class); 162 | self.lastLeft 163 | .removeClass(_class); 164 | }); 165 | this.lastTop = $([]); 166 | this.lastLeft = $([]); 167 | 168 | return this; 169 | }, 170 | 171 | 172 | 173 | /** 174 | * Sets a tab to be active 175 | */ 176 | setTab:function (tab) { 177 | var self = this; 178 | this.clearTab(); 179 | this.tabsCssClass.forEach(function(_class) { 180 | self.lastTab = tab.addClass(_class); 181 | }); 182 | 183 | return this; 184 | }, 185 | 186 | /** 187 | * Clears a tab from being active 188 | */ 189 | clearTab:function () { 190 | var self = this; 191 | this.tabsCssClass.forEach(function(_class) { 192 | self.lastTab 193 | .removeClass(_class); 194 | }); 195 | 196 | return this; 197 | }, 198 | 199 | setStart: function(cell) { 200 | this.startRowIndex = cell.rowIndex + 0; 201 | this.startColumnIndex = cell.columnIndex + 0; 202 | 203 | return this; 204 | }, 205 | 206 | setEnd: function(cell) { 207 | this.endRowIndex = cell.rowIndex + 0; 208 | this.endColumnIndex = cell.columnIndex + 0; 209 | 210 | return this; 211 | } 212 | }; 213 | 214 | return Highlighter; 215 | })(); 216 | -------------------------------------------------------------------------------- /src/WickedGrid/JSONStreamer.js: -------------------------------------------------------------------------------- 1 | WickedGrid.JSONStreamer = { 2 | sheetRows: function(sheetUrl, rowUrls, sheet, callback) { 3 | 4 | var t = new Thaw([], {each: function() { 5 | sheet.rows.push(JSON.parse(this)); 6 | }}); 7 | 8 | WickedGrid.thread() 9 | .streamJSONSheetRows(operative.getBaseURL(), sheetUrl, rowUrls, function(type, str) { 10 | var i, json; 11 | 12 | switch (type) { 13 | case 'sheet': 14 | json = JSON.parse(str); 15 | 16 | for(i in json) if (json.hasOwnProperty(i)) { 17 | sheet[i] = json[i]; 18 | } 19 | if (sheet.rows === undefined) sheet.rows = []; 20 | 21 | break; 22 | case 'row': 23 | t.add(str); 24 | break; 25 | default: 26 | if (callback) { 27 | callback(sheet); 28 | } 29 | break; 30 | } 31 | }); 32 | 33 | return this; 34 | }, 35 | 36 | rows: function(urls, sheet, callback) { 37 | if (sheet.rows === undefined) sheet.rows = []; 38 | 39 | var t = new Thaw([], {each: function() { 40 | sheet.rows.push(JSON.parse(this)); 41 | }}); 42 | 43 | WickedGrid.thread() 44 | .streamJSONRows(operative.getBaseURL(), urls, function(type, str) { 45 | switch (type) { 46 | case 'row': 47 | t.add(str); 48 | break; 49 | default: 50 | if (callback) callback.call(Sheet, sheet.rows); 51 | } 52 | }); 53 | 54 | return this; 55 | }, 56 | 57 | sheet: function(url, sheet, callback) { 58 | var t = new Thaw([], {each: function() { 59 | sheet.rows.push(JSON.parse(this)); 60 | }}); 61 | 62 | WickedGrid.thread() 63 | .streamJSONSheet(operative.getBaseURL(), url, function(type, str) { 64 | var json, i; 65 | 66 | switch(type) { 67 | case 'sheet': 68 | json = JSON.parse(str); 69 | 70 | for(i in json) if (json.hasOwnProperty(i)) { 71 | sheet[i] = json[i]; 72 | } 73 | if (sheet.rows === undefined) sheet.rows = []; 74 | break; 75 | case 'row': 76 | t.add(str); 77 | break; 78 | default: 79 | if (callback) callback.call(Sheet, sheet); 80 | } 81 | }); 82 | 83 | return this; 84 | } 85 | }; -------------------------------------------------------------------------------- /src/WickedGrid/Plugin/finance.js: -------------------------------------------------------------------------------- 1 | WickedGrid.plugin.finance = (function(){ 2 | var finance = { 3 | NPV: function(rate) { 4 | rate = rate * 1; 5 | var factor = 1, 6 | sum = 0, 7 | result; 8 | 9 | for(var i = 1; i < arguments.length; i++) { 10 | var factor = factor * (1 + rate); 11 | sum += arguments[i] / factor; 12 | } 13 | 14 | result = new Number(sum); 15 | 16 | result.html = Globalize.format( sum, "c" ); 17 | return result; 18 | }, 19 | PV: function(rate, nper, pmt, fv, type) { 20 | fv = fv || 0; 21 | type = type || 0; 22 | 23 | var pv, 24 | result; 25 | if (rate != 0) { 26 | pv = (-pmt * (1 + rate * type) * ((Math.pow(1 + rate, nper) - 1) / rate) - fv) / Math.pow(1 + rate, nper); 27 | } else { 28 | pv = -fv - pmt * nper; 29 | } 30 | 31 | result = new Number(pv); 32 | result.html = Globalize.format( pv, "c" ); 33 | return result; 34 | }, 35 | RATE: function(nper, pmt, pv, fv, type, estimate) { 36 | fv = fv || 0; 37 | type = type || 0; 38 | estimate = estimate || 0.1; 39 | 40 | var rate = estimate, y = 0, f = 0, 41 | FINANCIAL_MAX_ITERATIONS = 128, 42 | FINANCIAL_PRECISION = 1.0e-08, 43 | result; 44 | 45 | if (Math.abs(rate) < FINANCIAL_PRECISION) { 46 | y = pv * (1 + nper * rate) + pmt * (1 + rate * type) * nper + fv; 47 | } else { 48 | f = Math.exp(nper * Math.log(1 + rate)); 49 | y = pv * f + pmt * (1 / rate + type) * (f - 1) + fv; 50 | } 51 | var y0 = pv + pmt * nper + fv, 52 | y1 = pv * f + pmt * (1 / rate + type) * (f - 1) + fv; 53 | 54 | // find root by secant method 55 | var i = 0, x0 = 0, 56 | x1 = rate; 57 | while ((Math.abs(y0 - y1) > FINANCIAL_PRECISION) && (i < FINANCIAL_MAX_ITERATIONS)) { 58 | rate = (y1 * x0 - y0 * x1) / (y1 - y0); 59 | x0 = x1; 60 | x1 = rate; 61 | 62 | if (Math.abs(rate) < FINANCIAL_PRECISION) { 63 | y = pv * (1 + nper * rate) + pmt * (1 + rate * type) * nper + fv; 64 | } else { 65 | f = Math.exp(nper * Math.log(1 + rate)); 66 | y = pv * f + pmt * (1 / rate + type) * (f - 1) + fv; 67 | } 68 | 69 | y0 = y1; 70 | y1 = y; 71 | ++i; 72 | } 73 | 74 | result = new Number(rate); 75 | result.html = Globalize.format( rate, "p" ); 76 | return result; 77 | }, 78 | IPMT: function(rate, per, nper, pv, fv, type) { 79 | var pmt = finance.PMT(rate, nper, pv, fv, type), 80 | fv = finance.FV(rate, per - 1, pmt, pv, type), 81 | result = fv * rate; 82 | 83 | // account for payments at beginning of period versus end. 84 | if (type) { 85 | result /= (1 + rate); 86 | } 87 | 88 | result = new Number(result); 89 | result.html = Globalize.format( result, "c" ); 90 | return result; 91 | }, 92 | PMT: function(rate, nper, pv, fv, type){ 93 | rate = parseFloat(rate || 0); 94 | nper = parseFloat(nper || 0); 95 | pv = parseFloat(pv || 0); 96 | fv = fv || 0; 97 | type = type || 0; 98 | 99 | // pmt = rate / ((1 + rate)^N - 1) * -(pv * (1 + r)^N + fv) 100 | var pmt = (rate / (Math.pow(1 + rate, nper) - 1) 101 | * -(pv * Math.pow(1 + rate, nper) + fv)), 102 | result; 103 | // account for payments at beginning of period versus end. 104 | if (type == 1) { 105 | pmt = pmt / (1 + rate); 106 | } 107 | 108 | result = new Number(pmt); 109 | result.html = Globalize.format( pmt, "c" ); 110 | return result; 111 | }, 112 | NPER: function(rate, pmt, pv, fv, type) { //Taken from LibreOffice - http://opengrok.libreoffice.org/xref/core/sc/source/core/tool/interpr2.cxx#1382 ScInterpreter::ScZZR() 113 | var log, 114 | result; 115 | rate = parseFloat(rate || 0); 116 | pmt = parseFloat(pmt || 0); 117 | pv = parseFloat(pv || 0); 118 | fv = (fv || 0); 119 | type = (type || 0); 120 | 121 | log = function(prim) { 122 | if (isNaN(prim)) { 123 | return Math.log(0); 124 | } 125 | var num = Math.log(prim); 126 | return num; 127 | } 128 | 129 | if (rate == 0.0) { 130 | result = (-(pv + fv)/pmt); 131 | } else if (type > 0.0) { 132 | result = (log(-(rate*fv-pmt*(1.0+rate))/(rate*pv+pmt*(1.0+rate)))/(log(1.0+rate))); 133 | } else { 134 | result = (log(-(rate*fv-pmt)/(rate*pv+pmt))/(log(1.0+rate))); 135 | } 136 | 137 | if (isNaN(result)) { 138 | result = 0; 139 | } 140 | 141 | return result; 142 | }, 143 | FV: function(rate, nper, pmt, pv, type) { //not working yet 144 | pv = (pv ? pv : 0); 145 | type = (type ? type : 0); 146 | var resultPrimitive = -( 147 | pv*Math.pow(1.0+rate, nper) 148 | + pmt * (1.0 + rate*type) 149 | * (Math.pow(1.0+rate, nper) - 1.0) / rate 150 | ), 151 | result = new Number(resultPrimitive); 152 | 153 | result.html = Globalize.format( resultPrimitive, "c" ); 154 | return result; 155 | } 156 | }; 157 | return finance; 158 | })(); -------------------------------------------------------------------------------- /src/WickedGrid/RowContextMenu.js: -------------------------------------------------------------------------------- 1 | WickedGrid.RowContextMenu = (function() { 2 | function RowContextMenu(wickedGrid, menu) { 3 | this.wickedGrid = wickedGrid; 4 | this.menu = menu; 5 | } 6 | 7 | RowContextMenu.prototype = { 8 | show: function(x, y) { 9 | this.wickedGrid.hideMenus(); 10 | 11 | var wickedGrid = this.wickedGrid, 12 | menu = this.menu, 13 | style = menu.style; 14 | 15 | style.left = (x - 5) + 'px'; 16 | style.top = (y - 5) + 'px'; 17 | 18 | wickedGrid.pane().appendChild(menu); 19 | 20 | return this; 21 | }, 22 | hide: function() { 23 | var menu = this.menu; 24 | if (menu.parentNode === null) return this; 25 | 26 | menu.parentNode.removeChild(menu); 27 | 28 | return this; 29 | } 30 | }; 31 | 32 | return RowContextMenu; 33 | })(); -------------------------------------------------------------------------------- /src/WickedGrid/RowFreezer.js: -------------------------------------------------------------------------------- 1 | //Creates the draggable objects for freezing cells 2 | WickedGrid.rowFreezer = function(wickedGrid, index, pane) { 3 | if (wickedGrid.isBusy()) { 4 | return false; 5 | } 6 | var pane = wickedGrid.pane(), 7 | actionUI = pane.actionUI, 8 | table = pane.table, 9 | tBody = pane.tBody, 10 | frozenAt = actionUI.frozenAt, 11 | scrolledArea = actionUI.scrolledArea; 12 | 13 | if (!(scrolledArea.row <= (frozenAt.row + 1))) { 14 | return false; 15 | } 16 | 17 | wickedGrid.barHelper().remove(); 18 | 19 | var bar = tBody.children[frozenAt.row + 1].children[0], 20 | paneRectangle = pane.getBoundingClientRect(), 21 | paneTop = paneRectangle.top + document.body.scrollTop, 22 | paneLeft = paneRectangle.left + document.body.scrollLeft, 23 | handle = document.createElement('div'), 24 | $handle = pane.freezeHandleLeft = $(handle) 25 | .appendTo(pane) 26 | .addClass(wickedGrid.theme.rowFreezeHandle + ' ' + wickedGrid.cl.barHelper + ' ' + wickedGrid.cl.rowFreezeHandle) 27 | .width(bar.clientWidth) 28 | .css('top', (bar.offsetTop - handle.clientHeight + 1) + 'px') 29 | .attr('title', wickedGrid.msg.dragToFreezeRow), 30 | highlighter; 31 | 32 | wickedGrid.controls.bar.helper[wickedGrid.i] = wickedGrid.barHelper().add(handle); 33 | wickedGrid.controls.bar.y.handleFreeze[wickedGrid.i] = $handle; 34 | 35 | wickedGrid.draggable($handle, { 36 | axis:'y', 37 | start:function () { 38 | wickedGrid.setBusy(true); 39 | 40 | highlighter = $(document.createElement('div')) 41 | .appendTo(pane) 42 | .css('position', 'absolute') 43 | .addClass(wickedGrid.theme.barFreezeIndicator + ' ' + wickedGrid.cl.barHelper) 44 | .width(handle.clientWidth) 45 | .fadeTo(0,0.33); 46 | }, 47 | drag:function() { 48 | var target = $handle.nearest(bar.parentNode.parentNode.children).prev(); 49 | if (target.length && target.position) { 50 | highlighter.height(target.position().top + target.height()); 51 | } 52 | }, 53 | stop:function (e, ui) { 54 | highlighter.remove(); 55 | wickedGrid 56 | .setBusy(false) 57 | .setDirty(true); 58 | 59 | var target = $.nearest($handle, bar.parentNode.parentNode.children); 60 | wickedGrid.barHelper().remove(); 61 | scrolledArea.row = actionUI.frozenAt.row = Math.max(wickedGrid.getTdLocation(target.children(0)[0]).row - 1, 0); 62 | wickedGrid.autoFillerHide(); 63 | }, 64 | containment:[paneLeft, paneTop, paneLeft, paneTop + pane.clientHeight - window.scrollBarSize.height] 65 | }); 66 | 67 | return true; 68 | }; -------------------------------------------------------------------------------- /src/WickedGrid/SpreadsheetUI.js: -------------------------------------------------------------------------------- 1 | WickedGrid.SpreadsheetUI = (function() { 2 | var stack = []; 3 | 4 | function Constructor(i, ui, options) { 5 | options = options || {}; 6 | 7 | this.i = i; 8 | this.ui = ui; 9 | this.isLast = options.lastIndex === i; 10 | this.enclosure = null; 11 | this.pane = null; 12 | this.spreadsheet = null; 13 | 14 | this.initChildren = options.initChildren || function() {}; 15 | this.done = options.done || function() {}; 16 | this.load(); 17 | } 18 | 19 | Constructor.prototype = { 20 | load: function(enclosure, pane, spreadsheet) { 21 | this.initChildren(this.ui, this.i); 22 | 23 | this.enclosure = enclosure; 24 | this.pane = pane; 25 | this.spreadsheet = spreadsheet; 26 | 27 | stack.push(this.i); 28 | 29 | if (this.isLast) { 30 | this.loaded(); 31 | } 32 | }, 33 | loaded: function() { 34 | this.done(stack, this); 35 | } 36 | }; 37 | 38 | return Constructor; 39 | })(); -------------------------------------------------------------------------------- /src/WickedGrid/Theme.js: -------------------------------------------------------------------------------- 1 | WickedGrid.Theme = (function() { 2 | function Theme(theme) { 3 | theme = theme || WickedGrid.defaultTheme; 4 | 5 | switch (theme) { 6 | case WickedGrid.customTheme: 7 | this.cl = Theme.customClasses; 8 | break; 9 | 10 | 11 | case WickedGrid.bootstrapTheme: 12 | this.cl = Theme.bootstrapClasses; 13 | break; 14 | 15 | default: 16 | case WickedGrid.themeRollerTheme: 17 | this.cl = Theme.themeRollerClasses; 18 | break; 19 | } 20 | 21 | extend(this, this.cl); 22 | } 23 | 24 | Theme.themeRollerClasses = { 25 | autoFiller: 'ui-state-active', 26 | bar: 'ui-widget-header', 27 | barHighlight: 'ui-state-active', 28 | rowFreezeHandle: 'ui-state-default', 29 | columnFreezeHandle: 'ui-state-default', 30 | columnMenu: 'ui-state-default', 31 | columnMenuIcon: 'ui-icon ui-icon-triangle-1-s', 32 | tdActive: 'ui-state-active', 33 | tdHighlighted: 'ui-state-highlight', 34 | control: 'ui-widget-header ui-corner-top', 35 | controlTextBox: 'ui-widget-content', 36 | fullScreen: 'ui-widget-content ui-corner-all', 37 | inPlaceEdit: 'ui-state-highlight', 38 | menu: 'ui-widget-header', 39 | menuFixed: '', 40 | menuUl: 'ui-widget-header', 41 | menuLi: 'ui-widget-header', 42 | menuHover: 'ui-state-highlight', 43 | pane: 'ui-widget-content', 44 | parent: 'ui-widget-content ui-corner-all', 45 | table: 'ui-widget-content', 46 | tab: 'ui-widget-header', 47 | tabActive: 'ui-state-highlight', 48 | barResizer: 'ui-state-highlight', 49 | barFreezer: 'ui-state-highlight', 50 | barFreezeIndicator: 'ui-state-highlight' 51 | }; 52 | 53 | Theme.bootstrapClasses = { 54 | autoFiller: 'btn-info', 55 | bar: 'input-group-addon', 56 | barHighlight: 'label-info', 57 | rowFreezeHandle: 'bg-warning', 58 | columnFreezeHandle: 'bg-warning', 59 | columnMenu: '', 60 | columnMenuIcon: 'fa fa-sort-desc', 61 | tdActive: 'active', 62 | tdHighlighted: 'bg-info disabled', 63 | control: 'panel-heading', 64 | controlTextBox: 'form-control', 65 | fullScreen: '', 66 | inPlaceEdit: 'form-control', 67 | menu: 'panel panel-default', 68 | menuFixed: 'nav navbar-nav', 69 | menuUl: 'panel-info', 70 | menuLi: 'active', 71 | menuHover: 'bg-primary active', 72 | pane: 'well', 73 | parent: 'panel panel-default', 74 | table: 'table-bordered table-condensed', 75 | tab: 'btn-default btn-xs', 76 | tabActive: 'active', 77 | barResizer: 'bg-info', 78 | barFreezer: 'bg-warning', 79 | barFreezeIndicator: 'bg-warning' 80 | }; 81 | 82 | Theme.customClasses = { 83 | autoFiller: '', 84 | bar: '', 85 | barHighlight: '', 86 | rowFreezeHandle: '', 87 | columnFreezeHandle: '', 88 | columnMenu: '', 89 | columnMenuIcon: '', 90 | tdActive: '', 91 | tdHighlighted: '', 92 | control: '', 93 | controlTextBox: '', 94 | fullScreen: '', 95 | inPlaceEdit: '', 96 | menu: '', 97 | menuFixed: '', 98 | menuUl: '', 99 | menuLi: '', 100 | menuHover: '', 101 | pane: '', 102 | parent: '', 103 | table: '', 104 | tab: '', 105 | tabActive: '', 106 | barResizer: '', 107 | barFreezer: '', 108 | barFreezeIndicator: '' 109 | }; 110 | 111 | return Theme; 112 | })(); -------------------------------------------------------------------------------- /src/WickedGrid/Undo.js: -------------------------------------------------------------------------------- 1 | WickedGrid.Undo = (function() { 2 | function empty() {} 3 | function Undo(wickedGrid) { 4 | this.wickedGrid = wickedGrid; 5 | this.cells =[]; 6 | this.id = -1; 7 | 8 | if (typeof UndoManager !== 'undefined') { 9 | this.undoManager = new UndoManager(); 10 | } else { 11 | this.undoManager = { 12 | add: empty, 13 | undo: empty, 14 | redo: empty, 15 | register: empty 16 | }; 17 | } 18 | } 19 | 20 | Undo.prototype = { 21 | createCells: function(cells, fn, id) { 22 | if (typeof id === 'undefined') { 23 | this.id++; 24 | id = this.id; 25 | } 26 | 27 | var self = this, 28 | before = (new WickedGrid.CellRange(cells)).clone().cells, 29 | after = (typeof fn === 'function' ? (new WickedGrid.CellRange(fn(cells)).clone()).cells : before); 30 | 31 | before.id = id; 32 | after.id = id; 33 | 34 | this.undoManager.add({ 35 | undo: function() { 36 | self.removeCells(before, id); 37 | }, 38 | redo: function() { 39 | self.createCells(after, null, id); 40 | } 41 | }); 42 | 43 | if (id !== this.id) { 44 | this.draw(after); 45 | } 46 | 47 | return true; 48 | }, 49 | removeCells: function(cells, id) { 50 | var i = 0, index = -1; 51 | if (cells.id === id) { 52 | index = i; 53 | } 54 | 55 | if (index !== -1) { 56 | this.cells.splice(index, 1); 57 | } 58 | 59 | this.draw(cells); 60 | }, 61 | draw: function(clones) { 62 | var i, 63 | td, 64 | clone, 65 | cell, 66 | loc, 67 | wickedGrid = this.wickedGrid; 68 | 69 | for (i = 0; i < clones.length; i++) { 70 | clone = clones[i]; 71 | loc = wickedGrid.getTdLocation(clone.td); 72 | cell = clone.clone(); 73 | wickedGrid.spreadsheets[clone.sheetIndex][loc.row][loc.col] = cell; 74 | 75 | cell.setNeedsUpdated(); 76 | cell.updateValue(); 77 | } 78 | 79 | wickedGrid.pane().actionUI.redrawColumns(); 80 | wickedGrid.pane().actionUI.redrawRows(); 81 | } 82 | }; 83 | 84 | return Undo; 85 | })(); -------------------------------------------------------------------------------- /src/WickedGrid/autoFiller.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created the autoFiller object 3 | * @returns {*|jQuery|null}.controlFactory 4 | * @param {WickedGrid} wickedGrid 5 | * @param {HTMLElement} pane 6 | */ 7 | WickedGrid.autoFiller = function(wickedGrid, pane) { 8 | if (!wickedGrid.settings.autoFiller) return false; 9 | 10 | var autoFiller = document.createElement('div'), 11 | handle = document.createElement('div'), 12 | cover = document.createElement('div'); 13 | 14 | autoFiller.i = wickedGrid.i; 15 | 16 | autoFiller.className = WickedGrid.cl.autoFiller + ' ' + wickedGrid.theme.autoFiller; 17 | handle.className = WickedGrid.cl.autoFillerHandle; 18 | cover.className = WickedGrid.cl.autoFillerCover; 19 | 20 | autoFiller.onmousedown = function () { 21 | var td = this.tdActive(); 22 | if (td) { 23 | var loc = wickedGrid.getTdLocation(td); 24 | wickedGrid.cellSetActive(td, loc, true, WickedGrid.autoFillerNotGroup, function () { 25 | var highlighted = wickedGrid.highlighted(), 26 | hLoc = wickedGrid.getTdLocation(highlighted.last()); 27 | wickedGrid.fillUpOrDown(hLoc.row < loc.row || hLoc.col < loc.col); 28 | wickedGrid.autoFillerGoToTd(td); 29 | wickedGrid.autoFillerNotGroup = false; 30 | }); 31 | } 32 | 33 | return false; 34 | }; 35 | 36 | pane.autoFiller = wickedGrid.controls.autoFiller[wickedGrid.i] = autoFiller; 37 | pane.appendChild(autoFiller); 38 | return true; 39 | }; -------------------------------------------------------------------------------- /src/WickedGrid/cl.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Internal css classes of objects 3 | * @memberof WickedGrid 4 | * @type {Object} 5 | */ 6 | WickedGrid.cl = { 7 | list: function(list) { 8 | var result = [], 9 | self = WickedGrid.cl; 10 | list.forEach(function(prop) { 11 | if (!self.hasOwnProperty(prop)) throw new Error('prop ' + prop + ' not found on WickedGrid.cl'); 12 | result.push(self[prop]); 13 | }); 14 | 15 | return result.join(' '); 16 | }, 17 | autoFiller: 'wg-auto-filler', 18 | autoFillerHandle: 'wg-auto-filler-handle', 19 | autoFillerCover: 'wg-auto-filler-cover', 20 | corner: 'wg-corner', 21 | barController: 'wg-bar-controller', 22 | barControllerChild: 'wg-bar-controller-child', 23 | barHelper: 'wg-bar-helper', 24 | row: 'wg-row', 25 | rowFreezeHandle: 'wg-row-freeze-handle', 26 | column: 'wg-column', 27 | columnHelper: 'wg-column-helper', 28 | columnFocus: 'wg-column-focus', 29 | columnButton: 'wg-column-button', 30 | columnFreezeHandle: 'wg-column-freeze-handle', 31 | chart: 'wg-chart', 32 | formula: 'wg-formula', 33 | formulaParent: 'wg-formula-parent', 34 | header: 'wg-header', 35 | fullScreen: 'wg-full-screen', 36 | inPlaceEdit: 'wg-in-place-edit', 37 | headerMenu: 'wg-header-menu', 38 | menuFixed: 'wg-menu-fixed', 39 | element: 'wg-element', 40 | scroll: 'wg-scroll', 41 | sheetAdder: 'wg-sheet-adder', 42 | table: 'wg-table', 43 | label: 'wg-loc', 44 | pane: 'wg-edit-pane', 45 | tab: 'wg-tab', 46 | tabContainer: 'wg-tab-container', 47 | tabContainerScrollable: 'wg-tab-container-scrollable', 48 | menu: 'wg-menu', 49 | menuButton: 'wg-menu-button', 50 | title: 'wg-title', 51 | enclosure: 'wg-enclosure', 52 | ui: 'wg-ui' 53 | }; -------------------------------------------------------------------------------- /src/WickedGrid/columnMenu.js: -------------------------------------------------------------------------------- 1 | WickedGrid.ColumnMenu = (function() { 2 | function ColumnMenu(wickedGrid, menu) { 3 | this.wickedGrid = wickedGrid; 4 | this.menu = menu; 5 | this.index = -1; 6 | this.column = null; 7 | 8 | var self = this, 9 | barHelper = this.barHelper = widget( 10 | '
\ 11 | \ 12 |
' 13 | ), 14 | button = barHelper.children[0]; 15 | 16 | button.onmousedown = function () { 17 | self.showMenu(); 18 | }; 19 | } 20 | 21 | ColumnMenu.prototype = { 22 | setColumn: function(column, index) { 23 | if (this.column !== null) { 24 | this.column.classList.remove(this.wickedGrid.cl.columnFocus); 25 | } 26 | this.hideMenu(); 27 | this.column = column; 28 | this.index = index; 29 | 30 | return this; 31 | }, 32 | kill: function() { 33 | this.hide(); 34 | this.column = null; 35 | this.index = -1; 36 | return this; 37 | }, 38 | show: function() { 39 | this.wickedGrid.hideMenus(); 40 | 41 | this.column.appendChild(this.barHelper); 42 | this.column.classList.add(this.wickedGrid.cl.columnFocus); 43 | 44 | return this; 45 | }, 46 | hide: function() { 47 | if (this.barHelper.parentNode === null) return this; 48 | this.barHelper.parentNode.removeChild(this.barHelper); 49 | return this; 50 | }, 51 | showMenu: function() { 52 | this.barHelper.appendChild(this.menu); 53 | return this; 54 | }, 55 | hideMenu: function() { 56 | if (this.menu.parentNode === null) return this; 57 | this.menu.parentNode.removeChild(this.menu); 58 | return this; 59 | } 60 | }; 61 | return ColumnMenu; 62 | })(); -------------------------------------------------------------------------------- /src/WickedGrid/columnResizer.js: -------------------------------------------------------------------------------- 1 | WickedGrid.columnResizer = function(wickedGrid, bar) { 2 | wickedGrid.barTopControls().remove(); 3 | var barController = document.createElement('div'), 4 | $barController = $(barController) 5 | .addClass(wickedGrid.cl.barController + ' ' + wickedGrid.theme.barResizer) 6 | .width(bar.clientWidth) 7 | .prependTo(bar), 8 | handle, 9 | pane = wickedGrid.pane(); 10 | 11 | wickedGrid.controls.bar.x.controls[wickedGrid.i] = wickedGrid.barTopControls().add($barController); 12 | 13 | wickedGrid.resizableCells($barController, { 14 | handles:'e', 15 | start:function (e, ui) { 16 | wickedGrid.autoFillerHide(); 17 | wickedGrid.setBusy(true); 18 | if (pane.freezeHandleTop) { 19 | pane.freezeHandleTop.remove(); 20 | } 21 | }, 22 | resize:function (e, ui) { 23 | bar.col.style.width = ui.size.width + 'px'; 24 | 25 | if (pane.inPlaceEdit) { 26 | pane.inPlaceEdit.goToTd(); 27 | } 28 | }, 29 | stop:function (e, ui) { 30 | wickedGrid.setBusy(false); 31 | if (pane.inPlaceEdit) { 32 | pane.inPlaceEdit.goToTd(); 33 | } 34 | wickedGrid.followMe(); 35 | wickedGrid.setDirty(true); 36 | }, 37 | minWidth: 32 38 | }); 39 | 40 | handle = barController.children[0]; 41 | handle.style.height = bar.clientHeight + 'px'; 42 | }; -------------------------------------------------------------------------------- /src/WickedGrid/customTab.js: -------------------------------------------------------------------------------- 1 | WickedGrid.customTab = function(wickedGrid, title) { 2 | var tab = document.createElement('span'), 3 | $tab = $(tab).appendTo(wickedGrid.tabContainer()); 4 | 5 | tab.setAttribute('class', wickedGrid.cl.tab + ' ' + wickedGrid.theme.tab); 6 | tab.innerHTML = title; 7 | 8 | return $tab; 9 | }; -------------------------------------------------------------------------------- /src/WickedGrid/defaults.js: -------------------------------------------------------------------------------- 1 | WickedGrid.defaults = (function() { 2 | var defaults = { 3 | editable: true, 4 | editableNames: true, 5 | barMenus: true, 6 | freezableCells: true, 7 | allowToggleState: true, 8 | headerMenu: null, 9 | newColumnWidth: 120, 10 | title: null, 11 | calcOff: false, 12 | lockFormulas: false, 13 | parent: null, 14 | colMargin: 20, 15 | boxModelCorrection: 2, 16 | formulaFunctions: {}, 17 | formulaVariables: {}, 18 | cellSelectModel: WickedGrid.excelSelectModel, 19 | autoAddCells: true, 20 | resizableCells: true, 21 | resizableSheet: true, 22 | autoFiller: true, 23 | error: function (e) { 24 | return e.error; 25 | }, 26 | endOfNumber: false, 27 | frozenAt: [], 28 | columnMenuButtons: null, 29 | columnContextMenuButtons: { 30 | 'Insert column after': function () { 31 | this.addColumn(false, this.colLast); 32 | return false; 33 | }, 34 | 'Insert column before': function () { 35 | this.addColumn(true, this.colLast); 36 | return false; 37 | }, 38 | 'Add column to end': function () { 39 | this.addColumn(true); 40 | return false; 41 | }, 42 | 'Delete this column': function () { 43 | this.deleteColumn(); 44 | return false; 45 | }, 46 | 'Hide column': function () { 47 | this.toggleHideColumn(this.colLast); 48 | return false; 49 | }, 50 | 'Show all columns': function () { 51 | this.columnShowAll(); 52 | }, 53 | 'Toggle freeze columns to here': function () { 54 | var col = this.getTdLocation(this.tdActive()).col, 55 | actionUI = this.pane().actionUI; 56 | actionUI.frozenAt.col = (actionUI.frozenAt.col == col ? 0 : col); 57 | } 58 | }, 59 | rowContextMenuButtons: { 60 | 'Insert row after': function () { 61 | this.addRow(true, this.rowLast); 62 | return false; 63 | }, 64 | 'Insert row before': function () { 65 | this.addRow(false, this.rowLast); 66 | return false; 67 | }, 68 | 'Add row to end': function () { 69 | this.addRow(true); 70 | return false; 71 | }, 72 | 'Delete this row': function () { 73 | this.deleteRow(); 74 | return false; 75 | }, 76 | 'Hide row': function () { 77 | this.toggleHideRow(this.rowLast); 78 | return false; 79 | }, 80 | 'Show all rows': function () { 81 | this.rowShowAll(); 82 | }, 83 | 'Toggle freeze rows to here': function () { 84 | var row = this.getTdLocation(this.tdActive()).row, 85 | actionUI = this.pane().actionUI; 86 | actionUI.frozenAt.row = (actionUI.frozenAt.row == row ? 0 : row); 87 | } 88 | }, 89 | cellContextMenuButtons: { 90 | /*'Copy': false, 91 | 'Cut': false, 92 | 'line1': 'line',*/ 93 | 'Insert row after': function () { 94 | this.addRow(true, this.rowLast); 95 | return false; 96 | }, 97 | 'Insert row before': function () { 98 | this.addRow(false, this.rowLast); 99 | return false; 100 | }, 101 | 'Add row to end': function () { 102 | this.addRow(true); 103 | return false; 104 | }, 105 | 'Delete this row': function () { 106 | this.deleteRow(); 107 | return false; 108 | }, 109 | 'line2': 'line', 110 | 'Insert column after': function () { 111 | this.addColumn(true, this.colLast); 112 | return false; 113 | }, 114 | 'Insert column before': function () { 115 | this.addColumn(false, this.colLast); 116 | return false; 117 | }, 118 | 'Add column to end': function () { 119 | this.addColumn(true); 120 | return false; 121 | }, 122 | 'Delete this column': function () { 123 | this.deleteColumn(); 124 | return false; 125 | }, 126 | 'line3': 'line', 127 | 'Add spreadsheet': function () { 128 | this.addSheet(); 129 | }, 130 | 'Delete spreadsheet': function () { 131 | this.deleteSheet(); 132 | } 133 | }, 134 | alert: function(msg) { 135 | alert(msg); 136 | }, 137 | prompt: function(msg, callback, initialValue) { 138 | callback(prompt(msg, initialValue)); 139 | }, 140 | confirm: function(msg, callbackIfTrue, callbackIfFalse) { 141 | if (confirm(msg)) { 142 | callbackIfTrue(); 143 | } else if (callbackIfFalse) { 144 | callbackIfFalse(); 145 | } 146 | }, 147 | loader: null, 148 | useStack: true, 149 | useMultiThreads: true 150 | }; 151 | 152 | defaults.columnMenuButtons = defaults.columnContextMenuButtons; 153 | 154 | return defaults; 155 | })(); -------------------------------------------------------------------------------- /src/WickedGrid/enclosure.js: -------------------------------------------------------------------------------- 1 | // The viewing console for spreadsheet 2 | WickedGrid.enclosure = function(wickedGrid) { 3 | var enclosure = document.createElement('div'), 4 | $enclosure = $(enclosure), 5 | actionUI = new WickedGrid.ActionUI(wickedGrid, enclosure, this.cl.scroll, wickedGrid.settings.frozenAt[wickedGrid.i]), 6 | pane = actionUI.pane; 7 | 8 | pane.className = WickedGrid.cl.pane + ' ' + wickedGrid.theme.pane; 9 | enclosure.className = WickedGrid.cl.enclosure; 10 | 11 | enclosure.pane = pane; 12 | 13 | pane.enclosure = enclosure; 14 | pane.$enclosure = $enclosure; 15 | 16 | wickedGrid.controls.pane[wickedGrid.i] = pane; 17 | wickedGrid.controls.panes = wickedGrid.panes().add(pane); 18 | wickedGrid.controls.enclosures[wickedGrid.i] = enclosure; 19 | 20 | return enclosure; 21 | }; -------------------------------------------------------------------------------- /src/WickedGrid/formulaEditor.js: -------------------------------------------------------------------------------- 1 | //Creates the control/container for everything above the spreadsheet, removes them if they already exist.controlFactory 2 | WickedGrid.formulaEditor = function(wickedGrid, header) { 3 | var label = document.createElement('td'); 4 | label.className = wickedGrid.cl.label + ' ' + wickedGrid.theme.control; 5 | wickedGrid.controls.label = $(label); 6 | var formula = document.createElement('textarea'); 7 | formula.className = wickedGrid.cl.formula + ' ' + wickedGrid.theme.controlTextBox; 8 | formula.onkeydown = function(e) { 9 | return wickedGrid.formulaEvents.keydown(e); 10 | }; 11 | formula.onkeyup = function () { 12 | wickedGrid.inPlaceEdit().value = formula.value; 13 | }; 14 | formula.onchange = function () { 15 | wickedGrid.inPlaceEdit().value = formula.value; 16 | }; 17 | formula.onpaste = function(e) { 18 | return wickedGrid.pasteOverCells(e); 19 | }; 20 | formula.onfocus = function () { 21 | wickedGrid.setNav(false); 22 | }; 23 | formula.onfocusout = function () { 24 | wickedGrid.setNav(true); 25 | }; 26 | formula.onblur = function () { 27 | wickedGrid.setNav(true); 28 | }; 29 | wickedGrid.controls.formula = $(formula); 30 | 31 | // resizable formula area - a bit hard to grab the handle but is there! 32 | var formulaResize = document.createElement('span'); 33 | formulaResize.appendChild(formula); 34 | 35 | var secondRow = document.createElement('table'); 36 | var secondRowTr = document.createElement('tr'); 37 | secondRow.appendChild(secondRowTr); 38 | 39 | header.appendChild(secondRow); 40 | 41 | var formulaParent = document.createElement('td'); 42 | formulaParent.className = wickedGrid.cl.formulaParent; 43 | formulaParent.appendChild(formulaResize); 44 | secondRowTr.appendChild(label); 45 | secondRowTr.appendChild(formulaParent); 46 | 47 | //spacer 48 | secondRowTr.appendChild(document.createElement('td')); 49 | 50 | wickedGrid.resizableSheet($(formulaResize), { 51 | minHeight:wickedGrid.controls.formula.height(), 52 | maxHeight:78, 53 | handles:'s', 54 | resize:function (e, ui) { 55 | wickedGrid.controls.formula.height(ui.size.height); 56 | }, 57 | stop: function() { 58 | wickedGrid.sheetSyncSize(); 59 | } 60 | }); 61 | 62 | var instances = WickedGrid.instances; 63 | for(var i = 0; i < instances.length; i++) { 64 | (instances || {}).nav = false; 65 | } 66 | 67 | wickedGrid.setNav(true); 68 | 69 | $(document).keydown(function(e) { 70 | return wickedGrid.documentEvents.keydown(e); 71 | }); 72 | 73 | return formula; 74 | }; -------------------------------------------------------------------------------- /src/WickedGrid/header.js: -------------------------------------------------------------------------------- 1 | //Creates the control/container for everything above the spreadsheet, removes them if they already exist.controlFactory 2 | WickedGrid.header = function(wickedGrid) { 3 | wickedGrid.header().remove(); 4 | wickedGrid.sheetAdder().remove(); 5 | wickedGrid.tabContainer().remove(); 6 | 7 | var s = wickedGrid.settings, 8 | header = document.createElement('div'), 9 | title = document.createElement('h4'), 10 | menu, 11 | $menu; 12 | 13 | header.className = wickedGrid.cl.header + ' ' + wickedGrid.theme.control; 14 | 15 | wickedGrid.controls.header = $(header); 16 | 17 | if (s.title) { 18 | if ($.isFunction(s.title)) { 19 | s.title = wickedGrid.title(I); 20 | } 21 | 22 | title.className = wickedGrid.cl.title; 23 | wickedGrid.controls.title = $(title).html(s.title) 24 | } else { 25 | title.style.display = 'none'; 26 | } 27 | 28 | header.appendChild(title); 29 | 30 | if (wickedGrid.isSheetEditable()) { 31 | if (s.headerMenu) { 32 | menu = document.createElement('div'); 33 | $menu = $(menu); 34 | menu.className = wickedGrid.cl.headerMenu + ' ' + wickedGrid.cl.menuFixed + ' ' + wickedGrid.theme.menuFixed + ' ' + wickedGrid.cl.menu; 35 | header.appendChild(menu); 36 | 37 | wickedGrid.controls.headerMenu[wickedGrid.i] = $menu 38 | .append(s.headerMenu(wickedGrid)) 39 | .children() 40 | .addClass(wickedGrid.theme.menuFixed); 41 | 42 | $menu.find('img').load(function () { 43 | wickedGrid.sheetSyncSize(); 44 | }); 45 | } 46 | 47 | WickedGrid.formulaEditor(wickedGrid, header); 48 | } 49 | 50 | return header; 51 | }; -------------------------------------------------------------------------------- /src/WickedGrid/inPlaceEdit.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creates a textarea for a user to put a value in that floats on top of the current selected cell 3 | * @param {WickedGrid} wickedGrid 4 | * @param {jQuery|HTMLElement} td the td to be edited 5 | * @param {Boolean} selected selects the text in the inline editor.controlFactory 6 | */ 7 | WickedGrid.inPlaceEdit = function(wickedGrid, td, selected) { 8 | if (wickedGrid.cellActive()) { 9 | td = td || wickedGrid.cellActive().td || null; 10 | } else { 11 | td = td || null; 12 | } 13 | 14 | if (td === null) { 15 | td = wickedGrid.rowTds(null, 1)[1]; 16 | wickedGrid.cellEdit(td._cell); 17 | } 18 | 19 | if (td === null) return; 20 | 21 | (wickedGrid.inPlaceEdit().destroy || empty)(); 22 | 23 | var formula = wickedGrid.formula(), 24 | val = formula.val(), 25 | textarea, 26 | $textarea, 27 | pane = wickedGrid.pane(); 28 | 29 | textarea = document.createElement('textarea'); 30 | $textarea = $(textarea); 31 | pane.inPlaceEdit = textarea; 32 | textarea.i = wickedGrid.i; 33 | textarea.className = wickedGrid.cl.inPlaceEdit + ' ' + wickedGrid.theme.inPlaceEdit; 34 | textarea.td = td; 35 | //td / tr / tbody / table 36 | textarea.table = td.parentNode.parentNode.parentNode; 37 | textarea.goToTd = function() { 38 | textarea.offset = $(td).position(); 39 | if (!textarea.offset.left && !textarea.offset.right) { 40 | $(textarea).hide(); 41 | } else { 42 | textarea.setAttribute('style', 43 | 'left:' + (textarea.offset.left - 1) + 'px;' + 44 | 'top:' + (textarea.offset.top - 1) + 'px;' + 45 | 'width:' + textarea.td.clientWidth + 'px;' + 46 | 'height:' + textarea.td.clientHeight + 'px;' + 47 | 'min-width:' + textarea.td.clientWidth + 'px;' + 48 | 'min-height:' + textarea.td.clientHeight + 'px;'); 49 | } 50 | }; 51 | textarea.goToTd(); 52 | textarea.onkeydown = function (e) { 53 | e = e || window.event; 54 | wickedGrid.trigger('sheetFormulaKeydown', [true]); 55 | 56 | switch (e.keyCode) { 57 | case key.ENTER: 58 | return wickedGrid.formulaEvents.keydown(e); 59 | break; 60 | case key.TAB: 61 | return wickedGrid.formulaEvents.keydown(e); 62 | break; 63 | case key.ESCAPE: 64 | wickedGrid.cellEvents.editAbandon(); 65 | return false; 66 | break; 67 | } 68 | }; 69 | textarea.onchange = 70 | textarea.onkeyup = 71 | function() { formula[0].value = textarea.value; }; 72 | 73 | textarea.onfocus = function () { wickedGrid.setNav(false); }; 74 | 75 | textarea.onblur = 76 | textarea.onfocusout = 77 | function () { wickedGrid.setNav(true); }; 78 | 79 | textarea.onpaste = function(e) { 80 | wickedGrid.cellEvents.paste(e); 81 | }; 82 | 83 | textarea.destroy = function () { 84 | pane.inPlaceEdit = null; 85 | if (wickedGrid.cellLast !== null) { 86 | wickedGrid.cellLast.isEdit = (textarea.value != val); 87 | } 88 | textarea.parentNode.removeChild(textarea); 89 | wickedGrid.controls.inPlaceEdit[textarea.i] = false; 90 | }; 91 | 92 | pane.appendChild(textarea); 93 | 94 | textarea.onfocus(); 95 | 96 | wickedGrid.controls.inPlaceEdit[wickedGrid.i] = textarea; 97 | 98 | //This is a little trick to get the cursor to the end of the textarea 99 | $textarea 100 | .focus() 101 | .val('') 102 | .val(formula[0].value); 103 | 104 | if (selected) { 105 | $textarea.select(); 106 | } 107 | 108 | //Make the textarea resizable automatically 109 | if ($.fn.elastic) { 110 | $(textarea).elastic(); 111 | } 112 | 113 | function enter(e) { 114 | if (e.shiftKey) { 115 | return true; 116 | } 117 | return wickedGrid.cellSetActiveFromKeyCode(e, true); 118 | } 119 | 120 | function tab(e) { 121 | if (e.shiftKey) { 122 | return true; 123 | } 124 | return wickedGrid.cellSetActiveFromKeyCode(e, true); 125 | } 126 | }; -------------------------------------------------------------------------------- /src/WickedGrid/menu.js: -------------------------------------------------------------------------------- 1 | WickedGrid.menu = function(wickedGrid, menuEntities) { 2 | if (typeof menuEntities === 'undefined') throw new Error('no menuEntities defined'); 3 | 4 | var menu = document.createElement('div'), 5 | hoverClasses = wickedGrid.theme.menuHover.split(' '); 6 | 7 | menu.className = wickedGrid.theme.menu + ' ' + wickedGrid.cl.menu; 8 | disableSelectionSpecial(menu); 9 | 10 | menu.onmouseleave = function () { 11 | menu.parentNode.removeChild(menu); 12 | }; 13 | menu.oncontextmenu = function() { 14 | return false; 15 | }; 16 | menu.onscroll = function() { 17 | return false; 18 | }; 19 | 20 | for (var key in menuEntities) { 21 | if (menuEntities.hasOwnProperty(key)) { 22 | (function(key, menuEntity) { 23 | switch (typeof menuEntity) { 24 | case 'function': 25 | var button = document.createElement('div'); 26 | button.className = wickedGrid.cl.menuButton; 27 | button.textContent = key; 28 | button.onclick = function (e) { 29 | menuEntity.call(wickedGrid, e); 30 | menu.parentNode.removeChild(menu); 31 | return false; 32 | }; 33 | if (hoverClasses.length > 0) { 34 | button.onmouseover = function () { 35 | for (var i = 0, max = menu.children.length; i < max; i++) { 36 | hoverClasses.forEach(function(hoverClass) { 37 | menu.children[i].classList.remove(hoverClass); 38 | }); 39 | } 40 | hoverClasses.forEach(function(hoverClass) { 41 | button.classList.add(hoverClass); 42 | }); 43 | }; 44 | } 45 | menu.appendChild(button); 46 | break; 47 | case 'string': 48 | if (menuEntity === 'line') { 49 | menu.appendChild(document.createElement('hr')); 50 | break; 51 | } 52 | default: 53 | throw new Error('Unknown menu type'); 54 | } 55 | })(key, menuEntities[key]); 56 | } 57 | } 58 | 59 | return menu; 60 | }; -------------------------------------------------------------------------------- /src/WickedGrid/rowResizer.js: -------------------------------------------------------------------------------- 1 | WickedGrid.rowResizer = function(wickedGrid, bar, index, pane) { 2 | wickedGrid.barLeftControls().remove(); 3 | var barRectangle = bar.getBoundingClientRect(), 4 | barOffsetTop = barRectangle.top + document.body.scrollTop, 5 | barOffsetLeft = barRectangle.left + document.body.scrollLeft, 6 | barController = document.createElement('div'), 7 | $barController = $(barController) 8 | .addClass(wickedGrid.cl.barController + ' ' + wickedGrid.theme.barResizer) 9 | .offset({ 10 | top: barOffsetTop, 11 | left: barOffsetLeft 12 | }) 13 | .prependTo(bar), 14 | parent = bar.parentNode, 15 | child = document.createElement('div'), 16 | $child = $(child) 17 | .addClass(wickedGrid.cl.barControllerChild) 18 | .height(bar.clientHeight) 19 | .prependTo($barController), 20 | handle; 21 | 22 | wickedGrid.controls.bar.y.controls[wickedGrid.i] = wickedGrid.barLeftControls().add($barController); 23 | 24 | wickedGrid.resizableCells($child, { 25 | handles:'s', 26 | start:function () { 27 | wickedGrid.autoFillerHide(); 28 | wickedGrid.setBusy(true); 29 | if (pane.freezeHandleLeft) { 30 | pane.freezeHandleLeft.remove(); 31 | } 32 | }, 33 | resize:function (e, ui) { 34 | barController.style.height 35 | = bar.style.height 36 | = parent.style.height 37 | = ui.size.height + 'px'; 38 | 39 | if (pane.inPlaceEdit) { 40 | pane.inPlaceEdit.goToTd(); 41 | } 42 | }, 43 | stop:function (e, ui) { 44 | wickedGrid.setBusy(false); 45 | if (pane.inPlaceEdit) { 46 | pane.inPlaceEdit.goToTd(); 47 | } 48 | wickedGrid.followMe(); 49 | wickedGrid.setDirty(true); 50 | }, 51 | minHeight: 15 52 | }); 53 | 54 | handle = child.children[0]; 55 | handle.style.width = bar.offsetWidth + 'px'; 56 | }; -------------------------------------------------------------------------------- /src/WickedGrid/sheetUI.js: -------------------------------------------------------------------------------- 1 | WickedGrid.sheetUI = function(wickedGrid, ui, i) { 2 | //TODO: move to SpreadsheetUI 3 | wickedGrid.i = i; 4 | 5 | //TODO: readOnly from metadata 6 | //wickedGrid.readOnly[i] = (table.className || '').match(/\breadonly\b/i) != null; 7 | 8 | var enclosure = WickedGrid.enclosure(wickedGrid), 9 | pane = enclosure.pane, 10 | $pane = $(pane), 11 | actionUI = pane.actionUI, 12 | paneContextmenuEvent = function (e) { 13 | e = e || window.event; 14 | var target = e.target, 15 | parent = target.parentNode; 16 | 17 | if (wickedGrid.isBusy()) { 18 | return false; 19 | } 20 | 21 | if (wickedGrid.isCell(e.target)) { 22 | wickedGrid.cellContextMenu.show(e.pageX, e.pageY); 23 | return false; 24 | } 25 | 26 | if (!wickedGrid.isBar(target)) return; 27 | 28 | //corner 29 | if (target.cellIndex === 0 && parent.rowIndex === 0) return; 30 | 31 | //row 32 | if (parent.rowIndex === 0) { 33 | wickedGrid.columnContextMenu.show(e.pageX, e.pageY); 34 | } else { 35 | wickedGrid.rowContextMenu.show(e.pageX, e.pageY); 36 | } 37 | 38 | return false; 39 | }; 40 | 41 | ui.appendChild(enclosure); 42 | 43 | if (wickedGrid.isSheetEditable()) { 44 | WickedGrid.autoFiller(wickedGrid, pane); 45 | } 46 | 47 | if (wickedGrid.isSheetEditable()) { 48 | var formula = wickedGrid.formula(), 49 | mouseDownEntity = ''; 50 | 51 | $pane.mousedown(function (e) { 52 | wickedGrid.setNav(true); 53 | if (wickedGrid.isBusy()) { 54 | return false; 55 | } 56 | 57 | if (wickedGrid.isCell(e.target)) { 58 | if (e.button == 2) { 59 | paneContextmenuEvent.call(this, e); 60 | wickedGrid.cellEvents.mouseDown(e); 61 | return true; 62 | } 63 | wickedGrid.cellEvents.mouseDown(e); 64 | return false; 65 | } 66 | 67 | if (wickedGrid.isBar(e.target)) { //possibly a bar 68 | if (e.button == 2) { 69 | paneContextmenuEvent.call(this, e); 70 | } 71 | mouseDownEntity = e.target.entity; 72 | actionUI.selectBar(e.target); 73 | return false; 74 | } 75 | 76 | return true; 77 | }); 78 | 79 | pane.onmouseup = function() { 80 | mouseDownEntity = ''; 81 | }; 82 | 83 | pane.onmouseover = function (e) { 84 | e = e || window.event; 85 | //This manages bar resize, bar menu, and bar selection 86 | if (wickedGrid.isBusy()) { 87 | return false; 88 | } 89 | 90 | if (!wickedGrid.isBar(e.target)) { 91 | return false; 92 | } 93 | 94 | var bar = e.target || e.srcElement, 95 | index = bar.index, 96 | entity = bar.entity; 97 | 98 | if (index < 0) { 99 | return false; 100 | } 101 | 102 | 103 | switch (entity) { 104 | case WickedGrid.columnEntity: 105 | if (actionUI.columnCache.selecting && bar === mouseDownEntity) { 106 | actionUI.columnCache.last = index; 107 | wickedGrid.cellSetActiveBar(WickedGrid.columnEntity, actionUI.columnCache.first, actionUI.columnCache.last); 108 | } else { 109 | WickedGrid.columnResizer(wickedGrid, bar, index, pane); 110 | 111 | if (wickedGrid.isSheetEditable()) { 112 | WickedGrid.columnFreezer(wickedGrid, index, pane); 113 | wickedGrid.columnMenu 114 | .setColumn(bar) 115 | .show(e.pageX, e.pageY); 116 | } 117 | } 118 | break; 119 | case WickedGrid.rowEntity: 120 | if (actionUI.rowCache.selecting && bar === mouseDownEntity) { 121 | actionUI.rowCache.last = index; 122 | wickedGrid.cellSetActiveBar(WickedGrid.rowEntity, actionUI.rowCache.first, actionUI.rowCache.last); 123 | } else { 124 | WickedGrid.rowResizer(wickedGrid, bar, index, pane); 125 | 126 | if (wickedGrid.isSheetEditable()) { 127 | WickedGrid.rowFreezer(wickedGrid, index, pane); 128 | } 129 | } 130 | break; 131 | } 132 | 133 | return true; 134 | }; 135 | 136 | pane.ondblclick = function(e) { 137 | return wickedGrid.cellEvents.dblClick(e); 138 | }; 139 | 140 | pane.oncontextmenu = paneContextmenuEvent; 141 | 142 | $pane 143 | .disableSelectionSpecial() 144 | .bind('cellEdit', function(e) { 145 | if (!e.target._cell) return; 146 | return wickedGrid.cellEvents.edit(e.target._cell); 147 | }); 148 | } 149 | 150 | WickedGrid.tab(wickedGrid); 151 | 152 | wickedGrid.setChanged(true); 153 | }; -------------------------------------------------------------------------------- /src/WickedGrid/spreadsheetAdder.js: -------------------------------------------------------------------------------- 1 | WickedGrid.spreadsheetAdder = function(wickedGrid) { 2 | var adder = document.createElement('span'); 3 | if (wickedGrid.isSheetEditable()) { 4 | adder.setAttribute('class', WickedGrid.cl.sheetAdder + ' ' + WickedGrid.cl.tab + ' ' + wickedGrid.theme.tab); 5 | adder.setAttribute('title', WickedGrid.msg.addSheet); 6 | adder.innerHTML = '+'; 7 | adder.onmousedown = function () { 8 | wickedGrid.addSheet(); 9 | 10 | return false; 11 | }; 12 | adder.i = -1; 13 | } 14 | 15 | wickedGrid.controls.sheetAdder = $(adder); 16 | 17 | return adder; 18 | }; -------------------------------------------------------------------------------- /src/WickedGrid/statics.js: -------------------------------------------------------------------------------- 1 | /** 2 | */ 3 | WickedGrid.event = {}; 4 | /** 5 | */ 6 | WickedGrid.loader = {}; 7 | /** 8 | */ 9 | WickedGrid.plugin = {}; 10 | /** 11 | */ 12 | WickedGrid.calcStack = 0; 13 | /** 14 | * Array of instances of WickedGrid 15 | */ 16 | WickedGrid.instances = []; 17 | /** 18 | */ 19 | WickedGrid.defaultTheme = 0; 20 | /** 21 | */ 22 | WickedGrid.themeRollerTheme = 0; 23 | /** 24 | */ 25 | WickedGrid.bootstrapTheme = 1; 26 | /** 27 | */ 28 | WickedGrid.customTheme = 2; 29 | 30 | /** 31 | */ 32 | WickedGrid.excelSelectModel = 0; 33 | /** 34 | */ 35 | WickedGrid.googleDriveSelectModel = 1; 36 | /** 37 | */ 38 | WickedGrid.openOfficeSelectModel = 2; 39 | 40 | /** 41 | */ 42 | WickedGrid.defaultColumnWidth = 120; 43 | /** 44 | */ 45 | WickedGrid.defaultRowHeight = 20; 46 | 47 | /** 48 | */ 49 | WickedGrid.domRows = 40; 50 | /** 51 | */ 52 | WickedGrid.domColumns = 35; 53 | 54 | /** 55 | */ 56 | WickedGrid.formulaParserUrl = '../parser/formula/formula.js'; 57 | /** 58 | */ 59 | WickedGrid.threadScopeUrl = '../Sheet/threadScope.js'; 60 | /** 61 | */ 62 | WickedGrid.defaultFormulaParser = null; 63 | /** 64 | */ 65 | WickedGrid.spareFormulaParsers = []; 66 | /** 67 | */ 68 | WickedGrid.cornerEntity = 'corner'; 69 | /** 70 | */ 71 | WickedGrid.columnEntity = 'column'; 72 | /** 73 | */ 74 | WickedGrid.rowEntity = 'row'; 75 | /** 76 | */ 77 | WickedGrid.events = [ 78 | 'sheetAddRow', 79 | 'sheetAddColumn', 80 | 'sheetSwitch', 81 | 'sheetRename', 82 | 'sheetTabSortStart', 83 | 'sheetTabSortUpdate', 84 | 'sheetCellEdit', 85 | 'sheetCellEdited', 86 | 'sheetCalculation', 87 | 'sheetAdd', 88 | 'sheetDelete', 89 | 'sheetDeleteRow', 90 | 'sheetDeleteColumn', 91 | 'sheetOpen', 92 | 'sheetAllOpened', 93 | 'sheetSave', 94 | 'sheetFullScreen', 95 | 'sheetFormulaKeydown' 96 | ]; 97 | 98 | /** 99 | * Messages for user interface 100 | * @type {Object} 101 | */ 102 | WickedGrid.msg = { 103 | addRowMulti:'How many rows would you like to add?', 104 | addColumnMulti:'How many columns would you like to add?', 105 | cellFind:'What are you looking for in this spreadsheet?', 106 | cellNoFind:'No results found.', 107 | dragToFreezeCol:'Drag to freeze column', 108 | dragToFreezeRow:'Drag to freeze row', 109 | addSheet:'Add a spreadsheet', 110 | openSheet:'Are you sure you want to open a different sheet? All unsaved changes will be lost.', 111 | toggleHideRow:'No row selected.', 112 | toggleHideColumn:'No column selected.', 113 | loopDetected:'Loop Detected', 114 | newSheetTitle:'What would you like the sheet\'s title to be?', 115 | notFoundColumn:'Column not found', 116 | notFoundRow:'Row not found', 117 | notFoundSheet:'Sheet not found', 118 | setCellRef:'Enter the name you would like to reference the cell by.', 119 | sheetTitleDefault:'Spreadsheet {index}', 120 | cellLoading: 'Loading...' 121 | }; 122 | /** 123 | * Contains the dependencies if you use $.sheet.preLoad(); 124 | */ 125 | WickedGrid.dependencies = { 126 | coreCss:{css:'wickedgrid.css'}, 127 | 128 | jQueryUI:{script:'jquery-ui/jquery-ui.min.js', thirdParty:true}, 129 | jQueryUIThemeRoller:{css:'jquery-ui/themes/smoothness/jquery-ui.min.css', thirdParty:true}, 130 | 131 | globalize:{script:'globalize/lib/globalize.js', thirdParty:true}, 132 | 133 | nearest:{script:'jquery-nearest/src/jquery.nearest.min.js', thirdParty:true}, 134 | 135 | mousewheel:{script:'MouseWheel/MouseWheel.js', thirdParty:true}, 136 | 137 | operative:{script:'operative/dist/operative.js', thirdParty:true}, 138 | 139 | megatable:{script:'megaTable.js/megatable.js', thirdParty:true}, 140 | 141 | infiniscroll:{script:'infiniscroll.js/infinitescroll.js', thirdParty:true} 142 | }; 143 | 144 | /** 145 | * Contains the optional plugins if you use $.sheet.preLoad(); 146 | */ 147 | WickedGrid.optional = { 148 | //native 149 | advancedFn:{script:'src/Plugin/advanced.js'}, 150 | financeFn:{script:'src/Plugin/finance.js'}, 151 | 152 | //3rd party 153 | colorPicker:{ 154 | css:'really-simple-color-picker/css/colorPicker.css', 155 | script:'really-simple-color-picker/js/jquery.colorPicker.min.js', 156 | thirdParty:true 157 | }, 158 | 159 | elastic:{script:'jquery-elastic/jquery.elastic.source.js', thirdParty:true}, 160 | 161 | globalizeCultures:{script:'globalize/lib/cultures/globalize.cultures.js', thirdParty:true}, 162 | 163 | raphael:{script:'raphael/raphael.js', thirdParty:true}, 164 | gRaphael:{script:'graphael/g.raphael.js', thirdParty:true}, 165 | gRaphaelBar:{script:'graphael/g.bar.js', thirdParty:true}, 166 | gRaphaelDot:{script:'graphael/g.dot.js', thirdParty:true}, 167 | gRaphaelLine:{script:'graphael/g.line.js', thirdParty:true}, 168 | gRaphaelPie:{script:'graphael/g.pie.js', thirdParty:true}, 169 | 170 | thaw: {script:'thaw.js/thaw.js', thirdParty:true}, 171 | 172 | undoManager:{script: 'Javascript-Undo-Manager/lib/undomanager.js', thirdParty:true}, 173 | 174 | zeroClipboard:{script:'zeroclipboard/dist/ZeroClipboard.min.js', thirdParty:true} 175 | }; 176 | -------------------------------------------------------------------------------- /src/WickedGrid/tab.js: -------------------------------------------------------------------------------- 1 | WickedGrid.tab = function(wickedGrid) { 2 | var tab = document.createElement('span'), 3 | $tab = wickedGrid.controls.tab[wickedGrid.i] = $(tab).appendTo(wickedGrid.tabContainer()); 4 | 5 | tab.setAttribute('class', WickedGrid.cl.tab + ' ' + wickedGrid.theme.tab); 6 | wickedGrid.sheetTab(true, function(sheetTitle) { 7 | tab.innerHTML = sheetTitle; 8 | }); 9 | 10 | tab.i = wickedGrid.i; 11 | wickedGrid.controls.tabs = wickedGrid.tabs().add($tab); 12 | 13 | return tab; 14 | }; -------------------------------------------------------------------------------- /src/WickedGrid/tabs.js: -------------------------------------------------------------------------------- 1 | //Creates the tab interface 2 | WickedGrid.tabs = function(wickedGrid) { 3 | var tabContainer = document.createElement('span'), 4 | $tabContainer = $(tabContainer), 5 | startPosition; 6 | wickedGrid.controls.tabContainer = $tabContainer; 7 | tabContainer.setAttribute('class', WickedGrid.cl.tabContainer); 8 | 9 | tabContainer.onmousedown = function (e) { 10 | e = e || window.event; 11 | 12 | var i = (e.target || e.srcElement).i; 13 | if (i >= 0) { 14 | wickedGrid.trigger('sheetSwitch', [i]); 15 | } 16 | return false; 17 | }; 18 | tabContainer.ondblclick = function (e) { 19 | e = e || window.event; 20 | var i = (e.target || e.srcElement).i; 21 | if (i >= 0) { 22 | wickedGrid.trigger('sheetRename', [i]); 23 | } 24 | return false; 25 | }; 26 | 27 | if (wickedGrid.isSheetEditable() && $.fn.sortable) { 28 | $tabContainer.sortable({ 29 | placeholder: 'ui-state-highlight', 30 | axis: 'x', 31 | forceHelperSize: true, 32 | forcePlaceholderSize: true, 33 | opacity: 0.6, 34 | start: function (e, ui) { 35 | startPosition = ui.item.index(); 36 | wickedGrid.trigger('sheetTabSortStart', [e, ui]); 37 | }, 38 | update: function (e, ui) { 39 | wickedGrid.trigger('sheetTabSortUpdate', [e, ui, startPosition]); 40 | } 41 | }); 42 | } 43 | 44 | return tabContainer; 45 | }; -------------------------------------------------------------------------------- /src/WickedGrid/thread.js: -------------------------------------------------------------------------------- 1 | WickedGrid.thread = (function () { 2 | var i = 0, 3 | threads = []; 4 | 5 | function thread() { 6 | var t = threads[i], 7 | limit = thread.limit; 8 | 9 | if (t === undefined) { 10 | t = threads[i] = thread.create(); 11 | } else { 12 | t = threads[i]; 13 | } 14 | 15 | i++; 16 | if (i > limit) { 17 | i = 0; 18 | } 19 | 20 | return t; 21 | } 22 | 23 | thread.limit = 10; 24 | 25 | thread.create = function() { 26 | var t = operative({ 27 | parseFormula: function(formula) { 28 | formulaParser.yy.types = []; 29 | return formulaParser.parse(formula); 30 | }, 31 | streamJSONSheet: function(location, url, callback) { 32 | Promise 33 | .all([gR(location + url)]) 34 | .then(function(sheetSets) { 35 | var json = sheetSets[0], 36 | sheet = JSON.parse(json), 37 | rows, 38 | max, 39 | i = 0; 40 | 41 | if (sheet.pop !== undefined) { 42 | sheet = sheet[0]; 43 | } 44 | 45 | rows = sheet.rows; 46 | max = rows.length; 47 | 48 | sheet.rows = []; 49 | callback('sheet', JSON.stringify(sheet)); 50 | 51 | for (; i < max; i++) { 52 | callback('row', JSON.stringify(rows[i])); 53 | } 54 | 55 | callback(); 56 | 57 | }, function(err) { 58 | callback('error', err); 59 | }); 60 | }, 61 | streamJSONRows: function(location, urls, callback) { 62 | var i = 0, 63 | max = urls.length, 64 | getting = []; 65 | 66 | if (typeof urls === 'string') { 67 | getting.push(gR(location + urls)); 68 | } else { 69 | for (; i < max; i++) { 70 | getting.push(gR(location + urls[i])); 71 | } 72 | } 73 | 74 | Promise 75 | .all(getting) 76 | .then(function(jsons) { 77 | var i = 0, 78 | j, 79 | row, 80 | rowSet, 81 | rowSets = jsons, 82 | iMax = rowSets.length, 83 | jMax; 84 | 85 | for(;i 2 | 3 | 4 | 5 | jQuery.sheet cell trace route 6 | 25 | 26 | 27 | 28 | 29 | 74 | 75 | -------------------------------------------------------------------------------- /utilities/trace.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | Sheet.Cell.prototype.addDependency = function(cell) { 3 | if (cell === undefined || cell === null) return; 4 | 5 | if (cell.type !== Sheet.Cell) { 6 | throw new Error('Wrong Type'); 7 | } 8 | 9 | if (this.dependencies.indexOf(cell) < 0 && this !== cell) { 10 | 11 | cell.trace = cell.trace || []; 12 | cell.trace.push(this); 13 | 14 | this.dependencies.push(cell); 15 | if (this.loader !== null) { 16 | this.loader.addDependency(this, cell); 17 | } 18 | } 19 | }; 20 | 21 | Sheet.Cell.prototype.traceRoute = function(callback, rootSheetIndex) { 22 | if (rootSheetIndex === undefined) { 23 | rootSheetIndex = this.sheetIndex; 24 | this.setNeedsUpdated(true); 25 | } 26 | this.updateValue(function() { 27 | 28 | this.trace = this.trace || []; 29 | 30 | var route = { 31 | name: this.id, 32 | children: [] 33 | }, 34 | trace = this.trace, 35 | max = trace.length, 36 | progress = 0, 37 | i = 0; 38 | 39 | if (!route.name) { 40 | route.name = (rootSheetIndex !== this.sheetIndex ? '"' + this.jS.getSpreadsheetTitleByIndex(this.sheetIndex) + '"!' : ''); 41 | route.name += jQuery.sheet.engine.columnLabelString(this.columnIndex) + this.rowIndex; 42 | 43 | route.name += ' : ' + this.value; 44 | 45 | if (this.formula) { 46 | route.name += ' : "=' + this.formula + '"'; 47 | } 48 | } 49 | 50 | console.log(route.name); 51 | 52 | for(;i < max;i++) { 53 | (function(i, tracedCell) { 54 | //to avoid "too much recursion" 55 | setTimeout(function() { 56 | tracedCell.traceRoute(function(childRoute) { 57 | progress++; 58 | route.children[i] = childRoute; 59 | console.log('Progress: ' + progress + ', index: ' + i + ', max: ' + max); 60 | }, rootSheetIndex); 61 | }, 0); 62 | })(i, trace[i]); 63 | } 64 | 65 | callback(route); 66 | }); 67 | }; 68 | })(); -------------------------------------------------------------------------------- /wickedgrid.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sheet", 3 | "title": "jQuery.sheet", 4 | "description": "The ajax spreadsheet", 5 | "keywords": [ 6 | "spreadsheet", 7 | "sheet", 8 | "excel", 9 | "docs", 10 | "office", 11 | "sheet", 12 | "formula", 13 | "calculate", 14 | "calculation", 15 | "doc", 16 | "fast" 17 | ], 18 | "version": "4.0.0a", 19 | "author": { 20 | "name": "Robert Plummer", 21 | "email": "RobertLeePlummerJr@gmail.com", 22 | "url": "http://visop-dev.com" 23 | }, 24 | "maintainers": [ 25 | { 26 | "name": "Robert Plummer", 27 | "email": "RobertLeePlummerJr@gmail.com", 28 | "url": "http://visop-dev.com" 29 | } 30 | ], 31 | "licenses": [ 32 | { 33 | "type": "MIT", 34 | "url": "https://github.com/Spreadsheets/jQuery.sheet/wiki/License" 35 | } 36 | ], 37 | "bugs": "https://github.com/Spreadsheets/jQuery.sheet/issues", 38 | "homepage": "https://github.com/Spreadsheets/jQuery.sheet", 39 | "docs": "http://visop-dev.com/doc/js3/jQuery_.html#sheet", 40 | "download": "https://github.com/Spreadsheets/jQuery.sheet/releases", 41 | "dependencies": { 42 | "jquery": ">=1.5" 43 | } 44 | } --------------------------------------------------------------------------------