diff options
Diffstat (limited to 'web-tools/web/_static/jexcel/jexcel.js')
-rw-r--r-- | web-tools/web/_static/jexcel/jexcel.js | 14037 |
1 files changed, 14037 insertions, 0 deletions
diff --git a/web-tools/web/_static/jexcel/jexcel.js b/web-tools/web/_static/jexcel/jexcel.js new file mode 100644 index 0000000..3e34fcb --- /dev/null +++ b/web-tools/web/_static/jexcel/jexcel.js @@ -0,0 +1,14037 @@ +/**
+ * jExcel v4.4.1
+ *
+ * Author: Paul Hodel <paul.hodel@gmail.com>
+ * Website: https://bossanova.uk/jexcel/
+ * Description: Create amazing web based spreadsheets.
+ *
+ * This software is distribute under MIT License
+ */
+
+ if (! jSuites && typeof(require) === 'function') {
+ var jSuites = require('jsuites');
+ require('jsuites/dist/jsuites.css');
+}
+
+;(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+ typeof define === 'function' && define.amd ? define(factory) :
+ global.jexcel = factory();
+}(this, (function () {
+
+ 'use strict';
+
+ // Jexcel core object + + var jexcel = (function(el, options) { + // Create jexcel object + var obj = {}; + obj.options = {}; +
+ if (! (el instanceof Element || el instanceof HTMLDocument)) {
+ console.error('JEXCEL: el is not a valid DOM element');
+ return false;
+ } else if (el.tagName == 'TABLE') {
+ if (options = jexcel.createFromTable(el, options)) {
+ var div = document.createElement('div');
+ el.parentNode.insertBefore(div, el);
+ el.remove();
+ el = div;
+ } else {
+ console.error('JEXCEL: el is not a valid DOM element');
+ return false;
+ }
+ }
+ + // Loading default configuration + var defaults = { + // External data + url:null, + // Data + data:null, + // Copy behavior + copyCompatibility:false,
+ root:null, + // Rows and columns definitions + rows:[], + columns:[], + // Deprected legacy options + colHeaders:[], + colWidths:[], + colAlignments:[], + nestedHeaders:null, + // Column width that is used by default + defaultColWidth:50, + defaultColAlign:'center', + // Spare rows and columns + minSpareRows:0, + minSpareCols:0, + // Minimal table dimensions + minDimensions:[0,0], + // Allow Export + allowExport:true,
+ // @type {boolean} - Include the header titles on download
+ includeHeadersOnDownload:false,
+ // @type {boolean} - Include the header titles on copy
+ includeHeadersOnCopy:false, + // Allow column sorting + columnSorting:true, + // Allow column dragging + columnDrag:false, + // Allow column resizing + columnResize:true, + // Allow row resizing + rowResize:false, + // Allow row dragging + rowDrag:true, + // Allow table edition + editable:true, + // Allow new rows + allowInsertRow:true, + // Allow new rows + allowManualInsertRow:true, + // Allow new columns + allowInsertColumn:true, + // Allow new rows + allowManualInsertColumn:true, + // Allow row delete + allowDeleteRow:true, + // Allow deleting of all rows + allowDeletingAllRows:false, + // Allow column delete + allowDeleteColumn:true, + // Allow rename column + allowRenameColumn:true, + // Allow comments + allowComments:false, + // Global wrap + wordWrap:false, + // Image options + imageOptions: null, + // CSV source + csv:null, + // Filename + csvFileName:'jexcel', + // Consider first line as header + csvHeaders:true,
+ // Delimiters + csvDelimiter:',',
+ // First row as header
+ parseTableFirstRowAsHeader:false,
+ parseTableAutoCellType:false, + // Disable corner selection + selectionCopy:true, + // Merged cells + mergeCells:{}, + // Create toolbar + toolbar:null, + // Allow search + search:false, + // Create pagination + pagination:false, + paginationOptions:null,
+ // Full screen + fullscreen:false, + // Lazy loading + lazyLoading:false, + loadingSpin:false, + // Table overflow + tableOverflow:false, + tableHeight:'300px', + tableWidth:null, + // Meta + meta: null, + // Style + style:null, + // Execute formulas + parseFormulas:true, + autoIncrement:true, + autoCasting:true,
+ // Security
+ secureFormulas:true,
+ stripHTML:true,
+ stripHTMLOnCopy:false,
+ // Filters
+ filters:false,
+ footers:null,
+ // Event handles + onundo:null, + onredo:null, + onload:null, + onchange:null, + onbeforechange:null, + onafterchanges:null, + onbeforeinsertrow: null, + oninsertrow:null, + onbeforeinsertcolumn: null, + oninsertcolumn:null, + onbeforedeleterow:null, + ondeleterow:null, + onbeforedeletecolumn:null, + ondeletecolumn:null, + onmoverow:null, + onmovecolumn:null, + onresizerow:null, + onresizecolumn:null, + onsort:null, + onselection:null,
+ oncopy:null, + onpaste:null, + onbeforepaste:null, + onmerge:null, + onfocus:null, + onblur:null, + onchangeheader:null,
+ oncreateeditor:null, + oneditionstart:null, + oneditionend:null, + onchangestyle:null, + onchangemeta:null,
+ onchangepage:null,
+ onbeforesave:null,
+ onsave:null,
+ // Global event dispatcher
+ onevent:null,
+ // Persistance
+ persistance:false, + // Customize any cell behavior + updateTable:null, + // Detach the HTML table when calling updateTable
+ detachForUpdates: false,
+ freezeColumns:null, + // Texts + text:{ + noRecordsFound: 'No records found', + showingPage: 'Showing page {0} of {1} entries', + show: 'Show ', + search: 'Search', + entries: ' entries', + columnName: 'Column name', + insertANewColumnBefore: 'Insert a new column before', + insertANewColumnAfter: 'Insert a new column after', + deleteSelectedColumns: 'Delete selected columns', + renameThisColumn: 'Rename this column', + orderAscending: 'Order ascending', + orderDescending: 'Order descending', + insertANewRowBefore: 'Insert a new row before', + insertANewRowAfter: 'Insert a new row after', + deleteSelectedRows: 'Delete selected rows', + editComments: 'Edit comments', + addComments: 'Add comments', + comments: 'Comments', + clearComments: 'Clear comments', + copy: 'Copy...', + paste: 'Paste...', + saveAs: 'Save as...', + about: 'About', + areYouSureToDeleteTheSelectedRows: 'Are you sure to delete the selected rows?', + areYouSureToDeleteTheSelectedColumns: 'Are you sure to delete the selected columns?', + thisActionWillDestroyAnyExistingMergedCellsAreYouSure: 'This action will destroy any existing merged cells. Are you sure?', + thisActionWillClearYourSearchResultsAreYouSure: 'This action will clear your search results. Are you sure?', + thereIsAConflictWithAnotherMergedCell: 'There is a conflict with another merged cell', + invalidMergeProperties: 'Invalid merged properties', + cellAlreadyMerged: 'Cell already merged', + noCellsSelected: 'No cells selected', + }, + // About message + about:"jExcel CE Spreadsheet\nVersion 4.4.1\nAuthor: Paul Hodel <paul.hodel@gmail.com>\nWebsite: https://bossanova.uk/jexcel/v3", + }; + + // Loading initial configuration from user + for (var property in defaults) {
+ if (options && options.hasOwnProperty(property)) {
+ if (property === 'text') {
+ obj.options[property] = defaults[property];
+ for (var textKey in options[property]) {
+ if (options[property].hasOwnProperty(textKey)){
+ obj.options[property][textKey] = options[property][textKey];
+ }
+ }
+ } else {
+ obj.options[property] = options[property];
+ } + } else { + obj.options[property] = defaults[property]; + } + } + + // Global elements + obj.el = el; + obj.corner = null; + obj.contextMenu = null; + obj.textarea = null; + obj.ads = null; + obj.content = null; + obj.table = null; + obj.thead = null; + obj.tbody = null; + obj.rows = []; + obj.results = null; + obj.searchInput = null; + obj.toolbar = null; + obj.pagination = null; + obj.pageNumber = null; + obj.headerContainer = null; + obj.colgroupContainer = null; + + // Containers + obj.headers = []; + obj.records = []; + obj.history = []; + obj.formula = []; + obj.colgroup = []; + obj.selection = []; + obj.highlighted = []; + obj.selectedCell = null; + obj.selectedContainer = null; + obj.style = []; + obj.data = null;
+ obj.filter = null; + obj.filters = [];
+ + // Internal controllers + obj.cursor = null; + obj.historyIndex = -1; + obj.ignoreEvents = false; + obj.ignoreHistory = false; + obj.edition = null; + obj.hashString = null; + obj.resizing = null; + obj.dragging = null; + + // Lazy loading + if (obj.options.lazyLoading == true && (obj.options.tableOverflow == false && obj.options.fullscreen == false)) { + console.error('JEXCEL: The lazyloading only works when tableOverflow = yes or fullscreen = yes'); + obj.options.lazyLoading = false; + } + + /** + * Activate/Disable fullscreen + * use programmatically : table.fullscreen(); or table.fullscreen(true); or table.fullscreen(false); + * @Param {boolean} activate + */ + obj.fullscreen = function(activate) { + // If activate not defined, get reverse options.fullscreen + if (activate == null) { + activate = ! obj.options.fullscreen; + } + + // If change + if (obj.options.fullscreen != activate) { + obj.options.fullscreen = activate; + + // Test LazyLoading conflict + if (activate == true) { + el.classList.add('fullscreen'); + } else { + el.classList.remove('fullscreen'); + } + } + } +
+ /**
+ * Trigger events
+ */
+ obj.dispatch = function(event) {
+ // Dispatch events
+ if (! obj.ignoreEvents) {
+ // Call global event
+ if (typeof(obj.options.onevent) == 'function') {
+ var ret = obj.options.onevent.apply(this, arguments);
+ }
+ // Call specific events
+ if (typeof(obj.options[event]) == 'function') {
+ var ret = obj.options[event].apply(this, Array.prototype.slice.call(arguments, 1));
+ }
+ }
+
+ // Persistance
+ if (event == 'onafterchanges' && obj.options.persistance) {
+ var url = obj.options.persistance == true ? obj.options.url : obj.options.persistance;
+ var data = obj.prepareJson(arguments[2]);
+ obj.save(url, data);
+ }
+
+ return ret;
+ }
+ + /** + * Prepare the jexcel table + * + * @Param config + */ + obj.prepareTable = function() { + // Loading initial data from remote sources + var results = []; + + // Number of columns + var size = obj.options.columns.length; + + if (obj.options.data && typeof(obj.options.data[0]) !== 'undefined') { + // Data keys + var keys = Object.keys(obj.options.data[0]); + + if (keys.length > size) { + size = keys.length; + } + } + + // Minimal dimensions + if (obj.options.minDimensions[0] > size) { + size = obj.options.minDimensions[0]; + } + + // Requests + var multiple = []; + + // Preparations + for (var i = 0; i < size; i++) { + // Deprected options. You should use only columns + if (! obj.options.colHeaders[i]) { + obj.options.colHeaders[i] = ''; + } + if (! obj.options.colWidths[i]) { + obj.options.colWidths[i] = obj.options.defaultColWidth; + } + if (! obj.options.colAlignments[i]) { + obj.options.colAlignments[i] = obj.options.defaultColAlign; + } + + // Default column description + if (! obj.options.columns[i]) { + obj.options.columns[i] = { type:'text' }; + } else if (! obj.options.columns[i].type) { + obj.options.columns[i].type = 'text'; + } + if (! obj.options.columns[i].name) { + obj.options.columns[i].name = keys && keys[i] ? keys[i] : i; + } + if (! obj.options.columns[i].source) { + obj.options.columns[i].source = []; + } + if (! obj.options.columns[i].options) { + obj.options.columns[i].options = []; + } + if (! obj.options.columns[i].editor) { + obj.options.columns[i].editor = null; + } + if (! obj.options.columns[i].allowEmpty) { + obj.options.columns[i].allowEmpty = false; + } + if (! obj.options.columns[i].title) { + obj.options.columns[i].title = obj.options.colHeaders[i] ? obj.options.colHeaders[i] : ''; + } + if (! obj.options.columns[i].width) { + obj.options.columns[i].width = obj.options.colWidths[i] ? obj.options.colWidths[i] : obj.options.defaultColWidth; + } + if (! obj.options.columns[i].align) { + obj.options.columns[i].align = obj.options.colAlignments[i] ? obj.options.colAlignments[i] : 'center'; + } + + // Pre-load initial source for json autocomplete + if (obj.options.columns[i].type == 'autocomplete' || obj.options.columns[i].type == 'dropdown') { + // if remote content + if (obj.options.columns[i].url) { + multiple.push({ + url: obj.options.columns[i].url, + index: i, + method: 'GET', + dataType: 'json', + success: function(data) { + var source = []; + for (var i = 0; i < data.length; i++) { + obj.options.columns[this.index].source.push(data[i]); + } + } + }); + } + } else if (obj.options.columns[i].type == 'calendar') { + // Default format for date columns + if (! obj.options.columns[i].options.format) { + obj.options.columns[i].options.format = 'DD/MM/YYYY'; + } + } + } + + // Create the table when is ready
+ if (! multiple.length) {
+ obj.createTable();
+ } else {
+ jSuites.ajax(multiple, function() {
+ obj.createTable();
+ });
+ } + } + + obj.createTable = function() { + // Elements + obj.table = document.createElement('table'); + obj.thead = document.createElement('thead'); + obj.tbody = document.createElement('tbody'); + + // Create headers controllers + obj.headers = []; + obj.colgroup = []; + + // Create table container + obj.content = document.createElement('div'); + obj.content.classList.add('jexcel_content'); + obj.content.onscroll = function(e) {
+ obj.scrollControls(e);
+ }
+ obj.content.onwheel = function(e) {
+ obj.wheelControls(e);
+ }
+ + // Create toolbar object + obj.toolbar = document.createElement('div'); + obj.toolbar.classList.add('jexcel_toolbar'); + + // Search + var searchContainer = document.createElement('div'); + var searchText = document.createTextNode((obj.options.text.search) + ': '); + obj.searchInput = document.createElement('input'); + obj.searchInput.classList.add('jexcel_search'); + searchContainer.appendChild(searchText); + searchContainer.appendChild(obj.searchInput); + obj.searchInput.onfocus = function() { + obj.resetSelection(); + } + + // Pagination select option + var paginationUpdateContainer = document.createElement('div'); + + if (obj.options.pagination > 0 && obj.options.paginationOptions && obj.options.paginationOptions.length > 0) { + obj.paginationDropdown = document.createElement('select'); + obj.paginationDropdown.classList.add('jexcel_pagination_dropdown'); + obj.paginationDropdown.onchange = function() { + obj.options.pagination = parseInt(this.value); + obj.page(0); + } + + for (var i = 0; i < obj.options.paginationOptions.length; i++) { + var temp = document.createElement('option'); + temp.value = obj.options.paginationOptions[i]; + temp.innerHTML = obj.options.paginationOptions[i]; + obj.paginationDropdown.appendChild(temp); + } + + // Set initial pagination value
+ obj.paginationDropdown.value = obj.options.pagination;
+
+ paginationUpdateContainer.appendChild(document.createTextNode(obj.options.text.show)); + paginationUpdateContainer.appendChild(obj.paginationDropdown); + paginationUpdateContainer.appendChild(document.createTextNode(obj.options.text.entries)); + } + + // Filter and pagination container + var filter = document.createElement('div'); + filter.classList.add('jexcel_filter'); + filter.appendChild(paginationUpdateContainer); + filter.appendChild(searchContainer); + + // Colsgroup + obj.colgroupContainer = document.createElement('colgroup'); + var tempCol = document.createElement('col'); + tempCol.setAttribute('width', '50'); + obj.colgroupContainer.appendChild(tempCol); + + // Nested + if (obj.options.nestedHeaders && obj.options.nestedHeaders.length > 0) { + // Flexible way to handle nestedheaders + if (obj.options.nestedHeaders[0] && obj.options.nestedHeaders[0][0]) { + for (var j = 0; j < obj.options.nestedHeaders.length; j++) { + obj.thead.appendChild(obj.createNestedHeader(obj.options.nestedHeaders[j])); + } + } else { + obj.thead.appendChild(obj.createNestedHeader(obj.options.nestedHeaders)); + } + } + + // Row + obj.headerContainer = document.createElement('tr'); + var tempCol = document.createElement('td'); + tempCol.classList.add('jexcel_selectall'); + obj.headerContainer.appendChild(tempCol); + + for (var i = 0; i < obj.options.columns.length; i++) { + // Create header + obj.createCellHeader(i); + // Append cell to the container + obj.headerContainer.appendChild(obj.headers[i]); + obj.colgroupContainer.appendChild(obj.colgroup[i]); + }
+ + obj.thead.appendChild(obj.headerContainer);
+
+ // Filters + if (obj.options.filters == true) {
+ obj.filter = document.createElement('tr');
+ var td = document.createElement('td');
+ obj.filter.appendChild(td);
+
+ for (var i = 0; i < obj.options.columns.length; i++) {
+ var td = document.createElement('td');
+ td.innerHTML = ' ';
+ td.setAttribute('data-x', i);
+ td.className = 'jexcel_column_filter';
+ if (obj.options.columns[i].type == 'hidden') {
+ td.style.display = 'none';
+ }
+ obj.filter.appendChild(td);
+ }
+
+ obj.thead.appendChild(obj.filter);
+ } + + // Content table + obj.table = document.createElement('table'); + obj.table.classList.add('jexcel'); + obj.table.setAttribute('cellpadding', '0'); + obj.table.setAttribute('cellspacing', '0'); + obj.table.setAttribute('unselectable', 'yes'); + //obj.table.setAttribute('onselectstart', 'return false'); + obj.table.appendChild(obj.colgroupContainer); + obj.table.appendChild(obj.thead); + obj.table.appendChild(obj.tbody);
+ + // Spreadsheet corner + obj.corner = document.createElement('div'); + obj.corner.className = 'jexcel_corner'; + obj.corner.setAttribute('unselectable', 'on'); + obj.corner.setAttribute('onselectstart', 'return false'); + + if (obj.options.selectionCopy == false) { + obj.corner.style.display = 'none'; + } + + // Textarea helper + obj.textarea = document.createElement('textarea'); + obj.textarea.className = 'jexcel_textarea'; + obj.textarea.id = 'jexcel_textarea'; + obj.textarea.tabIndex = '-1';
+ + // Contextmenu container + obj.contextMenu = document.createElement('div'); + obj.contextMenu.className = 'jexcel_contextmenu'; + + // Create element + jSuites.contextmenu(obj.contextMenu, { + onclick:function() { + obj.contextMenu.contextmenu.close(false); + } + }); + + // Powered by jExcel
+ var ads = document.createElement('a');
+ ads.setAttribute('href', 'https://bossanova.uk/jexcel/');
+ obj.ads = document.createElement('div');
+ obj.ads.className = 'jexcel_about';
+ try { + if (typeof(sessionStorage) !== "undefined" && ! sessionStorage.getItem('jexcel')) {
+ sessionStorage.setItem('jexcel', true);
+ var img = document.createElement('img');
+ img.src = '//bossanova.uk/jexcel/logo.png';
+ ads.appendChild(img);
+ }
+ } catch (exception) { + } + var span = document.createElement('span');
+ span.innerHTML = 'Jexcel spreadsheet';
+ ads.appendChild(span);
+ obj.ads.appendChild(ads);
+ + // Create table container TODO: frozen columns + var container = document.createElement('div'); + container.classList.add('jexcel_table'); + + // Pagination + obj.pagination = document.createElement('div'); + obj.pagination.classList.add('jexcel_pagination'); + var paginationInfo = document.createElement('div'); + var paginationPages = document.createElement('div'); + obj.pagination.appendChild(paginationInfo); + obj.pagination.appendChild(paginationPages); +
+ // Hide pagination if not in use
+ if (! obj.options.pagination) {
+ obj.pagination.style.display = 'none';
+ }
+ + // Append containers to the table + if (obj.options.search == true) { + el.appendChild(filter); + } + + // Elements + obj.content.appendChild(obj.table); + obj.content.appendChild(obj.corner); + obj.content.appendChild(obj.textarea); + + el.appendChild(obj.toolbar); + el.appendChild(obj.content); + el.appendChild(obj.pagination); + el.appendChild(obj.contextMenu); + el.appendChild(obj.ads); + el.classList.add('jexcel_container'); + + // Create toolbar + if (obj.options.toolbar && obj.options.toolbar.length) { + obj.createToolbar(); + } + + // Fullscreen + if (obj.options.fullscreen == true) { + el.classList.add('fullscreen'); + } else { + // Overflow + if (obj.options.tableOverflow == true) { + if (obj.options.tableHeight) { + obj.content.style['overflow-y'] = 'auto'; + obj.content.style.maxHeight = obj.options.tableHeight; + } + if (obj.options.tableWidth) { + obj.content.style['overflow-x'] = 'auto'; + obj.content.style.width = obj.options.tableWidth; + } + } + } + + // With toolbars + if (obj.options.tableOverflow != true && obj.options.toolbar) { + el.classList.add('with-toolbar'); + } + + // Actions + if (obj.options.columnDrag == true) { + obj.thead.classList.add('draggable'); + } + if (obj.options.columnResize == true) { + obj.thead.classList.add('resizable'); + } + if (obj.options.rowDrag == true) { + obj.tbody.classList.add('draggable'); + } + if (obj.options.rowResize == true) { + obj.tbody.classList.add('resizable'); + } + + // Load data + obj.setData(); + + // Style + if (obj.options.style) { + obj.setStyle(obj.options.style, null, null, 1, 1); + } + } + + /** + * Refresh the data
+ *
+ * @return void
+ */
+ obj.refresh = function() {
+ if (obj.options.url) {
+ // Loading
+ if (obj.options.loadingSpin == true) {
+ jSuites.loading.show();
+ }
+
+ jSuites.ajax({
+ url: obj.options.url,
+ method: 'GET',
+ dataType: 'json',
+ success: function(result) {
+ // Data
+ obj.options.data = (result.data) ? result.data : result;
+ // Prepare table
+ obj.setData();
+ // Hide spin
+ if (obj.options.loadingSpin == true) {
+ jSuites.loading.hide();
+ }
+ }
+ });
+ } else {
+ obj.setData();
+ }
+ }
+ + /** + * Set data + * + * @param array data In case no data is sent, default is reloaded + * @return void + */ + obj.setData = function(data) { + // Update data + if (data) { + if (typeof(data) == 'string') { + data = JSON.parse(data); + } + + obj.options.data = data; + } + + // Data + if (! obj.options.data) { + obj.options.data = []; + } + + // Prepare data + if (obj.options.data && obj.options.data[0]) {
+ if (! Array.isArray(obj.options.data[0])) { + var data = []; + for (var j = 0; j < obj.options.data.length; j++) {
+ var row = []; + for (var i = 0; i < obj.options.columns.length; i++) {
+ row[i] = obj.options.data[j][obj.options.columns[i].name]; + } + data.push(row); + } + + obj.options.data = data;
+ } + } + + // Adjust minimal dimensions + var j = 0; + var i = 0; + var size_i = obj.options.columns.length; + var size_j = obj.options.data.length; + var min_i = obj.options.minDimensions[0]; + var min_j = obj.options.minDimensions[1]; + var max_i = min_i > size_i ? min_i : size_i; + var max_j = min_j > size_j ? min_j : size_j; + + for (j = 0; j < max_j; j++) { + for (i = 0; i < max_i; i++) { + if (obj.options.data[j] == undefined) { + obj.options.data[j] = []; + } + + if (obj.options.data[j][i] == undefined) { + obj.options.data[j][i] = ''; + } + } + } + + // Reset containers + obj.rows = []; + obj.results = null; + obj.records = []; + obj.history = []; + + // Reset internal controllers + obj.historyIndex = -1; + + // Reset data + obj.tbody.innerHTML = ''; + + // Lazy loading + if (obj.options.lazyLoading == true) { + // Load only 100 records + var startNumber = 0 + var finalNumber = obj.options.data.length < 100 ? obj.options.data.length : 100; + + if (obj.options.pagination) { + obj.options.pagination = false; + console.error('JEXCEL: Pagination will be disable due the lazyLoading'); + } + } else if (obj.options.pagination) { + // Pagination + if (! obj.pageNumber) { + obj.pageNumber = 0; + } + var quantityPerPage = obj.options.pagination; + startNumber = (obj.options.pagination * obj.pageNumber); + finalNumber = (obj.options.pagination * obj.pageNumber) + obj.options.pagination; + + if (obj.options.data.length < finalNumber) { + finalNumber = obj.options.data.length; + } + } else { + var startNumber = 0; + var finalNumber = obj.options.data.length; + } + + // Append nodes to the HTML + for (j = 0; j < obj.options.data.length; j++) { + // Create row + var tr = obj.createRow(j, obj.options.data[j]); + // Append line to the table + if (j >= startNumber && j < finalNumber) { + obj.tbody.appendChild(tr); + } + } + + if (obj.options.lazyLoading == true) { + // Do not create pagination with lazyloading activated + } else if (obj.options.pagination) { + obj.updatePagination(); + } + + // Merge cells + if (obj.options.mergeCells) { + var keys = Object.keys(obj.options.mergeCells); + for (var i = 0; i < keys.length; i++) { + var num = obj.options.mergeCells[keys[i]]; + obj.setMerge(keys[i], num[0], num[1], 1); + } + } + + // Updata table with custom configurations if applicable + obj.updateTable(); +
+ // Onload
+ obj.dispatch('onload', el, obj); + } + + /** + * Get the whole table data + * + * @param bool get highlighted cells only + * @return array data + */ + obj.getData = function(highlighted, dataOnly) { + // Control vars + var dataset = []; + var px = 0; + var py = 0; +
+ // Data type
+ var dataType = dataOnly == true || obj.options.copyCompatibility == false ? true : false;
+ + // Column and row length + var x = obj.options.columns.length + var y = obj.options.data.length + + // Go through the columns to get the data + for (var j = 0; j < y; j++) { + px = 0; + for (var i = 0; i < x; i++) { + // Cell selected or fullset + if (! highlighted || obj.records[j][i].classList.contains('highlight')) { + // Get value + if (! dataset[py]) { + dataset[py] = []; + } + if (! dataType) { + dataset[py][px] = obj.records[j][i].innerHTML; + } else { + dataset[py][px] = obj.options.data[j][i]; + } + px++; + } + } + if (px > 0) { + py++; + } + } + + return dataset; + } + + /** + * Get the whole table data + * + * @param integer row number + * @return string value + */ + obj.getJson = function(highlighted) { + // Control vars + var data = []; + + // Column and row length + var x = obj.options.columns.length + var y = obj.options.data.length + + // Go through the columns to get the data + for (var j = 0; j < y; j++) { + var row = null; + for (var i = 0; i < x; i++) { + if (! highlighted || obj.records[j][i].classList.contains('highlight')) { + if (row == null) { + row = {}; + }
+ if (! obj.options.columns[i].name) {
+ obj.options.columns[i].name = i;
+ } + row[obj.options.columns[i].name] = obj.options.data[j][i]; + } + } + + if (row != null) { + data.push(row); + } + } + + return data; + } +
+ /**
+ * Prepare JSON in the correct format
+ */
+ obj.prepareJson = function(data) {
+ var rows = [];
+ for (var i = 0; i < data.length; i++) {
+ var x = data[i].x;
+ var y = data[i].y;
+ var k = obj.options.columns[x].name ? obj.options.columns[x].name : x;
+
+ // Create row
+ if (! rows[y]) {
+ rows[y] = {
+ row: y,
+ data: {},
+ };
+ }
+ rows[y].data[k] = data[i].newValue;
+ }
+
+ // Filter rows
+ return rows.filter(function (el) {
+ return el != null;
+ });
+ }
+
+ /**
+ * Post json to a remote server
+ */
+ obj.save = function(url, data) {
+ // Parse anything in the data before sending to the server
+ var ret = obj.dispatch('onbeforesave', el, obj, data);
+console.log(ret);
+ if (ret) {
+ var data = ret;
+ } else {
+ if (ret === false) {
+ return false;
+ }
+ }
+
+ // Remove update
+ jSuites.ajax({
+ url: url,
+ method: 'POST',
+ dataType: 'json',
+ data: { data: JSON.stringify(data) },
+ success: function(result) {
+ // Event
+ obj.dispatch('onsave', el, obj, data);
+ }
+ });
+ }
+ + /** + * Get a row data by rowNumber + */ + obj.getRowData = function(rowNumber) { + return obj.options.data[rowNumber]; + } + + /** + * Set a row data by rowNumber + */ + obj.setRowData = function(rowNumber, data) { + for (var i = 0; i < obj.headers.length; i++) { + // Update cell + var columnName = jexcel.getColumnNameFromId([ i, rowNumber ]); + // Set value + if (data[i] != null) { + obj.setValue(columnName, data[i]); + } + } + } + + /** + * Get a column data by columnNumber + */ + obj.getColumnData = function(columnNumber) { + var dataset = []; + // Go through the rows to get the data + for (var j = 0; j < obj.options.data.length; j++) { + dataset.push(obj.options.data[j][columnNumber]); + } + return dataset; + } + + /** + * Set a column data by colNumber + */ + obj.setColumnData = function(colNumber, data) { + for (var j = 0; j < obj.rows.length; j++) { + // Update cell + var columnName = jexcel.getColumnNameFromId([ colNumber, j ]); + // Set value + if (data[j] != null) { + obj.setValue(columnName, data[j]); + } + } + } + + /** + * Create row + */ + obj.createRow = function(j, data) { + // Create container + if (! obj.records[j]) { + obj.records[j] = []; + } + // Default data + if (! data) { + var data = obj.options.data[j]; + } + // New line of data to be append in the table + obj.rows[j] = document.createElement('tr'); + obj.rows[j].setAttribute('data-y', j); + // Definitions
+ if (obj.options.rows[j]) {
+ if (obj.options.rows[j].height) {
+ obj.rows[j].style.height = obj.options.rows[j].height;
+ }
+
+ var index = obj.options.rows[j].title;
+ } else {
+ var index = parseInt(j + 1);
+ }
+ // Row number label + var td = document.createElement('td'); + td.innerHTML = index; + td.setAttribute('data-y', j); + td.className = 'jexcel_row'; + obj.rows[j].appendChild(td); + + // Data columns + for (var i = 0; i < obj.options.columns.length; i++) { + // New column of data to be append in the line + obj.records[j][i] = obj.createCell(i, j, data[i]); + // Add column to the row + obj.rows[j].appendChild(obj.records[j][i]); + } + + // Add row to the table body + return obj.rows[j]; + } +
+ obj.parseValue = function(i, j, value) {
+ if ((''+value).substr(0,1) == '=' && obj.options.parseFormulas == true) {
+ value = obj.executeFormula(value, i, j)
+ }
+ if (obj.options.columns[i].mask) {
+ var decimal = obj.options.columns[i].decimal || '.';
+ value = '' + jSuites.mask.run(value, obj.options.columns[i].mask, decimal);
+ }
+
+ return value;
+ }
+ + /** + * Create cell + */ + obj.createCell = function(i, j, value) { + // Create cell and properties + var td = document.createElement('td'); + td.setAttribute('data-x', i); + td.setAttribute('data-y', j);
+
+ // Security
+ if ((''+value).substr(0,1) == '=' && obj.options.secureFormulas == true) {
+ var val = secureFormula(value);
+ if (val != value) {
+ // Update the data container
+ value = val;
+ }
+ }
+
+ // Custom column
+ if (obj.options.columns[i].editor) {
+ if (obj.options.stripHTML === false || obj.options.columns[i].stripHTML === false) {
+ td.innerHTML = value;
+ } else {
+ td.innerText = value;
+ }
+ if (typeof(obj.options.columns[i].editor.createCell) == 'function') {
+ td = obj.options.columns[i].editor.createCell(td);
+ }
+ } else { + // Hidden column + if (obj.options.columns[i].type == 'hidden') { + td.style.display = 'none'; + td.innerText = value; + } else if (obj.options.columns[i].type == 'checkbox' || obj.options.columns[i].type == 'radio') { + // Create input + var element = document.createElement('input'); + element.type = obj.options.columns[i].type; + element.name = 'c' + i; + element.checked = (value == 1 || value == true || value == 'true') ? true : false; + element.onclick = function() { + obj.setValue(td, this.checked); + } + + if (obj.options.columns[i].readOnly == true || obj.options.editable == false) { + element.setAttribute('disabled', 'disabled'); + } + + // Append to the table + td.appendChild(element); + // Make sure the values are correct + obj.options.data[j][i] = element.checked; + } else if (obj.options.columns[i].type == 'calendar') { + // Try formatted date + var formatted = jSuites.calendar.extractDateFromString(value, obj.options.columns[i].options.format); + // Create calendar cell + td.innerText = jSuites.calendar.getDateString(formatted ? formatted : value, obj.options.columns[i].options.format); + } else if (obj.options.columns[i].type == 'dropdown' || obj.options.columns[i].type == 'autocomplete') { + // Create dropdown cell + td.classList.add('jexcel_dropdown'); + td.innerText = obj.getDropDownValue(i, value); + } else if (obj.options.columns[i].type == 'color') { + if (obj.options.columns[i].render == 'square') { + var color = document.createElement('div'); + color.className = 'color'; + color.style.backgroundColor = value; + td.appendChild(color); + } else { + td.style.color = value; + td.innerText = value; + } + } else if (obj.options.columns[i].type == 'image') { + if (value && value.substr(0, 10) == 'data:image') { + var img = document.createElement('img'); + img.src = value; + td.appendChild(img); + } + } else {
+ if (obj.options.columns[i].type == 'html') {
+ td.innerHTML = stripScript(obj.parseValue(i, j, value));
+ } else {
+ if (obj.options.stripHTML === false || obj.options.columns[i].stripHTML === false) {
+ td.innerHTML = stripScript(obj.parseValue(i, j, value));
+ } else {
+ td.innerText = obj.parseValue(i, j, value);
+ }
+ } + }
+ } + + // Readonly + if (obj.options.columns[i].readOnly == true) { + td.className = 'readonly'; + } + + // Text align + var colAlign = obj.options.columns[i].align ? obj.options.columns[i].align : 'center'; + td.style.textAlign = colAlign; + + // Wrap option + if (obj.options.columns[i].wordWrap != false && (obj.options.wordWrap == true || obj.options.columns[i].wordWrap == true || td.innerHTML.length > 200)) { + td.style.whiteSpace = 'pre-wrap'; + } + + // Overflow + if (i > 0) { + if (value || td.innerHTML) { + obj.records[j][i-1].style.overflow = 'hidden'; + } else { + if (i == obj.options.columns.length - 1) { + td.style.overflow = 'hidden'; + } + } + } + + return td; + } + + obj.createCellHeader = function(colNumber) { + // Create col global control + var colWidth = obj.options.columns[colNumber].width ? obj.options.columns[colNumber].width : obj.options.defaultColWidth; + var colAlign = obj.options.columns[colNumber].align ? obj.options.columns[colNumber].align : obj.options.defaultColAlign; + + // Create header cell + obj.headers[colNumber] = document.createElement('td');
+ if (obj.options.stripHTML) { + obj.headers[colNumber].innerText = obj.options.columns[colNumber].title ? obj.options.columns[colNumber].title : jexcel.getColumnName(colNumber);
+ } else {
+ obj.headers[colNumber].innerHTML = obj.options.columns[colNumber].title ? obj.options.columns[colNumber].title : jexcel.getColumnName(colNumber);
+ } + obj.headers[colNumber].setAttribute('data-x', colNumber); + obj.headers[colNumber].style.textAlign = colAlign; + if (obj.options.columns[colNumber].title) { + obj.headers[colNumber].setAttribute('title', obj.options.columns[colNumber].title); + } + + // Width control + obj.colgroup[colNumber] = document.createElement('col'); + obj.colgroup[colNumber].setAttribute('width', colWidth); + + // Hidden column + if (obj.options.columns[colNumber].type == 'hidden') { + obj.headers[colNumber].style.display = 'none'; + obj.colgroup[colNumber].style.display = 'none'; + } + } +
+ /**
+ * Update a nested header title
+ */
+ obj.updateNestedHeader = function(x, y, title) {
+ if (obj.options.nestedHeaders[y][x].title) {
+ obj.options.nestedHeaders[y][x].title = title;
+ obj.options.nestedHeaders[y].element.children[x+1].innerText = title;
+ }
+ }
+
+ /**
+ * Create a nested header object
+ */ + obj.createNestedHeader = function(nestedInformation) { + var tr = document.createElement('tr'); + tr.classList.add('jexcel_nested'); + var td = document.createElement('td'); + tr.appendChild(td);
+ // Element
+ nestedInformation.element = tr; + + var headerIndex = 0; + for (var i = 0; i < nestedInformation.length; i++) { + // Default values + if (! nestedInformation[i].colspan) { + nestedInformation[i].colspan = 1; + } + if (! nestedInformation[i].align) { + nestedInformation[i].align = 'center'; + } + if (! nestedInformation[i].title) { + nestedInformation[i].title = ''; + } + + // Number of columns + var numberOfColumns = nestedInformation[i].colspan; + + // Classes container + var column = []; + // Header classes for this cell + for (var x = 0; x < numberOfColumns; x++) { + if (obj.options.columns[headerIndex] && obj.options.columns[headerIndex].type == 'hidden') { + numberOfColumns++; + } + column.push(headerIndex); + headerIndex++; + } + + // Created the nested cell + var td = document.createElement('td'); + td.setAttribute('data-column', column.join(',')); + td.setAttribute('colspan', nestedInformation[i].colspan); + td.setAttribute('align', nestedInformation[i].align); + td.innerText = nestedInformation[i].title; + tr.appendChild(td); + } + + return tr; + } + + /** + * Create toolbar + */ + obj.createToolbar = function(toolbar) { + if (toolbar) { + obj.options.toolbar = toolbar; + } else { + var toolbar = obj.options.toolbar; + } + + for (var i = 0; i < toolbar.length; i++) { + if (toolbar[i].type == 'i') { + var toolbarItem = document.createElement('i'); + toolbarItem.classList.add('jexcel_toolbar_item'); + toolbarItem.classList.add('material-icons'); + toolbarItem.setAttribute('data-k', toolbar[i].k); + toolbarItem.setAttribute('data-v', toolbar[i].v); + // Tooltip + if (toolbar[i].tooltip) { + toolbarItem.setAttribute('title', toolbar[i].tooltip); + } + // Handle click + if (toolbar[i].onclick && typeof(toolbar[i].onclick)) { + toolbarItem.onclick = (function (a) { + var b = a; + return function () { + toolbar[b].onclick(el, obj, this); + }; + })(i); + } else { + toolbarItem.onclick = function() { + var k = this.getAttribute('data-k'); + var v = this.getAttribute('data-v'); + obj.setStyle(obj.highlighted, k, v); + } + } + // Append element + toolbarItem.innerText = toolbar[i].content; + obj.toolbar.appendChild(toolbarItem); + } else if (toolbar[i].type == 'select') { + var toolbarItem = document.createElement('select'); + toolbarItem.classList.add('jexcel_toolbar_item'); + toolbarItem.setAttribute('data-k', toolbar[i].k); + // Tooltip + if (toolbar[i].tooltip) { + toolbarItem.setAttribute('title', toolbar[i].tooltip); + } + // Handle onchange + if (toolbar[i].onchange && typeof(toolbar[i].onchange)) { + toolbarItem.onchange = toolbar[i].onchange; + } else { + toolbarItem.onchange = function() { + var k = this.getAttribute('data-k'); + obj.setStyle(obj.highlighted, k, this.value); + } + } + // Add options to the dropdown + for(var j = 0; j < toolbar[i].v.length; j++) { + var toolbarDropdownOption = document.createElement('option'); + toolbarDropdownOption.value = toolbar[i].v[j]; + toolbarDropdownOption.innerText = toolbar[i].v[j]; + toolbarItem.appendChild(toolbarDropdownOption); + } + obj.toolbar.appendChild(toolbarItem); + } else if (toolbar[i].type == 'color') { + var toolbarItem = document.createElement('i'); + toolbarItem.classList.add('jexcel_toolbar_item'); + toolbarItem.classList.add('material-icons'); + toolbarItem.setAttribute('data-k', toolbar[i].k); + toolbarItem.setAttribute('data-v', ''); + // Tooltip + if (toolbar[i].tooltip) { + toolbarItem.setAttribute('title', toolbar[i].tooltip); + } + obj.toolbar.appendChild(toolbarItem); + toolbarItem.onclick = function() { + this.color.open(); + } + toolbarItem.innerText = toolbar[i].content; + jSuites.color(toolbarItem, { + onchange:function(o, v) { + var k = o.getAttribute('data-k'); + obj.setStyle(obj.highlighted, k, v); + } + }); + } + } + } + + /** + * Merge cells + * @param cellName + * @param colspan + * @param rowspan + * @param ignoreHistoryAndEvents + */ + obj.setMerge = function(cellName, colspan, rowspan, ignoreHistoryAndEvents) { + var test = false; + + if (! cellName) { + if (! obj.highlighted.length) { + alert(obj.options.text.noCellsSelected); + return null; + } else { + var x1 = parseInt(obj.highlighted[0].getAttribute('data-x')); + var y1 = parseInt(obj.highlighted[0].getAttribute('data-y')); + var x2 = parseInt(obj.highlighted[obj.highlighted.length-1].getAttribute('data-x')); + var y2 = parseInt(obj.highlighted[obj.highlighted.length-1].getAttribute('data-y')); + var cellName = jexcel.getColumnNameFromId([ x1, y1 ]); + var colspan = (x2 - x1) + 1; + var rowspan = (y2 - y1) + 1; + } + } + + var cell = jexcel.getIdFromColumnName(cellName, true); + + if (obj.options.mergeCells[cellName]) { + if (obj.records[cell[1]][cell[0]].getAttribute('data-merged')) { + test = obj.options.text.cellAlreadyMerged; + } + } else if ((! colspan || colspan < 2) && (! rowspan || rowspan < 2)) { + test = obj.options.text.invalidMergeProperties; + } else { + var cells = []; + for (var j = cell[1]; j < cell[1] + rowspan; j++) { + for (var i = cell[0]; i < cell[0] + colspan; i++) { + var columnName = jexcel.getColumnNameFromId([i, j]); + if (obj.records[j][i].getAttribute('data-merged')) { + test = obj.options.text.thereIsAConflictWithAnotherMergedCell; + } + } + } + } + + if (test) { + alert(test); + } else { + // Add property + if (colspan > 1) { + obj.records[cell[1]][cell[0]].setAttribute('colspan', colspan); + } else { + colspan = 1; + } + if (rowspan > 1) { + obj.records[cell[1]][cell[0]].setAttribute('rowspan', rowspan); + } else { + rowspan = 1; + } + // Keep links to the existing nodes + obj.options.mergeCells[cellName] = [ colspan, rowspan, [] ]; + // Mark cell as merged + obj.records[cell[1]][cell[0]].setAttribute('data-merged', 'true'); + // Overflow + obj.records[cell[1]][cell[0]].style.overflow = 'hidden'; + // History data + var data = []; + // Adjust the nodes + for (var y = cell[1]; y < cell[1] + rowspan; y++) { + for (var x = cell[0]; x < cell[0] + colspan; x++) { + if (! (cell[0] == x && cell[1] == y)) { + data.push(obj.options.data[y][x]); + obj.updateCell(x, y, '', true); + obj.options.mergeCells[cellName][2].push(obj.records[y][x]); + obj.records[y][x].style.display = 'none'; + obj.records[y][x] = obj.records[cell[1]][cell[0]]; + } + } + } + // In the initialization is not necessary keep the history + obj.updateSelection(obj.records[cell[1]][cell[0]]); + + if (! ignoreHistoryAndEvents) { + obj.setHistory({ + action:'setMerge', + column:cellName, + colspan:colspan, + rowspan:rowspan, + data:data, + }); +
+ obj.dispatch('onmerge', el, cellName, colspan, rowspan); + } + } + } + + /** + * Merge cells + * @param cellName + * @param colspan + * @param rowspan + * @param ignoreHistoryAndEvents + */ + obj.getMerge = function(cellName) { + var data = {}; + if (cellName) { + if (obj.options.mergeCells[cellName]) { + data = [ obj.options.mergeCells[cellName][0], obj.options.mergeCells[cellName][1] ]; + } else { + data = null; + } + } else { + if (obj.options.mergeCells) { + var mergedCells = obj.options.mergeCells; + var keys = Object.keys(obj.options.mergeCells); + for (var i = 0; i < keys.length; i++) { + data[keys[i]] = [ obj.options.mergeCells[keys[i]][0], obj.options.mergeCells[keys[i]][1] ]; + } + } + } + + return data; + } + + /** + * Remove merge by cellname + * @param cellName + */ + obj.removeMerge = function(cellName, data, keepOptions) { + if (obj.options.mergeCells[cellName]) { + var cell = jexcel.getIdFromColumnName(cellName, true); + obj.records[cell[1]][cell[0]].removeAttribute('colspan'); + obj.records[cell[1]][cell[0]].removeAttribute('rowspan'); + obj.records[cell[1]][cell[0]].removeAttribute('data-merged'); + var info = obj.options.mergeCells[cellName]; + + var index = 0; + for (var j = 0; j < info[1]; j++) { + for (var i = 0; i < info[0]; i++) { + if (j > 0 || i > 0) { + obj.records[cell[1]+j][cell[0]+i] = info[2][index]; + obj.records[cell[1]+j][cell[0]+i].style.display = ''; + // Recover data + if (data && data[index]) { + obj.updateCell(cell[0]+i, cell[1]+j, data[index]); + } + index++; + } + } + } + + // Update selection + obj.updateSelection(obj.records[cell[1]][cell[0]], obj.records[cell[1]+j-1][cell[0]+i-1]); + + if (! keepOptions) { + delete(obj.options.mergeCells[cellName]); + } + } + } + + /** + * Remove all merged cells + */ + obj.destroyMerged = function(keepOptions) { + // Remove any merged cells + if (obj.options.mergeCells) { + var mergedCells = obj.options.mergeCells; + var keys = Object.keys(obj.options.mergeCells); + for (var i = 0; i < keys.length; i++) { + obj.removeMerge(keys[i], null, keepOptions); + } + } + } + + /** + * Is column merged + */ + obj.isColMerged = function(x, insertBefore) { + var cols = []; + // Remove any merged cells + if (obj.options.mergeCells) { + var keys = Object.keys(obj.options.mergeCells); + for (var i = 0; i < keys.length; i++) { + var info = jexcel.getIdFromColumnName(keys[i], true); + var colspan = obj.options.mergeCells[keys[i]][0]; + var x1 = info[0]; + var x2 = info[0] + (colspan > 1 ? colspan - 1 : 0); + + if (insertBefore == null) { + if ((x1 <= x && x2 >= x)) { + cols.push(keys[i]); + } + } else { + if (insertBefore) { + if ((x1 < x && x2 >= x)) { + cols.push(keys[i]); + } + } else { + if ((x1 <= x && x2 > x)) { + cols.push(keys[i]); + } + } + } + } + } + + return cols; + } + + /** + * Is rows merged + */ + obj.isRowMerged = function(y, insertBefore) { + var rows = []; + // Remove any merged cells + if (obj.options.mergeCells) { + var keys = Object.keys(obj.options.mergeCells); + for (var i = 0; i < keys.length; i++) { + var info = jexcel.getIdFromColumnName(keys[i], true); + var rowspan = obj.options.mergeCells[keys[i]][1]; + var y1 = info[1]; + var y2 = info[1] + (rowspan > 1 ? rowspan - 1 : 0); + + if (insertBefore == null) { + if ((y1 <= y && y2 >= y)) { + rows.push(keys[i]); + } + } else { + if (insertBefore) { + if ((y1 < y && y2 >= y)) { + rows.push(keys[i]); + } + } else { + if ((y1 <= y && y2 > y)) { + rows.push(keys[i]); + } + } + } + } + } + + return rows; + } +
+ /**
+ * Open the column filter
+ */
+ obj.openFilter = function(columnId) {
+ if (! obj.options.filters) {
+ console.log('JEXCEL: filters not enabled.');
+ } else {
+ // Make sure is integer
+ columnId = parseInt(columnId);
+ // Reset selection
+ obj.resetSelection();
+ // Load options
+ var options = [];
+ for (var j = 0; j < obj.options.data.length; j++) {
+ var k = obj.options.data[j][columnId];
+ var v = obj.records[j][columnId].innerHTML;
+ if (k && v) {
+ options[k] = v;
+ }
+ }
+ var keys = Object.keys(options);
+ var optionsFiltered = [];
+ optionsFiltered.push({ id: '', name: 'Blanks' });
+ for (var j = 0; j < keys.length; j++) {
+ optionsFiltered.push({ id: keys[j], name: options[keys[j]] });
+ }
+
+ // Create dropdown
+ var div = document.createElement('div');
+ obj.filter.children[columnId + 1].innerHTML = '';
+ obj.filter.children[columnId + 1].appendChild(div);
+ obj.filter.children[columnId + 1].style.paddingLeft = '0px';
+ obj.filter.children[columnId + 1].style.paddingRight = '0px';
+ obj.filter.children[columnId + 1].style.overflow = 'initial';
+
+ var opt = {
+ data: optionsFiltered,
+ multiple: true,
+ autocomplete: true,
+ opened: true,
+ value: obj.filters[columnId] !== undefined ? obj.filters[columnId] : null,
+ width:'100%',
+ position: (obj.options.tableOverflow == true || obj.options.fullscreen == true) ? true : false,
+ onclose: function(o) {
+ obj.resetFilters();
+ obj.filters[columnId] = o.dropdown.getValue(true);
+ obj.filter.children[columnId + 1].innerHTML = o.dropdown.getText();
+ obj.filter.children[columnId + 1].style.paddingLeft = '';
+ obj.filter.children[columnId + 1].style.paddingRight = '';
+ obj.filter.children[columnId + 1].style.overflow = '';
+ obj.closeFilter(columnId);
+ }
+ };
+
+ // Dynamic dropdown
+ jSuites.dropdown(div, opt);
+ }
+ }
+
+ obj.resetFilters = function() {
+ if (obj.options.filters) {
+ for (var i = 0; i < obj.filter.children.length; i++) {
+ obj.filter.children[i].innerHTML = ' ';
+ obj.filters[i] = null;
+ }
+ }
+ }
+
+ obj.closeFilter = function(columnId) {
+ if (! columnId) {
+ for (var i = 0; i < obj.filter.children.length; i++) {
+ if (obj.filters[i]) {
+ columnId = i;
+ }
+ }
+ }
+
+ // Search filter
+ var search = function(query, x, y) {
+ for (var i = 0; i < query.length; i++) {
+ if (query[i] == '') {
+ if (obj.options.data[y][x] == '') {
+ return true;
+ }
+ } else {
+ if ((''+obj.options.data[y][x]).search(query[i]) >= 0 ||
+ (''+obj.records[y][x].innerHTML).search(query[i]) >= 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ var query = obj.filters[columnId];
+ obj.results = [];
+ for (var j = 0; j < obj.options.data.length; j++) {
+ if (search(query, columnId, j)) {
+ obj.results.push(j);
+ }
+ }
+ if (! obj.results.length) {
+ obj.results = null;
+ }
+
+ obj.updateResult();
+ }
+ + /** + * Open the editor + * + * @param object cell + * @return void + */ + obj.openEditor = function(cell, empty, e) { + // Get cell position + var y = cell.getAttribute('data-y'); + var x = cell.getAttribute('data-x'); + + // On edition start + obj.dispatch('oneditionstart', el, cell, x, y); + + // Overflow + if (x > 0) { + obj.records[y][x-1].style.overflow = 'hidden'; + } + + // Create editor + var createEditor = function(type) { + // Cell information + var info = cell.getBoundingClientRect(); + + // Create dropdown + var editor = document.createElement(type); + editor.style.width = (info.width) + 'px'; + editor.style.height = (info.height - 2) + 'px'; + editor.style.minHeight = (info.height - 2) + 'px'; + + // Edit cell + cell.classList.add('editor'); + cell.innerHTML = ''; + cell.appendChild(editor); +
+ // On edition start
+ obj.dispatch('oncreateeditor', el, cell, x, y, editor);
+ + return editor; + } + + // Readonly + if (cell.classList.contains('readonly') == true) { + // Do nothing + } else { + // Holder + obj.edition = [ obj.records[y][x], obj.records[y][x].innerHTML, x, y ]; + + // If there is a custom editor for it + if (obj.options.columns[x].editor) { + // Custom editors + obj.options.columns[x].editor.openEditor(cell, el, empty, e); + } else { + // Native functions + if (obj.options.columns[x].type == 'hidden') { + // Do nothing + } else if (obj.options.columns[x].type == 'checkbox' || obj.options.columns[x].type == 'radio') { + // Get value + var value = cell.children[0].checked ? false : true; + // Toogle value + obj.setValue(cell, value); + // Do not keep edition open + obj.edition = null; + } else if (obj.options.columns[x].type == 'dropdown' || obj.options.columns[x].type == 'autocomplete') { + // Get current value + var value = obj.options.data[y][x]; + if (obj.options.columns[x].multiple && !Array.isArray(value)) {
+ value = value.split(';');
+ }
+ + // Create dropdown + if (typeof(obj.options.columns[x].filter) == 'function') { + var source = obj.options.columns[x].filter(el, cell, x, y, obj.options.columns[x].source); + } else { + var source = obj.options.columns[x].source; + } +
+ // Do not change the original source
+ var data = [];
+ for (var j = 0; j < source.length; j++) {
+ data.push(source[j]);
+ }
+ + // Create editor + var editor = createEditor('div'); + var options = { + data: data, + multiple: obj.options.columns[x].multiple ? true : false, + autocomplete: obj.options.columns[x].autocomplete || obj.options.columns[x].type == 'autocomplete' ? true : false, + opened:true, + value: value,
+ width:'100%', + height:editor.style.minHeight, + position: (obj.options.tableOverflow == true || obj.options.fullscreen == true) ? true : false, + onclose:function() { + obj.closeEditor(cell, true); + } + }; + if (obj.options.columns[x].options && obj.options.columns[x].options.type) { + options.type = obj.options.columns[x].options.type; + } + jSuites.dropdown(editor, options); + } else if (obj.options.columns[x].type == 'calendar' || obj.options.columns[x].type == 'color') { + // Value + var value = obj.options.data[y][x]; + // Create editor + var editor = createEditor('input'); + editor.value = value; + + if (obj.options.tableOverflow == true || obj.options.fullscreen == true) { + obj.options.columns[x].options.position = true; + } + obj.options.columns[x].options.value = obj.options.data[y][x]; + obj.options.columns[x].options.opened = true; + obj.options.columns[x].options.onclose = function(el, value) { + obj.closeEditor(cell, true); + } + // Current value + if (obj.options.columns[x].type == 'color') { + jSuites.color(editor, obj.options.columns[x].options); + } else { + jSuites.calendar(editor, obj.options.columns[x].options); + } + // Focus on editor + editor.focus();
+ } else if (obj.options.columns[x].type == 'html') {
+ var value = obj.options.data[y][x];
+ // Create editor
+ var editor = createEditor('div');
+ editor.style.position = 'relative';
+ var div = document.createElement('div');
+ div.classList.add('jexcel_richtext');
+ editor.appendChild(div);
+ jSuites.editor(div, {
+ focus: true,
+ value: value,
+ });
+ const rect = cell.getBoundingClientRect();
+ const rectContent = div.getBoundingClientRect();
+ if (window.innerHeight < rect.bottom + rectContent.height) {
+ div.style.top = (rect.top - (rectContent.height + 2)) + 'px';
+ } else {
+ div.style.top = (rect.top) + 'px';
+ } + } else if (obj.options.columns[x].type == 'image') { + // Value + var img = cell.children[0]; + // Create editor + var editor = createEditor('div'); + editor.style.position = 'relative'; + var div = document.createElement('div'); + div.classList.add('jclose'); + if (img && img.src) { + div.appendChild(img); + } + editor.appendChild(div); + jSuites.image(div, obj.options.imageOptions); + const rect = cell.getBoundingClientRect(); + const rectContent = div.getBoundingClientRect(); + if (window.innerHeight < rect.bottom + rectContent.height) { + div.style.top = (rect.top - (rectContent.height + 2)) + 'px'; + } else { + div.style.top = (rect.top) + 'px'; + } + } else { + // Value + var value = empty == true ? '' : obj.options.data[y][x]; + + // Basic editor + if (obj.options.columns[x].wordWrap != false && (obj.options.wordWrap == true || obj.options.columns[x].wordWrap == true)) { + var editor = createEditor('textarea'); + } else { + var editor = createEditor('input');
+ // Mask + if (obj.options.columns[x].mask) { + editor.setAttribute('data-mask', obj.options.columns[x].mask); + } + } + + editor.onblur = function() { + obj.closeEditor(cell, true); + }; + editor.focus();
+ editor.value = value; + } + } + } + } + + /** + * Close the editor and save the information + * + * @param object cell + * @param boolean save + * @return void + */ + obj.closeEditor = function(cell, save) { + var x = parseInt(cell.getAttribute('data-x')); + var y = parseInt(cell.getAttribute('data-y')); + + // Get cell properties + if (save == true) { + // If custom editor + if (obj.options.columns[x].editor) { + // Custom editor + var value = obj.options.columns[x].editor.closeEditor(cell, save); + } else { + // Native functions + if (obj.options.columns[x].type == 'checkbox' || obj.options.columns[x].type == 'radio' || obj.options.columns[x].type == 'hidden') { + // Do nothing + } else if (obj.options.columns[x].type == 'dropdown' || obj.options.columns[x].type == 'autocomplete') { + var value = cell.children[0].dropdown.close(true);
+ } else if (obj.options.columns[x].type == 'calendar') {
+ var value = cell.children[0].calendar.close(true);
+ } else if (obj.options.columns[x].type == 'color') { + var value = cell.children[0].color.close(true);
+ } else if (obj.options.columns[x].type == 'html') {
+ var value = cell.children[0].children[0].editor.getData(); + } else if (obj.options.columns[x].type == 'image') { + var img = cell.children[0].children[0].children[0]; + var value = img && img.tagName == 'IMG' ? img.src : ''; + } else if (obj.options.columns[x].type == 'numeric') { + var value = cell.children[0].value; + if (value.substr(0,1) != '=') { + if (value == '') { + value = obj.options.columns[x].allowEmpty ? '' : 0; + } + } + cell.children[0].onblur = null; + } else { + var value = cell.children[0].value; + cell.children[0].onblur = null; + } + } + + // Ignore changes if the value is the same + if (obj.options.data[y][x] == value) { + cell.innerHTML = obj.edition[1]; + } else { + obj.setValue(cell, value); + } + } else { + if (obj.options.columns[x].editor) { + // Custom editor + obj.options.columns[x].editor.closeEditor(cell, save); + } else { + if (obj.options.columns[x].type == 'dropdown' || obj.options.columns[x].type == 'autocomplete') { + cell.children[0].dropdown.close(true); + } else if (obj.options.columns[x].type == 'calendar') { + cell.children[0].calendar.close(true); + } else if (obj.options.columns[x].type == 'color') { + cell.children[0].color.close(true); + } else { + cell.children[0].onblur = null; + } + } + + // Restore value + cell.innerHTML = obj.edition && obj.edition[1] ? obj.edition[1] : ''; + } + + // On edition end
+ obj.dispatch('oneditionend', el, cell, x, y, value, save); + + // Remove editor class + cell.classList.remove('editor'); + + // Finish edition + obj.edition = null; + } + + /** + * Get the cell object + * + * @param object cell + * @return string value + */ + obj.getCell = function(cell) { + // Convert in case name is excel liked ex. A10, BB92 + cell = jexcel.getIdFromColumnName(cell, true); + var x = cell[0]; + var y = cell[1]; + + return obj.records[y][x]; + } + + /** + * Get the cell object from coords + * + * @param object cell + * @return string value + */ + obj.getCellFromCoords = function(x, y) { + return obj.records[y][x]; + } + + /** + * Get label + * + * @param object cell + * @return string value + */ + obj.getLabel = function(cell) { + // Convert in case name is excel liked ex. A10, BB92 + cell = jexcel.getIdFromColumnName(cell, true); + var x = cell[0]; + var y = cell[1]; + + return obj.records[y][x].innerHTML; + } + + /** + * Get labelfrom coords + * + * @param object cell + * @return string value + */ + obj.getLabelFromCoords = function(x, y) { + return obj.records[y][x].innerHTML; + } + + /** + * Get the value from a cell + * + * @param object cell + * @return string value + */ + obj.getValue = function(cell, processedValue) { + if (typeof(cell) == 'object') { + var x = cell.getAttribute('data-x'); + var y = cell.getAttribute('data-y'); + } else { + cell = jexcel.getIdFromColumnName(cell, true); + var x = cell[0]; + var y = cell[1]; + } + + var value = null; + + if (x != null && y != null) { + if (obj.records[y] && obj.records[y][x] && (processedValue || obj.options.copyCompatibility == true)) { + value = obj.records[y][x].innerHTML; + } else { + if (obj.options.data[y] && obj.options.data[y][x] != 'undefined') { + value = obj.options.data[y][x]; + } + } + } + + return value; + } + + /** + * Get the value from a coords + * + * @param int x + * @param int y + * @return string value + */ + obj.getValueFromCoords = function(x, y, processedValue) { + var value = null; + + if (x != null && y != null) { + if ((obj.records[y] && obj.records[y][x]) && processedValue || obj.options.copyCompatibility == true) { + value = obj.records[y][x].innerHTML; + } else { + if (obj.options.data[y] && obj.options.data[y][x] != 'undefined') { + value = obj.options.data[y][x]; + } + } + } + + return value; + } + + /** + * Set a cell value + * + * @param mixed cell destination cell + * @param string value value + * @return void + */ + obj.setValue = function(cell, value, force) { + var records = []; + + if (typeof(cell) == 'string') { + var columnId = jexcel.getIdFromColumnName(cell, true); + var x = columnId[0]; + var y = columnId[1]; + + // Update cell + records.push(obj.updateCell(x, y, value, force)); + + // Update all formulas in the chain + obj.updateFormulaChain(x, y, records); + } else { + var x = null; + var y = null; + if (cell && cell.getAttribute) { + var x = cell.getAttribute('data-x'); + var y = cell.getAttribute('data-y'); + } + + // Update cell + if (x != null && y != null) { + records.push(obj.updateCell(x, y, value, force)); + + // Update all formulas in the chain + obj.updateFormulaChain(x, y, records); + } else { + var keys = Object.keys(cell); + if (keys.length > 0) { + for (var i = 0; i < keys.length; i++) { + if (typeof(cell[i]) == 'string') { + var columnId = jexcel.getIdFromColumnName(cell[i], true); + var x = columnId[0]; + var y = columnId[1]; + } else {
+ if (cell[i].x != null && cell[i].y != null) {
+ var x = cell[i].x;
+ var y = cell[i].y;
+ // Flexible setup
+ if (cell[i].newValue != null) {
+ value = cell[i].newValue;
+ } else if (cell[i].value != null) {
+ value = cell[i].value;
+ }
+ } else { + var x = cell[i].getAttribute('data-x'); + var y = cell[i].getAttribute('data-y');
+ } + } + + // Update cell + if (x != null && y != null) { + records.push(obj.updateCell(x, y, value, force)); + + // Update all formulas in the chain + obj.updateFormulaChain(x, y, records); + } + } + } + } + } + + // Update history + obj.setHistory({ + action:'setValue', + records:records, + selection:obj.selectedCell, + }); + + // Update table with custom configurations if applicable + obj.updateTable(); + + // On after changes + obj.onafterchanges(el, records); + } + + /** + * Set a cell value based on coordinates + * + * @param int x destination cell + * @param int y destination cell + * @param string value + * @return void + */ + obj.setValueFromCoords = function(x, y, value, force) { + var records = []; + records.push(obj.updateCell(x, y, value, force)); + + // Update all formulas in the chain + obj.updateFormulaChain(x, y, records); + + // Update history + obj.setHistory({ + action:'setValue', + records:records, + selection:obj.selectedCell, + }); + + // Update table with custom configurations if applicable + obj.updateTable(); + + // On after changes + obj.onafterchanges(el, records); + } + + /** + * Toogle + */ + obj.setCheckRadioValue = function() { + var records = []; + var keys = Object.keys(obj.highlighted); + for (var i = 0; i < keys.length; i++) { + var x = obj.highlighted[i].getAttribute('data-x'); + var y = obj.highlighted[i].getAttribute('data-y'); + + if (obj.options.columns[x].type == 'checkbox' || obj.options.columns[x].type == 'radio') { + // Update cell + records.push(obj.updateCell(x, y, ! obj.options.data[y][x])); + } + } + + if (records.length) { + // Update history + obj.setHistory({ + action:'setValue', + records:records, + selection:obj.selectedCell, + }); + + // On after changes + obj.onafterchanges(el, records); + } + } +
+ /**
+ * Strip tags
+ */
+ var stripScript = function(a) {
+ var b = new Option;
+ b.innerHTML = a;
+ var c = null;
+ for (a = b.getElementsByTagName('script'); c=a[0];) c.parentNode.removeChild(c);
+ return b.innerHTML;
+ }
+ + /** + * Update cell content + * + * @param object cell + * @return void + */ + obj.updateCell = function(x, y, value, force) { + // Changing value depending on the column type + if (obj.records[y][x].classList.contains('readonly') == true && ! force) { + // Do nothing
+ var record = {
+ x: x,
+ y: y,
+ col: x,
+ row: y
+ } + } else {
+ // Security
+ if ((''+value).substr(0,1) == '=' && obj.options.secureFormulas == true) {
+ var val = secureFormula(value);
+ if (val != value) {
+ // Update the data container
+ value = val;
+ }
+ }
+ + // On change
+ var val = obj.dispatch('onbeforechange', el, obj.records[y][x], x, y, value);
+
+ // If you return something this will overwrite the value
+ if (val != undefined) {
+ value = val;
+ }
+
+ if (obj.options.columns[x].editor && typeof(obj.options.columns[x].editor.updateCell) == 'function') {
+ value = obj.options.columns[x].editor.updateCell(obj.records[y][x], value, force);
+ }
+ + // History format + var record = {
+ x: x,
+ y: y, + col: x, + row: y, + newValue: value, + oldValue: obj.options.data[y][x], + } + + if (obj.options.columns[x].editor) { + // Update data and cell + obj.options.data[y][x] = value; + } else { + // Native functions + if (obj.options.columns[x].type == 'checkbox' || obj.options.columns[x].type == 'radio') { + // Unchecked all options + if (obj.options.columns[x].type == 'radio') { + for (var j = 0; j < obj.options.data.length; j++) { + obj.options.data[j][x] = false; + } + } + + // Update data and cell + obj.records[y][x].children[0].checked = (value == 1 || value == true || value == 'true' || value == 'TRUE') ? true : false; + obj.options.data[y][x] = obj.records[y][x].children[0].checked; + } else if (obj.options.columns[x].type == 'dropdown' || obj.options.columns[x].type == 'autocomplete') { + // Update data and cell + obj.options.data[y][x] = value; + obj.records[y][x].innerText = obj.getDropDownValue(x, value); + } else if (obj.options.columns[x].type == 'calendar') { + // Update calendar + var formatted = jSuites.calendar.extractDateFromString(value, obj.options.columns[x].options.format); + // Update data and cell + obj.options.data[y][x] = value; + obj.records[y][x].innerText = jSuites.calendar.getDateString(formatted ? formatted : value, obj.options.columns[x].options.format); + } else if (obj.options.columns[x].type == 'color') { + // Update color + obj.options.data[y][x] = value; + // Render + if (obj.options.columns[x].render == 'square') { + var color = document.createElement('div'); + color.className = 'color'; + color.style.backgroundColor = value; + obj.records[y][x].innerText = ''; + obj.records[y][x].appendChild(color); + } else { + obj.records[y][x].style.color = value;
+ obj.records[y][x].innerText = value; + }
+ } else if (obj.options.columns[x].type == 'image') { + value = ''+value; + obj.options.data[y][x] = value; + obj.records[y][x].innerHTML = ''; + if (value && value.substr(0, 10) == 'data:image') { + var img = document.createElement('img'); + img.src = value; + obj.records[y][x].appendChild(img); + } + } else { + // Update data and cell + obj.options.data[y][x] = value;
+ // Label
+ if (obj.options.columns[x].type == 'html') {
+ obj.records[y][x].innerHTML = stripScript(obj.parseValue(x, y, value));
+ } else {
+ if (obj.options.stripHTML === false || obj.options.columns[x].stripHTML === false) { + obj.records[y][x].innerHTML = stripScript(obj.parseValue(x, y, value));
+ } else {
+ obj.records[y][x].innerText = obj.parseValue(x, y, value);
+ }
+ } + // Handle big text inside a cell + if (obj.options.columns[x].wordWrap != false && (obj.options.wordWrap == true || obj.options.columns[x].wordWrap == true || obj.records[y][x].innerHTML.length > 200)) { + obj.records[y][x].style.whiteSpace = 'pre-wrap'; + } else { + obj.records[y][x].style.whiteSpace = ''; + } + } + } + + // Overflow + if (x > 0) { + if (value) { + obj.records[y][x-1].style.overflow = 'hidden'; + } else { + obj.records[y][x-1].style.overflow = ''; + } + } + + // On change
+ obj.dispatch('onchange', el, (obj.records[y] && obj.records[y][x] ? obj.records[y][x] : null), x, y, value, record.oldValue); + } + + return record; + } + + /** + * Helper function to copy data using the corner icon + */ + obj.copyData = function(o, d) { + // Get data from all selected cells + var data = obj.getData(true, true); + + // Selected cells + var h = obj.selectedContainer; + + // Cells + var x1 = parseInt(o.getAttribute('data-x')); + var y1 = parseInt(o.getAttribute('data-y')); + var x2 = parseInt(d.getAttribute('data-x')); + var y2 = parseInt(d.getAttribute('data-y')); + + // Records + var records = []; + var breakControl = false; +
+ if (h[0] == x1) {
+ // Vertical copy
+ if (y1 < h[1]) {
+ var rowNumber = y1 - h[1];
+ } else {
+ var rowNumber = 1;
+ }
+ var colNumber = 0;
+ } else {
+ if (x1 < h[0]) {
+ var colNumber = x1 - h[0];
+ } else {
+ var colNumber = 1;
+ }
+ var rowNumber = 0;
+ }
+ + // Copy data procedure + var posx = 0; + var posy = 0; +
+ for (var j = y1; j <= y2; j++) {
+ // Skip hidden rows + if (obj.rows[j] && obj.rows[j].style.display == 'none') { + continue; + } + + // Controls + if (data[posy] == undefined) { + posy = 0; + } + posx = 0;
+ + // Data columns
+ if (h[0] != x1) {
+ if (x1 < h[0]) {
+ var colNumber = x1 - h[0];
+ } else {
+ var colNumber = 1;
+ }
+ } + // Data columns + for (var i = x1; i <= x2; i++) {
+ // Update non-readonly + if (obj.records[j][i] && ! obj.records[j][i].classList.contains('readonly') && obj.records[j][i].style.display != 'none' && breakControl == false) { + // Stop if contains value + if (! obj.selection.length) { + if (obj.options.data[j][i] != '') { + breakControl = true; + continue; + } + } + + // Column + if (data[posy] == undefined) { + posx = 0; + } else if (data[posy][posx] == undefined) { + posx = 0; + }
+
+ // Value + var value = data[posy][posx]; + + if (value && ! data[1] && obj.options.autoIncrement == true) { + if (obj.options.columns[i].type == 'text' || obj.options.columns[i].type == 'number') {
+ if ((''+value).substr(0,1) == '=') {
+ var tokens = value.match(/([A-Z]+[0-9]+)/g); + + if (tokens) { + var affectedTokens = []; + for (var index = 0; index < tokens.length; index++) { + var position = jexcel.getIdFromColumnName(tokens[index], 1); + position[0] += colNumber;
+ position[1] += rowNumber;
+ if (position[1] < 0) {
+ position[1] = 0;
+ } + var token = jexcel.getColumnNameFromId([position[0], position[1]]); + + if (token != tokens[index]) { + affectedTokens[tokens[index]] = token; + } + } + // Update formula + if (affectedTokens) { + value = obj.updateFormula(value, affectedTokens) + } + } + } else { + if (value == Number(value)) { + value = Number(value) + rowNumber; + } + } + } else if (obj.options.columns[i].type == 'calendar') { + var date = new Date(value); + date.setDate(date.getDate() + rowNumber); + value = date.getFullYear() + '-' + jexcel.doubleDigitFormat(parseInt(date.getMonth() + 1)) + '-' + jexcel.doubleDigitFormat(date.getDate()) + ' ' + '00:00:00'; + } + } + + records.push(obj.updateCell(i, j, value)); + + // Update all formulas in the chain + obj.updateFormulaChain(i, j, records); + } + posx++;
+ if (h[0] != x1) {
+ colNumber++;
+ } + } + posy++; + rowNumber++; + } + + // Update history + obj.setHistory({ + action:'setValue', + records:records, + selection:obj.selectedCell, + }); + + // Update table with custom configuration if applicable + obj.updateTable(); + + // On after changes + obj.onafterchanges(el, records); + } + + /** + * Refresh current selection + */ + obj.refreshSelection = function() { + if (obj.selectedCell) { + obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); + } + } + + /** + * Move coords to A1 in case ovelaps with an excluded cell + */ + obj.conditionalSelectionUpdate = function(type, o, d) { + if (type == 1) { + if (obj.selectedCell && ((o >= obj.selectedCell[1] && o <= obj.selectedCell[3]) || (d >= obj.selectedCell[1] && d <= obj.selectedCell[3]))) { + obj.resetSelection(); + return; + } + } else { + if (obj.selectedCell && ((o >= obj.selectedCell[0] && o <= obj.selectedCell[2]) || (d >= obj.selectedCell[0] && d <= obj.selectedCell[2]))) { + obj.resetSelection(); + return; + } + } + } + + /** + * Clear table selection + */ + obj.resetSelection = function(blur) { + // Remove style + if (! obj.highlighted.length) { + var previousStatus = 0; + } else { + var previousStatus = 1; + + for (var i = 0; i < obj.highlighted.length; i++) { + obj.highlighted[i].classList.remove('highlight'); + obj.highlighted[i].classList.remove('highlight-left'); + obj.highlighted[i].classList.remove('highlight-right'); + obj.highlighted[i].classList.remove('highlight-top'); + obj.highlighted[i].classList.remove('highlight-bottom'); + obj.highlighted[i].classList.remove('highlight-selected'); + + var px = parseInt(obj.highlighted[i].getAttribute('data-x')); + var py = parseInt(obj.highlighted[i].getAttribute('data-y')); + + // Check for merged cells + if (obj.highlighted[i].getAttribute('data-merged')) { + var colspan = parseInt(obj.highlighted[i].getAttribute('colspan')); + var rowspan = parseInt(obj.highlighted[i].getAttribute('rowspan')); + var ux = colspan > 0 ? px + (colspan - 1) : px; + var uy = rowspan > 0 ? py + (rowspan - 1): py; + } else { + var ux = px; + var uy = py; + } + + // Remove selected from headers + for (var j = px; j <= ux; j++) { + if (obj.headers[j]) { + obj.headers[j].classList.remove('selected'); + } + } + + // Remove selected from rows + for (var j = py; j <= uy; j++) { + if (obj.rows[j]) { + obj.rows[j].classList.remove('selected'); + } + } + } + } + + // Reset highlighed cells + obj.highlighted = []; + + // Reset + obj.selectedCell = null; + + // Hide corner + obj.corner.style.top = '-2000px'; + obj.corner.style.left = '-2000px'; + + if (blur == true && previousStatus == 1) {
+ obj.dispatch('onblur', el);
+ } + + return previousStatus; + } + + /** + * Update selection based on two cells + */ + obj.updateSelection = function(el1, el2, origin) { + var x1 = el1.getAttribute('data-x'); + var y1 = el1.getAttribute('data-y'); + if (el2) { + var x2 = el2.getAttribute('data-x'); + var y2 = el2.getAttribute('data-y'); + } else { + var x2 = x1; + var y2 = y1; + } + + obj.updateSelectionFromCoords(x1, y1, x2, y2, origin); + } + + /** + * Update selection from coords + */ + obj.updateSelectionFromCoords = function(x1, y1, x2, y2, origin) { + // Reset Selection + var updated = null; + var previousState = obj.resetSelection(); + + // Same element + if (x2 == null) { + x2 = x1; + } + if (y2 == null) { + y2 = y1; + } + + // Selection must be within the existing data + if (x1 >= obj.headers.length) { + x1 = obj.headers.length - 1; + } + if (y1 >= obj.rows.length) { + y1 = obj.rows.length - 1; + } + if (x2 >= obj.headers.length) { + x2 = obj.headers.length - 1; + } + if (y2 >= obj.rows.length) { + y2 = obj.rows.length - 1; + } + + // Keep selected cell + obj.selectedCell = [x1, y1, x2, y2]; + + // Select cells + if (x1 != null) { + // Add selected cell + if (obj.records[y1][x1]) { + obj.records[y1][x1].classList.add('highlight-selected'); + } + + // Origin & Destination + if (parseInt(x1) < parseInt(x2)) { + var px = parseInt(x1); + var ux = parseInt(x2); + } else { + var px = parseInt(x2); + var ux = parseInt(x1); + } + + if (parseInt(y1) < parseInt(y2)) { + var py = parseInt(y1); + var uy = parseInt(y2); + } else { + var py = parseInt(y2); + var uy = parseInt(y1); + } + + // Verify merged columns + for (var i = px; i <= ux; i++) { + for (var j = py; j <= uy; j++) { + if (obj.records[j][i] && obj.records[j][i].getAttribute('data-merged')) { + var x = parseInt(obj.records[j][i].getAttribute('data-x')); + var y = parseInt(obj.records[j][i].getAttribute('data-y')); + var colspan = parseInt(obj.records[j][i].getAttribute('colspan')); + var rowspan = parseInt(obj.records[j][i].getAttribute('rowspan')); + + if (colspan > 1) { + if (x < px) { + px = x; + } + if (x + colspan > ux) { + ux = x + colspan - 1; + } + } + + if (rowspan) { + if (y < py) { + py = y; + + } + if (y + rowspan > uy) { + uy = y + rowspan - 1; + } + } + } + } + } + + // Limits + var borderLeft = null; + var borderRight = null; + var borderTop = null; + var borderBottom = null; + + // Vertical limits + for (var j = py; j <= uy; j++) { + if (obj.rows[j].style.display != 'none') { + if (borderTop == null) { + borderTop = j; + } + borderBottom = j; + } + } + + // Redefining styles + for (var i = px; i <= ux; i++) { + for (var j = py; j <= uy; j++) { + if (obj.rows[j].style.display != 'none' && obj.records[j][i].style.display != 'none') { + obj.records[j][i].classList.add('highlight'); + obj.highlighted.push(obj.records[j][i]); + } + } + + // Horizontal limits + if (obj.options.columns[i].type != 'hidden') { + if (borderLeft == null) { + borderLeft = i; + } + borderRight = i; + } + } + + // Create borders + if (! borderLeft) { + borderLeft = 0; + } + if (! borderRight) { + borderRight = 0; + } + for (var i = borderLeft; i <= borderRight; i++) { + if (obj.options.columns[i].type != 'hidden') { + // Top border + if (obj.records[borderTop][i]) { + obj.records[borderTop][i].classList.add('highlight-top'); + } + // Bottom border + if (obj.records[borderBottom][i]) { + obj.records[borderBottom][i].classList.add('highlight-bottom'); + } + // Add selected from headers + obj.headers[i].classList.add('selected'); + } + } + + for (var j = borderTop; j <= borderBottom; j++) { + if (obj.rows[j].style.display != 'none') { + // Left border + obj.records[j][borderLeft].classList.add('highlight-left'); + // Right border + obj.records[j][borderRight].classList.add('highlight-right'); + // Add selected from rows + obj.rows[j].classList.add('selected'); + } + } + + obj.selectedContainer = [ borderLeft, borderTop, borderRight, borderBottom ]; + } + + // Handle events
+ if (previousState == 0) {
+ obj.dispatch('onfocus', el);
+
+ obj.removeCopyingSelection(); + } +
+ obj.dispatch('onselection', el, borderLeft, borderTop, borderRight, borderBottom, origin);
+
+ // Find corner cell + obj.updateCornerPosition(); + } + + /** + * Remove copy selection + * + * @return void + */ + obj.removeCopySelection = function() { + // Remove current selection + for (var i = 0; i < obj.selection.length; i++) { + obj.selection[i].classList.remove('selection'); + obj.selection[i].classList.remove('selection-left'); + obj.selection[i].classList.remove('selection-right'); + obj.selection[i].classList.remove('selection-top'); + obj.selection[i].classList.remove('selection-bottom'); + } + + obj.selection = []; + } + + /** + * Update copy selection + * + * @param int x, y + * @return void + */ + obj.updateCopySelection = function(x3, y3) { + // Remove selection + obj.removeCopySelection(); + + // Get elements first and last + var x1 = obj.selectedContainer[0]; + var y1 = obj.selectedContainer[1]; + var x2 = obj.selectedContainer[2]; + var y2 = obj.selectedContainer[3]; + + if (x3 != null && y3 != null) { + if (x3 - x2 > 0) { + var px = parseInt(x2) + 1; + var ux = parseInt(x3); + } else { + var px = parseInt(x3); + var ux = parseInt(x1) - 1; + } + + if (y3 - y2 > 0) { + var py = parseInt(y2) + 1; + var uy = parseInt(y3); + } else { + var py = parseInt(y3); + var uy = parseInt(y1) - 1; + } + + if (ux - px <= uy - py) { + var px = parseInt(x1); + var ux = parseInt(x2); + } else { + var py = parseInt(y1); + var uy = parseInt(y2); + } + + for (var j = py; j <= uy; j++) { + for (var i = px; i <= ux; i++) { + if (obj.records[j][i] && obj.rows[j].style.display != 'none' && obj.records[j][i].style.display != 'none') { + obj.records[j][i].classList.add('selection'); + obj.records[py][i].classList.add('selection-top'); + obj.records[uy][i].classList.add('selection-bottom'); + obj.records[j][px].classList.add('selection-left'); + obj.records[j][ux].classList.add('selection-right'); + + // Persist selected elements + obj.selection.push(obj.records[j][i]); + } + } + } + } + } + + /** + * Update corner position + * + * @return void + */ + obj.updateCornerPosition = function() { + // If any selected cells + if (! obj.highlighted.length) { + obj.corner.style.top = '-2000px'; + obj.corner.style.left = '-2000px'; + } else {
+ // Get last cell
+ var last = obj.highlighted[obj.highlighted.length-1];
+ + const contentRect = obj.content.getBoundingClientRect(); + var x1 = contentRect.left; + var y1 = contentRect.top; +
+ const lastRect = last.getBoundingClientRect(); + var x2 = lastRect.left; + var y2 = lastRect.top; + var w2 = lastRect.width; + var h2 = lastRect.height; + + var x = (x2 - x1) + obj.content.scrollLeft + w2 - 4; + var y = (y2 - y1) + obj.content.scrollTop + h2 - 4; + + // Place the corner in the correct place + obj.corner.style.top = y + 'px'; + obj.corner.style.left = x + 'px';
+
+ if (obj.options.freezeColumns) {
+ var width = obj.getFreezeWidth();
+ if (x2 - x1 + w2 < width) {
+ obj.corner.style.display = 'none';
+ } else {
+ if (obj.options.selectionCopy == true) { + obj.corner.style.display = '';
+ }
+ }
+ } else {
+ if (obj.options.selectionCopy == true) { + obj.corner.style.display = '';
+ } + } + } + } + + /** + * Update scroll position based on the selection + */ + obj.updateScroll = function(direction) { + // jExcel Container information + const contentRect = obj.content.getBoundingClientRect();
+ var x1 = contentRect.left;
+ var y1 = contentRect.top;
+ var w1 = contentRect.width;
+ var h1 = contentRect.height;
+ + // Direction Left or Up + var reference = obj.records[obj.selectedCell[3]][obj.selectedCell[2]]; + + // Reference
+ const referenceRect = reference.getBoundingClientRect();
+ var x2 = referenceRect.left;
+ var y2 = referenceRect.top;
+ var w2 = referenceRect.width;
+ var h2 = referenceRect.height; + + // Direction + if (direction == 0 || direction == 1) { + var x = (x2 - x1) + obj.content.scrollLeft; + var y = (y2 - y1) + obj.content.scrollTop - 2; + } else { + var x = (x2 - x1) + obj.content.scrollLeft + w2; + var y = (y2 - y1) + obj.content.scrollTop + h2; + } + + // Top position check + if (y > (obj.content.scrollTop + 30) && y < (obj.content.scrollTop + h1)) { + // In the viewport + } else { + // Out of viewport + if (y < obj.content.scrollTop + 30) { + obj.content.scrollTop = y - h2; + } else { + obj.content.scrollTop = y - (h1 - 2); + } + } +
+ // Freeze columns?
+ var freezed = obj.getFreezeWidth();
+ + // Left position check - TODO: change that to the bottom border of the element + if (x > (obj.content.scrollLeft + freezed) && x < (obj.content.scrollLeft + w1)) { + // In the viewport + } else { + // Out of viewport + if (x < obj.content.scrollLeft + 30) { + obj.content.scrollLeft = x; + if (obj.content.scrollLeft < 50) { + obj.content.scrollLeft = 0; + }
+ } else if (x < obj.content.scrollLeft + freezed) {
+ obj.content.scrollLeft = x - freezed - 1; + } else { + obj.content.scrollLeft = x - (w1 - 20); + } + } + } + + /** + * Get the column width + * + * @param int column column number (first column is: 0) + * @return int current width + */ + obj.getWidth = function(column) { + if (! column) { + // Get all headers + var data = []; + for (var i = 0; i < obj.headers.length; i++) { + data.push(obj.options.columns[i].width); + } + } else { + // In case the column is an object + if (typeof(column) == 'object') { + column = $(column).getAttribute('data-x'); + } + + data = obj.colgroup[column].getAttribute('width') + } + + return data; + } +
+
+ /**
+ * Set the column width
+ *
+ * @param int column number (first column is: 0)
+ * @param int new column width
+ * @param int old column width
+ */
+ obj.setWidth = function (column, width, oldWidth) {
+ if (width) {
+ if (Array.isArray(column)) {
+ // Oldwidth
+ if (! oldWidth) {
+ var oldWidth = [];
+ }
+ // Set width
+ for (var i = 0; i < column.length; i++) {
+ if (! oldWidth[i]) {
+ oldWidth[i] = obj.colgroup[column[i]].getAttribute('width');
+ }
+ var w = Array.isArray(width) && width[i] ? width[i] : width;
+ obj.colgroup[column[i]].setAttribute('width', w);
+ obj.options.columns[column[i]].width = w;
+ }
+ } else {
+ // Oldwidth
+ if (! oldWidth) {
+ oldWidth = obj.colgroup[column].getAttribute('width');
+ }
+ // Set width
+ obj.colgroup[column].setAttribute('width', width);
+ obj.options.columns[column].width = width;
+ }
+
+ // Keeping history of changes
+ obj.setHistory({
+ action:'setWidth',
+ column:column,
+ oldValue:oldWidth,
+ newValue:width,
+ });
+
+ // On resize column
+ obj.dispatch('onresizecolumn', el, column, width, oldWidth);
+
+ // Update corner position
+ obj.updateCornerPosition();
+ }
+ }
+ + /** + * Set the row height + * + * @param row - row number (first row is: 0) + * @param height - new row height + * @param oldHeight - old row height + */ + obj.setHeight = function (row, height, oldHeight) { + if (height > 0) { + // In case the column is an object + if (typeof(row) == 'object') { + row = row.getAttribute('data-y'); + } + + // Oldwidth
+ if (! oldHeight) {
+ oldHeight = obj.rows[row].getAttribute('height');
+
+ if (! oldHeight) {
+ var rect = obj.rows[row].getBoundingClientRect();
+ oldHeight = rect.height;
+ }
+ }
+
+ // Integer
+ height = parseInt(height);
+ + // Set width + obj.rows[row].style.height = height + 'px'; + + // Keep options updated + if (! obj.options.rows[row]) { + obj.options.rows[row] = {}; + } + obj.options.rows[row].height = height; + + // Keeping history of changes + obj.setHistory({ + action:'setHeight', + row:row, + oldValue:oldHeight, + newValue:height, + }); + + // On resize column
+ obj.dispatch('onresizerow', el, row, height, oldHeight); + + // Update corner position + obj.updateCornerPosition(); + } + } + + /** + * Get the row height + * + * @param row - row number (first row is: 0) + * @return height - current row height + */ + obj.getHeight = function(row) { + if (! row) { + // Get height of all rows + var data = []; + for (var j = 0; j < obj.rows.length; j++) { + var h = obj.rows[j].style.height; + if (h) { + data[j] = h; + } + } + } else { + // In case the row is an object + if (typeof(row) == 'object') { + row = $(row).getAttribute('data-y'); + } + + var data = obj.rows[row].style.height; + } + + return data; + } +
+ obj.setFooter = function(data) {
+ if (data) {
+ obj.options.footers = data;
+ }
+
+ if (obj.options.footers) {
+ if (! obj.tfoot) {
+ obj.tfoot = document.createElement('tfoot');
+ obj.table.appendChild(obj.tfoot);
+ }
+
+ for (var j = 0; j < obj.options.footers.length; j++) {
+ if (obj.tfoot.children[j]) {
+ var tr = obj.tfoot.children[j];
+ } else {
+ var tr = document.createElement('tr');
+ var td = document.createElement('td');
+ tr.appendChild(td);
+ obj.tfoot.appendChild(tr);
+ }
+ for (var i = 0; i < obj.headers.length; i++) {
+ if (! obj.options.footers[j][i]) {
+ obj.options.footers[j][i] = '';
+ }
+ if (obj.tfoot.children[j].children[i+1]) {
+ var td = obj.tfoot.children[j].children[i+1];
+ } else {
+ var td = document.createElement('td');
+ tr.appendChild(td);
+
+ // Text align
+ var colAlign = obj.options.columns[i].align ? obj.options.columns[i].align : 'center';
+ td.style.textAlign = colAlign;
+ }
+ td.innerText = obj.parseValue(i, j, obj.options.footers[j][i]);
+ }
+ }
+ }
+ }
+ + /** + * Get the column title + * + * @param column - column number (first column is: 0) + * @param title - new column title + */ + obj.getHeader = function(column) { + return obj.headers[column].innerText; + } + + /** + * Set the column title + * + * @param column - column number (first column is: 0) + * @param title - new column title + */ + obj.setHeader = function(column, newValue) { + if (obj.headers[column]) { + var oldValue = obj.headers[column].innerText; + + if (! newValue) { + newValue = prompt(obj.options.text.columnName, oldValue) + } + + if (newValue) { + obj.headers[column].innerText = newValue;
+ // Keep the title property
+ obj.headers[column].setAttribute('title', newValue);
+ // Update title
+ obj.options.columns[column].title = newValue; + } + + obj.setHistory({ + action: 'setHeader', + column: column, + oldValue: oldValue, + newValue: newValue + }); + + // On onchange header
+ obj.dispatch('onchangeheader', el, column, oldValue, newValue);
+ } + } + + /** + * Get the headers + * + * @param asArray + * @return mixed + */ + obj.getHeaders = function (asArray) { + var title = []; + + for (var i = 0; i < obj.headers.length; i++) { + title.push(obj.getHeader(i)); + } + + return asArray ? title : title.join(obj.options.csvDelimiter); + } + + /** + * Get meta information from cell(s) + * + * @return integer + */ + obj.getMeta = function(cell, key) { + if (! cell) { + return obj.options.meta; + } else { + if (key) { + return obj.options.meta[cell] && obj.options.meta[cell][key] ? obj.options.meta[cell][key] : null; + } else { + return obj.options.meta[cell] ? obj.options.meta[cell] : null; + } + } + } + + /** + * Set meta information to cell(s) + * + * @return integer + */ + obj.setMeta = function(o, k, v) { + if (! obj.options.meta) { + obj.options.meta = {} + } + + if (k && v) { + // Set data value + if (! obj.options.meta[o]) { + obj.options.meta[o] = {}; + } + obj.options.meta[o][k] = v; + } else { + // Apply that for all cells + var keys = Object.keys(o); + for (var i = 0; i < keys.length; i++) { + if (! obj.options.meta[keys[i]]) { + obj.options.meta[keys[i]] = {}; + } + + var prop = Object.keys(o[keys[i]]); + for (var j = 0; j < prop.length; j++) { + obj.options.meta[keys[i]][prop[j]] = o[keys[i]][prop[j]]; + } + } + } +
+ obj.dispatch('onchangemeta', el, o, k, v); + } + + /** + * Update meta information + * + * @return integer + */ + obj.updateMeta = function(affectedCells) { + if (obj.options.meta) { + var newMeta = {}; + var keys = Object.keys(obj.options.meta); + for (var i = 0; i < keys.length; i++) { + if (affectedCells[keys[i]]) { + newMeta[affectedCells[keys[i]]] = obj.options.meta[keys[i]]; + } else { + newMeta[keys[i]] = obj.options.meta[keys[i]]; + } + } + // Update meta information + obj.options.meta = newMeta; + } + } + + /** + * Get style information from cell(s) + * + * @return integer + */ + obj.getStyle = function(cell, key) { + // Cell + if (! cell) { + // Control vars + var data = {}; + + // Column and row length + var x = obj.options.data[0].length; + var y = obj.options.data.length; + + // Go through the columns to get the data + for (var j = 0; j < y; j++) { + for (var i = 0; i < x; i++) { + // Value + var v = key ? obj.records[j][i].style[key] : obj.records[j][i].getAttribute('style'); + + // Any meta data for this column? + if (v) { + // Column name + var k = jexcel.getColumnNameFromId([i, j]); + // Value + data[k] = v; + } + } + } + + return data; + } else { + cell = jexcel.getIdFromColumnName(cell, true); + + return key ? obj.records[cell[1]][cell[0]].style[key] : obj.records[cell[1]][cell[0]].getAttribute('style'); + } + }, + + obj.resetStyle = function(o, ignoreHistoryAndEvents) { + var keys = Object.keys(o); + for (var i = 0; i < keys.length; i++) { + // Position + var cell = jexcel.getIdFromColumnName(keys[i], true); + if (obj.records[cell[1]] && obj.records[cell[1]][cell[0]]) { + obj.records[cell[1]][cell[0]].setAttribute('style', ''); + } + } + obj.setStyle(o, null, null, null, ignoreHistoryAndEvents); + } + + /** + * Set meta information to cell(s) + * + * @return integer + */ + obj.setStyle = function(o, k, v, force, ignoreHistoryAndEvents) { + var newValue = {}; + var oldValue = {}; + + // Apply style + var applyStyle = function(cellId, key, value) { + // Position + var cell = jexcel.getIdFromColumnName(cellId, true); + + if (obj.records[cell[1]] && obj.records[cell[1]][cell[0]]) { + // Current value + var currentValue = obj.records[cell[1]][cell[0]].style[key]; + + // Change layout + if (currentValue == value && ! force) { + value = ''; + obj.records[cell[1]][cell[0]].style[key] = ''; + } else { + obj.records[cell[1]][cell[0]].style[key] = value; + } + + // History + if (! oldValue[cellId]) { + oldValue[cellId] = []; + } + if (! newValue[cellId]) { + newValue[cellId] = []; + } + + oldValue[cellId].push([key + ':' + currentValue]); + newValue[cellId].push([key + ':' + value]); + } + } + + if (k && v) { + // Get object from string + if (typeof(o) == 'string') { + applyStyle(o, k, v); + } else { + // Avoid duplications + var oneApplication = []; + // Apply that for all cells + for (var i = 0; i < o.length; i++) { + var x = o[i].getAttribute('data-x'); + var y = o[i].getAttribute('data-y'); + var cellName = jexcel.getColumnNameFromId([x, y]); + // This happens when is a merged cell + if (! oneApplication[cellName]) { + applyStyle(cellName, k, v); + oneApplication[cellName] = true; + } + } + } + } else { + var keys = Object.keys(o); + for (var i = 0; i < keys.length; i++) { + var style = o[keys[i]]; + if (typeof(style) == 'string') { + style = style.split(';'); + } + for (var j = 0; j < style.length; j++) { + if (typeof(style[j]) == 'string') { + style[j] = style[j].split(':'); + } + // Apply value + if (style[j][0].trim()) { + applyStyle(keys[i], style[j][0].trim(), style[j][1]); + } + } + } + } + + var keys = Object.keys(oldValue); + for (var i = 0; i < keys.length; i++) { + oldValue[keys[i]] = oldValue[keys[i]].join(';'); + } + var keys = Object.keys(newValue); + for (var i = 0; i < keys.length; i++) { + newValue[keys[i]] = newValue[keys[i]].join(';'); + } + + if (! ignoreHistoryAndEvents) { + // Keeping history of changes + obj.setHistory({ + action: 'setStyle', + oldValue: oldValue, + newValue: newValue, + }); + } +
+ obj.dispatch('onchangestyle', el, o, k, v);
+ } + + /** + * Get cell comments, null cell for all
+ */ + obj.getComments = function(cell, withAuthor) {
+ if (cell) { + if (typeof(cell) == 'string') { + var cell = jexcel.getIdFromColumnName(cell, true); + } + + if (withAuthor) { + return [obj.records[cell[1]][cell[0]].getAttribute('title'), obj.records[cell[1]][cell[0]].getAttribute('author')]; + } else { + return obj.records[cell[1]][cell[0]].getAttribute('title') || ''; + }
+ } else {
+ var data = {};
+ for (var j = 0; j < obj.options.data.length; j++) {
+ for (var i = 0; i < obj.options.columns.length; i++) {
+ var comments = obj.records[j][i].getAttribute('title');
+ if (comments) {
+ var cell = jexcel.getColumnNameFromId([i, j]);
+ data[cell] = comments;
+ }
+ }
+ }
+ return data;
+ } + } + + /** + * Set cell comments + */ + obj.setComments = function(cellId, comments, author) { + if (typeof(cellId) == 'string') { + var cell = jexcel.getIdFromColumnName(cellId, true); + } else { + var cell = cellId; + } + + // Keep old value + var title = obj.records[cell[1]][cell[0]].getAttribute('title'); + var author = obj.records[cell[1]][cell[0]].getAttribute('data-author'); + var oldValue = [ title, author ]; + + // Set new values + obj.records[cell[1]][cell[0]].setAttribute('title', comments ? comments : ''); + obj.records[cell[1]][cell[0]].setAttribute('data-author', author ? author : ''); + + // Remove class if there is no comment + if (comments) { + obj.records[cell[1]][cell[0]].classList.add('jexcel_comments'); + } else { + obj.records[cell[1]][cell[0]].classList.remove('jexcel_comments'); + } + + // Save history + obj.setHistory({ + action:'setComments', + column: cellId, + newValue: [ comments, author ], + oldValue: oldValue, + }); + } + + /** + * Get table config information + */ + obj.getConfig = function() { + var options = obj.options; + options.style = obj.getStyle(); + options.mergeCells = obj.getMerge();
+ options.comments = obj.getComments(); + + return options; + } + + /** + * Sort data and reload table + */ + obj.orderBy = function(column, order) { + if (column >= 0) { + // Merged cells + if (Object.keys(obj.options.mergeCells).length > 0) { + if (! confirm(obj.options.text.thisActionWillDestroyAnyExistingMergedCellsAreYouSure)) { + return false; + } else { + // Remove merged cells + obj.destroyMerged(); + } + } + + // Direction + if (order == null) { + order = obj.headers[column].classList.contains('arrow-down') ? 1 : 0; + } else { + order = order ? 1 : 0; + } + + // Filter
+ Array.prototype.orderBy = function(p, o) {
+ return this.slice(0).sort(function(a, b) {
+ var valueA = a[p];
+ var valueB = b[p];
+
+ if (! o) {
+ return (valueA == '' && valueB != '') ? 1 : (valueA != '' && valueB == '') ? -1 : (valueA > valueB) ? 1 : (valueA < valueB) ? -1 : 0;
+ } else {
+ return (valueA == '' && valueB != '') ? 1 : (valueA != '' && valueB == '') ? -1 : (valueA > valueB) ? -1 : (valueA < valueB) ? 1 : 0;
+ }
+ });
+ }
+
+ // Test order
+ var temp = [];
+ if (obj.options.columns[column].type == 'number' || obj.options.columns[column].type == 'percentage' || obj.options.columns[column].type == 'autonumber' || obj.options.columns[column].type == 'color') {
+ for (var j = 0; j < obj.options.data.length; j++) {
+ temp[j] = [ j, Number(obj.options.data[j][column]) ];
+ }
+ } else if (obj.options.columns[column].type == 'calendar' || obj.options.columns[column].type == 'checkbox' || obj.options.columns[column].type == 'radio') {
+ for (var j = 0; j < obj.options.data.length; j++) {
+ temp[j] = [ j, obj.options.data[j][column] ];
+ }
+ } else {
+ for (var j = 0; j < obj.options.data.length; j++) {
+ temp[j] = [ j, obj.records[j][column].innerText.toLowerCase() ];
+ }
+ }
+ temp = temp.orderBy(1, order); + + // Save history + var newValue = []; + for (var j = 0; j < temp.length; j++) { + newValue[j] = temp[j][0]; + } + + // Save history + obj.setHistory({ + action: 'orderBy', + rows: newValue, + column: column, + order: order, + }); + + // Update order + obj.updateOrderArrow(column, order); + obj.updateOrder(newValue); + + // On sort event
+ obj.dispatch('onsort', el, column, order); + + return true; + } + } + + /** + * Update order arrow + */ + obj.updateOrderArrow = function(column, order) { + // Remove order + for (var i = 0; i < obj.headers.length; i++) { + obj.headers[i].classList.remove('arrow-up'); + obj.headers[i].classList.remove('arrow-down'); + } + + // No order specified then toggle order + if (order) { + obj.headers[column].classList.add('arrow-up'); + } else { + obj.headers[column].classList.add('arrow-down'); + } + } + + /** + * Update rows position + */ + obj.updateOrder = function(rows) { + // History + var data = [] + for (var j = 0; j < rows.length; j++) { + data[j] = obj.options.data[rows[j]]; + } + obj.options.data = data; + + var data = [] + for (var j = 0; j < rows.length; j++) { + data[j] = obj.records[rows[j]]; + } + obj.records = data; + + var data = [] + for (var j = 0; j < rows.length; j++) { + data[j] = obj.rows[rows[j]]; + } + obj.rows = data; + + // Update references + obj.updateTableReferences(); + + // Redo search + if (obj.results && obj.results.length) {
+ if (obj.searchInput.value) { + obj.search(obj.searchInput.value);
+ } else {
+ obj.closeFilter();
+ } + } else { + // Create page + obj.results = null; + obj.pageNumber = 0; + + if (obj.options.pagination > 0) { + obj.page(0); + } else if (obj.options.lazyLoading == true) { + obj.loadPage(0); + } else { + for (var j = 0; j < obj.rows.length; j++) { + obj.tbody.appendChild(obj.rows[j]); + } + } + } + } + + /** + * Move row + * + * @return void + */ + obj.moveRow = function(o, d, ignoreDom) { + if (Object.keys(obj.options.mergeCells).length > 0) {
+ if (o > d) {
+ var insertBefore = 1;
+ } else {
+ var insertBefore = 0;
+ }
+ + if (obj.isRowMerged(o).length || obj.isRowMerged(d, insertBefore).length) { + if (! confirm(obj.options.text.thisActionWillDestroyAnyExistingMergedCellsAreYouSure)) { + return false; + } else { + obj.destroyMerged(); + } + } + } + + if (obj.options.search == true) { + if (obj.results && obj.results.length != obj.rows.length) { + if (confirm(obj.options.text.thisActionWillClearYourSearchResultsAreYouSure)) { + obj.resetSearch(); + } else { + return false; + } + } + + obj.results = null; + } + + if (! ignoreDom) { + if (Array.prototype.indexOf.call(obj.tbody.children, obj.rows[d]) >= 0) { + if (o > d) { + obj.tbody.insertBefore(obj.rows[o], obj.rows[d]); + } else { + obj.tbody.insertBefore(obj.rows[o], obj.rows[d].nextSibling); + } + } else { + obj.tbody.removeChild(obj.rows[o]); + } + } + + // Place references in the correct position + obj.rows.splice(d, 0, obj.rows.splice(o, 1)[0]); + obj.records.splice(d, 0, obj.records.splice(o, 1)[0]); + obj.options.data.splice(d, 0, obj.options.data.splice(o, 1)[0]); + + // Respect pagination + if (obj.options.pagination > 0 && obj.tbody.children.length != obj.options.pagination) { + obj.page(obj.pageNumber); + } + + // Keeping history of changes + obj.setHistory({ + action:'moveRow', + oldValue: o, + newValue: d, + }); + + // Update table references + obj.updateTableReferences(); + + // Events
+ obj.dispatch('onmoverow', el, o, d);
+ } + + /** + * Insert a new row + * + * @param mixed - number of blank lines to be insert or a single array with the data of the new row + * @param rowNumber + * @param insertBefore + * @return void + */ + obj.insertRow = function(mixed, rowNumber, insertBefore) { + // Configuration + if (obj.options.allowInsertRow == true) { + // Records + var records = []; + + // Data to be insert + var data = []; + + // The insert could be lead by number of rows or the array of data + if (mixed > 0) { + var numOfRows = mixed; + } else { + var numOfRows = 1; + + if (mixed) { + data = mixed; + } + } + + // Direction + var insertBefore = insertBefore ? true : false; + + // Current column number + var lastRow = obj.options.data.length - 1; +
+ if (rowNumber == undefined || rowNumber >= parseInt(lastRow) || rowNumber < 0) { + rowNumber = lastRow; + } + + // Onbeforeinsertrow
+ if (obj.dispatch('onbeforeinsertrow', el, rowNumber, numOfRows, insertBefore) === false) { + console.log('onbeforeinsertrow returned false'); + + return false; + } + + // Merged cells + if (Object.keys(obj.options.mergeCells).length > 0) { + if (obj.isRowMerged(rowNumber, insertBefore).length) { + if (! confirm(obj.options.text.thisActionWillDestroyAnyExistingMergedCellsAreYouSure)) { + return false; + } else { + obj.destroyMerged(); + } + } + } + + // Clear any search + if (obj.options.search == true) { + if (obj.results && obj.results.length != obj.rows.length) { + if (confirm(obj.options.text.thisActionWillClearYourSearchResultsAreYouSure)) { + obj.resetSearch(); + } else { + return false; + } + } + + obj.results = null; + } + + // Insertbefore + var rowIndex = (! insertBefore) ? rowNumber + 1 : rowNumber; + + // Keep the current data + var currentRecords = obj.records.splice(rowIndex); + var currentData = obj.options.data.splice(rowIndex); + var currentRows = obj.rows.splice(rowIndex); +
+ // Adding lines + var rowRecords = []; + var rowData = []; + var rowNode = []; + + for (var row = rowIndex; row < (numOfRows + rowIndex); row++) {
+ // Push data to the data container + obj.options.data[row] = []; + for (var col = 0; col < obj.options.columns.length; col++) { + obj.options.data[row][col] = data[col] ? data[col] : ''; + } + // Create row + var tr = obj.createRow(row, obj.options.data[row]);
+ // Append node
+ if (currentRows[0]) {
+ if (Array.prototype.indexOf.call(obj.tbody.children, currentRows[0]) >= 0) {
+ obj.tbody.insertBefore(tr, currentRows[0]);
+ }
+ } else {
+ if (Array.prototype.indexOf.call(obj.tbody.children, obj.rows[rowNumber]) >= 0) {
+ obj.tbody.appendChild(tr);
+ }
+ } + // Record History + rowRecords.push(obj.records[row]); + rowData.push(obj.options.data[row]); + rowNode.push(tr); + } + + // Copy the data back to the main data + Array.prototype.push.apply(obj.records, currentRecords); + Array.prototype.push.apply(obj.options.data, currentData); + Array.prototype.push.apply(obj.rows, currentRows); + + // Respect pagination + if (obj.options.pagination > 0) { + obj.page(obj.pageNumber); + } + + // Keep history + obj.setHistory({ + action: 'insertRow', + rowNumber: rowNumber, + numOfRows: numOfRows, + insertBefore: insertBefore, + rowRecords: rowRecords, + rowData: rowData, + rowNode: rowNode, + }); + + // Remove table references + obj.updateTableReferences(); + + // Events
+ obj.dispatch('oninsertrow', el, rowNumber, numOfRows, rowRecords, insertBefore); + } + } + + /** + * Delete a row by number + * + * @param integer rowNumber - row number to be excluded + * @param integer numOfRows - number of lines + * @return void + */ + obj.deleteRow = function(rowNumber, numOfRows) { + // Global Configuration + if (obj.options.allowDeleteRow == true) { + if (obj.options.allowDeletingAllRows == true || obj.options.data.length > 1) { + // Delete row definitions + if (rowNumber == undefined) { + var number = obj.getSelectedRows(); + + if (! number[0]) { + rowNumber = obj.options.data.length - 1; + numOfRows = 1; + } else { + rowNumber = parseInt(number[0].getAttribute('data-y')); + numOfRows = number.length; + } + } + + // Last column + var lastRow = obj.options.data.length - 1; + + if (rowNumber == undefined || rowNumber > lastRow || rowNumber < 0) { + rowNumber = lastRow; + } + + if (! numOfRows) { + numOfRows = 1; + } + + // Do not delete more than the number of recoreds + if (rowNumber + numOfRows >= obj.options.data.length) { + numOfRows = obj.options.data.length - rowNumber; + } + + // Onbeforedeleterow + if (obj.dispatch('onbeforedeleterow', el, rowNumber, numOfRows) === false) { + console.log('onbeforedeleterow returned false'); + return false; + } + + if (parseInt(rowNumber) > -1) { + // Merged cells + var mergeExists = false; + if (Object.keys(obj.options.mergeCells).length > 0) { + for (var row = rowNumber; row < rowNumber + numOfRows; row++) { + if (obj.isRowMerged(row, false).length) { + mergeExists = true; + } + } + } + if (mergeExists) { + if (! confirm(obj.options.text.thisActionWillDestroyAnyExistingMergedCellsAreYouSure)) { + return false; + } else { + obj.destroyMerged(); + } + } + + // Clear any search + if (obj.options.search == true) { + if (obj.results && obj.results.length != obj.rows.length) { + if (confirm(obj.options.text.thisActionWillClearYourSearchResultsAreYouSure)) { + obj.resetSearch(); + } else { + return false; + } + } + + obj.results = null; + } + + // Remove node + for (var row = rowNumber; row < rowNumber + numOfRows; row++) { + if (Array.prototype.indexOf.call(obj.tbody.children, obj.rows[row]) >= 0) { + obj.rows[row].className = ''; + obj.rows[row].parentNode.removeChild(obj.rows[row]); + } + } + + // Remove data + var rowRecords = obj.records.splice(rowNumber, numOfRows); + var rowData = obj.options.data.splice(rowNumber, numOfRows); + var rowNode = obj.rows.splice(rowNumber, numOfRows); + + // Respect pagination + if (obj.options.pagination > 0 && obj.tbody.children.length != obj.options.pagination) { + obj.page(obj.pageNumber); + } + + // Remove selection + obj.conditionalSelectionUpdate(1, rowNumber, (rowNumber + numOfRows) - 1); + + // Keep history + obj.setHistory({ + action: 'deleteRow', + rowNumber: rowNumber, + numOfRows: numOfRows, + insertBefore: 1, + rowRecords: rowRecords, + rowData: rowData, + rowNode: rowNode + }); + + // Remove table references + obj.updateTableReferences(); + + // Events
+ obj.dispatch('ondeleterow', el, rowNumber, numOfRows, rowRecords); + } + } else { + console.error('JEXCEL. It is not possible to delete the last row'); + } + } + } + + + /** + * Move column + * + * @return void + */ + obj.moveColumn = function(o, d) { + if (Object.keys(obj.options.mergeCells).length > 0) {
+ if (o > d) {
+ var insertBefore = 1;
+ } else {
+ var insertBefore = 0;
+ }
+ + if (obj.isColMerged(o).length || obj.isColMerged(d, insertBefore).length) { + if (! confirm(obj.options.text.thisActionWillDestroyAnyExistingMergedCellsAreYouSure)) { + return false; + } else { + obj.destroyMerged(); + } + } + } + + var o = parseInt(o); + var d = parseInt(d); + + if (o > d) { + obj.headerContainer.insertBefore(obj.headers[o], obj.headers[d]); + obj.colgroupContainer.insertBefore(obj.colgroup[o], obj.colgroup[d]); + + for (var j = 0; j < obj.rows.length; j++) { + obj.rows[j].insertBefore(obj.records[j][o], obj.records[j][d]); + } + } else { + obj.headerContainer.insertBefore(obj.headers[o], obj.headers[d].nextSibling); + obj.colgroupContainer.insertBefore(obj.colgroup[o], obj.colgroup[d].nextSibling); + + for (var j = 0; j < obj.rows.length; j++) { + obj.rows[j].insertBefore(obj.records[j][o], obj.records[j][d].nextSibling); + } + } + + obj.options.columns.splice(d, 0, obj.options.columns.splice(o, 1)[0]); + obj.headers.splice(d, 0, obj.headers.splice(o, 1)[0]); + obj.colgroup.splice(d, 0, obj.colgroup.splice(o, 1)[0]); + + for (var j = 0; j < obj.rows.length; j++) { + obj.options.data[j].splice(d, 0, obj.options.data[j].splice(o, 1)[0]); + obj.records[j].splice(d, 0, obj.records[j].splice(o, 1)[0]); + } +
+ // Update footers position
+ if (obj.options.footers) {
+ for (var j = 0; j < obj.options.footers.length; j++) {
+ obj.options.footers[j].splice(d, 0, obj.options.footers[j].splice(o, 1)[0]);
+ }
+ }
+ + // Keeping history of changes + obj.setHistory({ + action:'moveColumn', + oldValue: o, + newValue: d, + }); + + // Update table references + obj.updateTableReferences(); + + // Events
+ obj.dispatch('onmovecolumn', el, o, d); + } + + /** + * Insert a new column + * + * @param mixed - num of columns to be added or data to be added in one single column + * @param int columnNumber - number of columns to be created + * @param bool insertBefore + * @param object properties - column properties + * @return void + */ + obj.insertColumn = function(mixed, columnNumber, insertBefore, properties) { + // Configuration + if (obj.options.allowInsertColumn == true) { + // Records + var records = []; + + // Data to be insert + var data = []; + + // The insert could be lead by number of rows or the array of data + if (mixed > 0) { + var numOfColumns = mixed; + } else { + var numOfColumns = 1; + + if (mixed) { + data = mixed; + } + } + + // Direction + var insertBefore = insertBefore ? true : false; + + // Current column number + var lastColumn = obj.options.columns.length - 1; + + // Confirm position + if (columnNumber == undefined || columnNumber >= parseInt(lastColumn) || columnNumber < 0) { + columnNumber = lastColumn; + } + + // Onbeforeinsertcolumn + if (obj.dispatch('onbeforeinsertcolumn', el, columnNumber, numOfColumns, insertBefore) === false) { + console.log('onbeforeinsertcolumn returned false'); + + return false; + } + + // Merged cells + if (Object.keys(obj.options.mergeCells).length > 0) { + if (obj.isColMerged(columnNumber, insertBefore).length) { + if (! confirm(obj.options.text.thisActionWillDestroyAnyExistingMergedCellsAreYouSure)) { + return false; + } else { + obj.destroyMerged(); + } + } + } + + // Create default properties + if (! properties) { + properties = []; + } + + for (var i = 0; i < numOfColumns; i++) { + if (! properties[i]) { + properties[i] = { type:'text', source:[], options:[], width:obj.options.defaultColWidth, align:obj.options.defaultColAlign }; + } + } + + // Insert before + var columnIndex = (! insertBefore) ? columnNumber + 1 : columnNumber; + obj.options.columns = jexcel.injectArray(obj.options.columns, columnIndex, properties); + + // Open space in the containers + var currentHeaders = obj.headers.splice(columnIndex); + var currentColgroup = obj.colgroup.splice(columnIndex); + + // History + var historyHeaders = []; + var historyColgroup = []; + var historyRecords = []; + var historyData = [];
+ var historyFooters = [];
+ + // Add new headers + for (var col = columnIndex; col < (numOfColumns + columnIndex); col++) { + obj.createCellHeader(col); + obj.headerContainer.insertBefore(obj.headers[col], obj.headerContainer.children[col+1]); + obj.colgroupContainer.insertBefore(obj.colgroup[col], obj.colgroupContainer.children[col+1]); + + historyHeaders.push(obj.headers[col]); + historyColgroup.push(obj.colgroup[col]); + } +
+ // Add new footer cells
+ if (obj.options.footers) {
+ for (var j = 0; j < obj.options.footers.length; j++) {
+ historyFooters[j] = [];
+ for (var i = 0; i < numOfColumns; i++) {
+ historyFooters[j].push('');
+ }
+ obj.options.footers[j].splice(columnIndex, 0, historyFooters[j]);
+ }
+ }
+
+ // Adding visual columns + for (var row = 0; row < obj.options.data.length; row++) { + // Keep the current data + var currentData = obj.options.data[row].splice(columnIndex); + var currentRecord = obj.records[row].splice(columnIndex); + + // History + historyData[row] = []; + historyRecords[row] = []; + + for (var col = columnIndex; col < (numOfColumns + columnIndex); col++) { + // New value + var value = data[row] ? data[row] : ''; + obj.options.data[row][col] = value; + // New cell + var td = obj.createCell(col, row, obj.options.data[row][col]); + obj.records[row][col] = td; + // Add cell to the row + if (obj.rows[row]) { + obj.rows[row].insertBefore(td, obj.rows[row].children[col+1]); + } + + // Record History + historyData[row].push(value); + historyRecords[row].push(td); + } + + // Copy the data back to the main data + Array.prototype.push.apply(obj.options.data[row], currentData); + Array.prototype.push.apply(obj.records[row], currentRecord); + } + + Array.prototype.push.apply(obj.headers, currentHeaders); + Array.prototype.push.apply(obj.colgroup, currentColgroup); + + // Adjust nested headers + if (obj.options.nestedHeaders && obj.options.nestedHeaders.length > 0) { + // Flexible way to handle nestedheaders + if (obj.options.nestedHeaders[0] && obj.options.nestedHeaders[0][0]) { + for (var j = 0; j < obj.options.nestedHeaders.length; j++) { + var colspan = parseInt(obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan) + numOfColumns; + obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan = colspan; + obj.thead.children[j].children[obj.thead.children[j].children.length-1].setAttribute('colspan', colspan); + } + } else { + var colspan = parseInt(obj.options.nestedHeaders[0].colspan) + numOfColumns; + obj.options.nestedHeaders[0].colspan = colspan; + obj.thead.children[0].children[obj.thead.children[0].children.length-1].setAttribute('colspan', colspan); + } + } + + // Keep history + obj.setHistory({ + action: 'insertColumn', + columnNumber:columnNumber, + numOfColumns:numOfColumns, + insertBefore:insertBefore, + columns:properties, + headers:historyHeaders, + colgroup:historyColgroup, + records:historyRecords,
+ footers:historyFooters, + data:historyData, + }); + + // Remove table references + obj.updateTableReferences(); + + // Events
+ obj.dispatch('oninsertcolumn', el, columnNumber, numOfColumns, historyRecords, insertBefore); + } + } + + /** + * Delete a column by number + * + * @param integer columnNumber - reference column to be excluded + * @param integer numOfColumns - number of columns to be excluded from the reference column + * @return void + */ + obj.deleteColumn = function(columnNumber, numOfColumns) { + // Global Configuration + if (obj.options.allowDeleteColumn == true) { + if (obj.headers.length > 1) { + // Delete column definitions + if (columnNumber == undefined) { + var number = obj.getSelectedColumns(true); + + if (! number.length) { + // Remove last column + columnNumber = obj.headers.length - 1; + numOfColumns = 1; + } else { + // Remove selected + columnNumber = parseInt(number[0]); + numOfColumns = parseInt(number.length); + } + } + + // Lasat column + var lastColumn = obj.options.data[0].length - 1; + + if (columnNumber == undefined || columnNumber > lastColumn || columnNumber < 0) { + columnNumber = lastColumn; + } + + // Minimum of columns to be delete is 1 + if (! numOfColumns) { + numOfColumns = 1; + } + + // Can't delete more than the limit of the table + if (numOfColumns > obj.options.data[0].length - columnNumber) { + numOfColumns = obj.options.data[0].length - columnNumber; + } + + // onbeforedeletecolumn + if (obj.dispatch('onbeforedeletecolumn', el, columnNumber, numOfColumns) === false) { + console.log('onbeforedeletecolumn returned false'); + return false; + } + + // Can't remove the last column + if (parseInt(columnNumber) > -1) { + // Merged cells + var mergeExists = false; + if (Object.keys(obj.options.mergeCells).length > 0) { + for (var col = columnNumber; col < columnNumber + numOfColumns; col++) { + if (obj.isColMerged(col, false).length) { + mergeExists = true; + } + } + } + if (mergeExists) { + if (! confirm(obj.options.text.thisActionWillDestroyAnyExistingMergedCellsAreYouSure)) { + return false; + } else { + obj.destroyMerged(); + } + } + + // Delete the column properties + var columns = obj.options.columns.splice(columnNumber, numOfColumns); + + for (var col = columnNumber; col < columnNumber + numOfColumns; col++) { + obj.colgroup[col].className = ''; + obj.headers[col].className = ''; + obj.colgroup[col].parentNode.removeChild(obj.colgroup[col]); + obj.headers[col].parentNode.removeChild(obj.headers[col]); + } + + var historyHeaders = obj.headers.splice(columnNumber, numOfColumns); + var historyColgroup = obj.colgroup.splice(columnNumber, numOfColumns); + var historyRecords = []; + var historyData = []; + var historyFooters = [];
+ + for (var row = 0; row < obj.options.data.length; row++) { + for (var col = columnNumber; col < columnNumber + numOfColumns; col++) { + obj.records[row][col].className = ''; + obj.records[row][col].parentNode.removeChild(obj.records[row][col]); + } + } + + // Delete headers + for (var row = 0; row < obj.options.data.length; row++) { + // History + historyData[row] = obj.options.data[row].splice(columnNumber, numOfColumns); + historyRecords[row] = obj.records[row].splice(columnNumber, numOfColumns);
+ } +
+ // Delete footers
+ if (obj.options.footers) {
+ for (var row = 0; row < obj.options.footers.length; row++) {
+ historyFooters[row] = obj.options.footers[row].splice(columnNumber, numOfColumns);
+ }
+ }
+ + // Remove selection + obj.conditionalSelectionUpdate(0, columnNumber, (columnNumber + numOfColumns) - 1); + + // Adjust nested headers + if (obj.options.nestedHeaders && obj.options.nestedHeaders.length > 0) { + // Flexible way to handle nestedheaders + if (obj.options.nestedHeaders[0] && obj.options.nestedHeaders[0][0]) { + for (var j = 0; j < obj.options.nestedHeaders.length; j++) { + var colspan = parseInt(obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan) - numOfColumns; + obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan = colspan; + obj.thead.children[j].children[obj.thead.children[j].children.length-1].setAttribute('colspan', colspan); + } + } else { + var colspan = parseInt(obj.options.nestedHeaders[0].colspan) - numOfColumns; + obj.options.nestedHeaders[0].colspan = colspan; + obj.thead.children[0].children[obj.thead.children[0].children.length-1].setAttribute('colspan', colspan); + } + } + + // Keeping history of changes + obj.setHistory({ + action:'deleteColumn', + columnNumber:columnNumber, + numOfColumns:numOfColumns, + insertBefore: 1, + columns:columns, + headers:historyHeaders, + colgroup:historyColgroup, + records:historyRecords,
+ footers:historyFooters, + data:historyData, + }); + + // Update table references + obj.updateTableReferences(); + + // Delete
+ obj.dispatch('ondeletecolumn', el, columnNumber, numOfColumns, historyRecords); + } + } else { + console.error('JEXCEL. It is not possible to delete the last column'); + } + } + } + + /** + * Get seleted rows numbers + * + * @return array + */ + obj.getSelectedRows = function(asIds) { + var rows = []; + // Get all selected rows + for (var j = 0; j < obj.rows.length; j++) { + if (obj.rows[j].classList.contains('selected')) { + if (asIds) { + rows.push(j); + } else { + rows.push(obj.rows[j]); + } + } + } + + return rows; + }, + + /** + * Get seleted column numbers + * + * @return array + */ + obj.getSelectedColumns = function() { + var cols = []; + // Get all selected cols + for (var i = 0; i < obj.headers.length; i++) { + if (obj.headers[i].classList.contains('selected')) { + cols.push(i); + } + } + + return cols; + } + + /** + * Get highlighted + * + * @return array + */ + obj.getHighlighted = function() { + return obj.highlighted; + } + + /** + * Update cell references + * + * @return void + */ + obj.updateTableReferences = function() { + // Update headers + for (var i = 0; i < obj.headers.length; i++) { + var x = obj.headers[i].getAttribute('data-x'); + + if (x != i) { + // Update coords + obj.headers[i].setAttribute('data-x', i); + // Title + if (! obj.headers[i].getAttribute('title')) { + obj.headers[i].innerHTML = jexcel.getColumnName(i); + } + } + } + + // Update all rows + for (var j = 0; j < obj.rows.length; j++) { + if (obj.rows[j]) { + var y = obj.rows[j].getAttribute('data-y'); + + if (y != j) { + // Update coords + obj.rows[j].setAttribute('data-y', j); + obj.rows[j].children[0].setAttribute('data-y', j); + // Row number + obj.rows[j].children[0].innerHTML = j + 1; + } + } + } + + // Regular cells affected by this change + var affectedTokens = []; + var mergeCellUpdates = []; + + // Update cell + var updatePosition = function(x,y,i,j) { + if (x != i) { + obj.records[j][i].setAttribute('data-x', i); + } + if (y != j) { + obj.records[j][i].setAttribute('data-y', j); + } + + // Other updates + if (x != i || y != j) { + var columnIdFrom = jexcel.getColumnNameFromId([x, y]); + var columnIdTo = jexcel.getColumnNameFromId([i, j]); + affectedTokens[columnIdFrom] = columnIdTo; + } + } + + for (var j = 0; j < obj.records.length; j++) { + for (var i = 0; i < obj.records[0].length; i++) { + if (obj.records[j][i]) { + // Current values + var x = obj.records[j][i].getAttribute('data-x'); + var y = obj.records[j][i].getAttribute('data-y'); + + // Update column + if (obj.records[j][i].getAttribute('data-merged')) { + var columnIdFrom = jexcel.getColumnNameFromId([x, y]); + var columnIdTo = jexcel.getColumnNameFromId([i, j]); + if (mergeCellUpdates[columnIdFrom] == null) { + if (columnIdFrom == columnIdTo) { + mergeCellUpdates[columnIdFrom] = false; + } else { + var totalX = parseInt(i - x); + var totalY = parseInt(j - y); + mergeCellUpdates[columnIdFrom] = [ columnIdTo, totalX, totalY ]; + } + } + } else { + updatePosition(x,y,i,j); + } + } + } + } + + // Update merged if applicable + var keys = Object.keys(mergeCellUpdates); + if (keys.length) { + for (var i = 0; i < keys.length; i++) { + if (mergeCellUpdates[keys[i]]) { + var info = jexcel.getIdFromColumnName(keys[i], true) + var x = info[0]; + var y = info[1]; + updatePosition(x,y,x + mergeCellUpdates[keys[i]][1],y + mergeCellUpdates[keys[i]][2]); + + var columnIdFrom = keys[i]; + var columnIdTo = mergeCellUpdates[keys[i]][0]; + for (var j = 0; j < obj.options.mergeCells[columnIdFrom][2].length; j++) { + var x = parseInt(obj.options.mergeCells[columnIdFrom][2][j].getAttribute('data-x')); + var y = parseInt(obj.options.mergeCells[columnIdFrom][2][j].getAttribute('data-y')); + obj.options.mergeCells[columnIdFrom][2][j].setAttribute('data-x', x + mergeCellUpdates[keys[i]][1]); + obj.options.mergeCells[columnIdFrom][2][j].setAttribute('data-y', y + mergeCellUpdates[keys[i]][2]); + } + + obj.options.mergeCells[columnIdTo] = obj.options.mergeCells[columnIdFrom]; + delete(obj.options.mergeCells[columnIdFrom]); + } + } + } + + // Update formulas + obj.updateFormulas(affectedTokens); + + // Update meta data + obj.updateMeta(affectedTokens); + + // Refresh selection + obj.refreshSelection(); + + // Update table with custom configuration if applicable + obj.updateTable(); + } + + /** + * Custom settings for the cells + */ + obj.updateTable = function() { + // Check for spare + if (obj.options.minSpareRows > 0) { + var numBlankRows = 0; + for (var j = obj.rows.length - 1; j >= 0; j--) { + var test = false; + for (var i = 0; i < obj.headers.length; i++) { + if (obj.options.data[j][i]) { + test = true; + } + } + if (test) { + break; + } else { + numBlankRows++; + } + } + + if (obj.options.minSpareRows - numBlankRows > 0) { + obj.insertRow(obj.options.minSpareRows - numBlankRows) + } + } + + if (obj.options.minSpareCols > 0) { + var numBlankCols = 0; + for (var i = obj.headers.length - 1; i >= 0 ; i--) { + var test = false; + for (var j = 0; j < obj.rows.length; j++) { + if (obj.options.data[j][i]) { + test = true; + } + } + if (test) { + break; + } else { + numBlankCols++; + } + } + + if (obj.options.minSpareCols - numBlankCols > 0) { + obj.insertColumn(obj.options.minSpareCols - numBlankCols) + } + } + + // Customizations by the developer + if (typeof(obj.options.updateTable) == 'function') { + if (obj.options.detachForUpdates) {
+ el.removeChild(obj.content);
+ }
+ + for (var j = 0; j < obj.rows.length; j++) { + for (var i = 0; i < obj.headers.length; i++) { + obj.options.updateTable(el, obj.records[j][i], i, j, obj.options.data[j][i], obj.records[j][i].innerText, jexcel.getColumnNameFromId([i, j])); + } + } +
+ if (obj.options.detachForUpdates) {
+ el.insertBefore(obj.content, obj.pagination);
+ } + } +
+ // Update footers
+ if (obj.options.footers) {
+ obj.setFooter();
+ }
+ + // Update corner position + setTimeout(function() { + obj.updateCornerPosition(); + },0); + } +
+ /**
+ * Readonly
+ */
+ obj.isReadOnly = function(cell) {
+ if (cell = obj.getCell(cell)) {
+ return cell.classList.contains('readonly') ? true : false;
+ }
+ }
+
+ /**
+ * Readonly
+ */
+ obj.setReadOnly = function(cell, state) {
+ if (cell = obj.getCell(cell)) {
+ if (state) {
+ cell.classList.add('readonly');
+ } else {
+ cell.classList.remove('readonly');
+ }
+ }
+ }
+
+ /**
+ * Show row
+ */
+ obj.showRow = function(rowNumber) {
+ obj.rows[rowNumber].style.display = '';
+ }
+
+ /**
+ * Hide row
+ */
+ obj.hideRow = function(rowNumber) {
+ obj.rows[rowNumber].style.display = 'none';
+ }
+
+ /**
+ * Show column
+ */
+ obj.showColumn = function(colNumber) {
+ obj.headers[colNumber].style.display = '';
+ obj.colgroup[colNumber].style.display = '';
+ for (var j = 0; j < obj.options.data.length; j++) {
+ obj.records[j][colNumber].style.display = '';
+ }
+ }
+
+ /**
+ * Hide column
+ */
+ obj.hideColumn = function(colNumber) {
+ obj.headers[colNumber].style.display = 'none';
+ obj.colgroup[colNumber].style.display = 'none';
+ for (var j = 0; j < obj.options.data.length; j++) {
+ obj.records[j][colNumber].style.display = 'none';
+ }
+ }
+ + /** + * Show index column + */ + obj.showIndex = function() { + obj.table.classList.remove('jexcel_hidden_index'); + } + + /** + * Hide index column + */ + obj.hideIndex = function() { + obj.table.classList.add('jexcel_hidden_index'); + } + + /** + * Update all related cells in the chain + */ + var chainLoopProtection = []; + + obj.updateFormulaChain = function(x, y, records) { + var cellId = jexcel.getColumnNameFromId([x, y]); + if (obj.formula[cellId] && obj.formula[cellId].length > 0) { + if (chainLoopProtection[cellId]) { + obj.records[y][x].innerHTML = '#ERROR'; + obj.formula[cellId] = ''; + } else { + // Protection + chainLoopProtection[cellId] = true; + + for (var i = 0; i < obj.formula[cellId].length; i++) { + var cell = jexcel.getIdFromColumnName(obj.formula[cellId][i], true); + // Update cell + var value = ''+obj.options.data[cell[1]][cell[0]]; + if (value.substr(0,1) == '=') { + records.push(obj.updateCell(cell[0], cell[1], value, true)); + } else { + // No longer a formula, remove from the chain + Object.keys(obj.formula)[i] = null; + } + obj.updateFormulaChain(cell[0], cell[1], records); + } + } + } + + chainLoopProtection = []; + } + + /** + * Update formulas + */ + obj.updateFormulas = function(referencesToUpdate) { + // Update formulas + for (var j = 0; j < obj.options.data.length; j++) { + for (var i = 0; i < obj.options.data[0].length; i++) { + var value = '' + obj.options.data[j][i]; + // Is formula + if (value.substr(0,1) == '=') { + // Replace tokens + var newFormula = obj.updateFormula(value, referencesToUpdate); + if (newFormula != value) { + obj.options.data[j][i] = newFormula; + } + } + } + } + + // Update formula chain + var formula = []; + var keys = Object.keys(obj.formula); + for (var j = 0; j < keys.length; j++) { + // Current key and values + var key = keys[j]; + var value = obj.formula[key]; + // Update key + if (referencesToUpdate[key]) { + key = referencesToUpdate[key]; + } + // Update values + formula[key] = []; + for (var i = 0; i < value.length; i++) { + var letter = value[i]; + if (referencesToUpdate[letter]) { + letter = referencesToUpdate[letter]; + } + formula[key].push(letter); + } + } + obj.formula = formula; + } + + /** + * Update formula + */ + obj.updateFormula = function(formula, referencesToUpdate) { + var testLetter = /[A-Z]/; + var testNumber = /[0-9]/; + + var newFormula = ''; + var letter = null; + var number = null; + var token = ''; + + for (var index = 0; index < formula.length; index++) { + if (testLetter.exec(formula[index])) { + letter = 1; + number = 0; + token += formula[index]; + } else if (testNumber.exec(formula[index])) { + number = letter ? 1 : 0; + token += formula[index]; + } else { + if (letter && number) { + token = referencesToUpdate[token] ? referencesToUpdate[token] : token; + } + newFormula += token; + newFormula += formula[index]; + letter = 0; + number = 0; + token = ''; + } + } + + if (token) { + if (letter && number) { + token = referencesToUpdate[token] ? referencesToUpdate[token] : token; + } + newFormula += token; + } + + return newFormula; + } +
+ /**
+ * Secure formula
+ */
+ var secureFormula = function(oldValue) {
+ var newValue = '';
+ var inside = 0;
+
+ for (var i = 0; i < oldValue.length; i++) {
+ if (oldValue[i] == '"') {
+ if (inside == 0) {
+ inside = 1;
+ } else {
+ inside = 0;
+ }
+ }
+
+ if (inside == 1) {
+ newValue += oldValue[i];
+ } else {
+ newValue += oldValue[i].toUpperCase();
+ }
+ }
+
+ return newValue;
+ }
+ + /** + * Parse formulas + */ + obj.executeFormula = function(expression, x, y) { + + var formulaResults = []; + var formulaLoopProtection = []; + + // Execute formula with loop protection + var execute = function(expression, x, y) { + // Parent column identification + var parentId = jexcel.getColumnNameFromId([x, y]); + + // Code protection + if (formulaLoopProtection[parentId]) { + console.error('Reference loop detected'); + return '#ERROR'; + } + + formulaLoopProtection[parentId] = true; + + // Convert range tokens + var tokensUpdate = function(tokens) { + for (var index = 0; index < tokens.length; index++) { + var f = []; + var token = tokens[index].split(':'); + var e1 = jexcel.getIdFromColumnName(token[0], true); + var e2 = jexcel.getIdFromColumnName(token[1], true); + + if (e1[0] <= e2[0]) { + var x1 = e1[0]; + var x2 = e2[0]; + } else { + var x1 = e2[0]; + var x2 = e1[0]; + } + + if (e1[1] <= e2[1]) { + var y1 = e1[1]; + var y2 = e2[1]; + } else { + var y1 = e2[1]; + var y2 = e1[1]; + } + + for (var j = y1; j <= y2; j++) { + for (var i = x1; i <= x2; i++) { + f.push(jexcel.getColumnNameFromId([i, j])); + } + } + + expression = expression.replace(tokens[index], f.join(',')); + } + } + + var tokens = expression.match(/([A-Z]+[0-9]+)\:([A-Z]+[0-9]+)/g); + if (tokens && tokens.length) { + tokensUpdate(tokens); + } + + // String + var evalstring = ''; + + // Get tokens + var tokens = expression.match(/([A-Z]+[0-9]+)/g); + + // Direct self-reference protection + if (tokens && tokens.indexOf(parentId) > -1) { + console.error('Self Reference detected'); + return '#ERROR'; + } else { + if (tokens) { + for (var i = 0; i < tokens.length; i++) { + // Keep chain + if (! obj.formula[tokens[i]]) { + obj.formula[tokens[i]] = []; + } + // Is already in the register + if (obj.formula[tokens[i]].indexOf(parentId) < 0) { + obj.formula[tokens[i]].push(parentId); + } + + // Do not calculate again + if (eval('typeof(' + tokens[i] + ') == "undefined"')) { + // Coords + var position = jexcel.getIdFromColumnName(tokens[i], 1); + // Get value + if (typeof(obj.options.data[position[1]]) != 'undefined' && typeof(obj.options.data[position[1]][position[0]]) != 'undefined') { + var value = obj.options.data[position[1]][position[0]]; + } else { + var value = ''; + } + // Get column data + if ((''+value).substr(0,1) == '=') { + if (formulaResults[tokens[i]]) { + value = formulaResults[tokens[i]]; + } else { + value = execute(value, position[0], position[1]); + formulaResults[tokens[i]] = value; + } + } + // Type! + if ((''+value).trim() == '') { + // Null + evalstring += "var " + tokens[i] + " = null;"; + } else { + if (value == Number(value) && obj.options.autoCasting == true) { + // Number + evalstring += "var " + tokens[i] + " = " + Number(value) + ";"; + } else { + // Trying any formatted number + var number = obj.parseNumber(value, position[0]) + if (obj.options.autoCasting == true && number) { + // Render as number + evalstring += "var " + tokens[i] + " = " + number + ";"; + } else { + // Render as string + evalstring += "var " + tokens[i] + " = '" + value + "';"; + } + } + } + } + } + } + + // Convert formula to javascript + try { + evalstring += "function COLUMN() { return parseInt(x) + 1; }; function ROW() { return parseInt(y) + 1; }; function CELL() { return parentId; }; function TABLE() { return obj; }; function VALUE(col, row) { return obj.records[row-1][col-1].innerHTML; }; function THISROWCELL(col) { var id = jexcel.getIdFromColumnName(col+(parseInt(y)+1), true); return obj.records[id[1]][id[0]].innerHTML; }"; + + var res = eval(evalstring + expression.substr(1)); + } catch (e) { + var res = '#ERROR'; + } + + return res; + } + } + + return execute(expression, x, y); + } + + /** + * Trying to extract a number from a string + */ + obj.parseNumber = function(value, columnNumber) { + // Decimal point + var decimal = columnNumber && obj.options.columns[columnNumber].decimal ? obj.options.columns[columnNumber].decimal : '.'; + + // Parse both parts of the number + var number = ('' + value); + number = number.split(decimal); + number[0] = number[0].match(/[+-]?[0-9]/g); + if (number[0]) { + number[0] = number[0].join(''); + } + if (number[1]) { + number[1] = number[1].match(/[0-9]*/g).join(''); + } + + // Is a valid number + if (number[0] && Number(number[0]) >= 0) { + if (! number[1]) { + var value = Number(number[0] + '.00'); + } else { + var value = Number(number[0] + '.' + number[1]); + } + } else { + var value = null; + } + + return value; + } + + /** + * Get row number + */ + obj.row = function(cell) { + } + + /** + * Get col number + */ + obj.col = function(cell) { + } + + obj.up = function(shiftKey, ctrlKey) { + if (shiftKey) { + if (obj.selectedCell[3] > 0) { + obj.up.visible(1, ctrlKey ? 0 : 1) + } + } else { + if (obj.selectedCell[1] > 0) { + obj.up.visible(0, ctrlKey ? 0 : 1) + } + obj.selectedCell[2] = obj.selectedCell[0]; + obj.selectedCell[3] = obj.selectedCell[1]; + } + + // Update selection + obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); + + // Change page + if (obj.options.lazyLoading == true) { + if (obj.selectedCell[1] == 0 || obj.selectedCell[3] == 0) { + obj.loadPage(0); + obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); + } else { + if (obj.loadValidation()) { + obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); + } else { + var item = parseInt(obj.tbody.firstChild.getAttribute('data-y')); + if (obj.selectedCell[1] - item < 30) { + obj.loadUp(); + obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); + } + } + } + } else if (obj.options.pagination > 0) { + var pageNumber = obj.whichPage(obj.selectedCell[3]); + if (pageNumber != obj.pageNumber) { + obj.page(pageNumber); + } + } + + obj.updateScroll(1); + } + + obj.up.visible = function(group, direction) { + if (group == 0) { + var x = parseInt(obj.selectedCell[0]); + var y = parseInt(obj.selectedCell[1]); + } else { + var x = parseInt(obj.selectedCell[2]); + var y = parseInt(obj.selectedCell[3]); + } + + if (direction == 0) { + for (var j = 0; j < y; j++) { + if (obj.records[j][x].style.display != 'none' && obj.rows[j].style.display != 'none') { + y = j; + break; + } + } + } else { + y = obj.up.get(x, y); + } + + if (group == 0) { + obj.selectedCell[0] = x; + obj.selectedCell[1] = y; + } else { + obj.selectedCell[2] = x; + obj.selectedCell[3] = y; + } + } + + obj.up.get = function(x, y) { + var x = parseInt(x); + var y = parseInt(y); + for (var j = (y - 1); j >= 0; j--) { + if (obj.records[j][x].style.display != 'none' && obj.rows[j].style.display != 'none') { + if (obj.records[j][x].getAttribute('data-merged')) { + if (obj.records[j][x] == obj.records[y][x]) { + continue; + } + } + y = j; + break; + } + } + + return y; + } + + obj.down = function(shiftKey, ctrlKey) { + if (shiftKey) { + if (obj.selectedCell[3] < obj.records.length - 1) { + obj.down.visible(1, ctrlKey ? 0 : 1) + } + } else { + if (obj.selectedCell[1] < obj.records.length - 1) { + obj.down.visible(0, ctrlKey ? 0 : 1) + } + obj.selectedCell[2] = obj.selectedCell[0]; + obj.selectedCell[3] = obj.selectedCell[1]; + } + + obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); + + // Change page + if (obj.options.lazyLoading == true) { + if ((obj.selectedCell[1] == obj.records.length - 1 || obj.selectedCell[3] == obj.records.length - 1)) { + obj.loadPage(-1); + obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); + } else { + if (obj.loadValidation()) { + obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); + } else { + var item = parseInt(obj.tbody.lastChild.getAttribute('data-y')); + if (item - obj.selectedCell[3] < 30) { + obj.loadDown(); + obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); + } + } + } + } else if (obj.options.pagination > 0) { + var pageNumber = obj.whichPage(obj.selectedCell[3]); + if (pageNumber != obj.pageNumber) { + obj.page(pageNumber); + } + } + + obj.updateScroll(3); + } + + obj.down.visible = function(group, direction) { + if (group == 0) { + var x = parseInt(obj.selectedCell[0]); + var y = parseInt(obj.selectedCell[1]); + } else { + var x = parseInt(obj.selectedCell[2]); + var y = parseInt(obj.selectedCell[3]); + } + + if (direction == 0) { + for (var j = obj.rows.length - 1; j > y; j--) { + if (obj.records[j][x].style.display != 'none' && obj.rows[j].style.display != 'none') { + y = j; + break; + } + } + } else { + y = obj.down.get(x, y); + } + + if (group == 0) { + obj.selectedCell[0] = x; + obj.selectedCell[1] = y; + } else { + obj.selectedCell[2] = x; + obj.selectedCell[3] = y; + } + } + + obj.down.get = function(x, y) { + var x = parseInt(x); + var y = parseInt(y); + for (var j = (y + 1); j < obj.rows.length; j++) { + if (obj.records[j][x].style.display != 'none' && obj.rows[j].style.display != 'none') { + if (obj.records[j][x].getAttribute('data-merged')) { + if (obj.records[j][x] == obj.records[y][x]) { + continue; + } + } + y = j; + break; + } + } + + return y; + } + + obj.right = function(shiftKey, ctrlKey) { + if (shiftKey) { + if (obj.selectedCell[2] < obj.headers.length - 1) { + obj.right.visible(1, ctrlKey ? 0 : 1) + } + } else { + if (obj.selectedCell[0] < obj.headers.length - 1) { + obj.right.visible(0, ctrlKey ? 0 : 1) + } + obj.selectedCell[2] = obj.selectedCell[0]; + obj.selectedCell[3] = obj.selectedCell[1]; + } + + obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); + obj.updateScroll(2); + } + + obj.right.visible = function(group, direction) { + if (group == 0) { + var x = parseInt(obj.selectedCell[0]); + var y = parseInt(obj.selectedCell[1]); + } else { + var x = parseInt(obj.selectedCell[2]); + var y = parseInt(obj.selectedCell[3]); + } + + if (direction == 0) { + for (var i = obj.headers.length - 1; i > x; i--) { + if (obj.records[y][i].style.display != 'none') { + x = i; + break; + } + } + } else { + x = obj.right.get(x, y); + } + + if (group == 0) { + obj.selectedCell[0] = x; + obj.selectedCell[1] = y; + } else { + obj.selectedCell[2] = x; + obj.selectedCell[3] = y; + } + } + + obj.right.get = function(x, y) { + var x = parseInt(x); + var y = parseInt(y); + + for (var i = (x + 1); i < obj.headers.length; i++) { + if (obj.records[y][i].style.display != 'none') { + if (obj.records[y][i].getAttribute('data-merged')) { + if (obj.records[y][i] == obj.records[y][x]) { + continue; + } + } + x = i; + break; + } + } + + return x; + } + + obj.left = function(shiftKey, ctrlKey) { + if (shiftKey) { + if (obj.selectedCell[2] > 0) { + obj.left.visible(1, ctrlKey ? 0 : 1) + } + } else { + if (obj.selectedCell[0] > 0) { + obj.left.visible(0, ctrlKey ? 0 : 1) + } + obj.selectedCell[2] = obj.selectedCell[0]; + obj.selectedCell[3] = obj.selectedCell[1]; + } + + obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); + obj.updateScroll(0); + } + + obj.left.visible = function(group, direction) { + if (group == 0) { + var x = parseInt(obj.selectedCell[0]); + var y = parseInt(obj.selectedCell[1]); + } else { + var x = parseInt(obj.selectedCell[2]); + var y = parseInt(obj.selectedCell[3]); + } + + if (direction == 0) { + for (var i = 0; i < x; i++) { + if (obj.records[y][i].style.display != 'none') { + x = i; + break; + } + } + } else { + x = obj.left.get(x, y); + } + + if (group == 0) { + obj.selectedCell[0] = x; + obj.selectedCell[1] = y; + } else { + obj.selectedCell[2] = x; + obj.selectedCell[3] = y; + } + } + + obj.left.get = function(x, y) { + var x = parseInt(x); + var y = parseInt(y); + for (var i = (x - 1); i >= 0; i--) { + if (obj.records[y][i].style.display != 'none') { + if (obj.records[y][i].getAttribute('data-merged')) { + if (obj.records[y][i] == obj.records[y][x]) { + continue; + } + } + x = i; + break; + } + } + + return x; + } + + obj.first = function(shiftKey, ctrlKey) { + if (shiftKey) { + if (ctrlKey) { + obj.selectedCell[3] = 0; + } else { + obj.left.visible(1, 0); + } + } else { + if (ctrlKey) { + obj.selectedCell[1] = 0; + } else { + obj.left.visible(0, 0); + } + obj.selectedCell[2] = obj.selectedCell[0]; + obj.selectedCell[3] = obj.selectedCell[1]; + } + + // Change page + if (obj.options.lazyLoading == true && (obj.selectedCell[1] == 0 || obj.selectedCell[3] == 0)) { + obj.loadPage(0); + } else if (obj.options.pagination > 0) { + var pageNumber = obj.whichPage(obj.selectedCell[3]); + if (pageNumber != obj.pageNumber) { + obj.page(pageNumber); + } + } + + obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); + obj.updateScroll(1); + } + + obj.last = function(shiftKey, ctrlKey) { + if (shiftKey) { + if (ctrlKey) { + obj.selectedCell[3] = obj.records.length - 1; + } else { + obj.right.visible(1, 0); + } + } else { + if (ctrlKey) { + obj.selectedCell[1] = obj.records.length - 1; + } else { + obj.right.visible(0, 0); + } + obj.selectedCell[2] = obj.selectedCell[0]; + obj.selectedCell[3] = obj.selectedCell[1]; + } + + // Change page + if (obj.options.lazyLoading == true && (obj.selectedCell[1] == obj.records.length - 1 || obj.selectedCell[3] == obj.records.length - 1)) { + obj.loadPage(-1); + } else if (obj.options.pagination > 0) { + var pageNumber = obj.whichPage(obj.selectedCell[3]); + if (pageNumber != obj.pageNumber) { + obj.page(pageNumber); + } + } + + obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); + obj.updateScroll(3); + } + + obj.selectAll = function() { + if (! obj.selectedCell) { + obj.selectedCell = []; + } + + obj.selectedCell[0] = 0; + obj.selectedCell[1] = 0; + obj.selectedCell[2] = obj.headers.length - 1; + obj.selectedCell[3] = obj.records.length - 1; + + obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); + } + + /** + * Go to a page in a lazyLoading + */ + obj.loadPage = function(pageNumber) { + // Search + if (obj.options.search == true && obj.results) { + var results = obj.results; + } else { + var results = obj.rows; + } + + // Per page + var quantityPerPage = 100; + + // pageNumber + if (pageNumber == null || pageNumber == -1) { + // Last page + pageNumber = Math.ceil(results.length / quantityPerPage) - 1; + } + + var startRow = (pageNumber * quantityPerPage); + var finalRow = (pageNumber * quantityPerPage) + quantityPerPage; + if (finalRow > results.length) { + finalRow = results.length; + } + startRow = finalRow - 100; + if (startRow < 0) { + startRow = 0; + } + + // Appeding items + for (var j = startRow; j < finalRow; j++) { + if (obj.options.search == true && obj.results) { + obj.tbody.appendChild(obj.rows[results[j]]); + } else { + obj.tbody.appendChild(obj.rows[j]); + } + + if (obj.tbody.children.length > quantityPerPage) { + obj.tbody.removeChild(obj.tbody.firstChild); + } + } + } + + obj.loadUp = function() { + // Search + if (obj.options.search == true && obj.results) { + var results = obj.results; + } else { + var results = obj.rows; + } + var test = 0; + if (results.length > 100) { + // Get the first element in the page + var item = parseInt(obj.tbody.firstChild.getAttribute('data-y')); + if (obj.options.search == true && obj.results) { + item = results.indexOf(item); + } + if (item > 0) { + for (var j = 0; j < 30; j++) { + item = item - 1; + if (item > -1) { + if (obj.options.search == true && obj.results) { + obj.tbody.insertBefore(obj.rows[results[item]], obj.tbody.firstChild); + } else { + obj.tbody.insertBefore(obj.rows[item], obj.tbody.firstChild); + } + if (obj.tbody.children.length > 100) { + obj.tbody.removeChild(obj.tbody.lastChild); + test = 1; + } + } + } + } + } + return test; + } + + obj.loadDown = function() { + // Search + if (obj.options.search == true && obj.results) { + var results = obj.results; + } else { + var results = obj.rows; + } + var test = 0; + if (results.length > 100) { + // Get the last element in the page + var item = parseInt(obj.tbody.lastChild.getAttribute('data-y')); + if (obj.options.search == true && obj.results) { + item = results.indexOf(item); + } + if (item < obj.rows.length - 1) { + for (var j = 0; j <= 30; j++) { + if (item < results.length) { + if (obj.options.search == true && obj.results) { + obj.tbody.appendChild(obj.rows[results[item]]); + } else { + obj.tbody.appendChild(obj.rows[item]); + } + if (obj.tbody.children.length > 100) { + obj.tbody.removeChild(obj.tbody.firstChild); + test = 1; + } + } + item = item + 1; + } + } + } + + return test; + } + + obj.loadValidation = function() { + if (obj.selectedCell) { + var currentPage = parseInt(obj.tbody.firstChild.getAttribute('data-y')) / 100; + var selectedPage = parseInt(obj.selectedCell[3] / 100); + var totalPages = parseInt(obj.rows.length / 100); + + if (currentPage != selectedPage && selectedPage <= totalPages) { + if (! Array.prototype.indexOf.call(obj.tbody.children, obj.rows[obj.selectedCell[3]])) { + obj.loadPage(selectedPage); + return true; + } + } + } + + return false; + } + + /** + * Reset search + */ + obj.resetSearch = function() { + obj.searchInput.value = ''; + obj.search(''); + obj.results = null; + } +
+ /** + * Search + */ + obj.search = function(query) { + // Query + if (query) { + var query = query.toLowerCase(); + } +
+ // Reset any filter
+ if (obj.options.filters) {
+ obj.resetFilters();
+ }
+ + // Reset selection + obj.resetSelection(); + + // Total of results + obj.pageNumber = 0; + obj.results = []; + + if (query) { + // Search filter + var search = function(item, query, index) { + for (var i = 0; i < item.length; i++) { + if ((''+item[i]).toLowerCase().search(query) >= 0 || + (''+obj.records[index][i].innerHTML).toLowerCase().search(query) >= 0) { + return true; + } + } + return false; + } + + // Result + var addToResult = function(k) { + if (obj.results.indexOf(k) == -1) { + obj.results.push(k); + } + } + + // Filter + var data = obj.options.data.filter(function(v, k) { + if (search(v, query, k)) { + // Merged rows found + var rows = obj.isRowMerged(k); + if (rows.length) { + for (var i = 0; i < rows.length; i++) { + var row = jexcel.getIdFromColumnName(rows[i], true); + for (var j = 0; j < obj.options.mergeCells[rows[i]][1]; j++) { + addToResult(row[1]+j); + } + } + } else { + // Normal row found + addToResult(k); + } + return true; + } else { + return false; + } + }); + } else { + obj.results = null; + } +
+ return obj.updateResult(); + } +
+ obj.updateResult = function() {
+ var total = 0;
+ var index = 0;
+
+ // Page 1
+ if (obj.options.lazyLoading == true) {
+ total = 100;
+ } else if (obj.options.pagination > 0) {
+ total = obj.options.pagination;
+ } else {
+ if (obj.results) {
+ total = obj.results.length;
+ } else {
+ total = obj.rows.length;
+ }
+ }
+
+ // Reset current nodes
+ while (obj.tbody.firstChild) {
+ obj.tbody.removeChild(obj.tbody.firstChild);
+ }
+
+ // Hide all records from the table
+ for (var j = 0; j < obj.rows.length; j++) {
+ if (! obj.results || obj.results.indexOf(j) > -1) {
+ if (index < total) {
+ obj.tbody.appendChild(obj.rows[j]);
+ index++;
+ }
+ obj.rows[j].style.display = '';
+ } else {
+ obj.rows[j].style.display = 'none';
+ }
+ }
+
+ // Update pagination
+ if (obj.options.pagination > 0) {
+ obj.updatePagination();
+ }
+
+ obj.updateCornerPosition();
+
+ return total;
+ }
+ + /** + * Which page the cell is + */ + obj.whichPage = function(cell) { + // Search + if (obj.options.search == true && obj.results) { + cell = obj.results.indexOf(cell); + } + + return (Math.ceil((parseInt(cell) + 1) / parseInt(obj.options.pagination))) - 1; + } + + /** + * Go to page + */ + obj.page = function(pageNumber) {
+ var oldPage = obj.pageNumber;
+ + // Search + if (obj.options.search == true && obj.results) { + var results = obj.results; + } else { + var results = obj.rows; + } + + // Per page + var quantityPerPage = parseInt(obj.options.pagination); + + // pageNumber + if (pageNumber == null || pageNumber == -1) { + // Last page + pageNumber = Math.ceil(results.length / quantityPerPage) - 1; + } + + // Page number + obj.pageNumber = pageNumber; + + var startRow = (pageNumber * quantityPerPage); + var finalRow = (pageNumber * quantityPerPage) + quantityPerPage; + if (finalRow > results.length) { + finalRow = results.length; + } + if (startRow < 0) { + startRow = 0; + } + + // Reset container + while (obj.tbody.firstChild) { + obj.tbody.removeChild(obj.tbody.firstChild); + } + + // Appeding items + for (var j = startRow; j < finalRow; j++) { + if (obj.options.search == true && obj.results) { + obj.tbody.appendChild(obj.rows[results[j]]); + } else { + obj.tbody.appendChild(obj.rows[j]); + } + } + + if (obj.options.pagination > 0) { + obj.updatePagination(); + } + + // Update corner position + obj.updateCornerPosition();
+
+ // Events
+ obj.dispatch('onchangepage', el, pageNumber, oldPage);
+ } + + /** + * Update the pagination + */ + obj.updatePagination = function() { + // Reset container + obj.pagination.children[0].innerHTML = ''; + obj.pagination.children[1].innerHTML = ''; + + // Start pagination + if (obj.options.pagination) { + // Searchable + if (obj.options.search == true && obj.results) { + var results = obj.results.length; + } else { + var results = obj.rows.length; + } + + if (! results) { + // No records found + obj.pagination.children[0].innerHTML = obj.options.text.noRecordsFound; + } else { + // Pagination container + var quantyOfPages = Math.ceil(results / obj.options.pagination); + + if (obj.pageNumber < 6) { + var startNumber = 1; + var finalNumber = quantyOfPages < 10 ? quantyOfPages : 10; + } else if (quantyOfPages - obj.pageNumber < 5) { + var startNumber = quantyOfPages - 9; + var finalNumber = quantyOfPages; + if (startNumber < 1) { + startNumber = 1; + } + } else { + var startNumber = obj.pageNumber - 4; + var finalNumber = obj.pageNumber + 5; + } + + // First + if (startNumber > 1) { + var paginationItem = document.createElement('div'); + paginationItem.className = 'jexcel_page'; + paginationItem.innerHTML = '<'; + paginationItem.title = 1; + obj.pagination.children[1].appendChild(paginationItem); + } + + // Get page links + for (var i = startNumber; i <= finalNumber; i++) { + var paginationItem = document.createElement('div'); + paginationItem.className = 'jexcel_page'; + paginationItem.innerHTML = i; + obj.pagination.children[1].appendChild(paginationItem); + + if (obj.pageNumber == (i-1)) { + paginationItem.classList.add('jexcel_page_selected'); + } + } + + // Last + if (finalNumber < quantyOfPages) { + var paginationItem = document.createElement('div'); + paginationItem.className = 'jexcel_page'; + paginationItem.innerHTML = '>'; + paginationItem.title = quantyOfPages; + obj.pagination.children[1].appendChild(paginationItem); + } + + // Text + var format = function(format) { + var args = Array.prototype.slice.call(arguments, 1); + return format.replace(/{(\d+)}/g, function(match, number) { + return typeof args[number] != 'undefined' + ? args[number] + : match + ; + }); + }; + + obj.pagination.children[0].innerHTML = format(obj.options.text.showingPage, obj.pageNumber + 1, quantyOfPages) + } + } + } + + /** + * Download CSV table + * + * @return null + */ + obj.download = function(includeHeaders) { + if (obj.options.allowExport == false) { + console.error('Export not allowed'); + } else { + // Data + var data = ''; + if (includeHeaders == true || obj.options.includeHeadersOnDownload == true) { + data += obj.getHeaders(); + data += "\r\n"; + }
+
+ // Get data
+ data += obj.copy(false, obj.options.csvDelimiter, true);
+
+ // Download element
+ var blob = new Blob(["\uFEFF"+data], {type: 'text/csv;charset=utf-8;'});
+
+ // IE Compatibility
+ if (window.navigator && window.navigator.msSaveOrOpenBlob) {
+ window.navigator.msSaveOrOpenBlob(blob, obj.options.csvFileName + '.csv');
+ } else { + // Download element + var pom = document.createElement('a'); + var url = URL.createObjectURL(blob); + pom.href = url; + pom.setAttribute('download', obj.options.csvFileName + '.csv'); + document.body.appendChild(pom); + pom.click(); + pom.parentNode.removeChild(pom);
+ } + } + } + + /** + * Initializes a new history record for undo/redo + * + * @return null + */ + obj.setHistory = function(changes) { + if (obj.ignoreHistory != true) { + // Increment and get the current history index + var index = ++obj.historyIndex; + + // Slice the array to discard undone changes + obj.history = (obj.history = obj.history.slice(0, index + 1)); + + // Keep history + obj.history[index] = changes; + } + } + + /** + * Copy method + * + * @param bool highlighted - Get only highlighted cells + * @param delimiter - \t default to keep compatibility with excel + * @return string value + */ + obj.copy = function(highlighted, delimiter, returnData) { + if (! delimiter) { + delimiter = "\t"; + } + + // Controls
+ var header = []; + var col = []; + var colLabel = []; + var row = []; + var rowLabel = []; + var x = obj.options.data[0].length + var y = obj.options.data.length + var tmp = ''; + var copyHeader = obj.options.includeHeadersOnCopy;
+
+ // Reset container + obj.style = []; + + // Go through the columns to get the data + for (var j = 0; j < y; j++) { + col = []; + colLabel = []; + + for (var i = 0; i < x; i++) { + // If cell is highlighted + if (! highlighted || obj.records[j][i].classList.contains('highlight')) {
+ if (copyHeader == true) {
+ header.push(obj.headers[i].innerText);
+ }
+ // Values + var value = obj.options.data[j][i]; + if (value.match && (value.match(/,/g) || value.match(/\n/) || value.match(/\"/))) { + value = value.replace(new RegExp('"', 'g'), '""'); + value = '"' + value + '"'; + } + col.push(value); + + // Labels
+ if (obj.options.columns[i].type == 'checkbox' || obj.options.columns[i].type == 'radio') {
+ var label = value; + } else {
+ if (obj.options.stripHTMLOnCopy == true) {
+ var label = obj.records[j][i].innerText;
+ } else {
+ var label = obj.records[j][i].innerHTML;
+ }
+ if (label.match && (label.match(/,/g) || label.match(/\n/) || label.match(/\"/))) {
+ // Scape double quotes
+ label = label.replace(new RegExp('"', 'g'), '""');
+ label = '"' + label + '"';
+ }
+ } + colLabel.push(label); + + // Get style + tmp = obj.records[j][i].getAttribute('style');
+ tmp = tmp.replace('display: none;', ''); + obj.style.push(tmp ? tmp : ''); + } + } + + if (col.length) {
+ if (copyHeader) {
+ row.push(header.join(delimiter));
+ } + row.push(col.join(delimiter)); + } + if (colLabel.length) {
+ if (copyHeader) {
+ rowLabel.push(header.join(delimiter));
+ } + rowLabel.push(colLabel.join(delimiter)); + }
+ copyHeader = false; + } + + // Final string + var str = row.join("\r\n"); + var strLabel = rowLabel.join("\r\n");
+ + // Create a hidden textarea to copy the values + if (! returnData) { + if (obj.options.copyCompatibility == true) { + obj.textarea.value = strLabel; + } else { + obj.textarea.value = str; + } + obj.textarea.select(); + document.execCommand("copy"); + } + + // Keep data + if (obj.options.copyCompatibility == true) { + obj.data = strLabel; + } else { + obj.data = str; + } + // Keep non visible information + obj.hashString = obj.hash(obj.data); +
+ // Any exiting border should go
+ obj.removeCopyingSelection();
+
+ // Border
+ if (obj.highlighted) {
+ for (var i = 0; i < obj.highlighted.length; i++) {
+ obj.highlighted[i].classList.add('copying');
+ if (obj.highlighted[i].classList.contains('highlight-left')) {
+ obj.highlighted[i].classList.add('copying-left');
+ }
+ if (obj.highlighted[i].classList.contains('highlight-right')) {
+ obj.highlighted[i].classList.add('copying-right');
+ }
+ if (obj.highlighted[i].classList.contains('highlight-top')) {
+ obj.highlighted[i].classList.add('copying-top');
+ }
+ if (obj.highlighted[i].classList.contains('highlight-bottom')) {
+ obj.highlighted[i].classList.add('copying-bottom');
+ }
+ }
+ }
+
+ // Paste event
+ obj.dispatch('oncopy', el, obj.options.copyCompatibility == true ? rowLabel : row, obj.hashString);
+ + return obj.data; + } + + /** + * jExcel paste method + * + * @param integer row number + * @return string value + */ + obj.paste = function(x, y, data) { + // Paste filter + var ret = obj.dispatch('onbeforepaste', el, data, x, y);
+ + if (ret === false) {
+ return false;
+ } else if (ret) {
+ var data = ret;
+ } + + // Controls + var hash = obj.hash(data); + var style = (hash == obj.hashString) ? obj.style : null; + + // Depending on the behavior + if (obj.options.copyCompatibility == true && hash == obj.hashString) { + var data = obj.data; + } + + // Split new line + var data = obj.parseCSV(data, "\t"); + + if (x != null && y != null && data) { + // Records + var i = 0; + var j = 0; + var records = []; + var newStyle = {}; + var oldStyle = {}; + var styleIndex = 0; + + // Index + var colIndex = parseInt(x); + var rowIndex = parseInt(y); + var row = null; + + // Go through the columns to get the data + while (row = data[j]) { + i = 0; + colIndex = parseInt(x); + + while (row[i] != null) { + // Update and keep history + var record = obj.updateCell(colIndex, rowIndex, row[i]); + // Keep history + records.push(record); + // Update all formulas in the chain + obj.updateFormulaChain(colIndex, rowIndex, records); + // Style + if (style && style[styleIndex]) { + var columnName = jexcel.getColumnNameFromId([colIndex, rowIndex]); + newStyle[columnName] = style[styleIndex]; + oldStyle[columnName] = obj.getStyle(columnName); + obj.records[rowIndex][colIndex].setAttribute('style', style[styleIndex]); + styleIndex++ + } + i++; + if (row[i] != null) { + if (colIndex >= obj.headers.length - 1) { + obj.insertColumn(); + } + colIndex = obj.right.get(colIndex, rowIndex); + } + } + + j++; + if (data[j]) { + if (rowIndex >= obj.rows.length-1) { + obj.insertRow(); + } + rowIndex = obj.down.get(x, rowIndex); + } + } + + // Select the new cells + obj.updateSelectionFromCoords(x, y, colIndex, rowIndex); + + // Update history + obj.setHistory({ + action:'setValue', + records:records, + selection:obj.selectedCell, + newStyle:newStyle, + oldStyle:oldStyle, + }); + + // Update table + obj.updateTable(); + + // Paste event
+ obj.dispatch('onpaste', el, data); + + // On after changes + obj.onafterchanges(el, records); + }
+
+ obj.removeCopyingSelection(); + } +
+ /**
+ * Remove copying border
+ */
+ obj.removeCopyingSelection = function() {
+ var copying = document.querySelectorAll('.jexcel .copying');
+ for (var i = 0; i < copying.length; i++) {
+ copying[i].classList.remove('copying');
+ copying[i].classList.remove('copying-left');
+ copying[i].classList.remove('copying-right');
+ copying[i].classList.remove('copying-top');
+ copying[i].classList.remove('copying-bottom');
+ }
+ }
+ + /** + * Process row + */ + obj.historyProcessRow = function(type, historyRecord) { + var rowIndex = (! historyRecord.insertBefore) ? historyRecord.rowNumber + 1 : historyRecord.rowNumber; + + if (obj.options.search == true) { + if (obj.results && obj.results.length != obj.rows.length) { + obj.resetSearch(); + } + } + + // Remove row + if (type == 1) { + var numOfRows = historyRecord.numOfRows; + // Remove nodes + for (var j = rowIndex; j < (numOfRows + rowIndex); j++) { + obj.rows[j].parentNode.removeChild(obj.rows[j]); + } + // Remove references + obj.records.splice(rowIndex, numOfRows); + obj.options.data.splice(rowIndex, numOfRows); + obj.rows.splice(rowIndex, numOfRows); + + obj.conditionalSelectionUpdate(1, rowIndex, (numOfRows + rowIndex) - 1); + } else { + // Insert data + obj.records = jexcel.injectArray(obj.records, rowIndex, historyRecord.rowRecords); + obj.options.data = jexcel.injectArray(obj.options.data, rowIndex, historyRecord.rowData); + obj.rows = jexcel.injectArray(obj.rows, rowIndex, historyRecord.rowNode); + // Insert nodes + var index = 0 + for (var j = rowIndex; j < (historyRecord.numOfRows + rowIndex); j++) { + obj.tbody.insertBefore(historyRecord.rowNode[index], obj.tbody.children[j]); + index++; + } + } + + // Respect pagination + if (obj.options.pagination > 0) { + obj.page(obj.pageNumber); + } + + obj.updateTableReferences(); + } + + /** + * Process column + */ + obj.historyProcessColumn = function(type, historyRecord) { + var columnIndex = (! historyRecord.insertBefore) ? historyRecord.columnNumber + 1 : historyRecord.columnNumber; + + // Remove column + if (type == 1) { + var numOfColumns = historyRecord.numOfColumns; + + obj.options.columns.splice(columnIndex, numOfColumns); + for (var i = columnIndex; i < (numOfColumns + columnIndex); i++) { + obj.headers[i].parentNode.removeChild(obj.headers[i]); + obj.colgroup[i].parentNode.removeChild(obj.colgroup[i]); + } + obj.headers.splice(columnIndex, numOfColumns); + obj.colgroup.splice(columnIndex, numOfColumns); + for (var j = 0; j < historyRecord.data.length; j++) { + for (var i = columnIndex; i < (numOfColumns + columnIndex); i++) { + obj.records[j][i].parentNode.removeChild(obj.records[j][i]); + } + obj.records[j].splice(columnIndex, numOfColumns); + obj.options.data[j].splice(columnIndex, numOfColumns); + }
+ // Process footers
+ if (obj.options.footers) { + for (var j = 0; j < obj.options.footers.length; j++) {
+ obj.options.footers[j].splice(columnIndex, numOfColumns); + }
+ } + } else { + // Insert data + obj.options.columns = jexcel.injectArray(obj.options.columns, columnIndex, historyRecord.columns); + obj.headers = jexcel.injectArray(obj.headers, columnIndex, historyRecord.headers); + obj.colgroup = jexcel.injectArray(obj.colgroup, columnIndex, historyRecord.colgroup); +
+ var index = 0 + for (var i = columnIndex; i < (historyRecord.numOfColumns + columnIndex); i++) { + obj.headerContainer.insertBefore(historyRecord.headers[index], obj.headerContainer.children[i+1]); + obj.colgroupContainer.insertBefore(historyRecord.colgroup[index], obj.colgroupContainer.children[i+1]); + index++; + } + + for (var j = 0; j < historyRecord.data.length; j++) { + obj.options.data[j] = jexcel.injectArray(obj.options.data[j], columnIndex, historyRecord.data[j]); + obj.records[j] = jexcel.injectArray(obj.records[j], columnIndex, historyRecord.records[j]); + var index = 0 + for (var i = columnIndex; i < (historyRecord.numOfColumns + columnIndex); i++) { + obj.rows[j].insertBefore(historyRecord.records[j][index], obj.rows[j].children[i+1]); + index++; + } + }
+ // Process footers
+ if (obj.options.footers) {
+ for (var j = 0; j < obj.options.footers.length; j++) {
+ obj.options.footers[j] = jexcel.injectArray(obj.options.footers[j], columnIndex, historyRecord.footers[j]);
+ }
+ } + } + + // Adjust nested headers + if (obj.options.nestedHeaders && obj.options.nestedHeaders.length > 0) { + // Flexible way to handle nestedheaders + if (obj.options.nestedHeaders[0] && obj.options.nestedHeaders[0][0]) { + for (var j = 0; j < obj.options.nestedHeaders.length; j++) { + if (type == 1) { + var colspan = parseInt(obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan) - historyRecord.numOfColumns; + } else { + var colspan = parseInt(obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan) + historyRecord.numOfColumns; + } + obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan = colspan; + obj.thead.children[j].children[obj.thead.children[j].children.length-1].setAttribute('colspan', colspan); + } + } else { + if (type == 1) { + var colspan = parseInt(obj.options.nestedHeaders[0].colspan) - historyRecord.numOfColumns; + } else { + var colspan = parseInt(obj.options.nestedHeaders[0].colspan) + historyRecord.numOfColumns; + } + obj.options.nestedHeaders[0].colspan = colspan; + obj.thead.children[0].children[obj.thead.children[0].children.length-1].setAttribute('colspan', colspan); + } + } + + obj.updateTableReferences(); + } + + /** + * Undo last action + */ + obj.undo = function() { + // Ignore events and history + var ignoreEvents = obj.ignoreEvents ? true : false; + var ignoreHistory = obj.ignoreHistory ? true : false; + + obj.ignoreEvents = true; + obj.ignoreHistory = true; + + // Records + var records = []; + + // Update cells + if (obj.historyIndex >= 0) { + // History + var historyRecord = obj.history[obj.historyIndex--]; + + if (historyRecord.action == 'insertRow') { + obj.historyProcessRow(1, historyRecord); + } else if (historyRecord.action == 'deleteRow') { + obj.historyProcessRow(0, historyRecord); + } else if (historyRecord.action == 'insertColumn') { + obj.historyProcessColumn(1, historyRecord); + } else if (historyRecord.action == 'deleteColumn') { + obj.historyProcessColumn(0, historyRecord); + } else if (historyRecord.action == 'moveRow') { + obj.moveRow(historyRecord.newValue, historyRecord.oldValue); + } else if (historyRecord.action == 'moveColumn') { + obj.moveColumn(historyRecord.newValue, historyRecord.oldValue); + } else if (historyRecord.action == 'setMerge') { + obj.removeMerge(historyRecord.column, historyRecord.data); + } else if (historyRecord.action == 'setStyle') { + obj.setStyle(historyRecord.oldValue, null, null, 1); + } else if (historyRecord.action == 'setWidth') { + obj.setWidth(historyRecord.column, historyRecord.oldValue); + } else if (historyRecord.action == 'setHeight') { + obj.setHeight(historyRecord.row, historyRecord.oldValue); + } else if (historyRecord.action == 'setHeader') { + obj.setHeader(historyRecord.column, historyRecord.oldValue); + } else if (historyRecord.action == 'setComments') { + obj.setComments(historyRecord.column, historyRecord.oldValue[0], historyRecord.oldValue[1]); + } else if (historyRecord.action == 'orderBy') { + var rows = []; + for (var j = 0; j < historyRecord.rows.length; j++) { + rows[historyRecord.rows[j]] = j; + } + obj.updateOrderArrow(historyRecord.column, historyRecord.order ? 0 : 1); + obj.updateOrder(rows); + } else if (historyRecord.action == 'setValue') {
+ // Redo for changes in cells
+ for (var i = 0; i < historyRecord.records.length; i++) {
+ records.push({
+ x: historyRecord.records[i].x,
+ y: historyRecord.records[i].y,
+ newValue: historyRecord.records[i].oldValue,
+ });
+
+ if (historyRecord.oldStyle) {
+ obj.resetStyle(historyRecord.oldStyle);
+ }
+ }
+ // Update records
+ obj.setValue(records);
+
+ // Update selection
+ if (historyRecord.selection) {
+ obj.updateSelectionFromCoords(historyRecord.selection[0], historyRecord.selection[1], historyRecord.selection[2], historyRecord.selection[3]);
+ } + } + } + obj.ignoreEvents = ignoreEvents; + obj.ignoreHistory = ignoreHistory; +
+ // Events
+ obj.dispatch('onundo', el, historyRecord); + } + + /** + * Redo previously undone action + */ + obj.redo = function() { + // Ignore events and history + var ignoreEvents = obj.ignoreEvents ? true : false; + var ignoreHistory = obj.ignoreHistory ? true : false; + + obj.ignoreEvents = true; + obj.ignoreHistory = true; + + // Records + var records = []; + + // Update cells + if (obj.historyIndex < obj.history.length - 1) { + // History + var historyRecord = obj.history[++obj.historyIndex]; + + if (historyRecord.action == 'insertRow') { + obj.historyProcessRow(0, historyRecord); + } else if (historyRecord.action == 'deleteRow') { + obj.historyProcessRow(1, historyRecord); + } else if (historyRecord.action == 'insertColumn') { + obj.historyProcessColumn(0, historyRecord); + } else if (historyRecord.action == 'deleteColumn') { + obj.historyProcessColumn(1, historyRecord); + } else if (historyRecord.action == 'moveRow') { + obj.moveRow(historyRecord.oldValue, historyRecord.newValue); + } else if (historyRecord.action == 'moveColumn') { + obj.moveColumn(historyRecord.oldValue, historyRecord.newValue); + } else if (historyRecord.action == 'setMerge') { + obj.setMerge(historyRecord.column, historyRecord.colspan, historyRecord.rowspan, 1); + } else if (historyRecord.action == 'setStyle') { + obj.setStyle(historyRecord.newValue, null, null, 1); + } else if (historyRecord.action == 'setWidth') { + obj.setWidth(historyRecord.column, historyRecord.newValue); + } else if (historyRecord.action == 'setHeight') { + obj.setHeight(historyRecord.row, historyRecord.newValue); + } else if (historyRecord.action == 'setHeader') { + obj.setHeader(historyRecord.column, historyRecord.newValue); + } else if (historyRecord.action == 'setComments') { + obj.setComments(historyRecord.column, historyRecord.newValue[0], historyRecord.newValue[1]); + } else if (historyRecord.action == 'orderBy') { + obj.updateOrderArrow(historyRecord.column, historyRecord.order); + obj.updateOrder(historyRecord.rows); + } else if (historyRecord.action == 'setValue') {
+ obj.setValue(historyRecord.records);
+ // Redo for changes in cells
+ for (var i = 0; i < historyRecord.records.length; i++) {
+ if (historyRecord.oldStyle) {
+ obj.resetStyle(historyRecord.newStyle);
+ }
+ }
+ // Update selection
+ if (historyRecord.selection) {
+ obj.updateSelectionFromCoords(historyRecord.selection[0], historyRecord.selection[1], historyRecord.selection[2], historyRecord.selection[3]);
+ } + } + } + obj.ignoreEvents = ignoreEvents; + obj.ignoreHistory = ignoreHistory; +
+ // Events
+ obj.dispatch('onredo', el, historyRecord); + } + + /** + * Get dropdown value from key + */ + obj.getDropDownValue = function(column, key) { + var value = []; + + if (obj.options.columns[column] && obj.options.columns[column].source) { + // Create array from source + var combo = []; + var source = obj.options.columns[column].source; + + for (var i = 0; i < source.length; i++) { + if (typeof(source[i]) == 'object') { + combo[source[i].id] = source[i].name; + } else { + combo[source[i]] = source[i]; + } + } + + // Guarantee single multiple compatibility + var keys = Array.isArray(key) ? key : ('' + key).split(';'); + + for (var i = 0; i < keys.length; i++) { + if (typeof(keys[i]) === 'object') { + value.push(combo[keys[i].id]); + } else { + if (combo[keys[i]]) { + value.push(combo[keys[i]]); + } + } + } + } else { + console.error('Invalid column'); + } + + return (value.length > 0) ? value.join('; ') : ''; + } + + /** + * From starckoverflow contributions + */ + obj.parseCSV = function(str, delimiter) { + // Remove last line break + str = str.replace(/\r?\n$|\r$|\n$/g, ""); + // Last caracter is the delimiter + if (str.charCodeAt(str.length-1) == 9) { + str += "\0"; + } + // user-supplied delimeter or default comma + delimiter = (delimiter || ","); + + var arr = []; + var quote = false; // true means we're inside a quoted field + // iterate over each character, keep track of current row and column (of the returned array) + for (var row = 0, col = 0, c = 0; c < str.length; c++) { + var cc = str[c], nc = str[c+1]; + arr[row] = arr[row] || []; + arr[row][col] = arr[row][col] || ''; + + // If the current character is a quotation mark, and we're inside a quoted field, and the next character is also a quotation mark, add a quotation mark to the current column and skip the next character + if (cc == '"' && quote && nc == '"') { arr[row][col] += cc; ++c; continue; } + + // If it's just one quotation mark, begin/end quoted field + if (cc == '"') { quote = !quote; continue; } + + // If it's a comma and we're not in a quoted field, move on to the next column + if (cc == delimiter && !quote) { ++col; continue; } + + // If it's a newline (CRLF) and we're not in a quoted field, skip the next character and move on to the next row and move to column 0 of that new row + if (cc == '\r' && nc == '\n' && !quote) { ++row; col = 0; ++c; continue; } + + // If it's a newline (LF or CR) and we're not in a quoted field, move on to the next row and move to column 0 of that new row + if (cc == '\n' && !quote) { ++row; col = 0; continue; } + if (cc == '\r' && !quote) { ++row; col = 0; continue; } + + // Otherwise, append the current character to the current column + arr[row][col] += cc; + } + return arr; + } + + obj.hash = function(str) { + var hash = 0, i, chr; + + if (str.length === 0) { + return hash; + } else { + for (i = 0; i < str.length; i++) { + chr = str.charCodeAt(i); + hash = ((hash << 5) - hash) + chr; + hash |= 0; + } + } + return hash; + } + + obj.onafterchanges = function(el, records) {
+ // Events
+ obj.dispatch('onafterchanges', el, records);
+ } +
+ obj.destroy = function() {
+ jexcel.destroy(el);
+ }
+ + /** + * Initialization method + */ + obj.init = function() { + jexcel.current = obj; + + // Build handlers + if (typeof(jexcel.build) == 'function') { + if (obj.options.root) {
+ jexcel.build(obj.options.root);
+ } else {
+ jexcel.build(document);
+ jexcel.build = null;
+ } + } +
+ // Event
+ el.setAttribute('tabindex', 1);
+ el.addEventListener('focus', function(e) {
+ if (jexcel.current && ! obj.selectedCell) {
+ obj.updateSelectionFromCoords(0,0,0,0);
+ obj.left();
+ }
+ });
+ + // Load the table data based on an CSV file + if (obj.options.csv) { + // Loading + if (obj.options.loadingSpin == true) { + jSuites.loading.show(); + } + + // Load CSV file + jSuites.ajax({ + url: obj.options.csv, + method: 'GET', + dataType: 'text', + success: function(result) { + // Convert data + var newData = obj.parseCSV(result, obj.options.csvDelimiter) + + // Headers + if (obj.options.csvHeaders == true && newData.length > 0) { + var headers = newData.shift(); + for(var i = 0; i < headers.length; i++) { + if (! obj.options.columns[i]) { + obj.options.columns[i] = { type:'text', align:obj.options.defaultColAlign, width:obj.options.defaultColWidth }; + } + // Precedence over pre-configurated titles + if (typeof obj.options.columns[i].title === 'undefined') { + obj.options.columns[i].title = headers[i]; + } + } + } + // Data + obj.options.data = newData; + // Prepare table + obj.prepareTable(); + // Hide spin + if (obj.options.loadingSpin == true) { + jSuites.loading.hide(); + } + } + }); + } else if (obj.options.url) { + // Loading + if (obj.options.loadingSpin == true) { + jSuites.loading.show(); + } + + jSuites.ajax({ + url: obj.options.url, + method: 'GET', + dataType: 'json', + success: function(result) { + // Data + obj.options.data = (result.data) ? result.data : result; + // Prepare table + obj.prepareTable(); + // Hide spin + if (obj.options.loadingSpin == true) { + jSuites.loading.hide(); + } + } + }); + } else { + // Prepare table + obj.prepareTable(); + } + } + + // Context menu + if (options && options.contextMenu != null) { + obj.options.contextMenu = options.contextMenu; + } else { + obj.options.contextMenu = function(el, x, y, e) { + var items = []; + + if (y == null) { + // Insert a new column + if (obj.options.allowInsertColumn == true) { + items.push({ + title:obj.options.text.insertANewColumnBefore, + onclick:function() { + obj.insertColumn(1, parseInt(x), 1); + } + }); + } + + if (obj.options.allowInsertColumn == true) { + items.push({ + title:obj.options.text.insertANewColumnAfter, + onclick:function() { + obj.insertColumn(1, parseInt(x), 0); + } + }); + } + + // Delete a column + if (obj.options.allowDeleteColumn == true) { + items.push({ + title:obj.options.text.deleteSelectedColumns, + onclick:function() { + obj.deleteColumn(obj.getSelectedColumns().length ? undefined : parseInt(x)); + } + }); + } + + // Rename column + if (obj.options.allowRenameColumn == true) { + items.push({ + title:obj.options.text.renameThisColumn, + onclick:function() { + obj.setHeader(x); + } + }); + } + + // Sorting + if (obj.options.columnSorting == true) { + // Line + items.push({ type:'line' }); + + items.push({ + title:obj.options.text.orderAscending, + onclick:function() { + obj.orderBy(x, 0); + } + }); + items.push({ + title:obj.options.text.orderDescending, + onclick:function() { + obj.orderBy(x, 1); + } + }); + } + } else { + // Insert new row + if (obj.options.allowInsertRow == true) { + items.push({ + title:obj.options.text.insertANewRowBefore, + onclick:function() { + obj.insertRow(1, parseInt(y), 1); + } + }); + + items.push({ + title:obj.options.text.insertANewRowAfter, + onclick:function() { + obj.insertRow(1, parseInt(y)); + } + }); + } + + if (obj.options.allowDeleteRow == true) { + items.push({ + title:obj.options.text.deleteSelectedRows, + onclick:function() { + obj.deleteRow(obj.getSelectedRows().length ? undefined : parseInt(y)); + } + }); + } + + if (x) { + if (obj.options.allowComments == true) { + items.push({ type:'line' }); + + var title = obj.records[y][x].getAttribute('title') || '';
+
+ items.push({ + title: title ? obj.options.text.editComments : obj.options.text.addComments, + onclick:function() {
+ var comment = prompt(obj.options.text.comments, title);
+ if (comment) {
+ obj.setComments([ x, y ], comment);
+ } + } + }); + + if (title) { + items.push({ + title:obj.options.text.clearComments, + onclick:function() { + obj.setComments([ x, y ], ''); + } + }); + } + } + } + } + + // Line + items.push({ type:'line' }); + + // Copy + items.push({ + title:obj.options.text.copy, + shortcut:'Ctrl + C', + onclick:function() { + obj.copy(true); + } + }); + + // Paste + if (navigator && navigator.clipboard) { + items.push({ + title:obj.options.text.paste, + shortcut:'Ctrl + V', + onclick:function() { + if (obj.selectedCell) { + navigator.clipboard.readText().then(function(text) { + if (text) { + jexcel.current.paste(obj.selectedCell[0], obj.selectedCell[1], text); + } + }); + } + } + }); + } + + // Save + if (obj.options.allowExport) { + items.push({ + title: obj.options.text.saveAs, + shortcut: 'Ctrl + S', + onclick: function () { + obj.download(); + } + }); + } + + // About + if (obj.options.about) { + items.push({ + title:obj.options.text.about, + onclick:function() { + alert(obj.options.about); + } + }); + } + + return items; + } + } + + obj.scrollControls = function(e) {
+ obj.wheelControls();
+ + if (obj.options.freezeColumns > 0 && obj.content.scrollLeft != scrollLeft) {
+ obj.updateFreezePosition();
+ }
+ + // Close editor + if (obj.options.lazyLoading == true || obj.options.tableOverflow == true) { + if (obj.edition && e.target.className.substr(0,9) != 'jdropdown') { + obj.closeEditor(obj.edition[0], true); + } + } + } +
+ obj.wheelControls = function(e) {
+ if (obj.options.lazyLoading == true) {
+ if (jexcel.timeControlLoading == null) {
+ jexcel.timeControlLoading = setTimeout(function() {
+ if (obj.content.scrollTop + obj.content.clientHeight >= obj.content.scrollHeight) {
+ if (obj.loadDown()) {
+ if (obj.content.scrollTop + obj.content.clientHeight > obj.content.scrollHeight - 10) {
+ obj.content.scrollTop = obj.content.scrollTop - obj.content.clientHeight;
+ }
+ obj.updateCornerPosition();
+ }
+ } else if (obj.content.scrollTop <= obj.content.clientHeight) {
+ if (obj.loadUp()) {
+ if (obj.content.scrollTop < 10) {
+ obj.content.scrollTop = obj.content.scrollTop + obj.content.clientHeight;
+ }
+ obj.updateCornerPosition();
+ }
+ }
+
+ jexcel.timeControlLoading = null;
+ }, 100);
+ }
+ }
+ }
+
+ // Get width of all freezed cells together
+ obj.getFreezeWidth = function() {
+ var width = 0;
+ if (obj.options.freezeColumns > 0) {
+ for (var i = 0; i < obj.options.freezeColumns; i++) {
+ width += parseInt(obj.options.columns[i].width);
+ }
+ }
+ return width;
+ }
+
+ var scrollLeft = 0;
+
+ obj.updateFreezePosition = function() {
+ scrollLeft = obj.content.scrollLeft;
+ var width = 0;
+ if (scrollLeft > 50) {
+ for (var i = 0; i < obj.options.freezeColumns; i++) {
+ if (i > 0) {
+ width += parseInt(obj.options.columns[i-1].width);
+ }
+ obj.headers[i].classList.add('jexcel_freezed');
+ obj.headers[i].style.left = width + 'px';
+ for (var j = 0; j < obj.rows.length; j++) {
+ if (obj.rows[j] && obj.records[j][i]) {
+ var shifted = (scrollLeft + (i > 0 ? obj.records[j][i-1].style.width : 0)) - 51 + 'px';
+ obj.records[j][i].classList.add('jexcel_freezed');
+ obj.records[j][i].style.left = shifted;
+ }
+ }
+ }
+ } else {
+ for (var i = 0; i < obj.options.freezeColumns; i++) {
+ obj.headers[i].classList.remove('jexcel_freezed');
+ obj.headers[i].style.left = '';
+ for (var j = 0; j < obj.rows.length; j++) {
+ if (obj.records[j][i]) {
+ obj.records[j][i].classList.remove('jexcel_freezed');
+ obj.records[j][i].style.left = '';
+ }
+ }
+ }
+ }
+
+ // Place the corner in the correct place
+ obj.updateCornerPosition();
+ }
+ + el.addEventListener("DOMMouseScroll", obj.wheelControls); + el.addEventListener("mousewheel", obj.wheelControls); + + el.jexcel = obj; + + obj.init(); + + return obj; + }); + + jexcel.current = null; + jexcel.timeControl = null; + jexcel.timeControlLoading = null; + + jexcel.destroy = function(element, destroyEventHandlers) { + if (element.jexcel) {
+ var root = element.jexcel.options.root ? element.jexcel.options.root : document; + element.removeEventListener("DOMMouseScroll", element.jexcel.scrollControls); + element.removeEventListener("mousewheel", element.jexcel.scrollControls); + element.jexcel = null; + element.innerHTML = ''; + + if (destroyEventHandlers) { + root.removeEventListener("mouseup", jexcel.mouseUpControls); + root.removeEventListener("mousedown", jexcel.mouseDownControls); + root.removeEventListener("mousemove", jexcel.mouseMoveControls); + root.removeEventListener("mouseover", jexcel.mouseOverControls); + root.removeEventListener("dblclick", jexcel.doubleClickControls); + root.removeEventListener("paste", jexcel.pasteControls); + root.removeEventListener("contextmenu", jexcel.contextMenuControls); + root.removeEventListener("touchstart", jexcel.touchStartControls); + root.removeEventListener("touchend", jexcel.touchEndControls); + root.removeEventListener("touchcancel", jexcel.touchEndControls);
+ document.removeEventListener("keydown", jexcel.keyDownControls); + jexcel = null; + } + } + } + + jexcel.build = function(root) { + root.addEventListener("mouseup", jexcel.mouseUpControls); + root.addEventListener("mousedown", jexcel.mouseDownControls); + root.addEventListener("mousemove", jexcel.mouseMoveControls); + root.addEventListener("mouseover", jexcel.mouseOverControls); + root.addEventListener("dblclick", jexcel.doubleClickControls); + root.addEventListener("paste", jexcel.pasteControls); + root.addEventListener("contextmenu", jexcel.contextMenuControls); + root.addEventListener("touchstart", jexcel.touchStartControls); + root.addEventListener("touchend", jexcel.touchEndControls); + root.addEventListener("touchcancel", jexcel.touchEndControls); + root.addEventListener("touchmove", jexcel.touchEndControls);
+ document.addEventListener("keydown", jexcel.keyDownControls); + } + + /** + * Events + */ + jexcel.keyDownControls = function(e) { + if (jexcel.current) { + if (jexcel.current.edition) { + if (e.which == 27) { + // Escape + if (jexcel.current.edition) { + // Exit without saving + jexcel.current.closeEditor(jexcel.current.edition[0], false); + } + e.preventDefault(); + } else if (e.which == 13) { + // Enter + if (jexcel.current.options.columns[jexcel.current.edition[2]].type == 'calendar') { + jexcel.current.closeEditor(jexcel.current.edition[0], true); + } else if (jexcel.current.options.columns[jexcel.current.edition[2]].type == 'dropdown' || + jexcel.current.options.columns[jexcel.current.edition[2]].type == 'autocomplete') { + // Do nothing + } else { + // Alt enter -> do not close editor + if ((jexcel.current.options.wordWrap == true || + jexcel.current.options.columns[jexcel.current.edition[2]].wordWrap == true || + jexcel.current.options.data[jexcel.current.edition[3]][jexcel.current.edition[2]].length > 200) && e.altKey) { + // Add new line to the editor + var editorTextarea = jexcel.current.edition[0].children[0]; + var editorValue = jexcel.current.edition[0].children[0].value; + var editorIndexOf = editorTextarea.selectionStart; + editorValue = editorValue.slice(0, editorIndexOf) + "\n" + editorValue.slice(editorIndexOf); + editorTextarea.value = editorValue; + editorTextarea.focus(); + editorTextarea.selectionStart = editorIndexOf + 1; + editorTextarea.selectionEnd = editorIndexOf + 1; + } else { + jexcel.current.edition[0].children[0].blur(); + } + } + } else if (e.which == 9) { + // Tab + if (jexcel.current.options.columns[jexcel.current.edition[2]].type == 'calendar') { + jexcel.current.closeEditor(jexcel.current.edition[0], true); + } else { + jexcel.current.edition[0].children[0].blur(); + } + } + } + + if (! jexcel.current.edition && jexcel.current.selectedCell) {
+ // Which key + if (e.which == 37) { + jexcel.current.left(e.shiftKey, e.ctrlKey); + e.preventDefault(); + } else if (e.which == 39) { + jexcel.current.right(e.shiftKey, e.ctrlKey); + e.preventDefault(); + } else if (e.which == 38) { + jexcel.current.up(e.shiftKey, e.ctrlKey); + e.preventDefault(); + } else if (e.which == 40) { + jexcel.current.down(e.shiftKey, e.ctrlKey); + e.preventDefault(); + } else if (e.which == 36) { + jexcel.current.first(e.shiftKey, e.ctrlKey); + e.preventDefault(); + } else if (e.which == 35) { + jexcel.current.last(e.shiftKey, e.ctrlKey); + e.preventDefault(); + } else if (e.which == 32) { + if (jexcel.current.options.editable == true) { + jexcel.current.setCheckRadioValue(); + } + e.preventDefault(); + } else if (e.which == 46) { + // Delete + if (jexcel.current.options.editable == true) { + if (jexcel.current.selectedRow) { + if (jexcel.current.options.allowDeleteRow == true) { + if (confirm(jexcel.current.options.text.areYouSureToDeleteTheSelectedRows)) { + jexcel.current.deleteRow(); + } + } + } else if (jexcel.current.selectedHeader) { + if (jexcel.current.options.allowDeleteColumn == true) { + if (confirm(jexcel.current.options.text.areYouSureToDeleteTheSelectedColumns)) { + jexcel.current.deleteColumn(); + } + } + } else { + // Change value + jexcel.current.setValue(jexcel.current.highlighted, ''); + } + } + } else if (e.which == 13) { + // Move cursor + if (e.shiftKey) { + jexcel.current.up(); + } else { + if (jexcel.current.options.allowInsertRow == true) { + if (jexcel.current.options.allowManualInsertRow == true) { + if (jexcel.current.selectedCell[1] == jexcel.current.options.data.length - 1) { + // New record in case selectedCell in the last row + jexcel.current.insertRow(); + } + } + } + + jexcel.current.down(); + } + e.preventDefault(); + } else if (e.which == 9) { + // Tab + if (e.shiftKey) { + jexcel.current.left(); + } else { + if (jexcel.current.options.allowInsertColumn == true) { + if (jexcel.current.options.allowManualInsertColumn == true) { + if (jexcel.current.selectedCell[0] == jexcel.current.options.data[0].length - 1) { + // New record in case selectedCell in the last column + jexcel.current.insertColumn(); + } + } + } + + jexcel.current.right(); + } + e.preventDefault(); + } else { + if ((e.ctrlKey || e.metaKey) && ! e.shiftKey) { + if (e.which == 65) { + // Ctrl + A + jexcel.current.selectAll(); + e.preventDefault(); + } else if (e.which == 83) { + // Ctrl + S + jexcel.current.download(); + e.preventDefault(); + } else if (e.which == 89) { + // Ctrl + Y + jexcel.current.redo(); + e.preventDefault(); + } else if (e.which == 90) { + // Ctrl + Z + jexcel.current.undo(); + e.preventDefault(); + } else if (e.which == 67) { + // Ctrl + C + jexcel.current.copy(true); + e.preventDefault(); + } else if (e.which == 67) { + // Ctrl + C + jexcel.current.copy(true); + e.preventDefault(); + } else if (e.which == 88) { + // Ctrl + X + if (jexcel.current.options.editable == true) { + jexcel.cutControls(); + } else { + jexcel.copyControls(); + } + e.preventDefault(); + } else if (e.which == 86) { + // Ctrl + V + jexcel.pasteControls(); + } + } else { + if (jexcel.current.selectedCell) { + if (jexcel.current.options.editable == true) { + var rowId = jexcel.current.selectedCell[1]; + var columnId = jexcel.current.selectedCell[0]; + + // If is not readonly + if (jexcel.current.options.columns[columnId].type != 'readonly') { + // Characters able to start a edition + if (e.keyCode == 32) { + // Space + if (jexcel.current.options.columns[columnId].type == 'checkbox' || + jexcel.current.options.columns[columnId].type == 'radio') { + e.preventDefault(); + } else { + // Start edition + jexcel.current.openEditor(jexcel.current.records[rowId][columnId], true); + } + } else if (e.keyCode == 113) { + // Start edition with current content F2 + jexcel.current.openEditor(jexcel.current.records[rowId][columnId], false); + } else if ((e.keyCode == 8) || + (e.keyCode >= 48 && e.keyCode <= 57) || + (e.keyCode >= 96 && e.keyCode <= 111) || + (e.keyCode >= 187 && e.keyCode <= 190) || + ((String.fromCharCode(e.keyCode) == e.key || String.fromCharCode(e.keyCode).toLowerCase() == e.key.toLowerCase()) && jexcel.validLetter(String.fromCharCode(e.keyCode)))) { + // Start edition + jexcel.current.openEditor(jexcel.current.records[rowId][columnId], true); + // Prevent entries in the calendar + if (jexcel.current.options.columns[columnId].type == 'calendar') { + e.preventDefault(); + } + } + } + } + } + } + } + } else {
+ if (e.target.classList.contains('jexcel_search')) { + if (jexcel.timeControl) { + clearTimeout(jexcel.timeControl); + } + + jexcel.timeControl = setTimeout(function() { + jexcel.current.search(e.target.value); + }, 200); + } + } + } + } + + jexcel.isMouseAction = false; + + jexcel.mouseDownControls = function(e) { + e = e || window.event; + if (e.buttons) { + var mouseButton = e.buttons; + } else if (e.button) { + var mouseButton = e.button; + } else { + var mouseButton = e.which; + } + + // Get elements + var jexcelTable = jexcel.getElement(e.target); + + if (jexcelTable[0]) { + if (jexcel.current != jexcelTable[0].jexcel) { + if (jexcel.current) {
+ if (jexcel.current.edition) {
+ jexcel.current.closeEditor(jexcel.current.edition[0], true);
+ } + jexcel.current.resetSelection(); + } + jexcel.current = jexcelTable[0].jexcel; + } + } else { + if (jexcel.current) {
+ if (jexcel.current.edition) {
+ jexcel.current.closeEditor(jexcel.current.edition[0], true);
+ }
+ + jexcel.current.resetSelection(true); + jexcel.current = null; + } + } + + if (jexcel.current && mouseButton == 1) { + if (e.target.classList.contains('jexcel_selectall')) { + if (jexcel.current) { + jexcel.current.selectAll(); + } + } else if (e.target.classList.contains('jexcel_corner')) { + if (jexcel.current.options.editable == true) { + jexcel.current.selectedCorner = true; + } + } else { + // Header found + if (jexcelTable[1] == 1) { + var columnId = e.target.getAttribute('data-x'); + if (columnId) { + // Update cursor + var info = e.target.getBoundingClientRect(); + if (jexcel.current.options.columnResize == true && info.width - e.offsetX < 6) { + // Resize helper + jexcel.current.resizing = { + mousePosition: e.pageX, + column: columnId, + width: info.width, + }; + + // Border indication + jexcel.current.headers[columnId].classList.add('resizing'); + for (var j = 0; j < jexcel.current.records.length; j++) { + if (jexcel.current.records[j][columnId]) { + jexcel.current.records[j][columnId].classList.add('resizing'); + } + } + } else if (jexcel.current.options.columnDrag == true && info.height - e.offsetY < 6) { + if (jexcel.current.isColMerged(columnId).length) { + console.error('JEXCEL: This column is part of a merged cell.'); + } else { + // Reset selection + jexcel.current.resetSelection(); + // Drag helper + jexcel.current.dragging = { + element: e.target, + column:columnId, + destination:columnId, + }; + // Border indication + jexcel.current.headers[columnId].classList.add('dragging'); + for (var j = 0; j < jexcel.current.records.length; j++) { + if (jexcel.current.records[j][columnId]) { + jexcel.current.records[j][columnId].classList.add('dragging'); + } + } + } + } else { + if (jexcel.current.selectedHeader && (e.shiftKey || e.ctrlKey)) { + var o = jexcel.current.selectedHeader; + var d = columnId; + } else { + // Press to rename + if (jexcel.current.selectedHeader == columnId && jexcel.current.options.allowRenameColumn == true) { + jexcel.timeControl = setTimeout(function() { + jexcel.current.setHeader(columnId); + }, 800); + } + + // Keep track of which header was selected first + jexcel.current.selectedHeader = columnId; + + // Update selection single column + var o = columnId; + var d = columnId; + } + + // Update selection + jexcel.current.updateSelectionFromCoords(o, 0, d, jexcel.current.options.data.length - 1); + } + } else { + if (e.target.parentNode.classList.contains('jexcel_nested')) {
+ if (e.target.getAttribute('data-column')) { + var column = e.target.getAttribute('data-column').split(',');
+ var c1 = parseInt(column[0]);
+ var c2 = parseInt(column[column.length-1]);
+ } else {
+ var c1 = 0;
+ var c2 = jexcel.current.options.columns.length - 1;
+ } + jexcel.current.updateSelectionFromCoords(c1, 0, c2, jexcel.current.options.data.length - 1); + } + } + } else { + jexcel.current.selectedHeader = false; + } + + // Body found + if (jexcelTable[1] == 2) { + var rowId = e.target.getAttribute('data-y'); + + if (e.target.classList.contains('jexcel_row')) { + var info = e.target.getBoundingClientRect(); + if (jexcel.current.options.rowResize == true && info.height - e.offsetY < 6) { + // Resize helper + jexcel.current.resizing = { + element: e.target.parentNode, + mousePosition: e.pageY, + row: rowId, + height: info.height, + }; + // Border indication + e.target.parentNode.classList.add('resizing'); + } else if (jexcel.current.options.rowDrag == true && info.width - e.offsetX < 6) { + if (jexcel.current.isRowMerged(rowId).length) { + console.error('JEXCEL: This row is part of a merged cell'); + } else if (jexcel.current.options.search == true && jexcel.current.results) { + console.error('JEXCEL: Please clear your search before perform this action'); + } else { + // Reset selection + jexcel.current.resetSelection(); + // Drag helper + jexcel.current.dragging = { + element: e.target.parentNode, + row:rowId, + destination:rowId, + }; + // Border indication + e.target.parentNode.classList.add('dragging'); + } + } else { + if (jexcel.current.selectedRow && (e.shiftKey || e.ctrlKey)) { + var o = jexcel.current.selectedRow; + var d = rowId; + } else { + // Keep track of which header was selected first + jexcel.current.selectedRow = rowId; + + // Update selection single column + var o = rowId; + var d = rowId; + } + + // Update selection + jexcel.current.updateSelectionFromCoords(0, o, jexcel.current.options.data[0].length - 1, d); + } + } else {
+ // Jclose + if (e.target.classList.contains('jclose') && e.target.clientWidth - e.offsetX < 50 && e.offsetY < 50) { + jexcel.current.closeEditor(jexcel.current.edition[0], true); + } else { + var getCellCoords = function(element) { + var x = element.getAttribute('data-x'); + var y = element.getAttribute('data-y'); + if (x && y) { + return [x, y]; + } else { + if (element.parentNode) { + return getCellCoords(element.parentNode); + } + } + }; + + var position = getCellCoords(e.target); + if (position) {
+ + var columnId = position[0]; + var rowId = position[1]; + // Close edition + if (jexcel.current.edition) { + if (jexcel.current.edition[2] != columnId || jexcel.current.edition[3] != rowId) { + jexcel.current.closeEditor(jexcel.current.edition[0], true); + }
+ }
+ + if (! jexcel.current.edition) { + // Update cell selection + if (e.shiftKey) { + jexcel.current.updateSelectionFromCoords(jexcel.current.selectedCell[0], jexcel.current.selectedCell[1], columnId, rowId); + } else { + jexcel.current.updateSelectionFromCoords(columnId, rowId); + } + } + + // No full row selected + jexcel.current.selectedHeader = null; + jexcel.current.selectedRow = null; + } + } + } + } else { + jexcel.current.selectedRow = false; + } + + // Pagination + if (e.target.classList.contains('jexcel_page')) { + if (e.target.innerText == '<') { + jexcel.current.page(0); + } else if (e.target.innerText == '>') { + jexcel.current.page(e.target.getAttribute('title') - 1); + } else { + jexcel.current.page(e.target.innerText - 1); + } + } + } + + if (jexcel.current.edition) { + jexcel.isMouseAction = false; + } else { + jexcel.isMouseAction = true; + } + } else { + jexcel.isMouseAction = false; + } + } + + jexcel.mouseUpControls = function(e) { + if (jexcel.current) { + // Update cell size + if (jexcel.current.resizing) { + // Columns to be updated + if (jexcel.current.resizing.column) {
+ // New width
+ var newWidth = jexcel.current.colgroup[jexcel.current.resizing.column].getAttribute('width');
+ // Columns
+ var columns = jexcel.current.getSelectedColumns();
+ if (columns.length > 1) {
+ var currentWidth = [];
+ for (var i = 0; i < columns.length; i++) {
+ currentWidth.push(parseInt(jexcel.current.colgroup[columns[i]].getAttribute('width')));
+ }
+ // Previous width
+ var index = columns.indexOf(parseInt(jexcel.current.resizing.column));
+ currentWidth[index] = jexcel.current.resizing.width;
+ jexcel.current.setWidth(columns, newWidth, currentWidth);
+ } else {
+ jexcel.current.setWidth(jexcel.current.resizing.column, newWidth, jexcel.current.resizing.width);
+ }
+ // Remove border
+ jexcel.current.headers[jexcel.current.resizing.column].classList.remove('resizing');
+ for (var j = 0; j < jexcel.current.records.length; j++) {
+ if (jexcel.current.records[j][jexcel.current.resizing.column]) {
+ jexcel.current.records[j][jexcel.current.resizing.column].classList.remove('resizing');
+ }
+ } + } else { + // Remove Class + jexcel.current.rows[jexcel.current.resizing.row].children[0].classList.remove('resizing'); + var newHeight = jexcel.current.rows[jexcel.current.resizing.row].getAttribute('height'); + jexcel.current.setHeight(jexcel.current.resizing.row, newHeight, jexcel.current.resizing.height); + // Remove border + jexcel.current.resizing.element.classList.remove('resizing'); + } + // Reset resizing helper + jexcel.current.resizing = null; + } else if (jexcel.current.dragging) { + // Reset dragging helper + if (jexcel.current.dragging) { + if (jexcel.current.dragging.column) { + // Target + var columnId = e.target.getAttribute('data-x'); + // Remove move style + jexcel.current.headers[jexcel.current.dragging.column].classList.remove('dragging'); + for (var j = 0; j < jexcel.current.rows.length; j++) { + if (jexcel.current.records[j][jexcel.current.dragging.column]) { + jexcel.current.records[j][jexcel.current.dragging.column].classList.remove('dragging'); + } + } + for (var i = 0; i < jexcel.current.headers.length; i++) { + jexcel.current.headers[i].classList.remove('dragging-left'); + jexcel.current.headers[i].classList.remove('dragging-right'); + } + // Update position + if (columnId) { + if (jexcel.current.dragging.column != jexcel.current.dragging.destination) { + jexcel.current.moveColumn(jexcel.current.dragging.column, jexcel.current.dragging.destination); + } + } + } else {
+ if (jexcel.current.dragging.element.nextSibling) {
+ var position = parseInt(jexcel.current.dragging.element.nextSibling.getAttribute('data-y'));
+ if (jexcel.current.dragging.row < position) {
+ position -= 1;
+ }
+ } else {
+ var position = parseInt(jexcel.current.dragging.element.previousSibling.getAttribute('data-y'));
+ }
+ if (jexcel.current.dragging.row != jexcel.current.dragging.destination) { + jexcel.current.moveRow(jexcel.current.dragging.row, position, true); + } + jexcel.current.dragging.element.classList.remove('dragging'); + } + jexcel.current.dragging = null; + } + } else { + // Close any corner selection + if (jexcel.current.selectedCorner) { + jexcel.current.selectedCorner = false; + + // Data to be copied + if (jexcel.current.selection.length > 0) { + // Copy data + jexcel.current.copyData(jexcel.current.selection[0], jexcel.current.selection[jexcel.current.selection.length - 1]); + + // Remove selection + jexcel.current.removeCopySelection(); + } + } + } + } + + // Clear any time control + if (jexcel.timeControl) { + clearTimeout(jexcel.timeControl); + jexcel.timeControl = null; + } + + // Mouse up + jexcel.isMouseAction = false; + } + + // Mouse move controls + jexcel.mouseMoveControls = function(e) { + e = e || window.event; + if (e.buttons) { + var mouseButton = e.buttons; + } else if (e.button) { + var mouseButton = e.button; + } else { + var mouseButton = e.which; + } + + if (! mouseButton) { + jexcel.isMouseAction = false; + } + + if (jexcel.current) { + if (jexcel.isMouseAction == true) { + // Resizing is ongoing + if (jexcel.current.resizing) { + if (jexcel.current.resizing.column) { + var width = e.pageX - jexcel.current.resizing.mousePosition; + + if (jexcel.current.resizing.width + width > 0) { + var tempWidth = jexcel.current.resizing.width + width; + jexcel.current.colgroup[jexcel.current.resizing.column].setAttribute('width', tempWidth); + + jexcel.current.updateCornerPosition(); + } + } else { + var height = e.pageY - jexcel.current.resizing.mousePosition; + + if (jexcel.current.resizing.height + height > 0) { + var tempHeight = jexcel.current.resizing.height + height; + jexcel.current.rows[jexcel.current.resizing.row].setAttribute('height', tempHeight); + + jexcel.current.updateCornerPosition(); + } + } + } + } else { + var x = e.target.getAttribute('data-x'); + var y = e.target.getAttribute('data-y'); + var rect = e.target.getBoundingClientRect(); + + if (jexcel.current.cursor) { + jexcel.current.cursor.style.cursor = ''; + jexcel.current.cursor = null; + } + + if (e.target.parentNode.parentNode && e.target.parentNode.parentNode.className) { + if (e.target.parentNode.parentNode.classList.contains('resizable')) { + if (e.target && x && ! y && (rect.width - (e.clientX - rect.left) < 6)) { + jexcel.current.cursor = e.target; + jexcel.current.cursor.style.cursor = 'col-resize'; + } else if (e.target && ! x && y && (rect.height - (e.clientY - rect.top) < 6)) { + jexcel.current.cursor = e.target; + jexcel.current.cursor.style.cursor = 'row-resize'; + } + } + + if (e.target.parentNode.parentNode.classList.contains('draggable')) { + if (e.target && ! x && y && (rect.width - (e.clientX - rect.left) < 6)) { + jexcel.current.cursor = e.target; + jexcel.current.cursor.style.cursor = 'move'; + } else if (e.target && x && ! y && (rect.height - (e.clientY - rect.top) < 6)) { + jexcel.current.cursor = e.target; + jexcel.current.cursor.style.cursor = 'move'; + } + } + } + } + } + } + + jexcel.mouseOverControls = function(e) { + e = e || window.event; + if (e.buttons) { + var mouseButton = e.buttons; + } else if (e.button) { + var mouseButton = e.button; + } else { + var mouseButton = e.which; + } + + if (! mouseButton) { + jexcel.isMouseAction = false; + } + + if (jexcel.current && jexcel.isMouseAction == true) { + // Get elements + var jexcelTable = jexcel.getElement(e.target); + + if (jexcelTable[0]) { + // Avoid cross reference + if (jexcel.current != jexcelTable[0].jexcel) { + if (jexcel.current) { + return false; + } + } + + var columnId = e.target.getAttribute('data-x'); + var rowId = e.target.getAttribute('data-y'); + + if (jexcel.current.dragging) { + if (jexcel.current.dragging.column) { + if (columnId) { + if (jexcel.current.isColMerged(columnId).length) { + console.error('JEXCEL: This column is part of a merged cell.'); + } else { + for (var i = 0; i < jexcel.current.headers.length; i++) { + jexcel.current.headers[i].classList.remove('dragging-left'); + jexcel.current.headers[i].classList.remove('dragging-right'); + } + + if (jexcel.current.dragging.column == columnId) { + jexcel.current.dragging.destination = parseInt(columnId); + } else { + if (e.target.clientWidth / 2 > e.offsetX) { + if (jexcel.current.dragging.column < columnId) { + jexcel.current.dragging.destination = parseInt(columnId) - 1; + } else { + jexcel.current.dragging.destination = parseInt(columnId); + } + jexcel.current.headers[columnId].classList.add('dragging-left'); + } else { + if (jexcel.current.dragging.column < columnId) { + jexcel.current.dragging.destination = parseInt(columnId); + } else { + jexcel.current.dragging.destination = parseInt(columnId) + 1; + } + jexcel.current.headers[columnId].classList.add('dragging-right'); + } + } + } + } + } else { + if (rowId) { + if (jexcel.current.isRowMerged(rowId).length) { + console.error('JEXCEL: This row is part of a merged cell.'); + } else { + var target = (e.target.clientHeight / 2 > e.offsetY) ? e.target.parentNode.nextSibling : e.target.parentNode;
+ if (jexcel.current.dragging.element != target) {
+ e.target.parentNode.parentNode.insertBefore(jexcel.current.dragging.element, target);
+ jexcel.current.dragging.destination = Array.prototype.indexOf.call(jexcel.current.dragging.element.parentNode.children, jexcel.current.dragging.element);
+ }
+ } + } + } + } else if (jexcel.current.resizing) { + } else { + // Header found + if (jexcelTable[1] == 1) { + if (jexcel.current.selectedHeader) { + var columnId = e.target.getAttribute('data-x'); + var o = jexcel.current.selectedHeader; + var d = columnId; + // Update selection + jexcel.current.updateSelectionFromCoords(o, 0, d, jexcel.current.options.data.length - 1); + } + } + + // Body found + if (jexcelTable[1] == 2) { + if (e.target.classList.contains('jexcel_row')) { + if (jexcel.current.selectedRow) { + var o = jexcel.current.selectedRow; + var d = rowId; + // Update selection + jexcel.current.updateSelectionFromCoords(0, o, jexcel.current.options.data[0].length - 1, d); + } + } else { + // Do not select edtion is in progress + if (! jexcel.current.edition) { + if (columnId && rowId) { + if (jexcel.current.selectedCorner) { + jexcel.current.updateCopySelection(columnId, rowId); + } else { + if (jexcel.current.selectedCell) { + jexcel.current.updateSelectionFromCoords(jexcel.current.selectedCell[0], jexcel.current.selectedCell[1], columnId, rowId); + } + } + } + } + } + } + } + } + } + + // Clear any time control + if (jexcel.timeControl) { + clearTimeout(jexcel.timeControl); + jexcel.timeControl = null; + } + } + + /** + * Double click event handler: controls the double click in the corner, cell edition or column re-ordering. + */ + jexcel.doubleClickControls = function(e) { + // Jexcel is selected + if (jexcel.current) { + // Corner action + if (e.target.classList.contains('jexcel_corner')) { + // Any selected cells + if (jexcel.current.highlighted.length > 0) { + // Copy from this + var x1 = jexcel.current.highlighted[0].getAttribute('data-x'); + var y1 = parseInt(jexcel.current.highlighted[jexcel.current.highlighted.length - 1].getAttribute('data-y')) + 1; + // Until this + var x2 = jexcel.current.highlighted[jexcel.current.highlighted.length - 1].getAttribute('data-x'); + var y2 = jexcel.current.records.length - 1 + // Execute copy + jexcel.current.copyData(jexcel.current.records[y1][x1], jexcel.current.records[y2][x2]); + }
+ } else if (e.target.classList.contains('jexcel_column_filter')) {
+ // Column
+ var columnId = e.target.getAttribute('data-x');
+ // Open filter
+ jexcel.current.openFilter(columnId);
+
+ } else { + // Get table + var jexcelTable = jexcel.getElement(e.target); + + // Double click over header + if (jexcelTable[1] == 1 && jexcel.current.options.columnSorting == true) { + // Check valid column header coords + var columnId = e.target.getAttribute('data-x'); + if (columnId) { + jexcel.current.orderBy(columnId); + } + } + + // Double click over body + if (jexcelTable[1] == 2 && jexcel.current.options.editable == true) { + if (! jexcel.current.edition) { + var getCellCoords = function(element) { + if (element.parentNode) { + var x = element.getAttribute('data-x'); + var y = element.getAttribute('data-y'); + if (x && y) { + return element; + } else { + return getCellCoords(element.parentNode); + } + } + } + var cell = getCellCoords(e.target); + if (cell && cell.classList.contains('highlight')) {
+ jexcel.current.openEditor(cell);
+ } + } + } + } + } + } + + jexcel.copyControls = function(e) { + if (jexcel.current && jexcel.copyControls.enabled) { + if (! jexcel.current.edition) { + jexcel.current.copy(true); + } + } + } + + jexcel.cutControls = function(e) { + if (jexcel.current) { + if (! jexcel.current.edition) { + jexcel.current.copy(true); + if (jexcel.current.options.editable == true) { + jexcel.current.setValue(jexcel.current.highlighted, ''); + } + } + } + } + + jexcel.pasteControls = function(e) { + if (jexcel.current && jexcel.current.selectedCell) { + if (! jexcel.current.edition) { + if (jexcel.current.options.editable == true) { + if (e && e.clipboardData) { + jexcel.current.paste(jexcel.current.selectedCell[0], jexcel.current.selectedCell[1], e.clipboardData.getData('text')); + e.preventDefault(); + } else if (window.clipboardData) { + jexcel.current.paste(jexcel.current.selectedCell[0], jexcel.current.selectedCell[1], window.clipboardData.getData('text')); + } + } + } + } + } + + jexcel.contextMenuControls = function(e) { + e = e || window.event; + if ("buttons" in e) { + var mouseButton = e.buttons; + } else { + var mouseButton = e.which || e.button; + } + + if (jexcel.current) { + if (jexcel.current.edition) { + e.preventDefault(); + } else if (jexcel.current.options.contextMenu) { + jexcel.current.contextMenu.contextmenu.close(); + + if (jexcel.current) { + var x = e.target.getAttribute('data-x'); + var y = e.target.getAttribute('data-y'); + + if (x || y) { + // Table found + var items = jexcel.current.options.contextMenu(jexcel.current, x, y, e); + // The id is depending on header and body + jexcel.current.contextMenu.contextmenu.open(e, items); + // Avoid the real one + e.preventDefault(); + } + } + } + } + } + + jexcel.touchStartControls = function(e) { + var jexcelTable = jexcel.getElement(e.target); + + if (jexcelTable[0]) { + if (jexcel.current != jexcelTable[0].jexcel) { + if (jexcel.current) { + jexcel.current.resetSelection(); + } + jexcel.current = jexcelTable[0].jexcel; + } + } else { + if (jexcel.current) { + jexcel.current.resetSelection(); + jexcel.current = null; + } + } + + if (jexcel.current) { + if (! jexcel.current.edition) { + var columnId = e.target.getAttribute('data-x'); + var rowId = e.target.getAttribute('data-y'); + + if (columnId && rowId) { + jexcel.current.updateSelectionFromCoords(columnId, rowId); + + jexcel.timeControl = setTimeout(function() {
+ // Keep temporary reference to the element
+ if (jexcel.current.options.columns[columnId].type == 'color') {
+ jexcel.tmpElement = null;
+ } else {
+ jexcel.tmpElement = e.target;
+ }
+ jexcel.current.openEditor(e.target, false, e); + }, 500); + } + } + } + } + + jexcel.touchEndControls = function(e) {
+ // Clear any time control + if (jexcel.timeControl) { + clearTimeout(jexcel.timeControl); + jexcel.timeControl = null;
+ // Element
+ if (jexcel.tmpElement && jexcel.tmpElement.children[0].tagName == 'INPUT') {
+ jexcel.tmpElement.children[0].focus();
+ }
+ jexcel.tmpElement = null; + } + }
+
+ /**
+ * Jexcel extensions
+ */
+
+ jexcel.tabs = function(tabs, result) {
+ var instances = [];
+ // Create tab container
+ if (! tabs.classList.contains('jexcel_tabs')) {
+ tabs.innerHTML = '';
+ tabs.classList.add('jexcel_tabs')
+ tabs.jexcel = [];
+
+ var div = document.createElement('div');
+ var headers = tabs.appendChild(div);
+ var div = document.createElement('div');
+ var content = tabs.appendChild(div);
+ } else {
+ var headers = tabs.children[0];
+ var content = tabs.children[1];
+ }
+
+ var spreadsheet = []
+ var link = [];
+ for (var i = 0; i < result.length; i++) {
+ // Spreadsheet container
+ spreadsheet[i] = document.createElement('div');
+ spreadsheet[i].classList.add('jexcel_tab');
+ var worksheet = jexcel(spreadsheet[i], result[i]);
+ content.appendChild(spreadsheet[i]);
+ instances[i] = tabs.jexcel.push(worksheet);
+
+ // Tab link
+ link[i] = document.createElement('div');
+ link[i].classList.add('jexcel_tab_link');
+ link[i].setAttribute('data-spreadsheet', tabs.jexcel.length-1);
+ link[i].innerHTML = result[i].sheetName;
+ link[i].onclick = function() {
+ for (var j = 0; j < headers.children.length; j++) {
+ headers.children[j].classList.remove('selected');
+ content.children[j].style.display = 'none';
+ }
+ var i = this.getAttribute('data-spreadsheet');
+ content.children[i].style.display = 'block';
+ headers.children[i].classList.add('selected')
+ }
+ headers.appendChild(link[i]);
+ }
+
+ // First tab
+ for (var j = 0; j < headers.children.length; j++) {
+ headers.children[j].classList.remove('selected');
+ content.children[j].style.display = 'none';
+ }
+ headers.children[headers.children.length - 1].classList.add('selected');
+ content.children[headers.children.length - 1].style.display = 'block';
+
+ return instances;
+ }
+
+ // Compability to older versions
+ jexcel.createTabs = jexcel.tabs;
+
+ jexcel.fromSpreadsheet = function(file, __callback) {
+ var convert = function(workbook) {
+ var spreadsheets = [];
+ workbook.SheetNames.forEach(function(sheetName) {
+ var spreadsheet = {};
+ spreadsheet.rows = [];
+ spreadsheet.columns = [];
+ spreadsheet.data = [];
+ spreadsheet.style = {};
+ spreadsheet.sheetName = sheetName;
+
+ // Column widths
+ var temp = workbook.Sheets[sheetName]['!cols'];
+ if (temp && temp.length) {
+ for (var i = 0; i < temp.length; i++) {
+ spreadsheet.columns[i] = {};
+ if (temp[i] && temp[i].wpx) {
+ spreadsheet.columns[i].width = temp[i].wpx + 'px';
+ }
+ }
+ }
+ // Rows heights
+ var temp = workbook.Sheets[sheetName]['!rows'];
+ if (temp && temp.length) {
+ for (var i = 0; i < temp.length; i++) {
+ if (temp[i] && temp[i].hpx) {
+ spreadsheet.rows[i] = {};
+ spreadsheet.rows[i].height = temp[i].hpx + 'px';
+ }
+ }
+ }
+ // Merge cells
+ var temp = workbook.Sheets[sheetName]['!merges'];
+ if (temp && temp.length > 0) {
+ spreadsheet.mergeCells = [];
+ for (var i = 0; i < temp.length; i++) {
+ var x1 = temp[i].s.c;
+ var y1 = temp[i].s.r;
+ var x2 = temp[i].e.c;
+ var y2 = temp[i].e.r;
+ var key = jexcel.getColumnNameFromId([x1,y1]);
+ spreadsheet.mergeCells[key] = [ x2-x1+1, y2-y1+1 ];
+ }
+ }
+ // Data container
+ var max_x = 0;
+ var max_y = 0;
+ var temp = Object.keys(workbook.Sheets[sheetName]);
+ for (var i = 0; i < temp.length; i++) {
+ if (temp[i].substr(0,1) != '!') {
+ var cell = workbook.Sheets[sheetName][temp[i]];
+ var info = jexcel.getIdFromColumnName(temp[i], true);
+ if (! spreadsheet.data[info[1]]) {
+ spreadsheet.data[info[1]] = [];
+ }
+ spreadsheet.data[info[1]][info[0]] = cell.f ? '=' + cell.f : cell.w;
+ if (max_x < info[0]) {
+ max_x = info[0];
+ }
+ if (max_y < info[1]) {
+ max_y = info[1];
+ }
+ // Style
+ if (cell.style && Object.keys(cell.style).length > 0) {
+ spreadsheet.style[temp[i]] = cell.style;
+ }
+ if (cell.s && cell.s.fgColor) {
+ if (spreadsheet.style[temp[i]]) {
+ spreadsheet.style[temp[i]] += ';';
+ }
+ spreadsheet.style[temp[i]] += 'background-color:#' + cell.s.fgColor.rgb;
+ }
+ }
+ }
+ var numColumns = spreadsheet.columns;
+ for (var j = 0; j <= max_y; j++) {
+ for (var i = 0; i <= max_x; i++) {
+ if (! spreadsheet.data[j]) {
+ spreadsheet.data[j] = [];
+ }
+ if (! spreadsheet.data[j][i]) {
+ if (numColumns < i) {
+ spreadsheet.data[j][i] = '';
+ }
+ }
+ }
+ }
+ spreadsheets.push(spreadsheet);
+ });
+
+ return spreadsheets;
+ }
+
+ var oReq;
+ oReq = new XMLHttpRequest();
+ oReq.open("GET", file, true);
+
+ if(typeof Uint8Array !== 'undefined') {
+ oReq.responseType = "arraybuffer";
+ oReq.onload = function(e) {
+ var arraybuffer = oReq.response;
+ var data = new Uint8Array(arraybuffer);
+ var wb = XLSX.read(data, {type:"array", cellFormula:true, cellStyles:true });
+ __callback(convert(wb))
+ };
+ } else {
+ oReq.setRequestHeader("Accept-Charset", "x-user-defined");
+ oReq.onreadystatechange = function() { if(oReq.readyState == 4 && oReq.status == 200) {
+ var ff = convertResponseBodyToText(oReq.responseBody);
+ var wb = XLSX.read(ff, {type:"binary", cellFormula:true, cellStyles:true });
+ __callback(convert(wb))
+ }};
+ }
+
+ oReq.send();
+ }
+
+ /**
+ * Valid international letter
+ */
+
+ jexcel.validLetter = function (text) {
+ var regex = /([\u0041-\u005A\u0061-\u007A\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC-\u0400-\u04FF']+)/g;
+ return text.match(regex) ? 1 : 0;
+ }
+
+ /**
+ * Helper injectArray
+ */
+ jexcel.injectArray = function(o, idx, arr) {
+ return o.slice(0, idx).concat(arr).concat(o.slice(idx));
+ }
+
+ /**
+ * Get letter based on a number
+ *
+ * @param integer i
+ * @return string letter
+ */
+ jexcel.getColumnName = function(i) {
+ var letter = '';
+ if (i > 701) {
+ letter += String.fromCharCode(64 + parseInt(i / 676));
+ letter += String.fromCharCode(64 + parseInt((i % 676) / 26));
+ } else if (i > 25) {
+ letter += String.fromCharCode(64 + parseInt(i / 26));
+ }
+ letter += String.fromCharCode(65 + (i % 26));
+
+ return letter;
+ }
+
+ /**
+ * Convert excel like column to jexcel id
+ *
+ * @param string id
+ * @return string id
+ */
+ jexcel.getIdFromColumnName = function (id, arr) {
+ // Get the letters
+ var t = /^[a-zA-Z]+/.exec(id);
+
+ if (t) {
+ // Base 26 calculation
+ var code = 0;
+ for (var i = 0; i < t[0].length; i++) {
+ code += parseInt(t[0].charCodeAt(i) - 64) * Math.pow(26, (t[0].length - 1 - i));
+ }
+ code--;
+ // Make sure jexcel starts on zero
+ if (code < 0) {
+ code = 0;
+ }
+
+ // Number
+ var number = parseInt(/[0-9]+$/.exec(id));
+ if (number > 0) {
+ number--;
+ }
+
+ if (arr == true) {
+ id = [ code, number ];
+ } else {
+ id = code + '-' + number;
+ }
+ }
+
+ return id;
+ }
+
+ /**
+ * Convert jexcel id to excel like column name
+ *
+ * @param string id
+ * @return string id
+ */
+ jexcel.getColumnNameFromId = function (cellId) {
+ if (! Array.isArray(cellId)) {
+ cellId = cellId.split('-');
+ }
+
+ return jexcel.getColumnName(parseInt(cellId[0])) + (parseInt(cellId[1]) + 1);
+ }
+
+ /**
+ * Verify element inside jexcel table
+ *
+ * @param string id
+ * @return string id
+ */
+ jexcel.getElement = function(element) {
+ var jexcelSection = 0;
+ var jexcelElement = 0;
+
+ function path (element) {
+ if (element.className) {
+ if (element.classList.contains('jexcel_container')) {
+ jexcelElement = element;
+ }
+ }
+
+ if (element.tagName == 'THEAD') {
+ jexcelSection = 1;
+ } else if (element.tagName == 'TBODY') {
+ jexcelSection = 2;
+ }
+
+ if (element.parentNode) {
+ if (! jexcelElement) {
+ path(element.parentNode);
+ }
+ }
+ }
+
+ path(element);
+
+ return [ jexcelElement, jexcelSection ];
+ }
+
+ jexcel.doubleDigitFormat = function(v) {
+ v = ''+v;
+ if (v.length == 1) {
+ v = '0'+v;
+ }
+ return v;
+ }
+
+ jexcel.createFromTable = function(el, options) {
+ if (el.tagName != 'TABLE') {
+ console.log('Element is not a table');
+ } else {
+ // Configuration
+ if (! options) {
+ options = {};
+ }
+ options.columns = [];
+ options.data = [];
+
+ // Colgroup
+ var colgroup = el.querySelectorAll('colgroup > col');
+ if (colgroup.length) {
+ // Get column width
+ for (var i = 0; i < colgroup.length; i++) {
+ var width = colgroup[i].style.width;
+ if (! width) {
+ var width = colgroup[i].getAttribute('width');
+ }
+ // Set column width
+ if (width) {
+ if (! options.columns[i]) {
+ options.columns[i] = {}
+ }
+ options.columns[i].width = width;
+ }
+ }
+ }
+
+ // Parse header
+ var parseHeader = function(header) {
+ // Get width information
+ var info = header.getBoundingClientRect();
+ var width = info.width > 50 ? info.width : 50;
+
+ // Create column option
+ if (! options.columns[i]) {
+ options.columns[i] = {};
+ }
+ if (header.getAttribute('data-celltype')) {
+ options.columns[i].type = header.getAttribute('data-celltype');
+ } else {
+ options.columns[i].type = 'text';
+ }
+ options.columns[i].width = width + 'px';
+ options.columns[i].title = header.innerHTML;
+ options.columns[i].align = header.style.textAlign || 'center';
+ }
+
+ // Headers
+ var headers = el.querySelectorAll('thead > tr');
+ if (headers.length) {
+ // Get the last row in the thead
+ headers = headers[headers.length-1].children;
+ // Go though the headers
+ for (var i = 0; i < headers.length; i++) {
+ parseHeader(headers[i]);
+ }
+ }
+
+ // Content
+ var rowNumber = 0;
+ var mergeCells = {};
+ var rows = {};
+ var style = {};
+
+ var content = el.querySelectorAll('table > tr, tbody tr');
+ for (var j = 0; j < content.length; j++) {
+ options.data[rowNumber] = [];
+ if (options.parseTableFirstRowAsHeader == true && j == 0) {
+ for (var i = 0; i < content[j].children.length; i++) {
+ parseHeader(content[j].children[i]);
+ }
+ } else {
+ for (var i = 0; i < content[j].children.length; i++) {
+ // WickedGrid formula compatibility
+ var value = content[j].children[i].getAttribute('data-formula');
+ if (value) {
+ if (value.substr(0,1) != '=') {
+ value = '=' + value;
+ }
+ } else {
+ var value = content[j].children[i].innerHTML;
+ }
+ options.data[rowNumber].push(value);
+
+ // Key
+ var cellName = jexcel.getColumnNameFromId([ i, j ]);
+
+ // Merged cells
+ var mergedColspan = parseInt(content[j].children[i].getAttribute('colspan')) || 0;
+ var mergedRowspan = parseInt(content[j].children[i].getAttribute('rowspan')) || 0;
+ if (mergedColspan || mergedRowspan) {
+ mergeCells[cellName] = [ mergedColspan || 1, mergedRowspan || 1 ];
+ }
+
+ // Avoid problems with hidden cells
+ if (s = content[j].children[i].style && content[j].children[i].style.display == 'none') {
+ content[j].children[i].style.display = '';
+ }
+ // Get style
+ var s = content[j].children[i].getAttribute('style');
+ if (s) {
+ style[cellName] = s;
+ }
+ // Bold
+ if (content[j].children[i].classList.contains('styleBold')) {
+ if (style[cellName]) {
+ style[cellName] += '; font-weight:bold;';
+ } else {
+ style[cellName] = 'font-weight:bold;';
+ }
+ }
+ }
+
+ // Row Height
+ if (content[j].style && content[j].style.height) {
+ rows[j] = { height: content[j].style.height };
+ }
+
+ // Index
+ rowNumber++;
+ }
+ }
+
+ // Style
+ if (Object.keys(style).length > 0) {
+ //options.style = style;
+ }
+ // Merged
+ if (Object.keys(mergeCells).length > 0) {
+ options.mergeCells = mergeCells;
+ }
+ // Row height
+ if (Object.keys(rows).length > 0) {
+ options.rows = rows;
+ }
+
+ // TODO: data-hiddencolumns="3,4"
+
+ // I guess in terms the better column type
+ if (options.parseTableAutoCellType == true) {
+ var pattern = [];
+ for (var i = 0; i < options.columns.length; i++) {
+ var test = true;
+ var testCalendar = true;
+ pattern[i] = [];
+ for (var j = 0; j < options.data.length; j++) {
+ var value = options.data[j][i];
+ if (! pattern[i][value]) {
+ pattern[i][value] = 0;
+ }
+ pattern[i][value]++;
+ if (value.length > 25) {
+ test = false;
+ }
+ if (value.length == 10) {
+ if (! (value.substr(4,1) == '-' && value.substr(7,1) == '-')) {
+ testCalendar = false;
+ }
+ } else {
+ testCalendar = false;
+ }
+ }
+
+ var keys = Object.keys(pattern[i]).length;
+ if (testCalendar) {
+ options.columns[i].type = 'calendar';
+ } else if (test == true && keys > 1 && keys <= parseInt(options.data.length * 0.1)) {
+ options.columns[i].type = 'dropdown';
+ options.columns[i].source = Object.keys(pattern[i]);
+ }
+ }
+ }
+
+ return options;
+ }
+ }
+
+ /**
+ * Jquery Support
+ */
+ if (typeof(jQuery) != 'undefined') {
+ (function($){
+ $.fn.jexcel = function(method) {
+ var spreadsheetContainer = $(this).get(0);
+ if (! spreadsheetContainer.jexcel) {
+ return jexcel($(this).get(0), arguments[0]);
+ } else {
+ return spreadsheetContainer.jexcel[method].apply(this, Array.prototype.slice.call( arguments, 1 ));
+ }
+ };
+
+ })(jQuery);
+ }
+
+
+ // Based on sutoiku work (https://github.com/sutoiku)
+
+ var error = (function() {
+ var exports = {};
+
+ exports.nil = new Error('#NULL!');
+ exports.div0 = new Error('#DIV/0!');
+ exports.value = new Error('#VALUE!');
+ exports.ref = new Error('#REF!');
+ exports.name = new Error('#NAME?');
+ exports.num = new Error('#NUM!');
+ exports.na = new Error('#N/A');
+ exports.error = new Error('#ERROR!');
+ exports.data = new Error('#GETTING_DATA');
+
+ return exports;
+ })();
+
+ var utils = (function() {
+ var exports = {};
+
+ exports.flattenShallow = function(array) {
+ if (!array || !array.reduce) {
+ return array;
+ }
+
+ return array.reduce(function(a, b) {
+ var aIsArray = Array.isArray(a);
+ var bIsArray = Array.isArray(b);
+
+ if (aIsArray && bIsArray) {
+ return a.concat(b);
+ }
+ if (aIsArray) {
+ a.push(b);
+
+ return a;
+ }
+ if (bIsArray) {
+ return [ a ].concat(b);
+ }
+
+ return [ a, b ];
+ });
+ };
+
+ exports.isFlat = function(array) {
+ if (!array) {
+ return false;
+ }
+
+ for (var i = 0; i < array.length; ++i) {
+ if (Array.isArray(array[i])) {
+ return false;
+ }
+ }
+
+ return true;
+ };
+
+ exports.flatten = function() {
+ var result = exports.argsToArray.apply(null, arguments);
+
+ while (!exports.isFlat(result)) {
+ result = exports.flattenShallow(result);
+ }
+
+ return result;
+ };
+
+ exports.argsToArray = function(args) {
+ var result = [];
+
+ exports.arrayEach(args, function(value) {
+ result.push(value);
+ });
+
+ return result;
+ };
+
+ exports.numbers = function() {
+ var possibleNumbers = this.flatten.apply(null, arguments);
+ return possibleNumbers.filter(function(el) {
+ return typeof el === 'number';
+ });
+ };
+
+ exports.cleanFloat = function(number) {
+ var power = 1e14;
+ return Math.round(number * power) / power;
+ };
+
+ exports.parseBool = function(bool) {
+ if (typeof bool === 'boolean') {
+ return bool;
+ }
+
+ if (bool instanceof Error) {
+ return bool;
+ }
+
+ if (typeof bool === 'number') {
+ return bool !== 0;
+ }
+
+ if (typeof bool === 'string') {
+ var up = bool.toUpperCase();
+ if (up === 'TRUE') {
+ return true;
+ }
+
+ if (up === 'FALSE') {
+ return false;
+ }
+ }
+
+ if (bool instanceof Date && !isNaN(bool)) {
+ return true;
+ }
+
+ return error.value;
+ };
+
+ exports.parseNumber = function(string) {
+ if (string === undefined || string === '') {
+ return error.value;
+ }
+ if (!isNaN(string)) {
+ return parseFloat(string);
+ }
+
+ return error.value;
+ };
+
+ exports.parseNumberArray = function(arr) {
+ var len;
+
+ if (!arr || (len = arr.length) === 0) {
+ return error.value;
+ }
+
+ var parsed;
+
+ while (len--) {
+ parsed = exports.parseNumber(arr[len]);
+ if (parsed === error.value) {
+ return parsed;
+ }
+ arr[len] = parsed;
+ }
+
+ return arr;
+ };
+
+ exports.parseMatrix = function(matrix) {
+ var n;
+
+ if (!matrix || (n = matrix.length) === 0) {
+ return error.value;
+ }
+ var pnarr;
+
+ for (var i = 0; i < matrix.length; i++) {
+ pnarr = exports.parseNumberArray(matrix[i]);
+ matrix[i] = pnarr;
+
+ if (pnarr instanceof Error) {
+ return pnarr;
+ }
+ }
+
+ return matrix;
+ };
+
+ var d1900 = new Date(Date.UTC(1900, 0, 1));
+ exports.parseDate = function(date) {
+ if (!isNaN(date)) {
+ if (date instanceof Date) {
+ return new Date(date);
+ }
+ var d = parseInt(date, 10);
+ if (d < 0) {
+ return error.num;
+ }
+ if (d <= 60) {
+ return new Date(d1900.getTime() + (d - 1) * 86400000);
+ }
+ return new Date(d1900.getTime() + (d - 2) * 86400000);
+ }
+ if (typeof date === 'string') {
+ date = new Date(date);
+ if (!isNaN(date)) {
+ return date;
+ }
+ }
+ return error.value;
+ };
+
+ exports.parseDateArray = function(arr) {
+ var len = arr.length;
+ var parsed;
+ while (len--) {
+ parsed = this.parseDate(arr[len]);
+ if (parsed === error.value) {
+ return parsed;
+ }
+ arr[len] = parsed;
+ }
+ return arr;
+ };
+
+ exports.anyIsError = function() {
+ var n = arguments.length;
+ while (n--) {
+ if (arguments[n] instanceof Error) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+ exports.arrayValuesToNumbers = function(arr) {
+ var n = arr.length;
+ var el;
+ while (n--) {
+ el = arr[n];
+ if (typeof el === 'number') {
+ continue;
+ }
+ if (el === true) {
+ arr[n] = 1;
+ continue;
+ }
+ if (el === false) {
+ arr[n] = 0;
+ continue;
+ }
+ if (typeof el === 'string') {
+ var number = this.parseNumber(el);
+ if (number instanceof Error) {
+ arr[n] = 0;
+ } else {
+ arr[n] = number;
+ }
+ }
+ }
+ return arr;
+ };
+
+ exports.rest = function(array, idx) {
+ idx = idx || 1;
+ if (!array || typeof array.slice !== 'function') {
+ return array;
+ }
+ return array.slice(idx);
+ };
+
+ exports.initial = function(array, idx) {
+ idx = idx || 1;
+ if (!array || typeof array.slice !== 'function') {
+ return array;
+ }
+ return array.slice(0, array.length - idx);
+ };
+
+ exports.arrayEach = function(array, iteratee) {
+ var index = -1, length = array.length;
+
+ while (++index < length) {
+ if (iteratee(array[index], index, array) === false) {
+ break;
+ }
+ }
+
+ return array;
+ };
+
+ exports.transpose = function(matrix) {
+ if (!matrix) {
+ return error.value;
+ }
+
+ return matrix[0].map(function(col, i) {
+ return matrix.map(function(row) {
+ return row[i];
+ });
+ });
+ };
+
+ return exports;
+ })();
+
+ jexcel.methods = {};
+
+ jexcel.methods.datetime = (function() {
+ var exports = {};
+
+ var d1900 = new Date(1900, 0, 1);
+ var WEEK_STARTS = [
+ undefined,
+ 0,
+ 1,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 0
+ ];
+ var WEEK_TYPES = [
+ [],
+ [1, 2, 3, 4, 5, 6, 7],
+ [7, 1, 2, 3, 4, 5, 6],
+ [6, 0, 1, 2, 3, 4, 5],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [7, 1, 2, 3, 4, 5, 6],
+ [6, 7, 1, 2, 3, 4, 5],
+ [5, 6, 7, 1, 2, 3, 4],
+ [4, 5, 6, 7, 1, 2, 3],
+ [3, 4, 5, 6, 7, 1, 2],
+ [2, 3, 4, 5, 6, 7, 1],
+ [1, 2, 3, 4, 5, 6, 7]
+ ];
+ var WEEKEND_TYPES = [
+ [],
+ [6, 0],
+ [0, 1],
+ [1, 2],
+ [2, 3],
+ [3, 4],
+ [4, 5],
+ [5, 6],
+ undefined,
+ undefined,
+ undefined, [0, 0],
+ [1, 1],
+ [2, 2],
+ [3, 3],
+ [4, 4],
+ [5, 5],
+ [6, 6]
+ ];
+
+ exports.DATE = function(year, month, day) {
+ year = utils.parseNumber(year);
+ month = utils.parseNumber(month);
+ day = utils.parseNumber(day);
+ if (utils.anyIsError(year, month, day)) {
+ return error.value;
+ }
+ if (year < 0 || month < 0 || day < 0) {
+ return error.num;
+ }
+ var date = new Date(year, month - 1, day);
+ return date;
+ };
+
+ exports.DATEVALUE = function(date_text) {
+ if (typeof date_text !== 'string') {
+ return error.value;
+ }
+ var date = Date.parse(date_text);
+ if (isNaN(date)) {
+ return error.value;
+ }
+ if (date <= -2203891200000) {
+ return (date - d1900) / 86400000 + 1;
+ }
+ return (date - d1900) / 86400000 + 2;
+ };
+
+ exports.DAY = function(serial_number) {
+ var date = utils.parseDate(serial_number);
+ if (date instanceof Error) {
+ return date;
+ }
+ return date.getDate();
+ };
+
+ exports.DAYS = function(end_date, start_date) {
+ end_date = utils.parseDate(end_date);
+ start_date = utils.parseDate(start_date);
+ if (end_date instanceof Error) {
+ return end_date;
+ }
+ if (start_date instanceof Error) {
+ return start_date;
+ }
+ return serial(end_date) - serial(start_date);
+ };
+
+ exports.DAYS360 = function(start_date, end_date, method) {
+ };
+
+ exports.EDATE = function(start_date, months) {
+ start_date = utils.parseDate(start_date);
+ if (start_date instanceof Error) {
+ return start_date;
+ }
+ if (isNaN(months)) {
+ return error.value;
+ }
+ months = parseInt(months, 10);
+ start_date.setMonth(start_date.getMonth() + months);
+ return serial(start_date);
+ };
+
+ exports.EOMONTH = function(start_date, months) {
+ start_date = utils.parseDate(start_date);
+ if (start_date instanceof Error) {
+ return start_date;
+ }
+ if (isNaN(months)) {
+ return error.value;
+ }
+ months = parseInt(months, 10);
+ return serial(new Date(start_date.getFullYear(), start_date.getMonth() + months + 1, 0));
+ };
+
+ exports.HOUR = function(serial_number) {
+ serial_number = utils.parseDate(serial_number);
+ if (serial_number instanceof Error) {
+ return serial_number;
+ }
+ return serial_number.getHours();
+ };
+
+ exports.INTERVAL = function(second) {
+ if (typeof second !== 'number' && typeof second !== 'string') {
+ return error.value;
+ } else {
+ second = parseInt(second, 10);
+ }
+
+ var year = Math.floor(second/946080000);
+ second = second%946080000;
+ var month = Math.floor(second/2592000);
+ second = second%2592000;
+ var day = Math.floor(second/86400);
+ second = second%86400;
+
+ var hour = Math.floor(second/3600);
+ second = second%3600;
+ var min = Math.floor(second/60);
+ second = second%60;
+ var sec = second;
+
+ year = (year > 0) ? year + 'Y' : '';
+ month = (month > 0) ? month + 'M' : '';
+ day = (day > 0) ? day + 'D' : '';
+ hour = (hour > 0) ? hour + 'H' : '';
+ min = (min > 0) ? min + 'M' : '';
+ sec = (sec > 0) ? sec + 'S' : '';
+
+ return 'P' + year + month + day + 'T' + hour + min + sec;
+ };
+
+ exports.ISOWEEKNUM = function(date) {
+ date = utils.parseDate(date);
+ if (date instanceof Error) {
+ return date;
+ }
+
+ date.setHours(0, 0, 0);
+ date.setDate(date.getDate() + 4 - (date.getDay() || 7));
+ var yearStart = new Date(date.getFullYear(), 0, 1);
+ return Math.ceil((((date - yearStart) / 86400000) + 1) / 7);
+ };
+
+ exports.MINUTE = function(serial_number) {
+ serial_number = utils.parseDate(serial_number);
+ if (serial_number instanceof Error) {
+ return serial_number;
+ }
+ return serial_number.getMinutes();
+ };
+
+ exports.MONTH = function(serial_number) {
+ serial_number = utils.parseDate(serial_number);
+ if (serial_number instanceof Error) {
+ return serial_number;
+ }
+ return serial_number.getMonth() + 1;
+ };
+
+ exports.NETWORKDAYS = function(start_date, end_date, holidays) {
+ };
+
+ exports.NETWORKDAYS.INTL = function(start_date, end_date, weekend, holidays) {
+ };
+
+ exports.NOW = function() {
+ return new Date();
+ };
+
+ exports.SECOND = function(serial_number) {
+ serial_number = utils.parseDate(serial_number);
+ if (serial_number instanceof Error) {
+ return serial_number;
+ }
+ return serial_number.getSeconds();
+ };
+
+ exports.TIME = function(hour, minute, second) {
+ hour = utils.parseNumber(hour);
+ minute = utils.parseNumber(minute);
+ second = utils.parseNumber(second);
+ if (utils.anyIsError(hour, minute, second)) {
+ return error.value;
+ }
+ if (hour < 0 || minute < 0 || second < 0) {
+ return error.num;
+ }
+ return (3600 * hour + 60 * minute + second) / 86400;
+ };
+
+ exports.TIMEVALUE = function(time_text) {
+ time_text = utils.parseDate(time_text);
+ if (time_text instanceof Error) {
+ return time_text;
+ }
+ return (3600 * time_text.getHours() + 60 * time_text.getMinutes() + time_text.getSeconds()) / 86400;
+ };
+
+ exports.TODAY = function() {
+ return new Date();
+ };
+
+ exports.WEEKDAY = function(serial_number, return_type) {
+ serial_number = utils.parseDate(serial_number);
+ if (serial_number instanceof Error) {
+ return serial_number;
+ }
+ if (return_type === undefined) {
+ return_type = 1;
+ }
+ var day = serial_number.getDay();
+ return WEEK_TYPES[return_type][day];
+ };
+
+ exports.WEEKNUM = function(serial_number, return_type) {
+ };
+
+ exports.WORKDAY = function(start_date, days, holidays) {
+ };
+
+ exports.WORKDAY.INTL = function(start_date, days, weekend, holidays) {
+ };
+
+ exports.YEAR = function(serial_number) {
+ serial_number = utils.parseDate(serial_number);
+ if (serial_number instanceof Error) {
+ return serial_number;
+ }
+ return serial_number.getFullYear();
+ };
+
+ function isLeapYear(year) {
+ return new Date(year, 1, 29).getMonth() === 1;
+ }
+
+ exports.YEARFRAC = function(start_date, end_date, basis) {
+ };
+
+ function serial(date) {
+ var addOn = (date > -2203891200000)?2:1;
+ return (date - d1900) / 86400000 + addOn;
+ }
+
+ return exports;
+ })();
+
+ jexcel.methods.database = (function() {
+ var exports = {};
+
+ function compact(array) {
+ if (!array) {
+ return array;
+ }
+ var result = [];
+ for (var i = 0; i < array.length; ++i) {
+ if (!array[i]) {
+ continue;
+ }
+ result.push(array[i]);
+ }
+ return result;
+ }
+
+ exports.FINDFIELD = function(database, title) {
+ var index = null;
+ for (var i = 0; i < database.length; i++) {
+ if (database[i][0] === title) {
+ index = i;
+ break;
+ }
+ }
+
+ // Return error if the input field title is incorrect
+ if (index == null) {
+ return error.value;
+ }
+ return index;
+ };
+
+ function findResultIndex(database, criterias) {
+ var matches = {};
+ for (var i = 1; i < database[0].length; ++i) {
+ matches[i] = true;
+ }
+ var maxCriteriaLength = criterias[0].length;
+ for (i = 1; i < criterias.length; ++i) {
+ if (criterias[i].length > maxCriteriaLength) {
+ maxCriteriaLength = criterias[i].length;
+ }
+ }
+
+ for (var k = 1; k < database.length; ++k) {
+ for (var l = 1; l < database[k].length; ++l) {
+ var currentCriteriaResult = false;
+ var hasMatchingCriteria = false;
+ for (var j = 0; j < criterias.length; ++j) {
+ var criteria = criterias[j];
+ if (criteria.length < maxCriteriaLength) {
+ continue;
+ }
+
+ var criteriaField = criteria[0];
+ if (database[k][0] !== criteriaField) {
+ continue;
+ }
+ hasMatchingCriteria = true;
+ for (var p = 1; p < criteria.length; ++p) {
+ currentCriteriaResult = currentCriteriaResult
+ || eval(database[k][l] + criteria[p]); // jshint
+ // ignore:line
+ }
+ }
+ if (hasMatchingCriteria) {
+ matches[l] = matches[l] && currentCriteriaResult;
+ }
+ }
+ }
+
+ var result = [];
+ for (var n = 0; n < database[0].length; ++n) {
+ if (matches[n]) {
+ result.push(n - 1);
+ }
+ }
+ return result;
+ }
+
+ // Database functions
+ exports.DAVERAGE = function(database, field, criteria) {
+ // Return error if field is not a number and not a string
+ if (isNaN(field) && (typeof field !== "string")) {
+ return error.value;
+ }
+ var resultIndexes = findResultIndex(database, criteria);
+ var targetFields = [];
+ if (typeof field === "string") {
+ var index = exports.FINDFIELD(database, field);
+ targetFields = utils.rest(database[index]);
+ } else {
+ targetFields = utils.rest(database[field]);
+ }
+ var sum = 0;
+ for (var i = 0; i < resultIndexes.length; i++) {
+ sum += targetFields[resultIndexes[i]];
+ }
+ return resultIndexes.length === 0 ? error.div0 : sum / resultIndexes.length;
+ };
+
+ exports.DCOUNT = function(database, field, criteria) {
+ };
+
+ exports.DCOUNTA = function(database, field, criteria) {
+ };
+
+ exports.DGET = function(database, field, criteria) {
+ // Return error if field is not a number and not a string
+ if (isNaN(field) && (typeof field !== "string")) {
+ return error.value;
+ }
+ var resultIndexes = findResultIndex(database, criteria);
+ var targetFields = [];
+ if (typeof field === "string") {
+ var index = exports.FINDFIELD(database, field);
+ targetFields = utils.rest(database[index]);
+ } else {
+ targetFields = utils.rest(database[field]);
+ }
+ // Return error if no record meets the criteria
+ if (resultIndexes.length === 0) {
+ return error.value;
+ }
+ // Returns the #NUM! error value because more than one record meets the
+ // criteria
+ if (resultIndexes.length > 1) {
+ return error.num;
+ }
+
+ return targetFields[resultIndexes[0]];
+ };
+
+ exports.DMAX = function(database, field, criteria) {
+ // Return error if field is not a number and not a string
+ if (isNaN(field) && (typeof field !== "string")) {
+ return error.value;
+ }
+ var resultIndexes = findResultIndex(database, criteria);
+ var targetFields = [];
+ if (typeof field === "string") {
+ var index = exports.FINDFIELD(database, field);
+ targetFields = utils.rest(database[index]);
+ } else {
+ targetFields = utils.rest(database[field]);
+ }
+ var maxValue = targetFields[resultIndexes[0]];
+ for (var i = 1; i < resultIndexes.length; i++) {
+ if (maxValue < targetFields[resultIndexes[i]]) {
+ maxValue = targetFields[resultIndexes[i]];
+ }
+ }
+ return maxValue;
+ };
+
+ exports.DMIN = function(database, field, criteria) {
+ // Return error if field is not a number and not a string
+ if (isNaN(field) && (typeof field !== "string")) {
+ return error.value;
+ }
+ var resultIndexes = findResultIndex(database, criteria);
+ var targetFields = [];
+ if (typeof field === "string") {
+ var index = exports.FINDFIELD(database, field);
+ targetFields = utils.rest(database[index]);
+ } else {
+ targetFields = utils.rest(database[field]);
+ }
+ var minValue = targetFields[resultIndexes[0]];
+ for (var i = 1; i < resultIndexes.length; i++) {
+ if (minValue > targetFields[resultIndexes[i]]) {
+ minValue = targetFields[resultIndexes[i]];
+ }
+ }
+ return minValue;
+ };
+
+ exports.DPRODUCT = function(database, field, criteria) {
+ // Return error if field is not a number and not a string
+ if (isNaN(field) && (typeof field !== "string")) {
+ return error.value;
+ }
+ var resultIndexes = findResultIndex(database, criteria);
+ var targetFields = [];
+ if (typeof field === "string") {
+ var index = exports.FINDFIELD(database, field);
+ targetFields = utils.rest(database[index]);
+ } else {
+ targetFields = utils.rest(database[field]);
+ }
+ var targetValues = [];
+ for (var i = 0; i < resultIndexes.length; i++) {
+ targetValues[i] = targetFields[resultIndexes[i]];
+ }
+ targetValues = compact(targetValues);
+ var result = 1;
+ for (i = 0; i < targetValues.length; i++) {
+ result *= targetValues[i];
+ }
+ return result;
+ };
+
+ exports.DSTDEV = function(database, field, criteria) {
+ };
+
+ exports.DSTDEVP = function(database, field, criteria) {
+ };
+
+ exports.DSUM = function(database, field, criteria) {
+ };
+
+ exports.DVAR = function(database, field, criteria) {
+ };
+
+ exports.DVARP = function(database, field, criteria) {
+ };
+
+ exports.MATCH = function(lookupValue, lookupArray, matchType) {
+ if (!lookupValue && !lookupArray) {
+ return error.na;
+ }
+ if (arguments.length === 2) {
+ matchType = 1;
+ }
+ if (!(lookupArray instanceof Array)) {
+ return error.na;
+ }
+ if (matchType !== -1 && matchType !== 0 && matchType !== 1) {
+ return error.na;
+ }
+
+ var index;
+ var indexValue;
+
+ for (var idx = 0; idx < lookupArray.length; idx++) {
+ if (matchType === 1) {
+ if (lookupArray[idx] === lookupValue) {
+ return idx + 1;
+ } else if (lookupArray[idx] < lookupValue) {
+ if (!indexValue) {
+ index = idx + 1;
+ indexValue = lookupArray[idx];
+ } else if (lookupArray[idx] > indexValue) {
+ index = idx + 1;
+ indexValue = lookupArray[idx];
+ }
+ }
+ } else if (matchType === 0) {
+ if (typeof lookupValue === 'string') {
+ lookupValue = lookupValue.replace(/\?/g, '.');
+ if (lookupArray[idx].toLowerCase().match(lookupValue.toLowerCase())) {
+ return idx + 1;
+ }
+ } else {
+ if (lookupArray[idx] === lookupValue) {
+ return idx + 1;
+ }
+ }
+ } else if (matchType === -1) {
+ if (lookupArray[idx] === lookupValue) {
+ return idx + 1;
+ } else if (lookupArray[idx] > lookupValue) {
+ if (!indexValue) {
+ index = idx + 1;
+ indexValue = lookupArray[idx];
+ } else if (lookupArray[idx] < indexValue) {
+ index = idx + 1;
+ indexValue = lookupArray[idx];
+ }
+ }
+ }
+ }
+
+ return index ? index : error.na;
+ };
+
+ return exports;
+ })();
+
+ jexcel.methods.engineering = (function() {
+ var exports = {};
+
+ function isValidBinaryNumber(number) {
+ return (/^[01]{1,10}$/).test(number);
+ }
+
+ exports.BESSELI = function(x, n) {
+ };
+
+ exports.BESSELJ = function(x, n) {
+ };
+
+ exports.BESSELK = function(x, n) {
+ };
+
+ exports.BESSELY = function(x, n) {
+ };
+
+ exports.BIN2DEC = function(number) {
+ // Return error if number is not binary or contains more than 10
+ // characters (10 digits)
+ if (!isValidBinaryNumber(number)) {
+ return error.num;
+ }
+
+ // Convert binary number to decimal
+ var result = parseInt(number, 2);
+
+ // Handle negative numbers
+ var stringified = number.toString();
+ if (stringified.length === 10 && stringified.substring(0, 1) === '1') {
+ return parseInt(stringified.substring(1), 2) - 512;
+ } else {
+ return result;
+ }
+ };
+
+ exports.BIN2HEX = function(number, places) {
+ // Return error if number is not binary or contains more than 10
+ // characters (10 digits)
+ if (!isValidBinaryNumber(number)) {
+ return error.num;
+ }
+
+ // Ignore places and return a 10-character hexadecimal number if number
+ // is negative
+ var stringified = number.toString();
+ if (stringified.length === 10 && stringified.substring(0, 1) === '1') {
+ return (1099511627264 + parseInt(stringified.substring(1), 2)).toString(16);
+ }
+
+ // Convert binary number to hexadecimal
+ var result = parseInt(number, 2).toString(16);
+
+ // Return hexadecimal number using the minimum number of characters
+ // necessary if places is undefined
+ if (places === undefined) {
+ return result;
+ } else {
+ // Return error if places is nonnumeric
+ if (isNaN(places)) {
+ return error.value;
+ }
+
+ // Return error if places is negative
+ if (places < 0) {
+ return error.num;
+ }
+
+ // Truncate places in case it is not an integer
+ places = Math.floor(places);
+
+ // Pad return value with leading 0s (zeros) if necessary (using
+ // Underscore.string)
+ return (places >= result.length) ? REPT('0', places - result.length) + result : error.num;
+ }
+ };
+
+ exports.BIN2OCT = function(number, places) {
+ // Return error if number is not binary or contains more than 10
+ // characters (10 digits)
+ if (!isValidBinaryNumber(number)) {
+ return error.num;
+ }
+
+ // Ignore places and return a 10-character octal number if number is
+ // negative
+ var stringified = number.toString();
+ if (stringified.length === 10 && stringified.substring(0, 1) === '1') {
+ return (1073741312 + parseInt(stringified.substring(1), 2)).toString(8);
+ }
+
+ // Convert binary number to octal
+ var result = parseInt(number, 2).toString(8);
+
+ // Return octal number using the minimum number of characters necessary
+ // if places is undefined
+ if (places === undefined) {
+ return result;
+ } else {
+ // Return error if places is nonnumeric
+ if (isNaN(places)) {
+ return error.value;
+ }
+
+ // Return error if places is negative
+ if (places < 0) {
+ return error.num;
+ }
+
+ // Truncate places in case it is not an integer
+ places = Math.floor(places);
+
+ // Pad return value with leading 0s (zeros) if necessary (using
+ // Underscore.string)
+ return (places >= result.length) ? REPT('0', places - result.length) + result : error.num;
+ }
+ };
+
+ exports.BITAND = function(number1, number2) {
+ // Return error if either number is a non-numeric value
+ number1 = utils.parseNumber(number1);
+ number2 = utils.parseNumber(number2);
+ if (utils.anyIsError(number1, number2)) {
+ return error.value;
+ }
+
+ // Return error if either number is less than 0
+ if (number1 < 0 || number2 < 0) {
+ return error.num;
+ }
+
+ // Return error if either number is a non-integer
+ if (Math.floor(number1) !== number1 || Math.floor(number2) !== number2) {
+ return error.num;
+ }
+
+ // Return error if either number is greater than (2^48)-1
+ if (number1 > 281474976710655 || number2 > 281474976710655) {
+ return error.num;
+ }
+
+ // Return bitwise AND of two numbers
+ return number1 & number2;
+ };
+
+ exports.BITLSHIFT = function(number, shift) {
+ number = utils.parseNumber(number);
+ shift = utils.parseNumber(shift);
+ if (utils.anyIsError(number, shift)) {
+ return error.value;
+ }
+
+ // Return error if number is less than 0
+ if (number < 0) {
+ return error.num;
+ }
+
+ // Return error if number is a non-integer
+ if (Math.floor(number) !== number) {
+ return error.num;
+ }
+
+ // Return error if number is greater than (2^48)-1
+ if (number > 281474976710655) {
+ return error.num;
+ }
+
+ // Return error if the absolute value of shift is greater than 53
+ if (Math.abs(shift) > 53) {
+ return error.num;
+ }
+
+ // Return number shifted by shift bits to the left or to the right if
+ // shift is negative
+ return (shift >= 0) ? number << shift : number >> -shift;
+ };
+
+ exports.BITOR = function(number1, number2) {
+ number1 = utils.parseNumber(number1);
+ number2 = utils.parseNumber(number2);
+ if (utils.anyIsError(number1, number2)) {
+ return error.value;
+ }
+
+ // Return error if either number is less than 0
+ if (number1 < 0 || number2 < 0) {
+ return error.num;
+ }
+
+ // Return error if either number is a non-integer
+ if (Math.floor(number1) !== number1 || Math.floor(number2) !== number2) {
+ return error.num;
+ }
+
+ // Return error if either number is greater than (2^48)-1
+ if (number1 > 281474976710655 || number2 > 281474976710655) {
+ return error.num;
+ }
+
+ // Return bitwise OR of two numbers
+ return number1 | number2;
+ };
+
+ exports.BITRSHIFT = function(number, shift) {
+ number = utils.parseNumber(number);
+ shift = utils.parseNumber(shift);
+ if (utils.anyIsError(number, shift)) {
+ return error.value;
+ }
+
+ // Return error if number is less than 0
+ if (number < 0) {
+ return error.num;
+ }
+
+ // Return error if number is a non-integer
+ if (Math.floor(number) !== number) {
+ return error.num;
+ }
+
+ // Return error if number is greater than (2^48)-1
+ if (number > 281474976710655) {
+ return error.num;
+ }
+
+ // Return error if the absolute value of shift is greater than 53
+ if (Math.abs(shift) > 53) {
+ return error.num;
+ }
+
+ // Return number shifted by shift bits to the right or to the left if
+ // shift is negative
+ return (shift >= 0) ? number >> shift : number << -shift;
+ };
+
+ exports.BITXOR = function(number1, number2) {
+ number1 = utils.parseNumber(number1);
+ number2 = utils.parseNumber(number2);
+ if (utils.anyIsError(number1, number2)) {
+ return error.value;
+ }
+
+ // Return error if either number is less than 0
+ if (number1 < 0 || number2 < 0) {
+ return error.num;
+ }
+
+ // Return error if either number is a non-integer
+ if (Math.floor(number1) !== number1 || Math.floor(number2) !== number2) {
+ return error.num;
+ }
+
+ // Return error if either number is greater than (2^48)-1
+ if (number1 > 281474976710655 || number2 > 281474976710655) {
+ return error.num;
+ }
+
+ // Return bitwise XOR of two numbers
+ return number1 ^ number2;
+ };
+
+ exports.COMPLEX = function(real, imaginary, suffix) {
+ real = utils.parseNumber(real);
+ imaginary = utils.parseNumber(imaginary);
+ if (utils.anyIsError(real, imaginary)) {
+ return real;
+ }
+
+ // Set suffix
+ suffix = (suffix === undefined) ? 'i' : suffix;
+
+ // Return error if suffix is neither "i" nor "j"
+ if (suffix !== 'i' && suffix !== 'j') {
+ return error.value;
+ }
+
+ // Return complex number
+ if (real === 0 && imaginary === 0) {
+ return 0;
+ } else if (real === 0) {
+ return (imaginary === 1) ? suffix : imaginary.toString() + suffix;
+ } else if (imaginary === 0) {
+ return real.toString();
+ } else {
+ var sign = (imaginary > 0) ? '+' : '';
+ return real.toString() + sign + ((imaginary === 1) ? suffix : imaginary.toString() + suffix);
+ }
+ };
+
+ exports.CONVERT = function(number, from_unit, to_unit) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+
+ // List of units supported by CONVERT and units defined by the
+ // International System of Units
+ // [Name, Symbol, Alternate symbols, Quantity, ISU, CONVERT, Conversion
+ // ratio]
+ var units = [
+ ["a.u. of action", "?", null, "action", false, false, 1.05457168181818e-34],
+ ["a.u. of charge", "e", null, "electric_charge", false, false, 1.60217653141414e-19],
+ ["a.u. of energy", "Eh", null, "energy", false, false, 4.35974417757576e-18],
+ ["a.u. of length", "a?", null, "length", false, false, 5.29177210818182e-11],
+ ["a.u. of mass", "m?", null, "mass", false, false, 9.10938261616162e-31],
+ ["a.u. of time", "?/Eh", null, "time", false, false, 2.41888432650516e-17],
+ ["admiralty knot", "admkn", null, "speed", false, true, 0.514773333],
+ ["ampere", "A", null, "electric_current", true, false, 1],
+ ["ampere per meter", "A/m", null, "magnetic_field_intensity", true, false, 1],
+ ["ångström", "Å", ["ang"], "length", false, true, 1e-10],
+ ["are", "ar", null, "area", false, true, 100],
+ ["astronomical unit", "ua", null, "length", false, false, 1.49597870691667e-11],
+ ["bar", "bar", null, "pressure", false, false, 100000],
+ ["barn", "b", null, "area", false, false, 1e-28],
+ ["becquerel", "Bq", null, "radioactivity", true, false, 1],
+ ["bit", "bit", ["b"], "information", false, true, 1],
+ ["btu", "BTU", ["btu"], "energy", false, true, 1055.05585262],
+ ["byte", "byte", null, "information", false, true, 8],
+ ["candela", "cd", null, "luminous_intensity", true, false, 1],
+ ["candela per square metre", "cd/m?", null, "luminance", true, false, 1],
+ ["coulomb", "C", null, "electric_charge", true, false, 1],
+ ["cubic ångström", "ang3", ["ang^3"], "volume", false, true, 1e-30],
+ ["cubic foot", "ft3", ["ft^3"], "volume", false, true, 0.028316846592],
+ ["cubic inch", "in3", ["in^3"], "volume", false, true, 0.000016387064],
+ ["cubic light-year", "ly3", ["ly^3"], "volume", false, true, 8.46786664623715e-47],
+ ["cubic metre", "m?", null, "volume", true, true, 1],
+ ["cubic mile", "mi3", ["mi^3"], "volume", false, true, 4168181825.44058],
+ ["cubic nautical mile", "Nmi3", ["Nmi^3"], "volume", false, true, 6352182208],
+ ["cubic Pica", "Pica3", ["Picapt3", "Pica^3", "Picapt^3"], "volume", false, true, 7.58660370370369e-8],
+ ["cubic yard", "yd3", ["yd^3"], "volume", false, true, 0.764554857984],
+ ["cup", "cup", null, "volume", false, true, 0.0002365882365],
+ ["dalton", "Da", ["u"], "mass", false, false, 1.66053886282828e-27],
+ ["day", "d", ["day"], "time", false, true, 86400],
+ ["degree", "°", null, "angle", false, false, 0.0174532925199433],
+ ["degrees Rankine", "Rank", null, "temperature", false, true, 0.555555555555556],
+ ["dyne", "dyn", ["dy"], "force", false, true, 0.00001],
+ ["electronvolt", "eV", ["ev"], "energy", false, true, 1.60217656514141],
+ ["ell", "ell", null, "length", false, true, 1.143],
+ ["erg", "erg", ["e"], "energy", false, true, 1e-7],
+ ["farad", "F", null, "electric_capacitance", true, false, 1],
+ ["fluid ounce", "oz", null, "volume", false, true, 0.0000295735295625],
+ ["foot", "ft", null, "length", false, true, 0.3048],
+ ["foot-pound", "flb", null, "energy", false, true, 1.3558179483314],
+ ["gal", "Gal", null, "acceleration", false, false, 0.01],
+ ["gallon", "gal", null, "volume", false, true, 0.003785411784],
+ ["gauss", "G", ["ga"], "magnetic_flux_density", false, true, 1],
+ ["grain", "grain", null, "mass", false, true, 0.0000647989],
+ ["gram", "g", null, "mass", false, true, 0.001],
+ ["gray", "Gy", null, "absorbed_dose", true, false, 1],
+ ["gross registered ton", "GRT", ["regton"], "volume", false, true, 2.8316846592],
+ ["hectare", "ha", null, "area", false, true, 10000],
+ ["henry", "H", null, "inductance", true, false, 1],
+ ["hertz", "Hz", null, "frequency", true, false, 1],
+ ["horsepower", "HP", ["h"], "power", false, true, 745.69987158227],
+ ["horsepower-hour", "HPh", ["hh", "hph"], "energy", false, true, 2684519.538],
+ ["hour", "h", ["hr"], "time", false, true, 3600],
+ ["imperial gallon (U.K.)", "uk_gal", null, "volume", false, true, 0.00454609],
+ ["imperial hundredweight", "lcwt", ["uk_cwt", "hweight"], "mass", false, true, 50.802345],
+ ["imperial quart (U.K)", "uk_qt", null, "volume", false, true, 0.0011365225],
+ ["imperial ton", "brton", ["uk_ton", "LTON"], "mass", false, true, 1016.046909],
+ ["inch", "in", null, "length", false, true, 0.0254],
+ ["international acre", "uk_acre", null, "area", false, true, 4046.8564224],
+ ["IT calorie", "cal", null, "energy", false, true, 4.1868],
+ ["joule", "J", null, "energy", true, true, 1],
+ ["katal", "kat", null, "catalytic_activity", true, false, 1],
+ ["kelvin", "K", ["kel"], "temperature", true, true, 1],
+ ["kilogram", "kg", null, "mass", true, true, 1],
+ ["knot", "kn", null, "speed", false, true, 0.514444444444444],
+ ["light-year", "ly", null, "length", false, true, 9460730472580800],
+ ["litre", "L", ["l", "lt"], "volume", false, true, 0.001],
+ ["lumen", "lm", null, "luminous_flux", true, false, 1],
+ ["lux", "lx", null, "illuminance", true, false, 1],
+ ["maxwell", "Mx", null, "magnetic_flux", false, false, 1e-18],
+ ["measurement ton", "MTON", null, "volume", false, true, 1.13267386368],
+ ["meter per hour", "m/h", ["m/hr"], "speed", false, true, 0.00027777777777778],
+ ["meter per second", "m/s", ["m/sec"], "speed", true, true, 1],
+ ["meter per second squared", "m?s??", null, "acceleration", true, false, 1],
+ ["parsec", "pc", ["parsec"], "length", false, true, 30856775814671900],
+ ["meter squared per second", "m?/s", null, "kinematic_viscosity", true, false, 1],
+ ["metre", "m", null, "length", true, true, 1],
+ ["miles per hour", "mph", null, "speed", false, true, 0.44704],
+ ["millimetre of mercury", "mmHg", null, "pressure", false, false, 133.322],
+ ["minute", "?", null, "angle", false, false, 0.000290888208665722],
+ ["minute", "min", ["mn"], "time", false, true, 60],
+ ["modern teaspoon", "tspm", null, "volume", false, true, 0.000005],
+ ["mole", "mol", null, "amount_of_substance", true, false, 1],
+ ["morgen", "Morgen", null, "area", false, true, 2500],
+ ["n.u. of action", "?", null, "action", false, false, 1.05457168181818e-34],
+ ["n.u. of mass", "m?", null, "mass", false, false, 9.10938261616162e-31],
+ ["n.u. of speed", "c?", null, "speed", false, false, 299792458],
+ ["n.u. of time", "?/(me?c??)", null, "time", false, false, 1.28808866778687e-21],
+ ["nautical mile", "M", ["Nmi"], "length", false, true, 1852],
+ ["newton", "N", null, "force", true, true, 1],
+ ["œrsted", "Oe ", null, "magnetic_field_intensity", false, false, 79.5774715459477],
+ ["ohm", "Ω", null, "electric_resistance", true, false, 1],
+ ["ounce mass", "ozm", null, "mass", false, true, 0.028349523125],
+ ["pascal", "Pa", null, "pressure", true, false, 1],
+ ["pascal second", "Pa?s", null, "dynamic_viscosity", true, false, 1],
+ ["pferdestärke", "PS", null, "power", false, true, 735.49875],
+ ["phot", "ph", null, "illuminance", false, false, 0.0001],
+ ["pica (1/6 inch)", "pica", null, "length", false, true, 0.00035277777777778],
+ ["pica (1/72 inch)", "Pica", ["Picapt"], "length", false, true, 0.00423333333333333],
+ ["poise", "P", null, "dynamic_viscosity", false, false, 0.1],
+ ["pond", "pond", null, "force", false, true, 0.00980665],
+ ["pound force", "lbf", null, "force", false, true, 4.4482216152605],
+ ["pound mass", "lbm", null, "mass", false, true, 0.45359237],
+ ["quart", "qt", null, "volume", false, true, 0.000946352946],
+ ["radian", "rad", null, "angle", true, false, 1],
+ ["second", "?", null, "angle", false, false, 0.00000484813681109536],
+ ["second", "s", ["sec"], "time", true, true, 1],
+ ["short hundredweight", "cwt", ["shweight"], "mass", false, true, 45.359237],
+ ["siemens", "S", null, "electrical_conductance", true, false, 1],
+ ["sievert", "Sv", null, "equivalent_dose", true, false, 1],
+ ["slug", "sg", null, "mass", false, true, 14.59390294],
+ ["square ångström", "ang2", ["ang^2"], "area", false, true, 1e-20],
+ ["square foot", "ft2", ["ft^2"], "area", false, true, 0.09290304],
+ ["square inch", "in2", ["in^2"], "area", false, true, 0.00064516],
+ ["square light-year", "ly2", ["ly^2"], "area", false, true, 8.95054210748189e+31],
+ ["square meter", "m?", null, "area", true, true, 1],
+ ["square mile", "mi2", ["mi^2"], "area", false, true, 2589988.110336],
+ ["square nautical mile", "Nmi2", ["Nmi^2"], "area", false, true, 3429904],
+ ["square Pica", "Pica2", ["Picapt2", "Pica^2", "Picapt^2"], "area", false, true, 0.00001792111111111],
+ ["square yard", "yd2", ["yd^2"], "area", false, true, 0.83612736],
+ ["statute mile", "mi", null, "length", false, true, 1609.344],
+ ["steradian", "sr", null, "solid_angle", true, false, 1],
+ ["stilb", "sb", null, "luminance", false, false, 0.0001],
+ ["stokes", "St", null, "kinematic_viscosity", false, false, 0.0001],
+ ["stone", "stone", null, "mass", false, true, 6.35029318],
+ ["tablespoon", "tbs", null, "volume", false, true, 0.0000147868],
+ ["teaspoon", "tsp", null, "volume", false, true, 0.00000492892],
+ ["tesla", "T", null, "magnetic_flux_density", true, true, 1],
+ ["thermodynamic calorie", "c", null, "energy", false, true, 4.184],
+ ["ton", "ton", null, "mass", false, true, 907.18474],
+ ["tonne", "t", null, "mass", false, false, 1000],
+ ["U.K. pint", "uk_pt", null, "volume", false, true, 0.00056826125],
+ ["U.S. bushel", "bushel", null, "volume", false, true, 0.03523907],
+ ["U.S. oil barrel", "barrel", null, "volume", false, true, 0.158987295],
+ ["U.S. pint", "pt", ["us_pt"], "volume", false, true, 0.000473176473],
+ ["U.S. survey mile", "survey_mi", null, "length", false, true, 1609.347219],
+ ["U.S. survey/statute acre", "us_acre", null, "area", false, true, 4046.87261],
+ ["volt", "V", null, "voltage", true, false, 1],
+ ["watt", "W", null, "power", true, true, 1],
+ ["watt-hour", "Wh", ["wh"], "energy", false, true, 3600],
+ ["weber", "Wb", null, "magnetic_flux", true, false, 1],
+ ["yard", "yd", null, "length", false, true, 0.9144],
+ ["year", "yr", null, "time", false, true, 31557600]
+ ];
+
+ // Binary prefixes
+ // [Name, Prefix power of 2 value, Previx value, Abbreviation, Derived
+ // from]
+ var binary_prefixes = {
+ Yi: ["yobi", 80, 1208925819614629174706176, "Yi", "yotta"],
+ Zi: ["zebi", 70, 1180591620717411303424, "Zi", "zetta"],
+ Ei: ["exbi", 60, 1152921504606846976, "Ei", "exa"],
+ Pi: ["pebi", 50, 1125899906842624, "Pi", "peta"],
+ Ti: ["tebi", 40, 1099511627776, "Ti", "tera"],
+ Gi: ["gibi", 30, 1073741824, "Gi", "giga"],
+ Mi: ["mebi", 20, 1048576, "Mi", "mega"],
+ ki: ["kibi", 10, 1024, "ki", "kilo"]
+ };
+
+ // Unit prefixes
+ // [Name, Multiplier, Abbreviation]
+ var unit_prefixes = {
+ Y: ["yotta", 1e+24, "Y"],
+ Z: ["zetta", 1e+21, "Z"],
+ E: ["exa", 1e+18, "E"],
+ P: ["peta", 1e+15, "P"],
+ T: ["tera", 1e+12, "T"],
+ G: ["giga", 1e+09, "G"],
+ M: ["mega", 1e+06, "M"],
+ k: ["kilo", 1e+03, "k"],
+ h: ["hecto", 1e+02, "h"],
+ e: ["dekao", 1e+01, "e"],
+ d: ["deci", 1e-01, "d"],
+ c: ["centi", 1e-02, "c"],
+ m: ["milli", 1e-03, "m"],
+ u: ["micro", 1e-06, "u"],
+ n: ["nano", 1e-09, "n"],
+ p: ["pico", 1e-12, "p"],
+ f: ["femto", 1e-15, "f"],
+ a: ["atto", 1e-18, "a"],
+ z: ["zepto", 1e-21, "z"],
+ y: ["yocto", 1e-24, "y"]
+ };
+
+ // Initialize units and multipliers
+ var from = null;
+ var to = null;
+ var base_from_unit = from_unit;
+ var base_to_unit = to_unit;
+ var from_multiplier = 1;
+ var to_multiplier = 1;
+ var alt;
+
+ // Lookup from and to units
+ for (var i = 0; i < units.length; i++) {
+ alt = (units[i][2] === null) ? [] : units[i][2];
+ if (units[i][1] === base_from_unit || alt.indexOf(base_from_unit) >= 0) {
+ from = units[i];
+ }
+ if (units[i][1] === base_to_unit || alt.indexOf(base_to_unit) >= 0) {
+ to = units[i];
+ }
+ }
+
+ // Lookup from prefix
+ if (from === null) {
+ var from_binary_prefix = binary_prefixes[from_unit.substring(0, 2)];
+ var from_unit_prefix = unit_prefixes[from_unit.substring(0, 1)];
+
+ // Handle dekao unit prefix (only unit prefix with two characters)
+ if (from_unit.substring(0, 2) === 'da') {
+ from_unit_prefix = ["dekao", 1e+01, "da"];
+ }
+
+ // Handle binary prefixes first (so that 'Yi' is processed before
+ // 'Y')
+ if (from_binary_prefix) {
+ from_multiplier = from_binary_prefix[2];
+ base_from_unit = from_unit.substring(2);
+ } else if (from_unit_prefix) {
+ from_multiplier = from_unit_prefix[1];
+ base_from_unit = from_unit.substring(from_unit_prefix[2].length);
+ }
+
+ // Lookup from unit
+ for (var j = 0; j < units.length; j++) {
+ alt = (units[j][2] === null) ? [] : units[j][2];
+ if (units[j][1] === base_from_unit || alt.indexOf(base_from_unit) >= 0) {
+ from = units[j];
+ }
+ }
+ }
+
+ // Lookup to prefix
+ if (to === null) {
+ var to_binary_prefix = binary_prefixes[to_unit.substring(0, 2)];
+ var to_unit_prefix = unit_prefixes[to_unit.substring(0, 1)];
+
+ // Handle dekao unit prefix (only unit prefix with two characters)
+ if (to_unit.substring(0, 2) === 'da') {
+ to_unit_prefix = ["dekao", 1e+01, "da"];
+ }
+
+ // Handle binary prefixes first (so that 'Yi' is processed before
+ // 'Y')
+ if (to_binary_prefix) {
+ to_multiplier = to_binary_prefix[2];
+ base_to_unit = to_unit.substring(2);
+ } else if (to_unit_prefix) {
+ to_multiplier = to_unit_prefix[1];
+ base_to_unit = to_unit.substring(to_unit_prefix[2].length);
+ }
+
+ // Lookup to unit
+ for (var k = 0; k < units.length; k++) {
+ alt = (units[k][2] === null) ? [] : units[k][2];
+ if (units[k][1] === base_to_unit || alt.indexOf(base_to_unit) >= 0) {
+ to = units[k];
+ }
+ }
+ }
+
+ // Return error if a unit does not exist
+ if (from === null || to === null) {
+ return error.na;
+ }
+
+ // Return error if units represent different quantities
+ if (from[3] !== to[3]) {
+ return error.na;
+ }
+
+ // Return converted number
+ return number * from[6] * from_multiplier / (to[6] * to_multiplier);
+ };
+
+ exports.DEC2BIN = function(number, places) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+
+ // Return error if number is not decimal, is lower than -512, or is
+ // greater than 511
+ if (!/^-?[0-9]{1,3}$/.test(number) || number < -512 || number > 511) {
+ return error.num;
+ }
+
+ // Ignore places and return a 10-character binary number if number is
+ // negative
+ if (number < 0) {
+ return '1' + REPT('0', 9 - (512 + number).toString(2).length) + (512 + number).toString(2);
+ }
+
+ // Convert decimal number to binary
+ var result = parseInt(number, 10).toString(2);
+
+ // Return binary number using the minimum number of characters necessary
+ // if places is undefined
+ if (typeof places === 'undefined') {
+ return result;
+ } else {
+ // Return error if places is nonnumeric
+ if (isNaN(places)) {
+ return error.value;
+ }
+
+ // Return error if places is negative
+ if (places < 0) {
+ return error.num;
+ }
+
+ // Truncate places in case it is not an integer
+ places = Math.floor(places);
+
+ // Pad return value with leading 0s (zeros) if necessary (using
+ // Underscore.string)
+ return (places >= result.length) ? REPT('0', places - result.length) + result : error.num;
+ }
+ };
+
+ exports.DEC2HEX = function(number, places) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+
+ // Return error if number is not decimal, is lower than -549755813888,
+ // or is greater than 549755813887
+ if (!/^-?[0-9]{1,12}$/.test(number) || number < -549755813888 || number > 549755813887) {
+ return error.num;
+ }
+
+ // Ignore places and return a 10-character hexadecimal number if number
+ // is negative
+ if (number < 0) {
+ return (1099511627776 + number).toString(16);
+ }
+
+ // Convert decimal number to hexadecimal
+ var result = parseInt(number, 10).toString(16);
+
+ // Return hexadecimal number using the minimum number of characters
+ // necessary if places is undefined
+ if (typeof places === 'undefined') {
+ return result;
+ } else {
+ // Return error if places is nonnumeric
+ if (isNaN(places)) {
+ return error.value;
+ }
+
+ // Return error if places is negative
+ if (places < 0) {
+ return error.num;
+ }
+
+ // Truncate places in case it is not an integer
+ places = Math.floor(places);
+
+ // Pad return value with leading 0s (zeros) if necessary (using
+ // Underscore.string)
+ return (places >= result.length) ? REPT('0', places - result.length) + result : error.num;
+ }
+ };
+
+ exports.DEC2OCT = function(number, places) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+
+ // Return error if number is not decimal, is lower than -549755813888,
+ // or is greater than 549755813887
+ if (!/^-?[0-9]{1,9}$/.test(number) || number < -536870912 || number > 536870911) {
+ return error.num;
+ }
+
+ // Ignore places and return a 10-character octal number if number is
+ // negative
+ if (number < 0) {
+ return (1073741824 + number).toString(8);
+ }
+
+ // Convert decimal number to octal
+ var result = parseInt(number, 10).toString(8);
+
+ // Return octal number using the minimum number of characters necessary
+ // if places is undefined
+ if (typeof places === 'undefined') {
+ return result;
+ } else {
+ // Return error if places is nonnumeric
+ if (isNaN(places)) {
+ return error.value;
+ }
+
+ // Return error if places is negative
+ if (places < 0) {
+ return error.num;
+ }
+
+ // Truncate places in case it is not an integer
+ places = Math.floor(places);
+
+ // Pad return value with leading 0s (zeros) if necessary (using
+ // Underscore.string)
+ return (places >= result.length) ? REPT('0', places - result.length) + result : error.num;
+ }
+ };
+
+ exports.DELTA = function(number1, number2) {
+ // Set number2 to zero if undefined
+ number2 = (number2 === undefined) ? 0 : number2;
+ number1 = utils.parseNumber(number1);
+ number2 = utils.parseNumber(number2);
+ if (utils.anyIsError(number1, number2)) {
+ return error.value;
+ }
+
+ // Return delta
+ return (number1 === number2) ? 1 : 0;
+ };
+
+ exports.ERF = function(lower_bound, upper_bound) {
+ };
+
+ exports.ERF.PRECISE = function() {
+ };
+
+ exports.ERFC = function(x) {
+ };
+
+ exports.ERFC.PRECISE = function() {
+ };
+
+ exports.GESTEP = function(number, step) {
+ step = step || 0;
+ number = utils.parseNumber(number);
+ if (utils.anyIsError(step, number)) {
+ return number;
+ }
+
+ // Return delta
+ return (number >= step) ? 1 : 0;
+ };
+
+ exports.HEX2BIN = function(number, places) {
+ // Return error if number is not hexadecimal or contains more than ten
+ // characters (10 digits)
+ if (!/^[0-9A-Fa-f]{1,10}$/.test(number)) {
+ return error.num;
+ }
+
+ // Check if number is negative
+ var negative = (number.length === 10 && number.substring(0, 1).toLowerCase() === 'f') ? true : false;
+
+ // Convert hexadecimal number to decimal
+ var decimal = (negative) ? parseInt(number, 16) - 1099511627776 : parseInt(number, 16);
+
+ // Return error if number is lower than -512 or greater than 511
+ if (decimal < -512 || decimal > 511) {
+ return error.num;
+ }
+
+ // Ignore places and return a 10-character binary number if number is
+ // negative
+ if (negative) {
+ return '1' + REPT('0', 9 - (512 + decimal).toString(2).length) + (512 + decimal).toString(2);
+ }
+
+ // Convert decimal number to binary
+ var result = decimal.toString(2);
+
+ // Return binary number using the minimum number of characters necessary
+ // if places is undefined
+ if (places === undefined) {
+ return result;
+ } else {
+ // Return error if places is nonnumeric
+ if (isNaN(places)) {
+ return error.value;
+ }
+
+ // Return error if places is negative
+ if (places < 0) {
+ return error.num;
+ }
+
+ // Truncate places in case it is not an integer
+ places = Math.floor(places);
+
+ // Pad return value with leading 0s (zeros) if necessary (using
+ // Underscore.string)
+ return (places >= result.length) ? REPT('0', places - result.length) + result : error.num;
+ }
+ };
+
+ exports.HEX2DEC = function(number) {
+ // Return error if number is not hexadecimal or contains more than ten
+ // characters (10 digits)
+ if (!/^[0-9A-Fa-f]{1,10}$/.test(number)) {
+ return error.num;
+ }
+
+ // Convert hexadecimal number to decimal
+ var decimal = parseInt(number, 16);
+
+ // Return decimal number
+ return (decimal >= 549755813888) ? decimal - 1099511627776 : decimal;
+ };
+
+ exports.HEX2OCT = function(number, places) {
+ // Return error if number is not hexadecimal or contains more than ten
+ // characters (10 digits)
+ if (!/^[0-9A-Fa-f]{1,10}$/.test(number)) {
+ return error.num;
+ }
+
+ // Convert hexadecimal number to decimal
+ var decimal = parseInt(number, 16);
+
+ // Return error if number is positive and greater than 0x1fffffff
+ // (536870911)
+ if (decimal > 536870911 && decimal < 1098974756864) {
+ return error.num;
+ }
+
+ // Ignore places and return a 10-character octal number if number is
+ // negative
+ if (decimal >= 1098974756864) {
+ return (decimal - 1098437885952).toString(8);
+ }
+
+ // Convert decimal number to octal
+ var result = decimal.toString(8);
+
+ // Return octal number using the minimum number of characters necessary
+ // if places is undefined
+ if (places === undefined) {
+ return result;
+ } else {
+ // Return error if places is nonnumeric
+ if (isNaN(places)) {
+ return error.value;
+ }
+
+ // Return error if places is negative
+ if (places < 0) {
+ return error.num;
+ }
+
+ // Truncate places in case it is not an integer
+ places = Math.floor(places);
+
+ // Pad return value with leading 0s (zeros) if necessary (using
+ // Underscore.string)
+ return (places >= result.length) ? REPT('0', places - result.length) + result : error.num;
+ }
+ };
+
+ exports.IMABS = function(inumber) {
+ // Lookup real and imaginary coefficients using exports.js
+ // [http://formulajs.org]
+ var x = exports.IMREAL(inumber);
+ var y = exports.IMAGINARY(inumber);
+
+ // Return error if either coefficient is not a number
+ if (utils.anyIsError(x, y)) {
+ return error.value;
+ }
+
+ // Return absolute value of complex number
+ return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
+ };
+
+ exports.IMAGINARY = function(inumber) {
+ if (inumber === undefined || inumber === true || inumber === false) {
+ return error.value;
+ }
+
+ // Return 0 if inumber is equal to 0
+ if (inumber === 0 || inumber === '0') {
+ return 0;
+ }
+
+ // Handle special cases
+ if (['i', 'j'].indexOf(inumber) >= 0) {
+ return 1;
+ }
+
+ // Normalize imaginary coefficient
+ inumber = inumber.replace('+i', '+1i').replace('-i', '-1i').replace('+j', '+1j').replace('-j', '-1j');
+
+ // Lookup sign
+ var plus = inumber.indexOf('+');
+ var minus = inumber.indexOf('-');
+ if (plus === 0) {
+ plus = inumber.indexOf('+', 1);
+ }
+
+ if (minus === 0) {
+ minus = inumber.indexOf('-', 1);
+ }
+
+ // Lookup imaginary unit
+ var last = inumber.substring(inumber.length - 1, inumber.length);
+ var unit = (last === 'i' || last === 'j');
+
+ if (plus >= 0 || minus >= 0) {
+ // Return error if imaginary unit is neither i nor j
+ if (!unit) {
+ return error.num;
+ }
+
+ // Return imaginary coefficient of complex number
+ if (plus >= 0) {
+ return (isNaN(inumber.substring(0, plus)) || isNaN(inumber.substring(plus + 1, inumber.length - 1))) ?
+ error.num :
+ Number(inumber.substring(plus + 1, inumber.length - 1));
+ } else {
+ return (isNaN(inumber.substring(0, minus)) || isNaN(inumber.substring(minus + 1, inumber.length - 1))) ?
+ error.num :
+ -Number(inumber.substring(minus + 1, inumber.length - 1));
+ }
+ } else {
+ if (unit) {
+ return (isNaN(inumber.substring(0, inumber.length - 1))) ? error.num : inumber.substring(0, inumber.length - 1);
+ } else {
+ return (isNaN(inumber)) ? error.num : 0;
+ }
+ }
+ };
+
+ exports.IMARGUMENT = function(inumber) {
+ // Lookup real and imaginary coefficients using exports.js
+ // [http://formulajs.org]
+ var x = exports.IMREAL(inumber);
+ var y = exports.IMAGINARY(inumber);
+
+ // Return error if either coefficient is not a number
+ if (utils.anyIsError(x, y)) {
+ return error.value;
+ }
+
+ // Return error if inumber is equal to zero
+ if (x === 0 && y === 0) {
+ return error.div0;
+ }
+
+ // Return PI/2 if x is equal to zero and y is positive
+ if (x === 0 && y > 0) {
+ return Math.PI / 2;
+ }
+
+ // Return -PI/2 if x is equal to zero and y is negative
+ if (x === 0 && y < 0) {
+ return -Math.PI / 2;
+ }
+
+ // Return zero if x is negative and y is equal to zero
+ if (y === 0 && x > 0) {
+ return 0;
+ }
+
+ // Return zero if x is negative and y is equal to zero
+ if (y === 0 && x < 0) {
+ return -Math.PI;
+ }
+
+ // Return argument of complex number
+ if (x > 0) {
+ return Math.atan(y / x);
+ } else if (x < 0 && y >= 0) {
+ return Math.atan(y / x) + Math.PI;
+ } else {
+ return Math.atan(y / x) - Math.PI;
+ }
+ };
+
+ exports.IMCONJUGATE = function(inumber) {
+ // Lookup real and imaginary coefficients using exports.js
+ // [http://formulajs.org]
+ var x = exports.IMREAL(inumber);
+ var y = exports.IMAGINARY(inumber);
+
+ if (utils.anyIsError(x, y)) {
+ return error.value;
+ }
+
+ // Lookup imaginary unit
+ var unit = inumber.substring(inumber.length - 1);
+ unit = (unit === 'i' || unit === 'j') ? unit : 'i';
+
+ // Return conjugate of complex number
+ return (y !== 0) ? exports.COMPLEX(x, -y, unit) : inumber;
+ };
+
+ exports.IMCOS = function(inumber) {
+ // Lookup real and imaginary coefficients using exports.js
+ // [http://formulajs.org]
+ var x = exports.IMREAL(inumber);
+ var y = exports.IMAGINARY(inumber);
+
+ if (utils.anyIsError(x, y)) {
+ return error.value;
+ }
+
+ // Lookup imaginary unit
+ var unit = inumber.substring(inumber.length - 1);
+ unit = (unit === 'i' || unit === 'j') ? unit : 'i';
+
+ // Return cosine of complex number
+ return exports.COMPLEX(Math.cos(x) * (Math.exp(y) + Math.exp(-y)) / 2, -Math.sin(x) * (Math.exp(y) - Math.exp(-y)) / 2, unit);
+ };
+
+ exports.IMCOSH = function(inumber) {
+ // Lookup real and imaginary coefficients using exports.js
+ // [http://formulajs.org]
+ var x = exports.IMREAL(inumber);
+ var y = exports.IMAGINARY(inumber);
+
+ if (utils.anyIsError(x, y)) {
+ return error.value;
+ }
+
+ // Lookup imaginary unit
+ var unit = inumber.substring(inumber.length - 1);
+ unit = (unit === 'i' || unit === 'j') ? unit : 'i';
+
+ // Return hyperbolic cosine of complex number
+ return exports.COMPLEX(Math.cos(y) * (Math.exp(x) + Math.exp(-x)) / 2, Math.sin(y) * (Math.exp(x) - Math.exp(-x)) / 2, unit);
+ };
+
+ exports.IMCOT = function(inumber) {
+ // Lookup real and imaginary coefficients using Formula.js
+ // [http://formulajs.org]
+ var x = exports.IMREAL(inumber);
+ var y = exports.IMAGINARY(inumber);
+
+ if (utils.anyIsError(x, y)) {
+ return error.value;
+ }
+
+ // Return cotangent of complex number
+ return exports.IMDIV(exports.IMCOS(inumber), exports.IMSIN(inumber));
+ };
+
+ exports.IMDIV = function(inumber1, inumber2) {
+ // Lookup real and imaginary coefficients using Formula.js
+ // [http://formulajs.org]
+ var a = exports.IMREAL(inumber1);
+ var b = exports.IMAGINARY(inumber1);
+ var c = exports.IMREAL(inumber2);
+ var d = exports.IMAGINARY(inumber2);
+
+ if (utils.anyIsError(a, b, c, d)) {
+ return error.value;
+ }
+
+ // Lookup imaginary unit
+ var unit1 = inumber1.substring(inumber1.length - 1);
+ var unit2 = inumber2.substring(inumber2.length - 1);
+ var unit = 'i';
+ if (unit1 === 'j') {
+ unit = 'j';
+ } else if (unit2 === 'j') {
+ unit = 'j';
+ }
+
+ // Return error if inumber2 is null
+ if (c === 0 && d === 0) {
+ return error.num;
+ }
+
+ // Return exponential of complex number
+ var den = c * c + d * d;
+ return exports.COMPLEX((a * c + b * d) / den, (b * c - a * d) / den, unit);
+ };
+
+ exports.IMEXP = function(inumber) {
+ // Lookup real and imaginary coefficients using Formula.js
+ // [http://formulajs.org]
+ var x = exports.IMREAL(inumber);
+ var y = exports.IMAGINARY(inumber);
+
+ if (utils.anyIsError(x, y)) {
+ return error.value;
+ }
+
+ // Lookup imaginary unit
+ var unit = inumber.substring(inumber.length - 1);
+ unit = (unit === 'i' || unit === 'j') ? unit : 'i';
+
+ // Return exponential of complex number
+ var e = Math.exp(x);
+ return exports.COMPLEX(e * Math.cos(y), e * Math.sin(y), unit);
+ };
+
+ exports.IMLN = function(inumber) {
+ // Lookup real and imaginary coefficients using Formula.js
+ // [http://formulajs.org]
+ var x = exports.IMREAL(inumber);
+ var y = exports.IMAGINARY(inumber);
+
+ if (utils.anyIsError(x, y)) {
+ return error.value;
+ }
+
+ // Lookup imaginary unit
+ var unit = inumber.substring(inumber.length - 1);
+ unit = (unit === 'i' || unit === 'j') ? unit : 'i';
+
+ // Return exponential of complex number
+ return exports.COMPLEX(Math.log(Math.sqrt(x * x + y * y)), Math.atan(y / x), unit);
+ };
+
+ exports.IMLOG10 = function(inumber) {
+ // Lookup real and imaginary coefficients using Formula.js
+ // [http://formulajs.org]
+ var x = exports.IMREAL(inumber);
+ var y = exports.IMAGINARY(inumber);
+
+ if (utils.anyIsError(x, y)) {
+ return error.value;
+ }
+
+ // Lookup imaginary unit
+ var unit = inumber.substring(inumber.length - 1);
+ unit = (unit === 'i' || unit === 'j') ? unit : 'i';
+
+ // Return exponential of complex number
+ return exports.COMPLEX(Math.log(Math.sqrt(x * x + y * y)) / Math.log(10), Math.atan(y / x) / Math.log(10), unit);
+ };
+
+ exports.IMLOG2 = function(inumber) {
+ // Lookup real and imaginary coefficients using Formula.js
+ // [http://formulajs.org]
+ var x = exports.IMREAL(inumber);
+ var y = exports.IMAGINARY(inumber);
+
+ if (utils.anyIsError(x, y)) {
+ return error.value;
+ }
+
+ // Lookup imaginary unit
+ var unit = inumber.substring(inumber.length - 1);
+ unit = (unit === 'i' || unit === 'j') ? unit : 'i';
+
+ // Return exponential of complex number
+ return exports.COMPLEX(Math.log(Math.sqrt(x * x + y * y)) / Math.log(2), Math.atan(y / x) / Math.log(2), unit);
+ };
+
+ exports.IMPOWER = function(inumber, number) {
+ number = utils.parseNumber(number);
+ var x = exports.IMREAL(inumber);
+ var y = exports.IMAGINARY(inumber);
+ if (utils.anyIsError(number, x, y)) {
+ return error.value;
+ }
+
+ // Lookup imaginary unit
+ var unit = inumber.substring(inumber.length - 1);
+ unit = (unit === 'i' || unit === 'j') ? unit : 'i';
+
+ // Calculate power of modulus
+ var p = Math.pow(exports.IMABS(inumber), number);
+
+ // Calculate argument
+ var t = exports.IMARGUMENT(inumber);
+
+ // Return exponential of complex number
+ return exports.COMPLEX(p * Math.cos(number * t), p * Math.sin(number * t), unit);
+ };
+
+ exports.IMPRODUCT = function() {
+ // Initialize result
+ var result = arguments[0];
+
+ // Loop on all numbers
+ for (var i = 1; i < arguments.length; i++) {
+ // Lookup coefficients of two complex numbers
+ var a = exports.IMREAL(result);
+ var b = exports.IMAGINARY(result);
+ var c = exports.IMREAL(arguments[i]);
+ var d = exports.IMAGINARY(arguments[i]);
+
+ if (utils.anyIsError(a, b, c, d)) {
+ return error.value;
+ }
+
+ // Complute product of two complex numbers
+ result = exports.COMPLEX(a * c - b * d, a * d + b * c);
+ }
+
+ // Return product of complex numbers
+ return result;
+ };
+
+ exports.IMREAL = function(inumber) {
+ if (inumber === undefined || inumber === true || inumber === false) {
+ return error.value;
+ }
+
+ // Return 0 if inumber is equal to 0
+ if (inumber === 0 || inumber === '0') {
+ return 0;
+ }
+
+ // Handle special cases
+ if (['i', '+i', '1i', '+1i', '-i', '-1i', 'j', '+j', '1j', '+1j', '-j', '-1j'].indexOf(inumber) >= 0) {
+ return 0;
+ }
+
+ // Lookup sign
+ var plus = inumber.indexOf('+');
+ var minus = inumber.indexOf('-');
+ if (plus === 0) {
+ plus = inumber.indexOf('+', 1);
+ }
+ if (minus === 0) {
+ minus = inumber.indexOf('-', 1);
+ }
+
+ // Lookup imaginary unit
+ var last = inumber.substring(inumber.length - 1, inumber.length);
+ var unit = (last === 'i' || last === 'j');
+
+ if (plus >= 0 || minus >= 0) {
+ // Return error if imaginary unit is neither i nor j
+ if (!unit) {
+ return error.num;
+ }
+
+ // Return real coefficient of complex number
+ if (plus >= 0) {
+ return (isNaN(inumber.substring(0, plus)) || isNaN(inumber.substring(plus + 1, inumber.length - 1))) ?
+ error.num :
+ Number(inumber.substring(0, plus));
+ } else {
+ return (isNaN(inumber.substring(0, minus)) || isNaN(inumber.substring(minus + 1, inumber.length - 1))) ?
+ error.num :
+ Number(inumber.substring(0, minus));
+ }
+ } else {
+ if (unit) {
+ return (isNaN(inumber.substring(0, inumber.length - 1))) ? error.num : 0;
+ } else {
+ return (isNaN(inumber)) ? error.num : inumber;
+ }
+ }
+ };
+
+ exports.IMSEC = function(inumber) {
+ // Return error if inumber is a logical value
+ if (inumber === true || inumber === false) {
+ return error.value;
+ }
+
+ // Lookup real and imaginary coefficients using Formula.js
+ // [http://formulajs.org]
+ var x = exports.IMREAL(inumber);
+ var y = exports.IMAGINARY(inumber);
+
+ if (utils.anyIsError(x, y)) {
+ return error.value;
+ }
+
+ // Return secant of complex number
+ return exports.IMDIV('1', exports.IMCOS(inumber));
+ };
+
+ exports.IMSECH = function(inumber) {
+ // Lookup real and imaginary coefficients using Formula.js
+ // [http://formulajs.org]
+ var x = exports.IMREAL(inumber);
+ var y = exports.IMAGINARY(inumber);
+
+ if (utils.anyIsError(x, y)) {
+ return error.value;
+ }
+
+ // Return hyperbolic secant of complex number
+ return exports.IMDIV('1', exports.IMCOSH(inumber));
+ };
+
+ exports.IMSIN = function(inumber) {
+ // Lookup real and imaginary coefficients using Formula.js
+ // [http://formulajs.org]
+ var x = exports.IMREAL(inumber);
+ var y = exports.IMAGINARY(inumber);
+
+ if (utils.anyIsError(x, y)) {
+ return error.value;
+ }
+
+ // Lookup imaginary unit
+ var unit = inumber.substring(inumber.length - 1);
+ unit = (unit === 'i' || unit === 'j') ? unit : 'i';
+
+ // Return sine of complex number
+ return exports.COMPLEX(Math.sin(x) * (Math.exp(y) + Math.exp(-y)) / 2, Math.cos(x) * (Math.exp(y) - Math.exp(-y)) / 2, unit);
+ };
+
+ exports.IMSINH = function(inumber) {
+ // Lookup real and imaginary coefficients using Formula.js
+ // [http://formulajs.org]
+ var x = exports.IMREAL(inumber);
+ var y = exports.IMAGINARY(inumber);
+
+ if (utils.anyIsError(x, y)) {
+ return error.value;
+ }
+
+ // Lookup imaginary unit
+ var unit = inumber.substring(inumber.length - 1);
+ unit = (unit === 'i' || unit === 'j') ? unit : 'i';
+
+ // Return hyperbolic sine of complex number
+ return exports.COMPLEX(Math.cos(y) * (Math.exp(x) - Math.exp(-x)) / 2, Math.sin(y) * (Math.exp(x) + Math.exp(-x)) / 2, unit);
+ };
+
+ exports.IMSQRT = function(inumber) {
+ // Lookup real and imaginary coefficients using Formula.js
+ // [http://formulajs.org]
+ var x = exports.IMREAL(inumber);
+ var y = exports.IMAGINARY(inumber);
+
+ if (utils.anyIsError(x, y)) {
+ return error.value;
+ }
+
+ // Lookup imaginary unit
+ var unit = inumber.substring(inumber.length - 1);
+ unit = (unit === 'i' || unit === 'j') ? unit : 'i';
+
+ // Calculate power of modulus
+ var s = Math.sqrt(exports.IMABS(inumber));
+
+ // Calculate argument
+ var t = exports.IMARGUMENT(inumber);
+
+ // Return exponential of complex number
+ return exports.COMPLEX(s * Math.cos(t / 2), s * Math.sin(t / 2), unit);
+ };
+
+ exports.IMCSC = function (inumber) {
+ // Return error if inumber is a logical value
+ if (inumber === true || inumber === false) {
+ return error.value;
+ }
+
+ // Lookup real and imaginary coefficients using Formula.js
+ // [http://formulajs.org]
+ var x = exports.IMREAL(inumber);
+ var y = exports.IMAGINARY(inumber);
+
+ // Return error if either coefficient is not a number
+ if (utils.anyIsError(x, y)) {
+ return error.num;
+ }
+
+ // Return cosecant of complex number
+ return exports.IMDIV('1', exports.IMSIN(inumber));
+ };
+
+ exports.IMCSCH = function (inumber) {
+ // Return error if inumber is a logical value
+ if (inumber === true || inumber === false) {
+ return error.value;
+ }
+
+ // Lookup real and imaginary coefficients using Formula.js
+ // [http://formulajs.org]
+ var x = exports.IMREAL(inumber);
+ var y = exports.IMAGINARY(inumber);
+
+ // Return error if either coefficient is not a number
+ if (utils.anyIsError(x, y)) {
+ return error.num;
+ }
+
+ // Return hyperbolic cosecant of complex number
+ return exports.IMDIV('1', exports.IMSINH(inumber));
+ };
+
+ exports.IMSUB = function(inumber1, inumber2) {
+ // Lookup real and imaginary coefficients using Formula.js
+ // [http://formulajs.org]
+ var a = this.IMREAL(inumber1);
+ var b = this.IMAGINARY(inumber1);
+ var c = this.IMREAL(inumber2);
+ var d = this.IMAGINARY(inumber2);
+
+ if (utils.anyIsError(a, b, c, d)) {
+ return error.value;
+ }
+
+ // Lookup imaginary unit
+ var unit1 = inumber1.substring(inumber1.length - 1);
+ var unit2 = inumber2.substring(inumber2.length - 1);
+ var unit = 'i';
+ if (unit1 === 'j') {
+ unit = 'j';
+ } else if (unit2 === 'j') {
+ unit = 'j';
+ }
+
+ // Return _ of two complex numbers
+ return this.COMPLEX(a - c, b - d, unit);
+ };
+
+ exports.IMSUM = function() {
+ var args = utils.flatten(arguments);
+
+ // Initialize result
+ var result = args[0];
+
+ // Loop on all numbers
+ for (var i = 1; i < args.length; i++) {
+ // Lookup coefficients of two complex numbers
+ var a = this.IMREAL(result);
+ var b = this.IMAGINARY(result);
+ var c = this.IMREAL(args[i]);
+ var d = this.IMAGINARY(args[i]);
+
+ if (utils.anyIsError(a, b, c, d)) {
+ return error.value;
+ }
+
+ // Complute product of two complex numbers
+ result = this.COMPLEX(a + c, b + d);
+ }
+
+ // Return sum of complex numbers
+ return result;
+ };
+
+ exports.IMTAN = function(inumber) {
+ // Return error if inumber is a logical value
+ if (inumber === true || inumber === false) {
+ return error.value;
+ }
+
+ // Lookup real and imaginary coefficients using Formula.js
+ // [http://formulajs.org]
+ var x = exports.IMREAL(inumber);
+ var y = exports.IMAGINARY(inumber);
+
+ if (utils.anyIsError(x, y)) {
+ return error.value;
+ }
+
+ // Return tangent of complex number
+ return this.IMDIV(this.IMSIN(inumber), this.IMCOS(inumber));
+ };
+
+ exports.OCT2BIN = function(number, places) {
+ // Return error if number is not hexadecimal or contains more than ten
+ // characters (10 digits)
+ if (!/^[0-7]{1,10}$/.test(number)) {
+ return error.num;
+ }
+
+ // Check if number is negative
+ var negative = (number.length === 10 && number.substring(0, 1) === '7') ? true : false;
+
+ // Convert octal number to decimal
+ var decimal = (negative) ? parseInt(number, 8) - 1073741824 : parseInt(number, 8);
+
+ // Return error if number is lower than -512 or greater than 511
+ if (decimal < -512 || decimal > 511) {
+ return error.num;
+ }
+
+ // Ignore places and return a 10-character binary number if number is
+ // negative
+ if (negative) {
+ return '1' + REPT('0', 9 - (512 + decimal).toString(2).length) + (512 + decimal).toString(2);
+ }
+
+ // Convert decimal number to binary
+ var result = decimal.toString(2);
+
+ // Return binary number using the minimum number of characters necessary
+ // if places is undefined
+ if (typeof places === 'undefined') {
+ return result;
+ } else {
+ // Return error if places is nonnumeric
+ if (isNaN(places)) {
+ return error.value;
+ }
+
+ // Return error if places is negative
+ if (places < 0) {
+ return error.num;
+ }
+
+ // Truncate places in case it is not an integer
+ places = Math.floor(places);
+
+ // Pad return value with leading 0s (zeros) if necessary (using
+ // Underscore.string)
+ return (places >= result.length) ? REPT('0', places - result.length) + result : error.num;
+ }
+ };
+
+ exports.OCT2DEC = function(number) {
+ // Return error if number is not octal or contains more than ten
+ // characters (10 digits)
+ if (!/^[0-7]{1,10}$/.test(number)) {
+ return error.num;
+ }
+
+ // Convert octal number to decimal
+ var decimal = parseInt(number, 8);
+
+ // Return decimal number
+ return (decimal >= 536870912) ? decimal - 1073741824 : decimal;
+ };
+
+ exports.OCT2HEX = function(number, places) {
+ // Return error if number is not octal or contains more than ten
+ // characters (10 digits)
+ if (!/^[0-7]{1,10}$/.test(number)) {
+ return error.num;
+ }
+
+ // Convert octal number to decimal
+ var decimal = parseInt(number, 8);
+
+ // Ignore places and return a 10-character octal number if number is
+ // negative
+ if (decimal >= 536870912) {
+ return 'ff' + (decimal + 3221225472).toString(16);
+ }
+
+ // Convert decimal number to hexadecimal
+ var result = decimal.toString(16);
+
+ // Return hexadecimal number using the minimum number of characters
+ // necessary if places is undefined
+ if (places === undefined) {
+ return result;
+ } else {
+ // Return error if places is nonnumeric
+ if (isNaN(places)) {
+ return error.value;
+ }
+
+ // Return error if places is negative
+ if (places < 0) {
+ return error.num;
+ }
+
+ // Truncate places in case it is not an integer
+ places = Math.floor(places);
+
+ // Pad return value with leading 0s (zeros) if necessary (using
+ // Underscore.string)
+ return (places >= result.length) ? REPT('0', places - result.length) + result : error.num;
+ }
+ };
+
+ return exports;
+ })();
+
+ jexcel.methods.financial = (function() {
+ var exports = {};
+
+ function validDate(d) {
+ return d && d.getTime && !isNaN(d.getTime());
+ }
+
+ function ensureDate(d) {
+ return (d instanceof Date)?d:new Date(d);
+ }
+
+ exports.ACCRINT = function(issue, first, settlement, rate, par, frequency, basis) {
+ // Return error if either date is invalid
+ issue = ensureDate(issue);
+ first = ensureDate(first);
+ settlement = ensureDate(settlement);
+ if (!validDate(issue) || !validDate(first) || !validDate(settlement)) {
+ return '#VALUE!';
+ }
+
+ // Return error if either rate or par are lower than or equal to zero
+ if (rate <= 0 || par <= 0) {
+ return '#NUM!';
+ }
+
+ // Return error if frequency is neither 1, 2, or 4
+ if ([1, 2, 4].indexOf(frequency) === -1) {
+ return '#NUM!';
+ }
+
+ // Return error if basis is neither 0, 1, 2, 3, or 4
+ if ([0, 1, 2, 3, 4].indexOf(basis) === -1) {
+ return '#NUM!';
+ }
+
+ // Return error if settlement is before or equal to issue
+ if (settlement <= issue) {
+ return '#NUM!';
+ }
+
+ // Set default values
+ par = par || 0;
+ basis = basis || 0;
+
+ // Compute accrued interest
+ return par * rate * YEARFRAC(issue, settlement, basis);
+ };
+
+ exports.ACCRINTM = null;
+
+ exports.AMORDEGRC = null;
+
+ exports.AMORLINC = null;
+
+ exports.COUPDAYBS = null;
+
+ exports.COUPDAYS = null;
+
+ exports.COUPDAYSNC = null;
+
+ exports.COUPNCD = null;
+
+ exports.COUPNUM = null;
+
+ exports.COUPPCD = null;
+
+ exports.CUMIPMT = function(rate, periods, value, start, end, type) {
+ // Credits: algorithm inspired by Apache OpenOffice
+ // Credits: Hannes Stiebitzhofer for the translations of function and
+ // variable names
+ // Requires exports.FV() and exports.PMT() from exports.js
+ // [http://stoic.com/exports/]
+
+ rate = utils.parseNumber(rate);
+ periods = utils.parseNumber(periods);
+ value = utils.parseNumber(value);
+ if (utils.anyIsError(rate, periods, value)) {
+ return error.value;
+ }
+
+ // Return error if either rate, periods, or value are lower than or
+ // equal to zero
+ if (rate <= 0 || periods <= 0 || value <= 0) {
+ return error.num;
+ }
+
+ // Return error if start < 1, end < 1, or start > end
+ if (start < 1 || end < 1 || start > end) {
+ return error.num;
+ }
+
+ // Return error if type is neither 0 nor 1
+ if (type !== 0 && type !== 1) {
+ return error.num;
+ }
+
+ // Compute cumulative interest
+ var payment = exports.PMT(rate, periods, value, 0, type);
+ var interest = 0;
+
+ if (start === 1) {
+ if (type === 0) {
+ interest = -value;
+ start++;
+ }
+ }
+
+ for (var i = start; i <= end; i++) {
+ if (type === 1) {
+ interest += exports.FV(rate, i - 2, payment, value, 1) - payment;
+ } else {
+ interest += exports.FV(rate, i - 1, payment, value, 0);
+ }
+ }
+ interest *= rate;
+
+ // Return cumulative interest
+ return interest;
+ };
+
+ exports.CUMPRINC = function(rate, periods, value, start, end, type) {
+ // Credits: algorithm inspired by Apache OpenOffice
+ // Credits: Hannes Stiebitzhofer for the translations of function and
+ // variable names
+
+ rate = utils.parseNumber(rate);
+ periods = utils.parseNumber(periods);
+ value = utils.parseNumber(value);
+ if (utils.anyIsError(rate, periods, value)) {
+ return error.value;
+ }
+
+ // Return error if either rate, periods, or value are lower than or
+ // equal to zero
+ if (rate <= 0 || periods <= 0 || value <= 0) {
+ return error.num;
+ }
+
+ // Return error if start < 1, end < 1, or start > end
+ if (start < 1 || end < 1 || start > end) {
+ return error.num;
+ }
+
+ // Return error if type is neither 0 nor 1
+ if (type !== 0 && type !== 1) {
+ return error.num;
+ }
+
+ // Compute cumulative principal
+ var payment = exports.PMT(rate, periods, value, 0, type);
+ var principal = 0;
+ if (start === 1) {
+ if (type === 0) {
+ principal = payment + value * rate;
+ } else {
+ principal = payment;
+ }
+ start++;
+ }
+ for (var i = start; i <= end; i++) {
+ if (type > 0) {
+ principal += payment - (exports.FV(rate, i - 2, payment, value, 1) - payment) * rate;
+ } else {
+ principal += payment - exports.FV(rate, i - 1, payment, value, 0) * rate;
+ }
+ }
+
+ // Return cumulative principal
+ return principal;
+ };
+
+ exports.DB = function(cost, salvage, life, period, month) {
+ // Initialize month
+ month = (month === undefined) ? 12 : month;
+
+ cost = utils.parseNumber(cost);
+ salvage = utils.parseNumber(salvage);
+ life = utils.parseNumber(life);
+ period = utils.parseNumber(period);
+ month = utils.parseNumber(month);
+ if (utils.anyIsError(cost, salvage, life, period, month)) {
+ return error.value;
+ }
+
+ // Return error if any of the parameters is negative
+ if (cost < 0 || salvage < 0 || life < 0 || period < 0) {
+ return error.num;
+ }
+
+ // Return error if month is not an integer between 1 and 12
+ if ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].indexOf(month) === -1) {
+ return error.num;
+ }
+
+ // Return error if period is greater than life
+ if (period > life) {
+ return error.num;
+ }
+
+ // Return 0 (zero) if salvage is greater than or equal to cost
+ if (salvage >= cost) {
+ return 0;
+ }
+
+ // Rate is rounded to three decimals places
+ var rate = (1 - Math.pow(salvage / cost, 1 / life)).toFixed(3);
+
+ // Compute initial depreciation
+ var initial = cost * rate * month / 12;
+
+ // Compute total depreciation
+ var total = initial;
+ var current = 0;
+ var ceiling = (period === life) ? life - 1 : period;
+ for (var i = 2; i <= ceiling; i++) {
+ current = (cost - total) * rate;
+ total += current;
+ }
+
+ // Depreciation for the first and last periods are special cases
+ if (period === 1) {
+ // First period
+ return initial;
+ } else if (period === life) {
+ // Last period
+ return (cost - total) * rate;
+ } else {
+ return current;
+ }
+ };
+
+ exports.DDB = function(cost, salvage, life, period, factor) {
+ // Initialize factor
+ factor = (factor === undefined) ? 2 : factor;
+
+ cost = utils.parseNumber(cost);
+ salvage = utils.parseNumber(salvage);
+ life = utils.parseNumber(life);
+ period = utils.parseNumber(period);
+ factor = utils.parseNumber(factor);
+ if (utils.anyIsError(cost, salvage, life, period, factor)) {
+ return error.value;
+ }
+
+ // Return error if any of the parameters is negative or if factor is
+ // null
+ if (cost < 0 || salvage < 0 || life < 0 || period < 0 || factor <= 0) {
+ return error.num;
+ }
+
+ // Return error if period is greater than life
+ if (period > life) {
+ return error.num;
+ }
+
+ // Return 0 (zero) if salvage is greater than or equal to cost
+ if (salvage >= cost) {
+ return 0;
+ }
+
+ // Compute depreciation
+ var total = 0;
+ var current = 0;
+ for (var i = 1; i <= period; i++) {
+ current = Math.min((cost - total) * (factor / life), (cost - salvage - total));
+ total += current;
+ }
+
+ // Return depreciation
+ return current;
+ };
+
+ exports.DISC = null;
+
+ exports.DOLLARDE = function(dollar, fraction) {
+ // Credits: algorithm inspired by Apache OpenOffice
+
+ dollar = utils.parseNumber(dollar);
+ fraction = utils.parseNumber(fraction);
+ if (utils.anyIsError(dollar, fraction)) {
+ return error.value;
+ }
+
+ // Return error if fraction is negative
+ if (fraction < 0) {
+ return error.num;
+ }
+
+ // Return error if fraction is greater than or equal to 0 and less than
+ // 1
+ if (fraction >= 0 && fraction < 1) {
+ return error.div0;
+ }
+
+ // Truncate fraction if it is not an integer
+ fraction = parseInt(fraction, 10);
+
+ // Compute integer part
+ var result = parseInt(dollar, 10);
+
+ // Add decimal part
+ result += (dollar % 1) * Math.pow(10, Math.ceil(Math.log(fraction) / Math.LN10)) / fraction;
+
+ // Round result
+ var power = Math.pow(10, Math.ceil(Math.log(fraction) / Math.LN2) + 1);
+ result = Math.round(result * power) / power;
+
+ // Return converted dollar price
+ return result;
+ };
+
+ exports.DOLLARFR = function(dollar, fraction) {
+ // Credits: algorithm inspired by Apache OpenOffice
+
+ dollar = utils.parseNumber(dollar);
+ fraction = utils.parseNumber(fraction);
+ if (utils.anyIsError(dollar, fraction)) {
+ return error.value;
+ }
+
+ // Return error if fraction is negative
+ if (fraction < 0) {
+ return error.num;
+ }
+
+ // Return error if fraction is greater than or equal to 0 and less than
+ // 1
+ if (fraction >= 0 && fraction < 1) {
+ return error.div0;
+ }
+
+ // Truncate fraction if it is not an integer
+ fraction = parseInt(fraction, 10);
+
+ // Compute integer part
+ var result = parseInt(dollar, 10);
+
+ // Add decimal part
+ result += (dollar % 1) * Math.pow(10, -Math.ceil(Math.log(fraction) / Math.LN10)) * fraction;
+
+ // Return converted dollar price
+ return result;
+ };
+
+ exports.DURATION = null;
+
+ exports.EFFECT = function(rate, periods) {
+ rate = utils.parseNumber(rate);
+ periods = utils.parseNumber(periods);
+ if (utils.anyIsError(rate, periods)) {
+ return error.value;
+ }
+
+ // Return error if rate <=0 or periods < 1
+ if (rate <= 0 || periods < 1) {
+ return error.num;
+ }
+
+ // Truncate periods if it is not an integer
+ periods = parseInt(periods, 10);
+
+ // Return effective annual interest rate
+ return Math.pow(1 + rate / periods, periods) - 1;
+ };
+
+ exports.FV = function(rate, periods, payment, value, type) {
+ // Credits: algorithm inspired by Apache OpenOffice
+
+ value = value || 0;
+ type = type || 0;
+
+ rate = utils.parseNumber(rate);
+ periods = utils.parseNumber(periods);
+ payment = utils.parseNumber(payment);
+ value = utils.parseNumber(value);
+ type = utils.parseNumber(type);
+ if (utils.anyIsError(rate, periods, payment, value, type)) {
+ return error.value;
+ }
+
+ // Return future value
+ var result;
+ if (rate === 0) {
+ result = value + payment * periods;
+ } else {
+ var term = Math.pow(1 + rate, periods);
+ if (type === 1) {
+ result = value * term + payment * (1 + rate) * (term - 1) / rate;
+ } else {
+ result = value * term + payment * (term - 1) / rate;
+ }
+ }
+ return -result;
+ };
+
+ exports.FVSCHEDULE = function(principal, schedule) {
+ principal = utils.parseNumber(principal);
+ schedule = utils.parseNumberArray(utils.flatten(schedule));
+ if (utils.anyIsError(principal, schedule)) {
+ return error.value;
+ }
+
+ var n = schedule.length;
+ var future = principal;
+
+ // Apply all interests in schedule
+ for (var i = 0; i < n; i++) {
+ // Apply scheduled interest
+ future *= 1 + schedule[i];
+ }
+
+ // Return future value
+ return future;
+ };
+
+ exports.INTRATE = null;
+
+ exports.IPMT = function(rate, period, periods, present, future, type) {
+ // Credits: algorithm inspired by Apache OpenOffice
+
+ future = future || 0;
+ type = type || 0;
+
+ rate = utils.parseNumber(rate);
+ period = utils.parseNumber(period);
+ periods = utils.parseNumber(periods);
+ present = utils.parseNumber(present);
+ future = utils.parseNumber(future);
+ type = utils.parseNumber(type);
+ if (utils.anyIsError(rate, period, periods, present, future, type)) {
+ return error.value;
+ }
+
+ // Compute payment
+ var payment = exports.PMT(rate, periods, present, future, type);
+
+ // Compute interest
+ var interest;
+ if (period === 1) {
+ if (type === 1) {
+ interest = 0;
+ } else {
+ interest = -present;
+ }
+ } else {
+ if (type === 1) {
+ interest = exports.FV(rate, period - 2, payment, present, 1) - payment;
+ } else {
+ interest = exports.FV(rate, period - 1, payment, present, 0);
+ }
+ }
+
+ // Return interest
+ return interest * rate;
+ };
+
+ exports.IRR = function(values, guess) {
+ // Credits: algorithm inspired by Apache OpenOffice
+
+ guess = guess || 0;
+
+ values = utils.parseNumberArray(utils.flatten(values));
+ guess = utils.parseNumber(guess);
+ if (utils.anyIsError(values, guess)) {
+ return error.value;
+ }
+
+ // Calculates the resulting amount
+ var irrResult = function(values, dates, rate) {
+ var r = rate + 1;
+ var result = values[0];
+ for (var i = 1; i < values.length; i++) {
+ result += values[i] / Math.pow(r, (dates[i] - dates[0]) / 365);
+ }
+ return result;
+ };
+
+ // Calculates the first derivation
+ var irrResultDeriv = function(values, dates, rate) {
+ var r = rate + 1;
+ var result = 0;
+ for (var i = 1; i < values.length; i++) {
+ var frac = (dates[i] - dates[0]) / 365;
+ result -= frac * values[i] / Math.pow(r, frac + 1);
+ }
+ return result;
+ };
+
+ // Initialize dates and check that values contains at least one positive
+ // value and one negative value
+ var dates = [];
+ var positive = false;
+ var negative = false;
+ for (var i = 0; i < values.length; i++) {
+ dates[i] = (i === 0) ? 0 : dates[i - 1] + 365;
+ if (values[i] > 0) {
+ positive = true;
+ }
+ if (values[i] < 0) {
+ negative = true;
+ }
+ }
+
+ // Return error if values does not contain at least one positive value
+ // and one negative value
+ if (!positive || !negative) {
+ return error.num;
+ }
+
+ // Initialize guess and resultRate
+ guess = (guess === undefined) ? 0.1 : guess;
+ var resultRate = guess;
+
+ // Set maximum epsilon for end of iteration
+ var epsMax = 1e-10;
+
+ // Implement Newton's method
+ var newRate, epsRate, resultValue;
+ var contLoop = true;
+ do {
+ resultValue = irrResult(values, dates, resultRate);
+ newRate = resultRate - resultValue / irrResultDeriv(values, dates, resultRate);
+ epsRate = Math.abs(newRate - resultRate);
+ resultRate = newRate;
+ contLoop = (epsRate > epsMax) && (Math.abs(resultValue) > epsMax);
+ } while (contLoop);
+
+ // Return internal rate of return
+ return resultRate;
+ };
+
+ exports.ISPMT = function(rate, period, periods, value) {
+ rate = utils.parseNumber(rate);
+ period = utils.parseNumber(period);
+ periods = utils.parseNumber(periods);
+ value = utils.parseNumber(value);
+ if (utils.anyIsError(rate, period, periods, value)) {
+ return error.value;
+ }
+
+ // Return interest
+ return value * rate * (period / periods - 1);
+ };
+
+ exports.MDURATION = null;
+
+ exports.MIRR = function(values, finance_rate, reinvest_rate) {
+ values = utils.parseNumberArray(utils.flatten(values));
+ finance_rate = utils.parseNumber(finance_rate);
+ reinvest_rate = utils.parseNumber(reinvest_rate);
+ if (utils.anyIsError(values, finance_rate, reinvest_rate)) {
+ return error.value;
+ }
+
+ // Initialize number of values
+ var n = values.length;
+
+ // Lookup payments (negative values) and incomes (positive values)
+ var payments = [];
+ var incomes = [];
+ for (var i = 0; i < n; i++) {
+ if (values[i] < 0) {
+ payments.push(values[i]);
+ } else {
+ incomes.push(values[i]);
+ }
+ }
+
+ // Return modified internal rate of return
+ var num = -exports.NPV(reinvest_rate, incomes) * Math.pow(1 + reinvest_rate, n - 1);
+ var den = exports.NPV(finance_rate, payments) * (1 + finance_rate);
+ return Math.pow(num / den, 1 / (n - 1)) - 1;
+ };
+
+ exports.NOMINAL = function(rate, periods) {
+ rate = utils.parseNumber(rate);
+ periods = utils.parseNumber(periods);
+ if (utils.anyIsError(rate, periods)) {
+ return error.value;
+ }
+
+ // Return error if rate <=0 or periods < 1
+ if (rate <= 0 || periods < 1) {
+ return error.num;
+ }
+
+ // Truncate periods if it is not an integer
+ periods = parseInt(periods, 10);
+
+ // Return nominal annual interest rate
+ return (Math.pow(rate + 1, 1 / periods) - 1) * periods;
+ };
+
+ exports.NPER = function(rate, payment, present, future, type) {
+ type = (type === undefined) ? 0 : type;
+ future = (future === undefined) ? 0 : future;
+
+ rate = utils.parseNumber(rate);
+ payment = utils.parseNumber(payment);
+ present = utils.parseNumber(present);
+ future = utils.parseNumber(future);
+ type = utils.parseNumber(type);
+ if (utils.anyIsError(rate, payment, present, future, type)) {
+ return error.value;
+ }
+
+ // Return number of periods
+ var num = payment * (1 + rate * type) - future * rate;
+ var den = (present * rate + payment * (1 + rate * type));
+ return Math.log(num / den) / Math.log(1 + rate);
+ };
+
+ exports.NPV = function() {
+ var args = utils.parseNumberArray(utils.flatten(arguments));
+ if (args instanceof Error) {
+ return args;
+ }
+
+ // Lookup rate
+ var rate = args[0];
+
+ // Initialize net present value
+ var value = 0;
+
+ // Loop on all values
+ for (var j = 1; j < args.length; j++) {
+ value += args[j] / Math.pow(1 + rate, j);
+ }
+
+ // Return net present value
+ return value;
+ };
+
+ exports.ODDFPRICE = null;
+
+ exports.ODDFYIELD = null;
+
+ exports.ODDLPRICE = null;
+
+ exports.ODDLYIELD = null;
+
+ exports.PDURATION = function(rate, present, future) {
+ rate = utils.parseNumber(rate);
+ present = utils.parseNumber(present);
+ future = utils.parseNumber(future);
+ if (utils.anyIsError(rate, present, future)) {
+ return error.value;
+ }
+
+ // Return error if rate <=0
+ if (rate <= 0) {
+ return error.num;
+ }
+
+ // Return number of periods
+ return (Math.log(future) - Math.log(present)) / Math.log(1 + rate);
+ };
+
+ exports.PMT = function(rate, periods, present, future, type) {
+ // Credits: algorithm inspired by Apache OpenOffice
+
+ future = future || 0;
+ type = type || 0;
+
+ rate = utils.parseNumber(rate);
+ periods = utils.parseNumber(periods);
+ present = utils.parseNumber(present);
+ future = utils.parseNumber(future);
+ type = utils.parseNumber(type);
+ if (utils.anyIsError(rate, periods, present, future, type)) {
+ return error.value;
+ }
+
+ // Return payment
+ var result;
+ if (rate === 0) {
+ result = (present + future) / periods;
+ } else {
+ var term = Math.pow(1 + rate, periods);
+ if (type === 1) {
+ result = (future * rate / (term - 1) + present * rate / (1 - 1 / term)) / (1 + rate);
+ } else {
+ result = future * rate / (term - 1) + present * rate / (1 - 1 / term);
+ }
+ }
+ return -result;
+ };
+
+ exports.PPMT = function(rate, period, periods, present, future, type) {
+ future = future || 0;
+ type = type || 0;
+
+ rate = utils.parseNumber(rate);
+ periods = utils.parseNumber(periods);
+ present = utils.parseNumber(present);
+ future = utils.parseNumber(future);
+ type = utils.parseNumber(type);
+ if (utils.anyIsError(rate, periods, present, future, type)) {
+ return error.value;
+ }
+
+ return exports.PMT(rate, periods, present, future, type) - exports.IPMT(rate, period, periods, present, future, type);
+ };
+
+ exports.PRICE = null;
+
+ exports.PRICEDISC = null;
+
+ exports.PRICEMAT = null;
+
+ exports.PV = function(rate, periods, payment, future, type) {
+ future = future || 0;
+ type = type || 0;
+
+ rate = utils.parseNumber(rate);
+ periods = utils.parseNumber(periods);
+ payment = utils.parseNumber(payment);
+ future = utils.parseNumber(future);
+ type = utils.parseNumber(type);
+ if (utils.anyIsError(rate, periods, payment, future, type)) {
+ return error.value;
+ }
+
+ // Return present value
+ if (rate === 0) {
+ return -payment * periods - future;
+ } else {
+ return (((1 - Math.pow(1 + rate, periods)) / rate) * payment * (1 + rate * type) - future) / Math.pow(1 + rate, periods);
+ }
+ };
+
+ exports.RATE = function(periods, payment, present, future, type, guess) {
+ // Credits: rabugento
+
+ guess = (guess === undefined) ? 0.01 : guess;
+ future = (future === undefined) ? 0 : future;
+ type = (type === undefined) ? 0 : type;
+
+ periods = utils.parseNumber(periods);
+ payment = utils.parseNumber(payment);
+ present = utils.parseNumber(present);
+ future = utils.parseNumber(future);
+ type = utils.parseNumber(type);
+ guess = utils.parseNumber(guess);
+ if (utils.anyIsError(periods, payment, present, future, type, guess)) {
+ return error.value;
+ }
+
+ // Set maximum epsilon for end of iteration
+ var epsMax = 1e-6;
+
+ // Set maximum number of iterations
+ var iterMax = 100;
+ var iter = 0;
+ var close = false;
+ var rate = guess;
+
+ while (iter < iterMax && !close) {
+ var t1 = Math.pow(rate + 1, periods);
+ var t2 = Math.pow(rate + 1, periods - 1);
+
+ var f1 = future + t1 * present + payment * (t1 - 1) * (rate * type + 1) / rate;
+ var f2 = periods * t2 * present - payment * (t1 - 1) *(rate * type + 1) / Math.pow(rate,2);
+ var f3 = periods * payment * t2 * (rate * type + 1) / rate + payment * (t1 - 1) * type / rate;
+
+ var newRate = rate - f1 / (f2 + f3);
+
+ if (Math.abs(newRate - rate) < epsMax) close = true;
+ iter++
+ rate = newRate;
+ }
+
+ if (!close) return Number.NaN + rate;
+ return rate;
+ };
+
+ // TODO
+ exports.RECEIVED = null;
+
+ exports.RRI = function(periods, present, future) {
+ periods = utils.parseNumber(periods);
+ present = utils.parseNumber(present);
+ future = utils.parseNumber(future);
+ if (utils.anyIsError(periods, present, future)) {
+ return error.value;
+ }
+
+ // Return error if periods or present is equal to 0 (zero)
+ if (periods === 0 || present === 0) {
+ return error.num;
+ }
+
+ // Return equivalent interest rate
+ return Math.pow(future / present, 1 / periods) - 1;
+ };
+
+ exports.SLN = function(cost, salvage, life) {
+ cost = utils.parseNumber(cost);
+ salvage = utils.parseNumber(salvage);
+ life = utils.parseNumber(life);
+ if (utils.anyIsError(cost, salvage, life)) {
+ return error.value;
+ }
+
+ // Return error if life equal to 0 (zero)
+ if (life === 0) {
+ return error.num;
+ }
+
+ // Return straight-line depreciation
+ return (cost - salvage) / life;
+ };
+
+ exports.SYD = function(cost, salvage, life, period) {
+ // Return error if any of the parameters is not a number
+ cost = utils.parseNumber(cost);
+ salvage = utils.parseNumber(salvage);
+ life = utils.parseNumber(life);
+ period = utils.parseNumber(period);
+ if (utils.anyIsError(cost, salvage, life, period)) {
+ return error.value;
+ }
+
+ // Return error if life equal to 0 (zero)
+ if (life === 0) {
+ return error.num;
+ }
+
+ // Return error if period is lower than 1 or greater than life
+ if (period < 1 || period > life) {
+ return error.num;
+ }
+
+ // Truncate period if it is not an integer
+ period = parseInt(period, 10);
+
+ // Return straight-line depreciation
+ return ((cost - salvage) * (life - period + 1) * 2) / (life * (life + 1));
+ };
+
+ exports.TBILLEQ = function(settlement, maturity, discount) {
+ settlement = utils.parseDate(settlement);
+ maturity = utils.parseDate(maturity);
+ discount = utils.parseNumber(discount);
+ if (utils.anyIsError(settlement, maturity, discount)) {
+ return error.value;
+ }
+
+ // Return error if discount is lower than or equal to zero
+ if (discount <= 0) {
+ return error.num;
+ }
+
+ // Return error if settlement is greater than maturity
+ if (settlement > maturity) {
+ return error.num;
+ }
+
+ // Return error if maturity is more than one year after settlement
+ if (maturity - settlement > 365 * 24 * 60 * 60 * 1000) {
+ return error.num;
+ }
+
+ // Return bond-equivalent yield
+ return (365 * discount) / (360 - discount * DAYS360(settlement, maturity, false));
+ };
+
+ exports.TBILLPRICE = function(settlement, maturity, discount) {
+ settlement = utils.parseDate(settlement);
+ maturity = utils.parseDate(maturity);
+ discount = utils.parseNumber(discount);
+ if (utils.anyIsError(settlement, maturity, discount)) {
+ return error.value;
+ }
+
+ // Return error if discount is lower than or equal to zero
+ if (discount <= 0) {
+ return error.num;
+ }
+
+ // Return error if settlement is greater than maturity
+ if (settlement > maturity) {
+ return error.num;
+ }
+
+ // Return error if maturity is more than one year after settlement
+ if (maturity - settlement > 365 * 24 * 60 * 60 * 1000) {
+ return error.num;
+ }
+
+ // Return bond-equivalent yield
+ return 100 * (1 - discount * DAYS360(settlement, maturity, false) / 360);
+ };
+
+ exports.TBILLYIELD = function(settlement, maturity, price) {
+ settlement = utils.parseDate(settlement);
+ maturity = utils.parseDate(maturity);
+ price = utils.parseNumber(price);
+ if (utils.anyIsError(settlement, maturity, price)) {
+ return error.value;
+ }
+
+ // Return error if price is lower than or equal to zero
+ if (price <= 0) {
+ return error.num;
+ }
+
+ // Return error if settlement is greater than maturity
+ if (settlement > maturity) {
+ return error.num;
+ }
+
+ // Return error if maturity is more than one year after settlement
+ if (maturity - settlement > 365 * 24 * 60 * 60 * 1000) {
+ return error.num;
+ }
+
+ // Return bond-equivalent yield
+ return (100 - price) * 360 / (price * DAYS360(settlement, maturity, false));
+ };
+
+ exports.VDB = null;
+
+ exports.XIRR = function(values, dates, guess) {
+ // Credits: algorithm inspired by Apache OpenOffice
+
+ values = utils.parseNumberArray(utils.flatten(values));
+ dates = utils.parseDateArray(utils.flatten(dates));
+ guess = utils.parseNumber(guess);
+ if (utils.anyIsError(values, dates, guess)) {
+ return error.value;
+ }
+
+ // Calculates the resulting amount
+ var irrResult = function(values, dates, rate) {
+ var r = rate + 1;
+ var result = values[0];
+ for (var i = 1; i < values.length; i++) {
+ result += values[i] / Math.pow(r, DAYS(dates[i], dates[0]) / 365);
+ }
+ return result;
+ };
+
+ // Calculates the first derivation
+ var irrResultDeriv = function(values, dates, rate) {
+ var r = rate + 1;
+ var result = 0;
+ for (var i = 1; i < values.length; i++) {
+ var frac = DAYS(dates[i], dates[0]) / 365;
+ result -= frac * values[i] / Math.pow(r, frac + 1);
+ }
+ return result;
+ };
+
+ // Check that values contains at least one positive value and one
+ // negative value
+ var positive = false;
+ var negative = false;
+ for (var i = 0; i < values.length; i++) {
+ if (values[i] > 0) {
+ positive = true;
+ }
+ if (values[i] < 0) {
+ negative = true;
+ }
+ }
+
+ // Return error if values does not contain at least one positive value
+ // and one negative value
+ if (!positive || !negative) {
+ return error.num;
+ }
+
+ // Initialize guess and resultRate
+ guess = guess || 0.1;
+ var resultRate = guess;
+
+ // Set maximum epsilon for end of iteration
+ var epsMax = 1e-10;
+
+ // Implement Newton's method
+ var newRate, epsRate, resultValue;
+ var contLoop = true;
+ do {
+ resultValue = irrResult(values, dates, resultRate);
+ newRate = resultRate - resultValue / irrResultDeriv(values, dates, resultRate);
+ epsRate = Math.abs(newRate - resultRate);
+ resultRate = newRate;
+ contLoop = (epsRate > epsMax) && (Math.abs(resultValue) > epsMax);
+ } while (contLoop);
+
+ // Return internal rate of return
+ return resultRate;
+ };
+
+ exports.XNPV = function(rate, values, dates) {
+ rate = utils.parseNumber(rate);
+ values = utils.parseNumberArray(utils.flatten(values));
+ dates = utils.parseDateArray(utils.flatten(dates));
+ if (utils.anyIsError(rate, values, dates)) {
+ return error.value;
+ }
+
+ var result = 0;
+ for (var i = 0; i < values.length; i++) {
+ result += values[i] / Math.pow(1 + rate, DAYS(dates[i], dates[0]) / 365);
+ }
+ return result;
+ };
+
+ exports.YIELD = null;
+
+ exports.YIELDDISC = null;
+
+ exports.YIELDMAT = null;
+
+ return exports;
+ })();
+
+ jexcel.methods.information = (function() {
+ var exports = {};
+ exports.CELL = null;
+
+ exports.ERROR = {};
+ exports.ERROR.TYPE = function(error_val) {
+ switch (error_val) {
+ case error.nil: return 1;
+ case error.div0: return 2;
+ case error.value: return 3;
+ case error.ref: return 4;
+ case error.name: return 5;
+ case error.num: return 6;
+ case error.na: return 7;
+ case error.data: return 8;
+ }
+ return error.na;
+ };
+
+ exports.INFO = null;
+
+ exports.ISBLANK = function(value) {
+ return value === null;
+ };
+
+ exports.ISBINARY = function (number) {
+ return (/^[01]{1,10}$/).test(number);
+ };
+
+ exports.ISERR = function(value) {
+ return ([error.value, error.ref, error.div0, error.num, error.name, error.nil]).indexOf(value) >= 0 ||
+ (typeof value === 'number' && (isNaN(value) || !isFinite(value)));
+ };
+
+ exports.ISERROR = function(value) {
+ return exports.ISERR(value) || value === error.na;
+ };
+
+ exports.ISEVEN = function(number) {
+ return (Math.floor(Math.abs(number)) & 1) ? false : true;
+ };
+
+ // TODO
+ exports.ISFORMULA = null;
+
+ exports.ISLOGICAL = function(value) {
+ return value === true || value === false;
+ };
+
+ exports.ISNA = function(value) {
+ return value === error.na;
+ };
+
+ exports.ISNONTEXT = function(value) {
+ return typeof(value) !== 'string';
+ };
+
+ exports.ISNUMBER = function(value) {
+ return typeof(value) === 'number' && !isNaN(value) && isFinite(value);
+ };
+
+ exports.ISODD = function(number) {
+ return (Math.floor(Math.abs(number)) & 1) ? true : false;
+ };
+
+ exports.ISREF = null;
+
+ exports.ISTEXT = function(value) {
+ return typeof(value) === 'string';
+ };
+
+ exports.N = function(value) {
+ if (this.ISNUMBER(value)) {
+ return value;
+ }
+ if (value instanceof Date) {
+ return value.getTime();
+ }
+ if (value === true) {
+ return 1;
+ }
+ if (value === false) {
+ return 0;
+ }
+ if (this.ISERROR(value)) {
+ return value;
+ }
+ return 0;
+ };
+
+ exports.NA = function() {
+ return error.na;
+ };
+
+ exports.SHEET = null;
+
+ exports.SHEETS = null;
+
+ exports.TYPE = function(value) {
+ if (this.ISNUMBER(value)) {
+ return 1;
+ }
+ if (this.ISTEXT(value)) {
+ return 2;
+ }
+ if (this.ISLOGICAL(value)) {
+ return 4;
+ }
+ if (this.ISERROR(value)) {
+ return 16;
+ }
+ if (Array.isArray(value)) {
+ return 64;
+ }
+ };
+
+ return exports;
+ })();
+
+ jexcel.methods.logical = (function() {
+ var exports = {};
+
+ exports.AND = function() {
+ var args = utils.flatten(arguments);
+ var result = true;
+ for (var i = 0; i < args.length; i++) {
+ if (!args[i]) {
+ result = false;
+ }
+ }
+ return result;
+ };
+
+ exports.CHOOSE = function() {
+ if (arguments.length < 2) {
+ return error.na;
+ }
+
+ var index = arguments[0];
+ if (index < 1 || index > 254) {
+ return error.value;
+ }
+
+ if (arguments.length < index + 1) {
+ return error.value;
+ }
+
+ return arguments[index];
+ };
+
+ exports.FALSE = function() {
+ return false;
+ };
+
+ exports.IF = function(test, then_value, otherwise_value) {
+ return test ? then_value : otherwise_value;
+ };
+
+ exports.IFERROR = function(value, valueIfError) {
+ if (ISERROR(value)) {
+ return valueIfError;
+ }
+ return value;
+ };
+
+ exports.IFNA = function(value, value_if_na) {
+ return value === error.na ? value_if_na : value;
+ };
+
+ exports.NOT = function(logical) {
+ return !logical;
+ };
+
+ exports.OR = function() {
+ var args = utils.flatten(arguments);
+ var result = false;
+ for (var i = 0; i < args.length; i++) {
+ if (args[i]) {
+ result = true;
+ }
+ }
+ return result;
+ };
+
+ exports.TRUE = function() {
+ return true;
+ };
+
+ exports.XOR = function() {
+ var args = utils.flatten(arguments);
+ var result = 0;
+ for (var i = 0; i < args.length; i++) {
+ if (args[i]) {
+ result++;
+ }
+ }
+ return (Math.floor(Math.abs(result)) & 1) ? true : false;
+ };
+
+ exports.SWITCH = function() {
+ var result;
+ if (arguments.length > 0) {
+ var targetValue = arguments[0];
+ var argc = arguments.length - 1;
+ var switchCount = Math.floor(argc / 2);
+ var switchSatisfied = false;
+ var defaultClause = argc % 2 === 0 ? null : arguments[arguments.length - 1];
+
+ if (switchCount) {
+ for (var index = 0; index < switchCount; index++) {
+ if (targetValue === arguments[index * 2 + 1]) {
+ result = arguments[index * 2 + 2];
+ switchSatisfied = true;
+ break;
+ }
+ }
+ }
+
+ if (!switchSatisfied && defaultClause) {
+ result = defaultClause;
+ }
+ }
+
+ return result;
+ };
+
+ return exports;
+ })();
+
+ jexcel.methods.math = (function() {
+ var exports = {};
+
+ exports.ABS = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ return Math.abs(utils.parseNumber(number));
+ };
+
+ exports.ACOS = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ return Math.acos(number);
+ };
+
+ exports.ACOSH = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ return Math.log(number + Math.sqrt(number * number - 1));
+ };
+
+ exports.ACOT = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ return Math.atan(1 / number);
+ };
+
+ exports.ACOTH = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ return 0.5 * Math.log((number + 1) / (number - 1));
+ };
+
+ exports.AGGREGATE = null
+
+ exports.ARABIC = function(text) {
+ // Credits: Rafa? Kukawski
+ if (!/^M*(?:D?C{0,3}|C[MD])(?:L?X{0,3}|X[CL])(?:V?I{0,3}|I[XV])$/.test(text)) {
+ return error.value;
+ }
+ var r = 0;
+ text.replace(/[MDLV]|C[MD]?|X[CL]?|I[XV]?/g, function(i) {
+ r += {
+ M: 1000,
+ CM: 900,
+ D: 500,
+ CD: 400,
+ C: 100,
+ XC: 90,
+ L: 50,
+ XL: 40,
+ X: 10,
+ IX: 9,
+ V: 5,
+ IV: 4,
+ I: 1
+ }[i];
+ });
+ return r;
+ };
+
+ exports.ASIN = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ return Math.asin(number);
+ };
+
+ exports.ASINH = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ return Math.log(number + Math.sqrt(number * number + 1));
+ };
+
+ exports.ATAN = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ return Math.atan(number);
+ };
+
+ exports.ATAN2 = function(number_x, number_y) {
+ number_x = utils.parseNumber(number_x);
+ number_y = utils.parseNumber(number_y);
+ if (utils.anyIsError(number_x, number_y)) {
+ return error.value;
+ }
+ return Math.atan2(number_x, number_y);
+ };
+
+ exports.ATANH = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ return Math.log((1 + number) / (1 - number)) / 2;
+ };
+
+ exports.BASE = function(number, radix, min_length) {
+ min_length = min_length || 0;
+
+ number = utils.parseNumber(number);
+ radix = utils.parseNumber(radix);
+ min_length = utils.parseNumber(min_length);
+ if (utils.anyIsError(number, radix, min_length)) {
+ return error.value;
+ }
+ min_length = (min_length === undefined) ? 0 : min_length;
+ var result = number.toString(radix);
+ return new Array(Math.max(min_length + 1 - result.length, 0)).join('0') + result;
+ };
+
+ exports.CEILING = function(number, significance, mode) {
+ significance = (significance === undefined) ? 1 : significance;
+ mode = (mode === undefined) ? 0 : mode;
+
+ number = utils.parseNumber(number);
+ significance = utils.parseNumber(significance);
+ mode = utils.parseNumber(mode);
+ if (utils.anyIsError(number, significance, mode)) {
+ return error.value;
+ }
+ if (significance === 0) {
+ return 0;
+ }
+
+ significance = Math.abs(significance);
+ if (number >= 0) {
+ return Math.ceil(number / significance) * significance;
+ } else {
+ if (mode === 0) {
+ return -1 * Math.floor(Math.abs(number) / significance) * significance;
+ } else {
+ return -1 * Math.ceil(Math.abs(number) / significance) * significance;
+ }
+ }
+ };
+
+ exports.CEILING.MATH = exports.CEILING;
+
+ exports.CEILING.PRECISE = exports.CEILING;
+
+ exports.COMBIN = function(number, number_chosen) {
+ number = utils.parseNumber(number);
+ number_chosen = utils.parseNumber(number_chosen);
+ if (utils.anyIsError(number, number_chosen)) {
+ return error.value;
+ }
+ return exports.FACT(number) / (exports.FACT(number_chosen) * exports.FACT(number - number_chosen));
+ };
+
+ exports.COMBINA = function(number, number_chosen) {
+ number = utils.parseNumber(number);
+ number_chosen = utils.parseNumber(number_chosen);
+ if (utils.anyIsError(number, number_chosen)) {
+ return error.value;
+ }
+ return (number === 0 && number_chosen === 0) ? 1 : exports.COMBIN(number + number_chosen - 1, number - 1);
+ };
+
+ exports.COS = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ return Math.cos(number);
+ };
+
+ exports.COSH = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ return (Math.exp(number) + Math.exp(-number)) / 2;
+ };
+
+ exports.COT = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ return 1 / Math.tan(number);
+ };
+
+ exports.COTH = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ var e2 = Math.exp(2 * number);
+ return (e2 + 1) / (e2 - 1);
+ };
+
+ exports.CSC = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ return 1 / Math.sin(number);
+ };
+
+ exports.CSCH = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ return 2 / (Math.exp(number) - Math.exp(-number));
+ };
+
+ exports.DECIMAL = function(number, radix) {
+ if (arguments.length < 1) {
+ return error.value;
+ }
+
+
+ return parseInt(number, radix);
+ };
+
+ exports.DEGREES = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ return number * 180 / Math.PI;
+ };
+
+ exports.EVEN = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ return exports.CEILING(number, -2, -1);
+ };
+
+ exports.EXP = Math.exp;
+
+ var MEMOIZED_FACT = [];
+ exports.FACT = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ var n = Math.floor(number);
+ if (n === 0 || n === 1) {
+ return 1;
+ } else if (MEMOIZED_FACT[n] > 0) {
+ return MEMOIZED_FACT[n];
+ } else {
+ MEMOIZED_FACT[n] = exports.FACT(n - 1) * n;
+ return MEMOIZED_FACT[n];
+ }
+ };
+
+ exports.FACTDOUBLE = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ var n = Math.floor(number);
+ if (n <= 0) {
+ return 1;
+ } else {
+ return n * exports.FACTDOUBLE(n - 2);
+ }
+ };
+
+ exports.FLOOR = function(number, significance, mode) {
+ significance = (significance === undefined) ? 1 : significance;
+ mode = (mode === undefined) ? 0 : mode;
+
+ number = utils.parseNumber(number);
+ significance = utils.parseNumber(significance);
+ mode = utils.parseNumber(mode);
+ if (utils.anyIsError(number, significance, mode)) {
+ return error.value;
+ }
+ if (significance === 0) {
+ return 0;
+ }
+
+ significance = Math.abs(significance);
+ if (number >= 0) {
+ return Math.floor(number / significance) * significance;
+ } else {
+ if (mode === 0) {
+ return -1 * Math.ceil(Math.abs(number) / significance) * significance;
+ } else {
+ return -1 * Math.floor(Math.abs(number) / significance) * significance;
+ }
+ }
+ };
+
+ exports.FLOOR.MATH = exports.FLOOR;
+
+ exports.GCD = null;
+
+ exports.INT = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ return Math.floor(number);
+ };
+
+ exports.LCM = function() {
+ // Credits: Jonas Raoni Soares Silva
+ var o = utils.parseNumberArray(utils.flatten(arguments));
+ if (o instanceof Error) {
+ return o;
+ }
+ for (var i, j, n, d, r = 1;
+ (n = o.pop()) !== undefined;) {
+ while (n > 1) {
+ if (n % 2) {
+ for (i = 3, j = Math.floor(Math.sqrt(n)); i <= j && n % i; i += 2) {
+ //empty
+ }
+ d = (i <= j) ? i : n;
+ } else {
+ d = 2;
+ }
+ for (n /= d, r *= d, i = o.length; i;
+ (o[--i] % d) === 0 && (o[i] /= d) === 1 && o.splice(i, 1)) {
+ //empty
+ }
+ }
+ }
+ return r;
+ };
+
+ exports.LN = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ return Math.log(number);
+ };
+
+ exports.LOG = function(number, base) {
+ number = utils.parseNumber(number);
+ base = (base === undefined) ? 10 : utils.parseNumber(base);
+
+ if (utils.anyIsError(number, base)) {
+ return error.value;
+ }
+
+ return Math.log(number) / Math.log(base);
+ };
+
+ exports.LOG10 = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ return Math.log(number) / Math.log(10);
+ };
+
+ exports.MDETERM = null;
+
+ exports.MINVERSE = null;
+
+ exports.MMULT = null;
+
+ exports.MOD = function(dividend, divisor) {
+ dividend = utils.parseNumber(dividend);
+ divisor = utils.parseNumber(divisor);
+ if (utils.anyIsError(dividend, divisor)) {
+ return error.value;
+ }
+ if (divisor === 0) {
+ return error.div0;
+ }
+ var modulus = Math.abs(dividend % divisor);
+ return (divisor > 0) ? modulus : -modulus;
+ };
+
+ exports.MROUND = function(number, multiple) {
+ number = utils.parseNumber(number);
+ multiple = utils.parseNumber(multiple);
+ if (utils.anyIsError(number, multiple)) {
+ return error.value;
+ }
+ if (number * multiple < 0) {
+ return error.num;
+ }
+
+ return Math.round(number / multiple) * multiple;
+ };
+
+ exports.MULTINOMIAL = function() {
+ var args = utils.parseNumberArray(utils.flatten(arguments));
+ if (args instanceof Error) {
+ return args;
+ }
+ var sum = 0;
+ var divisor = 1;
+ for (var i = 0; i < args.length; i++) {
+ sum += args[i];
+ divisor *= exports.FACT(args[i]);
+ }
+ return exports.FACT(sum) / divisor;
+ };
+
+ exports.MUNIT = null;
+
+ exports.ODD = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ var temp = Math.ceil(Math.abs(number));
+ temp = (temp & 1) ? temp : temp + 1;
+ return (number > 0) ? temp : -temp;
+ };
+
+ exports.PI = function() {
+ return Math.PI;
+ };
+
+ exports.POWER = function(number, power) {
+ number = utils.parseNumber(number);
+ power = utils.parseNumber(power);
+ if (utils.anyIsError(number, power)) {
+ return error.value;
+ }
+ var result = Math.pow(number, power);
+ if (isNaN(result)) {
+ return error.num;
+ }
+
+ return result;
+ };
+
+ exports.PRODUCT = function() {
+ var args = utils.parseNumberArray(utils.flatten(arguments));
+ if (args instanceof Error) {
+ return args;
+ }
+ var result = 1;
+ for (var i = 0; i < args.length; i++) {
+ result *= args[i];
+ }
+ return result;
+ };
+
+ exports.QUOTIENT = function(numerator, denominator) {
+ numerator = utils.parseNumber(numerator);
+ denominator = utils.parseNumber(denominator);
+ if (utils.anyIsError(numerator, denominator)) {
+ return error.value;
+ }
+ return parseInt(numerator / denominator, 10);
+ };
+
+ exports.RADIANS = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ return number * Math.PI / 180;
+ };
+
+ exports.RAND = function() {
+ return Math.random();
+ };
+
+ exports.RANDBETWEEN = function(bottom, top) {
+ bottom = utils.parseNumber(bottom);
+ top = utils.parseNumber(top);
+ if (utils.anyIsError(bottom, top)) {
+ return error.value;
+ }
+ // Creative Commons Attribution 3.0 License
+ // Copyright (c) 2012 eqcode
+ return bottom + Math.ceil((top - bottom + 1) * Math.random()) - 1;
+ };
+
+ exports.ROMAN = null;
+
+ exports.ROUND = function(number, digits) {
+ number = utils.parseNumber(number);
+ digits = utils.parseNumber(digits);
+ if (utils.anyIsError(number, digits)) {
+ return error.value;
+ }
+ return Math.round(number * Math.pow(10, digits)) / Math.pow(10, digits);
+ };
+
+ exports.ROUNDDOWN = function(number, digits) {
+ number = utils.parseNumber(number);
+ digits = utils.parseNumber(digits);
+ if (utils.anyIsError(number, digits)) {
+ return error.value;
+ }
+ var sign = (number > 0) ? 1 : -1;
+ return sign * (Math.floor(Math.abs(number) * Math.pow(10, digits))) / Math.pow(10, digits);
+ };
+
+ exports.ROUNDUP = function(number, digits) {
+ number = utils.parseNumber(number);
+ digits = utils.parseNumber(digits);
+ if (utils.anyIsError(number, digits)) {
+ return error.value;
+ }
+ var sign = (number > 0) ? 1 : -1;
+ return sign * (Math.ceil(Math.abs(number) * Math.pow(10, digits))) / Math.pow(10, digits);
+ };
+
+ exports.SEC = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ return 1 / Math.cos(number);
+ };
+
+ exports.SECH = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ return 2 / (Math.exp(number) + Math.exp(-number));
+ };
+
+ exports.SERIESSUM = function(x, n, m, coefficients) {
+ x = utils.parseNumber(x);
+ n = utils.parseNumber(n);
+ m = utils.parseNumber(m);
+ coefficients = utils.parseNumberArray(coefficients);
+ if (utils.anyIsError(x, n, m, coefficients)) {
+ return error.value;
+ }
+ var result = coefficients[0] * Math.pow(x, n);
+ for (var i = 1; i < coefficients.length; i++) {
+ result += coefficients[i] * Math.pow(x, n + i * m);
+ }
+ return result;
+ };
+
+ exports.SIGN = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ if (number < 0) {
+ return -1;
+ } else if (number === 0) {
+ return 0;
+ } else {
+ return 1;
+ }
+ };
+
+ exports.SIN = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ return Math.sin(number);
+ };
+
+ exports.SINH = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ return (Math.exp(number) - Math.exp(-number)) / 2;
+ };
+
+ exports.SQRT = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ if (number < 0) {
+ return error.num;
+ }
+ return Math.sqrt(number);
+ };
+
+ exports.SQRTPI = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ return Math.sqrt(number * Math.PI);
+ };
+
+ exports.SUBTOTAL = null;
+
+ exports.ADD = function (num1, num2) {
+ if (arguments.length !== 2) {
+ return error.na;
+ }
+
+ num1 = utils.parseNumber(num1);
+ num2 = utils.parseNumber(num2);
+ if (utils.anyIsError(num1, num2)) {
+ return error.value;
+ }
+
+ return num1 + num2;
+ };
+
+ exports.MINUS = function (num1, num2) {
+ if (arguments.length !== 2) {
+ return error.na;
+ }
+
+ num1 = utils.parseNumber(num1);
+ num2 = utils.parseNumber(num2);
+ if (utils.anyIsError(num1, num2)) {
+ return error.value;
+ }
+
+ return num1 - num2;
+ };
+
+ exports.DIVIDE = function (dividend, divisor) {
+ if (arguments.length !== 2) {
+ return error.na;
+ }
+
+ dividend = utils.parseNumber(dividend);
+ divisor = utils.parseNumber(divisor);
+ if (utils.anyIsError(dividend, divisor)) {
+ return error.value;
+ }
+
+ if (divisor === 0) {
+ return error.div0;
+ }
+
+ return dividend / divisor;
+ };
+
+ exports.MULTIPLY = function (factor1, factor2) {
+ if (arguments.length !== 2) {
+ return error.na;
+ }
+
+ factor1 = utils.parseNumber(factor1);
+ factor2 = utils.parseNumber(factor2);
+ if (utils.anyIsError(factor1, factor2)) {
+ return error.value;
+ }
+
+ return factor1 * factor2;
+ };
+
+ exports.GTE = function (num1, num2) {
+ if (arguments.length !== 2) {
+ return error.na;
+ }
+
+ num1 = utils.parseNumber(num1);
+ num2 = utils.parseNumber(num2);
+ if (utils.anyIsError(num1, num2)) {
+ return error.error;
+ }
+
+ return num1 >= num2;
+ };
+
+ exports.LT = function (num1, num2) {
+ if (arguments.length !== 2) {
+ return error.na;
+ }
+
+ num1 = utils.parseNumber(num1);
+ num2 = utils.parseNumber(num2);
+ if (utils.anyIsError(num1, num2)) {
+ return error.error;
+ }
+
+ return num1 < num2;
+ };
+
+ exports.LTE = function (num1, num2) {
+ if (arguments.length !== 2) {
+ return error.na;
+ }
+
+ num1 = utils.parseNumber(num1);
+ num2 = utils.parseNumber(num2);
+ if (utils.anyIsError(num1, num2)) {
+ return error.error;
+ }
+
+ return num1 <= num2;
+ };
+
+ exports.EQ = function (value1, value2) {
+ if (arguments.length !== 2) {
+ return error.na;
+ }
+
+ return value1 === value2;
+ };
+
+ exports.NE = function (value1, value2) {
+ if (arguments.length !== 2) {
+ return error.na;
+ }
+
+ return value1 !== value2;
+ };
+
+ exports.POW = function (base, exponent) {
+ if (arguments.length !== 2) {
+ return error.na;
+ }
+
+ base = utils.parseNumber(base);
+ exponent = utils.parseNumber(exponent);
+ if (utils.anyIsError(base, exponent)) {
+ return error.error;
+ }
+
+ return exports.POWER(base, exponent);
+ };
+
+ exports.SUM = function() {
+ var result = 0;
+ var argsKeys = Object.keys(arguments);
+ for (var i = 0; i < argsKeys.length; ++i) {
+ var elt = arguments[argsKeys[i]];
+ if (typeof elt === 'number') {
+ result += elt;
+ } else if (typeof elt === 'string') {
+ var parsed = parseFloat(elt);
+ !isNaN(parsed) && (result += parsed);
+ } else if (Array.isArray(elt)) {
+ result += exports.SUM.apply(null, elt);
+ }
+ }
+ return result;
+ };
+
+ exports.SUMIF = function(range, criteria) {
+ range = utils.parseNumberArray(utils.flatten(range));
+ if (range instanceof Error) {
+ return range;
+ }
+ var result = 0;
+ for (var i = 0; i < range.length; i++) {
+ result += (eval(range[i] + criteria)) ? range[i] : 0; // jshint ignore:line
+ }
+ return result;
+ };
+
+ exports.SUMIFS = function() {
+ var args = utils.argsToArray(arguments);
+ var range = utils.parseNumberArray(utils.flatten(args.shift()));
+ if (range instanceof Error) {
+ return range;
+ }
+ var criteria = args;
+
+ var n_range_elements = range.length;
+ var n_criterias = criteria.length;
+
+ var result = 0;
+ for (var i = 0; i < n_range_elements; i++) {
+ var el = range[i];
+ var condition = '';
+ for (var c = 0; c < n_criterias; c++) {
+ condition += el + criteria[c];
+ if (c !== n_criterias - 1) {
+ condition += '&&';
+ }
+ }
+ if (eval(condition)) { // jshint ignore:line
+ result += el;
+ }
+ }
+ return result;
+ };
+
+ exports.SUMPRODUCT = null;
+
+ exports.SUMSQ = function() {
+ var numbers = utils.parseNumberArray(utils.flatten(arguments));
+ if (numbers instanceof Error) {
+ return numbers;
+ }
+ var result = 0;
+ var length = numbers.length;
+ for (var i = 0; i < length; i++) {
+ result += (ISNUMBER(numbers[i])) ? numbers[i] * numbers[i] : 0;
+ }
+ return result;
+ };
+
+ exports.SUMX2MY2 = function(array_x, array_y) {
+ array_x = utils.parseNumberArray(utils.flatten(array_x));
+ array_y = utils.parseNumberArray(utils.flatten(array_y));
+ if (utils.anyIsError(array_x, array_y)) {
+ return error.value;
+ }
+ var result = 0;
+ for (var i = 0; i < array_x.length; i++) {
+ result += array_x[i] * array_x[i] - array_y[i] * array_y[i];
+ }
+ return result;
+ };
+
+ exports.SUMX2PY2 = function(array_x, array_y) {
+ array_x = utils.parseNumberArray(utils.flatten(array_x));
+ array_y = utils.parseNumberArray(utils.flatten(array_y));
+ if (utils.anyIsError(array_x, array_y)) {
+ return error.value;
+ }
+ var result = 0;
+ array_x = utils.parseNumberArray(utils.flatten(array_x));
+ array_y = utils.parseNumberArray(utils.flatten(array_y));
+ for (var i = 0; i < array_x.length; i++) {
+ result += array_x[i] * array_x[i] + array_y[i] * array_y[i];
+ }
+ return result;
+ };
+
+ exports.SUMXMY2 = function(array_x, array_y) {
+ array_x = utils.parseNumberArray(utils.flatten(array_x));
+ array_y = utils.parseNumberArray(utils.flatten(array_y));
+ if (utils.anyIsError(array_x, array_y)) {
+ return error.value;
+ }
+ var result = 0;
+ array_x = utils.flatten(array_x);
+ array_y = utils.flatten(array_y);
+ for (var i = 0; i < array_x.length; i++) {
+ result += Math.pow(array_x[i] - array_y[i], 2);
+ }
+ return result;
+ };
+
+ exports.TAN = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ return Math.tan(number);
+ };
+
+ exports.TANH = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ var e2 = Math.exp(2 * number);
+ return (e2 - 1) / (e2 + 1);
+ };
+
+ exports.TRUNC = function(number, digits) {
+ digits = (digits === undefined) ? 0 : digits;
+ number = utils.parseNumber(number);
+ digits = utils.parseNumber(digits);
+ if (utils.anyIsError(number, digits)) {
+ return error.value;
+ }
+ var sign = (number > 0) ? 1 : -1;
+ return sign * (Math.floor(Math.abs(number) * Math.pow(10, digits))) / Math.pow(10, digits);
+ };
+
+ return exports;
+ })();
+
+ jexcel.methods.misc = (function() {
+ var exports = {};
+
+ exports.UNIQUE = function () {
+ var result = [];
+ for (var i = 0; i < arguments.length; ++i) {
+ var hasElement = false;
+ var element = arguments[i];
+
+ // Check if we've already seen this element.
+ for (var j = 0; j < result.length; ++j) {
+ hasElement = result[j] === element;
+ if (hasElement) { break; }
+ }
+
+ // If we did not find it, add it to the result.
+ if (!hasElement) {
+ result.push(element);
+ }
+ }
+ return result;
+ };
+
+ exports.FLATTEN = utils.flatten;
+
+ exports.ARGS2ARRAY = function () {
+ return Array.prototype.slice.call(arguments, 0);
+ };
+
+ exports.REFERENCE = function (context, reference) {
+ try {
+ var path = reference.split('.');
+ var result = context;
+ for (var i = 0; i < path.length; ++i) {
+ var step = path[i];
+ if (step[step.length - 1] === ']') {
+ var opening = step.indexOf('[');
+ var index = step.substring(opening + 1, step.length - 1);
+ result = result[step.substring(0, opening)][index];
+ } else {
+ result = result[step];
+ }
+ }
+ return result;
+ } catch (error) {}
+ };
+
+ exports.JOIN = function (array, separator) {
+ return array.join(separator);
+ };
+
+ exports.NUMBERS = function () {
+ var possibleNumbers = utils.flatten(arguments);
+ return possibleNumbers.filter(function (el) {
+ return typeof el === 'number';
+ });
+ };
+
+ exports.NUMERAL = null;
+
+ return exports;
+ })();
+
+ jexcel.methods.text = (function() {
+ var exports = {};
+
+ exports.ASC = null;
+
+ exports.BAHTTEXT = null;
+
+ exports.CHAR = function(number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ return String.fromCharCode(number);
+ };
+
+ exports.CLEAN = function(text) {
+ text = text || '';
+ var re = /[\0-\x1F]/g;
+ return text.replace(re, "");
+ };
+
+ exports.CODE = function(text) {
+ text = text || '';
+ return text.charCodeAt(0);
+ };
+
+ exports.CONCATENATE = function() {
+ var args = utils.flatten(arguments);
+
+ var trueFound = 0;
+ while ((trueFound = args.indexOf(true)) > -1) {
+ args[trueFound] = 'TRUE';
+ }
+
+ var falseFound = 0;
+ while ((falseFound = args.indexOf(false)) > -1) {
+ args[falseFound] = 'FALSE';
+ }
+
+ return args.join('');
+ };
+
+ exports.DBCS = null;
+
+ exports.DOLLAR = null;
+
+ exports.EXACT = function(text1, text2) {
+ return text1 === text2;
+ };
+
+ exports.FIND = function(find_text, within_text, position) {
+ position = (position === undefined) ? 0 : position;
+ return within_text ? within_text.indexOf(find_text, position - 1) + 1 : null;
+ };
+
+ exports.FIXED = null;
+
+ exports.HTML2TEXT = function (value) {
+ var result = '';
+
+ if (value) {
+ if (value instanceof Array) {
+ value.forEach(function (line) {
+ if (result !== '') {
+ result += '\n';
+ }
+ result += (line.replace(/<(?:.|\n)*?>/gm, ''));
+ });
+ } else {
+ result = value.replace(/<(?:.|\n)*?>/gm, '');
+ }
+ }
+
+ return result;
+ };
+
+ exports.LEFT = function(text, number) {
+ number = (number === undefined) ? 1 : number;
+ number = utils.parseNumber(number);
+ if (number instanceof Error || typeof text !== 'string') {
+ return error.value;
+ }
+ return text ? text.substring(0, number) : null;
+ };
+
+ exports.LEN = function(text) {
+ if (arguments.length === 0) {
+ return error.error;
+ }
+
+ if (typeof text === 'string') {
+ return text ? text.length : 0;
+ }
+
+ if (text.length) {
+ return text.length;
+ }
+
+ return error.value;
+ };
+
+ exports.LOWER = function(text) {
+ if (typeof text !== 'string') {
+ return error.value;
+ }
+ return text ? text.toLowerCase() : text;
+ };
+
+ exports.MID = function(text, start, number) {
+ start = utils.parseNumber(start);
+ number = utils.parseNumber(number);
+ if (utils.anyIsError(start, number) || typeof text !== 'string') {
+ return number;
+ }
+
+ var begin = start - 1;
+ var end = begin + number;
+
+ return text.substring(begin, end);
+ };
+
+ exports.NUMBERVALUE = null;
+
+ exports.PRONETIC = null;
+
+ exports.PROPER = function(text) {
+ if (text === undefined || text.length === 0) {
+ return error.value;
+ }
+ if (text === true) {
+ text = 'TRUE';
+ }
+ if (text === false) {
+ text = 'FALSE';
+ }
+ if (isNaN(text) && typeof text === 'number') {
+ return error.value;
+ }
+ if (typeof text === 'number') {
+ text = '' + text;
+ }
+
+ return text.replace(/\w\S*/g, function(txt) {
+ return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
+ });
+ };
+
+ exports.REGEXEXTRACT = function (text, regular_expression) {
+ var match = text.match(new RegExp(regular_expression));
+ return match ? (match[match.length > 1 ? match.length - 1 : 0]) : null;
+ };
+
+ exports.REGEXMATCH = function (text, regular_expression, full) {
+ var match = text.match(new RegExp(regular_expression));
+ return full ? match : !!match;
+ };
+
+ exports.REGEXREPLACE = function (text, regular_expression, replacement) {
+ return text.replace(new RegExp(regular_expression), replacement);
+ };
+
+ exports.REPLACE = function(text, position, length, new_text) {
+ position = utils.parseNumber(position);
+ length = utils.parseNumber(length);
+ if (utils.anyIsError(position, length) ||
+ typeof text !== 'string' ||
+ typeof new_text !== 'string') {
+ return error.value;
+ }
+ return text.substr(0, position - 1) + new_text + text.substr(position - 1 + length);
+ };
+
+ exports.REPT = function(text, number) {
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ return new Array(number + 1).join(text);
+ };
+
+ exports.RIGHT = function(text, number) {
+ number = (number === undefined) ? 1 : number;
+ number = utils.parseNumber(number);
+ if (number instanceof Error) {
+ return number;
+ }
+ return text ? text.substring(text.length - number) : null;
+ };
+
+ exports.SEARCH = function(find_text, within_text, position) {
+ var foundAt;
+ if (typeof find_text !== 'string' || typeof within_text !== 'string') {
+ return error.value;
+ }
+ position = (position === undefined) ? 0 : position;
+ foundAt = within_text.toLowerCase().indexOf(find_text.toLowerCase(), position - 1)+1;
+ return (foundAt === 0)?error.value:foundAt;
+ };
+
+ exports.SPLIT = function (text, separator) {
+ return text.split(separator);
+ };
+
+ exports.SUBSTITUTE = function(text, old_text, new_text, occurrence) {
+ if (!text || !old_text || !new_text) {
+ return text;
+ } else if (occurrence === undefined) {
+ return text.replace(new RegExp(old_text, 'g'), new_text);
+ } else {
+ var index = 0;
+ var i = 0;
+ while (text.indexOf(old_text, index) > 0) {
+ index = text.indexOf(old_text, index + 1);
+ i++;
+ if (i === occurrence) {
+ return text.substring(0, index) + new_text + text.substring(index + old_text.length);
+ }
+ }
+ }
+ };
+
+ exports.T = function(value) {
+ return (typeof value === "string") ? value : '';
+ };
+
+ exports.TEXT = null;
+
+ exports.TRIM = function(text) {
+ if (typeof text !== 'string') {
+ return error.value;
+ }
+ return text.replace(/ +/g, ' ').trim();
+ };
+
+ exports.UNICHAR = exports.CHAR;
+
+ exports.UNICODE = exports.CODE;
+
+ exports.UPPER = function(text) {
+ if (typeof text !== 'string') {
+ return error.value;
+ }
+ return text.toUpperCase();
+ };
+
+ exports.VALUE = null;
+
+ return exports;
+ })();
+
+ jexcel.methods.stats = (function() {
+ var exports = {};
+
+ var SQRT2PI = 2.5066282746310002;
+
+ exports.AVEDEV = null;
+
+ exports.AVERAGE = function() {
+ var range = utils.numbers(utils.flatten(arguments));
+ var n = range.length;
+ var sum = 0;
+ var count = 0;
+ for (var i = 0; i < n; i++) {
+ sum += range[i];
+ count += 1;
+ }
+ return sum / count;
+ };
+
+ exports.AVERAGEA = function() {
+ var range = utils.flatten(arguments);
+ var n = range.length;
+ var sum = 0;
+ var count = 0;
+ for (var i = 0; i < n; i++) {
+ var el = range[i];
+ if (typeof el === 'number') {
+ sum += el;
+ }
+ if (el === true) {
+ sum++;
+ }
+ if (el !== null) {
+ count++;
+ }
+ }
+ return sum / count;
+ };
+
+ exports.AVERAGEIF = function(range, criteria, average_range) {
+ average_range = average_range || range;
+ range = utils.flatten(range);
+ average_range = utils.parseNumberArray(utils.flatten(average_range));
+ if (average_range instanceof Error) {
+ return average_range;
+ }
+ var average_count = 0;
+ var result = 0;
+ for (var i = 0; i < range.length; i++) {
+ if (eval(range[i] + criteria)) { // jshint ignore:line
+ result += average_range[i];
+ average_count++;
+ }
+ }
+ return result / average_count;
+ };
+
+ exports.AVERAGEIFS = null;
+
+ exports.COUNT = function() {
+ return utils.numbers(utils.flatten(arguments)).length;
+ };
+
+ exports.COUNTA = function() {
+ var range = utils.flatten(arguments);
+ return range.length - exports.COUNTBLANK(range);
+ };
+
+ exports.COUNTIN = function (range, value) {
+ var result = 0;
+ for (var i = 0; i < range.length; i++) {
+ if (range[i] === value) {
+ result++;
+ }
+ }
+ return result;
+ };
+
+ exports.COUNTBLANK = function() {
+ var range = utils.flatten(arguments);
+ var blanks = 0;
+ var element;
+ for (var i = 0; i < range.length; i++) {
+ element = range[i];
+ if (element === null || element === '') {
+ blanks++;
+ }
+ }
+ return blanks;
+ };
+
+ exports.COUNTIF = function(range, criteria) {
+ range = utils.flatten(range);
+ if (!/[<>=!]/.test(criteria)) {
+ criteria = '=="' + criteria + '"';
+ }
+ var matches = 0;
+ for (var i = 0; i < range.length; i++) {
+ if (typeof range[i] !== 'string') {
+ if (eval(range[i] + criteria)) { // jshint ignore:line
+ matches++;
+ }
+ } else {
+ if (eval('"' + range[i] + '"' + criteria)) { // jshint ignore:line
+ matches++;
+ }
+ }
+ }
+ return matches;
+ };
+
+ exports.COUNTIFS = function() {
+ var args = utils.argsToArray(arguments);
+ var results = new Array(utils.flatten(args[0]).length);
+ for (var i = 0; i < results.length; i++) {
+ results[i] = true;
+ }
+ for (i = 0; i < args.length; i += 2) {
+ var range = utils.flatten(args[i]);
+ var criteria = args[i + 1];
+ if (!/[<>=!]/.test(criteria)) {
+ criteria = '=="' + criteria + '"';
+ }
+ for (var j = 0; j < range.length; j++) {
+ if (typeof range[j] !== 'string') {
+ results[j] = results[j] && eval(range[j] + criteria); // jshint ignore:line
+ } else {
+ results[j] = results[j] && eval('"' + range[j] + '"' + criteria); // jshint ignore:line
+ }
+ }
+ }
+ var result = 0;
+ for (i = 0; i < results.length; i++) {
+ if (results[i]) {
+ result++;
+ }
+ }
+ return result;
+ };
+
+ exports.COUNTUNIQUE = function () {
+ return UNIQUE.apply(null, utils.flatten(arguments)).length;
+ };
+
+ exports.FISHER = function(x) {
+ x = utils.parseNumber(x);
+ if (x instanceof Error) {
+ return x;
+ }
+ return Math.log((1 + x) / (1 - x)) / 2;
+ };
+
+ exports.FISHERINV = function(y) {
+ y = utils.parseNumber(y);
+ if (y instanceof Error) {
+ return y;
+ }
+ var e2y = Math.exp(2 * y);
+ return (e2y - 1) / (e2y + 1);
+ };
+
+ exports.FREQUENCY = function(data, bins) {
+ data = utils.parseNumberArray(utils.flatten(data));
+ bins = utils.parseNumberArray(utils.flatten(bins));
+ if (utils.anyIsError(data, bins)) {
+ return error.value;
+ }
+ var n = data.length;
+ var b = bins.length;
+ var r = [];
+ for (var i = 0; i <= b; i++) {
+ r[i] = 0;
+ for (var j = 0; j < n; j++) {
+ if (i === 0) {
+ if (data[j] <= bins[0]) {
+ r[0] += 1;
+ }
+ } else if (i < b) {
+ if (data[j] > bins[i - 1] && data[j] <= bins[i]) {
+ r[i] += 1;
+ }
+ } else if (i === b) {
+ if (data[j] > bins[b - 1]) {
+ r[b] += 1;
+ }
+ }
+ }
+ }
+ return r;
+ };
+
+ exports.LARGE = function(range, k) {
+ range = utils.parseNumberArray(utils.flatten(range));
+ k = utils.parseNumber(k);
+ if (utils.anyIsError(range, k)) {
+ return range;
+ }
+ return range.sort(function(a, b) {
+ return b - a;
+ })[k - 1];
+ };
+
+ exports.MAX = function() {
+ var range = utils.numbers(utils.flatten(arguments));
+ return (range.length === 0) ? 0 : Math.max.apply(Math, range);
+ };
+
+ exports.MAXA = function() {
+ var range = utils.arrayValuesToNumbers(utils.flatten(arguments));
+ return (range.length === 0) ? 0 : Math.max.apply(Math, range);
+ };
+
+ exports.MIN = function() {
+ var range = utils.numbers(utils.flatten(arguments));
+ return (range.length === 0) ? 0 : Math.min.apply(Math, range);
+ };
+
+ exports.MINA = function() {
+ var range = utils.arrayValuesToNumbers(utils.flatten(arguments));
+ return (range.length === 0) ? 0 : Math.min.apply(Math, range);
+ };
+
+ exports.MODE = {};
+
+ exports.MODE.MULT = function() {
+ // Credits: Roönaän
+ var range = utils.parseNumberArray(utils.flatten(arguments));
+ if (range instanceof Error) {
+ return range;
+ }
+ var n = range.length;
+ var count = {};
+ var maxItems = [];
+ var max = 0;
+ var currentItem;
+
+ for (var i = 0; i < n; i++) {
+ currentItem = range[i];
+ count[currentItem] = count[currentItem] ? count[currentItem] + 1 : 1;
+ if (count[currentItem] > max) {
+ max = count[currentItem];
+ maxItems = [];
+ }
+ if (count[currentItem] === max) {
+ maxItems[maxItems.length] = currentItem;
+ }
+ }
+ return maxItems;
+ };
+
+ exports.MODE.SNGL = function() {
+ var range = utils.parseNumberArray(utils.flatten(arguments));
+ if (range instanceof Error) {
+ return range;
+ }
+ return exports.MODE.MULT(range).sort(function(a, b) {
+ return a - b;
+ })[0];
+ };
+
+ exports.PERCENTILE = {};
+
+ exports.PERCENTILE.EXC = function(array, k) {
+ array = utils.parseNumberArray(utils.flatten(array));
+ k = utils.parseNumber(k);
+ if (utils.anyIsError(array, k)) {
+ return error.value;
+ }
+ array = array.sort(function(a, b) {
+ {
+ return a - b;
+ }
+ });
+ var n = array.length;
+ if (k < 1 / (n + 1) || k > 1 - 1 / (n + 1)) {
+ return error.num;
+ }
+ var l = k * (n + 1) - 1;
+ var fl = Math.floor(l);
+ return utils.cleanFloat((l === fl) ? array[l] : array[fl] + (l - fl) * (array[fl + 1] - array[fl]));
+ };
+
+ exports.PERCENTILE.INC = function(array, k) {
+ array = utils.parseNumberArray(utils.flatten(array));
+ k = utils.parseNumber(k);
+ if (utils.anyIsError(array, k)) {
+ return error.value;
+ }
+ array = array.sort(function(a, b) {
+ return a - b;
+ });
+ var n = array.length;
+ var l = k * (n - 1);
+ var fl = Math.floor(l);
+ return utils.cleanFloat((l === fl) ? array[l] : array[fl] + (l - fl) * (array[fl + 1] - array[fl]));
+ };
+
+ exports.PERCENTRANK = {};
+
+ exports.PERCENTRANK.EXC = function(array, x, significance) {
+ significance = (significance === undefined) ? 3 : significance;
+ array = utils.parseNumberArray(utils.flatten(array));
+ x = utils.parseNumber(x);
+ significance = utils.parseNumber(significance);
+ if (utils.anyIsError(array, x, significance)) {
+ return error.value;
+ }
+ array = array.sort(function(a, b) {
+ return a - b;
+ });
+ var uniques = UNIQUE.apply(null, array);
+ var n = array.length;
+ var m = uniques.length;
+ var power = Math.pow(10, significance);
+ var result = 0;
+ var match = false;
+ var i = 0;
+ while (!match && i < m) {
+ if (x === uniques[i]) {
+ result = (array.indexOf(uniques[i]) + 1) / (n + 1);
+ match = true;
+ } else if (x >= uniques[i] && (x < uniques[i + 1] || i === m - 1)) {
+ result = (array.indexOf(uniques[i]) + 1 + (x - uniques[i]) / (uniques[i + 1] - uniques[i])) / (n + 1);
+ match = true;
+ }
+ i++;
+ }
+ return Math.floor(result * power) / power;
+ };
+
+ exports.PERCENTRANK.INC = function(array, x, significance) {
+ significance = (significance === undefined) ? 3 : significance;
+ array = utils.parseNumberArray(utils.flatten(array));
+ x = utils.parseNumber(x);
+ significance = utils.parseNumber(significance);
+ if (utils.anyIsError(array, x, significance)) {
+ return error.value;
+ }
+ array = array.sort(function(a, b) {
+ return a - b;
+ });
+ var uniques = UNIQUE.apply(null, array);
+ var n = array.length;
+ var m = uniques.length;
+ var power = Math.pow(10, significance);
+ var result = 0;
+ var match = false;
+ var i = 0;
+ while (!match && i < m) {
+ if (x === uniques[i]) {
+ result = array.indexOf(uniques[i]) / (n - 1);
+ match = true;
+ } else if (x >= uniques[i] && (x < uniques[i + 1] || i === m - 1)) {
+ result = (array.indexOf(uniques[i]) + (x - uniques[i]) / (uniques[i + 1] - uniques[i])) / (n - 1);
+ match = true;
+ }
+ i++;
+ }
+ return Math.floor(result * power) / power;
+ };
+
+ exports.PERMUT = function(number, number_chosen) {
+ number = utils.parseNumber(number);
+ number_chosen = utils.parseNumber(number_chosen);
+ if (utils.anyIsError(number, number_chosen)) {
+ return error.value;
+ }
+ return FACT(number) / FACT(number - number_chosen);
+ };
+
+ exports.PERMUTATIONA = function(number, number_chosen) {
+ number = utils.parseNumber(number);
+ number_chosen = utils.parseNumber(number_chosen);
+ if (utils.anyIsError(number, number_chosen)) {
+ return error.value;
+ }
+ return Math.pow(number, number_chosen);
+ };
+
+ exports.PHI = function(x) {
+ x = utils.parseNumber(x);
+ if (x instanceof Error) {
+ return error.value;
+ }
+ return Math.exp(-0.5 * x * x) / SQRT2PI;
+ };
+
+ exports.PROB = function(range, probability, lower, upper) {
+ if (lower === undefined) {
+ return 0;
+ }
+ upper = (upper === undefined) ? lower : upper;
+
+ range = utils.parseNumberArray(utils.flatten(range));
+ probability = utils.parseNumberArray(utils.flatten(probability));
+ lower = utils.parseNumber(lower);
+ upper = utils.parseNumber(upper);
+ if (utils.anyIsError(range, probability, lower, upper)) {
+ return error.value;
+ }
+
+ if (lower === upper) {
+ return (range.indexOf(lower) >= 0) ? probability[range.indexOf(lower)] : 0;
+ }
+
+ var sorted = range.sort(function(a, b) {
+ return a - b;
+ });
+ var n = sorted.length;
+ var result = 0;
+ for (var i = 0; i < n; i++) {
+ if (sorted[i] >= lower && sorted[i] <= upper) {
+ result += probability[range.indexOf(sorted[i])];
+ }
+ }
+ return result;
+ };
+
+ exports.QUARTILE = {};
+
+ exports.QUARTILE.EXC = function(range, quart) {
+ range = utils.parseNumberArray(utils.flatten(range));
+ quart = utils.parseNumber(quart);
+ if (utils.anyIsError(range, quart)) {
+ return error.value;
+ }
+ switch (quart) {
+ case 1:
+ return exports.PERCENTILE.EXC(range, 0.25);
+ case 2:
+ return exports.PERCENTILE.EXC(range, 0.5);
+ case 3:
+ return exports.PERCENTILE.EXC(range, 0.75);
+ default:
+ return error.num;
+ }
+ };
+
+ exports.QUARTILE.INC = function(range, quart) {
+ range = utils.parseNumberArray(utils.flatten(range));
+ quart = utils.parseNumber(quart);
+ if (utils.anyIsError(range, quart)) {
+ return error.value;
+ }
+ switch (quart) {
+ case 1:
+ return exports.PERCENTILE.INC(range, 0.25);
+ case 2:
+ return exports.PERCENTILE.INC(range, 0.5);
+ case 3:
+ return exports.PERCENTILE.INC(range, 0.75);
+ default:
+ return error.num;
+ }
+ };
+
+ exports.RANK = {};
+
+ exports.RANK.AVG = function(number, range, order) {
+ number = utils.parseNumber(number);
+ range = utils.parseNumberArray(utils.flatten(range));
+ if (utils.anyIsError(number, range)) {
+ return error.value;
+ }
+ range = utils.flatten(range);
+ order = order || false;
+ var sort = (order) ? function(a, b) {
+ return a - b;
+ } : function(a, b) {
+ return b - a;
+ };
+ range = range.sort(sort);
+
+ var length = range.length;
+ var count = 0;
+ for (var i = 0; i < length; i++) {
+ if (range[i] === number) {
+ count++;
+ }
+ }
+
+ return (count > 1) ? (2 * range.indexOf(number) + count + 1) / 2 : range.indexOf(number) + 1;
+ };
+
+ exports.RANK.EQ = function(number, range, order) {
+ number = utils.parseNumber(number);
+ range = utils.parseNumberArray(utils.flatten(range));
+ if (utils.anyIsError(number, range)) {
+ return error.value;
+ }
+ order = order || false;
+ var sort = (order) ? function(a, b) {
+ return a - b;
+ } : function(a, b) {
+ return b - a;
+ };
+ range = range.sort(sort);
+ return range.indexOf(number) + 1;
+ };
+
+ exports.RSQ = function(data_x, data_y) { // no need to flatten here, PEARSON will take care of that
+ data_x = utils.parseNumberArray(utils.flatten(data_x));
+ data_y = utils.parseNumberArray(utils.flatten(data_y));
+ if (utils.anyIsError(data_x, data_y)) {
+ return error.value;
+ }
+ return Math.pow(exports.PEARSON(data_x, data_y), 2);
+ };
+
+ exports.SMALL = function(range, k) {
+ range = utils.parseNumberArray(utils.flatten(range));
+ k = utils.parseNumber(k);
+ if (utils.anyIsError(range, k)) {
+ return range;
+ }
+ return range.sort(function(a, b) {
+ return a - b;
+ })[k - 1];
+ };
+
+ exports.STANDARDIZE = function(x, mean, sd) {
+ x = utils.parseNumber(x);
+ mean = utils.parseNumber(mean);
+ sd = utils.parseNumber(sd);
+ if (utils.anyIsError(x, mean, sd)) {
+ return error.value;
+ }
+ return (x - mean) / sd;
+ };
+
+ exports.STDEV = {};
+
+ exports.STDEV.P = function() {
+ var v = exports.VAR.P.apply(this, arguments);
+ return Math.sqrt(v);
+ };
+
+ exports.STDEV.S = function() {
+ var v = exports.VAR.S.apply(this, arguments);
+ return Math.sqrt(v);
+ };
+
+ exports.STDEVA = function() {
+ var v = exports.VARA.apply(this, arguments);
+ return Math.sqrt(v);
+ };
+
+ exports.STDEVPA = function() {
+ var v = exports.VARPA.apply(this, arguments);
+ return Math.sqrt(v);
+ };
+
+ exports.VAR = {};
+
+ exports.VAR.P = function() {
+ var range = utils.numbers(utils.flatten(arguments));
+ var n = range.length;
+ var sigma = 0;
+ var mean = exports.AVERAGE(range);
+ for (var i = 0; i < n; i++) {
+ sigma += Math.pow(range[i] - mean, 2);
+ }
+ return sigma / n;
+ };
+
+ exports.VAR.S = function() {
+ var range = utils.numbers(utils.flatten(arguments));
+ var n = range.length;
+ var sigma = 0;
+ var mean = exports.AVERAGE(range);
+ for (var i = 0; i < n; i++) {
+ sigma += Math.pow(range[i] - mean, 2);
+ }
+ return sigma / (n - 1);
+ };
+
+ exports.VARA = function() {
+ var range = utils.flatten(arguments);
+ var n = range.length;
+ var sigma = 0;
+ var count = 0;
+ var mean = exports.AVERAGEA(range);
+ for (var i = 0; i < n; i++) {
+ var el = range[i];
+ if (typeof el === 'number') {
+ sigma += Math.pow(el - mean, 2);
+ } else if (el === true) {
+ sigma += Math.pow(1 - mean, 2);
+ } else {
+ sigma += Math.pow(0 - mean, 2);
+ }
+
+ if (el !== null) {
+ count++;
+ }
+ }
+ return sigma / (count - 1);
+ };
+
+ exports.VARPA = function() {
+ var range = utils.flatten(arguments);
+ var n = range.length;
+ var sigma = 0;
+ var count = 0;
+ var mean = exports.AVERAGEA(range);
+ for (var i = 0; i < n; i++) {
+ var el = range[i];
+ if (typeof el === 'number') {
+ sigma += Math.pow(el - mean, 2);
+ } else if (el === true) {
+ sigma += Math.pow(1 - mean, 2);
+ } else {
+ sigma += Math.pow(0 - mean, 2);
+ }
+
+ if (el !== null) {
+ count++;
+ }
+ }
+ return sigma / count;
+ };
+
+ exports.WEIBULL = {};
+
+ exports.WEIBULL.DIST = function(x, alpha, beta, cumulative) {
+ x = utils.parseNumber(x);
+ alpha = utils.parseNumber(alpha);
+ beta = utils.parseNumber(beta);
+ if (utils.anyIsError(x, alpha, beta)) {
+ return error.value;
+ }
+ return (cumulative) ? 1 - Math.exp(-Math.pow(x / beta, alpha)) : Math.pow(x, alpha - 1) * Math.exp(-Math.pow(x / beta, alpha)) * alpha / Math.pow(beta, alpha);
+ };
+
+ exports.Z = {};
+
+ exports.Z.TEST = function(range, x, sd) {
+ range = utils.parseNumberArray(utils.flatten(range));
+ x = utils.parseNumber(x);
+ if (utils.anyIsError(range, x)) {
+ return error.value;
+ }
+
+ sd = sd || exports.STDEV.S(range);
+ var n = range.length;
+ return 1 - exports.NORM.S.DIST((exports.AVERAGE(range) - x) / (sd / Math.sqrt(n)), true);
+ };
+
+ return exports;
+ })();
+
+ jexcel.methods.utils = (function() {
+ var exports = {};
+
+ exports.PROGRESS = function(p, c) {
+ var color = c ? c : 'red';
+ var value = p ? p : '0';
+
+ return '<div style="width:' + value + '%;height:4px;background-color:' + color + ';margin-top:1px;"></div>';
+ };
+
+ exports.RATING = function(v) {
+ var html = '<div class="jrating">';
+ for (var i = 0; i < 5; i++) {
+ if (i < v) {
+ html += '<div class="jrating-selected"></div>';
+ } else {
+ html += '<div></div>';
+ }
+ }
+ html += '</div>';
+ return html;
+ }
+
+ return exports;
+ })();
+
+ for (var i = 0; i < Object.keys(jexcel.methods).length; i++) {
+ var methods = jexcel.methods[Object.keys(jexcel.methods)[i]];
+ for (var j = 0; j < Object.keys(methods).length; j++) {
+ if (typeof(methods[Object.keys(methods)[j]]) == 'function') {
+ window[Object.keys(methods)[j]] = methods[Object.keys(methods)[j]];
+ } else {
+ window[Object.keys(methods)[j]] = function() {
+ return Object.keys(methods)[j] + 'Not implemented';
+ }
+ }
+ }
+ }
+
+ return jexcel;
+})));
\ No newline at end of file |