forked from sheetjs/sheetjs
		
	stox skip blank worksheets [ci skip]
This commit is contained in:
		
							parent
							
								
									d55b7a3063
								
							
						
					
					
						commit
						c02eb14255
					
				| @ -26,7 +26,7 @@ can be installed with Bash on Windows or with `cygwin`. | ||||
| - [`IndexedDB`](https://docs.sheetjs.com/docs/getting-started/demos/database#indexeddb) | ||||
| 
 | ||||
| **Frameworks** | ||||
| - [`Angular.JS`](angular/) | ||||
| - [`Angular.JS`](https://docs.sheetjs.com/docs/getting-started/demos/legacy#angularjs) | ||||
| - [`Angular 2+ and Ionic`](angular2/) | ||||
| - [`Knockout`](https://docs.sheetjs.com/docs/getting-started/demos/legacy#knockoutjs) | ||||
| - [`Meteor`](meteor/) | ||||
| @ -34,17 +34,19 @@ can be installed with Bash on Windows or with `cygwin`. | ||||
| - [`VueJS, WeeX and NuxtJS`](vue/) | ||||
| 
 | ||||
| **Front-End UI Components** | ||||
| - [`canvas-datagrid`](datagrid/) | ||||
| - [`canvas-datagrid`](https://docs.sheetjs.com/docs/getting-started/demos/grid#canvas-datagrid) | ||||
| - [`x-spreadsheet`](xspreadsheet/) | ||||
| - [`react-data-grid`](react/modify/) | ||||
| - [`vue3-table-light`](vue/modify/) | ||||
| - [`angular-ui-grid`](https://docs.sheetjs.com/docs/getting-started/demos/grid#angular-ui-grid) | ||||
| 
 | ||||
| **Platforms and Integrations** | ||||
| - [`Command-Line Tools`](https://docs.sheetjs.com/docs/getting-started/demos/cli) | ||||
| - [`NodeJS Server-Side Processing`](server/) | ||||
| - [`Deno`](deno/) | ||||
| - [`Electron`](electron/) | ||||
| - [`NW.js`](nwjs/) | ||||
| - [`Chrome / Chromium Extension`](chrome/) | ||||
| - [`NW.js`](https://docs.sheetjs.com/docs/getting-started/demos/desktop#nwjs) | ||||
| - [`Chrome / Chromium Extension`](https://docs.sheetjs.com/docs/getting-started/demos/chromium) | ||||
| - [`Google Sheets API`](https://docs.sheetjs.com/docs/getting-started/demos/gsheet) | ||||
| - [`ExtendScript for Adobe Apps`](https://docs.sheetjs.com/docs/getting-started/demos/extendscript) | ||||
| - [`NetSuite SuiteScript`](https://docs.sheetjs.com/docs/getting-started/demos/netsuite) | ||||
| @ -53,10 +55,9 @@ can be installed with Bash on Windows or with `cygwin`. | ||||
| - [`Headless Automation`](https://docs.sheetjs.com/docs/getting-started/demos/headless) | ||||
| - [`Swift JSC and Other JavaScript Engines`](altjs/) | ||||
| - [`"serverless" functions`](function/) | ||||
| - [`databases and key/value stores`](database/) | ||||
| - [`Databases and Structured Data Stores`](https://docs.sheetjs.com/docs/getting-started/demos/database) | ||||
| - [`NoSQL, K/V, and Unstructured Data Stores`](https://docs.sheetjs.com/docs/getting-started/demos/nosql) | ||||
| - [`internet explorer`](oldie/) | ||||
| - [`Legacy Internet Explorer`](oldie/) | ||||
| 
 | ||||
| **Bundlers and Tooling** | ||||
| - [`browserify`](https://docs.sheetjs.com/docs/getting-started/demos/bundler#browserify) | ||||
| @ -68,7 +69,6 @@ can be installed with Bash on Windows or with `cygwin`. | ||||
| - [`snowpack`](https://docs.sheetjs.com/docs/getting-started/demos/bundler#snowpack) | ||||
| - [`swc`](https://docs.sheetjs.com/docs/getting-started/demos/bundler#swc) | ||||
| - [`systemjs`](systemjs/) | ||||
| - [`typescript`](typescript/) | ||||
| - [`vite`](https://docs.sheetjs.com/docs/getting-started/demos/bundler#vite) | ||||
| - [`webpack 2.x`](webpack/) | ||||
| - [`wmr`](https://docs.sheetjs.com/docs/getting-started/demos/bundler#wmr) | ||||
|  | ||||
| @ -1,148 +1,10 @@ | ||||
| # AngularJS | ||||
| 
 | ||||
| The `xlsx.core.min.js` and `xlsx.full.min.js` scripts are designed to be dropped | ||||
| into web pages with script tags: | ||||
| 
 | ||||
| ```html | ||||
| <script src="xlsx.full.min.js"></script> | ||||
| ``` | ||||
| 
 | ||||
| Strictly speaking, there should be no need for an Angular demo!  You can proceed | ||||
| as you would with any other browser-friendly library. | ||||
| 
 | ||||
| This demo uses AngularJS 1.5.0. | ||||
| 
 | ||||
| 
 | ||||
| ## Array of Objects | ||||
| 
 | ||||
| A common data table is often stored as an array of objects: | ||||
| 
 | ||||
| ```js | ||||
| $scope.data = [ | ||||
|   { Name: "Bill Clinton", Index: 42 }, | ||||
|   { Name: "GeorgeW Bush", Index: 43 }, | ||||
|   { Name: "Barack Obama", Index: 44 }, | ||||
|   { Name: "Donald Trump", Index: 45 } | ||||
| ]; | ||||
| ``` | ||||
| 
 | ||||
| This neatly maps to a table with `ng-repeat`: | ||||
| 
 | ||||
| ```html | ||||
| <table id="sjs-table"> | ||||
|   <tr><th>Name</th><th>Index</th></tr> | ||||
|   <tr ng-repeat="row in data"> | ||||
|     <td>{{row.Name}}</td> | ||||
|     <td>{{row.Index}}</td> | ||||
|   </tr> | ||||
| </table> | ||||
| ``` | ||||
| 
 | ||||
| The `$http` service can request binary data using the `"arraybuffer"` response | ||||
| type coupled with `XLSX.read` with type `"array"`: | ||||
| 
 | ||||
| ```js | ||||
|   $http({ | ||||
|     method:'GET', | ||||
|     url:'https://sheetjs.com/pres.xlsx', | ||||
|     responseType:'arraybuffer' | ||||
|   }).then(function(data) { | ||||
|     var wb = XLSX.read(data.data, {type:"array"}); | ||||
|     var d = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]); | ||||
|     $scope.data = d; | ||||
|   }, function(err) { console.log(err); }); | ||||
| ``` | ||||
| 
 | ||||
| The HTML table can be directly exported with `XLSX.utils.table_to_book`: | ||||
| 
 | ||||
| ```js | ||||
| var wb = XLSX.utils.table_to_book(document.getElementById('sjs-table')); | ||||
| XLSX.writeFile(wb, "export.xlsx"); | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| ## Import Directive | ||||
| 
 | ||||
| A general import directive is fairly straightforward: | ||||
| 
 | ||||
| - Define the `importSheetJs` directive in the app: | ||||
| 
 | ||||
| ```js | ||||
| app.directive("importSheetJs", [SheetJSImportDirective]); | ||||
| ``` | ||||
| 
 | ||||
| - Add the attribute `import-sheet-js=""` to the file input element: | ||||
| 
 | ||||
| ```html | ||||
| <input type="file" import-sheet-js="" multiple="false"  /> | ||||
| ``` | ||||
| 
 | ||||
| - Define the directive: | ||||
| 
 | ||||
| ```js | ||||
| function SheetJSImportDirective() { | ||||
|   return { | ||||
|     scope: { opts: '=' }, | ||||
|     link: function ($scope, $elm) { | ||||
|       $elm.on('change', function (changeEvent) { | ||||
|         var reader = new FileReader(); | ||||
| 
 | ||||
|         reader.onload = function (e) { | ||||
|           /* read workbook */ | ||||
|           var ab = e.target.result; | ||||
|           var workbook = XLSX.read(ab); | ||||
| 
 | ||||
|           /* DO SOMETHING WITH workbook HERE */ | ||||
|         }; | ||||
| 
 | ||||
|         reader.readAsArrayBuffer(changeEvent.target.files[0]); | ||||
|       }); | ||||
|     } | ||||
|   }; | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| ## Export Service | ||||
| 
 | ||||
| An export can be triggered at any point!  Depending on how data is represented, | ||||
| a workbook object can be built using the utility functions.  For example, using | ||||
| an array of objects: | ||||
| 
 | ||||
| ```js | ||||
| /* starting from this data */ | ||||
| var data = [ | ||||
|   { name: "Barack Obama", pres: 44 }, | ||||
|   { name: "Donald Trump", pres: 45 } | ||||
| ]; | ||||
| 
 | ||||
| /* generate a worksheet */ | ||||
| var ws = XLSX.utils.json_to_sheet(data); | ||||
| 
 | ||||
| /* add to workbook */ | ||||
| var wb = XLSX.utils.book_new(); | ||||
| XLSX.utils.book_append_sheet(wb, ws, "Presidents"); | ||||
| 
 | ||||
| /* write workbook and force a download */ | ||||
| XLSX.writeFile(wb, "sheetjs.xlsx"); | ||||
| ``` | ||||
| 
 | ||||
| ## Demo | ||||
| 
 | ||||
| `grid.html` uses `angular-ui-grid` to display a table.  The library does not | ||||
| provide any way to modify the import button, so the demo includes a simple | ||||
| directive for a HTML File Input control.  It also includes a sample service for | ||||
| export which adds an item to the export menu. | ||||
| 
 | ||||
| The demo `SheetJSImportDirective` follows the prescription from the README for | ||||
| File input controls using `readAsArrayBuffer`, converting to a suitable | ||||
| representation and updating the scope. | ||||
| 
 | ||||
| `SheetJSExportService` exposes export functions for `XLSB` and `XLSX`.  Other | ||||
| file formats can be exported by changing the `bookType` variable.  It grabs | ||||
| values from the grid, builds an array of arrays, generates a workbook and forces | ||||
| a download.  By setting the `filename` and `sheetname` options in the `ui-grid` | ||||
| options, the output can be controlled. | ||||
| The content has been reorganized; | ||||
| 
 | ||||
| - [The "Legacy Frameworks" section](https://docs.sheetjs.com/docs/getting-started/demos/legacy#angularjs) | ||||
|   covers the AngularJS basics. | ||||
| - [The "Angular UI Grid" section](https://docs.sheetjs.com/docs/getting-started/demos/legacy#angularjs) | ||||
|   covers the integration with Angular UI Grid. | ||||
| 
 | ||||
| [](https://github.com/SheetJS/js-xlsx) | ||||
|  | ||||
							
								
								
									
										96
									
								
								demos/angular/SheetJS-angular.js
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										96
									
								
								demos/angular/SheetJS-angular.js
									
									
									
									
										vendored
									
									
								
							| @ -1,96 +0,0 @@ | ||||
| /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ | ||||
| /* eslint-env browser */ | ||||
| /* global XLSX */ | ||||
| /* exported SheetJSExportService, SheetJSImportDirective */ | ||||
| function SheetJSExportService(uiGridExporterService) { | ||||
| 
 | ||||
|   function exportSheetJS(gridApi, wopts) { | ||||
|     var columns = uiGridExporterService.getColumnHeaders(gridApi.grid, 'all'); | ||||
|     var data = uiGridExporterService.getData(gridApi.grid, 'all', 'all'); | ||||
| 
 | ||||
|     var fileName = gridApi.grid.options.filename || 'SheetJS'; | ||||
|     fileName += wopts.bookType ? "." + wopts.bookType : '.xlsx'; | ||||
| 
 | ||||
|     var sheetName = gridApi.grid.options.sheetname || 'Sheet1'; | ||||
| 
 | ||||
|     var wb = XLSX.utils.book_new(), ws = uigrid_to_sheet(data, columns); | ||||
|     XLSX.utils.book_append_sheet(wb, ws, sheetName); | ||||
|     XLSX.writeFile(wb, fileName); | ||||
|   } | ||||
| 
 | ||||
|   var service = {}; | ||||
|   service.exportXLSB = function exportXLSB(gridApi) { return exportSheetJS(gridApi, { bookType: 'xlsb', bookSST: true, type: 'array' }); }; | ||||
|   service.exportXLSX = function exportXLSX(gridApi) { return exportSheetJS(gridApi, { bookType: 'xlsx', bookSST: true, type: 'array' }); } | ||||
| 
 | ||||
|   return service; | ||||
| 
 | ||||
|   /* utilities */ | ||||
|   function uigrid_to_sheet(data, columns) { | ||||
|     var o = [], oo = [], i = 0, j = 0; | ||||
| 
 | ||||
|     /* column headers */ | ||||
|     for(j = 0; j < columns.length; ++j) oo.push(get_value(columns[j])); | ||||
|     o.push(oo); | ||||
| 
 | ||||
|     /* table data */ | ||||
|     for(i = 0; i < data.length; ++i) { | ||||
|       oo = []; | ||||
|       for(j = 0; j < data[i].length; ++j) oo.push(get_value(data[i][j])); | ||||
|       o.push(oo); | ||||
|     } | ||||
|     /* aoa_to_sheet converts an array of arrays into a worksheet object */ | ||||
|     return XLSX.utils.aoa_to_sheet(o); | ||||
|   } | ||||
| 
 | ||||
|   function get_value(col) { | ||||
|     if(!col) return col; | ||||
|     if(col.value) return col.value; | ||||
|     if(col.displayName) return col.displayName; | ||||
|     if(col.name) return col.name; | ||||
|     return null; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| function SheetJSImportDirective() { | ||||
|   return { | ||||
|     scope: { opts: '=' }, | ||||
|     link: function($scope, $elm) { | ||||
|       $elm.on('change', function(changeEvent) { | ||||
|         var reader = new FileReader(); | ||||
| 
 | ||||
|         reader.onload = function(e) { | ||||
|           /* read workbook */ | ||||
|           var ab = e.target.result; | ||||
|           var wb = XLSX.read(ab); | ||||
| 
 | ||||
|           /* grab first sheet */ | ||||
|           var wsname = wb.SheetNames[0]; | ||||
|           var ws = wb.Sheets[wsname]; | ||||
| 
 | ||||
|           /* grab first row and generate column headers */ | ||||
|           var aoa = XLSX.utils.sheet_to_json(ws, {header:1, raw:false}); | ||||
|           var cols = []; | ||||
|           for(var i = 0; i < aoa[0].length; ++i) cols[i] = { field: aoa[0][i] }; | ||||
| 
 | ||||
|           /* generate rest of the data */ | ||||
|           var data = []; | ||||
|           for(var r = 1; r < aoa.length; ++r) { | ||||
|             data[r-1] = {}; | ||||
|             for(i = 0; i < aoa[r].length; ++i) { | ||||
|               if(aoa[r][i] == null) continue; | ||||
|               data[r-1][aoa[0][i]] = aoa[r][i]; | ||||
|             } | ||||
|           } | ||||
| 
 | ||||
|           /* update scope */ | ||||
|           $scope.$apply(function() { | ||||
|             $scope.opts.columnDefs = cols; | ||||
|             $scope.opts.data = data; | ||||
|           }); | ||||
|         }; | ||||
| 
 | ||||
|         reader.readAsArrayBuffer(changeEvent.target.files[0]); | ||||
|       }); | ||||
|     } | ||||
|   }; | ||||
| } | ||||
							
								
								
									
										45
									
								
								demos/angular/app.js
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										45
									
								
								demos/angular/app.js
									
									
									
									
										vendored
									
									
								
							| @ -1,45 +0,0 @@ | ||||
| /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ | ||||
| /* eslint-env browser */ | ||||
| /* global angular, SheetJSExportService, SheetJSImportDirective */ | ||||
| var app = angular.module('app', ['ngAnimate', 'ngTouch', 'ui.grid', 'ui.grid.selection', 'ui.grid.exporter']); | ||||
| 
 | ||||
| /* Inject SheetJSExportService */ | ||||
| app.factory('SheetJSExportService', SheetJSExportService); | ||||
| SheetJSExportService.inject = ['uiGridExporterService']; | ||||
| 
 | ||||
| app.controller('MainCtrl', ['$scope', '$http','SheetJSExportService', function($scope, $http, SheetJSExportService) { | ||||
|   $scope.gridOptions = { | ||||
|     columnDefs: [ | ||||
|       { field: 'name' }, | ||||
|       { field: 'gender', visible: false}, | ||||
|       { field: 'company' } | ||||
|     ], | ||||
|     enableGridMenu: true, | ||||
|     enableSelectAll: true, | ||||
|     exporterMenuPdf: false, | ||||
|     exporterMenuCsv: false, | ||||
|     showHeader: true, | ||||
|     onRegisterApi: function(gridApi){ | ||||
|       $scope.gridApi = gridApi; | ||||
|     }, | ||||
|     /* SheetJS Service setup */ | ||||
|     filename: "SheetJSAngular", | ||||
|     sheetname: "ng-SheetJS", | ||||
|     gridMenuCustomItems: [ | ||||
|       { | ||||
|         title: 'Export all data as XLSX', | ||||
|         action: function() { SheetJSExportService.exportXLSX($scope.gridApi); }, | ||||
|         order: 200 | ||||
|       }, | ||||
|       { | ||||
|         title: 'Export all data as XLSB', | ||||
|         action: function() { SheetJSExportService.exportXLSB($scope.gridApi); }, | ||||
|         order: 201 | ||||
|       } | ||||
|     ] | ||||
|   }; | ||||
| 
 | ||||
|   $http.get('https://cdn.rawgit.com/angular-ui/ui-grid.info/gh-pages/data/100.json').success(function(data) { $scope.gridOptions.data = data; }); | ||||
| 
 | ||||
| }]); | ||||
| app.directive("importSheetJs", [SheetJSImportDirective]); | ||||
| @ -1,64 +0,0 @@ | ||||
| <!DOCTYPE html> | ||||
| <!-- xlsx.js (C) 2013-present  SheetJS http://sheetjs.com --> | ||||
| <!-- vim: set ts=2: --> | ||||
| <html ng-app="app"> | ||||
| <head> | ||||
|   <title>SheetJS + AngularJS + ui-grid</title> | ||||
|   <!-- Angular --> | ||||
|   <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.js"></script> | ||||
|   <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-touch.js"></script> | ||||
|   <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-animate.js"></script> | ||||
| 
 | ||||
|   <!-- ui-grid --> | ||||
|   <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-grid/4.11.0/ui-grid.js"></script> | ||||
|   <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-grid/4.11.0/ui-grid.css"/> | ||||
| 
 | ||||
|   <!-- SheetJS js-xlsx library --> | ||||
|   <script src="shim.js"></script> | ||||
|   <script src="xlsx.full.min.js"></script> | ||||
| 
 | ||||
|   <!-- SheetJS Service --> | ||||
|   <script src="SheetJS-angular.js"></script> | ||||
| 
 | ||||
| <style> | ||||
| .grid1 { | ||||
|   width: 500px; | ||||
|   height: 400px; | ||||
| }; | ||||
| </style> | ||||
| </head> | ||||
| <body> | ||||
| <pre> | ||||
| <b><a href="http://sheetjs.com">SheetJS + AngularJS demo</a></b> | ||||
| 
 | ||||
| The core library can be used as-is in AngularJS applications. | ||||
| The <a href="https://github.com/sheetjs/js-xlsx">Community Edition README</a> details some common use cases. | ||||
| We also have some <a href="http://sheetjs.com/demos/">more public demos</a> | ||||
| 
 | ||||
| This demo shows: | ||||
| - SheetJSExportService: a service for exporting data from a ui-grid | ||||
| - SheetJSImportDirective: a directive providing a file input button for import | ||||
| 
 | ||||
| <a href="https://obamawhitehouse.archives.gov/sites/default/files/omb/budget/fy2014/assets/receipts.xls">Sample Spreadsheet</a> | ||||
| </pre> | ||||
| 
 | ||||
| <div ng-controller="MainCtrl"> | ||||
|   <input type="file" import-sheet-js="" opts="gridOptions" multiple="false"  /> | ||||
|   <div id="grid1" ui-grid="gridOptions" ui-grid-selection ui-grid-exporter class="grid"></div> | ||||
| </div> | ||||
| 
 | ||||
| <script src="app.js"></script> | ||||
| <script type="text/javascript"> | ||||
| /* eslint no-use-before-define:0 */ | ||||
|   var _gaq = _gaq || []; | ||||
|   _gaq.push(['_setAccount', 'UA-36810333-1']); | ||||
|   _gaq.push(['_trackPageview']); | ||||
| 
 | ||||
|   (function() { | ||||
|     var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; | ||||
|     ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; | ||||
|     var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); | ||||
|   })(); | ||||
| </script> | ||||
| </body> | ||||
| </html> | ||||
| @ -1,75 +0,0 @@ | ||||
| <!DOCTYPE html> | ||||
| <!-- xlsx.js (C) 2013-present  SheetJS http://sheetjs.com --> | ||||
| <!-- vim: set ts=2: --> | ||||
| <html ng-app="sjs"> | ||||
| <head> | ||||
|   <title>SheetJS + AngularJS</title> | ||||
|   <!-- Angular --> | ||||
|   <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script> | ||||
| 
 | ||||
|   <!-- SheetJS js-xlsx library --> | ||||
|   <script src="shim.js"></script> | ||||
|   <script src="xlsx.full.min.js"></script> | ||||
| </head> | ||||
| <body> | ||||
| <pre> | ||||
| <b><a href="http://sheetjs.com">SheetJS + AngularJS demo</a></b> | ||||
| 
 | ||||
| The core library can be used as-is in AngularJS applications. | ||||
| The <a href="https://github.com/sheetjs/js-xlsx">Community Edition README</a> details some common use cases. | ||||
| We also have some <a href="http://sheetjs.com/demos/">more public demos</a> | ||||
| 
 | ||||
| This demo shows: | ||||
| - $http request for XLSX file and scope update with data | ||||
| - HTML table using ng-repeat | ||||
| - XLSX table export using `XLSX.utils.table_to_book` | ||||
| 
 | ||||
| <a href="https://sheetjs.com/pres.xlsx">Sample Spreadsheet</a> | ||||
| </pre> | ||||
| 
 | ||||
| <div ng-controller="sheetjs"> | ||||
| 
 | ||||
| <table id="sjs-table"> | ||||
|   <tr><th>Name</th><th>Index</th></tr> | ||||
|   <tr ng-repeat="row in data"> | ||||
|     <td>{{row.Name}}</td> | ||||
|     <td>{{row.Index}}</td> | ||||
|   </tr> | ||||
| </table> | ||||
| 
 | ||||
| <button id="exportbtn">Export Table</button> | ||||
| 
 | ||||
| </div> | ||||
| 
 | ||||
| <script> | ||||
| var app = angular.module('sjs', []); | ||||
| app.controller('sheetjs', function($scope, $http) { | ||||
|   $http({ | ||||
|     method:'GET', | ||||
|     url:'https://sheetjs.com/pres.xlsx', | ||||
|     responseType:'arraybuffer' | ||||
|   }).then(function(data) { | ||||
|     var wb = XLSX.read(data.data, {type:"array"}); | ||||
|     var d = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]); | ||||
|     $scope.data = d; | ||||
|   }, function(err) { console.log(err); }); | ||||
| }); | ||||
| exportbtn.addEventListener('click', function() { | ||||
|   var wb = XLSX.utils.table_to_book(document.getElementById('sjs-table')); | ||||
|   XLSX.writeFile(wb, "export.xlsx"); | ||||
| }); | ||||
| </script> | ||||
| <script type="text/javascript"> | ||||
| /* eslint no-use-before-define:0 */ | ||||
|   var _gaq = _gaq || []; | ||||
|   _gaq.push(['_setAccount', 'UA-36810333-1']); | ||||
|   _gaq.push(['_trackPageview']); | ||||
| 
 | ||||
|   (function() { | ||||
|     var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; | ||||
|     ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; | ||||
|     var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); | ||||
|   })(); | ||||
| </script> | ||||
| </body> | ||||
| </html> | ||||
							
								
								
									
										1
									
								
								demos/angular/shim.js
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										1
									
								
								demos/angular/shim.js
									
									
									
									
										vendored
									
									
								
							| @ -1 +0,0 @@ | ||||
| ../../shim.js | ||||
							
								
								
									
										1
									
								
								demos/angular/xlsx.core.min.js
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										1
									
								
								demos/angular/xlsx.core.min.js
									
									
									
									
										vendored
									
									
								
							| @ -1 +0,0 @@ | ||||
| ../../dist/xlsx.core.min.js | ||||
							
								
								
									
										1
									
								
								demos/angular/xlsx.full.min.js
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										1
									
								
								demos/angular/xlsx.full.min.js
									
									
									
									
										vendored
									
									
								
							| @ -1 +0,0 @@ | ||||
| ../../dist/xlsx.full.min.js | ||||
							
								
								
									
										4
									
								
								demos/browserify/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										4
									
								
								demos/browserify/.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,4 +0,0 @@ | ||||
| browserify.js | ||||
| browserify.min.js | ||||
| worker.js | ||||
| worker.min.js | ||||
							
								
								
									
										2
									
								
								demos/chrome/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										2
									
								
								demos/chrome/.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,2 +0,0 @@ | ||||
| xlsx.*.js | ||||
| logo.png | ||||
| @ -1,8 +0,0 @@ | ||||
| .PHONY: init | ||||
| init: | ||||
| 	cp ../../dist/xlsx.full.min.js . | ||||
| 	if [ ! -e logo.png ]; then curl -O https://sheetjs.com/logo.png; fi | ||||
| 
 | ||||
| .PHONY: lint | ||||
| lint: | ||||
| 	eslint content.js popup.js table.js | ||||
| @ -1,89 +1,6 @@ | ||||
| # Chrome and Chromium | ||||
| 
 | ||||
| This library is compatible with Chrome and Chromium extensions and should just | ||||
| work out of the box.  Specific API support is listed in the Chrome extensions | ||||
| API documentation. | ||||
| 
 | ||||
| ## Generating Downloads | ||||
| 
 | ||||
| The `writeFile` function works in a Chrome or Chromium extension: | ||||
| 
 | ||||
| ```js | ||||
| XLSX.writeFile(wb, "export.xlsx"); | ||||
| ``` | ||||
| 
 | ||||
| Under the hood, it uses the `chrome.downloads` API.  `"downloads"` permission | ||||
| should be set in `manifest.json`: | ||||
| 
 | ||||
| ```js | ||||
| "permissions": [ | ||||
|   "downloads" | ||||
| ] | ||||
| ``` | ||||
| 
 | ||||
| ## Content Script Table Scraping | ||||
| 
 | ||||
| `table_to_book` and `table_to_sheet` can help build workbooks from DOM tables: | ||||
| 
 | ||||
| ```js | ||||
| var tables = document.getElementsByTagName("table"); | ||||
| var wb = XLSX.utils.book_new(); | ||||
| for(var i = 0; i < tables.length; ++i) { | ||||
|   var ws = XLSX.utils.table_to_sheet(tables[i]); | ||||
|   XLSX.utils.book_append_sheet(wb, ws, "Table" + i); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ## Demo | ||||
| 
 | ||||
| The demo extension includes multiple features to demonstrate sample usage. | ||||
| Production extensions should include proper error handling. | ||||
| 
 | ||||
| #### Table Exporter | ||||
| 
 | ||||
| The `content.js` content script converts a table in the DOM to workbook object | ||||
| using the `table_to_book` utility function: | ||||
| 
 | ||||
| ```js | ||||
| // event page script trigger | ||||
| chrome.tabs.sendMessage(tab.id); | ||||
| // content script convert | ||||
| var wb = XLSX.utils.table_to_book(elt); | ||||
| // event page script callback | ||||
| XLSX.writeFile(wb, "export.xlsx"); | ||||
| ``` | ||||
| 
 | ||||
| Since the workbook object is a plain JS object, the object is sent back to an | ||||
| event page script which generates the file and attempts a download. | ||||
| 
 | ||||
| #### Bookmark Exporter | ||||
| 
 | ||||
| `chrome.bookmarks` API enables bookmark tree traversal.  The "Export Bookmarks" | ||||
| button in the extension pop-up recursively walks the bookmark tree, pushes the | ||||
| bookmark URLs into a data array, and exports into a simple spreadsheet: | ||||
| 
 | ||||
| ```js | ||||
| /* walk the bookmark tree */ | ||||
| function recurse_bookmarks(data, tree) { | ||||
|   if(tree.url) data.push({Name: tree.title, Location: tree.url}); | ||||
|   (tree.children||[]).forEach(function(child) { recurse_bookmarks(data, child); }); | ||||
| } | ||||
| 
 | ||||
| /* get bookmark data */ | ||||
| chrome.bookmarks.getTree(function(res) { | ||||
|   /* load into an array */ | ||||
|   var data = []; | ||||
|   res.forEach(function(t) { recurse_bookmarks(data, t); }); | ||||
| 
 | ||||
|   /* create worksheet */ | ||||
|   var ws = XLSX.utils.json_to_sheet(data, { header: ['Name', 'Location'] }); | ||||
| 
 | ||||
|   /* create workbook and export */ | ||||
|   var wb = XLSX.utils.book_new(); | ||||
|   XLSX.utils.book_append_sheet(wb, ws, 'Bookmarks'); | ||||
|   XLSX.writeFile(wb, "bookmarks.xlsx"); | ||||
| }); | ||||
| ``` | ||||
| 
 | ||||
| [The new demo](https://docs.sheetjs.com/docs/getting-started/demos/chromium) | ||||
| includes more up-to-date details. | ||||
| 
 | ||||
| [](https://github.com/SheetJS/js-xlsx) | ||||
|  | ||||
| @ -1,27 +0,0 @@ | ||||
| /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ | ||||
| /* eslint-env browser */ | ||||
| /* global XLSX, chrome */ | ||||
| var coords = [0,0]; | ||||
| document.addEventListener('mousedown', function(mouse) { | ||||
| 	if(mouse && mouse.button == 2) coords = [mouse.clientX, mouse.clientY]; | ||||
| }); | ||||
| 
 | ||||
| chrome.runtime.onMessage.addListener(function(msg, sender, cb) { | ||||
| 	if(!msg || !msg['Sheet']) return; | ||||
| 	if(msg.Sheet == "JS") { | ||||
| 		var elt = document.elementFromPoint(coords[0], coords[1]); | ||||
| 		while(elt != null) { | ||||
| 			if(elt.tagName.toLowerCase() == "table") return cb(XLSX.utils.table_to_book(elt)); | ||||
| 			elt = elt.parentElement; | ||||
| 		} | ||||
| 	} else if(msg.Sheet == "J5") { | ||||
| 		var tables = document.getElementsByTagName("table"); | ||||
| 		var wb = XLSX.utils.book_new(); | ||||
| 		for(var i = 0; i < tables.length; ++i) { | ||||
| 			var ws = XLSX.utils.table_to_sheet(tables[i]); | ||||
| 			XLSX.utils.book_append_sheet(wb, ws, "Table" + i); | ||||
| 		} | ||||
| 		return cb(wb); | ||||
| 	} | ||||
| 	cb(coords); | ||||
| }); | ||||
| @ -1,30 +0,0 @@ | ||||
| { | ||||
|   "manifest_version": 2, | ||||
|   "name": "SheetJS Demo", | ||||
|   "description": "Sample Extension using SheetJS to interact with Chrome", | ||||
|   "version": "0.0.1", | ||||
|   "browser_action": { | ||||
|     "default_popup": "popup.html", | ||||
|     "default_icon": "logo.png" | ||||
|   }, | ||||
|   "background": { | ||||
|     "scripts": ["xlsx.full.min.js", "table.js"], | ||||
|     "persistent": false | ||||
|   }, | ||||
|   "content_scripts": [{ | ||||
|     "matches": ["<all_urls>"], | ||||
|     "js": ["content.js", "xlsx.full.min.js"], | ||||
|     "run_at": "document_end" | ||||
|   }], | ||||
|   "icons": { | ||||
|     "16": "logo.png" | ||||
|   }, | ||||
|   "permissions": [ | ||||
|     "activeTab", | ||||
|     "<all_urls>", | ||||
|     "bookmarks", | ||||
|     "contextMenus", | ||||
|     "downloads", | ||||
|     "tabs" | ||||
|   ] | ||||
| } | ||||
| @ -1,18 +0,0 @@ | ||||
| <!DOCTYPE html> | ||||
| <!-- (C) 2013-present  SheetJS http://sheetjs.com --> | ||||
| <!-- vim: set ts=2: --> | ||||
| <html> | ||||
| <head> | ||||
| <title>SheetJS Chrome Extension Export Test</title> | ||||
| <meta charset="utf-8" /> | ||||
| </head> | ||||
| <body> | ||||
| <!-- SheetJS js-xlsx library --> | ||||
| <script type="text/javascript" src="xlsx.full.min.js"></script> | ||||
| 
 | ||||
| <button type="button" id="sjsdownload">Export Bookmarks</button> | ||||
| <a><div id="sjsversion">Version</div></a> | ||||
| 
 | ||||
| <script type="text/javascript" src="popup.js"></script> | ||||
| </body> | ||||
| </html> | ||||
| @ -1,31 +0,0 @@ | ||||
| /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ | ||||
| /* eslint-env browser */ | ||||
| /* global XLSX, chrome */ | ||||
| document.getElementById('sjsversion').innerText = "SheetJS " + XLSX.version; | ||||
| 
 | ||||
| document.getElementById('sjsversion').addEventListener('click', function() { | ||||
|   chrome.tabs.create({url: "https://sheetjs.com/"}); return false; | ||||
| }); | ||||
| 
 | ||||
| /* recursively walk the bookmark tree */ | ||||
| function recurse_bookmarks(data, tree, path) { | ||||
|   if(tree.url) data.push({Name: tree.title, Location: tree.url, Path:path}); | ||||
|   var T = path ? (path + "::" + tree.title) : tree.title; | ||||
|   (tree.children||[]).forEach(function(C) { recurse_bookmarks(data, C, T); }); | ||||
| } | ||||
| 
 | ||||
| /* export bookmark data */ | ||||
| document.getElementById('sjsdownload').addEventListener('click', function() { | ||||
|   chrome.bookmarks.getTree(function(res) { | ||||
|     var data = []; | ||||
|     res.forEach(function(t) { recurse_bookmarks(data, t, ""); }); | ||||
| 
 | ||||
|     /* create worksheet */ | ||||
|     var ws = XLSX.utils.json_to_sheet(data, { header: ['Name', 'Location', 'Path'] }); | ||||
| 
 | ||||
|     /* create workbook and export */ | ||||
|     var wb = XLSX.utils.book_new(); | ||||
|     XLSX.utils.book_append_sheet(wb, ws, 'Bookmarks'); | ||||
|     XLSX.writeFile(wb, "bookmarks.xlsx"); | ||||
|   }); | ||||
| }); | ||||
| @ -1,43 +0,0 @@ | ||||
| /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ | ||||
| /* eslint-env browser */ | ||||
| /* global XLSX, chrome */ | ||||
| chrome.runtime.onInstalled.addListener(function() { | ||||
| 	chrome.contextMenus.create({ | ||||
| 		type: "normal", | ||||
| 		id: "sjsexport", | ||||
| 		title: "Export Table to XLSX", | ||||
| 		contexts: ["page", "selection"] | ||||
| 	}); | ||||
| 	chrome.contextMenus.create({ | ||||
| 		type: "normal", | ||||
| 		id: "sj5export", | ||||
| 		title: "Export All Tables in Page", | ||||
| 		contexts: ["page", "selection"] | ||||
| 	}); | ||||
| 	chrome.contextMenus.onClicked.addListener(function(info/*, tab*/) { | ||||
| 		var mode = ""; | ||||
| 		switch(info.menuItemId) { | ||||
| 			case 'sjsexport': mode = "JS"; break; | ||||
| 			case 'sj5export': mode = "J5"; break; | ||||
| 			default: return; | ||||
| 		} | ||||
| 		chrome.tabs.query({active: true, currentWindow: true}, function(tabs){ | ||||
| 			chrome.tabs.sendMessage(tabs[0].id, {Sheet:mode}, sjsexport_cb); | ||||
| 		}); | ||||
| 	}); | ||||
| 
 | ||||
| 	chrome.contextMenus.create({ | ||||
| 		id: "sjsabout", | ||||
| 		title: "About", | ||||
| 		contexts: ["browser_action"] | ||||
| 	}); | ||||
| 	chrome.contextMenus.onClicked.addListener(function(info/*, tab*/) { | ||||
| 		if(info.menuItemId !== "sjsabout") return; | ||||
| 		chrome.tabs.create({url: "https://sheetjs.com/"}); | ||||
| 	}); | ||||
| }); | ||||
| 
 | ||||
| function sjsexport_cb(wb) { | ||||
| 	if(!wb || !wb.SheetNames || !wb.Sheets) { return alert("Error in exporting table"); } | ||||
| 	XLSX.writeFile(wb, "export.xlsx"); | ||||
| } | ||||
| @ -1,7 +0,0 @@ | ||||
| { | ||||
| 	"env": { "shared-node-browser":true }, | ||||
| 	"parserOptions": { | ||||
| 		"ecmaVersion": 8 | ||||
| 	}, | ||||
| 	"plugins": [ "html", "json" ] | ||||
| } | ||||
							
								
								
									
										1
									
								
								demos/database/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										1
									
								
								demos/database/.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1 +0,0 @@ | ||||
| *.db | ||||
| @ -1,89 +0,0 @@ | ||||
| <!DOCTYPE html> | ||||
| <!-- xlsx.js (C) 2013-present  SheetJS http://sheetjs.com --> | ||||
| <!-- vim: set ts=2: --> | ||||
| <html> | ||||
| <head> | ||||
| <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | ||||
| <title>SheetJS Firebase Dump Demo</title> | ||||
| <style> | ||||
| a { text-decoration: none } | ||||
| </style> | ||||
| </head> | ||||
| <body> | ||||
| <pre> | ||||
| <h3><a href="//sheetjs.com/">SheetJS</a> Firebase Dump Demo</h3> | ||||
| <b>Example Code</b> | ||||
| 
 | ||||
|   /* ... connect to firebase */ | ||||
|   const database = firebase.database(); | ||||
| 
 | ||||
|   /* import workbook */ | ||||
|   await database.ref('foo').set(workbook); | ||||
| 
 | ||||
|   /* change cells */ | ||||
|   database.ref('foo').update({ | ||||
|     "Sheets/Sheet1/A1": {"t": "s", "v": "J"}, | ||||
|     "Sheets/Sheet1/A2": {"t": "n", "v": 5}, | ||||
|   }); | ||||
| 
 | ||||
|   /* export snapshot */ | ||||
|   const val = await database.ref('foo').once('value').val(); | ||||
|   XLSX.writeFile(wb, "firebase.xlsx"); | ||||
| 
 | ||||
| </pre> | ||||
| <script src="xlsx.full.min.js"></script> | ||||
| <script src="https://www.gstatic.com/firebasejs/4.12.0/firebase.js"></script> | ||||
| <script> | ||||
| const Firebase = firebase; | ||||
| const config = { | ||||
|   credential: { | ||||
|     getAccessToken: () => ({ | ||||
|       expires_in: 0, | ||||
|       access_token: '', | ||||
|     }), | ||||
|   }, | ||||
|   databaseURL: 'ws://localhost:5555' | ||||
| }; | ||||
| 
 | ||||
| /* make new workbook object from CSV */ | ||||
| const wb = XLSX.read('a,b,c\n1,2,3', {type:"binary"}); | ||||
| 
 | ||||
| let P = Promise.resolve("sheetjs"); | ||||
| 
 | ||||
| /* Connect to Firebase server and initialize collection */ | ||||
| P = P.then(async () => { | ||||
|   Firebase.initializeApp(config); | ||||
|   const database = Firebase.database(); | ||||
|   await database.ref('foo').set(null); | ||||
|   return [database]; | ||||
| }); | ||||
| 
 | ||||
| /* Insert entire workbook object into `foo` ref */ | ||||
| P = P.then(async ([database]) => { | ||||
|   await database.ref('foo').set(wb); | ||||
|   return [database]; | ||||
| }); | ||||
| 
 | ||||
| /* Change cell A1 of Sheet1 to "J" and change A2 to 5 */ | ||||
| P = P.then(async ([database]) => { | ||||
|   database.ref('foo').update({ | ||||
|     "Sheets/Sheet1/A1": {"t": "s", "v": "J"}, | ||||
|     "Sheets/Sheet1/A2": {"t": "n", "v": 5}, | ||||
|   }); | ||||
|   return [database]; | ||||
| }); | ||||
| 
 | ||||
| /* Write to file */ | ||||
| P = P.then(async ([database]) => { | ||||
|   const val = await database.ref('foo').once('value'); | ||||
|   const wb = await val.val(); | ||||
|   XLSX.writeFile(wb, "firebase.xlsx"); | ||||
|   console.log(csv); | ||||
|   return [database]; | ||||
| }); | ||||
| 
 | ||||
| /* Close connection */ | ||||
| P = P.then(async ([database]) => { database.app.delete(); }); | ||||
| 
 | ||||
| </script> | ||||
| </body> | ||||
| @ -1,58 +0,0 @@ | ||||
| /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ | ||||
| /* eslint-env node */ | ||||
| const XLSX = require('xlsx'); | ||||
| const assert = require('assert'); | ||||
| const Firebase = require('firebase-admin'); | ||||
| 
 | ||||
| const config = { | ||||
| 	credential: { | ||||
| 		getAccessToken: () => ({ | ||||
| 			expires_in: 0, | ||||
| 			access_token: '', | ||||
| 		}), | ||||
| 	}, | ||||
| 	databaseURL: 'ws://localhost:5555' | ||||
| }; | ||||
| 
 | ||||
| /* make new workbook object from CSV */ | ||||
| const wb = XLSX.read('a,b,c\n1,2,3', {type:"binary", raw:true}); | ||||
| 
 | ||||
| let P = Promise.resolve("sheetjs"); | ||||
| 
 | ||||
| /* Connect to Firebase server and initialize collection */ | ||||
| P = P.then(async () => { | ||||
| 	Firebase.initializeApp(config); | ||||
| 	const database = Firebase.database(); | ||||
| 	await database.ref('foo').set(null); | ||||
| 	return [database]; | ||||
| }); | ||||
| 
 | ||||
| /* Insert entire workbook object into `foo` ref */ | ||||
| P = P.then(async ([database]) => { | ||||
| 	await database.ref('foo').set(wb); | ||||
| 	return [database]; | ||||
| }); | ||||
| 
 | ||||
| /* Change cell A1 of Sheet1 to "J" and change A2 to 5 */ | ||||
| P = P.then(async ([database]) => { | ||||
| 	database.ref('foo').update({ | ||||
| 		"Sheets/Sheet1/A1": {"t": "s", "v": "J"}, | ||||
| 		"Sheets/Sheet1/A2": {"t": "n", "v": 5}, | ||||
| 	}); | ||||
| 	return [database]; | ||||
| }); | ||||
| 
 | ||||
| /* Write to file */ | ||||
| P = P.then(async ([database]) => { | ||||
| 	const val = await database.ref('foo').once('value'); | ||||
| 	const wb = await val.val(); | ||||
| 	XLSX.writeFile(wb, "firebase.xlsx"); | ||||
| 	const ws = XLSX.readFile("firebase.xlsx").Sheets.Sheet1; | ||||
| 	const csv = XLSX.utils.sheet_to_csv(ws); | ||||
| 	assert.equal(csv, "J,b,c\n5,2,3\n"); | ||||
| 	console.log(csv); | ||||
| 	return [database]; | ||||
| }); | ||||
| 
 | ||||
| /* Close connection */ | ||||
| P = P.then(async ([database]) => { database.app.delete(); }); | ||||
| @ -1,73 +0,0 @@ | ||||
| /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ | ||||
| /* eslint-env node */ | ||||
| /* global Promise */ | ||||
| const XLSX = require('xlsx'); | ||||
| const assert = require('assert'); | ||||
| const SheetJSKnex = require("./SheetJSKnex"); | ||||
| const Knex = require('knex'); | ||||
| 
 | ||||
| /* Connection to both databases are passed around */ | ||||
| let P = Promise.resolve([ | ||||
| 	Knex({ client: 'sqlite3', connection: { filename: './knex1.db' } }), | ||||
| 	Knex({ client: 'sqlite3', connection: { filename: './knex2.db' } }) | ||||
| ]); | ||||
| 
 | ||||
| /* Sample data table */ | ||||
| P = P.then(async (_) => { | ||||
| 	const [knex] = _; | ||||
| 	await knex.schema.dropTableIfExists('pres'); | ||||
| 	await knex.schema.createTable('pres', (table) => { | ||||
| 		table.string('name'); | ||||
| 		table.integer('idx'); | ||||
| 	}); | ||||
| 	await knex.insert([ | ||||
| 		{ name: "Barack Obama", idx: 44 }, | ||||
| 		{ name: "Donald Trump", idx: 45 } | ||||
| 	]).into('pres'); | ||||
| 
 | ||||
| 	await knex.schema.dropTableIfExists('fmts'); | ||||
| 	await knex.schema.createTable('fmts', (table) => { | ||||
| 		table.string('ext'); | ||||
| 		table.string('ctr'); | ||||
| 		table.integer('multi'); | ||||
| 	}); | ||||
| 	await knex.insert([ | ||||
| 		{ ext: 'XLSB', ctr: 'ZIP', multi: 1 }, | ||||
| 		{ ext: 'XLS',  ctr: 'CFB', multi: 1 }, | ||||
| 		{ ext: 'XLML', ctr: '',    multi: 1 }, | ||||
| 		{ ext: 'CSV',  ctr: 'ZIP', multi: 0 }, | ||||
| 	]).into('fmts'); | ||||
| 
 | ||||
| 	return _; | ||||
| }); | ||||
| 
 | ||||
| /* Export database to XLSX */ | ||||
| P = P.then(async (_) => { | ||||
| 	const [knex] = _; | ||||
| 	const wb = XLSX.utils.book_new(); | ||||
| 	await SheetJSKnex.book_append_knex(wb, knex, "pres"); | ||||
| 	await SheetJSKnex.book_append_knex(wb, knex, "fmts"); | ||||
| 	XLSX.writeFile(wb, "knex.xlsx"); | ||||
| 	return _; | ||||
| }); | ||||
| 
 | ||||
| /* Import XLSX to database */ | ||||
| P = P.then(async (_) => { | ||||
| 	const [, knex] = _; | ||||
| 	const wb = XLSX.readFile("knex.xlsx"); | ||||
| 	await SheetJSKnex.wb_to_knex(wb, knex); | ||||
| 	return _; | ||||
| }); | ||||
| 
 | ||||
| /* Compare databases */ | ||||
| P = P.then(async (_) => { | ||||
| 	const [k1, k2] = _; | ||||
| 	const P1 = await k1.select("*").from('pres'); | ||||
| 	const P2 = await k2.select("*").from('pres'); | ||||
| 	const F1 = await k1.select("*").from('fmts'); | ||||
| 	const F2 = await k2.select("*").from('fmts'); | ||||
| 	assert.deepEqual(P1, P2); | ||||
| 	assert.deepEqual(F1, F2); | ||||
| }); | ||||
| 
 | ||||
| P.then(async () => { process.exit(); }); | ||||
| @ -1,59 +0,0 @@ | ||||
| <!DOCTYPE html> | ||||
| <!-- xlsx.js (C) 2013-present  SheetJS http://sheetjs.com --> | ||||
| <!-- vim: set ts=2: --> | ||||
| <html> | ||||
| <head> | ||||
| <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | ||||
| <title>SheetJS Live Demo</title> | ||||
| <style> | ||||
| a { text-decoration: none } | ||||
| </style> | ||||
| </head> | ||||
| <body> | ||||
| <pre> | ||||
| <b><a href="http://sheetjs.com">SheetJS LocalStorage Demo</a></b> | ||||
| <pre id="data_"> | ||||
| Original Data: | ||||
| </pre> | ||||
| <pre id="out"> | ||||
| Output: | ||||
| 
 | ||||
| </pre> | ||||
| <script src="xlsx.full.min.js"></script> | ||||
| <script src="ObjUtils.js"></script> | ||||
| <script src="https://unpkg.com/localforage/dist/localforage.min.js"></script> | ||||
| <script src="SheetJSForage.js"></script> | ||||
| <script> | ||||
| /* eslint-env browser */ | ||||
| /*global XLSX, localforage */ | ||||
| var data = { | ||||
|   "title": "SheetDB", | ||||
|   "metadata": { | ||||
|     "author": "SheetJS", | ||||
|     "code": 7262 | ||||
|   }, | ||||
|   "data": [ | ||||
|     { "Name": "Barack Obama", "Index": 44 }, | ||||
|     { "Name": "Donald Trump", "Index": 45 }, | ||||
|   ] | ||||
| }; | ||||
| document.getElementById("data_").innerText += JSON.stringify(data, 2, 2); | ||||
| 
 | ||||
| localforage.setDriver(localforage.INDEXEDDB); | ||||
| (async function() { | ||||
|   await localforage.clear(); | ||||
| 
 | ||||
|   await localforage.load(data); | ||||
|   var wb = await localforage.dump(); | ||||
|   console.log(wb); | ||||
| 
 | ||||
|   var OUT = document.getElementById("out"); | ||||
|   wb.SheetNames.forEach(function(n, i) { | ||||
|     OUT.innerText += "Sheet " + i + " (" + n + ")\n"; | ||||
|     OUT.innerText += XLSX.utils.sheet_to_csv(wb.Sheets[n]); | ||||
|     OUT.innerText += "\n"; | ||||
|   }); | ||||
| })(); | ||||
| </script> | ||||
| </body> | ||||
| </html> | ||||
| @ -1,57 +0,0 @@ | ||||
| <!DOCTYPE html> | ||||
| <!-- xlsx.js (C) 2013-present  SheetJS http://sheetjs.com --> | ||||
| <!-- vim: set ts=2: --> | ||||
| <html> | ||||
| <head> | ||||
| <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | ||||
| <title>SheetJS Live Demo</title> | ||||
| <style> | ||||
| a { text-decoration: none } | ||||
| </style> | ||||
| </head> | ||||
| <body> | ||||
| <pre> | ||||
| <b><a href="http://sheetjs.com">SheetJS LocalStorage Demo</a></b> | ||||
| <pre id="data_"> | ||||
| Original Data: | ||||
| </pre> | ||||
| <pre id="out"> | ||||
| Output: | ||||
| 
 | ||||
| </pre> | ||||
| <script src="xlsx.full.min.js"></script> | ||||
| <script src="ObjUtils.js"></script> | ||||
| <script src="SheetJSStorage.js"></script> | ||||
| <script> | ||||
| /* eslint-env browser */ | ||||
| /*global XLSX */ | ||||
| var data = { | ||||
|   "title": "SheetDB", | ||||
|   "metadata": { | ||||
|     "author": "SheetJS", | ||||
|     "code": 7262 | ||||
|   }, | ||||
|   "data": [ | ||||
|     { "Name": "Barack Obama", "Index": 44 }, | ||||
|     { "Name": "Donald Trump", "Index": 45 }, | ||||
|   ] | ||||
| }; | ||||
| document.getElementById("data_").innerText += JSON.stringify(data, 2, 2); | ||||
| 
 | ||||
| (function() { | ||||
|   localStorage.clear(); | ||||
| 
 | ||||
|   localStorage.load(data); | ||||
|   var wb = localStorage.dump(); | ||||
|   console.log(wb); | ||||
| 
 | ||||
|   var OUT = document.getElementById("out"); | ||||
|   wb.SheetNames.forEach(function(n, i) { | ||||
|     OUT.innerText += "Sheet " + i + " (" + n + ")\n"; | ||||
|     OUT.innerText += XLSX.utils.sheet_to_csv(wb.Sheets[n]); | ||||
|     OUT.innerText += "\n"; | ||||
|   }); | ||||
| })(); | ||||
| </script> | ||||
| </body> | ||||
| </html> | ||||
| @ -1,23 +0,0 @@ | ||||
| /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ | ||||
| /* eslint-env node */ | ||||
| var low = require('lowdb'); | ||||
| var SheetJSAdapter = require('./SheetJSLowDB'); | ||||
| var adapter = new SheetJSAdapter(); | ||||
| var db = low(adapter); | ||||
| 
 | ||||
| db.defaults({ posts: [], user: {}, count: 0 }).write(); | ||||
| db.get('posts').push({ id: 1, title: 'lowdb is awesome'}).write(); | ||||
| db.set('user.name', 'typicode').write(); | ||||
| db.update('count', function(n) { return n + 1; }).write(); | ||||
| 
 | ||||
| adapter.dumpFile('ldb1.xlsx'); | ||||
| 
 | ||||
| var adapter2 = new SheetJSAdapter(); | ||||
| adapter2.loadFile('ldb1.xlsx'); | ||||
| var db2 = low(adapter2); | ||||
| 
 | ||||
| db2.get('posts').push({ id: 2, title: 'mongodb is not'}).write(); | ||||
| db2.set('user.name', 'sheetjs').write(); | ||||
| db2.update('count', function(n) { return n + 1; }).write(); | ||||
| 
 | ||||
| adapter2.dumpFile('ldb2.xlsx'); | ||||
| @ -1,16 +0,0 @@ | ||||
| .PHONY: init | ||||
| init: | ||||
| 	rm -f node_modules/xlsx | ||||
| 	mkdir -p node_modules | ||||
| 	cd node_modules; ln -s ../../../ xlsx; cd - | ||||
| 	rm -f xlsx.full.min.js | ||||
| 	ln -s ../../dist/xlsx.full.min.js | ||||
| 
 | ||||
| FILES=$(filter-out xlsx.full.min.js,$(wildcard *.js)) $(wildcard *.html) | ||||
| .PHONY: lint | ||||
| lint: $(FILES) | ||||
| 	eslint $(FILES) | ||||
| 
 | ||||
| .PHONY: clean | ||||
| clean: | ||||
| 	rm -f *.db *.xlsx *.csv | ||||
| @ -1,63 +0,0 @@ | ||||
| /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ | ||||
| /* eslint-env node */ | ||||
| /* global Promise */ | ||||
| const XLSX = require('xlsx'); | ||||
| const SheetJSMongo = require("./SheetJSMongo"); | ||||
| const MongoClient = require('mongodb').MongoClient; | ||||
| 
 | ||||
| const url = 'mongodb://localhost:27017/sheetjs'; | ||||
| const db_name = 'sheetjs'; | ||||
| 
 | ||||
| let P = Promise.resolve("sheetjs"); | ||||
| 
 | ||||
| /* Connect to mongodb server */ | ||||
| P = P.then(async () => { | ||||
| 	const client = await MongoClient.connect(url,{ useUnifiedTopology: true }); | ||||
| 	return [client]; | ||||
| }); | ||||
| 
 | ||||
| /* Sample data table */ | ||||
| P = P.then(async ([client]) => { | ||||
| 	const db = client.db(db_name); | ||||
| 
 | ||||
| 	try { await db.collection('pres').drop(); } catch(e) {} | ||||
| 	const pres = db.collection('pres'); | ||||
| 	await pres.insertMany([ | ||||
| 		{ name: "Barack Obama", idx: 44 }, | ||||
| 		{ name: "Donald Trump", idx: 45 } | ||||
| 	], {ordered: true}); | ||||
| 
 | ||||
| 	try { await db.collection('fmts').drop(); } catch(e) {} | ||||
| 	const fmts = db.collection('fmts'); | ||||
| 	await fmts.insertMany([ | ||||
| 		{ ext: 'XLSB', ctr: 'ZIP', multi: 1 }, | ||||
| 		{ ext: 'XLS',  ctr: 'CFB', multi: 1 }, | ||||
| 		{ ext: 'XLML',             multi: 1 }, | ||||
| 		{ ext: 'CSV',  ctr: 'ZIP', multi: 0 }, | ||||
| 	], {ordered: true}); | ||||
| 
 | ||||
| 	return [client, pres, fmts]; | ||||
| }); | ||||
| 
 | ||||
| /* Export database to XLSX */ | ||||
| P = P.then(async ([client, pres, fmts]) => { | ||||
| 	const wb = XLSX.utils.book_new(); | ||||
| 	await SheetJSMongo.book_append_mongo(wb, pres, "pres"); | ||||
| 	await SheetJSMongo.book_append_mongo(wb, fmts, "fmts"); | ||||
| 	XLSX.writeFile(wb, "mongocrud.xlsx"); | ||||
| 	return [client, pres, fmts]; | ||||
| }); | ||||
| 
 | ||||
| /* Read the new file and dump all of the data */ | ||||
| P = P.then(([client]) => { | ||||
| 	const wb = XLSX.readFile('mongocrud.xlsx'); | ||||
| 	wb.SheetNames.forEach((n,i) => { | ||||
| 		console.log(`Sheet #${i+1}: ${n}`); | ||||
| 		const ws = wb.Sheets[n]; | ||||
| 		console.log(XLSX.utils.sheet_to_csv(ws)); | ||||
| 	}); | ||||
|     return [client]; | ||||
| }); | ||||
| 
 | ||||
| /* Close connection */ | ||||
| P.then(async ([client]) => { client.close(); }); | ||||
| @ -1,54 +0,0 @@ | ||||
| /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ | ||||
| /* eslint-env node */ | ||||
| /* global Promise */ | ||||
| const XLSX = require('xlsx'); | ||||
| const assert = require('assert'); | ||||
| const MongoClient = require('mongodb').MongoClient; | ||||
| 
 | ||||
| const url = 'mongodb://localhost:27017/sheetjs'; | ||||
| const db_name = 'sheetjs'; | ||||
| 
 | ||||
| /* make new workbook object from CSV */ | ||||
| const wb = XLSX.read('a,b,c\n1,2,3', {type:"binary", raw:true}); | ||||
| 
 | ||||
| let P = Promise.resolve("sheetjs"); | ||||
| 
 | ||||
| /* Connect to mongodb server and initialize collection */ | ||||
| P = P.then(async () => { | ||||
| 	const client = await MongoClient.connect(url,{ useUnifiedTopology: true }); | ||||
| 	const db = client.db(db_name); | ||||
| 	try { await db.collection('wb').drop(); } catch(e) {} | ||||
| 	const coll = db.collection('wb'); | ||||
| 	return [client, coll]; | ||||
| }); | ||||
| 
 | ||||
| /* Insert entire workbook object as a document */ | ||||
| P = P.then(async ([client, coll]) => { | ||||
| 	const res = await coll.insertOne(wb); | ||||
| 	assert.equal(res.insertedCount, 1); | ||||
| 	return [client, coll]; | ||||
| }); | ||||
| 
 | ||||
| /* Change cell A1 of Sheet1 to "J" and change A2 to 5 */ | ||||
| P = P.then(async ([client, coll]) => { | ||||
| 	const res = await coll.updateOne({}, { $set: { | ||||
| 		"Sheets.Sheet1.A1": {"t": "s", "v": "J"}, | ||||
| 		"Sheets.Sheet1.A2": {"t": "n", "v": 5}, | ||||
| 	}}); | ||||
| 	assert.equal(res.matchedCount, 1); | ||||
| 	assert.equal(res.modifiedCount, 1); | ||||
| 	return [client, coll]; | ||||
| }); | ||||
| 
 | ||||
| /* Write to file */ | ||||
| P = P.then(async ([client, coll]) => { | ||||
| 	const res = await coll.find({}).toArray(); | ||||
| 	const wb = res[0]; | ||||
| 	XLSX.writeFile(wb, "mongo.xlsx"); | ||||
| 	const ws = XLSX.readFile("mongo.xlsx").Sheets.Sheet1; | ||||
| 	console.log(XLSX.utils.sheet_to_csv(ws)); | ||||
| 	return [client, coll]; | ||||
| }); | ||||
| 
 | ||||
| /* Close connection */ | ||||
| P.then(async ([client]) => { client.close(); }); | ||||
| @ -1,70 +0,0 @@ | ||||
| /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ | ||||
| /* eslint-env node */ | ||||
| var XLSX = require('xlsx'); | ||||
| var assert = require('assert'); | ||||
| var SheetJSSQL = require('./SheetJSSQL'); | ||||
| var mysql = require('mysql2/promise'); | ||||
| 
 | ||||
| /* Connection options (requires two databases sheetjs and sheetj5) */ | ||||
| var opts = { | ||||
|   host     : 'localhost', | ||||
|   user     : 'SheetJS', | ||||
|   password : 'SheetJS', | ||||
| }; | ||||
| 
 | ||||
| /* Sample data table */ | ||||
| var init = [ | ||||
|   "DROP TABLE IF EXISTS pres", | ||||
|   "CREATE TABLE pres (name TEXT, idx TINYINT)", | ||||
|   "INSERT INTO pres VALUES ('Barack Obama', 44)", | ||||
|   "INSERT INTO pres VALUES ('Donald Trump', 45)", | ||||
|   "DROP TABLE IF EXISTS fmts", | ||||
|   "CREATE TABLE fmts (ext TEXT, ctr TEXT, multi TINYINT)", | ||||
|   "INSERT INTO fmts VALUES ('XLSB', 'ZIP', 1)", | ||||
|   "INSERT INTO fmts VALUES ('XLS',  'CFB', 1)", | ||||
|   "INSERT INTO fmts VALUES ('XLML', '',    1)", | ||||
|   "INSERT INTO fmts VALUES ('CSV',  '',    0)", | ||||
| ]; | ||||
| 
 | ||||
| (async () => { | ||||
|   const conn1 = await mysql.createConnection(Object.assign({}, opts, {database: "sheetjs"})); | ||||
|   for(var i = 0; i < init.length; ++i) await conn1.query(init[i]); | ||||
| 
 | ||||
|   /* Export table to XLSX */ | ||||
|   var wb = XLSX.utils.book_new(); | ||||
| 
 | ||||
|   async function book_append_table(wb, name) { | ||||
|     var r_f = await conn1.query('SELECT * FROM ' + name); | ||||
|     var r = r_f[0]; | ||||
|     var ws = XLSX.utils.json_to_sheet(r); | ||||
|     XLSX.utils.book_append_sheet(wb, ws, name); | ||||
|   } | ||||
| 
 | ||||
|   await book_append_table(wb, "pres"); | ||||
|   await book_append_table(wb, "fmts"); | ||||
|   XLSX.writeFile(wb, "mysql.xlsx"); | ||||
| 
 | ||||
|   /* Capture first database info and close */ | ||||
|   var P1 = (await conn1.query("SELECT * FROM pres"))[0]; | ||||
|   var F1 = (await conn1.query("SELECT * FROM fmts"))[0]; | ||||
|   await conn1.close(); | ||||
| 
 | ||||
|   /* Import XLSX to table */ | ||||
|   const conn2 = await mysql.createConnection(Object.assign({}, opts, {database: "sheetj5"})); | ||||
|   var wb2 = XLSX.readFile("mysql.xlsx"); | ||||
|   var queries = SheetJSSQL.book_to_sql(wb2, "MYSQL"); | ||||
|   for(i = 0; i < queries.length; ++i) await conn2.query(queries[i]); | ||||
| 
 | ||||
|   /* Capture first database info and close */ | ||||
|   var P2 = (await conn2.query("SELECT * FROM pres"))[0]; | ||||
|   var F2 = (await conn2.query("SELECT * FROM fmts"))[0]; | ||||
|   await conn2.close(); | ||||
| 
 | ||||
|   /* Compare results */ | ||||
|   assert.deepEqual(P1, P2); | ||||
|   assert.deepEqual(F1, F2); | ||||
| 
 | ||||
|   /* Display results */ | ||||
|   console.log(P2); | ||||
|   console.log(F2); | ||||
| })(); | ||||
| @ -1,59 +0,0 @@ | ||||
| /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ | ||||
| /*global XLSX, module, require */ | ||||
| var ObjUtils = (function() { | ||||
| 
 | ||||
| var X; | ||||
| if(typeof XLSX !== "undefined") X = XLSX; | ||||
| else if(typeof require !== 'undefined') X = require('xlsx'); | ||||
| else throw new Error("Could not find XLSX"); | ||||
| 
 | ||||
| function walk(obj, key, arr) { | ||||
|   if(Array.isArray(obj)) return; | ||||
|   if(typeof obj != "object" || obj instanceof Date) { arr.push({path:key, value:obj}); return; } | ||||
|   Object.keys(obj).forEach(function(k) { | ||||
|     walk(obj[k], key ? key + "." + k : k, arr); | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| function object_to_workbook(obj) { | ||||
|   var wb = X.utils.book_new(); | ||||
| 
 | ||||
|   var base = []; walk(obj, "", base); | ||||
|   var ws = X.utils.json_to_sheet(base, {header:["path", "value"]}); | ||||
|   X.utils.book_append_sheet(wb, ws, "_keys"); | ||||
| 
 | ||||
|   Object.keys(obj).forEach(function(k) { | ||||
|     if(!Array.isArray(obj[k])) return; | ||||
|     X.utils.book_append_sheet(wb, X.utils.json_to_sheet(obj[k]), k); | ||||
|   }); | ||||
| 
 | ||||
|   return wb; | ||||
| } | ||||
| 
 | ||||
| function deepset(obj, path, value) { | ||||
|   if(path.indexOf(".") == -1) return obj[path] = value; | ||||
|   var parts = path.split("."); | ||||
|   if(!obj[parts[0]]) obj[parts[0]] = {}; | ||||
|   return deepset(obj[parts[0]], parts.slice(1).join("."), value); | ||||
| } | ||||
| function workbook_set_object(obj, wb) { | ||||
|   var ws = wb.Sheets["_keys"]; if(ws) { | ||||
|     var data = X.utils.sheet_to_json(ws, {raw:true}); | ||||
|     data.forEach(function(r) { deepset(obj, r.path, r.value); }); | ||||
|   } | ||||
|   wb.SheetNames.forEach(function(n) { | ||||
|     if(n == "_keys") return; | ||||
|     obj[n] = X.utils.sheet_to_json(wb.Sheets[n], {raw:true}); | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| function workbook_to_object(wb) { var obj = {}; workbook_set_object(obj, wb); return obj; } | ||||
| 
 | ||||
| return { | ||||
|   workbook_to_object: workbook_to_object, | ||||
|   object_to_workbook: object_to_workbook, | ||||
|   workbook_set_object: workbook_set_object | ||||
| }; | ||||
| })(); | ||||
| 
 | ||||
| if(typeof module !== 'undefined') module.exports = ObjUtils; | ||||
| @ -1,72 +0,0 @@ | ||||
| /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ | ||||
| /* eslint-env node */ | ||||
| var XLSX = require('xlsx'); | ||||
| var assert = require('assert'); | ||||
| var SheetJSSQL = require('./SheetJSSQL'); | ||||
| var Client = require('pg').Client; | ||||
| 
 | ||||
| /* Connection options (requires two databases sheetjs and sheetj5) */ | ||||
| var opts = { | ||||
|   host     : 'localhost', | ||||
|   user     : 'SheetJS', | ||||
|   password : 'SheetJS', | ||||
| }; | ||||
| 
 | ||||
| /* Sample data table */ | ||||
| var init = [ | ||||
|   "DROP TABLE IF EXISTS pres", | ||||
|   "CREATE TABLE pres (name text, idx smallint)", | ||||
|   "INSERT INTO pres VALUES ('Barack Obama', 44)", | ||||
|   "INSERT INTO pres VALUES ('Donald Trump', 45)", | ||||
|   "DROP TABLE IF EXISTS fmts", | ||||
|   "CREATE TABLE fmts (ext text, ctr text, multi smallint)", | ||||
|   "INSERT INTO fmts VALUES ('XLSB', 'ZIP', 1)", | ||||
|   "INSERT INTO fmts VALUES ('XLS',  'CFB', 1)", | ||||
|   "INSERT INTO fmts VALUES ('XLML', '',    1)", | ||||
|   "INSERT INTO fmts VALUES ('CSV',  '',    0)", | ||||
| ]; | ||||
| 
 | ||||
| var conn1 = new Client(Object.assign({}, opts, {database: "sheetjs"})); | ||||
| var conn2 = new Client(Object.assign({}, opts, {database: "sheetj5"})); | ||||
| (async () => { | ||||
|   await conn1.connect(); | ||||
|   for(var i = 0; i < init.length; ++i) await conn1.query(init[i]); | ||||
| 
 | ||||
|   /* Export table to XLSX */ | ||||
|   var wb = XLSX.utils.book_new(); | ||||
| 
 | ||||
|   async function book_append_table(wb, name) { | ||||
|     var r_f = await conn1.query('SELECT * FROM ' + name); | ||||
|     var r = r_f.rows; | ||||
|     var ws = XLSX.utils.json_to_sheet(r); | ||||
|     XLSX.utils.book_append_sheet(wb, ws, name); | ||||
|   } | ||||
| 
 | ||||
|   await book_append_table(wb, "pres"); | ||||
|   await book_append_table(wb, "fmts"); | ||||
|   XLSX.writeFile(wb, "pgsql.xlsx"); | ||||
| 
 | ||||
|   /* Capture first database info and close */ | ||||
|   var P1 = (await conn1.query("SELECT * FROM pres")).rows; | ||||
|   var F1 = (await conn1.query("SELECT * FROM fmts")).rows; | ||||
|   await conn1.end(); | ||||
| 
 | ||||
|   /* Import XLSX to table */ | ||||
|   await conn2.connect(); | ||||
|   var wb2 = XLSX.readFile("pgsql.xlsx"); | ||||
|   var queries = SheetJSSQL.book_to_sql(wb2, "PGSQL"); | ||||
|   for(i = 0; i < queries.length; ++i) { console.log(queries[i]); await conn2.query(queries[i]); } | ||||
| 
 | ||||
|   /* Capture first database info and close */ | ||||
|   var P2 = (await conn2.query("SELECT * FROM pres")).rows; | ||||
|   var F2 = (await conn2.query("SELECT * FROM fmts")).rows; | ||||
|   await conn2.end(); | ||||
| 
 | ||||
|   /* Compare results */ | ||||
|   assert.deepEqual(P1, P2); | ||||
|   assert.deepEqual(F1, F2); | ||||
| 
 | ||||
|   /* Display results */ | ||||
|   console.log(P2); | ||||
|   console.log(F2); | ||||
| })(); | ||||
| @ -1,340 +1,11 @@ | ||||
| # Databases | ||||
| 
 | ||||
| "Database" is a catch-all term referring to traditional RDBMS as well as K/V | ||||
| stores, document databases, and other "NoSQL" storages. There are many external | ||||
| database systems as well as browser APIs like WebSQL and `localStorage` | ||||
| This demo has been split up for clarity: | ||||
| 
 | ||||
| This demo discusses general strategies and provides examples for a variety of | ||||
| database systems.  The examples are merely intended to demonstrate very basic | ||||
| functionality. | ||||
| - <https://docs.sheetjs.com/docs/getting-started/demos/database> covers SQL and | ||||
|   structured data (including CRUD operations) | ||||
| 
 | ||||
| 
 | ||||
| ## Structured Tables | ||||
| 
 | ||||
| Database tables are a common import and export target for spreadsheets.  One | ||||
| common representation of a database table is an array of JS objects whose keys | ||||
| are column headers and whose values are the underlying data values. For example, | ||||
| 
 | ||||
| | Name         | Index | | ||||
| | :----------- | ----: | | ||||
| | Barack Obama |    44 | | ||||
| | Donald Trump |    45 | | ||||
| 
 | ||||
| is naturally represented as an array of objects | ||||
| 
 | ||||
| ```js | ||||
| [ | ||||
|   { Name: "Barack Obama", Index: 44 }, | ||||
|   { Name: "Donald Trump", Index: 45 } | ||||
| ] | ||||
| ``` | ||||
| 
 | ||||
| The `sheet_to_json` and `json_to_sheet` helper functions work with objects of | ||||
| similar shape, converting to and from worksheet objects.  The corresponding | ||||
| worksheet would include a header row for the labels: | ||||
| 
 | ||||
| ``` | ||||
| XXX|      A       |   B   | | ||||
| ---+--------------+-------+ | ||||
|  1 | Name         | Index | | ||||
|  2 | Barack Obama |    44 | | ||||
|  3 | Donald Trump |    45 | | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| ## Building Schemas from Worksheets | ||||
| 
 | ||||
| The `sheet_to_json` helper function generates arrays of JS objects that can be | ||||
| scanned to determine the column "types", and there are third-party connectors | ||||
| that can push arrays of JS objects to database tables. | ||||
| 
 | ||||
| The [`sql`](http://sheetjs.com/sql) browser demo uses WebSQL, which is | ||||
| limited to the SQLite fundamental types. | ||||
| 
 | ||||
| <details> | ||||
| 	<summary><b>Implementation details</b> (click to show)</summary> | ||||
| 
 | ||||
| The `sql` schema builder scans the first row to find headers: | ||||
| 
 | ||||
| ```js | ||||
|   if(!ws || !ws['!ref']) return; | ||||
|   var range = XLSX.utils.decode_range(ws['!ref']); | ||||
|   if(!range || !range.s || !range.e || range.s > range.e) return; | ||||
|   var R = range.s.r, C = range.s.c; | ||||
| 
 | ||||
|   var names = new Array(range.e.c-range.s.c+1); | ||||
|   for(C = range.s.c; C<= range.e.c; ++C){ | ||||
|     var addr = XLSX.utils.encode_cell({c:C,r:R}); | ||||
|     names[C-range.s.c] = ws[addr] ? ws[addr].v : XLSX.utils.encode_col(C); | ||||
|   } | ||||
| ``` | ||||
| 
 | ||||
| After finding the headers, a deduplication step ensures that data is not lost. | ||||
| Duplicate headers will be suffixed with `_1`, `_2`, etc. | ||||
| 
 | ||||
| ```js | ||||
|   for(var i = 0; i < names.length; ++i) if(names.indexOf(names[i]) < i) | ||||
|     for(var j = 0; j < names.length; ++j) { | ||||
|       var _name = names[i] + "_" + (j+1); | ||||
|       if(names.indexOf(_name) > -1) continue; | ||||
|       names[i] = _name; | ||||
|     } | ||||
| ``` | ||||
| 
 | ||||
| A column-major walk helps determine the data type.  For SQLite the only relevant | ||||
| data types are `REAL` and `TEXT`.  If a string or date or error is seen in any | ||||
| value of a column, the column is marked as `TEXT`: | ||||
| 
 | ||||
| ```js | ||||
|   var types = new Array(range.e.c-range.s.c+1); | ||||
|   for(C = range.s.c; C<= range.e.c; ++C) { | ||||
|     var seen = {}, _type = ""; | ||||
|     for(R = range.s.r+1; R<= range.e.r; ++R) | ||||
|       seen[(ws[XLSX.utils.encode_cell({c:C,r:R})]||{t:"z"}).t] = true; | ||||
|     if(seen.s || seen.str) _type = "TEXT"; | ||||
|     else if(seen.n + seen.b + seen.d + seen.e > 1) _type = "TEXT"; | ||||
|     else switch(true) { | ||||
|       case seen.b: | ||||
|       case seen.n: _type = "REAL"; break; | ||||
|       case seen.e: _type = "TEXT"; break; | ||||
|       case seen.d: _type = "TEXT"; break; | ||||
|     } | ||||
|     types[C-range.s.c] = _type || "TEXT"; | ||||
|   } | ||||
| ``` | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| The included `SheetJSSQL.js` script demonstrates SQL statement generation. | ||||
| 
 | ||||
| 
 | ||||
| ## Objects, K/V and "Schema-less" Databases | ||||
| 
 | ||||
| So-called "Schema-less" databases allow for arbitrary keys and values within the | ||||
| entries in the database.  K/V stores and Objects add additional restrictions. | ||||
| 
 | ||||
| There is no natural way to translate arbitrarily shaped schemas to worksheets | ||||
| in a workbook.  One common trick is to dedicate one worksheet to holding named | ||||
| keys.  For example, considering the JS object: | ||||
| 
 | ||||
| ```json | ||||
| { | ||||
|   "title": "SheetDB", | ||||
|   "metadata": { | ||||
|     "author": "SheetJS", | ||||
|     "code": 7262 | ||||
|   }, | ||||
|   "data": [ | ||||
|     { "Name": "Barack Obama", "Index": 44 }, | ||||
|     { "Name": "Donald Trump", "Index": 45 }, | ||||
|   ] | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| A dedicated worksheet should store the one-off named values: | ||||
| 
 | ||||
| ``` | ||||
| XXX|        A        |    B    | | ||||
| ---+-----------------+---------+ | ||||
|  1 | Path            | Value   | | ||||
|  2 | title           | SheetDB | | ||||
|  3 | metadata.author | SheetJS | | ||||
|  4 | metadata.code   |    7262 | | ||||
| ``` | ||||
| 
 | ||||
| The included `ObjUtils.js` script demonstrates object-workbook conversion: | ||||
| 
 | ||||
| <details> | ||||
| 	<summary><b>Implementation details</b> (click to show)</summary> | ||||
| 
 | ||||
| ```js | ||||
| function deepset(obj, path, value) { | ||||
|   if(path.indexOf(".") == -1) return obj[path] = value; | ||||
|   var parts = path.split("."); | ||||
|   if(!obj[parts[0]]) obj[parts[0]] = {}; | ||||
|   return deepset(obj[parts[0]], parts.slice(1).join("."), value); | ||||
| } | ||||
| function workbook_to_object(wb) { | ||||
|   var out = {}; | ||||
| 
 | ||||
|   /* assign one-off keys */ | ||||
|   var ws = wb.Sheets["_keys"]; if(ws) { | ||||
|     var data = XLSX.utils.sheet_to_json(ws, {raw:true}); | ||||
|     data.forEach(function(r) { deepset(out, r.path, r.value); }); | ||||
|   } | ||||
| 
 | ||||
|   /* assign arrays from worksheet tables */ | ||||
|   wb.SheetNames.forEach(function(n) { | ||||
|     if(n == "_keys") return; | ||||
|     out[n] = XLSX.utils.sheet_to_json(wb.Sheets[n], {raw:true}); | ||||
|   }); | ||||
| 
 | ||||
|   return out; | ||||
| } | ||||
| 
 | ||||
| function walk(obj, key, arr) { | ||||
|   if(Array.isArray(obj)) return; | ||||
|   if(typeof obj != "object") { arr.push({path:key, value:obj}); return; } | ||||
|   Object.keys(obj).forEach(function(k) { walk(obj[k], key?key+"."+k:k, arr); }); | ||||
| } | ||||
| function object_to_workbook(obj) { | ||||
|   var wb = XLSX.utils.book_new(); | ||||
| 
 | ||||
|   /* keyed entries */ | ||||
|   var base = []; walk(obj, "", base); | ||||
|   var ws = XLSX.utils.json_to_sheet(base, {header:["path", "value"]}); | ||||
|   XLSX.utils.book_append_sheet(wb, ws, "_keys"); | ||||
| 
 | ||||
|   /* arrays */ | ||||
|   Object.keys(obj).forEach(function(k) { | ||||
|     if(!Array.isArray(obj[k])) return; | ||||
|     XLSX.utils.book_append_sheet(wb, XLSX.utils.json_to_sheet(obj[k]), k); | ||||
|   }); | ||||
| 
 | ||||
|   return wb; | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| 
 | ||||
| ## Browser APIs | ||||
| 
 | ||||
| #### WebSQL | ||||
| 
 | ||||
| WebSQL is a popular SQL-based in-browser database available on Chrome.  In | ||||
| practice, it is powered by SQLite, and most simple SQLite-compatible queries | ||||
| work as-is in WebSQL. | ||||
| 
 | ||||
| The public demo <http://sheetjs.com/sql> generates a database from workbook. | ||||
| 
 | ||||
| #### LocalStorage and SessionStorage | ||||
| 
 | ||||
| The Storage API, encompassing `localStorage` and `sessionStorage`, describes | ||||
| simple key-value stores that only support string values and keys. Objects can be | ||||
| stored as JSON using `JSON.stringify` and `JSON.parse` to set and get keys. | ||||
| 
 | ||||
| `SheetJSStorage.js` extends the `Storage` prototype with a `load` function to | ||||
| populate the db based on an object and a `dump` function to generate a workbook | ||||
| from the data in the storage.  `LocalStorage.html` tests `localStorage`. | ||||
| 
 | ||||
| #### IndexedDB | ||||
| 
 | ||||
| IndexedDB is a more complex storage solution, but the `localForage` wrapper | ||||
| supplies a Promise-based interface mimicking the `Storage` API. | ||||
| 
 | ||||
| `SheetJSForage.js` extends the `localforage` object with a `load` function to | ||||
| populate the db based on an object and a `dump` function to generate a workbook | ||||
| from the data in the storage.  `LocalForage.html` forces IndexedDB mode. | ||||
| 
 | ||||
| 
 | ||||
| ## External Database Demos | ||||
| 
 | ||||
| ### SQL Databases | ||||
| 
 | ||||
| There are nodejs connector libraries for all of the popular RDBMS systems.  They | ||||
| have facilities for connecting to a database, executing queries, and obtaining | ||||
| results as arrays of JS objects that can be passed to `json_to_sheet`.  The main | ||||
| differences surround API shape and supported data types. | ||||
| 
 | ||||
| #### SQLite | ||||
| 
 | ||||
| [The `better-sqlite3` module](https://www.npmjs.com/package/better-sqlite3) | ||||
| provides a very simple API for working with SQLite databases.  `Statement#all` | ||||
| runs a prepared statement and returns an array of JS objects. | ||||
| 
 | ||||
| `SQLiteTest.js` generates a simple two-table SQLite database (`SheetJS1.db`), | ||||
| exports to XLSX (`sqlite.xlsx`), imports the new XLSX file to a new database | ||||
| (`SheetJS2.db`) and verifies the tables are preserved. | ||||
| 
 | ||||
| #### MySQL / MariaDB | ||||
| 
 | ||||
| [The `mysql2` module](https://www.npmjs.com/package/mysql2) supplies a callback | ||||
| API as well as a Promise wrapper.  `Connection#query` runs a statement and | ||||
| returns an array whose first element is an array of JS objects. | ||||
| 
 | ||||
| `MySQLTest.js` connects to the MySQL instance running on `localhost`, builds two | ||||
| tables in the `sheetjs` database, exports to XLSX, imports the new XLSX file to | ||||
| the `sheetj5` database and verifies the tables are preserved. | ||||
| 
 | ||||
| #### PostgreSQL | ||||
| 
 | ||||
| [The `pg` module](https://node-postgres.com/) supplies a Promise wrapper. | ||||
| `Client#query` runs a statement and returns a result object.  The `rows` key of | ||||
| the object is an array of JS objects. | ||||
| 
 | ||||
| `PgSQLTest.js` connects to the PostgreSQL server on `localhost`, builds two | ||||
| tables in the `sheetjs` database, exports to XLSX, imports the new XLSX file to | ||||
| the `sheetj5` database and verifies the tables are preserved. | ||||
| 
 | ||||
| #### Knex Query Builder | ||||
| 
 | ||||
| [The `knex` module](https://www.npmjs.com/package/knex) builds SQL queries.  The | ||||
| same exact code can be used against Oracle Database, MSSQL, and other engines. | ||||
| 
 | ||||
| `KnexTest.js` uses the `sqlite3` connector and follows the same procedure as the | ||||
| SQLite test.  The included `SheetJSKnex.js` script converts between the query | ||||
| builder and the common spreadsheet format. | ||||
| 
 | ||||
| ### Key/Value Stores | ||||
| 
 | ||||
| #### Redis | ||||
| 
 | ||||
| Redis is a powerful data structure server that can store simple strings, sets, | ||||
| sorted sets, hashes and lists.  One simple database representation stores the | ||||
| strings in a special worksheet (`_strs`), the manifest in another worksheet | ||||
| (`_manifest`), and each object in its own worksheet (`obj##`). | ||||
| 
 | ||||
| `RedisTest.js` connects to a local Redis server, populates data based on the | ||||
| official Redis tutorial, exports to XLSX, flushes the server, imports the new | ||||
| XLSX file and verifies the data round-tripped correctly.  `SheetJSRedis.js` | ||||
| includes the implementation details. | ||||
| 
 | ||||
| #### LowDB | ||||
| 
 | ||||
| LowDB is a small schemaless database powered by `lodash`.  `_.get` and `_.set` | ||||
| helper functions make storing metadata a breeze.  The included `SheetJSLowDB.js` | ||||
| script demonstrates a simple adapter that can load and dump data. | ||||
| 
 | ||||
| ### Document Databases | ||||
| 
 | ||||
| Since document databases are capable of holding more complex objects, they can | ||||
| actually hold the underlying worksheet objects!  In some cases, where arrays are | ||||
| supported, they can even hold the workbook object. | ||||
| 
 | ||||
| #### MongoDB | ||||
| 
 | ||||
| MongoDB is a popular document-oriented database engine.  `MongoDBTest.js` uses | ||||
| MongoDB to hold a simple workbook and export to XLSX. | ||||
| 
 | ||||
| `MongoDBCRUD.js` follows the SQL examples using an idiomatic collection | ||||
| structure.  Exporting and importing collections are straightforward: | ||||
| 
 | ||||
| <details> | ||||
| 	<summary><b>Example code</b> (click to show)</summary> | ||||
| 
 | ||||
| ```js | ||||
| /* generate a worksheet from a collection */ | ||||
| const aoa = await db.collection('coll').find({}).toArray(); | ||||
| aoa.forEach((x) => delete x._id); | ||||
| const ws = XLSX.utils.json_to_sheet(aoa); | ||||
| 
 | ||||
| 
 | ||||
| /* import data from a worksheet to a collection */ | ||||
| const aoa = XLSX.utils.sheet_to_json(ws); | ||||
| await db.collection('coll').insertMany(aoa, {ordered: true}); | ||||
| ``` | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| #### Firebase | ||||
| 
 | ||||
| [`firebase-server`](https://www.npmjs.com/package/firebase-server) is a simple | ||||
| mock Firebase server used in the tests, but the same code works in an external | ||||
| Firebase deployment when plugging in the database connection info. | ||||
| 
 | ||||
| `FirebaseDemo.html` and `FirebaseTest.js` demonstrate a whole-workbook process. | ||||
| The entire workbook object is persisted, a few cells are changed, and the stored | ||||
| data is dumped and exported to XLSX. | ||||
| - https://docs.sheetjs.com/docs/getting-started/demos/nosql covers unstructured | ||||
|   data including "NoSQL" data stores. | ||||
| 
 | ||||
| [](https://github.com/SheetJS/js-xlsx) | ||||
|  | ||||
| @ -1,50 +0,0 @@ | ||||
| /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ | ||||
| /* eslint-env node */ | ||||
| var XLSX = require("xlsx"); | ||||
| var SheetJSRedis = require("./SheetJSRedis"); | ||||
| var assert = require('assert'); | ||||
| var redis = require("redis"), util = require("util"); | ||||
| var client = redis.createClient(); | ||||
| 
 | ||||
| 
 | ||||
| /* Sample data */ | ||||
| var init = [ | ||||
|   ["FLUSHALL", []], | ||||
|   ["SADD",  ["birdpowers", "flight", "pecking"]], | ||||
|   ["SET",   ["foo", "bar"]], | ||||
|   ["SET",   ["baz", 0]], | ||||
|   ["RPUSH", ["friends", "sam", "alice", "bob"]], | ||||
|   ["ZADD",  ["hackers", 1906, 'Grace Hopper', 1912, 'Alan Turing', 1916, 'Claude Shannon', 1940, 'Alan Kay', 1953, 'Richard Stallman', 1957, 'Sophie Wilson', 1965, 'Yukihiro Matsumoto', 1969, 'Linus Torvalds']], | ||||
|   ["SADD",  ["superpowers", "flight", 'x-ray vision']], | ||||
|   ["HMSET", ["user:1000", "name", 'John Smith', "email", 'john.smith@example.com', "password", "s3cret", "visits", 1]], | ||||
|   ["HMSET", ["user:1001", "name", 'Mary Jones', "email", 'mjones@example.com', "password", "hidden"]] | ||||
| ]; | ||||
| 
 | ||||
| const R = (()=>{ | ||||
|   const Rcache = {}; | ||||
|   const R_ = (n) => Rcache[n] || (Rcache[n] = util.promisify(client[n]).bind(client)); | ||||
|   return (n) => R_(n.toLowerCase()); | ||||
| })(); | ||||
| 
 | ||||
| (async () => { | ||||
|   for(var i = 0; i < init.length; ++i) await R(init[i][0])(init[i][1]); | ||||
| 
 | ||||
|   /* Export database to XLSX */ | ||||
|   var wb = await SheetJSRedis.redis_to_wb(R); | ||||
|   XLSX.writeFile(wb, "redis.xlsx"); | ||||
| 
 | ||||
|   /* Import XLSX to database */ | ||||
|   await R("flushall")(); | ||||
|   var wb2 = XLSX.readFile("redis.xlsx"); | ||||
|   await SheetJSRedis.wb_to_redis(wb2, R); | ||||
| 
 | ||||
|   /* Verify */ | ||||
|   assert.equal(await R("get")("foo"), "bar"); | ||||
|   assert.equal(await R("lindex")("friends", 1), "alice"); | ||||
|   assert.equal(await R("zscore")("hackers", "Claude Shannon"), 1916); | ||||
|   assert.equal(await R("hget")("user:1000", "name"), "John Smith"); | ||||
|   assert.equal(await R("sismember")("superpowers", "flight"), "1"); | ||||
|   assert.equal(await R("sismember")("birdpowers", "pecking"), "1"); | ||||
| 
 | ||||
|   client.quit(); | ||||
| })(); | ||||
| @ -1,51 +0,0 @@ | ||||
| /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ | ||||
| /* eslint-env node */ | ||||
| var XLSX = require('xlsx'); | ||||
| var assert = require('assert'); | ||||
| var SheetJSSQL = require('./SheetJSSQL'); | ||||
| var Database = require('better-sqlite3'); | ||||
| var db1 = new Database('SheetJS1.db'); | ||||
| 
 | ||||
| /* Sample data table */ | ||||
| var init = [ | ||||
|   "DROP TABLE IF EXISTS pres", | ||||
|   "CREATE TABLE pres (name TEXT, idx INTEGER)", | ||||
|   "INSERT INTO pres VALUES ('Barack Obama', 44)", | ||||
|   "INSERT INTO pres VALUES ('Donald Trump', 45)", | ||||
|   "DROP TABLE IF EXISTS fmts", | ||||
|   "CREATE TABLE fmts (ext TEXT, ctr TEXT, multi INTEGER)", | ||||
|   "INSERT INTO fmts VALUES ('XLSB', 'ZIP', 1)", | ||||
|   "INSERT INTO fmts VALUES ('XLS',  'CFB', 1)", | ||||
|   "INSERT INTO fmts VALUES ('XLML', '',    1)", | ||||
|   "INSERT INTO fmts VALUES ('CSV',  '',    0)", | ||||
| ]; | ||||
| db1.exec(init.join(";")); | ||||
| 
 | ||||
| /* Export database to XLSX */ | ||||
| var wb = XLSX.utils.book_new(); | ||||
| function book_append_table(wb, db, name) { | ||||
|   var r = db.prepare('SELECT * FROM ' + name).all(); | ||||
|   var ws = XLSX.utils.json_to_sheet(r); | ||||
|   XLSX.utils.book_append_sheet(wb, ws, name); | ||||
| } | ||||
| book_append_table(wb, db1, "pres"); | ||||
| book_append_table(wb, db1, "fmts"); | ||||
| XLSX.writeFile(wb, "sqlite.xlsx"); | ||||
| 
 | ||||
| /* Import XLSX to database */ | ||||
| var db2 = new Database('SheetJS2.db'); | ||||
| var wb2 = XLSX.readFile("sqlite.xlsx"); | ||||
| var queries = SheetJSSQL.book_to_sql(wb2, "SQLITE"); | ||||
| queries.forEach(function(q) { db2.exec(q); }); | ||||
| 
 | ||||
| /* Compare databases */ | ||||
| var P1 = db1.prepare("SELECT * FROM pres").all(); | ||||
| var P2 = db2.prepare("SELECT * FROM pres").all(); | ||||
| var F1 = db1.prepare("SELECT * FROM fmts").all(); | ||||
| var F2 = db2.prepare("SELECT * FROM fmts").all(); | ||||
| assert.deepEqual(P1, P2); | ||||
| assert.deepEqual(F1, F2); | ||||
| 
 | ||||
| console.log(P2); | ||||
| console.log(F2); | ||||
| 
 | ||||
| @ -1,20 +0,0 @@ | ||||
| /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ | ||||
| /*global ObjUtils, localforage */ | ||||
| localforage.load = async function foo(data) { | ||||
|   var keys = Object.keys(data); | ||||
|   for(var i = 0; i < keys.length; ++i) { | ||||
|     var key = keys[i], val = JSON.stringify(data[keys[i]]) | ||||
|     await localforage.setItem(key, val); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| localforage.dump = async function() { | ||||
|   var obj = {}; | ||||
|   var length = await localforage.length(); | ||||
|   for(var i = 0; i < length; ++i) { | ||||
|     var key = await this.key(i); | ||||
|     var val = await this.getItem(key); | ||||
|     obj[key] = JSON.parse(val); | ||||
|   } | ||||
|   return ObjUtils.object_to_workbook(obj); | ||||
| }; | ||||
| @ -1,78 +0,0 @@ | ||||
| /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ | ||||
| /* eslint-env node */ | ||||
| var XLSX = require("xlsx"); | ||||
| 
 | ||||
| async function book_append_knex(wb, knex, tbl) { | ||||
| 	const aoo = await knex.select("*").from(tbl); | ||||
| 	XLSX.utils.book_append_sheet(wb, XLSX.utils.json_to_sheet(aoo), tbl); | ||||
| } | ||||
| 
 | ||||
| const TYPES = { | ||||
|   b: "boolean", | ||||
|   n: "float", | ||||
|   t: "string", | ||||
|   d: "dateTime" | ||||
| }; | ||||
| async function ws_to_knex(ws, knex, n) { | ||||
|   if(!ws || !ws['!ref']) return; | ||||
|   var range = XLSX.utils.decode_range(ws['!ref']); | ||||
|   if(!range || !range.s || !range.e || range.s > range.e) return; | ||||
|   var R = range.s.r, C = range.s.c; | ||||
| 
 | ||||
|   var names = new Array(range.e.c-range.s.c+1); | ||||
|   for(C = range.s.c; C<= range.e.c; ++C){ | ||||
|     var addr = XLSX.utils.encode_cell({c:C,r:R}); | ||||
|     names[C-range.s.c] = ws[addr] ? ws[addr].v : XLSX.utils.encode_col(C); | ||||
|   } | ||||
| 
 | ||||
|   for(var i = 0; i < names.length; ++i) if(names.indexOf(names[i]) < i) | ||||
|     for(var j = 0; j < names.length; ++j) { | ||||
|       var _name = names[i] + "_" + (j+1); | ||||
|       if(names.indexOf(_name) > -1) continue; | ||||
|       names[i] = _name; | ||||
|     } | ||||
| 
 | ||||
|   var types = new Array(range.e.c-range.s.c+1); | ||||
|   for(C = range.s.c; C<= range.e.c; ++C) { | ||||
|     var seen = {}, _type = ""; | ||||
|     for(R = range.s.r+1; R<= range.e.r; ++R) | ||||
|       seen[(ws[XLSX.utils.encode_cell({c:C,r:R})]||{t:"z"}).t] = true; | ||||
|     if(seen.s || seen.str) _type = TYPES.t; | ||||
|     else if(seen.n + seen.b + seen.d + seen.e > 1) _type = TYPES.t; | ||||
|     else switch(true) { | ||||
|       case seen.b: _type = TYPES.b; break; | ||||
|       case seen.n: _type = TYPES.n; break; | ||||
|       case seen.e: _type = TYPES.t; break; | ||||
|       case seen.d: _type = TYPES.d; break; | ||||
|     } | ||||
|     types[C-range.s.c] = _type || TYPES.t; | ||||
|   } | ||||
| 
 | ||||
|   await knex.schema.dropTableIfExists(n); | ||||
| 	await knex.schema.createTable(n, (table) => { names.forEach((n, i) => { table[types[i] || "text"](n); }); }); | ||||
| 
 | ||||
|   for(R = range.s.r+1; R<= range.e.r; ++R) { | ||||
|     var row = {}; | ||||
|     for(C = range.s.c; C<= range.e.c; ++C) { | ||||
|       var cell = ws[XLSX.utils.encode_cell({c:C,r:R})]; | ||||
|       if(!cell) continue; | ||||
|       var key = names[C-range.s.c], val = cell.v; | ||||
|       if(types[C-range.s.c] == TYPES.n) if(cell.t == 'b' || typeof val == 'boolean' ) val = +val; | ||||
|       row[key] = val; | ||||
|     } | ||||
|     await knex.insert(row).into(n);; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| async function wb_to_knex(wb, knex) { | ||||
| 	for(var i = 0; i < wb.SheetNames.length; ++i) { | ||||
| 		var n = wb.SheetNames[i]; | ||||
| 		var ws = wb.Sheets[n]; | ||||
| 		await ws_to_knex(ws, knex, n); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| module.exports = { | ||||
| 	book_append_knex, | ||||
| 	wb_to_knex | ||||
| }; | ||||
| @ -1,20 +0,0 @@ | ||||
| /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ | ||||
| /* eslint-env node */ | ||||
| 
 | ||||
| var XLSX = require('xlsx'); | ||||
| var ObjUtils = require('./ObjUtils'); | ||||
| 
 | ||||
| function SheetJSAdapter() { this.defaultValue = {}; }; | ||||
| 
 | ||||
| SheetJSAdapter.prototype.read = function() { return this.defaultValue; }; | ||||
| SheetJSAdapter.prototype.write = function(/*data*/) {}; | ||||
| 
 | ||||
| SheetJSAdapter.prototype.dumpRaw = function() { return ObjUtils.object_to_workbook(this.defaultValue); }; | ||||
| SheetJSAdapter.prototype.dump = function(options) { XLSX.write(this.dumpRaw(), options); }; | ||||
| SheetJSAdapter.prototype.dumpFile = function(path, options) { XLSX.writeFile(this.dumpRaw(), path, options); }; | ||||
| 
 | ||||
| SheetJSAdapter.prototype.loadRaw = function(wb) { ObjUtils.workbook_set_object(this.defaultValue, wb); }; | ||||
| SheetJSAdapter.prototype.load = function(data, options) { this.loadRaw(XLSX.read(data, options)); }; | ||||
| SheetJSAdapter.prototype.loadFile = function(path, options) { this.loadRaw(XLSX.readFile(path, options)); }; | ||||
| 
 | ||||
| if(typeof module !== 'undefined') module.exports = SheetJSAdapter; | ||||
| @ -1,14 +0,0 @@ | ||||
| /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ | ||||
| /* eslint-env node */ | ||||
| var XLSX = require("xlsx"); | ||||
| 
 | ||||
| async function book_append_mongo(wb, coll, name) { | ||||
| 	const aoo = await coll.find({}, {projection:{_id:0}}).toArray(); | ||||
| 	const ws = XLSX.utils.json_to_sheet(aoo); | ||||
| 	XLSX.utils.book_append_sheet(wb, ws, name); | ||||
| 	return ws; | ||||
| } | ||||
| 
 | ||||
| module.exports = { | ||||
| 	book_append_mongo | ||||
| }; | ||||
| @ -1,74 +0,0 @@ | ||||
| /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ | ||||
| /* eslint-env node */ | ||||
| var XLSX = require("xlsx"); | ||||
| 
 | ||||
| const pair = (arr) => arr.map((x,i)=>!(i%2)&&[x,+arr[i+1]]).filter(x=>x); | ||||
| const keyify = (obj) => Object.keys(obj).map(x => [x, obj[x]]); | ||||
| 
 | ||||
| async function redis_to_wb(R) { | ||||
|   var wb = XLSX.utils.book_new(); | ||||
|   var manifest = [], strs = []; | ||||
| 
 | ||||
|   /* store strings in strs and keep note of other objects in manifest */ | ||||
|   var keys = await R("keys")("*"), type = ""; | ||||
|   for(var i = 0; i < keys.length; ++i) { | ||||
|     type = await R("type")(keys[i]); | ||||
|     switch(type) { | ||||
|       case "string": strs.push({key:keys[i], value: await R("get")(keys[i])}); break; | ||||
|       case "list": case "zset": case "set": case "hash": manifest.push({key:keys[i], type:type}); break; | ||||
|       default: throw new Error("bad type " + type); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /* add worksheets if relevant */ | ||||
|   if(strs.length > 0) { | ||||
|     var wss = XLSX.utils.json_to_sheet(strs, {header: ["key", "value"], skipHeader:1}); | ||||
|     XLSX.utils.book_append_sheet(wb, wss, "_strs"); | ||||
|   } | ||||
|   if(manifest.length > 0) { | ||||
|     var wsm = XLSX.utils.json_to_sheet(manifest, {header: ["key", "type"]}); | ||||
|     XLSX.utils.book_append_sheet(wb, wsm, "_manifest"); | ||||
|   } | ||||
|   for(i = 0; i < manifest.length; ++i) { | ||||
|     var sn = "obj" + i; | ||||
|     var aoa, key = manifest[i].key; | ||||
|     switch((type=manifest[i].type)) { | ||||
|       case "list": | ||||
|         aoa = (await R("lrange")(key, 0, -1)).map(x => [x]); break; | ||||
|       case "set": | ||||
|         aoa = (await R("smembers")(key)).map(x => [x]); break; | ||||
|       case "zset": | ||||
|         aoa = pair(await R("zrange")(key, 0, -1, "withscores")); break; | ||||
|       case "hash": | ||||
|         aoa = keyify(await R("hgetall")(key)); break; | ||||
|       default: throw new Error("bad type " + type); | ||||
|     } | ||||
|     XLSX.utils.book_append_sheet(wb, XLSX.utils.aoa_to_sheet(aoa), sn); | ||||
|   } | ||||
|   return wb; | ||||
| } | ||||
| 
 | ||||
| /* convert worksheet aoa to specific redis type */ | ||||
| const aoa_to_redis = { | ||||
|   list: async (aoa, R, key) => await R("RPUSH")([key].concat(aoa.map(x=>x[0]))), | ||||
|   zset: async (aoa, R, key) => await R("ZADD" )([key].concat(aoa.reduce((acc,x)=>acc.concat([+x[1], x[0]]), []))), | ||||
|   hash: async (aoa, R, key) => await R("HMSET")([key].concat(aoa.reduce((acc,x)=>acc.concat(x), []))), | ||||
|   set:  async (aoa, R, key) => await R("SADD" )([key].concat(aoa.map(x=>x[0]))) | ||||
| }; | ||||
| async function wb_to_redis(wb, R) { | ||||
|   if(wb.Sheets._strs) { | ||||
|     var strs = XLSX.utils.sheet_to_json(wb.Sheets._strs, {header:1}); | ||||
|     for(var i = 0; i < strs.length; ++i) await R("SET")(strs[i]); | ||||
|   } | ||||
|   if(!wb.Sheets._manifest) return; | ||||
|   var M = XLSX.utils.sheet_to_json(wb.Sheets._manifest); | ||||
|   for(i = 0; i < M.length; ++i) { | ||||
|     var aoa = XLSX.utils.sheet_to_json(wb.Sheets["obj" + i], {header:1}); | ||||
|     await aoa_to_redis[M[i].type](aoa, R, M[i].key); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| module.exports = { | ||||
|   redis_to_wb, | ||||
|   wb_to_redis | ||||
| }; | ||||
| @ -1,89 +0,0 @@ | ||||
| /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ | ||||
| /* global XLSX, require, module */ | ||||
| var SheetJSSQL = (function() { | ||||
| 
 | ||||
| var X; | ||||
| if(typeof XLSX !== "undefined") X = XLSX; | ||||
| else if(typeof require !== 'undefined') X = require('xlsx'); | ||||
| else throw new Error("Could not find XLSX"); | ||||
| 
 | ||||
| var _TYPES = { | ||||
|   "PGSQL":  { t:"text", n:"float8", d:"timestamp", b:"boolean" }, | ||||
|   "MYSQL":  { t:"TEXT", n:"REAL",   d:"DATETIME",  b:"TINYINT" }, | ||||
|   "SQLITE": { t:"TEXT", n:"REAL",   d:"TEXT",      b:"REAL"    } | ||||
| } | ||||
| function sheet_to_sql(ws, sname, mode) { | ||||
|   var TYPES = _TYPES[mode || "SQLITE"] | ||||
|   if(!ws || !ws['!ref']) return; | ||||
|   var range = X.utils.decode_range(ws['!ref']); | ||||
|   if(!range || !range.s || !range.e || range.s > range.e) return; | ||||
|   var R = range.s.r, C = range.s.c; | ||||
| 
 | ||||
|   var names = new Array(range.e.c-range.s.c+1); | ||||
|   for(C = range.s.c; C<= range.e.c; ++C){ | ||||
|     var addr = X.utils.encode_cell({c:C,r:R}); | ||||
|     names[C-range.s.c] = ws[addr] ? ws[addr].v : X.utils.encode_col(C); | ||||
|   } | ||||
| 
 | ||||
|   for(var i = 0; i < names.length; ++i) if(names.indexOf(names[i]) < i) | ||||
|     for(var j = 0; j < names.length; ++j) { | ||||
|       var _name = names[i] + "_" + (j+1); | ||||
|       if(names.indexOf(_name) > -1) continue; | ||||
|       names[i] = _name; | ||||
|     } | ||||
| 
 | ||||
|   var types = new Array(range.e.c-range.s.c+1); | ||||
|   for(C = range.s.c; C<= range.e.c; ++C) { | ||||
|     var seen = {}, _type = ""; | ||||
|     for(R = range.s.r+1; R<= range.e.r; ++R) | ||||
|       seen[(ws[X.utils.encode_cell({c:C,r:R})]||{t:"z"}).t] = true; | ||||
|     if(seen.s || seen.str) _type = TYPES.t; | ||||
|     else if(seen.n + seen.b + seen.d + seen.e > 1) _type = TYPES.t; | ||||
|     else switch(true) { | ||||
|       case seen.b: _type = TYPES.b; break; | ||||
|       case seen.n: _type = TYPES.n; break; | ||||
|       case seen.e: _type = TYPES.t; break; | ||||
|       case seen.d: _type = TYPES.d; break; | ||||
|     } | ||||
|     types[C-range.s.c] = _type || TYPES.t; | ||||
|   } | ||||
| 
 | ||||
|   var out = []; | ||||
| 
 | ||||
|   var BT = mode == "PGSQL" ? "" : "`"; | ||||
|   var Q  = mode == "PGSQL" ? "'" : '"'; | ||||
|   var J  = mode == "PGSQL" ? /'/g : /"/g; | ||||
|   out.push("DROP TABLE IF EXISTS " + BT + sname + BT ); | ||||
|   out.push("CREATE TABLE " + BT + sname + BT + " (" + names.map(function(n, i) { return BT + n + BT + " " + (types[i]||"TEXT"); }).join(", ") + ");" ); | ||||
| 
 | ||||
|   for(R = range.s.r+1; R<= range.e.r; ++R) { | ||||
|     var fields = [], values = []; | ||||
|     for(C = range.s.c; C<= range.e.c; ++C) { | ||||
|       var cell = ws[X.utils.encode_cell({c:C,r:R})]; | ||||
|       if(!cell) continue; | ||||
|       fields.push(BT + names[C-range.s.c] + BT); | ||||
|       var val = cell.v; | ||||
|       switch(types[C-range.s.c]) { | ||||
|         case TYPES.n: if(cell.t == 'b' || typeof val == 'boolean' ) val = +val; break; | ||||
|         default: val = Q + val.toString().replace(J, Q + Q) + Q; | ||||
|       } | ||||
|       values.push(val); | ||||
|     } | ||||
|     out.push("INSERT INTO " + BT +sname+ BT + " (" + fields.join(", ") + ") VALUES (" + values.join(",") + ");"); | ||||
|   } | ||||
| 
 | ||||
|   return out; | ||||
| } | ||||
| 
 | ||||
| function book_to_sql(wb, mode) { | ||||
|   return wb.SheetNames.reduce(function(acc, n) { | ||||
|     return acc.concat(sheet_to_sql(wb.Sheets[n], n, mode)); | ||||
|   }, []); | ||||
| } | ||||
| 
 | ||||
| return { | ||||
|   book_to_sql: book_to_sql, | ||||
|   sheet_to_sql: sheet_to_sql | ||||
| }; | ||||
| })(); | ||||
| if(typeof module !== 'undefined') module.exports = SheetJSSQL; | ||||
| @ -1,18 +0,0 @@ | ||||
| /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ | ||||
| /* eslint-env browser */ | ||||
| /*global ObjUtils */ | ||||
| Storage.prototype.load = function(data) { | ||||
|   var self = this; | ||||
|   Object.keys(data).forEach(function(k) { | ||||
|     self.setItem(k, JSON.stringify(data[k])); | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| Storage.prototype.dump = function() { | ||||
|   var obj = {}; | ||||
|   for(var i = 0; i < this.length; ++i) { | ||||
|     var key = this.key(i); | ||||
|     obj[key] = JSON.parse(this.getItem(key)); | ||||
|   } | ||||
|   return ObjUtils.object_to_workbook(obj); | ||||
| }; | ||||
							
								
								
									
										1
									
								
								demos/database/xlsx.full.min.js
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										1
									
								
								demos/database/xlsx.full.min.js
									
									
									
									
										vendored
									
									
								
							| @ -1 +0,0 @@ | ||||
| ../../dist/xlsx.full.min.js | ||||
| @ -1,92 +1,7 @@ | ||||
| # canvas-datagrid | ||||
| 
 | ||||
| The `sheet_to_json` utility function generates output arrays suitable for use | ||||
| with other JS libraries such as data grids for previewing data.  After extensive | ||||
| testing, [`canvas-datagrid`](https://canvas-datagrid.js.org/demo.html) stood | ||||
| out as a very high-performance grid with an incredibly simple API. | ||||
| [The new demo](https://docs.sheetjs.com/docs/getting-started/demos/grid#canvas-datagrid) | ||||
| includes a live example and improved explanations. | ||||
| 
 | ||||
| This demo is available at <https://oss.sheetjs.com/sheetjs/datagrid.html> | ||||
| 
 | ||||
| ## Obtaining the Library | ||||
| 
 | ||||
| The `canvas-datagrid` NodeJS packages include a minified script that can be | ||||
| directly inserted as a script tag.  The unpkg CDN also serves this script: | ||||
| 
 | ||||
| ```html | ||||
| <script src="https://unpkg.com/canvas-datagrid/dist/canvas-datagrid.js"></script> | ||||
| ``` | ||||
| 
 | ||||
| ## Previewing Data | ||||
| 
 | ||||
| The HTML document needs a container element: | ||||
| 
 | ||||
| ```html | ||||
| <div id="gridctr"></div> | ||||
| ``` | ||||
| 
 | ||||
| Grid initialization is a one-liner: | ||||
| 
 | ||||
| ```js | ||||
| var grid = canvasDatagrid({ | ||||
|   parentNode: document.getElementById('gridctr'), | ||||
|   data: [] | ||||
| }); | ||||
| ``` | ||||
| 
 | ||||
| For large data sets, it's necessary to constrain the size of the grid. | ||||
| 
 | ||||
| ```js | ||||
| grid.style.height = '100%'; | ||||
| grid.style.width = '100%'; | ||||
| ``` | ||||
| 
 | ||||
| Once the workbook is read and the worksheet is selected, assigning the data | ||||
| variable automatically updates the view: | ||||
| 
 | ||||
| ```js | ||||
| grid.data = XLSX.utils.sheet_to_json(ws, {header:1}); | ||||
| ``` | ||||
| 
 | ||||
| This demo previews the first worksheet. | ||||
| 
 | ||||
| ## Editing | ||||
| 
 | ||||
| `canvas-datagrid` handles the entire edit cycle.  No intervention is necessary. | ||||
| 
 | ||||
| ## Saving Data | ||||
| 
 | ||||
| `grid.data` is immediately readable and can be converted back to a worksheet. | ||||
| Some versions return an array-like object without the length, so a little bit of | ||||
| preparation may be needed: | ||||
| 
 | ||||
| ```js | ||||
| /* converts an array of array-like objects into an array of arrays */ | ||||
| function prep(arr) { | ||||
|   var out = []; | ||||
|   for(var i = 0; i < arr.length; ++i) { | ||||
|     if(!arr[i]) continue; | ||||
|     if(Array.isArray(arr[i])) { out[i] = arr[i]; continue }; | ||||
|     var o = new Array(); | ||||
|     Object.keys(arr[i]).forEach(function(k) { o[+k] = arr[i][k] }); | ||||
|     out[i] = o; | ||||
|   } | ||||
|   return out; | ||||
| } | ||||
| 
 | ||||
| /* build worksheet from the grid data */ | ||||
| var ws = XLSX.utils.aoa_to_sheet(prep(grid.data)); | ||||
| 
 | ||||
| /* build up workbook */ | ||||
| var wb = XLSX.utils.book_new(); | ||||
| XLSX.utils.book_append_sheet(wb, ws, 'SheetJS'); | ||||
| 
 | ||||
| /* generate download */ | ||||
| XLSX.writeFile(wb, "SheetJS.xlsx"); | ||||
| ``` | ||||
| 
 | ||||
| ## Additional Features | ||||
| 
 | ||||
| This demo barely scratches the surface.  The underlying grid component includes | ||||
| many additional features including massive data streaming, sorting and styling. | ||||
| 
 | ||||
| [](https://github.com/SheetJS/js-xlsx) | ||||
|  | ||||
| @ -1,162 +0,0 @@ | ||||
| <!DOCTYPE html> | ||||
| <!-- xlsx.js (C) 2013-present  SheetJS http://sheetjs.com --> | ||||
| <!-- vim: set ts=2: --> | ||||
| <html> | ||||
| <head> | ||||
| <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | ||||
| <title>SheetJS + canvas-datagrid Live Demo</title> | ||||
| <style> | ||||
| #drop{ | ||||
| 	border:2px dashed #bbb; | ||||
| 	-moz-border-radius:5px; | ||||
| 	-webkit-border-radius:5px; | ||||
| 	border-radius:5px; | ||||
| 	padding:25px; | ||||
| 	text-align:center; | ||||
| 	font:20pt bold,"Vollkorn";color:#bbb | ||||
| } | ||||
| #b64data{ | ||||
| 	width:100%; | ||||
| } | ||||
| a { text-decoration: none } | ||||
| </style> | ||||
| </head> | ||||
| <body> | ||||
| <pre> | ||||
| <b><a href="http://sheetjs.com">SheetJS Data Preview Live Demo</a></b> | ||||
| 
 | ||||
| <a href="https://canvas-datagrid.js.org/">canvas-datagrid component library</a> | ||||
| 
 | ||||
| <a href="https://github.com/SheetJS/sheetjs">Source Code Repo</a> | ||||
| <a href="https://github.com/SheetJS/sheetjs/issues">Issues?  Something look weird?  Click here and report an issue</a> | ||||
| 
 | ||||
| <div id="drop">Drop a spreadsheet file here to see sheet data</div> | ||||
| <input type="file" name="xlfile" id="xlf" /> ... or click here to select a file | ||||
| <textarea id="b64data">... or paste a base64-encoding here</textarea> | ||||
| <b>Advanced Demo Options:</b> | ||||
| </pre> | ||||
| <p><input type="submit" value="Export to XLSX!" id="xport" onclick="export_xlsx();" disabled="true"></p> | ||||
| <div id="htmlout"></div> | ||||
| <br /> | ||||
| <script src="https://unpkg.com/canvas-datagrid/dist/canvas-datagrid.js"></script> | ||||
| <script src="shim.js"></script> | ||||
| <script src="xlsx.full.min.js"></script> | ||||
| <script> | ||||
| /*jshint browser:true */ | ||||
| /* eslint-env browser */ | ||||
| /* eslint no-use-before-define:0 */ | ||||
| /*global Uint8Array, Uint16Array, ArrayBuffer */ | ||||
| /*global XLSX */ | ||||
| 
 | ||||
| var cDg; | ||||
| 
 | ||||
| var process_wb = (function() { | ||||
| 	var XPORT = document.getElementById('xport'); | ||||
| 	var HTMLOUT = document.getElementById('htmlout'); | ||||
| 
 | ||||
| 	return function process_wb(wb) { | ||||
| 		/* get data */ | ||||
| 		var ws = wb.Sheets[wb.SheetNames[0]]; | ||||
| 		var data = XLSX.utils.sheet_to_json(ws, {header:1}); | ||||
| 
 | ||||
| 		/* update canvas-datagrid */ | ||||
| 		if(!cDg) cDg = canvasDatagrid({ parentNode:HTMLOUT, data:data }); | ||||
| 		cDg.style.height = '100%'; | ||||
| 		cDg.style.width = '100%'; | ||||
| 		cDg.data = data; | ||||
| 		XPORT.disabled = false; | ||||
| 
 | ||||
| 		/* create schema (for A,B,C column headings) */ | ||||
| 		var range = XLSX.utils.decode_range(ws['!ref']); | ||||
| 		for(var i = range.s.c; i <= range.e.c; ++i) cDg.schema[i - range.s.c].title = XLSX.utils.encode_col(i); | ||||
| 
 | ||||
| 		HTMLOUT.style.height = (window.innerHeight - 400) + "px"; | ||||
| 		HTMLOUT.style.width = (window.innerWidth - 50) + "px"; | ||||
| 
 | ||||
| 		if(typeof console !== 'undefined') console.log("output", new Date()); | ||||
| 	}; | ||||
| })(); | ||||
| 
 | ||||
| var do_file = (function() { | ||||
| 	return function do_file(files) { | ||||
| 		var f = files[0]; | ||||
| 		var reader = new FileReader(); | ||||
| 		reader.onload = function(e) { | ||||
| 			if(typeof console !== 'undefined') console.log("onload", new Date()); | ||||
| 			var data = e.target.result; | ||||
| 			data = new Uint8Array(data); | ||||
| 			process_wb(XLSX.read(data, {type: 'array'})); | ||||
| 		}; | ||||
| 		reader.readAsArrayBuffer(f); | ||||
| 	}; | ||||
| })(); | ||||
| 
 | ||||
| (function() { | ||||
| 	var drop = document.getElementById('drop'); | ||||
| 	if(!drop.addEventListener) return; | ||||
| 
 | ||||
| 	function handleDrop(e) { | ||||
| 		e.stopPropagation(); | ||||
| 		e.preventDefault(); | ||||
| 		do_file(e.dataTransfer.files); | ||||
| 	} | ||||
| 
 | ||||
| 	function handleDragover(e) { | ||||
| 		e.stopPropagation(); | ||||
| 		e.preventDefault(); | ||||
| 		e.dataTransfer.dropEffect = 'copy'; | ||||
| 	} | ||||
| 
 | ||||
| 	drop.addEventListener('dragenter', handleDragover, false); | ||||
| 	drop.addEventListener('dragover', handleDragover, false); | ||||
| 	drop.addEventListener('drop', handleDrop, false); | ||||
| })(); | ||||
| 
 | ||||
| (function() { | ||||
| 	var xlf = document.getElementById('xlf'); | ||||
| 	if(!xlf.addEventListener) return; | ||||
| 	function handleFile(e) { do_file(e.target.files); } | ||||
| 	xlf.addEventListener('change', handleFile, false); | ||||
| })(); | ||||
| 
 | ||||
| var export_xlsx = (function() { | ||||
| 	function prep(arr) { | ||||
| 		var out = []; | ||||
| 		for(var i = 0; i < arr.length; ++i) { | ||||
| 			if(!arr[i]) continue; | ||||
| 			if(Array.isArray(arr[i])) { out[i] = arr[i]; continue }; | ||||
| 			var o = new Array(); | ||||
| 			Object.keys(arr[i]).forEach(function(k) { o[+k] = arr[i][k] }); | ||||
| 			out[i] = o; | ||||
| 		} | ||||
| 		return out; | ||||
| 	} | ||||
| 
 | ||||
| 	return function export_xlsx() { | ||||
| 		if(!cDg) return; | ||||
| 		/* convert canvas-datagrid data to worksheet */ | ||||
| 		var new_ws = XLSX.utils.aoa_to_sheet(prep(cDg.data)); | ||||
| 
 | ||||
| 		/* build workbook */ | ||||
| 		var new_wb = XLSX.utils.book_new(); | ||||
| 		XLSX.utils.book_append_sheet(new_wb, new_ws, 'SheetJS'); | ||||
| 
 | ||||
| 		/* write file and trigger a download */ | ||||
| 		XLSX.writeFile(new_wb, 'sheetjs.xlsx', {bookSST:true}); | ||||
| 	}; | ||||
| })(); | ||||
| </script> | ||||
| <script type="text/javascript"> | ||||
| /* eslint no-use-before-define:0 */ | ||||
| 	var _gaq = _gaq || []; | ||||
| 	_gaq.push(['_setAccount', 'UA-36810333-1']); | ||||
| 	_gaq.push(['_trackPageview']); | ||||
| 
 | ||||
| 	(function() { | ||||
| 		var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; | ||||
| 		ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; | ||||
| 		var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); | ||||
| 	})(); | ||||
| </script> | ||||
| </body> | ||||
| </html> | ||||
| @ -1 +0,0 @@ | ||||
| ../../shim.js | ||||
							
								
								
									
										1
									
								
								demos/datagrid/xlsx.full.min.js
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										1
									
								
								demos/datagrid/xlsx.full.min.js
									
									
									
									
										vendored
									
									
								
							| @ -1 +0,0 @@ | ||||
| ../../dist/xlsx.full.min.js | ||||
| @ -1,11 +0,0 @@ | ||||
| { | ||||
| 	"env": { | ||||
| 		"es6": true, | ||||
| 		"browser": true, | ||||
| 		"node": true | ||||
| 	}, | ||||
| 	"parserOptions": { | ||||
| 		"ecmaVersion": 8 | ||||
| 	}, | ||||
| 	"plugins": [ "html", "json" ] | ||||
| } | ||||
							
								
								
									
										1
									
								
								demos/electron/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										1
									
								
								demos/electron/.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1 +0,0 @@ | ||||
| out/ | ||||
| @ -1,7 +0,0 @@ | ||||
| .PHONY: init | ||||
| init: | ||||
| 	if [ ! -e xlsx.full.min.js ]; then ln -s ../../dist/xlsx.full.min.js . ; fi | ||||
| 
 | ||||
| .PHONY: run | ||||
| run: | ||||
| 	npx nw . | ||||
| @ -1,36 +1,6 @@ | ||||
| # NW.js | ||||
| 
 | ||||
| This library is compatible with NW.js and should just work out of the box. | ||||
| The demonstration uses NW.js 0.63.0 with the dist script. | ||||
| 
 | ||||
| ## Reading data | ||||
| 
 | ||||
| The standard HTML5 `FileReader` techniques from the browser apply to NW.js! | ||||
| This demo includes a drag-and-drop box as well as a file input box, mirroring | ||||
| the [SheetJS Data Preview Live Demo](https://oss.sheetjs.com/sheetjs/). | ||||
| 
 | ||||
| ## Writing data | ||||
| 
 | ||||
| File input elements with the attribute `nwsaveas` show UI for saving a file. The | ||||
| standard trick is to generate a hidden file input DOM element and "click" it. | ||||
| Since NW.js does not present a `writeFileSync` in the `fs` package, a manual | ||||
| step is required: | ||||
| 
 | ||||
| ```js | ||||
| /* from within the input change callback, `this.value` is the file name */ | ||||
| var filename = this.value, bookType = (filename.match(/[^\.]*$/)||["xlsx"])[0]; | ||||
| 
 | ||||
| /* convert the TABLE element back to a workbook */ | ||||
| var wb = XLSX.utils.table_to_book(HTMLOUT); | ||||
| 
 | ||||
| /* write to buffer */ | ||||
| var wbout = XLSX.write(wb, {type:'buffer', bookType:bookType}); | ||||
| 
 | ||||
| /* use the async fs.writeFile to save the data */ | ||||
| fs.writeFile(filename, wbout, function(err) { | ||||
| 	if(!err) return alert("Saved to " + filename); | ||||
| 	alert("Error: " + (err.message || err)); | ||||
| }); | ||||
| ``` | ||||
| [The new demo](https://docs.sheetjs.com/docs/getting-started/demos/desktop#nwjs) | ||||
| includes an improved example and detailed explanations. | ||||
| 
 | ||||
| [](https://github.com/SheetJS/js-xlsx) | ||||
|  | ||||
| @ -1,38 +0,0 @@ | ||||
| <!DOCTYPE html> | ||||
| <!-- xlsx.js (C) 2013-present  SheetJS http://sheetjs.com --> | ||||
| <!-- vim: set ts=2: --> | ||||
| <html> | ||||
| <head> | ||||
| <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | ||||
| <title>SheetJS NW.js Demo</title> | ||||
| <style> | ||||
| #drop{ | ||||
| 	border:2px dashed #bbb; | ||||
| 	-moz-border-radius:5px; | ||||
| 	-webkit-border-radius:5px; | ||||
| 	border-radius:5px; | ||||
| 	padding:25px; | ||||
| 	text-align:center; | ||||
| 	font:20pt bold,"Vollkorn";color:#bbb | ||||
| } | ||||
| a { text-decoration: none } | ||||
| </style> | ||||
| </head> | ||||
| <body> | ||||
| <pre> | ||||
| <b><a href="http://sheetjs.com">SheetJS NW.js Demo</a></b> | ||||
| 
 | ||||
| <a href="https://github.com/SheetJS/js-xlsx">Source Code Repo</a> | ||||
| <a href="https://github.com/SheetJS/js-xlsx/issues">Issues?  Something look weird?  Click here and report an issue</a> | ||||
| <br /> | ||||
| <div id="drop">Drop a spreadsheet file here to see sheet data</div> | ||||
| <input type="file" name="xlfile" id="xlf" /> ... or click here to select a file | ||||
| 
 | ||||
| </pre> | ||||
| <p><input type="submit" value="Export Data!" id="xport" onclick="export_xlsx();" disabled="true"></p> | ||||
| <div id="htmlout"></div> | ||||
| <br /> | ||||
| <script src="xlsx.full.min.js"></script> | ||||
| <script src="index.js"></script> | ||||
| </body> | ||||
| </html> | ||||
| @ -1,86 +0,0 @@ | ||||
| var fs = require('fs'); | ||||
| 
 | ||||
| var process_wb = (function() { | ||||
| 	var HTMLOUT = document.getElementById('htmlout'); | ||||
| 	var XPORT = document.getElementById('xport'); | ||||
| 
 | ||||
| 	return function process_wb(wb) { | ||||
| 		XPORT.disabled = false; | ||||
| 		HTMLOUT.innerHTML = ""; | ||||
| 		wb.SheetNames.forEach(function(sheetName) { | ||||
| 			var htmlstr = XLSX.utils.sheet_to_html(wb.Sheets[sheetName],{editable:true}); | ||||
| 			HTMLOUT.innerHTML += htmlstr; | ||||
| 		}); | ||||
| 	}; | ||||
| })(); | ||||
| 
 | ||||
| var _gaq = _gaq || []; | ||||
| _gaq.push(['_setAccount', 'UA-36810333-1']); | ||||
| _gaq.push(['_trackPageview']); | ||||
| 
 | ||||
| (function() { | ||||
| 	var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; | ||||
| 	ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; | ||||
| 	var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); | ||||
| })(); | ||||
| 
 | ||||
| var do_file = (function() { | ||||
| 	return function do_file(files) { | ||||
| 		var f = files[0]; | ||||
| 		var reader = new FileReader(); | ||||
| 		reader.onload = function(e) { | ||||
| 			var data = e.target.result; | ||||
| 			data = new Uint8Array(data); | ||||
| 			process_wb(XLSX.read(data, {type: 'array'})); | ||||
| 		}; | ||||
| 		reader.readAsArrayBuffer(f); | ||||
| 	}; | ||||
| })(); | ||||
| 
 | ||||
| (function() { | ||||
| 	var drop = document.getElementById('drop'); | ||||
| 
 | ||||
| 	function handleDrop(e) { | ||||
| 		e.stopPropagation(); | ||||
| 		e.preventDefault(); | ||||
| 		do_file(e.dataTransfer.files); | ||||
| 	} | ||||
| 
 | ||||
| 	function handleDragover(e) { | ||||
| 		e.stopPropagation(); | ||||
| 		e.preventDefault(); | ||||
| 		e.dataTransfer.dropEffect = 'copy'; | ||||
| 	} | ||||
| 
 | ||||
| 	drop.addEventListener('dragenter', handleDragover, false); | ||||
| 	drop.addEventListener('dragover', handleDragover, false); | ||||
| 	drop.addEventListener('drop', handleDrop, false); | ||||
| })(); | ||||
| 
 | ||||
| (function() { | ||||
| 	var xlf = document.getElementById('xlf'); | ||||
| 	function handleFile(e) { do_file(e.target.files); } | ||||
| 	xlf.addEventListener('change', handleFile, false); | ||||
| })(); | ||||
| 
 | ||||
| var export_xlsx = (function() { | ||||
| 	/* pre-build the nwsaveas input element */ | ||||
| 	var HTMLOUT = document.getElementById('htmlout'); | ||||
| 	var input = document.createElement('input'); | ||||
| 	input.style.display = 'none'; | ||||
| 	input.setAttribute('nwsaveas', 'SheetJSNWDemo.xlsx'); | ||||
| 	input.setAttribute('type', 'file'); | ||||
| 	document.body.appendChild(input); | ||||
| 	input.addEventListener('cancel',function(){ alert("Save was canceled!"); }); | ||||
| 	input.addEventListener('change',function(e){ | ||||
| 		var filename=this.value, bookType=(filename.match(/[^\.]*$/)||["xlsx"])[0]; | ||||
| 		var wb = XLSX.utils.table_to_book(HTMLOUT.getElementsByTagName("TABLE")[0]); | ||||
| 		var wbout = XLSX.write(wb, {type:'buffer', bookType:bookType}); | ||||
| 		fs.writeFile(filename, wbout, function(err) { | ||||
| 			if(!err) return alert("Saved to " + filename); | ||||
| 			alert("Error: " + (err.message || err)); | ||||
| 		}); | ||||
| 	}); | ||||
| 
 | ||||
| 	return function() { input.click(); }; | ||||
| })(); | ||||
| @ -1,10 +0,0 @@ | ||||
| { | ||||
| 	"name": "sheetjs-nwjs", | ||||
| 	"author": "sheetjs", | ||||
| 	"version": "0.0.0", | ||||
| 	"main": "index.html", | ||||
| 	"dependencies": { | ||||
| 		"nw": "~0.66.0", | ||||
| 		"xlsx": "https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz" | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										1
									
								
								demos/nwjs/xlsx.full.min.js
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										1
									
								
								demos/nwjs/xlsx.full.min.js
									
									
									
									
										vendored
									
									
								
							| @ -1 +0,0 @@ | ||||
| ../../dist/xlsx.full.min.js | ||||
							
								
								
									
										3
									
								
								demos/parcel/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										3
									
								
								demos/parcel/.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,3 +0,0 @@ | ||||
| .cache | ||||
| .parcel-cache | ||||
| dist | ||||
							
								
								
									
										5
									
								
								demos/rollup/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										5
									
								
								demos/rollup/.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,5 +0,0 @@ | ||||
| rollup.js | ||||
| rollup.min.js | ||||
| rollup.node.js | ||||
| worker.js | ||||
| worker.min.js | ||||
							
								
								
									
										1
									
								
								demos/typescript/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										1
									
								
								demos/typescript/.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1 +0,0 @@ | ||||
| dist | ||||
| @ -1,13 +0,0 @@ | ||||
| .PHONY: all | ||||
| all: | ||||
| 	@npm run build | ||||
| 
 | ||||
| .PHONY: lint | ||||
| lint: | ||||
| 	@npm run lint | ||||
| 
 | ||||
| .PHONY: init | ||||
| init: | ||||
| 	mkdir -p node_modules | ||||
| 	npm install typescript | ||||
| 	cd node_modules; ln -s ../../../ xlsx; cd - | ||||
| @ -1,71 +1,9 @@ | ||||
| # TypeScript | ||||
| 
 | ||||
| The library exports can be imported directly from TS code with: | ||||
| 
 | ||||
| ```typescript | ||||
| import * as XLSX from 'xlsx'; | ||||
| ``` | ||||
| 
 | ||||
| The library type definitions are available in the repo at `types/index.d.ts` and | ||||
| in the node module.  The definitions are also available in places that serve the | ||||
| node module, like [unpkg](https://unpkg.com/xlsx/types/index.d.ts). | ||||
| 
 | ||||
| This demo shows a small utility function that reads the first worksheet and | ||||
| converts to an array of arrays.  The utility function is designed to be used in | ||||
| the browser and server.  This project shows a complete deployment as a simple | ||||
| browser script and as a node module. | ||||
| 
 | ||||
| This demo is intended to illustrate simple and direct use of the `tsc` command | ||||
| line utility.  The Angular 2+ demo shows a more advanced TypeScript deployment. | ||||
| 
 | ||||
| 
 | ||||
| ## Named Exports | ||||
| 
 | ||||
| Newer TypeScript versions (2.6+) support named exports: | ||||
| 
 | ||||
| ```typescript | ||||
| import { read, write, utils } from 'xlsx' | ||||
| ``` | ||||
| 
 | ||||
| However, since this is not supported in all deployments, it is generally easier | ||||
| to use the glob import form and destructuring assignment: | ||||
| 
 | ||||
| ```typescript | ||||
| import * as XLSX from 'xlsx'; | ||||
| const { read, write, utils } = XLSX; | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| ## Library Type Definitions | ||||
| 
 | ||||
| Types are exposed in the node module directly in the path `/types/index.d.ts`. | ||||
| [unpkg CDN includes the definitions](https://unpkg.com/xlsx/types/index.d.ts). | ||||
| The named `@types/xlsx` module should not be installed! | ||||
| 
 | ||||
| Using the glob import, types must be explicitly scoped: | ||||
| 
 | ||||
| ```typescript | ||||
| import * as XLSX from 'xlsx'; | ||||
| /* the workbook type is accessible as XLSX.WorkBook */ | ||||
| const wb: XLSX.WorkBook = XLSX.read(data, options); | ||||
| ``` | ||||
| 
 | ||||
| Using named imports, the explicit type name should be imported: | ||||
| 
 | ||||
| ```typescript | ||||
| import { read, WorkBook } from 'xlsx' | ||||
| const wb: WorkBook = read(data, options); | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| ## Demo Project Structure | ||||
| 
 | ||||
| `lib/index.ts` is the TS library that will be transpiled to `dist/index.js` and | ||||
| `dist/index.d.ts`. | ||||
| 
 | ||||
| `demo.js` is a node script that uses the generated library. | ||||
| 
 | ||||
| `src/index.js` is the browser entry point.  The `browserify` bundle tool is used | ||||
| to generate `dist/browser.js`, a browser script loaded by `index.html`. | ||||
| This demo originally covered direct use of the `tsc` TypeScript compiler.  At | ||||
| the time when the demo was first written, TypeScript 2.2 had a module system | ||||
| that was incompatibile with the pure JS ecosystem.  Since then, various | ||||
| language improvements and compiler changes have obviated this demo.  Uses of | ||||
| TypeScript are scattered throughout other demos. | ||||
| 
 | ||||
| [](https://github.com/SheetJS/js-xlsx) | ||||
|  | ||||
| @ -1,5 +0,0 @@ | ||||
| /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ | ||||
| /* vim: set ts=2: */ | ||||
| /* eslint-env node */ | ||||
| var readFirstSheet = require("./").readFirstSheet; | ||||
| console.log(readFirstSheet("../../sheetjs.xlsb", {type:"file", cellDates:true})); | ||||
| @ -1 +0,0 @@ | ||||
| <script src="dist/browser.js"></script> | ||||
| @ -1,12 +0,0 @@ | ||||
| /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ | ||||
| /* vim: set ts=2: */ | ||||
| 
 | ||||
| import * as XLSX from 'xlsx'; | ||||
| 
 | ||||
| const { read, utils: { sheet_to_json } } = XLSX; | ||||
| 
 | ||||
| export function readFirstSheet(data: any, options: XLSX.ParsingOptions): any[][] { | ||||
|   const wb: XLSX.WorkBook = read(data, options); | ||||
|   const ws: XLSX.WorkSheet = wb.Sheets[wb.SheetNames[0]]; | ||||
|   return sheet_to_json(ws, { header: 1, raw: true }); | ||||
| } | ||||
| @ -1,19 +0,0 @@ | ||||
| { | ||||
|   "name": "xlsx-ts", | ||||
|   "main": "dist/index.js", | ||||
|   "types": "dist/index.d.ts", | ||||
|   "version": "0.0.0", | ||||
|   "license": "Apache-2.0", | ||||
|   "scripts": { | ||||
|     "build": "tsc && browserify -o dist/browser.js src/index.js", | ||||
|     "lint": "tslint lib/*.ts" | ||||
|   }, | ||||
|   "private": true, | ||||
|   "dependencies": { | ||||
|     "xlsx": "https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "typescript": "~2.6.1", | ||||
|     "browserify": "~14.5.0" | ||||
|   } | ||||
| } | ||||
| @ -1,6 +0,0 @@ | ||||
| /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ | ||||
| /* vim: set ts=2: */ | ||||
| /* eslint-env browser */ | ||||
| /* global require */ | ||||
| var readFirstSheet = require("../").readFirstSheet; | ||||
| console.log(readFirstSheet("a,b,c\n1,2,3\n4,5,6", {type:"binary"})); | ||||
| @ -1,9 +0,0 @@ | ||||
| { | ||||
|   "compilerOptions": { | ||||
|     "target": "es5", | ||||
|     "module": "commonjs", | ||||
|     "declaration": true, | ||||
|     "outDir": "./dist", | ||||
|     "strict": true | ||||
|   } | ||||
| } | ||||
| @ -1,11 +0,0 @@ | ||||
| { | ||||
| 	"extends": "tslint-config-airbnb", | ||||
| 	"rules": { | ||||
| 		"whitespace": false, | ||||
| 		"no-sparse-arrays": false, | ||||
| 		"only-arrow-functions": false, | ||||
| 		"no-consecutive-blank-lines": false, | ||||
| 		"prefer-conditional-expression": false, | ||||
| 		"one-variable-per-declaration": false | ||||
| 	} | ||||
| } | ||||
| @ -15,6 +15,7 @@ function stox(wb) { | ||||
|   wb.SheetNames.forEach(function (name) { | ||||
|     var o = { name: name, rows: {} }; | ||||
|     var ws = wb.Sheets[name]; | ||||
|     if(!ws || !ws["!ref"]) return; | ||||
|     var range = XLSX.utils.decode_range(ws['!ref']); | ||||
|     // sheet_to_json will lost empty row and col at begin as default
 | ||||
|     range.s = { r: 0, c: 0 }; | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user