1/** 2 * jExcel v3.9.1 3 * 4 * Author: Paul Hodel <[email protected]> 5 * Website: https://bossanova.uk/jexcel/ 6 * Description: Create amazing web based spreadsheets. 7 * 8 * This software is distribute under MIT License 9 */ 10 11 if (! jSuites && typeof(require) === 'function') { 12 var jSuites = require('jsuites'); 13 require('jsuites/dist/jsuites.css'); 14} 15 16;(function (global, factory) { 17 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 18 typeof define === 'function' && define.amd ? define(factory) : 19 global.jexcel = factory(); 20}(this, (function () { 21 22 'use strict'; 23 24 // Jexcel core object 25 26 var jexcel = (function(el, options) { 27 // Create jexcel object 28 var obj = {}; 29 obj.options = {}; 30 31 if (! (el instanceof Element || el instanceof HTMLDocument)) { 32 console.error('JEXCEL: el is not a valid DOM element'); 33 return false; 34 } 35 36 // Loading default configuration 37 var defaults = { 38 // External data 39 url:null, 40 // Data 41 data:null, 42 // Copy behavior 43 copyCompatibility:false, 44 // Rows and columns definitions 45 rows:[], 46 columns:[], 47 // Deprected legacy options 48 colHeaders:[], 49 colWidths:[], 50 colAlignments:[], 51 nestedHeaders:null, 52 // Column width that is used by default 53 defaultColWidth:50, 54 defaultColAlign:'center', 55 // Spare rows and columns 56 minSpareRows:0, 57 minSpareCols:0, 58 // Minimal table dimensions 59 minDimensions:[0,0], 60 // Allow Export 61 allowExport:true, 62 // @type {boolean} - Include the header titles on download 63 includeHeadersOnDownload:false, 64 // Allow column sorting 65 columnSorting:true, 66 // Allow column dragging 67 columnDrag:false, 68 // Allow column resizing 69 columnResize:true, 70 // Allow row resizing 71 rowResize:false, 72 // Allow row dragging 73 rowDrag:true, 74 // Allow table edition 75 editable:true, 76 // Allow new rows 77 allowInsertRow:true, 78 // Allow new rows 79 allowManualInsertRow:true, 80 // Allow new columns 81 allowInsertColumn:true, 82 // Allow new rows 83 allowManualInsertColumn:true, 84 // Allow row delete 85 allowDeleteRow:true, 86 // Allow deleting of all rows 87 allowDeletingAllRows:false, 88 // Allow column delete 89 allowDeleteColumn:true, 90 // Allow rename column 91 allowRenameColumn:true, 92 // Allow comments 93 allowComments:false, 94 // Global wrap 95 wordWrap:false, 96 // Image options 97 imageOptions: null, 98 // CSV source 99 csv:null, 100 // Filename 101 csvFileName:'jexcel', 102 // Consider first line as header 103 csvHeaders:true, 104 // Delimiters 105 csvDelimiter:',', 106 // Disable corner selection 107 selectionCopy:true, 108 // Merged cells 109 mergeCells:{}, 110 // Create toolbar 111 toolbar:null, 112 // Allow search 113 search:false, 114 // Create pagination 115 pagination:false, 116 paginationOptions:null, 117 // Full screen 118 fullscreen:false, 119 // Lazy loading 120 lazyLoading:false, 121 loadingSpin:false, 122 // Table overflow 123 tableOverflow:false, 124 tableHeight:'300px', 125 tableWidth:null, 126 // Meta 127 meta: null, 128 // Style 129 style:null, 130 // Execute formulas 131 parseFormulas:true, 132 autoIncrement:true, 133 autoCasting:true, 134 // Event handles 135 onundo:null, 136 onredo:null, 137 onload:null, 138 onchange:null, 139 onbeforechange:null, 140 onafterchanges:null, 141 onbeforeinsertrow: null, 142 oninsertrow:null, 143 onbeforeinsertcolumn: null, 144 oninsertcolumn:null, 145 onbeforedeleterow:null, 146 ondeleterow:null,
vendor: 119,504 bytes, lines 147-3210
147 onbeforedeletecolumn:null, 148 ondeletecolumn:null, 149 onmoverow:null, 150 onmovecolumn:null, 151 onresizerow:null, 152 onresizecolumn:null, 153 onsort:null, 154 onselection:null, 155 onpaste:null, 156 onbeforepaste:null, 157 onmerge:null, 158 onfocus:null, 159 onblur:null, 160 onchangeheader:null, 161 oneditionstart:null, 162 oneditionend:null, 163 onchangestyle:null, 164 onchangemeta:null, 165 onchangepage:null, 166 // Customize any cell behavior 167 updateTable:null, 168 // Detach the HTML table when calling updateTable 169 detachForUpdates: false, 170 // Texts 171 text:{ 172 noRecordsFound: 'No records found', 173 showingPage: 'Showing page {0} of {1} entries', 174 show: 'Show ', 175 search: 'Search', 176 entries: ' entries', 177 columnName: 'Column name', 178 insertANewColumnBefore: 'Insert a new column before', 179 insertANewColumnAfter: 'Insert a new column after', 180 deleteSelectedColumns: 'Delete selected columns', 181 renameThisColumn: 'Rename this column', 182 orderAscending: 'Order ascending', 183 orderDescending: 'Order descending', 184 insertANewRowBefore: 'Insert a new row before', 185 insertANewRowAfter: 'Insert a new row after', 186 deleteSelectedRows: 'Delete selected rows', 187 editComments: 'Edit comments', 188 addComments: 'Add comments', 189 comments: 'Comments', 190 clearComments: 'Clear comments', 191 copy: 'Copy...', 192 paste: 'Paste...', 193 saveAs: 'Save as...', 194 about: 'About', 195 areYouSureToDeleteTheSelectedRows: 'Are you sure to delete the selected rows?', 196 areYouSureToDeleteTheSelectedColumns: 'Are you sure to delete the selected columns?', 197 thisActionWillDestroyAnyExistingMergedCellsAreYouSure: 'This action will destroy any existing merged cells. Are you sure?', 198 thisActionWillClearYourSearchResultsAreYouSure: 'This action will clear your search results. Are you sure?', 199 thereIsAConflictWithAnotherMergedCell: 'There is a conflict with another merged cell', 200 invalidMergeProperties: 'Invalid merged properties', 201 cellAlreadyMerged: 'Cell already merged', 202 noCellsSelected: 'No cells selected', 203 }, 204 // About message 205 about:"jExcel CE Spreadsheet\nVersion 3.9.1\nAuthor: Paul Hodel <[email protected]>\nWebsite: https://bossanova.uk/jexcel/v3", 206 }; 207 208 // Loading initial configuration from user 209 for (var property in defaults) { 210 if (options && options.hasOwnProperty(property)) { 211 if (property === 'text') { 212 obj.options[property] = defaults[property]; 213 for (var textKey in options[property]) { 214 if (options[property].hasOwnProperty(textKey)){ 215 obj.options[property][textKey] = options[property][textKey]; 216 } 217 } 218 } else { 219 obj.options[property] = options[property]; 220 } 221 } else { 222 obj.options[property] = defaults[property]; 223 } 224 } 225 226 // Global elements 227 obj.el = el; 228 obj.corner = null; 229 obj.contextMenu = null; 230 obj.textarea = null; 231 obj.ads = null; 232 obj.content = null; 233 obj.table = null; 234 obj.thead = null; 235 obj.tbody = null; 236 obj.rows = []; 237 obj.results = null; 238 obj.searchInput = null; 239 obj.toolbar = null; 240 obj.pagination = null; 241 obj.pageNumber = null; 242 obj.headerContainer = null; 243 obj.colgroupContainer = null; 244 245 // Containers 246 obj.headers = []; 247 obj.records = []; 248 obj.history = []; 249 obj.formula = []; 250 obj.colgroup = []; 251 obj.selection = []; 252 obj.highlighted = []; 253 obj.selectedCell = null; 254 obj.selectedContainer = null; 255 obj.style = []; 256 obj.data = null; 257 258 // Internal controllers 259 obj.cursor = null; 260 obj.historyIndex = -1; 261 obj.ignoreEvents = false; 262 obj.ignoreHistory = false; 263 obj.edition = null; 264 obj.hashString = null; 265 obj.resizing = null; 266 obj.dragging = null; 267 268 // Lazy loading 269 if (obj.options.lazyLoading == true && (obj.options.tableOverflow == false && obj.options.fullscreen == false)) { 270 console.error('JEXCEL: The lazyloading only works when tableOverflow = yes or fullscreen = yes'); 271 obj.options.lazyLoading = false; 272 } 273 274 /** 275 * Activate/Disable fullscreen 276 * use programmatically : table.fullscreen(); or table.fullscreen(true); or table.fullscreen(false); 277 * @Param {boolean} activate 278 */ 279 obj.fullscreen = function(activate) { 280 // If activate not defined, get reverse options.fullscreen 281 if (activate == null) { 282 activate = ! obj.options.fullscreen; 283 } 284 285 // If change 286 if (obj.options.fullscreen != activate) { 287 obj.options.fullscreen = activate; 288 289 // Test LazyLoading conflict 290 if (activate == true) { 291 el.classList.add('fullscreen'); 292 } else { 293 el.classList.remove('fullscreen'); 294 } 295 } 296 } 297 298 /** 299 * Prepare the jexcel table 300 * 301 * @Param config 302 */ 303 obj.prepareTable = function() { 304 // Loading initial data from remote sources 305 var results = []; 306 307 // Number of columns 308 var size = obj.options.columns.length; 309 310 if (obj.options.data && typeof(obj.options.data[0]) !== 'undefined') { 311 // Data keys 312 var keys = Object.keys(obj.options.data[0]); 313 314 if (keys.length > size) { 315 size = keys.length; 316 } 317 } 318 319 // Minimal dimensions 320 if (obj.options.minDimensions[0] > size) { 321 size = obj.options.minDimensions[0]; 322 } 323 324 // Requests 325 var multiple = []; 326 327 // Preparations 328 for (var i = 0; i < size; i++) { 329 // Deprected options. You should use only columns 330 if (! obj.options.colHeaders[i]) { 331 obj.options.colHeaders[i] = ''; 332 } 333 if (! obj.options.colWidths[i]) { 334 obj.options.colWidths[i] = obj.options.defaultColWidth; 335 } 336 if (! obj.options.colAlignments[i]) { 337 obj.options.colAlignments[i] = obj.options.defaultColAlign; 338 } 339 340 // Default column description 341 if (! obj.options.columns[i]) { 342 obj.options.columns[i] = { type:'text' }; 343 } else if (! obj.options.columns[i].type) { 344 obj.options.columns[i].type = 'text'; 345 } 346 if (! obj.options.columns[i].name) { 347 obj.options.columns[i].name = keys && keys[i] ? keys[i] : i; 348 } 349 if (! obj.options.columns[i].source) { 350 obj.options.columns[i].source = []; 351 } 352 if (! obj.options.columns[i].options) { 353 obj.options.columns[i].options = []; 354 } 355 if (! obj.options.columns[i].editor) { 356 obj.options.columns[i].editor = null; 357 } 358 if (! obj.options.columns[i].allowEmpty) { 359 obj.options.columns[i].allowEmpty = false; 360 } 361 if (! obj.options.columns[i].title) { 362 obj.options.columns[i].title = obj.options.colHeaders[i] ? obj.options.colHeaders[i] : ''; 363 } 364 if (! obj.options.columns[i].width) { 365 obj.options.columns[i].width = obj.options.colWidths[i] ? obj.options.colWidths[i] : obj.options.defaultColWidth; 366 } 367 if (! obj.options.columns[i].align) { 368 obj.options.columns[i].align = obj.options.colAlignments[i] ? obj.options.colAlignments[i] : 'center'; 369 } 370 371 // Pre-load initial source for json autocomplete 372 if (obj.options.columns[i].type == 'autocomplete' || obj.options.columns[i].type == 'dropdown') { 373 // if remote content 374 if (obj.options.columns[i].url) { 375 multiple.push(jSuites.ajax({ 376 url: obj.options.columns[i].url, 377 index: i, 378 method: 'GET', 379 dataType: 'json', 380 multiple: multiple, 381 success: function(data) { 382 var source = []; 383 for (var i = 0; i < data.length; i++) { 384 obj.options.columns[this.index].source.push(data[i]); 385 } 386 }, 387 complete: function() { 388 obj.createTable(); 389 } 390 })); 391 } 392 } else if (obj.options.columns[i].type == 'calendar') { 393 // Default format for date columns 394 if (! obj.options.columns[i].options.format) { 395 obj.options.columns[i].options.format = 'DD/MM/YYYY'; 396 } 397 } 398 } 399 400 // On complete 401 if (! multiple.length) { 402 obj.createTable(); 403 } 404 } 405 406 obj.createTable = function() { 407 // Elements 408 obj.table = document.createElement('table'); 409 obj.thead = document.createElement('thead'); 410 obj.tbody = document.createElement('tbody'); 411 412 // Create headers controllers 413 obj.headers = []; 414 obj.colgroup = []; 415 416 // Create table container 417 obj.content = document.createElement('div'); 418 obj.content.classList.add('jexcel_content'); 419 420 // Create toolbar object 421 obj.toolbar = document.createElement('div'); 422 obj.toolbar.classList.add('jexcel_toolbar'); 423 424 // Search 425 var searchContainer = document.createElement('div'); 426 var searchText = document.createTextNode((obj.options.text.search) + ': '); 427 obj.searchInput = document.createElement('input'); 428 obj.searchInput.classList.add('jexcel_search'); 429 searchContainer.appendChild(searchText); 430 searchContainer.appendChild(obj.searchInput); 431 obj.searchInput.onfocus = function() { 432 obj.resetSelection(); 433 } 434 435 // Pagination select option 436 var paginationUpdateContainer = document.createElement('div'); 437 438 if (obj.options.pagination > 0 && obj.options.paginationOptions && obj.options.paginationOptions.length > 0) { 439 obj.paginationDropdown = document.createElement('select'); 440 obj.paginationDropdown.classList.add('jexcel_pagination_dropdown'); 441 obj.paginationDropdown.onchange = function() { 442 obj.options.pagination = parseInt(this.value); 443 obj.page(0); 444 } 445 446 for (var i = 0; i < obj.options.paginationOptions.length; i++) { 447 var temp = document.createElement('option'); 448 temp.value = obj.options.paginationOptions[i]; 449 temp.innerHTML = obj.options.paginationOptions[i]; 450 obj.paginationDropdown.appendChild(temp); 451 } 452 453 paginationUpdateContainer.appendChild(document.createTextNode(obj.options.text.show)); 454 paginationUpdateContainer.appendChild(obj.paginationDropdown); 455 paginationUpdateContainer.appendChild(document.createTextNode(obj.options.text.entries)); 456 } 457 458 // Filter and pagination container 459 obj.filter = document.createElement('div'); 460 obj.filter.classList.add('jexcel_filter'); 461 obj.filter.appendChild(paginationUpdateContainer); 462 obj.filter.appendChild(searchContainer); 463 464 // Colsgroup 465 obj.colgroupContainer = document.createElement('colgroup'); 466 var tempCol = document.createElement('col'); 467 tempCol.setAttribute('width', '50'); 468 obj.colgroupContainer.appendChild(tempCol); 469 470 // Nested 471 if (obj.options.nestedHeaders && obj.options.nestedHeaders.length > 0) { 472 // Flexible way to handle nestedheaders 473 if (obj.options.nestedHeaders[0] && obj.options.nestedHeaders[0][0]) { 474 for (var j = 0; j < obj.options.nestedHeaders.length; j++) { 475 obj.thead.appendChild(obj.createNestedHeader(obj.options.nestedHeaders[j])); 476 } 477 } else { 478 obj.thead.appendChild(obj.createNestedHeader(obj.options.nestedHeaders)); 479 } 480 } 481 482 // Row 483 obj.headerContainer = document.createElement('tr'); 484 var tempCol = document.createElement('td'); 485 tempCol.classList.add('jexcel_selectall'); 486 obj.headerContainer.appendChild(tempCol); 487 488 for (var i = 0; i < obj.options.columns.length; i++) { 489 // Create header 490 obj.createCellHeader(i); 491 // Append cell to the container 492 obj.headerContainer.appendChild(obj.headers[i]); 493 obj.colgroupContainer.appendChild(obj.colgroup[i]); 494 } 495 496 obj.thead.appendChild(obj.headerContainer); 497 498 // Content table 499 obj.table = document.createElement('table'); 500 obj.table.classList.add('jexcel'); 501 obj.table.setAttribute('cellpadding', '0'); 502 obj.table.setAttribute('cellspacing', '0'); 503 obj.table.setAttribute('unselectable', 'yes'); 504 obj.table.setAttribute('onselectstart', 'return false'); 505 obj.table.appendChild(obj.colgroupContainer); 506 obj.table.appendChild(obj.thead); 507 obj.table.appendChild(obj.tbody); 508 509 // Spreadsheet corner 510 obj.corner = document.createElement('div'); 511 obj.corner.className = 'jexcel_corner'; 512 obj.corner.setAttribute('unselectable', 'on'); 513 obj.corner.setAttribute('onselectstart', 'return false'); 514 515 if (obj.options.selectionCopy == false) { 516 obj.corner.style.display = 'none'; 517 } 518 519 // Textarea helper 520 obj.textarea = document.createElement('textarea'); 521 obj.textarea.className = 'jexcel_textarea'; 522 obj.textarea.id = 'jexcel_textarea'; 523 obj.textarea.tabIndex = '-1'; 524 525 // Contextmenu container 526 obj.contextMenu = document.createElement('div'); 527 obj.contextMenu.className = 'jexcel_contextmenu'; 528 529 // Create element 530 jSuites.contextmenu(obj.contextMenu, { 531 onclick:function() { 532 obj.contextMenu.contextmenu.close(false); 533 } 534 }); 535 536 // Powered by jExcel 537 var ads = document.createElement('a'); 538 ads.setAttribute('href', 'https://bossanova.uk/jexcel/'); 539 obj.ads = document.createElement('div'); 540 obj.ads.className = 'jexcel_about'; 541 if (typeof(sessionStorage) !== "undefined" && ! sessionStorage.getItem('jexcel')) { 542 sessionStorage.setItem('jexcel', true); 543 var img = document.createElement('img'); 544 img.src = '//bossanova.uk/jexcel/logo.png'; 545 ads.appendChild(img); 546 } 547 var span = document.createElement('span'); 548 span.innerHTML = 'Jexcel spreadsheet'; 549 ads.appendChild(span); 550 obj.ads.appendChild(ads); 551 552 // Create table container TODO: frozen columns 553 var container = document.createElement('div'); 554 container.classList.add('jexcel_table'); 555 556 // Pagination 557 obj.pagination = document.createElement('div'); 558 obj.pagination.classList.add('jexcel_pagination'); 559 var paginationInfo = document.createElement('div'); 560 var paginationPages = document.createElement('div'); 561 obj.pagination.appendChild(paginationInfo); 562 obj.pagination.appendChild(paginationPages); 563 564 // Hide pagination if not in use 565 if (! obj.options.pagination) { 566 obj.pagination.style.display = 'none'; 567 } 568 569 // Append containers to the table 570 if (obj.options.search == true) { 571 el.appendChild(obj.filter); 572 } 573 574 // Elements 575 obj.content.appendChild(obj.table); 576 obj.content.appendChild(obj.corner); 577 obj.content.appendChild(obj.textarea); 578 579 el.appendChild(obj.toolbar); 580 el.appendChild(obj.content); 581 el.appendChild(obj.pagination); 582 el.appendChild(obj.contextMenu); 583 el.appendChild(obj.ads); 584 el.classList.add('jexcel_container'); 585 586 // Create toolbar 587 if (obj.options.toolbar && obj.options.toolbar.length) { 588 obj.createToolbar(); 589 } 590 591 // Fullscreen 592 if (obj.options.fullscreen == true) { 593 el.classList.add('fullscreen'); 594 } else { 595 // Overflow 596 if (obj.options.tableOverflow == true) { 597 if (obj.options.tableHeight) { 598 obj.content.style['overflow-y'] = 'auto'; 599 obj.content.style.maxHeight = obj.options.tableHeight; 600 } 601 if (obj.options.tableWidth) { 602 obj.content.style['overflow-x'] = 'auto'; 603 obj.content.style.width = obj.options.tableWidth; 604 } 605 } 606 } 607 608 // With toolbars 609 if (obj.options.tableOverflow != true && obj.options.toolbar) { 610 el.classList.add('with-toolbar'); 611 } 612 613 // Actions 614 if (obj.options.columnDrag == true) { 615 obj.thead.classList.add('draggable'); 616 } 617 if (obj.options.columnResize == true) { 618 obj.thead.classList.add('resizable'); 619 } 620 if (obj.options.rowDrag == true) { 621 obj.tbody.classList.add('draggable'); 622 } 623 if (obj.options.rowResize == true) { 624 obj.tbody.classList.add('resizable'); 625 } 626 627 // Load data 628 obj.setData(); 629 630 // Style 631 if (obj.options.style) { 632 obj.setStyle(obj.options.style, null, null, 1, 1); 633 } 634 } 635 636 /** 637 * Refresh the data 638 * 639 * @return void 640 */ 641 obj.refresh = function() { 642 if (obj.options.url) { 643 // Loading 644 if (obj.options.loadingSpin == true) { 645 jSuites.loading.show(); 646 } 647 648 jSuites.ajax({ 649 url: obj.options.url, 650 method: 'GET', 651 dataType: 'json', 652 success: function(result) { 653 // Data 654 obj.options.data = (result.data) ? result.data : result; 655 // Prepare table 656 obj.setData(); 657 // Hide spin 658 if (obj.options.loadingSpin == true) { 659 jSuites.loading.hide(); 660 } 661 } 662 }); 663 } else { 664 obj.setData(); 665 } 666 } 667 668 /** 669 * Set data 670 * 671 * @param array data In case no data is sent, default is reloaded 672 * @return void 673 */ 674 obj.setData = function(data) { 675 // Update data 676 if (data) { 677 if (typeof(data) == 'string') { 678 data = JSON.parse(data); 679 } 680 681 obj.options.data = data; 682 } 683 684 // Data 685 if (! obj.options.data) { 686 obj.options.data = []; 687 } 688 689 // Prepare data 690 if (obj.options.data) { 691 var data = []; 692 for (var j = 0; j < obj.options.data.length; j++) { 693 var row = []; 694 for (var i = 0; i < obj.options.columns.length; i++) { 695 row[i] = obj.options.data[j][obj.options.columns[i].name]; 696 } 697 data.push(row); 698 } 699 700 obj.options.data = data; 701 } 702 703 // Adjust minimal dimensions 704 var j = 0; 705 var i = 0; 706 var size_i = obj.options.columns.length; 707 var size_j = obj.options.data.length; 708 var min_i = obj.options.minDimensions[0]; 709 var min_j = obj.options.minDimensions[1]; 710 var max_i = min_i > size_i ? min_i : size_i; 711 var max_j = min_j > size_j ? min_j : size_j; 712 713 for (j = 0; j < max_j; j++) { 714 for (i = 0; i < max_i; i++) { 715 if (obj.options.data[j] == undefined) { 716 obj.options.data[j] = []; 717 } 718 719 if (obj.options.data[j][i] == undefined) { 720 obj.options.data[j][i] = ''; 721 } 722 } 723 } 724 725 // Reset containers 726 obj.rows = []; 727 obj.results = null; 728 obj.records = []; 729 obj.history = []; 730 731 // Reset internal controllers 732 obj.historyIndex = -1; 733 734 // Reset data 735 obj.tbody.innerHTML = ''; 736 737 // Lazy loading 738 if (obj.options.lazyLoading == true) { 739 // Load only 100 records 740 var startNumber = 0 741 var finalNumber = obj.options.data.length < 100 ? obj.options.data.length : 100; 742 743 if (obj.options.pagination) { 744 obj.options.pagination = false; 745 console.error('JEXCEL: Pagination will be disable due the lazyLoading'); 746 } 747 } else if (obj.options.pagination) { 748 // Pagination 749 if (! obj.pageNumber) { 750 obj.pageNumber = 0; 751 } 752 var quantityPerPage = obj.options.pagination; 753 startNumber = (obj.options.pagination * obj.pageNumber); 754 finalNumber = (obj.options.pagination * obj.pageNumber) + obj.options.pagination; 755 756 if (obj.options.data.length < finalNumber) { 757 finalNumber = obj.options.data.length; 758 } 759 } else { 760 var startNumber = 0; 761 var finalNumber = obj.options.data.length; 762 } 763 764 // Append nodes to the HTML 765 for (j = 0; j < obj.options.data.length; j++) { 766 // Create row 767 var tr = obj.createRow(j, obj.options.data[j]); 768 // Append line to the table 769 if (j >= startNumber && j < finalNumber) { 770 obj.tbody.appendChild(tr); 771 } 772 } 773 774 if (obj.options.lazyLoading == true) { 775 // Do not create pagination with lazyloading activated 776 } else if (obj.options.pagination) { 777 obj.updatePagination(); 778 } 779 780 // Merge cells 781 if (obj.options.mergeCells) { 782 var keys = Object.keys(obj.options.mergeCells); 783 for (var i = 0; i < keys.length; i++) { 784 var num = obj.options.mergeCells[keys[i]]; 785 obj.setMerge(keys[i], num[0], num[1], 1); 786 } 787 } 788 789 // Updata table with custom configurations if applicable 790 obj.updateTable(); 791 792 // Onload 793 if (! obj.ignoreEvents) { 794 if (typeof(obj.options.onload) == 'function') { 795 obj.options.onload(el, obj); 796 } 797 } 798 } 799 800 /** 801 * Get the whole table data 802 * 803 * @param bool get highlighted cells only 804 * @return array data 805 */ 806 obj.getData = function(highlighted, dataOnly) { 807 // Control vars 808 var dataset = []; 809 var px = 0; 810 var py = 0; 811 812 // Data type 813 var dataType = dataOnly == true || obj.options.copyCompatibility == false ? true : false; 814 815 // Column and row length 816 var x = obj.options.columns.length 817 var y = obj.options.data.length 818 819 // Go through the columns to get the data 820 for (var j = 0; j < y; j++) { 821 px = 0; 822 for (var i = 0; i < x; i++) { 823 // Cell selected or fullset 824 if (! highlighted || obj.records[j][i].classList.contains('highlight')) { 825 // Get value 826 if (! dataset[py]) { 827 dataset[py] = []; 828 } 829 if (! dataType) { 830 dataset[py][px] = obj.records[j][i].innerHTML; 831 } else { 832 dataset[py][px] = obj.options.data[j][i]; 833 } 834 px++; 835 } 836 } 837 if (px > 0) { 838 py++; 839 } 840 } 841 842 return dataset; 843 } 844 845 /** 846 * Get the whole table data 847 * 848 * @param integer row number 849 * @return string value 850 */ 851 obj.getJson = function(highlighted) { 852 // Control vars 853 var data = []; 854 855 // Column and row length 856 var x = obj.options.columns.length 857 var y = obj.options.data.length 858 859 // Go through the columns to get the data 860 for (var j = 0; j < y; j++) { 861 var row = null; 862 for (var i = 0; i < x; i++) { 863 if (! highlighted || obj.records[j][i].classList.contains('highlight')) { 864 if (row == null) { 865 row = {}; 866 } 867 row[obj.options.columns[i].name] = obj.options.data[j][i]; 868 } 869 } 870 871 if (row != null) { 872 data.push(row); 873 } 874 } 875 876 return data; 877 } 878 879 /** 880 * Get a row data by rowNumber 881 */ 882 obj.getRowData = function(rowNumber) { 883 return obj.options.data[rowNumber]; 884 } 885 886 /** 887 * Set a row data by rowNumber 888 */ 889 obj.setRowData = function(rowNumber, data) { 890 for (var i = 0; i < obj.headers.length; i++) { 891 // Update cell 892 var columnName = jexcel.getColumnNameFromId([ i, rowNumber ]); 893 // Set value 894 if (data[i] != null) { 895 obj.setValue(columnName, data[i]); 896 } 897 } 898 } 899 900 /** 901 * Get a column data by columnNumber 902 */ 903 obj.getColumnData = function(columnNumber) { 904 var dataset = []; 905 // Go through the rows to get the data 906 for (var j = 0; j < obj.options.data.length; j++) { 907 dataset.push(obj.options.data[j][columnNumber]); 908 } 909 return dataset; 910 } 911 912 /** 913 * Set a column data by colNumber 914 */ 915 obj.setColumnData = function(colNumber, data) { 916 for (var j = 0; j < obj.rows.length; j++) { 917 // Update cell 918 var columnName = jexcel.getColumnNameFromId([ colNumber, j ]); 919 // Set value 920 if (data[j] != null) { 921 obj.setValue(columnName, data[j]); 922 } 923 } 924 } 925 926 /** 927 * Create row 928 */ 929 obj.createRow = function(j, data) { 930 // Create container 931 if (! obj.records[j]) { 932 obj.records[j] = []; 933 } 934 // Default data 935 if (! data) { 936 var data = obj.options.data[j]; 937 } 938 // New line of data to be append in the table 939 obj.rows[j] = document.createElement('tr'); 940 obj.rows[j].setAttribute('data-y', j); 941 // Definitions 942 if (obj.options.rows[j]) { 943 if (obj.options.rows[j].height) { 944 obj.rows[j].style.height = obj.options.rows[j].height; 945 } 946 } 947 // Row number label 948 var td = document.createElement('td'); 949 td.innerHTML = parseInt(j + 1); 950 td.setAttribute('data-y', j); 951 td.className = 'jexcel_row'; 952 obj.rows[j].appendChild(td); 953 954 // Data columns 955 for (i = 0; i < obj.options.columns.length; i++) { 956 // New column of data to be append in the line 957 obj.records[j][i] = obj.createCell(i, j, data[i]); 958 // Add column to the row 959 obj.rows[j].appendChild(obj.records[j][i]); 960 } 961 962 // Add row to the table body 963 return obj.rows[j]; 964 } 965 966 /** 967 * Create cell 968 */ 969 obj.createCell = function(i, j, value) { 970 // Create cell and properties 971 var td = document.createElement('td'); 972 td.setAttribute('data-x', i); 973 td.setAttribute('data-y', j); 974 // Hidden column 975 if (obj.options.columns[i].type == 'hidden') { 976 td.style.display = 'none'; 977 td.innerHTML = value; 978 } else if (obj.options.columns[i].type == 'checkbox' || obj.options.columns[i].type == 'radio') { 979 // Create input 980 var element = document.createElement('input'); 981 element.type = obj.options.columns[i].type; 982 element.name = 'c' + i; 983 element.checked = (value == 1 || value == true || value == 'true') ? true : false; 984 element.onclick = function() { 985 obj.setValue(td, this.checked); 986 } 987 988 if (obj.options.columns[i].readOnly == true || obj.options.editable == false) { 989 element.setAttribute('disabled', 'disabled'); 990 } 991 992 // Append to the table 993 td.appendChild(element); 994 // Make sure the values are correct 995 obj.options.data[j][i] = element.checked; 996 } else if (obj.options.columns[i].type == 'calendar') { 997 // Try formatted date 998 var formatted = jSuites.calendar.extractDateFromString(value, obj.options.columns[i].options.format); 999 // Create calendar cell 1000 td.innerHTML = jSuites.calendar.getDateString(formatted ? formatted : value, obj.options.columns[i].options.format); 1001 } else if (obj.options.columns[i].type == 'dropdown' || obj.options.columns[i].type == 'autocomplete') { 1002 // Create dropdown cell 1003 td.classList.add('jexcel_dropdown'); 1004 td.innerHTML = obj.getDropDownValue(i, value); 1005 } else if (obj.options.columns[i].type == 'color') { 1006 if (obj.options.columns[i].render == 'square') { 1007 var color = document.createElement('div'); 1008 color.className = 'color'; 1009 color.style.backgroundColor = value; 1010 td.appendChild(color); 1011 } else { 1012 td.style.color = value; 1013 td.innerHTML = value; 1014 } 1015 } else if (obj.options.columns[i].type == 'image') { 1016 if (value && value.substr(0, 10) == 'data:image') { 1017 var img = document.createElement('img'); 1018 img.src = value; 1019 td.appendChild(img); 1020 } 1021 } else { 1022 if ((''+value).substr(0,1) == '=' && obj.options.parseFormulas == true) { 1023 value = obj.executeFormula(value, i, j) 1024 } 1025 if (obj.options.columns[i].mask) { 1026 var decimal = obj.options.columns[i].decimal || '.'; 1027 value = '' + jSuites.mask.run(value, obj.options.columns[i].mask, decimal); 1028 } 1029 1030 td.innerHTML = value; 1031 } 1032 1033 // Readonly 1034 if (obj.options.columns[i].readOnly == true) { 1035 td.className = 'readonly'; 1036 } 1037 1038 // Text align 1039 var colAlign = obj.options.columns[i].align ? obj.options.columns[i].align : 'center'; 1040 td.style.textAlign = colAlign; 1041 1042 // Wrap option 1043 if (obj.options.columns[i].wordWrap != false && (obj.options.wordWrap == true || obj.options.columns[i].wordWrap == true || td.innerHTML.length > 200)) { 1044 td.style.whiteSpace = 'pre-wrap'; 1045 } 1046 1047 // Overflow 1048 if (i > 0) { 1049 if (value || td.innerHTML) { 1050 obj.records[j][i-1].style.overflow = 'hidden'; 1051 } else { 1052 if (i == obj.options.columns.length - 1) { 1053 td.style.overflow = 'hidden'; 1054 } 1055 } 1056 } 1057 1058 return td; 1059 } 1060 1061 obj.createCellHeader = function(colNumber) { 1062 // Create col global control 1063 var colWidth = obj.options.columns[colNumber].width ? obj.options.columns[colNumber].width : obj.options.defaultColWidth; 1064 var colAlign = obj.options.columns[colNumber].align ? obj.options.columns[colNumber].align : obj.options.defaultColAlign; 1065 1066 // Create header cell 1067 obj.headers[colNumber] = document.createElement('td'); 1068 obj.headers[colNumber].innerHTML = obj.options.columns[colNumber].title ? obj.options.columns[colNumber].title : jexcel.getColumnName(colNumber); 1069 obj.headers[colNumber].setAttribute('data-x', colNumber); 1070 obj.headers[colNumber].style.textAlign = colAlign; 1071 if (obj.options.columns[colNumber].title) { 1072 obj.headers[colNumber].setAttribute('title', obj.options.columns[colNumber].title); 1073 } 1074 1075 // Width control 1076 obj.colgroup[colNumber] = document.createElement('col'); 1077 obj.colgroup[colNumber].setAttribute('width', colWidth); 1078 1079 // Hidden column 1080 if (obj.options.columns[colNumber].type == 'hidden') { 1081 obj.headers[colNumber].style.display = 'none'; 1082 obj.colgroup[colNumber].style.display = 'none'; 1083 } 1084 } 1085 1086 obj.createNestedHeader = function(nestedInformation) { 1087 var tr = document.createElement('tr'); 1088 tr.classList.add('jexcel_nested'); 1089 var td = document.createElement('td'); 1090 tr.appendChild(td); 1091 1092 var headerIndex = 0; 1093 for (var i = 0; i < nestedInformation.length; i++) { 1094 // Default values 1095 if (! nestedInformation[i].colspan) { 1096 nestedInformation[i].colspan = 1; 1097 } 1098 if (! nestedInformation[i].align) { 1099 nestedInformation[i].align = 'center'; 1100 } 1101 if (! nestedInformation[i].title) { 1102 nestedInformation[i].title = ''; 1103 } 1104 1105 // Number of columns 1106 var numberOfColumns = nestedInformation[i].colspan; 1107 1108 // Classes container 1109 var column = []; 1110 // Header classes for this cell 1111 for (var x = 0; x < numberOfColumns; x++) { 1112 if (obj.options.columns[headerIndex] && obj.options.columns[headerIndex].type == 'hidden') { 1113 numberOfColumns++; 1114 } 1115 column.push(headerIndex); 1116 headerIndex++; 1117 } 1118 1119 // Created the nested cell 1120 var td = document.createElement('td'); 1121 td.setAttribute('data-column', column.join(',')); 1122 td.setAttribute('colspan', nestedInformation[i].colspan); 1123 td.setAttribute('align', nestedInformation[i].align); 1124 td.innerHTML = nestedInformation[i].title; 1125 tr.appendChild(td); 1126 } 1127 1128 return tr; 1129 } 1130 1131 /** 1132 * Create toolbar 1133 */ 1134 obj.createToolbar = function(toolbar) { 1135 if (toolbar) { 1136 obj.options.toolbar = toolbar; 1137 } else { 1138 var toolbar = obj.options.toolbar; 1139 } 1140 1141 for (var i = 0; i < toolbar.length; i++) { 1142 if (toolbar[i].type == 'i') { 1143 var toolbarItem = document.createElement('i'); 1144 toolbarItem.classList.add('jexcel_toolbar_item'); 1145 toolbarItem.classList.add('material-icons'); 1146 toolbarItem.setAttribute('data-k', toolbar[i].k); 1147 toolbarItem.setAttribute('data-v', toolbar[i].v); 1148 // Tooltip 1149 if (toolbar[i].tooltip) { 1150 toolbarItem.setAttribute('title', toolbar[i].tooltip); 1151 } 1152 // Handle click 1153 if (toolbar[i].onclick && typeof(toolbar[i].onclick)) { 1154 toolbarItem.onclick = toolbar[i].onclick; 1155 } else { 1156 toolbarItem.onclick = function() { 1157 var k = this.getAttribute('data-k'); 1158 var v = this.getAttribute('data-v'); 1159 obj.setStyle(obj.highlighted, k, v); 1160 } 1161 } 1162 // Append element 1163 toolbarItem.innerHTML = toolbar[i].content; 1164 obj.toolbar.appendChild(toolbarItem); 1165 } else if (toolbar[i].type == 'select') { 1166 var toolbarItem = document.createElement('select'); 1167 toolbarItem.classList.add('jexcel_toolbar_item'); 1168 toolbarItem.setAttribute('data-k', toolbar[i].k); 1169 // Tooltip 1170 if (toolbar[i].tooltip) { 1171 toolbarItem.setAttribute('title', toolbar[i].tooltip); 1172 } 1173 // Handle onchange 1174 if (toolbar[i].onchange && typeof(toolbar[i].onchange)) { 1175 toolbarItem.onchange = toolbar[i].onchange; 1176 } else { 1177 toolbarItem.onchange = function() { 1178 var k = this.getAttribute('data-k'); 1179 obj.setStyle(obj.highlighted, k, this.value); 1180 } 1181 } 1182 // Add options to the dropdown 1183 for(var j = 0; j < toolbar[i].v.length; j++) { 1184 var toolbarDropdownOption = document.createElement('option'); 1185 toolbarDropdownOption.value = toolbar[i].v[j]; 1186 toolbarDropdownOption.innerHTML = toolbar[i].v[j]; 1187 toolbarItem.appendChild(toolbarDropdownOption); 1188 } 1189 obj.toolbar.appendChild(toolbarItem); 1190 } else if (toolbar[i].type == 'color') { 1191 var toolbarItem = document.createElement('i'); 1192 toolbarItem.classList.add('jexcel_toolbar_item'); 1193 toolbarItem.classList.add('material-icons'); 1194 toolbarItem.setAttribute('data-k', toolbar[i].k); 1195 toolbarItem.setAttribute('data-v', ''); 1196 // Tooltip 1197 if (toolbar[i].tooltip) { 1198 toolbarItem.setAttribute('title', toolbar[i].tooltip); 1199 } 1200 obj.toolbar.appendChild(toolbarItem); 1201 toolbarItem.onclick = function() { 1202 this.color.open(); 1203 } 1204 toolbarItem.innerHTML = toolbar[i].content; 1205 jSuites.color(toolbarItem, { 1206 onchange:function(o, v) { 1207 var k = o.getAttribute('data-k'); 1208 obj.setStyle(obj.highlighted, k, v); 1209 } 1210 }); 1211 } 1212 } 1213 } 1214 1215 /** 1216 * Merge cells 1217 * @param cellName 1218 * @param colspan 1219 * @param rowspan 1220 * @param ignoreHistoryAndEvents 1221 */ 1222 obj.setMerge = function(cellName, colspan, rowspan, ignoreHistoryAndEvents) { 1223 var test = false; 1224 1225 if (! cellName) { 1226 if (! obj.highlighted.length) { 1227 alert(obj.options.text.noCellsSelected); 1228 return null; 1229 } else { 1230 var x1 = parseInt(obj.highlighted[0].getAttribute('data-x')); 1231 var y1 = parseInt(obj.highlighted[0].getAttribute('data-y')); 1232 var x2 = parseInt(obj.highlighted[obj.highlighted.length-1].getAttribute('data-x')); 1233 var y2 = parseInt(obj.highlighted[obj.highlighted.length-1].getAttribute('data-y')); 1234 var cellName = jexcel.getColumnNameFromId([ x1, y1 ]); 1235 var colspan = (x2 - x1) + 1; 1236 var rowspan = (y2 - y1) + 1; 1237 } 1238 } 1239 1240 var cell = jexcel.getIdFromColumnName(cellName, true); 1241 1242 if (obj.options.mergeCells[cellName]) { 1243 if (obj.records[cell[1]][cell[0]].getAttribute('data-merged')) { 1244 test = obj.options.text.cellAlreadyMerged; 1245 } 1246 } else if ((! colspan || colspan < 2) && (! rowspan || rowspan < 2)) { 1247 test = obj.options.text.invalidMergeProperties; 1248 } else { 1249 var cells = []; 1250 for (var j = cell[1]; j < cell[1] + rowspan; j++) { 1251 for (var i = cell[0]; i < cell[0] + colspan; i++) { 1252 var columnName = jexcel.getColumnNameFromId([i, j]); 1253 if (obj.records[j][i].getAttribute('data-merged')) { 1254 test = obj.options.text.thereIsAConflictWithAnotherMergedCell; 1255 } 1256 } 1257 } 1258 } 1259 1260 if (test) { 1261 alert(test); 1262 } else { 1263 // Add property 1264 if (colspan > 1) { 1265 obj.records[cell[1]][cell[0]].setAttribute('colspan', colspan); 1266 } else { 1267 colspan = 1; 1268 } 1269 if (rowspan > 1) { 1270 obj.records[cell[1]][cell[0]].setAttribute('rowspan', rowspan); 1271 } else { 1272 rowspan = 1; 1273 } 1274 // Keep links to the existing nodes 1275 obj.options.mergeCells[cellName] = [ colspan, rowspan, [] ]; 1276 // Mark cell as merged 1277 obj.records[cell[1]][cell[0]].setAttribute('data-merged', 'true'); 1278 // Overflow 1279 obj.records[cell[1]][cell[0]].style.overflow = 'hidden'; 1280 // History data 1281 var data = []; 1282 // Adjust the nodes 1283 for (var y = cell[1]; y < cell[1] + rowspan; y++) { 1284 for (var x = cell[0]; x < cell[0] + colspan; x++) { 1285 if (! (cell[0] == x && cell[1] == y)) { 1286 data.push(obj.options.data[y][x]); 1287 obj.updateCell(x, y, '', true); 1288 obj.options.mergeCells[cellName][2].push(obj.records[y][x]); 1289 obj.records[y][x].style.display = 'none'; 1290 obj.records[y][x] = obj.records[cell[1]][cell[0]]; 1291 } 1292 } 1293 } 1294 // In the initialization is not necessary keep the history 1295 obj.updateSelection(obj.records[cell[1]][cell[0]]); 1296 1297 if (! ignoreHistoryAndEvents) { 1298 obj.setHistory({ 1299 action:'setMerge', 1300 column:cellName, 1301 colspan:colspan, 1302 rowspan:rowspan, 1303 data:data, 1304 }); 1305 1306 if (typeof(obj.options.onmerge) == 'function') { 1307 obj.options.onmerge(el, cellName, colspan, rowspan); 1308 } 1309 } 1310 } 1311 } 1312 1313 /** 1314 * Merge cells 1315 * @param cellName 1316 * @param colspan 1317 * @param rowspan 1318 * @param ignoreHistoryAndEvents 1319 */ 1320 obj.getMerge = function(cellName) { 1321 var data = {}; 1322 if (cellName) { 1323 if (obj.options.mergeCells[cellName]) { 1324 data = [ obj.options.mergeCells[cellName][0], obj.options.mergeCells[cellName][1] ]; 1325 } else { 1326 data = null; 1327 } 1328 } else { 1329 if (obj.options.mergeCells) { 1330 var mergedCells = obj.options.mergeCells; 1331 var keys = Object.keys(obj.options.mergeCells); 1332 for (var i = 0; i < keys.length; i++) { 1333 data[keys[i]] = [ obj.options.mergeCells[keys[i]][0], obj.options.mergeCells[keys[i]][1] ]; 1334 } 1335 } 1336 } 1337 1338 return data; 1339 } 1340 1341 /** 1342 * Remove merge by cellname 1343 * @param cellName 1344 */ 1345 obj.removeMerge = function(cellName, data, keepOptions) { 1346 if (obj.options.mergeCells[cellName]) { 1347 var cell = jexcel.getIdFromColumnName(cellName, true); 1348 obj.records[cell[1]][cell[0]].removeAttribute('colspan'); 1349 obj.records[cell[1]][cell[0]].removeAttribute('rowspan'); 1350 obj.records[cell[1]][cell[0]].removeAttribute('data-merged'); 1351 var info = obj.options.mergeCells[cellName]; 1352 1353 var index = 0; 1354 for (var j = 0; j < info[1]; j++) { 1355 for (var i = 0; i < info[0]; i++) { 1356 if (j > 0 || i > 0) { 1357 obj.records[cell[1]+j][cell[0]+i] = info[2][index]; 1358 obj.records[cell[1]+j][cell[0]+i].style.display = ''; 1359 // Recover data 1360 if (data && data[index]) { 1361 obj.updateCell(cell[0]+i, cell[1]+j, data[index]); 1362 } 1363 index++; 1364 } 1365 } 1366 } 1367 1368 // Update selection 1369 obj.updateSelection(obj.records[cell[1]][cell[0]], obj.records[cell[1]+j-1][cell[0]+i-1]); 1370 1371 if (! keepOptions) { 1372 delete(obj.options.mergeCells[cellName]); 1373 } 1374 } 1375 } 1376 1377 /** 1378 * Remove all merged cells 1379 */ 1380 obj.destroyMerged = function(keepOptions) { 1381 // Remove any merged cells 1382 if (obj.options.mergeCells) { 1383 var mergedCells = obj.options.mergeCells; 1384 var keys = Object.keys(obj.options.mergeCells); 1385 for (var i = 0; i < keys.length; i++) { 1386 obj.removeMerge(keys[i], null, keepOptions); 1387 } 1388 } 1389 } 1390 1391 /** 1392 * Is column merged 1393 */ 1394 obj.isColMerged = function(x, insertBefore) { 1395 var cols = []; 1396 // Remove any merged cells 1397 if (obj.options.mergeCells) { 1398 var keys = Object.keys(obj.options.mergeCells); 1399 for (var i = 0; i < keys.length; i++) { 1400 var info = jexcel.getIdFromColumnName(keys[i], true); 1401 var colspan = obj.options.mergeCells[keys[i]][0]; 1402 var x1 = info[0]; 1403 var x2 = info[0] + (colspan > 1 ? colspan - 1 : 0); 1404 1405 if (insertBefore == null) { 1406 if ((x1 <= x && x2 >= x)) { 1407 cols.push(keys[i]); 1408 } 1409 } else { 1410 if (insertBefore) { 1411 if ((x1 < x && x2 >= x)) { 1412 cols.push(keys[i]); 1413 } 1414 } else { 1415 if ((x1 <= x && x2 > x)) { 1416 cols.push(keys[i]); 1417 } 1418 } 1419 } 1420 } 1421 } 1422 1423 return cols; 1424 } 1425 1426 /** 1427 * Is rows merged 1428 */ 1429 obj.isRowMerged = function(y, insertBefore) { 1430 var rows = []; 1431 // Remove any merged cells 1432 if (obj.options.mergeCells) { 1433 var keys = Object.keys(obj.options.mergeCells); 1434 for (var i = 0; i < keys.length; i++) { 1435 var info = jexcel.getIdFromColumnName(keys[i], true); 1436 var rowspan = obj.options.mergeCells[keys[i]][1]; 1437 var y1 = info[1]; 1438 var y2 = info[1] + (rowspan > 1 ? rowspan - 1 : 0); 1439 1440 if (insertBefore == null) { 1441 if ((y1 <= y && y2 >= y)) { 1442 rows.push(keys[i]); 1443 } 1444 } else { 1445 if (insertBefore) { 1446 if ((y1 < y && y2 >= y)) { 1447 rows.push(keys[i]); 1448 } 1449 } else { 1450 if ((y1 <= y && y2 > y)) { 1451 rows.push(keys[i]); 1452 } 1453 } 1454 } 1455 } 1456 } 1457 1458 return rows; 1459 } 1460 1461 /** 1462 * Open the editor 1463 * 1464 * @param object cell 1465 * @return void 1466 */ 1467 obj.openEditor = function(cell, empty, e) { 1468 // Get cell position 1469 var y = cell.getAttribute('data-y'); 1470 var x = cell.getAttribute('data-x'); 1471 1472 // On edition start 1473 if (! obj.ignoreEvents) { 1474 if (typeof(obj.options.oneditionstart) == 'function') { 1475 obj.options.oneditionstart(el, cell, x, y); 1476 } 1477 } 1478 1479 // Overflow 1480 if (x > 0) { 1481 obj.records[y][x-1].style.overflow = 'hidden'; 1482 } 1483 1484 // Create editor 1485 var createEditor = function(type) { 1486 // Cell information 1487 var info = cell.getBoundingClientRect(); 1488 1489 // Create dropdown 1490 var editor = document.createElement(type); 1491 editor.style.width = (info.width) + 'px'; 1492 editor.style.height = (info.height - 2) + 'px'; 1493 editor.style.minHeight = (info.height - 2) + 'px'; 1494 1495 // Edit cell 1496 cell.classList.add('editor'); 1497 cell.innerHTML = ''; 1498 cell.appendChild(editor); 1499 1500 return editor; 1501 } 1502 1503 // Readonly 1504 if (cell.classList.contains('readonly') == true) { 1505 // Do nothing 1506 } else { 1507 // Holder 1508 obj.edition = [ obj.records[y][x], obj.records[y][x].innerHTML, x, y ]; 1509 1510 // If there is a custom editor for it 1511 if (obj.options.columns[x].editor) { 1512 // Custom editors 1513 obj.options.columns[x].editor.openEditor(cell, el, empty, e); 1514 } else { 1515 // Native functions 1516 if (obj.options.columns[x].type == 'hidden') { 1517 // Do nothing 1518 } else if (obj.options.columns[x].type == 'checkbox' || obj.options.columns[x].type == 'radio') { 1519 // Get value 1520 var value = cell.children[0].checked ? false : true; 1521 // Toogle value 1522 obj.setValue(cell, value); 1523 // Do not keep edition open 1524 obj.edition = null; 1525 } else if (obj.options.columns[x].type == 'dropdown' || obj.options.columns[x].type == 'autocomplete') { 1526 // Get current value 1527 var value = obj.options.data[y][x]; 1528 1529 // Create dropdown 1530 if (typeof(obj.options.columns[x].filter) == 'function') { 1531 var source = obj.options.columns[x].filter(el, cell, x, y, obj.options.columns[x].source); 1532 } else { 1533 var source = obj.options.columns[x].source; 1534 } 1535 1536 // Create editor 1537 var editor = createEditor('div'); 1538 var options = { 1539 data: source, 1540 multiple: obj.options.columns[x].multiple ? true : false, 1541 autocomplete: obj.options.columns[x].autocomplete || obj.options.columns[x].type == 'autocomplete' ? true : false, 1542 opened:true, 1543 value: obj.options.columns[x].multiple ? value.split(';') : value, 1544 width:'100%', 1545 height:editor.style.minHeight, 1546 position: (obj.options.tableOverflow == true || obj.options.fullscreen == true) ? true : false, 1547 onclose:function() { 1548 obj.closeEditor(cell, true); 1549 } 1550 }; 1551 if (obj.options.columns[x].options && obj.options.columns[x].options.type) { 1552 options.type = obj.options.columns[x].options.type; 1553 } 1554 jSuites.dropdown(editor, options); 1555 } else if (obj.options.columns[x].type == 'calendar' || obj.options.columns[x].type == 'color') { 1556 // Value 1557 var value = obj.options.data[y][x]; 1558 // Create editor 1559 var editor = createEditor('input'); 1560 editor.value = value; 1561 1562 if (obj.options.tableOverflow == true || obj.options.fullscreen == true) { 1563 obj.options.columns[x].options.position = true; 1564 } 1565 obj.options.columns[x].options.value = obj.options.data[y][x]; 1566 obj.options.columns[x].options.opened = true; 1567 obj.options.columns[x].options.onclose = function(el, value) { 1568 obj.closeEditor(cell, true); 1569 } 1570 // Current value 1571 if (obj.options.columns[x].type == 'color') { 1572 jSuites.color(editor, obj.options.columns[x].options); 1573 } else { 1574 var calendar = jSuites.calendar(editor, obj.options.columns[x].options); 1575 calendar.setValue(value); 1576 } 1577 // Focus on editor 1578 editor.focus(); 1579 } else if (obj.options.columns[x].type == 'image') { 1580 // Value 1581 var img = cell.children[0]; 1582 // Create editor 1583 var editor = createEditor('div'); 1584 editor.style.position = 'relative'; 1585 var div = document.createElement('div'); 1586 div.classList.add('jclose'); 1587 if (img && img.src) { 1588 div.appendChild(img); 1589 } 1590 editor.appendChild(div); 1591 jSuites.image(div, obj.options.imageOptions); 1592 const rect = cell.getBoundingClientRect(); 1593 const rectContent = div.getBoundingClientRect(); 1594 if (window.innerHeight < rect.bottom + rectContent.height) { 1595 div.style.top = (rect.top - (rectContent.height + 2)) + 'px'; 1596 } else { 1597 div.style.top = (rect.top) + 'px'; 1598 } 1599 } else { 1600 // Value 1601 var value = empty == true ? '' : obj.options.data[y][x]; 1602 1603 // Basic editor 1604 if (obj.options.columns[x].wordWrap != false && (obj.options.wordWrap == true || obj.options.columns[x].wordWrap == true)) { 1605 var editor = createEditor('textarea'); 1606 } else { 1607 var editor = createEditor('input'); 1608 // Mask 1609 if (obj.options.columns[x].mask) { 1610 editor.setAttribute('data-mask', obj.options.columns[x].mask); 1611 } 1612 } 1613 1614 editor.value = value; 1615 editor.onblur = function() { 1616 obj.closeEditor(cell, true); 1617 }; 1618 editor.focus(); 1619 } 1620 } 1621 } 1622 } 1623 1624 /** 1625 * Close the editor and save the information 1626 * 1627 * @param object cell 1628 * @param boolean save 1629 * @return void 1630 */ 1631 obj.closeEditor = function(cell, save) { 1632 var x = parseInt(cell.getAttribute('data-x')); 1633 var y = parseInt(cell.getAttribute('data-y')); 1634 1635 // Get cell properties 1636 if (save == true) { 1637 // If custom editor 1638 if (obj.options.columns[x].editor) { 1639 // Custom editor 1640 var value = obj.options.columns[x].editor.closeEditor(cell, save); 1641 } else { 1642 // Native functions 1643 if (obj.options.columns[x].type == 'checkbox' || obj.options.columns[x].type == 'radio' || obj.options.columns[x].type == 'hidden') { 1644 // Do nothing 1645 } else if (obj.options.columns[x].type == 'dropdown' || obj.options.columns[x].type == 'autocomplete') { 1646 var value = cell.children[0].dropdown.close(true); 1647 } else if (obj.options.columns[x].type == 'calendar') { 1648 var value = cell.children[0].calendar.close(true); 1649 } else if (obj.options.columns[x].type == 'color') { 1650 var value = cell.children[0].color.close(true); 1651 } else if (obj.options.columns[x].type == 'image') { 1652 var img = cell.children[0].children[0].children[0]; 1653 var value = img && img.tagName == 'IMG' ? img.src : ''; 1654 } else if (obj.options.columns[x].type == 'numeric') { 1655 var value = cell.children[0].value; 1656 if (value.substr(0,1) != '=') { 1657 if (value == '') { 1658 value = obj.options.columns[x].allowEmpty ? '' : 0; 1659 } 1660 } 1661 cell.children[0].onblur = null; 1662 } else { 1663 var value = cell.children[0].value; 1664 cell.children[0].onblur = null; 1665 } 1666 } 1667 1668 // Ignore changes if the value is the same 1669 if (obj.options.data[y][x] == value) { 1670 cell.innerHTML = obj.edition[1]; 1671 } else { 1672 obj.setValue(cell, value); 1673 } 1674 } else { 1675 if (obj.options.columns[x].editor) { 1676 // Custom editor 1677 obj.options.columns[x].editor.closeEditor(cell, save); 1678 } else { 1679 if (obj.options.columns[x].type == 'dropdown' || obj.options.columns[x].type == 'autocomplete') { 1680 cell.children[0].dropdown.close(true); 1681 } else if (obj.options.columns[x].type == 'calendar') { 1682 cell.children[0].calendar.close(true); 1683 } else if (obj.options.columns[x].type == 'color') { 1684 cell.children[0].color.close(true); 1685 } else { 1686 cell.children[0].onblur = null; 1687 } 1688 } 1689 1690 // Restore value 1691 cell.innerHTML = obj.edition && obj.edition[1] ? obj.edition[1] : ''; 1692 } 1693 1694 // On edition end 1695 if (! obj.ignoreEvents) { 1696 if (typeof(obj.options.oneditionend) == 'function') { 1697 obj.options.oneditionend(el, cell, x, y, value, save); 1698 } 1699 } 1700 1701 // Remove editor class 1702 cell.classList.remove('editor'); 1703 1704 // Finish edition 1705 obj.edition = null; 1706 } 1707 1708 /** 1709 * Get the cell object 1710 * 1711 * @param object cell 1712 * @return string value 1713 */ 1714 obj.getCell = function(cell) { 1715 // Convert in case name is excel liked ex. A10, BB92 1716 cell = jexcel.getIdFromColumnName(cell, true); 1717 var x = cell[0]; 1718 var y = cell[1]; 1719 1720 return obj.records[y][x]; 1721 } 1722 1723 /** 1724 * Get the cell object from coords 1725 * 1726 * @param object cell 1727 * @return string value 1728 */ 1729 obj.getCellFromCoords = function(x, y) { 1730 return obj.records[y][x]; 1731 } 1732 1733 /** 1734 * Get label 1735 * 1736 * @param object cell 1737 * @return string value 1738 */ 1739 obj.getLabel = function(cell) { 1740 // Convert in case name is excel liked ex. A10, BB92 1741 cell = jexcel.getIdFromColumnName(cell, true); 1742 var x = cell[0]; 1743 var y = cell[1]; 1744 1745 return obj.records[y][x].innerHTML; 1746 } 1747 1748 /** 1749 * Get labelfrom coords 1750 * 1751 * @param object cell 1752 * @return string value 1753 */ 1754 obj.getLabelFromCoords = function(x, y) { 1755 return obj.records[y][x].innerHTML; 1756 } 1757 1758 /** 1759 * Get the value from a cell 1760 * 1761 * @param object cell 1762 * @return string value 1763 */ 1764 obj.getValue = function(cell, processedValue) { 1765 if (typeof(cell) == 'object') { 1766 var x = cell.getAttribute('data-x'); 1767 var y = cell.getAttribute('data-y'); 1768 } else { 1769 cell = jexcel.getIdFromColumnName(cell, true); 1770 var x = cell[0]; 1771 var y = cell[1]; 1772 } 1773 1774 var value = null; 1775 1776 if (x != null && y != null) { 1777 if (obj.records[y] && obj.records[y][x] && (processedValue || obj.options.copyCompatibility == true)) { 1778 value = obj.records[y][x].innerHTML; 1779 } else { 1780 if (obj.options.data[y] && obj.options.data[y][x] != 'undefined') { 1781 value = obj.options.data[y][x]; 1782 } 1783 } 1784 } 1785 1786 return value; 1787 } 1788 1789 /** 1790 * Get the value from a coords 1791 * 1792 * @param int x 1793 * @param int y 1794 * @return string value 1795 */ 1796 obj.getValueFromCoords = function(x, y, processedValue) { 1797 var value = null; 1798 1799 if (x != null && y != null) { 1800 if ((obj.records[y] && obj.records[y][x]) && processedValue || obj.options.copyCompatibility == true) { 1801 value = obj.records[y][x].innerHTML; 1802 } else { 1803 if (obj.options.data[y] && obj.options.data[y][x] != 'undefined') { 1804 value = obj.options.data[y][x]; 1805 } 1806 } 1807 } 1808 1809 return value; 1810 } 1811 1812 /** 1813 * Set a cell value 1814 * 1815 * @param mixed cell destination cell 1816 * @param string value value 1817 * @return void 1818 */ 1819 obj.setValue = function(cell, value, force) { 1820 var records = []; 1821 1822 if (typeof(cell) == 'string') { 1823 var columnId = jexcel.getIdFromColumnName(cell, true); 1824 var x = columnId[0]; 1825 var y = columnId[1]; 1826 1827 // Update cell 1828 records.push(obj.updateCell(x, y, value, force)); 1829 1830 // Update all formulas in the chain 1831 obj.updateFormulaChain(x, y, records); 1832 } else { 1833 var x = null; 1834 var y = null; 1835 if (cell && cell.getAttribute) { 1836 var x = cell.getAttribute('data-x'); 1837 var y = cell.getAttribute('data-y'); 1838 } 1839 1840 // Update cell 1841 if (x != null && y != null) { 1842 records.push(obj.updateCell(x, y, value, force)); 1843 1844 // Update all formulas in the chain 1845 obj.updateFormulaChain(x, y, records); 1846 } else { 1847 var keys = Object.keys(cell); 1848 if (keys.length > 0) { 1849 for (var i = 0; i < keys.length; i++) { 1850 if (typeof(cell[i]) == 'string') { 1851 var columnId = jexcel.getIdFromColumnName(cell[i], true); 1852 var x = columnId[0]; 1853 var y = columnId[1]; 1854 } else { 1855 var x = cell[i].getAttribute('data-x'); 1856 var y = cell[i].getAttribute('data-y'); 1857 } 1858 1859 // Update cell 1860 if (x != null && y != null) { 1861 records.push(obj.updateCell(x, y, value, force)); 1862 1863 // Update all formulas in the chain 1864 obj.updateFormulaChain(x, y, records); 1865 } 1866 } 1867 } 1868 } 1869 } 1870 1871 // Update history 1872 obj.setHistory({ 1873 action:'setValue', 1874 records:records, 1875 selection:obj.selectedCell, 1876 }); 1877 1878 // Update table with custom configurations if applicable 1879 obj.updateTable(); 1880 1881 // On after changes 1882 obj.onafterchanges(el, records); 1883 } 1884 1885 /** 1886 * Set a cell value based on coordinates 1887 * 1888 * @param int x destination cell 1889 * @param int y destination cell 1890 * @param string value 1891 * @return void 1892 */ 1893 obj.setValueFromCoords = function(x, y, value, force) { 1894 var records = []; 1895 records.push(obj.updateCell(x, y, value, force)); 1896 1897 // Update all formulas in the chain 1898 obj.updateFormulaChain(x, y, records); 1899 1900 // Update history 1901 obj.setHistory({ 1902 action:'setValue', 1903 records:records, 1904 selection:obj.selectedCell, 1905 }); 1906 1907 // Update table with custom configurations if applicable 1908 obj.updateTable(); 1909 1910 // On after changes 1911 obj.onafterchanges(el, records); 1912 } 1913 1914 /** 1915 * Toogle 1916 */ 1917 obj.setCheckRadioValue = function() { 1918 var records = []; 1919 var keys = Object.keys(obj.highlighted); 1920 for (var i = 0; i < keys.length; i++) { 1921 var x = obj.highlighted[i].getAttribute('data-x'); 1922 var y = obj.highlighted[i].getAttribute('data-y'); 1923 1924 if (obj.options.columns[x].type == 'checkbox' || obj.options.columns[x].type == 'radio') { 1925 // Update cell 1926 records.push(obj.updateCell(x, y, ! obj.options.data[y][x])); 1927 } 1928 } 1929 1930 if (records.length) { 1931 // Update history 1932 obj.setHistory({ 1933 action:'setValue', 1934 records:records, 1935 selection:obj.selectedCell, 1936 }); 1937 1938 // On after changes 1939 obj.onafterchanges(el, records); 1940 } 1941 } 1942 1943 /** 1944 * Update cell content 1945 * 1946 * @param object cell 1947 * @return void 1948 */ 1949 obj.updateCell = function(x, y, value, force) { 1950 // Changing value depending on the column type 1951 if (obj.records[y][x].classList.contains('readonly') == true && ! force) { 1952 // Do nothing 1953 var record = { 1954 x: x, 1955 y: y, 1956 col: x, 1957 row: y 1958 } 1959 } else { 1960 // On change 1961 if (! obj.ignoreEvents) { 1962 if (typeof(obj.options.onbeforechange) == 'function') { 1963 // Overwrite a value 1964 var val = obj.options.onbeforechange(el, obj.records[y][x], x, y, value); 1965 1966 // If you return something this will overwrite the value 1967 if (val != undefined) { 1968 value = val; 1969 } 1970 } 1971 } 1972 1973 // History format 1974 var record = { 1975 x: x, 1976 y: y, 1977 col: x, 1978 row: y, 1979 newValue: value, 1980 oldValue: obj.options.data[y][x], 1981 } 1982 1983 if (obj.options.columns[x].editor) { 1984 // Update data and cell 1985 obj.options.data[y][x] = value; 1986 obj.options.columns[x].editor.setValue(obj.records[y][x], value, force); 1987 } else { 1988 // Native functions 1989 if (obj.options.columns[x].type == 'checkbox' || obj.options.columns[x].type == 'radio') { 1990 // Unchecked all options 1991 if (obj.options.columns[x].type == 'radio') { 1992 for (var j = 0; j < obj.options.data.length; j++) { 1993 obj.options.data[j][x] = false; 1994 } 1995 } 1996 1997 // Update data and cell 1998 obj.records[y][x].children[0].checked = (value == 1 || value == true || value == 'true' || value == 'TRUE') ? true : false; 1999 obj.options.data[y][x] = obj.records[y][x].children[0].checked; 2000 } else if (obj.options.columns[x].type == 'dropdown' || obj.options.columns[x].type == 'autocomplete') { 2001 // Update data and cell 2002 obj.options.data[y][x] = value; 2003 obj.records[y][x].innerHTML = obj.getDropDownValue(x, value); 2004 } else if (obj.options.columns[x].type == 'calendar') { 2005 // Update calendar 2006 var formatted = jSuites.calendar.extractDateFromString(value, obj.options.columns[x].options.format); 2007 // Update data and cell 2008 obj.options.data[y][x] = value; 2009 obj.records[y][x].innerHTML = jSuites.calendar.getDateString(formatted ? formatted : value, obj.options.columns[x].options.format); 2010 } else if (obj.options.columns[x].type == 'color') { 2011 // Update color 2012 obj.options.data[y][x] = value; 2013 // Render 2014 if (obj.options.columns[x].render == 'square') { 2015 var color = document.createElement('div'); 2016 color.className = 'color'; 2017 color.style.backgroundColor = value; 2018 obj.records[y][x].innerHTML = ''; 2019 obj.records[y][x].appendChild(color); 2020 } else { 2021 obj.records[y][x].style.color = value; 2022 obj.records[y][x].innerHTML = value; 2023 } 2024 } else if (obj.options.columns[x].type == 'image') { 2025 value = ''+value; 2026 obj.options.data[y][x] = value; 2027 obj.records[y][x].innerHTML = ''; 2028 if (value && value.substr(0, 10) == 'data:image') { 2029 var img = document.createElement('img'); 2030 img.src = value; 2031 obj.records[y][x].appendChild(img); 2032 } 2033 } else { 2034 // Update data and cell 2035 obj.options.data[y][x] = value; 2036 // Label 2037 if (('' + value).substr(0,1) == '=' && obj.options.parseFormulas == true) { 2038 value = obj.executeFormula(value, x, y); 2039 } 2040 if (obj.options.columns[x].mask) { 2041 var decimal = obj.options.columns[x].decimal || '.'; 2042 value = '' + jSuites.mask.run(value, obj.options.columns[x].mask, decimal); 2043 } 2044 obj.records[y][x].innerHTML = value; 2045 2046 // Handle big text inside a cell 2047 if (obj.options.columns[x].wordWrap != false && (obj.options.wordWrap == true || obj.options.columns[x].wordWrap == true || obj.records[y][x].innerHTML.length > 200)) { 2048 obj.records[y][x].style.whiteSpace = 'pre-wrap'; 2049 } else { 2050 obj.records[y][x].style.whiteSpace = ''; 2051 } 2052 } 2053 } 2054 2055 // Overflow 2056 if (x > 0) { 2057 if (value) { 2058 obj.records[y][x-1].style.overflow = 'hidden'; 2059 } else { 2060 obj.records[y][x-1].style.overflow = ''; 2061 } 2062 } 2063 2064 // On change 2065 if (! obj.ignoreEvents) { 2066 if (typeof(obj.options.onchange) == 'function') { 2067 obj.options.onchange(el, (obj.records[y] && obj.records[y][x] ? obj.records[y][x] : null), x, y, value, record.oldValue); 2068 } 2069 } 2070 } 2071 2072 return record; 2073 } 2074 2075 /** 2076 * Helper function to copy data using the corner icon 2077 */ 2078 obj.copyData = function(o, d) { 2079 // Get data from all selected cells 2080 var data = obj.getData(true, true); 2081 2082 // Selected cells 2083 var h = obj.selectedContainer; 2084 2085 // Cells 2086 var x1 = parseInt(o.getAttribute('data-x')); 2087 var y1 = parseInt(o.getAttribute('data-y')); 2088 var x2 = parseInt(d.getAttribute('data-x')); 2089 var y2 = parseInt(d.getAttribute('data-y')); 2090 2091 // Records 2092 var records = []; 2093 var breakControl = false; 2094 2095 if (h[0] == x1) { 2096 // Vertical copy 2097 if (y1 < h[1]) { 2098 var rowNumber = y1 - h[1]; 2099 } else { 2100 var rowNumber = 1; 2101 } 2102 var colNumber = 0; 2103 } else { 2104 if (x1 < h[0]) { 2105 var colNumber = x1 - h[0]; 2106 } else { 2107 var colNumber = 1; 2108 } 2109 var rowNumber = 0; 2110 } 2111 2112 // Copy data procedure 2113 var posx = 0; 2114 var posy = 0; 2115 2116 for (var j = y1; j <= y2; j++) { 2117 // Skip hidden rows 2118 if (obj.rows[j] && obj.rows[j].style.display == 'none') { 2119 continue; 2120 } 2121 2122 // Controls 2123 if (data[posy] == undefined) { 2124 posy = 0; 2125 } 2126 posx = 0; 2127 2128 // Data columns 2129 if (h[0] != x1) { 2130 if (x1 < h[0]) { 2131 var colNumber = x1 - h[0]; 2132 } else { 2133 var colNumber = 1; 2134 } 2135 } 2136 // Data columns 2137 for (var i = x1; i <= x2; i++) { 2138 // Update non-readonly 2139 if (obj.records[j][i] && ! obj.records[j][i].classList.contains('readonly') && obj.records[j][i].style.display != 'none' && breakControl == false) { 2140 // Stop if contains value 2141 if (! obj.selection.length) { 2142 if (obj.options.data[j][i] != '') { 2143 breakControl = true; 2144 continue; 2145 } 2146 } 2147 2148 // Column 2149 if (data[posy] == undefined) { 2150 posx = 0; 2151 } else if (data[posy][posx] == undefined) { 2152 posx = 0; 2153 } 2154 2155 // Value 2156 var value = data[posy][posx]; 2157 2158 if (value && ! data[1] && obj.options.autoIncrement == true) { 2159 if (obj.options.columns[i].type == 'text' || obj.options.columns[i].type == 'number') { 2160 if ((''+value).substr(0,1) == '=') { 2161 var tokens = value.match(/([A-Z]+[0-9]+)/g); 2162 2163 if (tokens) { 2164 var affectedTokens = []; 2165 for (var index = 0; index < tokens.length; index++) { 2166 var position = jexcel.getIdFromColumnName(tokens[index], 1); 2167 position[0] += colNumber; 2168 position[1] += rowNumber; 2169 if (position[1] < 0) { 2170 position[1] = 0; 2171 } 2172 var token = jexcel.getColumnNameFromId([position[0], position[1]]); 2173 2174 if (token != tokens[index]) { 2175 affectedTokens[tokens[index]] = token; 2176 } 2177 } 2178 // Update formula 2179 if (affectedTokens) { 2180 value = obj.updateFormula(value, affectedTokens) 2181 } 2182 } 2183 } else { 2184 if (value == Number(value)) { 2185 value = Number(value) + rowNumber; 2186 } 2187 } 2188 } else if (obj.options.columns[i].type == 'calendar') { 2189 var date = new Date(value); 2190 date.setDate(date.getDate() + rowNumber); 2191 value = date.getFullYear() + '-' + jexcel.doubleDigitFormat(parseInt(date.getMonth() + 1)) + '-' + jexcel.doubleDigitFormat(date.getDate()) + ' ' + '00:00:00'; 2192 } 2193 } 2194 2195 records.push(obj.updateCell(i, j, value)); 2196 2197 // Update all formulas in the chain 2198 obj.updateFormulaChain(i, j, records); 2199 } 2200 posx++; 2201 if (h[0] != x1) { 2202 colNumber++; 2203 } 2204 } 2205 posy++; 2206 rowNumber++; 2207 } 2208 2209 // Update history 2210 obj.setHistory({ 2211 action:'setValue', 2212 records:records, 2213 selection:obj.selectedCell, 2214 }); 2215 2216 // Update table with custom configuration if applicable 2217 obj.updateTable(); 2218 2219 // On after changes 2220 obj.onafterchanges(el, records); 2221 } 2222 2223 /** 2224 * Refresh current selection 2225 */ 2226 obj.refreshSelection = function() { 2227 if (obj.selectedCell) { 2228 obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); 2229 } 2230 } 2231 2232 /** 2233 * Move coords to A1 in case ovelaps with an excluded cell 2234 */ 2235 obj.conditionalSelectionUpdate = function(type, o, d) { 2236 if (type == 1) { 2237 if (obj.selectedCell && ((o >= obj.selectedCell[1] && o <= obj.selectedCell[3]) || (d >= obj.selectedCell[1] && d <= obj.selectedCell[3]))) { 2238 obj.resetSelection(); 2239 return; 2240 } 2241 } else { 2242 if (obj.selectedCell && ((o >= obj.selectedCell[0] && o <= obj.selectedCell[2]) || (d >= obj.selectedCell[0] && d <= obj.selectedCell[2]))) { 2243 obj.resetSelection(); 2244 return; 2245 } 2246 } 2247 } 2248 2249 /** 2250 * Clear table selection 2251 */ 2252 obj.resetSelection = function(blur) { 2253 // Remove style 2254 if (! obj.highlighted.length) { 2255 var previousStatus = 0; 2256 } else { 2257 var previousStatus = 1; 2258 2259 for (var i = 0; i < obj.highlighted.length; i++) { 2260 obj.highlighted[i].classList.remove('highlight'); 2261 obj.highlighted[i].classList.remove('highlight-left'); 2262 obj.highlighted[i].classList.remove('highlight-right'); 2263 obj.highlighted[i].classList.remove('highlight-top'); 2264 obj.highlighted[i].classList.remove('highlight-bottom'); 2265 obj.highlighted[i].classList.remove('highlight-selected'); 2266 2267 var px = parseInt(obj.highlighted[i].getAttribute('data-x')); 2268 var py = parseInt(obj.highlighted[i].getAttribute('data-y')); 2269 2270 // Check for merged cells 2271 if (obj.highlighted[i].getAttribute('data-merged')) { 2272 var colspan = parseInt(obj.highlighted[i].getAttribute('colspan')); 2273 var rowspan = parseInt(obj.highlighted[i].getAttribute('rowspan')); 2274 var ux = colspan > 0 ? px + (colspan - 1) : px; 2275 var uy = rowspan > 0 ? py + (rowspan - 1): py; 2276 } else { 2277 var ux = px; 2278 var uy = py; 2279 } 2280 2281 // Remove selected from headers 2282 for (var j = px; j <= ux; j++) { 2283 if (obj.headers[j]) { 2284 obj.headers[j].classList.remove('selected'); 2285 } 2286 } 2287 2288 // Remove selected from rows 2289 for (var j = py; j <= uy; j++) { 2290 if (obj.rows[j]) { 2291 obj.rows[j].classList.remove('selected'); 2292 } 2293 } 2294 } 2295 } 2296 2297 // Reset highlighed cells 2298 obj.highlighted = []; 2299 2300 // Reset 2301 obj.selectedCell = null; 2302 2303 // Hide corner 2304 obj.corner.style.top = '-2000px'; 2305 obj.corner.style.left = '-2000px'; 2306 2307 if (obj.ignoreEvents != true && blur == true) { 2308 if (obj.options.onblur) { 2309 if (typeof(obj.options.onblur) == 'function') { 2310 if (previousStatus == 1) { 2311 obj.options.onblur(el); 2312 } 2313 } 2314 } 2315 } 2316 2317 return previousStatus; 2318 } 2319 2320 /** 2321 * Update selection based on two cells 2322 */ 2323 obj.updateSelection = function(el1, el2, origin) { 2324 var x1 = el1.getAttribute('data-x'); 2325 var y1 = el1.getAttribute('data-y'); 2326 if (el2) { 2327 var x2 = el2.getAttribute('data-x'); 2328 var y2 = el2.getAttribute('data-y'); 2329 } else { 2330 var x2 = x1; 2331 var y2 = y1; 2332 } 2333 2334 obj.updateSelectionFromCoords(x1, y1, x2, y2, origin); 2335 } 2336 2337 /** 2338 * Update selection from coords 2339 */ 2340 obj.updateSelectionFromCoords = function(x1, y1, x2, y2, origin) { 2341 // Reset Selection 2342 var updated = null; 2343 var previousState = obj.resetSelection(); 2344 2345 // Same element 2346 if (x2 == null) { 2347 x2 = x1; 2348 } 2349 if (y2 == null) { 2350 y2 = y1; 2351 } 2352 2353 // Selection must be within the existing data 2354 if (x1 >= obj.headers.length) { 2355 x1 = obj.headers.length - 1; 2356 } 2357 if (y1 >= obj.rows.length) { 2358 y1 = obj.rows.length - 1; 2359 } 2360 if (x2 >= obj.headers.length) { 2361 x2 = obj.headers.length - 1; 2362 } 2363 if (y2 >= obj.rows.length) { 2364 y2 = obj.rows.length - 1; 2365 } 2366 2367 // Keep selected cell 2368 obj.selectedCell = [x1, y1, x2, y2]; 2369 2370 // Select cells 2371 if (x1 != null) { 2372 // Add selected cell 2373 if (obj.records[y1][x1]) { 2374 obj.records[y1][x1].classList.add('highlight-selected'); 2375 } 2376 2377 // Origin & Destination 2378 if (parseInt(x1) < parseInt(x2)) { 2379 var px = parseInt(x1); 2380 var ux = parseInt(x2); 2381 } else { 2382 var px = parseInt(x2); 2383 var ux = parseInt(x1); 2384 } 2385 2386 if (parseInt(y1) < parseInt(y2)) { 2387 var py = parseInt(y1); 2388 var uy = parseInt(y2); 2389 } else { 2390 var py = parseInt(y2); 2391 var uy = parseInt(y1); 2392 } 2393 2394 // Verify merged columns 2395 for (var i = px; i <= ux; i++) { 2396 for (var j = py; j <= uy; j++) { 2397 if (obj.records[j][i] && obj.records[j][i].getAttribute('data-merged')) { 2398 var x = parseInt(obj.records[j][i].getAttribute('data-x')); 2399 var y = parseInt(obj.records[j][i].getAttribute('data-y')); 2400 var colspan = parseInt(obj.records[j][i].getAttribute('colspan')); 2401 var rowspan = parseInt(obj.records[j][i].getAttribute('rowspan')); 2402 2403 if (colspan > 1) { 2404 if (x < px) { 2405 px = x; 2406 } 2407 if (x + colspan > ux) { 2408 ux = x + colspan - 1; 2409 } 2410 } 2411 2412 if (rowspan) { 2413 if (y < py) { 2414 py = y; 2415 2416 } 2417 if (y + rowspan > uy) { 2418 uy = y + rowspan - 1; 2419 } 2420 } 2421 } 2422 } 2423 } 2424 2425 // Limits 2426 var borderLeft = null; 2427 var borderRight = null; 2428 var borderTop = null; 2429 var borderBottom = null; 2430 2431 // Vertical limits 2432 for (var j = py; j <= uy; j++) { 2433 if (obj.rows[j].style.display != 'none') { 2434 if (borderTop == null) { 2435 borderTop = j; 2436 } 2437 borderBottom = j; 2438 } 2439 } 2440 2441 // Redefining styles 2442 for (var i = px; i <= ux; i++) { 2443 for (var j = py; j <= uy; j++) { 2444 if (obj.rows[j].style.display != 'none' && obj.records[j][i].style.display != 'none') { 2445 obj.records[j][i].classList.add('highlight'); 2446 obj.highlighted.push(obj.records[j][i]); 2447 } 2448 } 2449 2450 // Horizontal limits 2451 if (obj.options.columns[i].type != 'hidden') { 2452 if (borderLeft == null) { 2453 borderLeft = i; 2454 } 2455 borderRight = i; 2456 } 2457 } 2458 2459 // Create borders 2460 if (! borderLeft) { 2461 borderLeft = 0; 2462 } 2463 if (! borderRight) { 2464 borderRight = 0; 2465 } 2466 for (var i = borderLeft; i <= borderRight; i++) { 2467 if (obj.options.columns[i].type != 'hidden') { 2468 // Top border 2469 if (obj.records[borderTop][i]) { 2470 obj.records[borderTop][i].classList.add('highlight-top'); 2471 } 2472 // Bottom border 2473 if (obj.records[borderBottom][i]) { 2474 obj.records[borderBottom][i].classList.add('highlight-bottom'); 2475 } 2476 // Add selected from headers 2477 obj.headers[i].classList.add('selected'); 2478 } 2479 } 2480 2481 for (var j = borderTop; j <= borderBottom; j++) { 2482 if (obj.rows[j].style.display != 'none') { 2483 // Left border 2484 obj.records[j][borderLeft].classList.add('highlight-left'); 2485 // Right border 2486 obj.records[j][borderRight].classList.add('highlight-right'); 2487 // Add selected from rows 2488 obj.rows[j].classList.add('selected'); 2489 } 2490 } 2491 2492 obj.selectedContainer = [ borderLeft, borderTop, borderRight, borderBottom ]; 2493 } 2494 2495 // Handle events 2496 if (obj.ignoreEvents != true) { 2497 if (obj.options.onfocus) { 2498 if (typeof(obj.options.onfocus) == 'function') { 2499 if (previousState == 0) { 2500 obj.options.onfocus(el); 2501 } 2502 } 2503 } 2504 2505 if (typeof(obj.options.onselection) == 'function') { 2506 obj.options.onselection(el, borderLeft, borderTop, borderRight, borderBottom, origin); 2507 } 2508 } 2509 2510 // Find corner cell 2511 obj.updateCornerPosition(); 2512 } 2513 2514 /** 2515 * Remove copy selection 2516 * 2517 * @return void 2518 */ 2519 obj.removeCopySelection = function() { 2520 // Remove current selection 2521 for (var i = 0; i < obj.selection.length; i++) { 2522 obj.selection[i].classList.remove('selection'); 2523 obj.selection[i].classList.remove('selection-left'); 2524 obj.selection[i].classList.remove('selection-right'); 2525 obj.selection[i].classList.remove('selection-top'); 2526 obj.selection[i].classList.remove('selection-bottom'); 2527 } 2528 2529 obj.selection = []; 2530 } 2531 2532 /** 2533 * Update copy selection 2534 * 2535 * @param int x, y 2536 * @return void 2537 */ 2538 obj.updateCopySelection = function(x3, y3) { 2539 // Remove selection 2540 obj.removeCopySelection(); 2541 2542 // Get elements first and last 2543 var x1 = obj.selectedContainer[0]; 2544 var y1 = obj.selectedContainer[1]; 2545 var x2 = obj.selectedContainer[2]; 2546 var y2 = obj.selectedContainer[3]; 2547 2548 if (x3 != null && y3 != null) { 2549 if (x3 - x2 > 0) { 2550 var px = parseInt(x2) + 1; 2551 var ux = parseInt(x3); 2552 } else { 2553 var px = parseInt(x3); 2554 var ux = parseInt(x1) - 1; 2555 } 2556 2557 if (y3 - y2 > 0) { 2558 var py = parseInt(y2) + 1; 2559 var uy = parseInt(y3); 2560 } else { 2561 var py = parseInt(y3); 2562 var uy = parseInt(y1) - 1; 2563 } 2564 2565 if (ux - px < uy - py) { 2566 var px = parseInt(x1); 2567 var ux = parseInt(x2); 2568 } else { 2569 var py = parseInt(y1); 2570 var uy = parseInt(y2); 2571 } 2572 2573 for (var j = py; j <= uy; j++) { 2574 for (var i = px; i <= ux; i++) { 2575 if (obj.records[j][i] && obj.rows[j].style.display != 'none' && obj.records[j][i].style.display != 'none') { 2576 obj.records[j][i].classList.add('selection'); 2577 obj.records[py][i].classList.add('selection-top'); 2578 obj.records[uy][i].classList.add('selection-bottom'); 2579 obj.records[j][px].classList.add('selection-left'); 2580 obj.records[j][ux].classList.add('selection-right'); 2581 2582 // Persist selected elements 2583 obj.selection.push(obj.records[j][i]); 2584 } 2585 } 2586 } 2587 } 2588 } 2589 2590 /** 2591 * Update corner position 2592 * 2593 * @return void 2594 */ 2595 obj.updateCornerPosition = function() { 2596 // If any selected cells 2597 if (! obj.highlighted.length) { 2598 obj.corner.style.top = '-2000px'; 2599 obj.corner.style.left = '-2000px'; 2600 } else { 2601 // Get last cell 2602 var last = obj.highlighted[obj.highlighted.length-1]; 2603 2604 const contentRect = obj.content.getBoundingClientRect(); 2605 var x1 = contentRect.left; 2606 var y1 = contentRect.top; 2607 2608 const lastRect = last.getBoundingClientRect(); 2609 var x2 = lastRect.left; 2610 var y2 = lastRect.top; 2611 var w2 = lastRect.width; 2612 var h2 = lastRect.height; 2613 2614 var x = (x2 - x1) + obj.content.scrollLeft + w2 - 4; 2615 var y = (y2 - y1) + obj.content.scrollTop + h2 - 4; 2616 2617 // Place the corner in the correct place 2618 obj.corner.style.top = y + 'px'; 2619 obj.corner.style.left = x + 'px'; 2620 } 2621 } 2622 2623 /** 2624 * Update scroll position based on the selection 2625 */ 2626 obj.updateScroll = function(direction) { 2627 // jExcel Container information 2628 const contentRect = obj.content.getBoundingClientRect(); 2629 var x1 = contentRect.left; 2630 var y1 = contentRect.top; 2631 var w1 = contentRect.width; 2632 var h1 = contentRect.height; 2633 2634 // Direction Left or Up 2635 var reference = obj.records[obj.selectedCell[3]][obj.selectedCell[2]]; 2636 2637 // Reference 2638 const referenceRect = reference.getBoundingClientRect(); 2639 var x2 = referenceRect.left; 2640 var y2 = referenceRect.top; 2641 var w2 = referenceRect.width; 2642 var h2 = referenceRect.height; 2643 2644 // Direction 2645 if (direction == 0 || direction == 1) { 2646 var x = (x2 - x1) + obj.content.scrollLeft; 2647 var y = (y2 - y1) + obj.content.scrollTop - 2; 2648 } else { 2649 var x = (x2 - x1) + obj.content.scrollLeft + w2; 2650 var y = (y2 - y1) + obj.content.scrollTop + h2; 2651 } 2652 2653 // Top position check 2654 if (y > (obj.content.scrollTop + 30) && y < (obj.content.scrollTop + h1)) { 2655 // In the viewport 2656 } else { 2657 // Out of viewport 2658 if (y < obj.content.scrollTop + 30) { 2659 obj.content.scrollTop = y - h2; 2660 } else { 2661 obj.content.scrollTop = y - (h1 - 2); 2662 } 2663 } 2664 2665 // Left position check - TODO: change that to the bottom border of the element 2666 if (x > (obj.content.scrollLeft) && x < (obj.content.scrollLeft + w1)) { 2667 // In the viewport 2668 } else { 2669 // Out of viewport 2670 if (x < obj.content.scrollLeft + 30) { 2671 obj.content.scrollLeft = x; 2672 if (obj.content.scrollLeft < 50) { 2673 obj.content.scrollLeft = 0; 2674 } 2675 } else { 2676 obj.content.scrollLeft = x - (w1 - 20); 2677 } 2678 } 2679 } 2680 2681 /** 2682 * Get the column width 2683 * 2684 * @param int column column number (first column is: 0) 2685 * @return int current width 2686 */ 2687 obj.getWidth = function(column) { 2688 if (! column) { 2689 // Get all headers 2690 var data = []; 2691 for (var i = 0; i < obj.headers.length; i++) { 2692 data.push(obj.options.columns[i].width); 2693 } 2694 } else { 2695 // In case the column is an object 2696 if (typeof(column) == 'object') { 2697 column = $(column).getAttribute('data-x'); 2698 } 2699 2700 data = obj.colgroup[column].getAttribute('width') 2701 } 2702 2703 return data; 2704 } 2705 2706 /** 2707 * Set the column width 2708 * 2709 * @param int column number (first column is: 0) 2710 * @param int new column width 2711 * @param int old column width 2712 */ 2713 obj.setWidth = function (column, width, oldWidth) { 2714 if (width > 0) { 2715 // In case the column is an object 2716 if (typeof(column) == 'object') { 2717 column = column.getAttribute('data-x'); 2718 } 2719 2720 // Oldwidth 2721 if (! oldWidth) { 2722 oldWidth = obj.colgroup[column].getAttribute('width'); 2723 } 2724 2725 // Set width 2726 obj.colgroup[column].setAttribute('width', width); 2727 obj.options.columns[column].width = width; 2728 2729 // Keeping history of changes 2730 obj.setHistory({ 2731 action:'setWidth', 2732 column:column, 2733 oldValue:oldWidth, 2734 newValue:width, 2735 }); 2736 2737 // On resize column 2738 if (obj.ignoreEvents != true) { 2739 if (typeof(obj.options.onresizecolumn) == 'function') { 2740 obj.options.onresizecolumn(el, column, width, oldWidth); 2741 } 2742 } 2743 2744 // Update corner position 2745 obj.updateCornerPosition(); 2746 } 2747 } 2748 2749 /** 2750 * Set the row height 2751 * 2752 * @param row - row number (first row is: 0) 2753 * @param height - new row height 2754 * @param oldHeight - old row height 2755 */ 2756 obj.setHeight = function (row, height, oldHeight) { 2757 if (height > 0) { 2758 // In case the column is an object 2759 if (typeof(row) == 'object') { 2760 row = row.getAttribute('data-y'); 2761 } 2762 2763 // Oldwidth 2764 if (! oldHeight) { 2765 oldHeight = obj.rows[row].getAttribute('height'); 2766 2767 if (! oldHeight) { 2768 var rect = obj.rows[row].getBoundingClientRect(); 2769 oldHeight = rect.height; 2770 } 2771 } 2772 2773 // Integer 2774 height = parseInt(height); 2775 2776 // Set width 2777 obj.rows[row].style.height = height + 'px'; 2778 2779 // Keep options updated 2780 if (! obj.options.rows[row]) { 2781 obj.options.rows[row] = {}; 2782 } 2783 obj.options.rows[row].height = height; 2784 2785 // Keeping history of changes 2786 obj.setHistory({ 2787 action:'setHeight', 2788 row:row, 2789 oldValue:oldHeight, 2790 newValue:height, 2791 }); 2792 2793 // On resize column 2794 if (obj.ignoreEvents != true) { 2795 if (typeof(obj.options.onresizerow) == 'function') { 2796 obj.options.onresizerow(el, row, height, oldHeight); 2797 } 2798 } 2799 2800 // Update corner position 2801 obj.updateCornerPosition(); 2802 } 2803 } 2804 2805 /** 2806 * Get the row height 2807 * 2808 * @param row - row number (first row is: 0) 2809 * @return height - current row height 2810 */ 2811 obj.getHeight = function(row) { 2812 if (! row) { 2813 // Get height of all rows 2814 var data = []; 2815 for (var j = 0; j < obj.rows.length; j++) { 2816 var h = obj.rows[j].style.height; 2817 if (h) { 2818 data[j] = h; 2819 } 2820 } 2821 } else { 2822 // In case the row is an object 2823 if (typeof(row) == 'object') { 2824 row = $(row).getAttribute('data-y'); 2825 } 2826 2827 var data = obj.rows[row].style.height; 2828 } 2829 2830 return data; 2831 } 2832 2833 /** 2834 * Get the column title 2835 * 2836 * @param column - column number (first column is: 0) 2837 * @param title - new column title 2838 */ 2839 obj.getHeader = function(column) { 2840 return obj.headers[column].innerText; 2841 } 2842 2843 /** 2844 * Set the column title 2845 * 2846 * @param column - column number (first column is: 0) 2847 * @param title - new column title 2848 */ 2849 obj.setHeader = function(column, newValue) { 2850 if (obj.headers[column]) { 2851 var oldValue = obj.headers[column].innerText; 2852 2853 if (! newValue) { 2854 newValue = prompt(obj.options.text.columnName, oldValue) 2855 } 2856 2857 if (newValue) { 2858 obj.headers[column].innerHTML = newValue; 2859 // Keep the title property 2860 obj.headers[column].setAttribute('title', newValue); 2861 // Update title 2862 obj.options.columns[column].title = newValue; 2863 } 2864 2865 obj.setHistory({ 2866 action: 'setHeader', 2867 column: column, 2868 oldValue: oldValue, 2869 newValue: newValue 2870 }); 2871 2872 // On change 2873 if (! obj.ignoreEvents) { 2874 if (typeof(obj.options.onchangeheader) == 'function') { 2875 obj.options.onchangeheader(el, column, oldValue, newValue); 2876 } 2877 } 2878 } 2879 } 2880 2881 /** 2882 * Get the headers 2883 * 2884 * @param asArray 2885 * @return mixed 2886 */ 2887 obj.getHeaders = function (asArray) { 2888 var title = []; 2889 2890 for (var i = 0; i < obj.headers.length; i++) { 2891 title.push(obj.getHeader(i)); 2892 } 2893 2894 return asArray ? title : title.join(obj.options.csvDelimiter); 2895 } 2896 2897 /** 2898 * Get meta information from cell(s) 2899 * 2900 * @return integer 2901 */ 2902 obj.getMeta = function(cell, key) { 2903 if (! cell) { 2904 return obj.options.meta; 2905 } else { 2906 if (key) { 2907 return obj.options.meta[cell] && obj.options.meta[cell][key] ? obj.options.meta[cell][key] : null; 2908 } else { 2909 return obj.options.meta[cell] ? obj.options.meta[cell] : null; 2910 } 2911 } 2912 } 2913 2914 /** 2915 * Set meta information to cell(s) 2916 * 2917 * @return integer 2918 */ 2919 obj.setMeta = function(o, k, v) { 2920 if (! obj.options.meta) { 2921 obj.options.meta = {} 2922 } 2923 2924 if (k && v) { 2925 // Set data value 2926 if (! obj.options.meta[o]) { 2927 obj.options.meta[o] = {}; 2928 } 2929 obj.options.meta[o][k] = v; 2930 } else { 2931 // Apply that for all cells 2932 var keys = Object.keys(o); 2933 for (var i = 0; i < keys.length; i++) { 2934 if (! obj.options.meta[keys[i]]) { 2935 obj.options.meta[keys[i]] = {}; 2936 } 2937 2938 var prop = Object.keys(o[keys[i]]); 2939 for (var j = 0; j < prop.length; j++) { 2940 obj.options.meta[keys[i]][prop[j]] = o[keys[i]][prop[j]]; 2941 } 2942 } 2943 } 2944 2945 if (obj.ignoreEvents != true) { 2946 if (typeof(obj.options.onchangemeta) == 'function') { 2947 obj.options.onchangemeta(el, o, k, v); 2948 } 2949 } 2950 } 2951 2952 /** 2953 * Update meta information 2954 * 2955 * @return integer 2956 */ 2957 obj.updateMeta = function(affectedCells) { 2958 if (obj.options.meta) { 2959 var newMeta = {}; 2960 var keys = Object.keys(obj.options.meta); 2961 for (var i = 0; i < keys.length; i++) { 2962 if (affectedCells[keys[i]]) { 2963 newMeta[affectedCells[keys[i]]] = obj.options.meta[keys[i]]; 2964 } else { 2965 newMeta[keys[i]] = obj.options.meta[keys[i]]; 2966 } 2967 } 2968 // Update meta information 2969 obj.options.meta = newMeta; 2970 } 2971 } 2972 2973 /** 2974 * Get style information from cell(s) 2975 * 2976 * @return integer 2977 */ 2978 obj.getStyle = function(cell, key) { 2979 // Cell 2980 if (! cell) { 2981 // Control vars 2982 var data = {}; 2983 2984 // Column and row length 2985 var x = obj.options.data[0].length; 2986 var y = obj.options.data.length; 2987 2988 // Go through the columns to get the data 2989 for (var j = 0; j < y; j++) { 2990 for (var i = 0; i < x; i++) { 2991 // Value 2992 var v = key ? obj.records[j][i].style[key] : obj.records[j][i].getAttribute('style'); 2993 2994 // Any meta data for this column? 2995 if (v) { 2996 // Column name 2997 var k = jexcel.getColumnNameFromId([i, j]); 2998 // Value 2999 data[k] = v; 3000 } 3001 } 3002 } 3003 3004 return data; 3005 } else { 3006 cell = jexcel.getIdFromColumnName(cell, true); 3007 3008 return key ? obj.records[cell[1]][cell[0]].style[key] : obj.records[cell[1]][cell[0]].getAttribute('style'); 3009 } 3010 }, 3011 3012 obj.resetStyle = function(o, ignoreHistoryAndEvents) { 3013 var keys = Object.keys(o); 3014 for (var i = 0; i < keys.length; i++) { 3015 // Position 3016 var cell = jexcel.getIdFromColumnName(keys[i], true); 3017 if (obj.records[cell[1]] && obj.records[cell[1]][cell[0]]) { 3018 obj.records[cell[1]][cell[0]].setAttribute('style', ''); 3019 } 3020 } 3021 obj.setStyle(o, null, null, null, ignoreHistoryAndEvents); 3022 } 3023 3024 /** 3025 * Set meta information to cell(s) 3026 * 3027 * @return integer 3028 */ 3029 obj.setStyle = function(o, k, v, force, ignoreHistoryAndEvents) { 3030 var newValue = {}; 3031 var oldValue = {}; 3032 3033 // Apply style 3034 var applyStyle = function(cellId, key, value) { 3035 // Position 3036 var cell = jexcel.getIdFromColumnName(cellId, true); 3037 3038 if (obj.records[cell[1]] && obj.records[cell[1]][cell[0]]) { 3039 // Current value 3040 var currentValue = obj.records[cell[1]][cell[0]].style[key]; 3041 3042 // Change layout 3043 if (currentValue == value && ! force) { 3044 value = ''; 3045 obj.records[cell[1]][cell[0]].style[key] = ''; 3046 } else { 3047 obj.records[cell[1]][cell[0]].style[key] = value; 3048 } 3049 3050 // History 3051 if (! oldValue[cellId]) { 3052 oldValue[cellId] = []; 3053 } 3054 if (! newValue[cellId]) { 3055 newValue[cellId] = []; 3056 } 3057 3058 oldValue[cellId].push([key + ':' + currentValue]); 3059 newValue[cellId].push([key + ':' + value]); 3060 } 3061 } 3062 3063 if (k && v) { 3064 // Get object from string 3065 if (typeof(o) == 'string') { 3066 applyStyle(o, k, v); 3067 } else { 3068 // Avoid duplications 3069 var oneApplication = []; 3070 // Apply that for all cells 3071 for (var i = 0; i < o.length; i++) { 3072 var x = o[i].getAttribute('data-x'); 3073 var y = o[i].getAttribute('data-y'); 3074 var cellName = jexcel.getColumnNameFromId([x, y]); 3075 // This happens when is a merged cell 3076 if (! oneApplication[cellName]) { 3077 applyStyle(cellName, k, v); 3078 oneApplication[cellName] = true; 3079 } 3080 } 3081 } 3082 } else { 3083 var keys = Object.keys(o); 3084 for (var i = 0; i < keys.length; i++) { 3085 var style = o[keys[i]]; 3086 if (typeof(style) == 'string') { 3087 style = style.split(';'); 3088 } 3089 for (var j = 0; j < style.length; j++) { 3090 if (typeof(style[j]) == 'string') { 3091 style[j] = style[j].split(':'); 3092 } 3093 // Apply value 3094 if (style[j][0].trim()) { 3095 applyStyle(keys[i], style[j][0].trim(), style[j][1]); 3096 } 3097 } 3098 } 3099 } 3100 3101 var keys = Object.keys(oldValue); 3102 for (var i = 0; i < keys.length; i++) { 3103 oldValue[keys[i]] = oldValue[keys[i]].join(';'); 3104 } 3105 var keys = Object.keys(newValue); 3106 for (var i = 0; i < keys.length; i++) { 3107 newValue[keys[i]] = newValue[keys[i]].join(';'); 3108 } 3109 3110 if (! ignoreHistoryAndEvents) { 3111 // Keeping history of changes 3112 obj.setHistory({ 3113 action: 'setStyle', 3114 oldValue: oldValue, 3115 newValue: newValue, 3116 }); 3117 } 3118 3119 if (obj.ignoreEvents != true) { 3120 if (typeof(obj.options.onchangestyle) == 'function') { 3121 obj.options.onchangestyle(el, o, k, v); 3122 } 3123 } 3124 } 3125 3126 /** 3127 * Get cell comments, null cell for all 3128 */ 3129 obj.getComments = function(cell, withAuthor) { 3130 if (cell) { 3131 if (typeof(cell) == 'string') { 3132 var cell = jexcel.getIdFromColumnName(cell, true); 3133 } 3134 3135 if (withAuthor) { 3136 return [obj.records[cell[1]][cell[0]].getAttribute('title'), obj.records[cell[1]][cell[0]].getAttribute('author')]; 3137 } else { 3138 return obj.records[cell[1]][cell[0]].getAttribute('title') || ''; 3139 } 3140 } else { 3141 var data = {}; 3142 for (var j = 0; j < obj.options.data.length; j++) { 3143 for (var i = 0; i < obj.options.columns.length; i++) { 3144 var comments = obj.records[j][i].getAttribute('title'); 3145 if (comments) { 3146 var cell = jexcel.getColumnNameFromId([i, j]); 3147 data[cell] = comments; 3148 } 3149 } 3150 } 3151 return data; 3152 } 3153 } 3154 3155 /** 3156 * Set cell comments 3157 */ 3158 obj.setComments = function(cellId, comments, author) { 3159 if (typeof(cellId) == 'string') { 3160 var cell = jexcel.getIdFromColumnName(cellId, true); 3161 } else { 3162 var cell = cellId; 3163 } 3164 3165 // Keep old value 3166 var title = obj.records[cell[1]][cell[0]].getAttribute('title'); 3167 var author = obj.records[cell[1]][cell[0]].getAttribute('data-author'); 3168 var oldValue = [ title, author ]; 3169 3170 // Set new values 3171 obj.records[cell[1]][cell[0]].setAttribute('title', comments ? comments : ''); 3172 obj.records[cell[1]][cell[0]].setAttribute('data-author', author ? author : ''); 3173 3174 // Remove class if there is no comment 3175 if (comments) { 3176 obj.records[cell[1]][cell[0]].classList.add('jexcel_comments'); 3177 } else { 3178 obj.records[cell[1]][cell[0]].classList.remove('jexcel_comments'); 3179 } 3180 3181 // Save history 3182 obj.setHistory({ 3183 action:'setComments', 3184 column: cellId, 3185 newValue: [ comments, author ], 3186 oldValue: oldValue, 3187 }); 3188 } 3189 3190 /** 3191 * Get table config information 3192 */ 3193 obj.getConfig = function() { 3194 var options = obj.options; 3195 options.style = obj.getStyle(); 3196 options.mergeCells = obj.getMerge(); 3197 options.comments = obj.getComments(); 3198 3199 return options; 3200 } 3201 3202 /** 3203 * Sort data and reload table 3204 */ 3205 obj.orderBy = function(column, order) { 3206 if (column >= 0) { 3207 // Merged cells 3208 if (Object.keys(obj.options.mergeCells).length > 0) { 3209 if (! confirm(obj.options.text.thisActionWillDestroyAnyExistingMergedCellsAreYouSure)) { 3210 return false;
3211 } else { 3212 // Remove merged cells 3213 obj.destroyMerged(); 3214 } 3215 } 3216 3217 // Direction 3218 if (order == null) { 3219 order = obj.headers[column].classList.contains('arrow-down') ? 1 : 0; 3220 } else { 3221 order = order ? 1 : 0; 3222 } 3223 3224 // Filter 3225 Array.prototype.orderBy = function(p, o) { 3226 return this.slice(0).sort(function(a, b) { 3227 var valueA = a[p] == '' ? '' : Number(a[p]) == a[p] ? Number(a[p]) : a[p].toLowerCase(); 3228 var valueB = b[p] == '' ? '' : Number(b[p]) == b[p] ? Number(b[p]) : b[p].toLowerCase(); 3229 3230 if (! o) { 3231 return (valueA == '' && valueB != '') ? 1 : (valueA != '' && valueB == '') ? -1 : (valueA > valueB) ? 1 : (valueA < valueB) ? -1 : 0; 3232 } else { 3233 return (valueA == '' && valueB != '') ? 1 : (valueA != '' && valueB == '') ? -1 : (valueA > valueB) ? -1 : (valueA < valueB) ? 1 : 0; 3234 } 3235 }); 3236 } 3237 3238 // Test order 3239 var temp = []; 3240 if (obj.options.columns[column].type == 'calendar' || 3241 obj.options.columns[column].type == 'checkbox' || 3242 obj.options.columns[column].type == 'radio') { 3243 for (var j = 0; j < obj.options.data.length; j++) { 3244 temp[j] = [ j, obj.options.data[j][column] ]; 3245 } 3246 } else { 3247 for (var j = 0; j < obj.options.data.length; j++) { 3248 temp[j] = [ j, obj.records[j][column].innerHTML ]; 3249 } 3250 } 3251 temp = temp.orderBy(1, order); 3252 3253 // Save history 3254 var newValue = []; 3255 for (var j = 0; j < temp.length; j++) { 3256 newValue[j] = temp[j][0]; 3257 } 3258 3259 // Save history 3260 obj.setHistory({ 3261 action: 'orderBy', 3262 rows: newValue, 3263 column: column, 3264 order: order, 3265 }); 3266 3267 // Update order 3268 obj.updateOrderArrow(column, order); 3269 obj.updateOrder(newValue); 3270 3271 // On sort event 3272 if (obj.ignoreEvents != true) { 3273 if (typeof(obj.options.onsort) == 'function') { 3274 obj.options.onsort(el, column, order); 3275 } 3276 } 3277 3278 return true; 3279 } 3280 } 3281 3282 /** 3283 * Update order arrow 3284 */ 3285 obj.updateOrderArrow = function(column, order) { 3286 // Remove order 3287 for (var i = 0; i < obj.headers.length; i++) { 3288 obj.headers[i].classList.remove('arrow-up'); 3289 obj.headers[i].classList.remove('arrow-down'); 3290 } 3291 3292 // No order specified then toggle order 3293 if (order) { 3294 obj.headers[column].classList.add('arrow-up'); 3295 } else { 3296 obj.headers[column].classList.add('arrow-down'); 3297 } 3298 } 3299 3300 /** 3301 * Update rows position 3302 */ 3303 obj.updateOrder = function(rows) { 3304 // History 3305 var data = [] 3306 for (var j = 0; j < rows.length; j++) { 3307 data[j] = obj.options.data[rows[j]]; 3308 } 3309 obj.options.data = data; 3310 3311 var data = [] 3312 for (var j = 0; j < rows.length; j++) { 3313 data[j] = obj.records[rows[j]]; 3314 } 3315 obj.records = data; 3316 3317 var data = [] 3318 for (var j = 0; j < rows.length; j++) { 3319 data[j] = obj.rows[rows[j]]; 3320 } 3321 obj.rows = data; 3322 3323 // Update references 3324 obj.updateTableReferences(); 3325 3326 // Redo search 3327 if (obj.searchInput.value) { 3328 obj.search(obj.searchInput.value); 3329 } else { 3330 // Create page 3331 obj.results = null; 3332 obj.pageNumber = 0; 3333 3334 if (obj.options.pagination > 0) { 3335 obj.page(0); 3336 } else if (obj.options.lazyLoading == true) { 3337 obj.loadPage(0); 3338 } else { 3339 for (var j = 0; j < obj.rows.length; j++) { 3340 obj.tbody.appendChild(obj.rows[j]); 3341 } 3342 } 3343 } 3344 } 3345 3346 /** 3347 * Move row 3348 * 3349 * @return void 3350 */ 3351 obj.moveRow = function(o, d, ignoreDom) { 3352 if (Object.keys(obj.options.mergeCells).length > 0) { 3353 if (o > d) { 3354 var insertBefore = 1; 3355 } else { 3356 var insertBefore = 0; 3357 } 3358 3359 if (obj.isRowMerged(o).length || obj.isRowMerged(d, insertBefore).length) { 3360 if (! confirm(obj.options.text.thisActionWillDestroyAnyExistingMergedCellsAreYouSure)) { 3361 return false;
vendor: 97,692 bytes, lines 3362-5823
3362 } else { 3363 obj.destroyMerged(); 3364 } 3365 } 3366 } 3367 3368 if (obj.options.search == true) { 3369 if (obj.results && obj.results.length != obj.rows.length) { 3370 if (confirm(obj.options.text.thisActionWillClearYourSearchResultsAreYouSure)) { 3371 obj.resetSearch(); 3372 } else { 3373 return false; 3374 } 3375 } 3376 3377 obj.results = null; 3378 } 3379 3380 if (! ignoreDom) { 3381 if (Array.prototype.indexOf.call(obj.tbody.children, obj.rows[d]) >= 0) { 3382 if (o > d) { 3383 obj.tbody.insertBefore(obj.rows[o], obj.rows[d]); 3384 } else { 3385 obj.tbody.insertBefore(obj.rows[o], obj.rows[d].nextSibling); 3386 } 3387 } else { 3388 obj.tbody.removeChild(obj.rows[o]); 3389 } 3390 } 3391 3392 // Place references in the correct position 3393 obj.rows.splice(d, 0, obj.rows.splice(o, 1)[0]); 3394 obj.records.splice(d, 0, obj.records.splice(o, 1)[0]); 3395 obj.options.data.splice(d, 0, obj.options.data.splice(o, 1)[0]); 3396 3397 // Respect pagination 3398 if (obj.options.pagination > 0 && obj.tbody.children.length != obj.options.pagination) { 3399 obj.page(obj.pageNumber); 3400 } 3401 3402 // Keeping history of changes 3403 obj.setHistory({ 3404 action:'moveRow', 3405 oldValue: o, 3406 newValue: d, 3407 }); 3408 3409 // Update table references 3410 obj.updateTableReferences(); 3411 3412 // Events 3413 if (obj.ignoreEvents != true) { 3414 if (typeof(obj.options.onmoverow) == 'function') { 3415 obj.options.onmoverow(el, o, d); 3416 } 3417 } 3418 } 3419 3420 /** 3421 * Insert a new row 3422 * 3423 * @param mixed - number of blank lines to be insert or a single array with the data of the new row 3424 * @param rowNumber 3425 * @param insertBefore 3426 * @return void 3427 */ 3428 obj.insertRow = function(mixed, rowNumber, insertBefore) { 3429 // Configuration 3430 if (obj.options.allowInsertRow == true) { 3431 // Records 3432 var records = []; 3433 3434 // Data to be insert 3435 var data = []; 3436 3437 // The insert could be lead by number of rows or the array of data 3438 if (mixed > 0) { 3439 var numOfRows = mixed; 3440 } else { 3441 var numOfRows = 1; 3442 3443 if (mixed) { 3444 data = mixed; 3445 } 3446 } 3447 3448 // Direction 3449 var insertBefore = insertBefore ? true : false; 3450 3451 // Current column number 3452 var lastRow = obj.options.data.length - 1; 3453 3454 if (rowNumber == undefined || rowNumber >= parseInt(lastRow) || rowNumber < 0) { 3455 rowNumber = lastRow; 3456 } 3457 3458 // Onbeforeinsertrow 3459 if (typeof(obj.options.onbeforeinsertrow) == 'function') { 3460 if (! obj.options.onbeforeinsertrow(el, rowNumber, numOfRows, insertBefore)) { 3461 console.log('onbeforeinsertrow returned false'); 3462 3463 return false; 3464 } 3465 } 3466 3467 // Merged cells 3468 if (Object.keys(obj.options.mergeCells).length > 0) { 3469 if (obj.isRowMerged(rowNumber, insertBefore).length) { 3470 if (! confirm(obj.options.text.thisActionWillDestroyAnyExistingMergedCellsAreYouSure)) { 3471 return false; 3472 } else { 3473 obj.destroyMerged(); 3474 } 3475 } 3476 } 3477 3478 // Clear any search 3479 if (obj.options.search == true) { 3480 if (obj.results && obj.results.length != obj.rows.length) { 3481 if (confirm(obj.options.text.thisActionWillClearYourSearchResultsAreYouSure)) { 3482 obj.resetSearch(); 3483 } else { 3484 return false; 3485 } 3486 } 3487 3488 obj.results = null; 3489 } 3490 3491 // Insertbefore 3492 var rowIndex = (! insertBefore) ? rowNumber + 1 : rowNumber; 3493 3494 // Keep the current data 3495 var currentRecords = obj.records.splice(rowIndex); 3496 var currentData = obj.options.data.splice(rowIndex); 3497 var currentRows = obj.rows.splice(rowIndex); 3498 3499 // Adding lines 3500 var rowRecords = []; 3501 var rowData = []; 3502 var rowNode = []; 3503 3504 for (var row = rowIndex; row < (numOfRows + rowIndex); row++) { 3505 // Push data to the data container 3506 obj.options.data[row] = []; 3507 for (var col = 0; col < obj.options.columns.length; col++) { 3508 obj.options.data[row][col] = data[col] ? data[col] : ''; 3509 } 3510 // Create row 3511 var tr = obj.createRow(row, obj.options.data[row]); 3512 // Append node 3513 if (currentRows[0]) { 3514 if (Array.prototype.indexOf.call(obj.tbody.children, currentRows[0]) >= 0) { 3515 obj.tbody.insertBefore(tr, currentRows[0]); 3516 } 3517 } else { 3518 if (Array.prototype.indexOf.call(obj.tbody.children, obj.rows[rowNumber]) >= 0) { 3519 obj.tbody.appendChild(tr); 3520 } 3521 } 3522 // Record History 3523 rowRecords.push(obj.records[row]); 3524 rowData.push(obj.options.data[row]); 3525 rowNode.push(tr); 3526 } 3527 3528 // Copy the data back to the main data 3529 Array.prototype.push.apply(obj.records, currentRecords); 3530 Array.prototype.push.apply(obj.options.data, currentData); 3531 Array.prototype.push.apply(obj.rows, currentRows); 3532 3533 // Respect pagination 3534 if (obj.options.pagination > 0) { 3535 obj.page(obj.pageNumber); 3536 } 3537 3538 // Keep history 3539 obj.setHistory({ 3540 action: 'insertRow', 3541 rowNumber: rowNumber, 3542 numOfRows: numOfRows, 3543 insertBefore: insertBefore, 3544 rowRecords: rowRecords, 3545 rowData: rowData, 3546 rowNode: rowNode, 3547 }); 3548 3549 // Remove table references 3550 obj.updateTableReferences(); 3551 3552 // Events 3553 if (obj.ignoreEvents != true) { 3554 if (typeof(obj.options.oninsertrow) == 'function') { 3555 obj.options.oninsertrow(el, rowNumber, numOfRows, rowRecords, insertBefore); 3556 } 3557 } 3558 } 3559 } 3560 3561 /** 3562 * Delete a row by number 3563 * 3564 * @param integer rowNumber - row number to be excluded 3565 * @param integer numOfRows - number of lines 3566 * @return void 3567 */ 3568 obj.deleteRow = function(rowNumber, numOfRows) { 3569 // Global Configuration 3570 if (obj.options.allowDeleteRow == true) { 3571 if (obj.options.allowDeletingAllRows == true || obj.options.data.length > 1) { 3572 // Delete row definitions 3573 if (rowNumber == undefined) { 3574 var number = obj.getSelectedRows(); 3575 3576 if (! number[0]) { 3577 rowNumber = obj.options.data.length - 1; 3578 numOfRows = 1; 3579 } else { 3580 rowNumber = parseInt(number[0].getAttribute('data-y')); 3581 numOfRows = number.length; 3582 } 3583 } 3584 3585 // Last column 3586 var lastRow = obj.options.data.length - 1; 3587 3588 if (rowNumber == undefined || rowNumber > lastRow || rowNumber < 0) { 3589 rowNumber = lastRow; 3590 } 3591 3592 if (! numOfRows) { 3593 numOfRows = 1; 3594 } 3595 3596 // Do not delete more than the number of recoreds 3597 if (rowNumber + numOfRows >= obj.options.data.length) { 3598 numOfRows = obj.options.data.length - rowNumber; 3599 } 3600 3601 // Onbeforedeleterow 3602 if (typeof(obj.options.onbeforedeleterow) == 'function') { 3603 if (! obj.options.onbeforedeleterow(el, rowNumber, numOfRows)) { 3604 console.log('onbeforedeleterow returned false'); 3605 return false; 3606 } 3607 } 3608 3609 if (parseInt(rowNumber) > -1) { 3610 // Merged cells 3611 var mergeExists = false; 3612 if (Object.keys(obj.options.mergeCells).length > 0) { 3613 for (var row = rowNumber; row < rowNumber + numOfRows; row++) { 3614 if (obj.isRowMerged(row, false).length) { 3615 mergeExists = true; 3616 } 3617 } 3618 } 3619 if (mergeExists) { 3620 if (! confirm(obj.options.text.thisActionWillDestroyAnyExistingMergedCellsAreYouSure)) { 3621 return false; 3622 } else { 3623 obj.destroyMerged(); 3624 } 3625 } 3626 3627 // Clear any search 3628 if (obj.options.search == true) { 3629 if (obj.results && obj.results.length != obj.rows.length) { 3630 if (confirm(obj.options.text.thisActionWillClearYourSearchResultsAreYouSure)) { 3631 obj.resetSearch(); 3632 } else { 3633 return false; 3634 } 3635 } 3636 3637 obj.results = null; 3638 } 3639 3640 // Remove node 3641 for (var row = rowNumber; row < rowNumber + numOfRows; row++) { 3642 if (Array.prototype.indexOf.call(obj.tbody.children, obj.rows[row]) >= 0) { 3643 obj.rows[row].className = ''; 3644 obj.rows[row].parentNode.removeChild(obj.rows[row]); 3645 } 3646 } 3647 3648 // Remove data 3649 var rowRecords = obj.records.splice(rowNumber, numOfRows); 3650 var rowData = obj.options.data.splice(rowNumber, numOfRows); 3651 var rowNode = obj.rows.splice(rowNumber, numOfRows); 3652 3653 // Respect pagination 3654 if (obj.options.pagination > 0 && obj.tbody.children.length != obj.options.pagination) { 3655 obj.page(obj.pageNumber); 3656 } 3657 3658 // Remove selection 3659 obj.conditionalSelectionUpdate(1, rowNumber, (rowNumber + numOfRows) - 1); 3660 3661 // Keep history 3662 obj.setHistory({ 3663 action: 'deleteRow', 3664 rowNumber: rowNumber, 3665 numOfRows: numOfRows, 3666 insertBefore: 1, 3667 rowRecords: rowRecords, 3668 rowData: rowData, 3669 rowNode: rowNode 3670 }); 3671 3672 // Remove table references 3673 obj.updateTableReferences(); 3674 3675 // Events 3676 if (obj.ignoreEvents != true) { 3677 if (typeof(obj.options.ondeleterow) == 'function') { 3678 obj.options.ondeleterow(el, rowNumber, numOfRows, rowRecords); 3679 } 3680 } 3681 } 3682 } else { 3683 console.error('JEXCEL. It is not possible to delete the last row'); 3684 } 3685 } 3686 } 3687 3688 3689 /** 3690 * Move column 3691 * 3692 * @return void 3693 */ 3694 obj.moveColumn = function(o, d) { 3695 if (Object.keys(obj.options.mergeCells).length > 0) { 3696 if (o > d) { 3697 var insertBefore = 1; 3698 } else { 3699 var insertBefore = 0; 3700 } 3701 3702 if (obj.isColMerged(o).length || obj.isColMerged(d, insertBefore).length) { 3703 if (! confirm(obj.options.text.thisActionWillDestroyAnyExistingMergedCellsAreYouSure)) { 3704 return false; 3705 } else { 3706 obj.destroyMerged(); 3707 } 3708 } 3709 } 3710 3711 var o = parseInt(o); 3712 var d = parseInt(d); 3713 3714 if (o > d) { 3715 obj.headerContainer.insertBefore(obj.headers[o], obj.headers[d]); 3716 obj.colgroupContainer.insertBefore(obj.colgroup[o], obj.colgroup[d]); 3717 3718 for (var j = 0; j < obj.rows.length; j++) { 3719 obj.rows[j].insertBefore(obj.records[j][o], obj.records[j][d]); 3720 } 3721 } else { 3722 obj.headerContainer.insertBefore(obj.headers[o], obj.headers[d].nextSibling); 3723 obj.colgroupContainer.insertBefore(obj.colgroup[o], obj.colgroup[d].nextSibling); 3724 3725 for (var j = 0; j < obj.rows.length; j++) { 3726 obj.rows[j].insertBefore(obj.records[j][o], obj.records[j][d].nextSibling); 3727 } 3728 } 3729 3730 obj.options.columns.splice(d, 0, obj.options.columns.splice(o, 1)[0]); 3731 obj.headers.splice(d, 0, obj.headers.splice(o, 1)[0]); 3732 obj.colgroup.splice(d, 0, obj.colgroup.splice(o, 1)[0]); 3733 3734 for (var j = 0; j < obj.rows.length; j++) { 3735 obj.options.data[j].splice(d, 0, obj.options.data[j].splice(o, 1)[0]); 3736 obj.records[j].splice(d, 0, obj.records[j].splice(o, 1)[0]); 3737 } 3738 3739 // Keeping history of changes 3740 obj.setHistory({ 3741 action:'moveColumn', 3742 oldValue: o, 3743 newValue: d, 3744 }); 3745 3746 // Update table references 3747 obj.updateTableReferences(); 3748 3749 // Events 3750 if (obj.ignoreEvents != true) { 3751 if (typeof(obj.options.onmovecolumn) == 'function') { 3752 obj.options.onmovecolumn(el, o, d); 3753 } 3754 } 3755 } 3756 3757 3758 /** 3759 * Insert a new column 3760 * 3761 * @param mixed - num of columns to be added or data to be added in one single column 3762 * @param int columnNumber - number of columns to be created 3763 * @param bool insertBefore 3764 * @param object properties - column properties 3765 * @return void 3766 */ 3767 obj.insertColumn = function(mixed, columnNumber, insertBefore, properties) { 3768 // Configuration 3769 if (obj.options.allowInsertColumn == true) { 3770 // Records 3771 var records = []; 3772 3773 // Data to be insert 3774 var data = []; 3775 3776 // The insert could be lead by number of rows or the array of data 3777 if (mixed > 0) { 3778 var numOfColumns = mixed; 3779 } else { 3780 var numOfColumns = 1; 3781 3782 if (mixed) { 3783 data = mixed; 3784 } 3785 } 3786 3787 // Direction 3788 var insertBefore = insertBefore ? true : false; 3789 3790 // Current column number 3791 var lastColumn = obj.options.columns.length - 1; 3792 3793 // Confirm position 3794 if (columnNumber == undefined || columnNumber >= parseInt(lastColumn) || columnNumber < 0) { 3795 columnNumber = lastColumn; 3796 } 3797 3798 // Onbeforeinsertcolumn 3799 if (typeof(obj.options.onbeforeinsertcolumn) == 'function') { 3800 if (! obj.options.onbeforeinsertcolumn(el, columnNumber, numOfColumns, insertBefore)) { 3801 console.log('onbeforeinsertcolumn returned false'); 3802 3803 return false; 3804 } 3805 } 3806 3807 // Merged cells 3808 if (Object.keys(obj.options.mergeCells).length > 0) { 3809 if (obj.isColMerged(columnNumber, insertBefore).length) { 3810 if (! confirm(obj.options.text.thisActionWillDestroyAnyExistingMergedCellsAreYouSure)) { 3811 return false; 3812 } else { 3813 obj.destroyMerged(); 3814 } 3815 } 3816 } 3817 3818 // Create default properties 3819 if (! properties) { 3820 properties = []; 3821 } 3822 3823 for (var i = 0; i < numOfColumns; i++) { 3824 if (! properties[i]) { 3825 properties[i] = { type:'text', source:[], options:[], width:obj.options.defaultColWidth, align:obj.options.defaultColAlign }; 3826 } 3827 } 3828 3829 // Insert before 3830 var columnIndex = (! insertBefore) ? columnNumber + 1 : columnNumber; 3831 obj.options.columns = jexcel.injectArray(obj.options.columns, columnIndex, properties); 3832 3833 // Open space in the containers 3834 var currentHeaders = obj.headers.splice(columnIndex); 3835 var currentColgroup = obj.colgroup.splice(columnIndex); 3836 3837 // History 3838 var historyHeaders = []; 3839 var historyColgroup = []; 3840 var historyRecords = []; 3841 var historyData = []; 3842 3843 // Add new headers 3844 for (var col = columnIndex; col < (numOfColumns + columnIndex); col++) { 3845 obj.createCellHeader(col); 3846 obj.headerContainer.insertBefore(obj.headers[col], obj.headerContainer.children[col+1]); 3847 obj.colgroupContainer.insertBefore(obj.colgroup[col], obj.colgroupContainer.children[col+1]); 3848 3849 historyHeaders.push(obj.headers[col]); 3850 historyColgroup.push(obj.colgroup[col]); 3851 } 3852 3853 // Adding visual columns 3854 for (var row = 0; row < obj.options.data.length; row++) { 3855 // Keep the current data 3856 var currentData = obj.options.data[row].splice(columnIndex); 3857 var currentRecord = obj.records[row].splice(columnIndex); 3858 3859 // History 3860 historyData[row] = []; 3861 historyRecords[row] = []; 3862 3863 for (var col = columnIndex; col < (numOfColumns + columnIndex); col++) { 3864 // New value 3865 var value = data[row] ? data[row] : ''; 3866 obj.options.data[row][col] = value; 3867 // New cell 3868 var td = obj.createCell(col, row, obj.options.data[row][col]); 3869 obj.records[row][col] = td; 3870 // Add cell to the row 3871 if (obj.rows[row]) { 3872 obj.rows[row].insertBefore(td, obj.rows[row].children[col+1]); 3873 } 3874 3875 // Record History 3876 historyData[row].push(value); 3877 historyRecords[row].push(td); 3878 } 3879 3880 // Copy the data back to the main data 3881 Array.prototype.push.apply(obj.options.data[row], currentData); 3882 Array.prototype.push.apply(obj.records[row], currentRecord); 3883 } 3884 3885 Array.prototype.push.apply(obj.headers, currentHeaders); 3886 Array.prototype.push.apply(obj.colgroup, currentColgroup); 3887 3888 // Adjust nested headers 3889 if (obj.options.nestedHeaders && obj.options.nestedHeaders.length > 0) { 3890 // Flexible way to handle nestedheaders 3891 if (obj.options.nestedHeaders[0] && obj.options.nestedHeaders[0][0]) { 3892 for (var j = 0; j < obj.options.nestedHeaders.length; j++) { 3893 var colspan = parseInt(obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan) + numOfColumns; 3894 obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan = colspan; 3895 obj.thead.children[j].children[obj.thead.children[j].children.length-1].setAttribute('colspan', colspan); 3896 } 3897 } else { 3898 var colspan = parseInt(obj.options.nestedHeaders[0].colspan) + numOfColumns; 3899 obj.options.nestedHeaders[0].colspan = colspan; 3900 obj.thead.children[0].children[obj.thead.children[0].children.length-1].setAttribute('colspan', colspan); 3901 } 3902 } 3903 3904 // Keep history 3905 obj.setHistory({ 3906 action: 'insertColumn', 3907 columnNumber:columnNumber, 3908 numOfColumns:numOfColumns, 3909 insertBefore:insertBefore, 3910 columns:properties, 3911 headers:historyHeaders, 3912 colgroup:historyColgroup, 3913 records:historyRecords, 3914 data:historyData, 3915 }); 3916 3917 // Remove table references 3918 obj.updateTableReferences(); 3919 3920 // Events 3921 if (obj.ignoreEvents != true) { 3922 if (typeof(obj.options.oninsertcolumn) == 'function') { 3923 obj.options.oninsertcolumn(el, columnNumber, numOfColumns, historyRecords, insertBefore); 3924 } 3925 } 3926 } 3927 } 3928 3929 /** 3930 * Delete a column by number 3931 * 3932 * @param integer columnNumber - reference column to be excluded 3933 * @param integer numOfColumns - number of columns to be excluded from the reference column 3934 * @return void 3935 */ 3936 obj.deleteColumn = function(columnNumber, numOfColumns) { 3937 // Global Configuration 3938 if (obj.options.allowDeleteColumn == true) { 3939 if (obj.headers.length > 1) { 3940 // Delete column definitions 3941 if (columnNumber == undefined) { 3942 var number = obj.getSelectedColumns(true); 3943 3944 if (! number.length) { 3945 // Remove last column 3946 columnNumber = obj.headers.length - 1; 3947 numOfColumns = 1; 3948 } else { 3949 // Remove selected 3950 columnNumber = parseInt(number[0]); 3951 numOfColumns = parseInt(number.length); 3952 } 3953 } 3954 3955 // Lasat column 3956 var lastColumn = obj.options.data[0].length - 1; 3957 3958 if (columnNumber == undefined || columnNumber > lastColumn || columnNumber < 0) { 3959 columnNumber = lastColumn; 3960 } 3961 3962 // Minimum of columns to be delete is 1 3963 if (! numOfColumns) { 3964 numOfColumns = 1; 3965 } 3966 3967 3968 3969 // Can't delete more than the limit of the table 3970 if (numOfColumns > obj.options.data[0].length - columnNumber) { 3971 numOfColumns = obj.options.data[0].length - columnNumber; 3972 } 3973 3974 // onbeforedeletecolumn 3975 if (typeof(obj.options.onbeforedeletecolumn) == 'function') { 3976 if (! obj.options.onbeforedeletecolumn(el, columnNumber, numOfColumns)) { 3977 console.log('onbeforedeletecolumn returned false'); 3978 return false; 3979 } 3980 } 3981 3982 // Can't remove the last column 3983 if (parseInt(columnNumber) > -1) { 3984 // Merged cells 3985 var mergeExists = false; 3986 if (Object.keys(obj.options.mergeCells).length > 0) { 3987 for (var col = columnNumber; col < columnNumber + numOfColumns; col++) { 3988 if (obj.isColMerged(col, false).length) { 3989 mergeExists = true; 3990 } 3991 } 3992 } 3993 if (mergeExists) { 3994 if (! confirm(obj.options.text.thisActionWillDestroyAnyExistingMergedCellsAreYouSure)) { 3995 return false; 3996 } else { 3997 obj.destroyMerged(); 3998 } 3999 } 4000 4001 // Delete the column properties 4002 var columns = obj.options.columns.splice(columnNumber, numOfColumns); 4003 4004 for (var col = columnNumber; col < columnNumber + numOfColumns; col++) { 4005 obj.colgroup[col].className = ''; 4006 obj.headers[col].className = ''; 4007 obj.colgroup[col].parentNode.removeChild(obj.colgroup[col]); 4008 obj.headers[col].parentNode.removeChild(obj.headers[col]); 4009 } 4010 4011 var historyHeaders = obj.headers.splice(columnNumber, numOfColumns); 4012 var historyColgroup = obj.colgroup.splice(columnNumber, numOfColumns); 4013 var historyRecords = []; 4014 var historyData = []; 4015 4016 for (var row = 0; row < obj.options.data.length; row++) { 4017 for (var col = columnNumber; col < columnNumber + numOfColumns; col++) { 4018 obj.records[row][col].className = ''; 4019 obj.records[row][col].parentNode.removeChild(obj.records[row][col]); 4020 } 4021 } 4022 4023 // Delete headers 4024 for (var row = 0; row < obj.options.data.length; row++) { 4025 // History 4026 historyData[row] = obj.options.data[row].splice(columnNumber, numOfColumns); 4027 historyRecords[row] = obj.records[row].splice(columnNumber, numOfColumns); 4028 } 4029 4030 // Remove selection 4031 obj.conditionalSelectionUpdate(0, columnNumber, (columnNumber + numOfColumns) - 1); 4032 4033 // Adjust nested headers 4034 if (obj.options.nestedHeaders && obj.options.nestedHeaders.length > 0) { 4035 // Flexible way to handle nestedheaders 4036 if (obj.options.nestedHeaders[0] && obj.options.nestedHeaders[0][0]) { 4037 for (var j = 0; j < obj.options.nestedHeaders.length; j++) { 4038 var colspan = parseInt(obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan) - numOfColumns; 4039 obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan = colspan; 4040 obj.thead.children[j].children[obj.thead.children[j].children.length-1].setAttribute('colspan', colspan); 4041 } 4042 } else { 4043 var colspan = parseInt(obj.options.nestedHeaders[0].colspan) - numOfColumns; 4044 obj.options.nestedHeaders[0].colspan = colspan; 4045 obj.thead.children[0].children[obj.thead.children[0].children.length-1].setAttribute('colspan', colspan); 4046 } 4047 } 4048 4049 // Keeping history of changes 4050 obj.setHistory({ 4051 action:'deleteColumn', 4052 columnNumber:columnNumber, 4053 numOfColumns:numOfColumns, 4054 insertBefore: 1, 4055 columns:columns, 4056 headers:historyHeaders, 4057 colgroup:historyColgroup, 4058 records:historyRecords, 4059 data:historyData, 4060 }); 4061 4062 // Update table references 4063 obj.updateTableReferences(); 4064 4065 // Delete 4066 if (obj.ignoreEvents != true) { 4067 if (typeof(obj.options.ondeletecolumn) == 'function') { 4068 obj.options.ondeletecolumn(el, columnNumber, numOfColumns, historyRecords); 4069 } 4070 } 4071 } 4072 } else { 4073 console.error('JEXCEL. It is not possible to delete the last column'); 4074 } 4075 } 4076 } 4077 4078 /** 4079 * Get seleted rows numbers 4080 * 4081 * @return array 4082 */ 4083 obj.getSelectedRows = function(asIds) { 4084 var rows = []; 4085 // Get all selected rows 4086 for (var j = 0; j < obj.rows.length; j++) { 4087 if (obj.rows[j].classList.contains('selected')) { 4088 if (asIds) { 4089 rows.push(j); 4090 } else { 4091 rows.push(obj.rows[j]); 4092 } 4093 } 4094 } 4095 4096 return rows; 4097 }, 4098 4099 /** 4100 * Get seleted column numbers 4101 * 4102 * @return array 4103 */ 4104 obj.getSelectedColumns = function() { 4105 var cols = []; 4106 // Get all selected cols 4107 for (var i = 0; i < obj.headers.length; i++) { 4108 if (obj.headers[i].classList.contains('selected')) { 4109 cols.push(i); 4110 } 4111 } 4112 4113 return cols; 4114 } 4115 4116 /** 4117 * Get highlighted 4118 * 4119 * @return array 4120 */ 4121 obj.getHighlighted = function() { 4122 return obj.highlighted; 4123 } 4124 4125 /** 4126 * Update cell references 4127 * 4128 * @return void 4129 */ 4130 obj.updateTableReferences = function() { 4131 // Update headers 4132 for (var i = 0; i < obj.headers.length; i++) { 4133 var x = obj.headers[i].getAttribute('data-x'); 4134 4135 if (x != i) { 4136 // Update coords 4137 obj.headers[i].setAttribute('data-x', i); 4138 // Title 4139 if (! obj.headers[i].getAttribute('title')) { 4140 obj.headers[i].innerHTML = jexcel.getColumnName(i); 4141 } 4142 } 4143 } 4144 4145 // Update all rows 4146 for (var j = 0; j < obj.rows.length; j++) { 4147 if (obj.rows[j]) { 4148 var y = obj.rows[j].getAttribute('data-y'); 4149 4150 if (y != j) { 4151 // Update coords 4152 obj.rows[j].setAttribute('data-y', j); 4153 obj.rows[j].children[0].setAttribute('data-y', j); 4154 // Row number 4155 obj.rows[j].children[0].innerHTML = j + 1; 4156 } 4157 } 4158 } 4159 4160 // Regular cells affected by this change 4161 var affectedTokens = []; 4162 var mergeCellUpdates = []; 4163 4164 // Update cell 4165 var updatePosition = function(x,y,i,j) { 4166 if (x != i) { 4167 obj.records[j][i].setAttribute('data-x', i); 4168 } 4169 if (y != j) { 4170 obj.records[j][i].setAttribute('data-y', j); 4171 } 4172 4173 // Other updates 4174 if (x != i || y != j) { 4175 var columnIdFrom = jexcel.getColumnNameFromId([x, y]); 4176 var columnIdTo = jexcel.getColumnNameFromId([i, j]); 4177 affectedTokens[columnIdFrom] = columnIdTo; 4178 } 4179 } 4180 4181 for (var j = 0; j < obj.records.length; j++) { 4182 for (var i = 0; i < obj.records[0].length; i++) { 4183 if (obj.records[j][i]) { 4184 // Current values 4185 var x = obj.records[j][i].getAttribute('data-x'); 4186 var y = obj.records[j][i].getAttribute('data-y'); 4187 4188 // Update column 4189 if (obj.records[j][i].getAttribute('data-merged')) { 4190 var columnIdFrom = jexcel.getColumnNameFromId([x, y]); 4191 var columnIdTo = jexcel.getColumnNameFromId([i, j]); 4192 if (mergeCellUpdates[columnIdFrom] == null) { 4193 if (columnIdFrom == columnIdTo) { 4194 mergeCellUpdates[columnIdFrom] = false; 4195 } else { 4196 var totalX = parseInt(i - x); 4197 var totalY = parseInt(j - y); 4198 mergeCellUpdates[columnIdFrom] = [ columnIdTo, totalX, totalY ]; 4199 } 4200 } 4201 } else { 4202 updatePosition(x,y,i,j); 4203 } 4204 } 4205 } 4206 } 4207 4208 // Update merged if applicable 4209 var keys = Object.keys(mergeCellUpdates); 4210 if (keys.length) { 4211 for (var i = 0; i < keys.length; i++) { 4212 if (mergeCellUpdates[keys[i]]) { 4213 var info = jexcel.getIdFromColumnName(keys[i], true) 4214 var x = info[0]; 4215 var y = info[1]; 4216 updatePosition(x,y,x + mergeCellUpdates[keys[i]][1],y + mergeCellUpdates[keys[i]][2]); 4217 4218 var columnIdFrom = keys[i]; 4219 var columnIdTo = mergeCellUpdates[keys[i]][0]; 4220 for (var j = 0; j < obj.options.mergeCells[columnIdFrom][2].length; j++) { 4221 var x = parseInt(obj.options.mergeCells[columnIdFrom][2][j].getAttribute('data-x')); 4222 var y = parseInt(obj.options.mergeCells[columnIdFrom][2][j].getAttribute('data-y')); 4223 obj.options.mergeCells[columnIdFrom][2][j].setAttribute('data-x', x + mergeCellUpdates[keys[i]][1]); 4224 obj.options.mergeCells[columnIdFrom][2][j].setAttribute('data-y', y + mergeCellUpdates[keys[i]][2]); 4225 } 4226 4227 obj.options.mergeCells[columnIdTo] = obj.options.mergeCells[columnIdFrom]; 4228 delete(obj.options.mergeCells[columnIdFrom]); 4229 } 4230 } 4231 } 4232 4233 // Update formulas 4234 obj.updateFormulas(affectedTokens); 4235 4236 // Update meta data 4237 obj.updateMeta(affectedTokens); 4238 4239 // Refresh selection 4240 obj.refreshSelection(); 4241 4242 // Update table with custom configuration if applicable 4243 obj.updateTable(); 4244 } 4245 4246 /** 4247 * Custom settings for the cells 4248 */ 4249 obj.updateTable = function() { 4250 // Check for spare 4251 if (obj.options.minSpareRows > 0) { 4252 var numBlankRows = 0; 4253 for (var j = obj.rows.length - 1; j >= 0; j--) { 4254 var test = false; 4255 for (var i = 0; i < obj.headers.length; i++) { 4256 if (obj.options.data[j][i]) { 4257 test = true; 4258 } 4259 } 4260 if (test) { 4261 break; 4262 } else { 4263 numBlankRows++; 4264 } 4265 } 4266 4267 if (obj.options.minSpareRows - numBlankRows > 0) { 4268 obj.insertRow(obj.options.minSpareRows - numBlankRows) 4269 } 4270 } 4271 4272 if (obj.options.minSpareCols > 0) { 4273 var numBlankCols = 0; 4274 for (var i = obj.headers.length - 1; i >= 0 ; i--) { 4275 var test = false; 4276 for (var j = 0; j < obj.rows.length; j++) { 4277 if (obj.options.data[j][i]) { 4278 test = true; 4279 } 4280 } 4281 if (test) { 4282 break; 4283 } else { 4284 numBlankCols++; 4285 } 4286 } 4287 4288 if (obj.options.minSpareCols - numBlankCols > 0) { 4289 obj.insertColumn(obj.options.minSpareCols - numBlankCols) 4290 } 4291 } 4292 4293 // Customizations by the developer 4294 if (typeof(obj.options.updateTable) == 'function') { 4295 if (obj.options.detachForUpdates) { 4296 el.removeChild(obj.content); 4297 } 4298 4299 for (var j = 0; j < obj.rows.length; j++) { 4300 for (var i = 0; i < obj.headers.length; i++) { 4301 obj.options.updateTable(el, obj.records[j][i], i, j, obj.options.data[j][i], obj.records[j][i].innerText, jexcel.getColumnNameFromId([i, j])); 4302 } 4303 } 4304 4305 if (obj.options.detachForUpdates) { 4306 el.insertBefore(obj.content, obj.pagination); 4307 } 4308 } 4309 4310 // Update corner position 4311 setTimeout(function() { 4312 obj.updateCornerPosition(); 4313 },0); 4314 } 4315 4316 /** 4317 * Show row 4318 */ 4319 obj.showRow = function(rowNumber) { 4320 obj.rows[rowNumber].style.display = ''; 4321 } 4322 4323 /** 4324 * Hide row 4325 */ 4326 obj.hideRow = function(rowNumber) { 4327 obj.rows[rowNumber].style.display = 'none'; 4328 } 4329 4330 /** 4331 * Show column 4332 */ 4333 obj.showColumn = function(colNumber) { 4334 obj.headers[colNumber].style.display = ''; 4335 obj.colgroup[colNumber].style.display = ''; 4336 for (var j = 0; j < obj.options.data.length; j++) { 4337 obj.records[j][colNumber].style.display = ''; 4338 } 4339 } 4340 4341 /** 4342 * Hide column 4343 */ 4344 obj.hideColumn = function(colNumber) { 4345 obj.headers[colNumber].style.display = 'none'; 4346 obj.colgroup[colNumber].style.display = 'none'; 4347 for (var j = 0; j < obj.options.data.length; j++) { 4348 obj.records[j][colNumber].style.display = 'none'; 4349 } 4350 } 4351 4352 /** 4353 * Show index column 4354 */ 4355 obj.showIndex = function() { 4356 obj.table.classList.remove('jexcel_hidden_index'); 4357 } 4358 4359 /** 4360 * Hide index column 4361 */ 4362 obj.hideIndex = function() { 4363 obj.table.classList.add('jexcel_hidden_index'); 4364 } 4365 4366 /** 4367 * Update all related cells in the chain 4368 */ 4369 var chainLoopProtection = []; 4370 4371 obj.updateFormulaChain = function(x, y, records) { 4372 var cellId = jexcel.getColumnNameFromId([x, y]); 4373 if (obj.formula[cellId] && obj.formula[cellId].length > 0) { 4374 if (chainLoopProtection[cellId]) { 4375 obj.records[y][x].innerHTML = '#ERROR'; 4376 obj.formula[cellId] = ''; 4377 } else { 4378 // Protection 4379 chainLoopProtection[cellId] = true; 4380 4381 for (var i = 0; i < obj.formula[cellId].length; i++) { 4382 var cell = jexcel.getIdFromColumnName(obj.formula[cellId][i], true); 4383 // Update cell 4384 var value = ''+obj.options.data[cell[1]][cell[0]]; 4385 if (value.substr(0,1) == '=') { 4386 records.push(obj.updateCell(cell[0], cell[1], value, true)); 4387 } else { 4388 // No longer a formula, remove from the chain 4389 Object.keys(obj.formula)[i] = null; 4390 } 4391 obj.updateFormulaChain(cell[0], cell[1], records); 4392 } 4393 } 4394 } 4395 4396 chainLoopProtection = []; 4397 } 4398 4399 /** 4400 * Update formulas 4401 */ 4402 obj.updateFormulas = function(referencesToUpdate) { 4403 // Update formulas 4404 for (var j = 0; j < obj.options.data.length; j++) { 4405 for (var i = 0; i < obj.options.data[0].length; i++) { 4406 var value = '' + obj.options.data[j][i]; 4407 // Is formula 4408 if (value.substr(0,1) == '=') { 4409 // Replace tokens 4410 var newFormula = obj.updateFormula(value, referencesToUpdate); 4411 if (newFormula != value) { 4412 obj.options.data[j][i] = newFormula; 4413 } 4414 } 4415 } 4416 } 4417 4418 // Update formula chain 4419 var formula = []; 4420 var keys = Object.keys(obj.formula); 4421 for (var j = 0; j < keys.length; j++) { 4422 // Current key and values 4423 var key = keys[j]; 4424 var value = obj.formula[key]; 4425 // Update key 4426 if (referencesToUpdate[key]) { 4427 key = referencesToUpdate[key]; 4428 } 4429 // Update values 4430 formula[key] = []; 4431 for (var i = 0; i < value.length; i++) { 4432 var letter = value[i]; 4433 if (referencesToUpdate[letter]) { 4434 letter = referencesToUpdate[letter]; 4435 } 4436 formula[key].push(letter); 4437 } 4438 } 4439 obj.formula = formula; 4440 } 4441 4442 /** 4443 * Update formula 4444 */ 4445 obj.updateFormula = function(formula, referencesToUpdate) { 4446 var testLetter = /[A-Z]/; 4447 var testNumber = /[0-9]/; 4448 4449 var newFormula = ''; 4450 var letter = null; 4451 var number = null; 4452 var token = ''; 4453 4454 for (var index = 0; index < formula.length; index++) { 4455 if (testLetter.exec(formula[index])) { 4456 letter = 1; 4457 number = 0; 4458 token += formula[index]; 4459 } else if (testNumber.exec(formula[index])) { 4460 number = letter ? 1 : 0; 4461 token += formula[index]; 4462 } else { 4463 if (letter && number) { 4464 token = referencesToUpdate[token] ? referencesToUpdate[token] : token; 4465 } 4466 newFormula += token; 4467 newFormula += formula[index]; 4468 letter = 0; 4469 number = 0; 4470 token = ''; 4471 } 4472 } 4473 4474 if (token) { 4475 if (letter && number) { 4476 token = referencesToUpdate[token] ? referencesToUpdate[token] : token; 4477 } 4478 newFormula += token; 4479 } 4480 4481 return newFormula; 4482 } 4483 4484 /** 4485 * Parse formulas 4486 */ 4487 obj.executeFormula = function(expression, x, y) { 4488 4489 var formulaResults = []; 4490 var formulaLoopProtection = []; 4491 4492 // Execute formula with loop protection 4493 var execute = function(expression, x, y) { 4494 // Parent column identification 4495 var parentId = jexcel.getColumnNameFromId([x, y]); 4496 4497 // Code protection 4498 if (formulaLoopProtection[parentId]) { 4499 console.error('Reference loop detected'); 4500 return '#ERROR'; 4501 } 4502 4503 formulaLoopProtection[parentId] = true; 4504 4505 // Convert range tokens 4506 var tokensUpdate = function(tokens) { 4507 for (var index = 0; index < tokens.length; index++) { 4508 var f = []; 4509 var token = tokens[index].split(':'); 4510 var e1 = jexcel.getIdFromColumnName(token[0], true); 4511 var e2 = jexcel.getIdFromColumnName(token[1], true); 4512 4513 if (e1[0] <= e2[0]) { 4514 var x1 = e1[0]; 4515 var x2 = e2[0]; 4516 } else { 4517 var x1 = e2[0]; 4518 var x2 = e1[0]; 4519 } 4520 4521 if (e1[1] <= e2[1]) { 4522 var y1 = e1[1]; 4523 var y2 = e2[1]; 4524 } else { 4525 var y1 = e2[1]; 4526 var y2 = e1[1]; 4527 } 4528 4529 for (var j = y1; j <= y2; j++) { 4530 for (var i = x1; i <= x2; i++) { 4531 f.push(jexcel.getColumnNameFromId([i, j])); 4532 } 4533 } 4534 4535 expression = expression.replace(tokens[index], f.join(',')); 4536 } 4537 } 4538 4539 var tokens = expression.match(/([A-Z]+[0-9]+)\:([A-Z]+[0-9]+)/g); 4540 if (tokens && tokens.length) { 4541 tokensUpdate(tokens); 4542 } 4543 4544 // String 4545 var evalstring = ''; 4546 4547 // Get tokens 4548 var tokens = expression.match(/([A-Z]+[0-9]+)/g); 4549 4550 // Direct self-reference protection 4551 if (tokens && tokens.indexOf(parentId) > -1) { 4552 console.error('Self Reference detected'); 4553 return '#ERROR'; 4554 } else { 4555 if (tokens) { 4556 for (var i = 0; i < tokens.length; i++) { 4557 // Keep chain 4558 if (! obj.formula[tokens[i]]) { 4559 obj.formula[tokens[i]] = []; 4560 } 4561 // Is already in the register 4562 if (obj.formula[tokens[i]].indexOf(parentId) < 0) { 4563 obj.formula[tokens[i]].push(parentId); 4564 } 4565 4566 // Do not calculate again 4567 if (eval('typeof(' + tokens[i] + ') == "undefined"')) { 4568 // Coords 4569 var position = jexcel.getIdFromColumnName(tokens[i], 1); 4570 // Get value 4571 if (typeof(obj.options.data[position[1]]) != 'undefined' && typeof(obj.options.data[position[1]][position[0]]) != 'undefined') { 4572 var value = obj.options.data[position[1]][position[0]]; 4573 } else { 4574 var value = ''; 4575 } 4576 // Get column data 4577 if ((''+value).substr(0,1) == '=') { 4578 if (formulaResults[tokens[i]]) { 4579 value = formulaResults[tokens[i]]; 4580 } else { 4581 value = execute(value, position[0], position[1]); 4582 formulaResults[tokens[i]] = value; 4583 } 4584 } 4585 // Type! 4586 if ((''+value).trim() == '') { 4587 // Null 4588 evalstring += "var " + tokens[i] + " = null;"; 4589 } else { 4590 if (value == Number(value) && obj.options.autoCasting == true) { 4591 // Number 4592 evalstring += "var " + tokens[i] + " = " + Number(value) + ";"; 4593 } else { 4594 // Trying any formatted number 4595 var number = obj.parseNumber(value, position[0]) 4596 if (obj.options.autoCasting == true && number) { 4597 // Render as number 4598 evalstring += "var " + tokens[i] + " = " + number + ";"; 4599 } else { 4600 // Render as string 4601 evalstring += "var " + tokens[i] + " = '" + value + "';"; 4602 } 4603 } 4604 } 4605 } 4606 } 4607 } 4608 4609 // Convert formula to javascript 4610 try { 4611 evalstring += "function COLUMN() { return parseInt(x) + 1; }; function ROW() { return parseInt(y) + 1; }; function CELL() { return parentId; };"; 4612 4613 var res = eval(evalstring + expression.substr(1)); 4614 } catch (e) { 4615 var res = '#ERROR'; 4616 } 4617 4618 return res; 4619 } 4620 } 4621 4622 return execute(expression, x, y); 4623 } 4624 4625 /** 4626 * Trying to extract a number from a string 4627 */ 4628 obj.parseNumber = function(value, columnNumber) { 4629 // Decimal point 4630 var decimal = columnNumber && obj.options.columns[columnNumber].decimal ? obj.options.columns[columnNumber].decimal : '.'; 4631 4632 // Parse both parts of the number 4633 var number = ('' + value); 4634 number = number.split(decimal); 4635 number[0] = number[0].match(/[+-]?[0-9]/g); 4636 if (number[0]) { 4637 number[0] = number[0].join(''); 4638 } 4639 if (number[1]) { 4640 number[1] = number[1].match(/[0-9]*/g).join(''); 4641 } 4642 4643 // Is a valid number 4644 if (number[0] && Number(number[0]) >= 0) { 4645 if (! number[1]) { 4646 var value = Number(number[0] + '.00'); 4647 } else { 4648 var value = Number(number[0] + '.' + number[1]); 4649 } 4650 } else { 4651 var value = null; 4652 } 4653 4654 return value; 4655 } 4656 4657 /** 4658 * Get row number 4659 */ 4660 obj.row = function(cell) { 4661 } 4662 4663 /** 4664 * Get col number 4665 */ 4666 obj.col = function(cell) { 4667 } 4668 4669 obj.up = function(shiftKey, ctrlKey) { 4670 if (shiftKey) { 4671 if (obj.selectedCell[3] > 0) { 4672 obj.up.visible(1, ctrlKey ? 0 : 1) 4673 } 4674 } else { 4675 if (obj.selectedCell[1] > 0) { 4676 obj.up.visible(0, ctrlKey ? 0 : 1) 4677 } 4678 obj.selectedCell[2] = obj.selectedCell[0]; 4679 obj.selectedCell[3] = obj.selectedCell[1]; 4680 } 4681 4682 // Update selection 4683 obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); 4684 4685 // Change page 4686 if (obj.options.lazyLoading == true) { 4687 if (obj.selectedCell[1] == 0 || obj.selectedCell[3] == 0) { 4688 obj.loadPage(0); 4689 obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); 4690 } else { 4691 if (obj.loadValidation()) { 4692 obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); 4693 } else { 4694 var item = parseInt(obj.tbody.firstChild.getAttribute('data-y')); 4695 if (obj.selectedCell[1] - item < 30) { 4696 obj.loadUp(); 4697 obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); 4698 } 4699 } 4700 } 4701 } else if (obj.options.pagination > 0) { 4702 var pageNumber = obj.whichPage(obj.selectedCell[3]); 4703 if (pageNumber != obj.pageNumber) { 4704 obj.page(pageNumber); 4705 } 4706 } 4707 4708 obj.updateScroll(1); 4709 } 4710 4711 obj.up.visible = function(group, direction) { 4712 if (group == 0) { 4713 var x = parseInt(obj.selectedCell[0]); 4714 var y = parseInt(obj.selectedCell[1]); 4715 } else { 4716 var x = parseInt(obj.selectedCell[2]); 4717 var y = parseInt(obj.selectedCell[3]); 4718 } 4719 4720 if (direction == 0) { 4721 for (var j = 0; j < y; j++) { 4722 if (obj.records[j][x].style.display != 'none' && obj.rows[j].style.display != 'none') { 4723 y = j; 4724 break; 4725 } 4726 } 4727 } else { 4728 y = obj.up.get(x, y); 4729 } 4730 4731 if (group == 0) { 4732 obj.selectedCell[0] = x; 4733 obj.selectedCell[1] = y; 4734 } else { 4735 obj.selectedCell[2] = x; 4736 obj.selectedCell[3] = y; 4737 } 4738 } 4739 4740 obj.up.get = function(x, y) { 4741 var x = parseInt(x); 4742 var y = parseInt(y); 4743 for (var j = (y - 1); j >= 0; j--) { 4744 if (obj.records[j][x].style.display != 'none' && obj.rows[j].style.display != 'none') { 4745 if (obj.records[j][x].getAttribute('data-merged')) { 4746 if (obj.records[j][x] == obj.records[y][x]) { 4747 continue; 4748 } 4749 } 4750 y = j; 4751 break; 4752 } 4753 } 4754 4755 return y; 4756 } 4757 4758 obj.down = function(shiftKey, ctrlKey) { 4759 if (shiftKey) { 4760 if (obj.selectedCell[3] < obj.records.length - 1) { 4761 obj.down.visible(1, ctrlKey ? 0 : 1) 4762 } 4763 } else { 4764 if (obj.selectedCell[1] < obj.records.length - 1) { 4765 obj.down.visible(0, ctrlKey ? 0 : 1) 4766 } 4767 obj.selectedCell[2] = obj.selectedCell[0]; 4768 obj.selectedCell[3] = obj.selectedCell[1]; 4769 } 4770 4771 obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); 4772 4773 // Change page 4774 if (obj.options.lazyLoading == true) { 4775 if ((obj.selectedCell[1] == obj.records.length - 1 || obj.selectedCell[3] == obj.records.length - 1)) { 4776 obj.loadPage(-1); 4777 obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); 4778 } else { 4779 if (obj.loadValidation()) { 4780 obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); 4781 } else { 4782 var item = parseInt(obj.tbody.lastChild.getAttribute('data-y')); 4783 if (item - obj.selectedCell[3] < 30) { 4784 obj.loadDown(); 4785 obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); 4786 } 4787 } 4788 } 4789 } else if (obj.options.pagination > 0) { 4790 var pageNumber = obj.whichPage(obj.selectedCell[3]); 4791 if (pageNumber != obj.pageNumber) { 4792 obj.page(pageNumber); 4793 } 4794 } 4795 4796 obj.updateScroll(3); 4797 } 4798 4799 obj.down.visible = function(group, direction) { 4800 if (group == 0) { 4801 var x = parseInt(obj.selectedCell[0]); 4802 var y = parseInt(obj.selectedCell[1]); 4803 } else { 4804 var x = parseInt(obj.selectedCell[2]); 4805 var y = parseInt(obj.selectedCell[3]); 4806 } 4807 4808 if (direction == 0) { 4809 for (var j = obj.rows.length - 1; j > y; j--) { 4810 if (obj.records[j][x].style.display != 'none' && obj.rows[j].style.display != 'none') { 4811 y = j; 4812 break; 4813 } 4814 } 4815 } else { 4816 y = obj.down.get(x, y); 4817 } 4818 4819 if (group == 0) { 4820 obj.selectedCell[0] = x; 4821 obj.selectedCell[1] = y; 4822 } else { 4823 obj.selectedCell[2] = x; 4824 obj.selectedCell[3] = y; 4825 } 4826 } 4827 4828 obj.down.get = function(x, y) { 4829 var x = parseInt(x); 4830 var y = parseInt(y); 4831 for (var j = (y + 1); j < obj.rows.length; j++) { 4832 if (obj.records[j][x].style.display != 'none' && obj.rows[j].style.display != 'none') { 4833 if (obj.records[j][x].getAttribute('data-merged')) { 4834 if (obj.records[j][x] == obj.records[y][x]) { 4835 continue; 4836 } 4837 } 4838 y = j; 4839 break; 4840 } 4841 } 4842 4843 return y; 4844 } 4845 4846 obj.right = function(shiftKey, ctrlKey) { 4847 if (shiftKey) { 4848 if (obj.selectedCell[2] < obj.headers.length - 1) { 4849 obj.right.visible(1, ctrlKey ? 0 : 1) 4850 } 4851 } else { 4852 if (obj.selectedCell[0] < obj.headers.length - 1) { 4853 obj.right.visible(0, ctrlKey ? 0 : 1) 4854 } 4855 obj.selectedCell[2] = obj.selectedCell[0]; 4856 obj.selectedCell[3] = obj.selectedCell[1]; 4857 } 4858 4859 obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); 4860 obj.updateScroll(2); 4861 } 4862 4863 obj.right.visible = function(group, direction) { 4864 if (group == 0) { 4865 var x = parseInt(obj.selectedCell[0]); 4866 var y = parseInt(obj.selectedCell[1]); 4867 } else { 4868 var x = parseInt(obj.selectedCell[2]); 4869 var y = parseInt(obj.selectedCell[3]); 4870 } 4871 4872 if (direction == 0) { 4873 for (var i = obj.headers.length - 1; i > x; i--) { 4874 if (obj.records[y][i].style.display != 'none') { 4875 x = i; 4876 break; 4877 } 4878 } 4879 } else { 4880 x = obj.right.get(x, y); 4881 } 4882 4883 if (group == 0) { 4884 obj.selectedCell[0] = x; 4885 obj.selectedCell[1] = y; 4886 } else { 4887 obj.selectedCell[2] = x; 4888 obj.selectedCell[3] = y; 4889 } 4890 } 4891 4892 obj.right.get = function(x, y) { 4893 var x = parseInt(x); 4894 var y = parseInt(y); 4895 4896 for (var i = (x + 1); i < obj.headers.length; i++) { 4897 if (obj.records[y][i].style.display != 'none') { 4898 if (obj.records[y][i].getAttribute('data-merged')) { 4899 if (obj.records[y][i] == obj.records[y][x]) { 4900 continue; 4901 } 4902 } 4903 x = i; 4904 break; 4905 } 4906 } 4907 4908 return x; 4909 } 4910 4911 obj.left = function(shiftKey, ctrlKey) { 4912 if (shiftKey) { 4913 if (obj.selectedCell[2] > 0) { 4914 obj.left.visible(1, ctrlKey ? 0 : 1) 4915 } 4916 } else { 4917 if (obj.selectedCell[0] > 0) { 4918 obj.left.visible(0, ctrlKey ? 0 : 1) 4919 } 4920 obj.selectedCell[2] = obj.selectedCell[0]; 4921 obj.selectedCell[3] = obj.selectedCell[1]; 4922 } 4923 4924 obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); 4925 obj.updateScroll(0); 4926 } 4927 4928 obj.left.visible = function(group, direction) { 4929 if (group == 0) { 4930 var x = parseInt(obj.selectedCell[0]); 4931 var y = parseInt(obj.selectedCell[1]); 4932 } else { 4933 var x = parseInt(obj.selectedCell[2]); 4934 var y = parseInt(obj.selectedCell[3]); 4935 } 4936 4937 if (direction == 0) { 4938 for (var i = 0; i < x; i++) { 4939 if (obj.records[y][i].style.display != 'none') { 4940 x = i; 4941 break; 4942 } 4943 } 4944 } else { 4945 x = obj.left.get(x, y); 4946 } 4947 4948 if (group == 0) { 4949 obj.selectedCell[0] = x; 4950 obj.selectedCell[1] = y; 4951 } else { 4952 obj.selectedCell[2] = x; 4953 obj.selectedCell[3] = y; 4954 } 4955 } 4956 4957 obj.left.get = function(x, y) { 4958 var x = parseInt(x); 4959 var y = parseInt(y); 4960 for (var i = (x - 1); i >= 0; i--) { 4961 if (obj.records[y][i].style.display != 'none') { 4962 if (obj.records[y][i].getAttribute('data-merged')) { 4963 if (obj.records[y][i] == obj.records[y][x]) { 4964 continue; 4965 } 4966 } 4967 x = i; 4968 break; 4969 } 4970 } 4971 4972 return x; 4973 } 4974 4975 obj.first = function(shiftKey, ctrlKey) { 4976 if (shiftKey) { 4977 if (ctrlKey) { 4978 obj.selectedCell[3] = 0; 4979 } else { 4980 obj.left.visible(1, 0); 4981 } 4982 } else { 4983 if (ctrlKey) { 4984 obj.selectedCell[1] = 0; 4985 } else { 4986 obj.left.visible(0, 0); 4987 } 4988 obj.selectedCell[2] = obj.selectedCell[0]; 4989 obj.selectedCell[3] = obj.selectedCell[1]; 4990 } 4991 4992 // Change page 4993 if (obj.options.lazyLoading == true && (obj.selectedCell[1] == 0 || obj.selectedCell[3] == 0)) { 4994 obj.loadPage(0); 4995 } else if (obj.options.pagination > 0) { 4996 var pageNumber = obj.whichPage(obj.selectedCell[3]); 4997 if (pageNumber != obj.pageNumber) { 4998 obj.page(pageNumber); 4999 } 5000 } 5001 5002 obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); 5003 obj.updateScroll(1); 5004 } 5005 5006 obj.last = function(shiftKey, ctrlKey) { 5007 if (shiftKey) { 5008 if (ctrlKey) { 5009 obj.selectedCell[3] = obj.records.length - 1; 5010 } else { 5011 obj.right.visible(1, 0); 5012 } 5013 } else { 5014 if (ctrlKey) { 5015 obj.selectedCell[1] = obj.records.length - 1; 5016 } else { 5017 obj.right.visible(0, 0); 5018 } 5019 obj.selectedCell[2] = obj.selectedCell[0]; 5020 obj.selectedCell[3] = obj.selectedCell[1]; 5021 } 5022 5023 // Change page 5024 if (obj.options.lazyLoading == true && (obj.selectedCell[1] == obj.records.length - 1 || obj.selectedCell[3] == obj.records.length - 1)) { 5025 obj.loadPage(-1); 5026 } else if (obj.options.pagination > 0) { 5027 var pageNumber = obj.whichPage(obj.selectedCell[3]); 5028 if (pageNumber != obj.pageNumber) { 5029 obj.page(pageNumber); 5030 } 5031 } 5032 5033 obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); 5034 obj.updateScroll(3); 5035 } 5036 5037 obj.selectAll = function() { 5038 if (! obj.selectedCell) { 5039 obj.selectedCell = []; 5040 } 5041 5042 obj.selectedCell[0] = 0; 5043 obj.selectedCell[1] = 0; 5044 obj.selectedCell[2] = obj.headers.length - 1; 5045 obj.selectedCell[3] = obj.records.length - 1; 5046 5047 obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); 5048 } 5049 5050 /** 5051 * Go to a page in a lazyLoading 5052 */ 5053 obj.loadPage = function(pageNumber) { 5054 // Search 5055 if (obj.options.search == true && obj.results) { 5056 var results = obj.results; 5057 } else { 5058 var results = obj.rows; 5059 } 5060 5061 // Per page 5062 var quantityPerPage = 100; 5063 5064 // pageNumber 5065 if (pageNumber == null || pageNumber == -1) { 5066 // Last page 5067 pageNumber = Math.ceil(results.length / quantityPerPage); 5068 } 5069 5070 var startRow = (pageNumber * quantityPerPage); 5071 var finalRow = (pageNumber * quantityPerPage) + quantityPerPage; 5072 if (finalRow > results.length) { 5073 finalRow = results.length; 5074 } 5075 startRow = finalRow - 100; 5076 if (startRow < 0) { 5077 startRow = 0; 5078 } 5079 5080 // Appeding items 5081 for (var j = startRow; j < finalRow; j++) { 5082 if (obj.options.search == true && obj.results) { 5083 obj.tbody.appendChild(obj.rows[results[j]]); 5084 } else { 5085 obj.tbody.appendChild(obj.rows[j]); 5086 } 5087 5088 if (obj.tbody.children.length > quantityPerPage) { 5089 obj.tbody.removeChild(obj.tbody.firstChild); 5090 } 5091 } 5092 } 5093 5094 obj.loadUp = function() { 5095 // Search 5096 if (obj.options.search == true && obj.results) { 5097 var results = obj.results; 5098 } else { 5099 var results = obj.rows; 5100 } 5101 var test = 0; 5102 if (results.length > 100) { 5103 // Get the first element in the page 5104 var item = parseInt(obj.tbody.firstChild.getAttribute('data-y')); 5105 if (obj.options.search == true && obj.results) { 5106 item = results.indexOf(item); 5107 } 5108 if (item > 0) { 5109 for (var j = 0; j < 30; j++) { 5110 item = item - 1; 5111 if (item > -1) { 5112 if (obj.options.search == true && obj.results) { 5113 obj.tbody.insertBefore(obj.rows[results[item]], obj.tbody.firstChild); 5114 } else { 5115 obj.tbody.insertBefore(obj.rows[item], obj.tbody.firstChild); 5116 } 5117 if (obj.tbody.children.length > 100) { 5118 obj.tbody.removeChild(obj.tbody.lastChild); 5119 test = 1; 5120 } 5121 } 5122 } 5123 } 5124 } 5125 return test; 5126 } 5127 5128 obj.loadDown = function() { 5129 // Search 5130 if (obj.options.search == true && obj.results) { 5131 var results = obj.results; 5132 } else { 5133 var results = obj.rows; 5134 } 5135 var test = 0; 5136 if (results.length > 100) { 5137 // Get the last element in the page 5138 var item = parseInt(obj.tbody.lastChild.getAttribute('data-y')); 5139 if (obj.options.search == true && obj.results) { 5140 item = results.indexOf(item); 5141 } 5142 if (item < obj.rows.length - 1) { 5143 for (var j = 0; j <= 30; j++) { 5144 if (item < results.length) { 5145 if (obj.options.search == true && obj.results) { 5146 obj.tbody.appendChild(obj.rows[results[item]]); 5147 } else { 5148 obj.tbody.appendChild(obj.rows[item]); 5149 } 5150 if (obj.tbody.children.length > 100) { 5151 obj.tbody.removeChild(obj.tbody.firstChild); 5152 test = 1; 5153 } 5154 } 5155 item = item + 1; 5156 } 5157 } 5158 } 5159 5160 return test; 5161 } 5162 5163 obj.loadValidation = function() { 5164 if (obj.selectedCell) { 5165 var currentPage = parseInt(obj.tbody.firstChild.getAttribute('data-y')) / 100; 5166 var selectedPage = parseInt(obj.selectedCell[3] / 100); 5167 var totalPages = parseInt(obj.rows.length / 100); 5168 5169 if (currentPage != selectedPage && selectedPage <= totalPages) { 5170 if (! Array.prototype.indexOf.call(obj.tbody.children, obj.rows[obj.selectedCell[3]])) { 5171 obj.loadPage(selectedPage); 5172 return true; 5173 } 5174 } 5175 } 5176 5177 return false; 5178 } 5179 5180 /** 5181 * Reset search 5182 */ 5183 obj.resetSearch = function() { 5184 obj.searchInput.value = ''; 5185 obj.search(''); 5186 obj.results = null; 5187 } 5188 5189 /** 5190 * Search 5191 */ 5192 obj.search = function(query) { 5193 // Query 5194 if (query) { 5195 var query = query.toLowerCase(); 5196 } 5197 5198 // Reset selection 5199 obj.resetSelection(); 5200 5201 // Total of results 5202 obj.pageNumber = 0; 5203 obj.results = []; 5204 5205 if (query) { 5206 // Search filter 5207 var search = function(item, query, index) { 5208 for (var i = 0; i < item.length; i++) { 5209 if ((''+item[i]).toLowerCase().search(query) >= 0 || 5210 (''+obj.records[index][i].innerHTML).toLowerCase().search(query) >= 0) { 5211 return true; 5212 } 5213 } 5214 return false; 5215 } 5216 5217 // Result 5218 var addToResult = function(k) { 5219 if (obj.results.indexOf(k) == -1) { 5220 obj.results.push(k); 5221 } 5222 } 5223 5224 // Filter 5225 var data = obj.options.data.filter(function(v, k) { 5226 if (search(v, query, k)) { 5227 // Merged rows found 5228 var rows = obj.isRowMerged(k); 5229 if (rows.length) { 5230 for (var i = 0; i < rows.length; i++) { 5231 var row = jexcel.getIdFromColumnName(rows[i], true); 5232 for (var j = 0; j < obj.options.mergeCells[rows[i]][1]; j++) { 5233 addToResult(row[1]+j); 5234 } 5235 } 5236 } else { 5237 // Normal row found 5238 addToResult(k); 5239 } 5240 return true; 5241 } else { 5242 return false; 5243 } 5244 }); 5245 } else { 5246 obj.results = null; 5247 } 5248 5249 var total = 0; 5250 var index = 0; 5251 5252 // Page 1 5253 if (obj.options.lazyLoading == true) { 5254 total = 100; 5255 } else if (obj.options.pagination > 0) { 5256 total = obj.options.pagination; 5257 } else { 5258 if (obj.results) { 5259 total = obj.results.length; 5260 } else { 5261 total = obj.rows.length; 5262 } 5263 } 5264 5265 // Reset current nodes 5266 while (obj.tbody.firstChild) { 5267 obj.tbody.removeChild(obj.tbody.firstChild); 5268 } 5269 5270 // Hide all records from the table 5271 for (var j = 0; j < obj.rows.length; j++) { 5272 if (! obj.results || obj.results.indexOf(j) > -1) { 5273 if (index < total) { 5274 obj.tbody.appendChild(obj.rows[j]); 5275 index++; 5276 } 5277 obj.rows[j].style.display = ''; 5278 } else { 5279 obj.rows[j].style.display = 'none'; 5280 } 5281 } 5282 5283 // Update pagination 5284 if (obj.options.pagination > 0) { 5285 obj.updatePagination(); 5286 } 5287 5288 return total; 5289 } 5290 5291 /** 5292 * Which page the cell is 5293 */ 5294 obj.whichPage = function(cell) { 5295 // Search 5296 if (obj.options.search == true && obj.results) { 5297 cell = obj.results.indexOf(cell); 5298 } 5299 5300 return (Math.ceil((parseInt(cell) + 1) / parseInt(obj.options.pagination))) - 1; 5301 } 5302 5303 /** 5304 * Go to page 5305 */ 5306 obj.page = function(pageNumber) { 5307 var oldPage = obj.pageNumber; 5308 5309 // Search 5310 if (obj.options.search == true && obj.results) { 5311 var results = obj.results; 5312 } else { 5313 var results = obj.rows; 5314 } 5315 5316 // Per page 5317 var quantityPerPage = parseInt(obj.options.pagination); 5318 5319 // pageNumber 5320 if (pageNumber == null || pageNumber == -1) { 5321 // Last page 5322 pageNumber = Math.ceil(results.length / quantityPerPage); 5323 } 5324 5325 // Page number 5326 obj.pageNumber = pageNumber; 5327 5328 var startRow = (pageNumber * quantityPerPage); 5329 var finalRow = (pageNumber * quantityPerPage) + quantityPerPage; 5330 if (finalRow > results.length) { 5331 finalRow = results.length; 5332 } 5333 if (startRow < 0) { 5334 startRow = 0; 5335 } 5336 5337 // Reset container 5338 while (obj.tbody.firstChild) { 5339 obj.tbody.removeChild(obj.tbody.firstChild); 5340 } 5341 5342 // Appeding items 5343 for (var j = startRow; j < finalRow; j++) { 5344 if (obj.options.search == true && obj.results) { 5345 obj.tbody.appendChild(obj.rows[results[j]]); 5346 } else { 5347 obj.tbody.appendChild(obj.rows[j]); 5348 } 5349 } 5350 5351 if (obj.options.pagination > 0) { 5352 obj.updatePagination(); 5353 } 5354 5355 // Update corner position 5356 obj.updateCornerPosition(); 5357 5358 if (typeof(obj.options.onchangepage) == 'function') { 5359 obj.options.onchangepage(el, pageNumber, oldPage); 5360 } 5361 } 5362 5363 /** 5364 * Update the pagination 5365 */ 5366 obj.updatePagination = function() { 5367 // Reset container 5368 obj.pagination.children[0].innerHTML = ''; 5369 obj.pagination.children[1].innerHTML = ''; 5370 5371 // Start pagination 5372 if (obj.options.pagination) { 5373 // Searchable 5374 if (obj.options.search == true && obj.results) { 5375 var results = obj.results.length; 5376 } else { 5377 var results = obj.rows.length; 5378 } 5379 5380 if (! results) { 5381 // No records found 5382 obj.pagination.children[0].innerHTML = obj.options.text.noRecordsFound; 5383 } else { 5384 // Pagination container 5385 var quantyOfPages = Math.ceil(results / obj.options.pagination); 5386 5387 if (obj.pageNumber < 6) { 5388 var startNumber = 1; 5389 var finalNumber = quantyOfPages < 10 ? quantyOfPages : 10; 5390 } else if (quantyOfPages - obj.pageNumber < 5) { 5391 var startNumber = quantyOfPages - 9; 5392 var finalNumber = quantyOfPages; 5393 if (startNumber < 1) { 5394 startNumber = 1; 5395 } 5396 } else { 5397 var startNumber = obj.pageNumber - 4; 5398 var finalNumber = obj.pageNumber + 5; 5399 } 5400 5401 // First 5402 if (startNumber > 1) { 5403 var paginationItem = document.createElement('div'); 5404 paginationItem.className = 'jexcel_page'; 5405 paginationItem.innerHTML = '<'; 5406 paginationItem.title = 1; 5407 obj.pagination.children[1].appendChild(paginationItem); 5408 } 5409 5410 // Get page links 5411 for (var i = startNumber; i <= finalNumber; i++) { 5412 var paginationItem = document.createElement('div'); 5413 paginationItem.className = 'jexcel_page'; 5414 paginationItem.innerHTML = i; 5415 obj.pagination.children[1].appendChild(paginationItem); 5416 5417 if (obj.pageNumber == (i-1)) { 5418 paginationItem.classList.add('jexcel_page_selected'); 5419 } 5420 } 5421 5422 // Last 5423 if (finalNumber < quantyOfPages) { 5424 var paginationItem = document.createElement('div'); 5425 paginationItem.className = 'jexcel_page'; 5426 paginationItem.innerHTML = '>'; 5427 paginationItem.title = quantyOfPages; 5428 obj.pagination.children[1].appendChild(paginationItem); 5429 } 5430 5431 // Text 5432 var format = function(format) { 5433 var args = Array.prototype.slice.call(arguments, 1); 5434 return format.replace(/{(\d+)}/g, function(match, number) { 5435 return typeof args[number] != 'undefined' 5436 ? args[number] 5437 : match 5438 ; 5439 }); 5440 }; 5441 5442 obj.pagination.children[0].innerHTML = format(obj.options.text.showingPage, obj.pageNumber + 1, quantyOfPages) 5443 } 5444 } 5445 } 5446 5447 /** 5448 * Download CSV table 5449 * 5450 * @return null 5451 */ 5452 obj.download = function(includeHeaders) { 5453 if (obj.options.allowExport == false) { 5454 console.error('Export not allowed'); 5455 } else { 5456 // Data 5457 var data = ''; 5458 if (includeHeaders == true || obj.options.includeHeadersOnDownload == true) { 5459 data += obj.getHeaders(); 5460 data += "\r\n"; 5461 } 5462 5463 // Get data 5464 data += obj.copy(false, obj.options.csvDelimiter, true); 5465 5466 // Download element 5467 var blob = new Blob(["\uFEFF"+data], {type: 'text/csv;charset=utf-8;'}); 5468 5469 // IE Compatibility 5470 if (window.navigator && window.navigator.msSaveOrOpenBlob) { 5471 window.navigator.msSaveOrOpenBlob(blob, options.csvFileName + '.csv'); 5472 } else { 5473 // Download element 5474 var pom = document.createElement('a'); 5475 var url = URL.createObjectURL(blob); 5476 pom.href = url; 5477 pom.setAttribute('download', obj.options.csvFileName + '.csv'); 5478 document.body.appendChild(pom); 5479 pom.click(); 5480 pom.parentNode.removeChild(pom); 5481 } 5482 } 5483 } 5484 5485 /** 5486 * Initializes a new history record for undo/redo 5487 * 5488 * @return null 5489 */ 5490 obj.setHistory = function(changes) { 5491 if (obj.ignoreHistory != true) { 5492 // Increment and get the current history index 5493 var index = ++obj.historyIndex; 5494 5495 // Slice the array to discard undone changes 5496 obj.history = (obj.history = obj.history.slice(0, index + 1)); 5497 5498 // Keep history 5499 obj.history[index] = changes; 5500 } 5501 } 5502 5503 /** 5504 * Copy method 5505 * 5506 * @param bool highlighted - Get only highlighted cells 5507 * @param delimiter - \t default to keep compatibility with excel 5508 * @return string value 5509 */ 5510 obj.copy = function(highlighted, delimiter, returnData) { 5511 if (! delimiter) { 5512 delimiter = "\t"; 5513 } 5514 5515 // Controls 5516 var col = []; 5517 var colLabel = []; 5518 var row = []; 5519 var rowLabel = []; 5520 var x = obj.options.data[0].length 5521 var y = obj.options.data.length 5522 var tmp = ''; 5523 5524 // Reset container 5525 obj.style = []; 5526 5527 // Go through the columns to get the data 5528 for (var j = 0; j < y; j++) { 5529 col = []; 5530 colLabel = []; 5531 5532 for (var i = 0; i < x; i++) { 5533 // If cell is highlighted 5534 if (! highlighted || obj.records[j][i].classList.contains('highlight')) { 5535 // Values 5536 var value = obj.options.data[j][i]; 5537 if (value.match && (value.match(/,/g) || value.match(/\n/) || value.match(/\"/))) { 5538 value = value.replace(new RegExp('"', 'g'), '""'); 5539 value = '"' + value + '"'; 5540 } 5541 col.push(value); 5542 5543 // Labels 5544 if (obj.options.columns[i].type == 'checkbox' || obj.options.columns[i].type == 'radio') { 5545 var label = value; 5546 } else { 5547 var label = obj.records[j][i].innerHTML; 5548 if (label.match && (label.match(/,/g) || label.match(/\n/) || label.match(/\"/))) { 5549 // Scape double quotes 5550 label = label.replace(new RegExp('"', 'g'), '""'); 5551 label = '"' + label + '"'; 5552 } 5553 } 5554 colLabel.push(label); 5555 5556 // Get style 5557 tmp = obj.records[j][i].getAttribute('style'); 5558 obj.style.push(tmp ? tmp : ''); 5559 } 5560 } 5561 5562 if (col.length) { 5563 row.push(col.join(delimiter)); 5564 } 5565 if (colLabel.length) { 5566 rowLabel.push(colLabel.join(delimiter)); 5567 } 5568 } 5569 5570 // Final string 5571 var str = row.join("\r\n"); 5572 var strLabel = rowLabel.join("\r\n"); 5573 5574 // Create a hidden textarea to copy the values 5575 if (! returnData) { 5576 if (obj.options.copyCompatibility == true) { 5577 obj.textarea.value = strLabel; 5578 } else { 5579 obj.textarea.value = str; 5580 } 5581 obj.textarea.select(); 5582 document.execCommand("copy"); 5583 } 5584 5585 // Keep data 5586 if (obj.options.copyCompatibility == true) { 5587 obj.data = strLabel; 5588 } else { 5589 obj.data = str; 5590 } 5591 // Keep non visible information 5592 obj.hashString = obj.hash(obj.data); 5593 5594 return obj.data; 5595 } 5596 5597 /** 5598 * jExcel paste method 5599 * 5600 * @param integer row number 5601 * @return string value 5602 */ 5603 obj.paste = function(x, y, data) { 5604 // Paste filter 5605 if (typeof(obj.options.onbeforepaste) == 'function') { 5606 var data = obj.options.onbeforepaste(el, data); 5607 } 5608 5609 // Controls 5610 var hash = obj.hash(data); 5611 var style = (hash == obj.hashString) ? obj.style : null; 5612 5613 // Depending on the behavior 5614 if (obj.options.copyCompatibility == true && hash == obj.hashString) { 5615 var data = obj.data; 5616 } 5617 5618 // Split new line 5619 var data = obj.parseCSV(data, "\t"); 5620 5621 if (x != null && y != null && data) { 5622 // Records 5623 var i = 0; 5624 var j = 0; 5625 var records = []; 5626 var newStyle = {}; 5627 var oldStyle = {}; 5628 var styleIndex = 0; 5629 5630 // Index 5631 var colIndex = parseInt(x); 5632 var rowIndex = parseInt(y); 5633 var row = null; 5634 5635 // Go through the columns to get the data 5636 while (row = data[j]) { 5637 i = 0; 5638 colIndex = parseInt(x); 5639 5640 while (row[i] != null) { 5641 // Update and keep history 5642 var record = obj.updateCell(colIndex, rowIndex, row[i]); 5643 // Keep history 5644 records.push(record); 5645 // Update all formulas in the chain 5646 obj.updateFormulaChain(colIndex, rowIndex, records); 5647 // Style 5648 if (style && style[styleIndex]) { 5649 var columnName = jexcel.getColumnNameFromId([colIndex, rowIndex]); 5650 newStyle[columnName] = style[styleIndex]; 5651 oldStyle[columnName] = obj.getStyle(columnName); 5652 obj.records[rowIndex][colIndex].setAttribute('style', style[styleIndex]); 5653 styleIndex++ 5654 } 5655 i++; 5656 if (row[i] != null) { 5657 if (colIndex >= obj.headers.length - 1) { 5658 obj.insertColumn(); 5659 } 5660 colIndex = obj.right.get(colIndex, rowIndex); 5661 } 5662 } 5663 5664 j++; 5665 if (data[j]) { 5666 if (rowIndex >= obj.rows.length-1) { 5667 obj.insertRow(); 5668 } 5669 rowIndex = obj.down.get(x, rowIndex); 5670 } 5671 } 5672 5673 // Select the new cells 5674 obj.updateSelectionFromCoords(x, y, colIndex, rowIndex); 5675 5676 // Update history 5677 obj.setHistory({ 5678 action:'setValue', 5679 records:records, 5680 selection:obj.selectedCell, 5681 newStyle:newStyle, 5682 oldStyle:oldStyle, 5683 }); 5684 5685 // Update table 5686 obj.updateTable(); 5687 5688 // Paste event 5689 if (typeof(obj.options.onpaste) == 'function') { 5690 obj.options.onpaste(el, records); 5691 } 5692 5693 // On after changes 5694 obj.onafterchanges(el, records); 5695 } 5696 } 5697 5698 /** 5699 * Process row 5700 */ 5701 obj.historyProcessRow = function(type, historyRecord) { 5702 var rowIndex = (! historyRecord.insertBefore) ? historyRecord.rowNumber + 1 : historyRecord.rowNumber; 5703 5704 if (obj.options.search == true) { 5705 if (obj.results && obj.results.length != obj.rows.length) { 5706 obj.resetSearch(); 5707 } 5708 } 5709 5710 // Remove row 5711 if (type == 1) { 5712 var numOfRows = historyRecord.numOfRows; 5713 // Remove nodes 5714 for (var j = rowIndex; j < (numOfRows + rowIndex); j++) { 5715 obj.rows[j].parentNode.removeChild(obj.rows[j]); 5716 } 5717 // Remove references 5718 obj.records.splice(rowIndex, numOfRows); 5719 obj.options.data.splice(rowIndex, numOfRows); 5720 obj.rows.splice(rowIndex, numOfRows); 5721 5722 obj.conditionalSelectionUpdate(1, rowIndex, (numOfRows + rowIndex) - 1); 5723 } else { 5724 // Insert data 5725 obj.records = jexcel.injectArray(obj.records, rowIndex, historyRecord.rowRecords); 5726 obj.options.data = jexcel.injectArray(obj.options.data, rowIndex, historyRecord.rowData); 5727 obj.rows = jexcel.injectArray(obj.rows, rowIndex, historyRecord.rowNode); 5728 // Insert nodes 5729 var index = 0 5730 for (var j = rowIndex; j < (historyRecord.numOfRows + rowIndex); j++) { 5731 obj.tbody.insertBefore(historyRecord.rowNode[index], obj.tbody.children[j]); 5732 index++; 5733 } 5734 } 5735 5736 // Respect pagination 5737 if (obj.options.pagination > 0) { 5738 obj.page(obj.pageNumber); 5739 } 5740 5741 obj.updateTableReferences(); 5742 } 5743 5744 /** 5745 * Process column 5746 */ 5747 obj.historyProcessColumn = function(type, historyRecord) { 5748 var columnIndex = (! historyRecord.insertBefore) ? historyRecord.columnNumber + 1 : historyRecord.columnNumber; 5749 5750 // Remove column 5751 if (type == 1) { 5752 var numOfColumns = historyRecord.numOfColumns; 5753 5754 obj.options.columns.splice(columnIndex, numOfColumns); 5755 for (var i = columnIndex; i < (numOfColumns + columnIndex); i++) { 5756 obj.headers[i].parentNode.removeChild(obj.headers[i]); 5757 obj.colgroup[i].parentNode.removeChild(obj.colgroup[i]); 5758 } 5759 obj.headers.splice(columnIndex, numOfColumns); 5760 obj.colgroup.splice(columnIndex, numOfColumns); 5761 for (var j = 0; j < historyRecord.data.length; j++) { 5762 for (var i = columnIndex; i < (numOfColumns + columnIndex); i++) { 5763 obj.records[j][i].parentNode.removeChild(obj.records[j][i]); 5764 } 5765 obj.records[j].splice(columnIndex, numOfColumns); 5766 obj.options.data[j].splice(columnIndex, numOfColumns); 5767 } 5768 5769 obj.conditionalSelectionUpdate(0, columnIndex, (numOfColumns + columnIndex) - 1); 5770 } else { 5771 // Insert data 5772 obj.options.columns = jexcel.injectArray(obj.options.columns, columnIndex, historyRecord.columns); 5773 obj.headers = jexcel.injectArray(obj.headers, columnIndex, historyRecord.headers); 5774 obj.colgroup = jexcel.injectArray(obj.colgroup, columnIndex, historyRecord.colgroup); 5775 5776 var index = 0 5777 for (var i = columnIndex; i < (historyRecord.numOfColumns + columnIndex); i++) { 5778 obj.headerContainer.insertBefore(historyRecord.headers[index], obj.headerContainer.children[i+1]); 5779 obj.colgroupContainer.insertBefore(historyRecord.colgroup[index], obj.colgroupContainer.children[i+1]); 5780 index++; 5781 } 5782 5783 for (var j = 0; j < historyRecord.data.length; j++) { 5784 obj.options.data[j] = jexcel.injectArray(obj.options.data[j], columnIndex, historyRecord.data[j]); 5785 obj.records[j] = jexcel.injectArray(obj.records[j], columnIndex, historyRecord.records[j]); 5786 var index = 0 5787 for (var i = columnIndex; i < (historyRecord.numOfColumns + columnIndex); i++) { 5788 obj.rows[j].insertBefore(historyRecord.records[j][index], obj.rows[j].children[i+1]); 5789 index++; 5790 } 5791 } 5792 } 5793 5794 // Adjust nested headers 5795 if (obj.options.nestedHeaders && obj.options.nestedHeaders.length > 0) { 5796 // Flexible way to handle nestedheaders 5797 if (obj.options.nestedHeaders[0] && obj.options.nestedHeaders[0][0]) { 5798 for (var j = 0; j < obj.options.nestedHeaders.length; j++) { 5799 if (type == 1) { 5800 var colspan = parseInt(obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan) - historyRecord.numOfColumns; 5801 } else { 5802 var colspan = parseInt(obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan) + historyRecord.numOfColumns; 5803 } 5804 obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan = colspan; 5805 obj.thead.children[j].children[obj.thead.children[j].children.length-1].setAttribute('colspan', colspan); 5806 } 5807 } else { 5808 if (type == 1) { 5809 var colspan = parseInt(obj.options.nestedHeaders[0].colspan) - historyRecord.numOfColumns; 5810 } else { 5811 var colspan = parseInt(obj.options.nestedHeaders[0].colspan) + historyRecord.numOfColumns; 5812 } 5813 obj.options.nestedHeaders[0].colspan = colspan; 5814 obj.thead.children[0].children[obj.thead.children[0].children.length-1].setAttribute('colspan', colspan); 5815 } 5816 } 5817 5818 obj.updateTableReferences(); 5819 } 5820 5821 /** 5822 * Undo last action 5823 */
5824 obj.undo = function() { 5825 // Ignore events and history 5826 var ignoreEvents = obj.ignoreEvents ? true : false; 5827 var ignoreHistory = obj.ignoreHistory ? true : false; 5828 5829 obj.ignoreEvents = true; 5830 obj.ignoreHistory = true; 5831 5832 // Records 5833 var records = []; 5834 5835 // Update cells 5836 if (obj.historyIndex >= 0) { 5837 // History 5838 var historyRecord = obj.history[obj.historyIndex--]; 5839 5840 if (historyRecord.action == 'insertRow') { 5841 obj.historyProcessRow(1, historyRecord); 5842 } else if (historyRecord.action == 'deleteRow') { 5843 obj.historyProcessRow(0, historyRecord); 5844 } else if (historyRecord.action == 'insertColumn') { 5845 obj.historyProcessColumn(1, historyRecord); 5846 } else if (historyRecord.action == 'deleteColumn') { 5847 obj.historyProcessColumn(0, historyRecord); 5848 } else if (historyRecord.action == 'moveRow') { 5849 obj.moveRow(historyRecord.newValue, historyRecord.oldValue); 5850 } else if (historyRecord.action == 'moveColumn') { 5851 obj.moveColumn(historyRecord.newValue, historyRecord.oldValue); 5852 } else if (historyRecord.action == 'setMerge') { 5853 obj.removeMerge(historyRecord.column, historyRecord.data); 5854 } else if (historyRecord.action == 'setStyle') { 5855 obj.setStyle(historyRecord.oldValue, null, null, 1); 5856 } else if (historyRecord.action == 'setWidth') { 5857 obj.setWidth(historyRecord.column, historyRecord.oldValue); 5858 } else if (historyRecord.action == 'setHeight') { 5859 obj.setHeight(historyRecord.row, historyRecord.oldValue); 5860 } else if (historyRecord.action == 'setHeader') { 5861 obj.setHeader(historyRecord.column, historyRecord.oldValue); 5862 } else if (historyRecord.action == 'setComments') { 5863 obj.setComments(historyRecord.column, historyRecord.oldValue[0], historyRecord.oldValue[1]); 5864 } else if (historyRecord.action == 'orderBy') { 5865 var rows = []; 5866 for (var j = 0; j < historyRecord.rows.length; j++) { 5867 rows[historyRecord.rows[j]] = j; 5868 } 5869 obj.updateOrderArrow(historyRecord.column, historyRecord.order ? 0 : 1); 5870 obj.updateOrder(rows); 5871 } else if (historyRecord.action == 'setValue') { 5872 // Redo for changes in cells 5873 for (var i = 0; i < historyRecord.records.length; i++) { 5874 obj.updateCell(historyRecord.records[i].col, historyRecord.records[i].row, historyRecord.records[i].oldValue); 5875 obj.updateFormulaChain(historyRecord.records[i].col, historyRecord.records[i].row, records); 5876 if (historyRecord.oldStyle) { 5877 obj.resetStyle(historyRecord.oldStyle, true); 5878 } 5879 } 5880 // Update selection 5881 if (! historyRecord.selection) { 5882 historyRecord.selection = [historyRecord.records[0].col, historyRecord.records[0].row]; 5883 } 5884 obj.updateSelectionFromCoords(historyRecord.selection[0], historyRecord.selection[1], historyRecord.selection[2], historyRecord.selection[3]); 5885 // Update table 5886 obj.updateTable(); 5887 } 5888 } 5889 obj.ignoreEvents = ignoreEvents; 5890 obj.ignoreHistory = ignoreHistory; 5891 5892 if (typeof(obj.options.onundo) == 'function') { 5893 obj.options.onundo(el, historyRecord); 5894 } 5895 } 5896 5897 /** 5898 * Redo previously undone action 5899 */ 5900 obj.redo = function() { 5901 // Ignore events and history 5902 var ignoreEvents = obj.ignoreEvents ? true : false;
5903 var ignoreHistory = obj.ignoreHistory ? true : false; 5904 5905 obj.ignoreEvents = true; 5906 obj.ignoreHistory = true; 5907 5908 // Records 5909 var records = []; 5910 5911 // Update cells 5912 if (obj.historyIndex < obj.history.length - 1) { 5913 // History 5914 var historyRecord = obj.history[++obj.historyIndex]; 5915 5916 if (historyRecord.action == 'insertRow') { 5917 obj.historyProcessRow(0, historyRecord); 5918 } else if (historyRecord.action == 'deleteRow') { 5919 obj.historyProcessRow(1, historyRecord); 5920 } else if (historyRecord.action == 'insertColumn') { 5921 obj.historyProcessColumn(0, historyRecord); 5922 } else if (historyRecord.action == 'deleteColumn') { 5923 obj.historyProcessColumn(1, historyRecord); 5924 } else if (historyRecord.action == 'moveRow') { 5925 obj.moveRow(historyRecord.oldValue, historyRecord.newValue); 5926 } else if (historyRecord.action == 'moveColumn') { 5927 obj.moveColumn(historyRecord.oldValue, historyRecord.newValue); 5928 } else if (historyRecord.action == 'setMerge') { 5929 obj.setMerge(historyRecord.column, historyRecord.colspan, historyRecord.rowspan, 1); 5930 } else if (historyRecord.action == 'setStyle') { 5931 obj.setStyle(historyRecord.newValue, null, null, 1); 5932 } else if (historyRecord.action == 'setWidth') { 5933 obj.setWidth(historyRecord.column, historyRecord.newValue); 5934 } else if (historyRecord.action == 'setHeight') { 5935 obj.setHeight(historyRecord.row, historyRecord.newValue); 5936 } else if (historyRecord.action == 'setHeader') { 5937 obj.setHeader(historyRecord.column, historyRecord.newValue); 5938 } else if (historyRecord.action == 'setComments') { 5939 obj.setComments(historyRecord.column, historyRecord.newValue[0], historyRecord.newValue[1]); 5940 } else if (historyRecord.action == 'orderBy') { 5941 obj.updateOrderArrow(historyRecord.column, historyRecord.order); 5942 obj.updateOrder(historyRecord.rows); 5943 } else if (historyRecord.action == 'setValue') { 5944 // Redo for changes in cells 5945 for (var i = 0; i < historyRecord.records.length; i++) { 5946 obj.updateCell(historyRecord.records[i].col, historyRecord.records[i].row, historyRecord.records[i].newValue); 5947 obj.updateFormulaChain(historyRecord.records[i].col, historyRecord.records[i].row, records); 5948 if (historyRecord.newStyle) { 5949 obj.resetStyle(historyRecord.newStyle, true); 5950 } 5951 } 5952 5953 // Update selection 5954 if (! historyRecord.selection) { 5955 historyRecord.selection = [historyRecord.records[0].col, historyRecord.records[0].row]; 5956 } 5957 obj.updateSelectionFromCoords(historyRecord.selection[0], historyRecord.selection[1], historyRecord.selection[2], historyRecord.selection[3]); 5958 // Update table 5959 obj.updateTable(); 5960 } 5961 } 5962 obj.ignoreEvents = ignoreEvents; 5963 obj.ignoreHistory = ignoreHistory; 5964 5965 if (typeof(obj.options.onredo) == 'function') { 5966 obj.options.onredo(el, historyRecord); 5967 } 5968 } 5969 5970 /** 5971 * Get dropdown value from key 5972 */ 5973 obj.getDropDownValue = function(column, key) { 5974 var value = []; 5975 5976 if (obj.options.columns[column] && obj.options.columns[column].source) { 5977 // Create array from source 5978 var combo = []; 5979 var source = obj.options.columns[column].source; 5980 5981 for (var i = 0; i < source.length; i++) { 5982 if (typeof(source[i]) == 'object') { 5983 combo[source[i].id] = source[i].name; 5984 } else { 5985 combo[source[i]] = source[i]; 5986 } 5987 } 5988 5989 // Garante single multiple compatibily 5990 var keys = ('' + key).split(';') 5991 5992 for (var i = 0; i < keys.length; i++) { 5993 if (combo[keys[i]]) { 5994 value.push(combo[keys[i]]); 5995 } 5996 } 5997 } else { 5998 console.error('Invalid column'); 5999 } 6000 6001 return (value.length > 0) ? value.join('; ') : ''; 6002 } 6003 6004 /** 6005 * From starckoverflow contributions 6006 */ 6007 obj.parseCSV = function(str, delimiter) { 6008 // Remove last line break 6009 str = str.replace(/\r?\n$|\r$|\n$/g, "");
vendor: 61,180 bytes, lines 6010-7314
6010 // Last caracter is the delimiter 6011 if (str.charCodeAt(str.length-1) == 9) { 6012 str += "\0"; 6013 } 6014 // user-supplied delimeter or default comma 6015 delimiter = (delimiter || ","); 6016 6017 var arr = []; 6018 var quote = false; // true means we're inside a quoted field 6019 // iterate over each character, keep track of current row and column (of the returned array) 6020 for (var row = 0, col = 0, c = 0; c < str.length; c++) { 6021 var cc = str[c], nc = str[c+1]; 6022 arr[row] = arr[row] || []; 6023 arr[row][col] = arr[row][col] || ''; 6024 6025 // 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 6026 if (cc == '"' && quote && nc == '"') { arr[row][col] += cc; ++c; continue; } 6027 6028 // If it's just one quotation mark, begin/end quoted field 6029 if (cc == '"') { quote = !quote; continue; } 6030 6031 // If it's a comma and we're not in a quoted field, move on to the next column 6032 if (cc == delimiter && !quote) { ++col; continue; } 6033 6034 // 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 6035 if (cc == '\r' && nc == '\n' && !quote) { ++row; col = 0; ++c; continue; } 6036 6037 // 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 6038 if (cc == '\n' && !quote) { ++row; col = 0; continue; } 6039 if (cc == '\r' && !quote) { ++row; col = 0; continue; } 6040 6041 // Otherwise, append the current character to the current column 6042 arr[row][col] += cc; 6043 } 6044 return arr; 6045 } 6046 6047 obj.hash = function(str) { 6048 var hash = 0, i, chr; 6049 6050 if (str.length === 0) { 6051 return hash; 6052 } else { 6053 for (i = 0; i < str.length; i++) { 6054 chr = str.charCodeAt(i); 6055 hash = ((hash << 5) - hash) + chr; 6056 hash |= 0; 6057 } 6058 } 6059 return hash; 6060 } 6061 6062 obj.onafterchanges = function(el, records) { 6063 if (! obj.ignoreEvents) { 6064 // On after changes 6065 if (typeof(obj.options.onafterchanges) == 'function') { 6066 obj.options.onafterchanges(el, records); 6067 } 6068 } 6069 } 6070 6071 obj.destroy = function() { 6072 jexcel.destroy(el); 6073 } 6074 6075 /** 6076 * Initialization method 6077 */ 6078 obj.init = function() { 6079 jexcel.current = obj; 6080 6081 // Build handlers 6082 if (typeof(jexcel.build) == 'function') { 6083 jexcel.build(); 6084 jexcel.build = null; 6085 } 6086 6087 // Load the table data based on an CSV file 6088 if (obj.options.csv) { 6089 // Loading 6090 if (obj.options.loadingSpin == true) { 6091 jSuites.loading.show(); 6092 } 6093 6094 // Load CSV file 6095 jSuites.ajax({ 6096 url: obj.options.csv, 6097 method: 'GET', 6098 dataType: 'text', 6099 success: function(result) { 6100 // Convert data 6101 var newData = obj.parseCSV(result, obj.options.csvDelimiter) 6102 6103 // Headers 6104 if (obj.options.csvHeaders == true && newData.length > 0) { 6105 var headers = newData.shift(); 6106 for(var i = 0; i < headers.length; i++) { 6107 if (! obj.options.columns[i]) { 6108 obj.options.columns[i] = { type:'text', align:obj.options.defaultColAlign, width:obj.options.defaultColWidth }; 6109 } 6110 // Precedence over pre-configurated titles 6111 if (typeof obj.options.columns[i].title === 'undefined') { 6112 obj.options.columns[i].title = headers[i]; 6113 } 6114 } 6115 } 6116 // Data 6117 obj.options.data = newData; 6118 // Prepare table 6119 obj.prepareTable(); 6120 // Hide spin 6121 if (obj.options.loadingSpin == true) { 6122 jSuites.loading.hide(); 6123 } 6124 } 6125 }); 6126 } else if (obj.options.url) { 6127 // Loading 6128 if (obj.options.loadingSpin == true) { 6129 jSuites.loading.show(); 6130 } 6131 6132 jSuites.ajax({ 6133 url: obj.options.url, 6134 method: 'GET', 6135 dataType: 'json', 6136 success: function(result) { 6137 // Data 6138 obj.options.data = (result.data) ? result.data : result; 6139 // Prepare table 6140 obj.prepareTable(); 6141 // Hide spin 6142 if (obj.options.loadingSpin == true) { 6143 jSuites.loading.hide(); 6144 } 6145 } 6146 }); 6147 } else { 6148 // Prepare table 6149 obj.prepareTable(); 6150 } 6151 } 6152 6153 // Context menu 6154 if (options && options.contextMenu != null) { 6155 obj.options.contextMenu = options.contextMenu; 6156 } else { 6157 obj.options.contextMenu = function(el, x, y, e) { 6158 var items = []; 6159 6160 if (y == null) { 6161 // Insert a new column 6162 if (obj.options.allowInsertColumn == true) { 6163 items.push({ 6164 title:obj.options.text.insertANewColumnBefore, 6165 onclick:function() { 6166 obj.insertColumn(1, parseInt(x), 1); 6167 } 6168 }); 6169 } 6170 6171 if (obj.options.allowInsertColumn == true) { 6172 items.push({ 6173 title:obj.options.text.insertANewColumnAfter, 6174 onclick:function() { 6175 obj.insertColumn(1, parseInt(x), 0); 6176 } 6177 }); 6178 } 6179 6180 // Delete a column 6181 if (obj.options.allowDeleteColumn == true) { 6182 items.push({ 6183 title:obj.options.text.deleteSelectedColumns, 6184 onclick:function() { 6185 obj.deleteColumn(obj.getSelectedColumns().length ? undefined : parseInt(x)); 6186 } 6187 }); 6188 } 6189 6190 // Rename column 6191 if (obj.options.allowRenameColumn == true) { 6192 items.push({ 6193 title:obj.options.text.renameThisColumn, 6194 onclick:function() { 6195 obj.setHeader(x); 6196 } 6197 }); 6198 } 6199 6200 // Sorting 6201 if (obj.options.columnSorting == true) { 6202 // Line 6203 items.push({ type:'line' }); 6204 6205 items.push({ 6206 title:obj.options.text.orderAscending, 6207 onclick:function() { 6208 obj.orderBy(x, 0); 6209 } 6210 }); 6211 items.push({ 6212 title:obj.options.text.orderDescending, 6213 onclick:function() { 6214 obj.orderBy(x, 1); 6215 } 6216 }); 6217 } 6218 } else { 6219 // Insert new row 6220 if (obj.options.allowInsertRow == true) { 6221 items.push({ 6222 title:obj.options.text.insertANewRowBefore, 6223 onclick:function() { 6224 obj.insertRow(1, parseInt(y), 1); 6225 } 6226 }); 6227 6228 items.push({ 6229 title:obj.options.text.insertANewRowAfter, 6230 onclick:function() { 6231 obj.insertRow(1, parseInt(y)); 6232 } 6233 }); 6234 } 6235 6236 if (obj.options.allowDeleteRow == true) { 6237 items.push({ 6238 title:obj.options.text.deleteSelectedRows, 6239 onclick:function() { 6240 obj.deleteRow(obj.getSelectedRows().length ? undefined : parseInt(y)); 6241 } 6242 }); 6243 } 6244 6245 if (x) { 6246 if (obj.options.allowComments == true) { 6247 items.push({ type:'line' }); 6248 6249 var title = obj.records[y][x].getAttribute('title') || ''; 6250 6251 items.push({ 6252 title: title ? obj.options.text.editComments : obj.options.text.addComments, 6253 onclick:function() { 6254 var comment = prompt(obj.options.text.comments, title); 6255 if (comment) { 6256 obj.setComments([ x, y ], comment); 6257 } 6258 } 6259 }); 6260 6261 if (title) { 6262 items.push({ 6263 title:obj.options.text.clearComments, 6264 onclick:function() { 6265 obj.setComments([ x, y ], ''); 6266 } 6267 }); 6268 } 6269 } 6270 } 6271 } 6272 6273 // Line 6274 items.push({ type:'line' }); 6275 6276 // Copy 6277 items.push({ 6278 title:obj.options.text.copy, 6279 shortcut:'Ctrl + C', 6280 onclick:function() { 6281 obj.copy(true); 6282 } 6283 }); 6284 6285 // Paste 6286 if (navigator && navigator.clipboard) { 6287 items.push({ 6288 title:obj.options.text.paste, 6289 shortcut:'Ctrl + V', 6290 onclick:function() { 6291 if (obj.selectedCell) { 6292 navigator.clipboard.readText().then(function(text) { 6293 if (text) { 6294 jexcel.current.paste(obj.selectedCell[0], obj.selectedCell[1], text); 6295 } 6296 }); 6297 } 6298 } 6299 }); 6300 } 6301 6302 // Save 6303 if (obj.options.allowExport) { 6304 items.push({ 6305 title: obj.options.text.saveAs, 6306 shortcut: 'Ctrl + S', 6307 onclick: function () { 6308 obj.download(); 6309 } 6310 }); 6311 } 6312 6313 // About 6314 if (obj.options.about) { 6315 items.push({ 6316 title:obj.options.text.about, 6317 onclick:function() { 6318 alert(obj.options.about); 6319 } 6320 }); 6321 } 6322 6323 return items; 6324 } 6325 } 6326 6327 obj.scrollControls = function(e) { 6328 if (obj.options.lazyLoading == true) { 6329 if (jexcel.timeControlLoading == null) { 6330 jexcel.timeControlLoading = setTimeout(function() { 6331 if (obj.content.scrollTop + obj.content.clientHeight >= obj.content.scrollHeight) { 6332 if (obj.loadDown()) { 6333 if (obj.content.scrollTop + obj.content.clientHeight > obj.content.scrollHeight - 10) { 6334 obj.content.scrollTop = obj.content.scrollTop - obj.content.clientHeight; 6335 } 6336 obj.updateCornerPosition(); 6337 } 6338 } else if (obj.content.scrollTop <= obj.content.clientHeight) { 6339 if (obj.loadUp()) { 6340 if (obj.content.scrollTop < 10) { 6341 obj.content.scrollTop = obj.content.scrollTop + obj.content.clientHeight; 6342 } 6343 obj.updateCornerPosition(); 6344 } 6345 } 6346 6347 jexcel.timeControlLoading = null; 6348 }, 100); 6349 } 6350 } 6351 6352 // Close editor 6353 if (obj.options.lazyLoading == true || obj.options.tableOverflow == true) { 6354 if (obj.edition && e.target.className.substr(0,9) != 'jdropdown') { 6355 obj.closeEditor(obj.edition[0], true); 6356 } 6357 } 6358 } 6359 6360 el.addEventListener("DOMMouseScroll", obj.scrollControls); 6361 el.addEventListener("mousewheel", obj.scrollControls); 6362 6363 el.jexcel = obj; 6364 6365 obj.init(); 6366 6367 return obj; 6368 }); 6369 6370 jexcel.current = null; 6371 jexcel.timeControl = null; 6372 jexcel.timeControlLoading = null; 6373 6374 jexcel.destroy = function(element, destroyEventHandlers) { 6375 if (element.jexcel) { 6376 element.removeEventListener("DOMMouseScroll", element.jexcel.scrollControls); 6377 element.removeEventListener("mousewheel", element.jexcel.scrollControls); 6378 element.jexcel = null; 6379 element.innerHTML = ''; 6380 6381 if (destroyEventHandlers) { 6382 document.removeEventListener("keydown", jexcel.keyDownControls); 6383 document.removeEventListener("mouseup", jexcel.mouseUpControls); 6384 document.removeEventListener("mousedown", jexcel.mouseDownControls); 6385 document.removeEventListener("mousemove", jexcel.mouseMoveControls); 6386 document.removeEventListener("mouseover", jexcel.mouseOverControls); 6387 document.removeEventListener("dblclick", jexcel.doubleClickControls); 6388 document.removeEventListener("paste", jexcel.pasteControls); 6389 document.removeEventListener("contextmenu", jexcel.contextMenuControls); 6390 document.removeEventListener("touchstart", jexcel.touchStartControls); 6391 document.removeEventListener("touchend", jexcel.touchEndControls); 6392 document.removeEventListener("touchcancel", jexcel.touchEndControls); 6393 jexcel = null; 6394 } 6395 } 6396 } 6397 6398 jexcel.build = function() { 6399 document.addEventListener("keydown", jexcel.keyDownControls); 6400 document.addEventListener("mouseup", jexcel.mouseUpControls); 6401 document.addEventListener("mousedown", jexcel.mouseDownControls); 6402 document.addEventListener("mousemove", jexcel.mouseMoveControls); 6403 document.addEventListener("mouseover", jexcel.mouseOverControls); 6404 document.addEventListener("dblclick", jexcel.doubleClickControls); 6405 document.addEventListener("paste", jexcel.pasteControls); 6406 document.addEventListener("contextmenu", jexcel.contextMenuControls); 6407 document.addEventListener("touchstart", jexcel.touchStartControls); 6408 document.addEventListener("touchend", jexcel.touchEndControls); 6409 document.addEventListener("touchcancel", jexcel.touchEndControls); 6410 document.addEventListener("touchmove", jexcel.touchEndControls); 6411 } 6412 6413 /** 6414 * Events 6415 */ 6416 jexcel.keyDownControls = function(e) { 6417 if (jexcel.current) { 6418 if (jexcel.current.edition) { 6419 if (e.which == 27) { 6420 // Escape 6421 if (jexcel.current.edition) { 6422 // Exit without saving 6423 jexcel.current.closeEditor(jexcel.current.edition[0], false); 6424 } 6425 e.preventDefault(); 6426 } else if (e.which == 13) { 6427 // Enter 6428 if (jexcel.current.options.columns[jexcel.current.edition[2]].type == 'calendar') { 6429 jexcel.current.editor[0].children[0].calendar.close(1); 6430 } else if (jexcel.current.options.columns[jexcel.current.edition[2]].type == 'dropdown' || 6431 jexcel.current.options.columns[jexcel.current.edition[2]].type == 'autocomplete') { 6432 // Do nothing 6433 } else { 6434 // Alt enter -> do not close editor 6435 if ((jexcel.current.options.wordWrap == true || 6436 jexcel.current.options.columns[jexcel.current.edition[2]].wordWrap == true || 6437 jexcel.current.options.data[jexcel.current.edition[3]][jexcel.current.edition[2]].length > 200) && e.altKey) { 6438 // Add new line to the editor 6439 var editorTextarea = jexcel.current.edition[0].children[0]; 6440 var editorValue = jexcel.current.edition[0].children[0].value; 6441 var editorIndexOf = editorTextarea.selectionStart; 6442 editorValue = editorValue.slice(0, editorIndexOf) + "\n" + editorValue.slice(editorIndexOf); 6443 editorTextarea.value = editorValue; 6444 editorTextarea.focus(); 6445 editorTextarea.selectionStart = editorIndexOf + 1; 6446 editorTextarea.selectionEnd = editorIndexOf + 1; 6447 } else { 6448 jexcel.current.edition[0].children[0].blur(); 6449 } 6450 } 6451 } else if (e.which == 9) { 6452 // Tab 6453 if (jexcel.current.options.columns[jexcel.current.edition[2]].type == 'calendar') { 6454 jexcel.current.edition[0].children[0].calendar.close(1); 6455 } else { 6456 jexcel.current.edition[0].children[0].blur(); 6457 } 6458 } 6459 } 6460 6461 if (! jexcel.current.edition && jexcel.current.selectedCell) { 6462 // Which key 6463 if (e.which == 37) { 6464 jexcel.current.left(e.shiftKey, e.ctrlKey); 6465 e.preventDefault(); 6466 } else if (e.which == 39) { 6467 jexcel.current.right(e.shiftKey, e.ctrlKey); 6468 e.preventDefault(); 6469 } else if (e.which == 38) { 6470 jexcel.current.up(e.shiftKey, e.ctrlKey); 6471 e.preventDefault(); 6472 } else if (e.which == 40) { 6473 jexcel.current.down(e.shiftKey, e.ctrlKey); 6474 e.preventDefault(); 6475 } else if (e.which == 36) { 6476 jexcel.current.first(e.shiftKey, e.ctrlKey); 6477 e.preventDefault(); 6478 } else if (e.which == 35) { 6479 jexcel.current.last(e.shiftKey, e.ctrlKey); 6480 e.preventDefault(); 6481 } else if (e.which == 32) { 6482 if (jexcel.current.options.editable == true) { 6483 jexcel.current.setCheckRadioValue(); 6484 } 6485 e.preventDefault(); 6486 } else if (e.which == 46) { 6487 // Delete 6488 if (jexcel.current.options.editable == true) { 6489 if (jexcel.current.selectedRow) { 6490 if (jexcel.current.options.allowDeleteRow == true) { 6491 if (confirm(jexcel.current.options.text.areYouSureToDeleteTheSelectedRows)) { 6492 jexcel.current.deleteRow(); 6493 } 6494 } 6495 } else if (jexcel.current.selectedHeader) { 6496 if (jexcel.current.options.allowDeleteColumn == true) { 6497 if (confirm(jexcel.current.options.text.areYouSureToDeleteTheSelectedColumns)) { 6498 jexcel.current.deleteColumn(); 6499 } 6500 } 6501 } else { 6502 // Change value 6503 jexcel.current.setValue(jexcel.current.highlighted, ''); 6504 } 6505 } 6506 } else if (e.which == 13) { 6507 // Move cursor 6508 if (e.shiftKey) { 6509 jexcel.current.up(); 6510 } else { 6511 if (jexcel.current.options.allowInsertRow == true) { 6512 if (jexcel.current.options.allowManualInsertRow == true) { 6513 if (jexcel.current.selectedCell[1] == jexcel.current.options.data.length - 1) { 6514 // New record in case selectedCell in the last row 6515 jexcel.current.insertRow(); 6516 } 6517 } 6518 } 6519 6520 jexcel.current.down(); 6521 } 6522 e.preventDefault(); 6523 } else if (e.which == 9) { 6524 // Tab 6525 if (e.shiftKey) { 6526 jexcel.current.left(); 6527 } else { 6528 if (jexcel.current.options.allowInsertColumn == true) { 6529 if (jexcel.current.options.allowManualInsertColumn == true) { 6530 if (jexcel.current.selectedCell[0] == jexcel.current.options.data[0].length - 1) { 6531 // New record in case selectedCell in the last column 6532 jexcel.current.insertColumn(); 6533 } 6534 } 6535 } 6536 6537 jexcel.current.right(); 6538 } 6539 e.preventDefault(); 6540 } else { 6541 if ((e.ctrlKey || e.metaKey) && ! e.shiftKey) { 6542 if (e.which == 65) { 6543 // Ctrl + A 6544 jexcel.current.selectAll(); 6545 e.preventDefault(); 6546 } else if (e.which == 83) { 6547 // Ctrl + S 6548 jexcel.current.download(); 6549 e.preventDefault(); 6550 } else if (e.which == 89) { 6551 // Ctrl + Y 6552 jexcel.current.redo(); 6553 e.preventDefault(); 6554 } else if (e.which == 90) { 6555 // Ctrl + Z 6556 jexcel.current.undo(); 6557 e.preventDefault(); 6558 } else if (e.which == 67) { 6559 // Ctrl + C 6560 jexcel.current.copy(true); 6561 e.preventDefault(); 6562 } else if (e.which == 67) { 6563 // Ctrl + C 6564 jexcel.current.copy(true); 6565 e.preventDefault(); 6566 } else if (e.which == 88) { 6567 // Ctrl + X 6568 if (jexcel.current.options.editable == true) { 6569 jexcel.cutControls(); 6570 } else { 6571 jexcel.copyControls(); 6572 } 6573 e.preventDefault(); 6574 } else if (e.which == 86) { 6575 // Ctrl + V 6576 jexcel.pasteControls(); 6577 } 6578 } else { 6579 if (jexcel.current.selectedCell) { 6580 if (jexcel.current.options.editable == true) { 6581 var rowId = jexcel.current.selectedCell[1]; 6582 var columnId = jexcel.current.selectedCell[0]; 6583 6584 // If is not readonly 6585 if (jexcel.current.options.columns[columnId].type != 'readonly') { 6586 // Characters able to start a edition 6587 if (e.keyCode == 32) { 6588 // Space 6589 if (jexcel.current.options.columns[columnId].type == 'checkbox' || 6590 jexcel.current.options.columns[columnId].type == 'radio') { 6591 e.preventDefault(); 6592 } else { 6593 // Start edition 6594 jexcel.current.openEditor(jexcel.current.records[rowId][columnId], true); 6595 } 6596 } else if (e.keyCode == 113) { 6597 // Start edition with current content F2 6598 jexcel.current.openEditor(jexcel.current.records[rowId][columnId], false); 6599 } else if ((e.keyCode == 8) || 6600 (e.keyCode >= 48 && e.keyCode <= 57) || 6601 (e.keyCode >= 96 && e.keyCode <= 111) || 6602 (e.keyCode == 187) || 6603 (e.keyCode == 189) || 6604 ((String.fromCharCode(e.keyCode) == e.key || String.fromCharCode(e.keyCode).toLowerCase() == e.key.toLowerCase()) && jexcel.validLetter(String.fromCharCode(e.keyCode)))) { 6605 // Start edition 6606 jexcel.current.openEditor(jexcel.current.records[rowId][columnId], true); 6607 // Prevent entries in the calendar 6608 if (jexcel.current.options.columns[columnId].type == 'calendar') { 6609 e.preventDefault(); 6610 } 6611 } 6612 } 6613 } 6614 } 6615 } 6616 } 6617 } else { 6618 if (e.target.classList.contains('jexcel_search')) { 6619 if (jexcel.timeControl) { 6620 clearTimeout(jexcel.timeControl); 6621 } 6622 6623 jexcel.timeControl = setTimeout(function() { 6624 jexcel.current.search(e.target.value); 6625 }, 200); 6626 } 6627 } 6628 } 6629 } 6630 6631 jexcel.isMouseAction = false; 6632 6633 jexcel.mouseDownControls = function(e) { 6634 e = e || window.event; 6635 if (e.buttons) { 6636 var mouseButton = e.buttons; 6637 } else if (e.button) { 6638 var mouseButton = e.button; 6639 } else { 6640 var mouseButton = e.which; 6641 } 6642 6643 // Get elements 6644 var jexcelTable = jexcel.getElement(e.target); 6645 6646 if (jexcelTable[0]) { 6647 if (jexcel.current != jexcelTable[0].jexcel) { 6648 if (jexcel.current) { 6649 jexcel.current.resetSelection(); 6650 } 6651 jexcel.current = jexcelTable[0].jexcel; 6652 } 6653 } else { 6654 if (jexcel.current) { 6655 jexcel.current.resetSelection(true); 6656 jexcel.current = null; 6657 } 6658 } 6659 6660 if (jexcel.current && mouseButton == 1) { 6661 if (e.target.classList.contains('jexcel_selectall')) { 6662 if (jexcel.current) { 6663 jexcel.current.selectAll(); 6664 } 6665 } else if (e.target.classList.contains('jexcel_corner')) { 6666 if (jexcel.current.options.editable == true) { 6667 jexcel.current.selectedCorner = true; 6668 } 6669 } else { 6670 // Header found 6671 if (jexcelTable[1] == 1) { 6672 var columnId = e.target.getAttribute('data-x'); 6673 if (columnId) { 6674 // Update cursor 6675 var info = e.target.getBoundingClientRect(); 6676 if (jexcel.current.options.columnResize == true && info.width - e.offsetX < 6) { 6677 // Resize helper 6678 jexcel.current.resizing = { 6679 mousePosition: e.pageX, 6680 column: columnId, 6681 width: info.width, 6682 }; 6683 6684 // Border indication 6685 jexcel.current.headers[columnId].classList.add('resizing'); 6686 for (var j = 0; j < jexcel.current.records.length; j++) { 6687 if (jexcel.current.records[j][columnId]) { 6688 jexcel.current.records[j][columnId].classList.add('resizing'); 6689 } 6690 } 6691 } else if (jexcel.current.options.columnDrag == true && info.height - e.offsetY < 6) { 6692 if (jexcel.current.isColMerged(columnId).length) { 6693 console.error('JEXCEL: This column is part of a merged cell.'); 6694 } else { 6695 // Reset selection 6696 jexcel.current.resetSelection(); 6697 // Drag helper 6698 jexcel.current.dragging = { 6699 element: e.target, 6700 column:columnId, 6701 destination:columnId, 6702 }; 6703 // Border indication 6704 jexcel.current.headers[columnId].classList.add('dragging'); 6705 for (var j = 0; j < jexcel.current.records.length; j++) { 6706 if (jexcel.current.records[j][columnId]) { 6707 jexcel.current.records[j][columnId].classList.add('dragging'); 6708 } 6709 } 6710 } 6711 } else { 6712 if (jexcel.current.selectedHeader && (e.shiftKey || e.ctrlKey)) { 6713 var o = jexcel.current.selectedHeader; 6714 var d = columnId; 6715 } else { 6716 // Press to rename 6717 if (jexcel.current.selectedHeader == columnId && jexcel.current.options.allowRenameColumn == true) { 6718 jexcel.timeControl = setTimeout(function() { 6719 jexcel.current.setHeader(columnId); 6720 }, 800); 6721 } 6722 6723 // Keep track of which header was selected first 6724 jexcel.current.selectedHeader = columnId; 6725 6726 // Update selection single column 6727 var o = columnId; 6728 var d = columnId; 6729 } 6730 6731 // Update selection 6732 jexcel.current.updateSelectionFromCoords(o, 0, d, jexcel.current.options.data.length - 1); 6733 } 6734 } else { 6735 if (e.target.parentNode.classList.contains('jexcel_nested')) { 6736 if (e.target.getAttribute('data-column')) { 6737 var column = e.target.getAttribute('data-column').split(','); 6738 var c1 = parseInt(column[0]); 6739 var c2 = parseInt(column[column.length-1]); 6740 } else { 6741 var c1 = 0; 6742 var c2 = jexcel.current.options.columns.length - 1; 6743 } 6744 jexcel.current.updateSelectionFromCoords(c1, 0, c2, jexcel.current.options.data.length - 1); 6745 } 6746 } 6747 } else { 6748 jexcel.current.selectedHeader = false; 6749 } 6750 6751 // Body found 6752 if (jexcelTable[1] == 2) { 6753 var rowId = e.target.getAttribute('data-y'); 6754 6755 if (e.target.classList.contains('jexcel_row')) { 6756 var info = e.target.getBoundingClientRect(); 6757 if (jexcel.current.options.rowResize == true && info.height - e.offsetY < 6) { 6758 // Resize helper 6759 jexcel.current.resizing = { 6760 element: e.target.parentNode, 6761 mousePosition: e.pageY, 6762 row: rowId, 6763 height: info.height, 6764 }; 6765 // Border indication 6766 e.target.parentNode.classList.add('resizing'); 6767 } else if (jexcel.current.options.rowDrag == true && info.width - e.offsetX < 6) { 6768 if (jexcel.current.isRowMerged(rowId).length) { 6769 console.error('JEXCEL: This row is part of a merged cell'); 6770 } else if (jexcel.current.options.search == true && jexcel.current.results) { 6771 console.error('JEXCEL: Please clear your search before perform this action'); 6772 } else { 6773 // Reset selection 6774 jexcel.current.resetSelection(); 6775 // Drag helper 6776 jexcel.current.dragging = { 6777 element: e.target.parentNode, 6778 row:rowId, 6779 destination:rowId, 6780 }; 6781 // Border indication 6782 e.target.parentNode.classList.add('dragging'); 6783 } 6784 } else { 6785 if (jexcel.current.selectedRow && (e.shiftKey || e.ctrlKey)) { 6786 var o = jexcel.current.selectedRow; 6787 var d = rowId; 6788 } else { 6789 // Keep track of which header was selected first 6790 jexcel.current.selectedRow = rowId; 6791 6792 // Update selection single column 6793 var o = rowId; 6794 var d = rowId; 6795 } 6796 6797 // Update selection 6798 jexcel.current.updateSelectionFromCoords(0, o, jexcel.current.options.data[0].length - 1, d); 6799 } 6800 } else { 6801 // Jclose 6802 if (e.target.classList.contains('jclose') && e.target.clientWidth - e.offsetX < 50 && e.offsetY < 50) { 6803 jexcel.current.closeEditor(jexcel.current.edition[0], true); 6804 } else { 6805 var getCellCoords = function(element) { 6806 var x = element.getAttribute('data-x'); 6807 var y = element.getAttribute('data-y'); 6808 if (x && y) { 6809 return [x, y]; 6810 } else { 6811 if (element.parentNode) { 6812 return getCellCoords(element.parentNode); 6813 } 6814 } 6815 }; 6816 6817 var position = getCellCoords(e.target); 6818 if (position) { 6819 var columnId = position[0]; 6820 var rowId = position[1]; 6821 // Close edition 6822 if (jexcel.current.edition) { 6823 if (jexcel.current.edition[2] != columnId || jexcel.current.edition[3] != rowId) { 6824 jexcel.current.closeEditor(jexcel.current.edition[0], true); 6825 } 6826 } 6827 6828 if (! jexcel.current.edition) { 6829 // Update cell selection 6830 if (e.shiftKey) { 6831 jexcel.current.updateSelectionFromCoords(jexcel.current.selectedCell[0], jexcel.current.selectedCell[1], columnId, rowId); 6832 } else { 6833 jexcel.current.updateSelectionFromCoords(columnId, rowId); 6834 } 6835 } 6836 6837 // No full row selected 6838 jexcel.current.selectedHeader = null; 6839 jexcel.current.selectedRow = null; 6840 } 6841 } 6842 } 6843 } else { 6844 jexcel.current.selectedRow = false; 6845 } 6846 6847 // Pagination 6848 if (e.target.classList.contains('jexcel_page')) { 6849 if (e.target.innerText == '<') { 6850 jexcel.current.page(0); 6851 } else if (e.target.innerText == '>') { 6852 jexcel.current.page(e.target.getAttribute('title') - 1); 6853 } else { 6854 jexcel.current.page(e.target.innerText - 1); 6855 } 6856 } 6857 } 6858 6859 if (jexcel.current.edition) { 6860 jexcel.isMouseAction = false; 6861 } else { 6862 jexcel.isMouseAction = true; 6863 } 6864 } else { 6865 jexcel.isMouseAction = false; 6866 } 6867 } 6868 6869 jexcel.mouseUpControls = function(e) { 6870 if (jexcel.current) { 6871 // Update cell size 6872 if (jexcel.current.resizing) { 6873 // Columns to be updated 6874 if (jexcel.current.resizing.column) { 6875 // Remove Class 6876 jexcel.current.headers[jexcel.current.resizing.column].classList.remove('resizing'); 6877 var newWidth = jexcel.current.colgroup[jexcel.current.resizing.column].getAttribute('width'); 6878 jexcel.current.setWidth(jexcel.current.resizing.column, newWidth, jexcel.current.resizing.width); 6879 // Remove border 6880 jexcel.current.headers[jexcel.current.resizing.column].classList.remove('resizing'); 6881 for (var j = 0; j < jexcel.current.records.length; j++) { 6882 if (jexcel.current.records[j][jexcel.current.resizing.column]){ 6883 jexcel.current.records[j][jexcel.current.resizing.column].classList.remove('resizing'); 6884 } 6885 } 6886 } else { 6887 // Remove Class 6888 jexcel.current.rows[jexcel.current.resizing.row].children[0].classList.remove('resizing'); 6889 var newHeight = jexcel.current.rows[jexcel.current.resizing.row].getAttribute('height'); 6890 jexcel.current.setHeight(jexcel.current.resizing.row, newHeight, jexcel.current.resizing.height); 6891 // Remove border 6892 jexcel.current.resizing.element.classList.remove('resizing'); 6893 } 6894 // Reset resizing helper 6895 jexcel.current.resizing = null; 6896 } else if (jexcel.current.dragging) { 6897 // Reset dragging helper 6898 if (jexcel.current.dragging) { 6899 if (jexcel.current.dragging.column) { 6900 // Target 6901 var columnId = e.target.getAttribute('data-x'); 6902 // Remove move style 6903 jexcel.current.headers[jexcel.current.dragging.column].classList.remove('dragging'); 6904 for (var j = 0; j < jexcel.current.rows.length; j++) { 6905 if (jexcel.current.records[j][jexcel.current.dragging.column]) { 6906 jexcel.current.records[j][jexcel.current.dragging.column].classList.remove('dragging'); 6907 } 6908 } 6909 for (var i = 0; i < jexcel.current.headers.length; i++) { 6910 jexcel.current.headers[i].classList.remove('dragging-left'); 6911 jexcel.current.headers[i].classList.remove('dragging-right'); 6912 } 6913 // Update position 6914 if (columnId) { 6915 if (jexcel.current.dragging.column != jexcel.current.dragging.destination) { 6916 jexcel.current.moveColumn(jexcel.current.dragging.column, jexcel.current.dragging.destination); 6917 } 6918 } 6919 } else { 6920 if (jexcel.current.dragging.element.nextSibling) { 6921 var position = parseInt(jexcel.current.dragging.element.nextSibling.getAttribute('data-y')); 6922 if (jexcel.current.dragging.row < position) { 6923 position -= 1; 6924 } 6925 } else { 6926 var position = parseInt(jexcel.current.dragging.element.previousSibling.getAttribute('data-y')); 6927 } 6928 if (jexcel.current.dragging.row != position) { 6929 jexcel.current.moveRow(jexcel.current.dragging.row, position, true); 6930 } 6931 jexcel.current.dragging.element.classList.remove('dragging'); 6932 } 6933 jexcel.current.dragging = null; 6934 } 6935 } else { 6936 // Close any corner selection 6937 if (jexcel.current.selectedCorner) { 6938 jexcel.current.selectedCorner = false; 6939 6940 // Data to be copied 6941 if (jexcel.current.selection.length > 0) { 6942 // Copy data 6943 jexcel.current.copyData(jexcel.current.selection[0], jexcel.current.selection[jexcel.current.selection.length - 1]); 6944 6945 // Remove selection 6946 jexcel.current.removeCopySelection(); 6947 } 6948 } 6949 } 6950 } 6951 6952 // Clear any time control 6953 if (jexcel.timeControl) { 6954 clearTimeout(jexcel.timeControl); 6955 jexcel.timeControl = null; 6956 } 6957 6958 // Mouse up 6959 jexcel.isMouseAction = false; 6960 } 6961 6962 // Mouse move controls 6963 jexcel.mouseMoveControls = function(e) { 6964 e = e || window.event; 6965 if (e.buttons) { 6966 var mouseButton = e.buttons; 6967 } else if (e.button) { 6968 var mouseButton = e.button; 6969 } else { 6970 var mouseButton = e.which; 6971 } 6972 6973 if (! mouseButton) { 6974 jexcel.isMouseAction = false; 6975 } 6976 6977 if (jexcel.current) { 6978 if (jexcel.isMouseAction == true) { 6979 // Resizing is ongoing 6980 if (jexcel.current.resizing) { 6981 if (jexcel.current.resizing.column) { 6982 var width = e.pageX - jexcel.current.resizing.mousePosition; 6983 6984 if (jexcel.current.resizing.width + width > 0) { 6985 var tempWidth = jexcel.current.resizing.width + width; 6986 jexcel.current.colgroup[jexcel.current.resizing.column].setAttribute('width', tempWidth); 6987 6988 jexcel.current.updateCornerPosition(); 6989 } 6990 } else { 6991 var height = e.pageY - jexcel.current.resizing.mousePosition; 6992 6993 if (jexcel.current.resizing.height + height > 0) { 6994 var tempHeight = jexcel.current.resizing.height + height; 6995 jexcel.current.rows[jexcel.current.resizing.row].setAttribute('height', tempHeight); 6996 6997 jexcel.current.updateCornerPosition(); 6998 } 6999 } 7000 } 7001 } else { 7002 var x = e.target.getAttribute('data-x'); 7003 var y = e.target.getAttribute('data-y'); 7004 var rect = e.target.getBoundingClientRect(); 7005 7006 if (jexcel.current.cursor) { 7007 jexcel.current.cursor.style.cursor = ''; 7008 jexcel.current.cursor = null; 7009 } 7010 7011 if (e.target.parentNode.parentNode && e.target.parentNode.parentNode.className) { 7012 if (e.target.parentNode.parentNode.classList.contains('resizable')) { 7013 if (e.target && x && ! y && (rect.width - (e.clientX - rect.left) < 6)) { 7014 jexcel.current.cursor = e.target; 7015 jexcel.current.cursor.style.cursor = 'col-resize'; 7016 } else if (e.target && ! x && y && (rect.height - (e.clientY - rect.top) < 6)) { 7017 jexcel.current.cursor = e.target; 7018 jexcel.current.cursor.style.cursor = 'row-resize'; 7019 } 7020 } 7021 7022 if (e.target.parentNode.parentNode.classList.contains('draggable')) { 7023 if (e.target && ! x && y && (rect.width - (e.clientX - rect.left) < 6)) { 7024 jexcel.current.cursor = e.target; 7025 jexcel.current.cursor.style.cursor = 'move'; 7026 } else if (e.target && x && ! y && (rect.height - (e.clientY - rect.top) < 6)) { 7027 jexcel.current.cursor = e.target; 7028 jexcel.current.cursor.style.cursor = 'move'; 7029 } 7030 } 7031 } 7032 } 7033 } 7034 } 7035 7036 jexcel.mouseOverControls = function(e) { 7037 e = e || window.event; 7038 if (e.buttons) { 7039 var mouseButton = e.buttons; 7040 } else if (e.button) { 7041 var mouseButton = e.button; 7042 } else { 7043 var mouseButton = e.which; 7044 } 7045 7046 if (! mouseButton) { 7047 jexcel.isMouseAction = false; 7048 } 7049 7050 if (jexcel.current && jexcel.isMouseAction == true) { 7051 // Get elements 7052 var jexcelTable = jexcel.getElement(e.target); 7053 7054 if (jexcelTable[0]) { 7055 // Avoid cross reference 7056 if (jexcel.current != jexcelTable[0].jexcel) { 7057 if (jexcel.current) { 7058 return false; 7059 } 7060 } 7061 7062 var columnId = e.target.getAttribute('data-x'); 7063 var rowId = e.target.getAttribute('data-y'); 7064 7065 if (jexcel.current.dragging) { 7066 if (jexcel.current.dragging.column) { 7067 if (columnId) { 7068 if (jexcel.current.isColMerged(columnId).length) { 7069 console.error('JEXCEL: This column is part of a merged cell.'); 7070 } else { 7071 for (var i = 0; i < jexcel.current.headers.length; i++) { 7072 jexcel.current.headers[i].classList.remove('dragging-left'); 7073 jexcel.current.headers[i].classList.remove('dragging-right'); 7074 } 7075 7076 if (jexcel.current.dragging.column == columnId) { 7077 jexcel.current.dragging.destination = parseInt(columnId); 7078 } else { 7079 if (e.target.clientWidth / 2 > e.offsetX) { 7080 if (jexcel.current.dragging.column < columnId) { 7081 jexcel.current.dragging.destination = parseInt(columnId) - 1; 7082 } else { 7083 jexcel.current.dragging.destination = parseInt(columnId); 7084 } 7085 jexcel.current.headers[columnId].classList.add('dragging-left'); 7086 } else { 7087 if (jexcel.current.dragging.column < columnId) { 7088 jexcel.current.dragging.destination = parseInt(columnId); 7089 } else { 7090 jexcel.current.dragging.destination = parseInt(columnId) + 1; 7091 } 7092 jexcel.current.headers[columnId].classList.add('dragging-right'); 7093 } 7094 } 7095 } 7096 } 7097 } else { 7098 if (rowId) { 7099 if (jexcel.current.isRowMerged(rowId).length) { 7100 console.error('JEXCEL: This row is part of a merged cell.'); 7101 } else { 7102 var target = (e.target.clientHeight / 2 > e.offsetY) ? e.target.parentNode.nextSibling : e.target.parentNode; 7103 e.target.parentNode.parentNode.insertBefore(jexcel.current.dragging.element, target); 7104 } 7105 } 7106 } 7107 } else if (jexcel.current.resizing) { 7108 } else { 7109 // Header found 7110 if (jexcelTable[1] == 1) { 7111 if (jexcel.current.selectedHeader) { 7112 var columnId = e.target.getAttribute('data-x'); 7113 var o = jexcel.current.selectedHeader; 7114 var d = columnId; 7115 // Update selection 7116 jexcel.current.updateSelectionFromCoords(o, 0, d, jexcel.current.options.data.length - 1); 7117 } 7118 } 7119 7120 // Body found 7121 if (jexcelTable[1] == 2) { 7122 if (e.target.classList.contains('jexcel_row')) { 7123 if (jexcel.current.selectedRow) { 7124 var o = jexcel.current.selectedRow; 7125 var d = rowId; 7126 // Update selection 7127 jexcel.current.updateSelectionFromCoords(0, o, jexcel.current.options.data[0].length - 1, d); 7128 } 7129 } else { 7130 // Do not select edtion is in progress 7131 if (! jexcel.current.edition) { 7132 if (columnId && rowId) { 7133 if (jexcel.current.selectedCorner) { 7134 jexcel.current.updateCopySelection(columnId, rowId); 7135 } else { 7136 if (jexcel.current.selectedCell) { 7137 jexcel.current.updateSelectionFromCoords(jexcel.current.selectedCell[0], jexcel.current.selectedCell[1], columnId, rowId); 7138 } 7139 } 7140 } 7141 } 7142 } 7143 } 7144 } 7145 } 7146 } 7147 7148 // Clear any time control 7149 if (jexcel.timeControl) { 7150 clearTimeout(jexcel.timeControl); 7151 jexcel.timeControl = null; 7152 } 7153 } 7154 7155 /** 7156 * Double click event handler: controls the double click in the corner, cell edition or column re-ordering. 7157 */ 7158 jexcel.doubleClickControls = function(e) { 7159 // Jexcel is selected 7160 if (jexcel.current) { 7161 // Corner action 7162 if (e.target.classList.contains('jexcel_corner')) { 7163 // Any selected cells 7164 if (jexcel.current.highlighted.length > 0) { 7165 // Copy from this 7166 var x1 = jexcel.current.highlighted[0].getAttribute('data-x'); 7167 var y1 = parseInt(jexcel.current.highlighted[jexcel.current.highlighted.length - 1].getAttribute('data-y')) + 1; 7168 // Until this 7169 var x2 = jexcel.current.highlighted[jexcel.current.highlighted.length - 1].getAttribute('data-x'); 7170 var y2 = jexcel.current.records.length - 1 7171 // Execute copy 7172 jexcel.current.copyData(jexcel.current.records[y1][x1], jexcel.current.records[y2][x2]); 7173 } 7174 } else { 7175 // Get table 7176 var jexcelTable = jexcel.getElement(e.target); 7177 7178 // Double click over header 7179 if (jexcelTable[1] == 1 && jexcel.current.options.columnSorting == true) { 7180 // Check valid column header coords 7181 var columnId = e.target.getAttribute('data-x'); 7182 if (columnId) { 7183 jexcel.current.orderBy(columnId); 7184 } 7185 } 7186 7187 // Double click over body 7188 if (jexcelTable[1] == 2 && jexcel.current.options.editable == true) { 7189 if (! jexcel.current.edition) { 7190 var getCellCoords = function(element) { 7191 if (element.parentNode) { 7192 var x = element.getAttribute('data-x'); 7193 var y = element.getAttribute('data-y'); 7194 if (x && y) { 7195 return element; 7196 } else { 7197 return getCellCoords(element.parentNode); 7198 } 7199 } 7200 } 7201 var cell = getCellCoords(e.target); 7202 if (cell && cell.classList.contains('highlight')) { 7203 jexcel.current.openEditor(cell); 7204 } 7205 } 7206 } 7207 } 7208 } 7209 } 7210 7211 jexcel.copyControls = function(e) { 7212 if (jexcel.current && jexcel.copyControls.enabled) { 7213 if (! jexcel.current.edition) { 7214 jexcel.current.copy(true); 7215 } 7216 } 7217 } 7218 7219 jexcel.cutControls = function(e) { 7220 if (jexcel.current) { 7221 if (! jexcel.current.edition) { 7222 jexcel.current.copy(true); 7223 if (jexcel.current.options.editable == true) { 7224 jexcel.current.setValue(jexcel.current.highlighted, ''); 7225 } 7226 } 7227 } 7228 } 7229 7230 jexcel.pasteControls = function(e) { 7231 if (jexcel.current && jexcel.current.selectedCell) { 7232 if (! jexcel.current.edition) { 7233 if (jexcel.current.options.editable == true) { 7234 if (e && e.clipboardData) { 7235 jexcel.current.paste(jexcel.current.selectedCell[0], jexcel.current.selectedCell[1], e.clipboardData.getData('text')); 7236 e.preventDefault(); 7237 } else if (window.clipboardData) { 7238 jexcel.current.paste(jexcel.current.selectedCell[0], jexcel.current.selectedCell[1], window.clipboardData.getData('text')); 7239 } 7240 } 7241 } 7242 } 7243 } 7244 7245 jexcel.contextMenuControls = function(e) { 7246 e = e || window.event; 7247 if ("buttons" in e) { 7248 var mouseButton = e.buttons; 7249 } else { 7250 var mouseButton = e.which || e.button; 7251 } 7252 7253 if (jexcel.current) { 7254 if (jexcel.current.edition) { 7255 e.preventDefault(); 7256 } else if (jexcel.current.options.contextMenu) { 7257 jexcel.current.contextMenu.contextmenu.close(); 7258 7259 if (jexcel.current) { 7260 var x = e.target.getAttribute('data-x'); 7261 var y = e.target.getAttribute('data-y'); 7262 7263 if (x || y) { 7264 // Table found 7265 var items = jexcel.current.options.contextMenu(jexcel.current, x, y, e); 7266 // The id is depending on header and body 7267 jexcel.current.contextMenu.contextmenu.open(e, items); 7268 // Avoid the real one 7269 e.preventDefault(); 7270 } 7271 } 7272 } 7273 } 7274 } 7275 7276 jexcel.touchStartControls = function(e) { 7277 var jexcelTable = jexcel.getElement(e.target); 7278 7279 if (jexcelTable[0]) { 7280 if (jexcel.current != jexcelTable[0].jexcel) { 7281 if (jexcel.current) { 7282 jexcel.current.resetSelection(); 7283 } 7284 jexcel.current = jexcelTable[0].jexcel; 7285 } 7286 } else { 7287 if (jexcel.current) { 7288 jexcel.current.resetSelection(); 7289 jexcel.current = null; 7290 } 7291 } 7292 7293 if (jexcel.current) { 7294 if (! jexcel.current.edition) { 7295 var columnId = e.target.getAttribute('data-x'); 7296 var rowId = e.target.getAttribute('data-y'); 7297 7298 if (columnId && rowId) { 7299 jexcel.current.updateSelectionFromCoords(columnId, rowId); 7300 7301 jexcel.timeControl = setTimeout(function() { 7302 // Keep temporary reference to the element 7303 if (jexcel.current.options.columns[columnId].type == 'color') { 7304 jexcel.tmpElement = null; 7305 } else { 7306 jexcel.tmpElement = e.target; 7307 } 7308 jexcel.current.openEditor(e.target, false, e); 7309 }, 500); 7310 } 7311 } 7312 } 7313 } 7314
7315 jexcel.touchEndControls = function(e) { 7316 // Clear any time control 7317 if (jexcel.timeControl) { 7318 clearTimeout(jexcel.timeControl); 7319 jexcel.timeControl = null; 7320 // Element 7321 if (jexcel.tmpElement && jexcel.tmpElement.children[0].tagName == 'INPUT') { 7322 jexcel.tmpElement.children[0].focus(); 7323 } 7324 jexcel.tmpElement = null; 7325 } 7326 } 7327 7328 /** 7329 * Jexcel extensions 7330 */ 7331 7332 jexcel.createTabs = function(tabs, result) { 7333 // Create tab container 7334 tabs.innerHTML = ''; 7335 tabs.classList.add('jexcel_tabs'); 7336 var spreadsheet = [] 7337 var link = []; 7338 for (var i = 0; i < result.length; i++) { 7339 // Spreadsheet container 7340 spreadsheet[i] = document.createElement('div'); 7341 spreadsheet[i].classList.add('jexcel_tab'); 7342 // Tab link 7343 link[i] = document.createElement('div'); 7344 link[i].classList.add('jexcel_tab_link'); 7345 link[i].setAttribute('data-spreadsheet', i); 7346 link[i].innerHTML = result[i].sheetName; 7347 link[i].onclick = function() { 7348 for (var j = 0; j < spreadsheet.length; j++) { 7349 spreadsheet[j].style.display = 'none'; 7350 link[j].classList.remove('selected'); 7351 } 7352 var i = this.getAttribute('data-spreadsheet'); 7353 spreadsheet[i].style.display = 'block'; 7354 link[i].classList.add('selected') 7355 } 7356 tabs.appendChild(link[i]); 7357 } 7358 7359 // Append spreadsheet 7360 for (var i = 0; i < spreadsheet.length - 1; i++) { 7361 tabs.appendChild(spreadsheet[i]); 7362 jexcel(spreadsheet[i], result[i]); 7363 } 7364 7365 // First tab 7366 spreadsheet[0].style.display = 'block'; 7367 link[0].classList.add('selected') 7368 } 7369 7370 jexcel.fromSpreadsheet = function(file, __callback) { 7371 var convert = function(workbook) { 7372 var spreadsheets = []; 7373 workbook.SheetNames.forEach(function(sheetName) { 7374 var spreadsheet = {}; 7375 spreadsheet.rows = []; 7376 spreadsheet.columns = []; 7377 spreadsheet.data = []; 7378 spreadsheet.style = {}; 7379 spreadsheet.sheetName = sheetName; 7380 7381 // Column widths 7382 var temp = workbook.Sheets[sheetName]['!cols']; 7383 if (temp && temp.length) { 7384 for (var i = 0; i < temp.length; i++) { 7385 spreadsheet.columns[i] = {}; 7386 if (temp[i] && temp[i].wpx) { 7387 spreadsheet.columns[i].width = temp[i].wpx + 'px'; 7388 } 7389 } 7390 } 7391 // Rows heights 7392 var temp = workbook.Sheets[sheetName]['!rows']; 7393 if (temp && temp.length) { 7394 for (var i = 0; i < temp.length; i++) { 7395 if (temp[i] && temp[i].hpx) { 7396 spreadsheet.rows[i] = {}; 7397 spreadsheet.rows[i].height = temp[i].hpx + 'px'; 7398 } 7399 } 7400 } 7401 // Merge cells 7402 var temp = workbook.Sheets[sheetName]['!merges']; 7403 if (temp && temp.length > 0) { 7404 spreadsheet.mergeCells = []; 7405 for (var i = 0; i < temp.length; i++) { 7406 var x1 = temp[i].s.c; 7407 var y1 = temp[i].s.r; 7408 var x2 = temp[i].e.c; 7409 var y2 = temp[i].e.r; 7410 var key = jexcel.getColumnNameFromId([x1,y1]); 7411 spreadsheet.mergeCells[key] = [ x2-x1+1, y2-y1+1 ]; 7412 } 7413 } 7414 // Data container 7415 var max_x = 0; 7416 var max_y = 0; 7417 var temp = Object.keys(workbook.Sheets[sheetName]); 7418 for (var i = 0; i < temp.length; i++) { 7419 if (temp[i].substr(0,1) != '!') { 7420 var cell = workbook.Sheets[sheetName][temp[i]]; 7421 var info = jexcel.getIdFromColumnName(temp[i], true);
vendor: 207,886 bytes, lines 7422-13182
7422 if (! spreadsheet.data[info[1]]) { 7423 spreadsheet.data[info[1]] = []; 7424 } 7425 spreadsheet.data[info[1]][info[0]] = cell.f ? '=' + cell.f : cell.w; 7426 if (max_x < info[0]) { 7427 max_x = info[0]; 7428 } 7429 if (max_y < info[1]) { 7430 max_y = info[1]; 7431 } 7432 // Style 7433 if (cell.style && Object.keys(cell.style).length > 0) { 7434 spreadsheet.style[temp[i]] = cell.style; 7435 } 7436 if (cell.s && cell.s.fgColor) { 7437 if (spreadsheet.style[temp[i]]) { 7438 spreadsheet.style[temp[i]] += ';'; 7439 } 7440 spreadsheet.style[temp[i]] += 'background-color:#' + cell.s.fgColor.rgb; 7441 } 7442 } 7443 } 7444 var numColumns = spreadsheet.columns; 7445 for (var j = 0; j <= max_y; j++) { 7446 for (var i = 0; i <= max_x; i++) { 7447 if (! spreadsheet.data[j]) { 7448 spreadsheet.data[j] = []; 7449 } 7450 if (! spreadsheet.data[j][i]) { 7451 if (numColumns < i) { 7452 spreadsheet.data[j][i] = ''; 7453 } 7454 } 7455 } 7456 } 7457 spreadsheets.push(spreadsheet); 7458 }); 7459 7460 return spreadsheets; 7461 } 7462 7463 var oReq; 7464 oReq = new XMLHttpRequest(); 7465 oReq.open("GET", file, true); 7466 7467 if(typeof Uint8Array !== 'undefined') { 7468 oReq.responseType = "arraybuffer"; 7469 oReq.onload = function(e) { 7470 var arraybuffer = oReq.response; 7471 var data = new Uint8Array(arraybuffer); 7472 var wb = XLSX.read(data, {type:"array", cellFormula:true, cellStyles:true }); 7473 __callback(convert(wb)) 7474 }; 7475 } else { 7476 oReq.setRequestHeader("Accept-Charset", "x-user-defined"); 7477 oReq.onreadystatechange = function() { if(oReq.readyState == 4 && oReq.status == 200) { 7478 var ff = convertResponseBodyToText(oReq.responseBody); 7479 var wb = XLSX.read(ff, {type:"binary", cellFormula:true, cellStyles:true }); 7480 __callback(convert(wb)) 7481 }}; 7482 } 7483 7484 oReq.send(); 7485 } 7486 7487 /** 7488 * Valid international letter 7489 */ 7490 7491 jexcel.validLetter = function (text) { 7492 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; 7493 return text.match(regex) ? 1 : 0; 7494 } 7495 7496 /** 7497 * Helper injectArray 7498 */ 7499 jexcel.injectArray = function(o, idx, arr) { 7500 return o.slice(0, idx).concat(arr).concat(o.slice(idx)); 7501 } 7502 7503 /** 7504 * Get letter based on a number 7505 * 7506 * @param integer i 7507 * @return string letter 7508 */ 7509 jexcel.getColumnName = function(i) { 7510 var letter = ''; 7511 if (i > 701) { 7512 letter += String.fromCharCode(64 + parseInt(i / 676)); 7513 letter += String.fromCharCode(64 + parseInt((i % 676) / 26)); 7514 } else if (i > 25) { 7515 letter += String.fromCharCode(64 + parseInt(i / 26)); 7516 } 7517 letter += String.fromCharCode(65 + (i % 26)); 7518 7519 return letter; 7520 } 7521 7522 /** 7523 * Convert excel like column to jexcel id 7524 * 7525 * @param string id 7526 * @return string id 7527 */ 7528 jexcel.getIdFromColumnName = function (id, arr) { 7529 // Get the letters 7530 var t = /^[a-zA-Z]+/.exec(id); 7531 7532 if (t) { 7533 // Base 26 calculation 7534 var code = 0; 7535 for (var i = 0; i < t[0].length; i++) { 7536 code += parseInt(t[0].charCodeAt(i) - 64) * Math.pow(26, (t[0].length - 1 - i)); 7537 } 7538 code--; 7539 // Make sure jexcel starts on zero 7540 if (code < 0) { 7541 code = 0; 7542 } 7543 7544 // Number 7545 var number = parseInt(/[0-9]+$/.exec(id)); 7546 if (number > 0) { 7547 number--; 7548 } 7549 7550 if (arr == true) { 7551 id = [ code, number ]; 7552 } else { 7553 id = code + '-' + number; 7554 } 7555 } 7556 7557 return id; 7558 } 7559 7560 /** 7561 * Convert jexcel id to excel like column name 7562 * 7563 * @param string id 7564 * @return string id 7565 */ 7566 jexcel.getColumnNameFromId = function (cellId) { 7567 if (! Array.isArray(cellId)) { 7568 cellId = cellId.split('-'); 7569 } 7570 7571 return jexcel.getColumnName(parseInt(cellId[0])) + (parseInt(cellId[1]) + 1); 7572 } 7573 7574 /** 7575 * Verify element inside jexcel table 7576 * 7577 * @param string id 7578 * @return string id 7579 */ 7580 jexcel.getElement = function(element) { 7581 var jexcelSection = 0; 7582 var jexcelElement = 0; 7583 7584 function path (element) { 7585 if (element.className) { 7586 if (element.classList.contains('jexcel_container')) { 7587 jexcelElement = element; 7588 } 7589 } 7590 7591 if (element.tagName == 'THEAD') { 7592 jexcelSection = 1; 7593 } else if (element.tagName == 'TBODY') { 7594 jexcelSection = 2; 7595 } 7596 7597 if (element.parentNode) { 7598 path(element.parentNode); 7599 } 7600 } 7601 7602 path(element); 7603 7604 return [ jexcelElement, jexcelSection ]; 7605 } 7606 7607 jexcel.doubleDigitFormat = function(v) { 7608 v = ''+v; 7609 if (v.length == 1) { 7610 v = '0'+v; 7611 } 7612 return v; 7613 } 7614 7615 /** 7616 * Jquery Support 7617 */ 7618 if (typeof(jQuery) != 'undefined') { 7619 (function($){ 7620 $.fn.jexcel = function(method) { 7621 var spreadsheetContainer = $(this).get(0); 7622 if (! spreadsheetContainer.jexcel) { 7623 return jexcel($(this).get(0), arguments[0]); 7624 } else { 7625 return spreadsheetContainer.jexcel[method].apply(this, Array.prototype.slice.call( arguments, 1 )); 7626 } 7627 }; 7628 7629 })(jQuery); 7630 } 7631 7632 7633 // Based on sutoiku work (https://github.com/sutoiku) 7634 7635 var error = (function() { 7636 var exports = {}; 7637 7638 exports.nil = new Error('#NULL!'); 7639 exports.div0 = new Error('#DIV/0!'); 7640 exports.value = new Error('#VALUE!'); 7641 exports.ref = new Error('#REF!'); 7642 exports.name = new Error('#NAME?'); 7643 exports.num = new Error('#NUM!'); 7644 exports.na = new Error('#N/A'); 7645 exports.error = new Error('#ERROR!'); 7646 exports.data = new Error('#GETTING_DATA'); 7647 7648 return exports; 7649 })(); 7650 7651 var utils = (function() { 7652 var exports = {}; 7653 7654 exports.flattenShallow = function(array) { 7655 if (!array || !array.reduce) { 7656 return array; 7657 } 7658 7659 return array.reduce(function(a, b) { 7660 var aIsArray = Array.isArray(a); 7661 var bIsArray = Array.isArray(b); 7662 7663 if (aIsArray && bIsArray) { 7664 return a.concat(b); 7665 } 7666 if (aIsArray) { 7667 a.push(b); 7668 7669 return a; 7670 } 7671 if (bIsArray) { 7672 return [ a ].concat(b); 7673 } 7674 7675 return [ a, b ]; 7676 }); 7677 }; 7678 7679 exports.isFlat = function(array) { 7680 if (!array) { 7681 return false; 7682 } 7683 7684 for (var i = 0; i < array.length; ++i) { 7685 if (Array.isArray(array[i])) { 7686 return false; 7687 } 7688 } 7689 7690 return true; 7691 }; 7692 7693 exports.flatten = function() { 7694 var result = exports.argsToArray.apply(null, arguments); 7695 7696 while (!exports.isFlat(result)) { 7697 result = exports.flattenShallow(result); 7698 } 7699 7700 return result; 7701 }; 7702 7703 exports.argsToArray = function(args) { 7704 var result = []; 7705 7706 exports.arrayEach(args, function(value) { 7707 result.push(value); 7708 }); 7709 7710 return result; 7711 }; 7712 7713 exports.numbers = function() { 7714 var possibleNumbers = this.flatten.apply(null, arguments); 7715 return possibleNumbers.filter(function(el) { 7716 return typeof el === 'number'; 7717 }); 7718 }; 7719 7720 exports.cleanFloat = function(number) { 7721 var power = 1e14; 7722 return Math.round(number * power) / power; 7723 }; 7724 7725 exports.parseBool = function(bool) { 7726 if (typeof bool === 'boolean') { 7727 return bool; 7728 } 7729 7730 if (bool instanceof Error) { 7731 return bool; 7732 } 7733 7734 if (typeof bool === 'number') { 7735 return bool !== 0; 7736 } 7737 7738 if (typeof bool === 'string') { 7739 var up = bool.toUpperCase(); 7740 if (up === 'TRUE') { 7741 return true; 7742 } 7743 7744 if (up === 'FALSE') { 7745 return false; 7746 } 7747 } 7748 7749 if (bool instanceof Date && !isNaN(bool)) { 7750 return true; 7751 } 7752 7753 return error.value; 7754 }; 7755 7756 exports.parseNumber = function(string) { 7757 if (string === undefined || string === '') { 7758 return error.value; 7759 } 7760 if (!isNaN(string)) { 7761 return parseFloat(string); 7762 } 7763 7764 return error.value; 7765 }; 7766 7767 exports.parseNumberArray = function(arr) { 7768 var len; 7769 7770 if (!arr || (len = arr.length) === 0) { 7771 return error.value; 7772 } 7773 7774 var parsed; 7775 7776 while (len--) { 7777 parsed = exports.parseNumber(arr[len]); 7778 if (parsed === error.value) { 7779 return parsed; 7780 } 7781 arr[len] = parsed; 7782 } 7783 7784 return arr; 7785 }; 7786 7787 exports.parseMatrix = function(matrix) { 7788 var n; 7789 7790 if (!matrix || (n = matrix.length) === 0) { 7791 return error.value; 7792 } 7793 var pnarr; 7794 7795 for (var i = 0; i < matrix.length; i++) { 7796 pnarr = exports.parseNumberArray(matrix[i]); 7797 matrix[i] = pnarr; 7798 7799 if (pnarr instanceof Error) { 7800 return pnarr; 7801 } 7802 } 7803 7804 return matrix; 7805 }; 7806 7807 var d1900 = new Date(Date.UTC(1900, 0, 1)); 7808 exports.parseDate = function(date) { 7809 if (!isNaN(date)) { 7810 if (date instanceof Date) { 7811 return new Date(date); 7812 } 7813 var d = parseInt(date, 10); 7814 if (d < 0) { 7815 return error.num; 7816 } 7817 if (d <= 60) { 7818 return new Date(d1900.getTime() + (d - 1) * 86400000); 7819 } 7820 return new Date(d1900.getTime() + (d - 2) * 86400000); 7821 } 7822 if (typeof date === 'string') { 7823 date = new Date(date); 7824 if (!isNaN(date)) { 7825 return date; 7826 } 7827 } 7828 return error.value; 7829 }; 7830 7831 exports.parseDateArray = function(arr) { 7832 var len = arr.length; 7833 var parsed; 7834 while (len--) { 7835 parsed = this.parseDate(arr[len]); 7836 if (parsed === error.value) { 7837 return parsed; 7838 } 7839 arr[len] = parsed; 7840 } 7841 return arr; 7842 }; 7843 7844 exports.anyIsError = function() { 7845 var n = arguments.length; 7846 while (n--) { 7847 if (arguments[n] instanceof Error) { 7848 return true; 7849 } 7850 } 7851 return false; 7852 }; 7853 7854 exports.arrayValuesToNumbers = function(arr) { 7855 var n = arr.length; 7856 var el; 7857 while (n--) { 7858 el = arr[n]; 7859 if (typeof el === 'number') { 7860 continue; 7861 } 7862 if (el === true) { 7863 arr[n] = 1; 7864 continue; 7865 } 7866 if (el === false) { 7867 arr[n] = 0; 7868 continue; 7869 } 7870 if (typeof el === 'string') { 7871 var number = this.parseNumber(el); 7872 if (number instanceof Error) { 7873 arr[n] = 0; 7874 } else { 7875 arr[n] = number; 7876 } 7877 } 7878 } 7879 return arr; 7880 }; 7881 7882 exports.rest = function(array, idx) { 7883 idx = idx || 1; 7884 if (!array || typeof array.slice !== 'function') { 7885 return array; 7886 } 7887 return array.slice(idx); 7888 }; 7889 7890 exports.initial = function(array, idx) { 7891 idx = idx || 1; 7892 if (!array || typeof array.slice !== 'function') { 7893 return array; 7894 } 7895 return array.slice(0, array.length - idx); 7896 }; 7897 7898 exports.arrayEach = function(array, iteratee) { 7899 var index = -1, length = array.length; 7900 7901 while (++index < length) { 7902 if (iteratee(array[index], index, array) === false) { 7903 break; 7904 } 7905 } 7906 7907 return array; 7908 }; 7909 7910 exports.transpose = function(matrix) { 7911 if (!matrix) { 7912 return error.value; 7913 } 7914 7915 return matrix[0].map(function(col, i) { 7916 return matrix.map(function(row) { 7917 return row[i]; 7918 }); 7919 }); 7920 }; 7921 7922 return exports; 7923 })(); 7924 7925 jexcel.methods = {}; 7926 7927 jexcel.methods.datetime = (function() { 7928 var exports = {}; 7929 7930 var d1900 = new Date(1900, 0, 1); 7931 var WEEK_STARTS = [ 7932 undefined, 7933 0, 7934 1, 7935 undefined, 7936 undefined, 7937 undefined, 7938 undefined, 7939 undefined, 7940 undefined, 7941 undefined, 7942 undefined, 7943 undefined, 7944 1, 7945 2, 7946 3, 7947 4, 7948 5, 7949 6, 7950 0 7951 ]; 7952 var WEEK_TYPES = [ 7953 [], 7954 [1, 2, 3, 4, 5, 6, 7], 7955 [7, 1, 2, 3, 4, 5, 6], 7956 [6, 0, 1, 2, 3, 4, 5], 7957 [], 7958 [], 7959 [], 7960 [], 7961 [], 7962 [], 7963 [], 7964 [7, 1, 2, 3, 4, 5, 6], 7965 [6, 7, 1, 2, 3, 4, 5], 7966 [5, 6, 7, 1, 2, 3, 4], 7967 [4, 5, 6, 7, 1, 2, 3], 7968 [3, 4, 5, 6, 7, 1, 2], 7969 [2, 3, 4, 5, 6, 7, 1], 7970 [1, 2, 3, 4, 5, 6, 7] 7971 ]; 7972 var WEEKEND_TYPES = [ 7973 [], 7974 [6, 0], 7975 [0, 1], 7976 [1, 2], 7977 [2, 3], 7978 [3, 4], 7979 [4, 5], 7980 [5, 6], 7981 undefined, 7982 undefined, 7983 undefined, [0, 0], 7984 [1, 1], 7985 [2, 2], 7986 [3, 3], 7987 [4, 4], 7988 [5, 5], 7989 [6, 6] 7990 ]; 7991 7992 exports.DATE = function(year, month, day) { 7993 year = utils.parseNumber(year); 7994 month = utils.parseNumber(month); 7995 day = utils.parseNumber(day); 7996 if (utils.anyIsError(year, month, day)) { 7997 return error.value; 7998 } 7999 if (year < 0 || month < 0 || day < 0) { 8000 return error.num; 8001 } 8002 var date = new Date(year, month - 1, day); 8003 return date; 8004 }; 8005 8006 exports.DATEVALUE = function(date_text) { 8007 if (typeof date_text !== 'string') { 8008 return error.value; 8009 } 8010 var date = Date.parse(date_text); 8011 if (isNaN(date)) { 8012 return error.value; 8013 } 8014 if (date <= -2203891200000) { 8015 return (date - d1900) / 86400000 + 1; 8016 } 8017 return (date - d1900) / 86400000 + 2; 8018 }; 8019 8020 exports.DAY = function(serial_number) { 8021 var date = utils.parseDate(serial_number); 8022 if (date instanceof Error) { 8023 return date; 8024 } 8025 return date.getDate(); 8026 }; 8027 8028 exports.DAYS = function(end_date, start_date) { 8029 end_date = utils.parseDate(end_date); 8030 start_date = utils.parseDate(start_date); 8031 if (end_date instanceof Error) { 8032 return end_date; 8033 } 8034 if (start_date instanceof Error) { 8035 return start_date; 8036 } 8037 return serial(end_date) - serial(start_date); 8038 }; 8039 8040 exports.DAYS360 = function(start_date, end_date, method) { 8041 }; 8042 8043 exports.EDATE = function(start_date, months) { 8044 start_date = utils.parseDate(start_date); 8045 if (start_date instanceof Error) { 8046 return start_date; 8047 } 8048 if (isNaN(months)) { 8049 return error.value; 8050 } 8051 months = parseInt(months, 10); 8052 start_date.setMonth(start_date.getMonth() + months); 8053 return serial(start_date); 8054 }; 8055 8056 exports.EOMONTH = function(start_date, months) { 8057 start_date = utils.parseDate(start_date); 8058 if (start_date instanceof Error) { 8059 return start_date; 8060 } 8061 if (isNaN(months)) { 8062 return error.value; 8063 } 8064 months = parseInt(months, 10); 8065 return serial(new Date(start_date.getFullYear(), start_date.getMonth() + months + 1, 0)); 8066 }; 8067 8068 exports.HOUR = function(serial_number) { 8069 serial_number = utils.parseDate(serial_number); 8070 if (serial_number instanceof Error) { 8071 return serial_number; 8072 } 8073 return serial_number.getHours(); 8074 }; 8075 8076 exports.INTERVAL = function(second) { 8077 if (typeof second !== 'number' && typeof second !== 'string') { 8078 return error.value; 8079 } else { 8080 second = parseInt(second, 10); 8081 } 8082 8083 var year = Math.floor(second/946080000); 8084 second = second%946080000; 8085 var month = Math.floor(second/2592000); 8086 second = second%2592000; 8087 var day = Math.floor(second/86400); 8088 second = second%86400; 8089 8090 var hour = Math.floor(second/3600); 8091 second = second%3600; 8092 var min = Math.floor(second/60); 8093 second = second%60; 8094 var sec = second; 8095 8096 year = (year > 0) ? year + 'Y' : ''; 8097 month = (month > 0) ? month + 'M' : ''; 8098 day = (day > 0) ? day + 'D' : ''; 8099 hour = (hour > 0) ? hour + 'H' : ''; 8100 min = (min > 0) ? min + 'M' : ''; 8101 sec = (sec > 0) ? sec + 'S' : ''; 8102 8103 return 'P' + year + month + day + 'T' + hour + min + sec; 8104 }; 8105 8106 exports.ISOWEEKNUM = function(date) { 8107 date = utils.parseDate(date); 8108 if (date instanceof Error) { 8109 return date; 8110 } 8111 8112 date.setHours(0, 0, 0); 8113 date.setDate(date.getDate() + 4 - (date.getDay() || 7)); 8114 var yearStart = new Date(date.getFullYear(), 0, 1); 8115 return Math.ceil((((date - yearStart) / 86400000) + 1) / 7); 8116 }; 8117 8118 exports.MINUTE = function(serial_number) { 8119 serial_number = utils.parseDate(serial_number); 8120 if (serial_number instanceof Error) { 8121 return serial_number; 8122 } 8123 return serial_number.getMinutes(); 8124 }; 8125 8126 exports.MONTH = function(serial_number) { 8127 serial_number = utils.parseDate(serial_number); 8128 if (serial_number instanceof Error) { 8129 return serial_number; 8130 } 8131 return serial_number.getMonth() + 1; 8132 }; 8133 8134 exports.NETWORKDAYS = function(start_date, end_date, holidays) { 8135 }; 8136 8137 exports.NETWORKDAYS.INTL = function(start_date, end_date, weekend, holidays) { 8138 }; 8139 8140 exports.NOW = function() { 8141 return new Date(); 8142 }; 8143 8144 exports.SECOND = function(serial_number) { 8145 serial_number = utils.parseDate(serial_number); 8146 if (serial_number instanceof Error) { 8147 return serial_number; 8148 } 8149 return serial_number.getSeconds(); 8150 }; 8151 8152 exports.TIME = function(hour, minute, second) { 8153 hour = utils.parseNumber(hour); 8154 minute = utils.parseNumber(minute); 8155 second = utils.parseNumber(second); 8156 if (utils.anyIsError(hour, minute, second)) { 8157 return error.value; 8158 } 8159 if (hour < 0 || minute < 0 || second < 0) { 8160 return error.num; 8161 } 8162 return (3600 * hour + 60 * minute + second) / 86400; 8163 }; 8164 8165 exports.TIMEVALUE = function(time_text) { 8166 time_text = utils.parseDate(time_text); 8167 if (time_text instanceof Error) { 8168 return time_text; 8169 } 8170 return (3600 * time_text.getHours() + 60 * time_text.getMinutes() + time_text.getSeconds()) / 86400; 8171 }; 8172 8173 exports.TODAY = function() { 8174 return new Date(); 8175 }; 8176 8177 exports.WEEKDAY = function(serial_number, return_type) { 8178 serial_number = utils.parseDate(serial_number); 8179 if (serial_number instanceof Error) { 8180 return serial_number; 8181 } 8182 if (return_type === undefined) { 8183 return_type = 1; 8184 } 8185 var day = serial_number.getDay(); 8186 return WEEK_TYPES[return_type][day]; 8187 }; 8188 8189 exports.WEEKNUM = function(serial_number, return_type) { 8190 }; 8191 8192 exports.WORKDAY = function(start_date, days, holidays) { 8193 }; 8194 8195 exports.WORKDAY.INTL = function(start_date, days, weekend, holidays) { 8196 }; 8197 8198 exports.YEAR = function(serial_number) { 8199 serial_number = utils.parseDate(serial_number); 8200 if (serial_number instanceof Error) { 8201 return serial_number; 8202 } 8203 return serial_number.getFullYear(); 8204 }; 8205 8206 function isLeapYear(year) { 8207 return new Date(year, 1, 29).getMonth() === 1; 8208 } 8209 8210 exports.YEARFRAC = function(start_date, end_date, basis) { 8211 }; 8212 8213 function serial(date) { 8214 var addOn = (date > -2203891200000)?2:1; 8215 return (date - d1900) / 86400000 + addOn; 8216 } 8217 8218 return exports; 8219 })(); 8220 8221 jexcel.methods.database = (function() { 8222 var exports = {}; 8223 8224 function compact(array) { 8225 if (!array) { 8226 return array; 8227 } 8228 var result = []; 8229 for (var i = 0; i < array.length; ++i) { 8230 if (!array[i]) { 8231 continue; 8232 } 8233 result.push(array[i]); 8234 } 8235 return result; 8236 } 8237 8238 exports.FINDFIELD = function(database, title) { 8239 var index = null; 8240 for (var i = 0; i < database.length; i++) { 8241 if (database[i][0] === title) { 8242 index = i; 8243 break; 8244 } 8245 } 8246 8247 // Return error if the input field title is incorrect 8248 if (index == null) { 8249 return error.value; 8250 } 8251 return index; 8252 }; 8253 8254 function findResultIndex(database, criterias) { 8255 var matches = {}; 8256 for (var i = 1; i < database[0].length; ++i) { 8257 matches[i] = true; 8258 } 8259 var maxCriteriaLength = criterias[0].length; 8260 for (i = 1; i < criterias.length; ++i) { 8261 if (criterias[i].length > maxCriteriaLength) { 8262 maxCriteriaLength = criterias[i].length; 8263 } 8264 } 8265 8266 for (var k = 1; k < database.length; ++k) { 8267 for (var l = 1; l < database[k].length; ++l) { 8268 var currentCriteriaResult = false; 8269 var hasMatchingCriteria = false; 8270 for (var j = 0; j < criterias.length; ++j) { 8271 var criteria = criterias[j]; 8272 if (criteria.length < maxCriteriaLength) { 8273 continue; 8274 } 8275 8276 var criteriaField = criteria[0]; 8277 if (database[k][0] !== criteriaField) { 8278 continue; 8279 } 8280 hasMatchingCriteria = true; 8281 for (var p = 1; p < criteria.length; ++p) { 8282 currentCriteriaResult = currentCriteriaResult 8283 || eval(database[k][l] + criteria[p]); // jshint 8284 // ignore:line 8285 } 8286 } 8287 if (hasMatchingCriteria) { 8288 matches[l] = matches[l] && currentCriteriaResult; 8289 } 8290 } 8291 } 8292 8293 var result = []; 8294 for (var n = 0; n < database[0].length; ++n) { 8295 if (matches[n]) { 8296 result.push(n - 1); 8297 } 8298 } 8299 return result; 8300 } 8301 8302 // Database functions 8303 exports.DAVERAGE = function(database, field, criteria) { 8304 // Return error if field is not a number and not a string 8305 if (isNaN(field) && (typeof field !== "string")) { 8306 return error.value; 8307 } 8308 var resultIndexes = findResultIndex(database, criteria); 8309 var targetFields = []; 8310 if (typeof field === "string") { 8311 var index = exports.FINDFIELD(database, field); 8312 targetFields = utils.rest(database[index]); 8313 } else { 8314 targetFields = utils.rest(database[field]); 8315 } 8316 var sum = 0; 8317 for (var i = 0; i < resultIndexes.length; i++) { 8318 sum += targetFields[resultIndexes[i]]; 8319 } 8320 return resultIndexes.length === 0 ? error.div0 : sum / resultIndexes.length; 8321 }; 8322 8323 exports.DCOUNT = function(database, field, criteria) { 8324 }; 8325 8326 exports.DCOUNTA = function(database, field, criteria) { 8327 }; 8328 8329 exports.DGET = function(database, field, criteria) { 8330 // Return error if field is not a number and not a string 8331 if (isNaN(field) && (typeof field !== "string")) { 8332 return error.value; 8333 } 8334 var resultIndexes = findResultIndex(database, criteria); 8335 var targetFields = []; 8336 if (typeof field === "string") { 8337 var index = exports.FINDFIELD(database, field); 8338 targetFields = utils.rest(database[index]); 8339 } else { 8340 targetFields = utils.rest(database[field]); 8341 } 8342 // Return error if no record meets the criteria 8343 if (resultIndexes.length === 0) { 8344 return error.value; 8345 } 8346 // Returns the #NUM! error value because more than one record meets the 8347 // criteria 8348 if (resultIndexes.length > 1) { 8349 return error.num; 8350 } 8351 8352 return targetFields[resultIndexes[0]]; 8353 }; 8354 8355 exports.DMAX = function(database, field, criteria) { 8356 // Return error if field is not a number and not a string 8357 if (isNaN(field) && (typeof field !== "string")) { 8358 return error.value; 8359 } 8360 var resultIndexes = findResultIndex(database, criteria); 8361 var targetFields = []; 8362 if (typeof field === "string") { 8363 var index = exports.FINDFIELD(database, field); 8364 targetFields = utils.rest(database[index]); 8365 } else { 8366 targetFields = utils.rest(database[field]); 8367 } 8368 var maxValue = targetFields[resultIndexes[0]]; 8369 for (var i = 1; i < resultIndexes.length; i++) { 8370 if (maxValue < targetFields[resultIndexes[i]]) { 8371 maxValue = targetFields[resultIndexes[i]]; 8372 } 8373 } 8374 return maxValue; 8375 }; 8376 8377 exports.DMIN = function(database, field, criteria) { 8378 // Return error if field is not a number and not a string 8379 if (isNaN(field) && (typeof field !== "string")) { 8380 return error.value; 8381 } 8382 var resultIndexes = findResultIndex(database, criteria); 8383 var targetFields = []; 8384 if (typeof field === "string") { 8385 var index = exports.FINDFIELD(database, field); 8386 targetFields = utils.rest(database[index]); 8387 } else { 8388 targetFields = utils.rest(database[field]); 8389 } 8390 var minValue = targetFields[resultIndexes[0]]; 8391 for (var i = 1; i < resultIndexes.length; i++) { 8392 if (minValue > targetFields[resultIndexes[i]]) { 8393 minValue = targetFields[resultIndexes[i]]; 8394 } 8395 } 8396 return minValue; 8397 }; 8398 8399 exports.DPRODUCT = function(database, field, criteria) { 8400 // Return error if field is not a number and not a string 8401 if (isNaN(field) && (typeof field !== "string")) { 8402 return error.value; 8403 } 8404 var resultIndexes = findResultIndex(database, criteria); 8405 var targetFields = []; 8406 if (typeof field === "string") { 8407 var index = exports.FINDFIELD(database, field); 8408 targetFields = utils.rest(database[index]); 8409 } else { 8410 targetFields = utils.rest(database[field]); 8411 } 8412 var targetValues = []; 8413 for (var i = 0; i < resultIndexes.length; i++) { 8414 targetValues[i] = targetFields[resultIndexes[i]]; 8415 } 8416 targetValues = compact(targetValues); 8417 var result = 1; 8418 for (i = 0; i < targetValues.length; i++) { 8419 result *= targetValues[i]; 8420 } 8421 return result; 8422 }; 8423 8424 exports.DSTDEV = function(database, field, criteria) { 8425 }; 8426 8427 exports.DSTDEVP = function(database, field, criteria) { 8428 }; 8429 8430 exports.DSUM = function(database, field, criteria) { 8431 }; 8432 8433 exports.DVAR = function(database, field, criteria) { 8434 }; 8435 8436 exports.DVARP = function(database, field, criteria) { 8437 }; 8438 8439 exports.MATCH = function(lookupValue, lookupArray, matchType) { 8440 if (!lookupValue && !lookupArray) { 8441 return error.na; 8442 } 8443 if (arguments.length === 2) { 8444 matchType = 1; 8445 } 8446 if (!(lookupArray instanceof Array)) { 8447 return error.na; 8448 } 8449 if (matchType !== -1 && matchType !== 0 && matchType !== 1) { 8450 return error.na; 8451 } 8452 8453 var index; 8454 var indexValue; 8455 8456 for (var idx = 0; idx < lookupArray.length; idx++) { 8457 if (matchType === 1) { 8458 if (lookupArray[idx] === lookupValue) { 8459 return idx + 1; 8460 } else if (lookupArray[idx] < lookupValue) { 8461 if (!indexValue) { 8462 index = idx + 1; 8463 indexValue = lookupArray[idx]; 8464 } else if (lookupArray[idx] > indexValue) { 8465 index = idx + 1; 8466 indexValue = lookupArray[idx]; 8467 } 8468 } 8469 } else if (matchType === 0) { 8470 if (typeof lookupValue === 'string') { 8471 lookupValue = lookupValue.replace(/\?/g, '.'); 8472 if (lookupArray[idx].toLowerCase().match(lookupValue.toLowerCase())) { 8473 return idx + 1; 8474 } 8475 } else { 8476 if (lookupArray[idx] === lookupValue) { 8477 return idx + 1; 8478 } 8479 } 8480 } else if (matchType === -1) { 8481 if (lookupArray[idx] === lookupValue) { 8482 return idx + 1; 8483 } else if (lookupArray[idx] > lookupValue) { 8484 if (!indexValue) { 8485 index = idx + 1; 8486 indexValue = lookupArray[idx]; 8487 } else if (lookupArray[idx] < indexValue) { 8488 index = idx + 1; 8489 indexValue = lookupArray[idx]; 8490 } 8491 } 8492 } 8493 } 8494 8495 return index ? index : error.na; 8496 }; 8497 8498 return exports; 8499 })(); 8500 8501 jexcel.methods.engineering = (function() { 8502 var exports = {}; 8503 8504 function isValidBinaryNumber(number) { 8505 return (/^[01]{1,10}$/).test(number); 8506 } 8507 8508 exports.BESSELI = function(x, n) { 8509 }; 8510 8511 exports.BESSELJ = function(x, n) { 8512 }; 8513 8514 exports.BESSELK = function(x, n) { 8515 }; 8516 8517 exports.BESSELY = function(x, n) { 8518 }; 8519 8520 exports.BIN2DEC = function(number) { 8521 // Return error if number is not binary or contains more than 10 8522 // characters (10 digits) 8523 if (!isValidBinaryNumber(number)) { 8524 return error.num; 8525 } 8526 8527 // Convert binary number to decimal 8528 var result = parseInt(number, 2); 8529 8530 // Handle negative numbers 8531 var stringified = number.toString(); 8532 if (stringified.length === 10 && stringified.substring(0, 1) === '1') { 8533 return parseInt(stringified.substring(1), 2) - 512; 8534 } else { 8535 return result; 8536 } 8537 }; 8538 8539 exports.BIN2HEX = function(number, places) { 8540 // Return error if number is not binary or contains more than 10 8541 // characters (10 digits) 8542 if (!isValidBinaryNumber(number)) { 8543 return error.num; 8544 } 8545 8546 // Ignore places and return a 10-character hexadecimal number if number 8547 // is negative 8548 var stringified = number.toString(); 8549 if (stringified.length === 10 && stringified.substring(0, 1) === '1') { 8550 return (1099511627264 + parseInt(stringified.substring(1), 2)).toString(16); 8551 } 8552 8553 // Convert binary number to hexadecimal 8554 var result = parseInt(number, 2).toString(16); 8555 8556 // Return hexadecimal number using the minimum number of characters 8557 // necessary if places is undefined 8558 if (places === undefined) { 8559 return result; 8560 } else { 8561 // Return error if places is nonnumeric 8562 if (isNaN(places)) { 8563 return error.value; 8564 } 8565 8566 // Return error if places is negative 8567 if (places < 0) { 8568 return error.num; 8569 } 8570 8571 // Truncate places in case it is not an integer 8572 places = Math.floor(places); 8573 8574 // Pad return value with leading 0s (zeros) if necessary (using 8575 // Underscore.string) 8576 return (places >= result.length) ? REPT('0', places - result.length) + result : error.num; 8577 } 8578 }; 8579 8580 exports.BIN2OCT = function(number, places) { 8581 // Return error if number is not binary or contains more than 10 8582 // characters (10 digits) 8583 if (!isValidBinaryNumber(number)) { 8584 return error.num; 8585 } 8586 8587 // Ignore places and return a 10-character octal number if number is 8588 // negative 8589 var stringified = number.toString(); 8590 if (stringified.length === 10 && stringified.substring(0, 1) === '1') { 8591 return (1073741312 + parseInt(stringified.substring(1), 2)).toString(8); 8592 } 8593 8594 // Convert binary number to octal 8595 var result = parseInt(number, 2).toString(8); 8596 8597 // Return octal number using the minimum number of characters necessary 8598 // if places is undefined 8599 if (places === undefined) { 8600 return result; 8601 } else { 8602 // Return error if places is nonnumeric 8603 if (isNaN(places)) { 8604 return error.value; 8605 } 8606 8607 // Return error if places is negative 8608 if (places < 0) { 8609 return error.num; 8610 } 8611 8612 // Truncate places in case it is not an integer 8613 places = Math.floor(places); 8614 8615 // Pad return value with leading 0s (zeros) if necessary (using 8616 // Underscore.string) 8617 return (places >= result.length) ? REPT('0', places - result.length) + result : error.num; 8618 } 8619 }; 8620 8621 exports.BITAND = function(number1, number2) { 8622 // Return error if either number is a non-numeric value 8623 number1 = utils.parseNumber(number1); 8624 number2 = utils.parseNumber(number2); 8625 if (utils.anyIsError(number1, number2)) { 8626 return error.value; 8627 } 8628 8629 // Return error if either number is less than 0 8630 if (number1 < 0 || number2 < 0) { 8631 return error.num; 8632 } 8633 8634 // Return error if either number is a non-integer 8635 if (Math.floor(number1) !== number1 || Math.floor(number2) !== number2) { 8636 return error.num; 8637 } 8638 8639 // Return error if either number is greater than (2^48)-1 8640 if (number1 > 281474976710655 || number2 > 281474976710655) { 8641 return error.num; 8642 } 8643 8644 // Return bitwise AND of two numbers 8645 return number1 & number2; 8646 }; 8647 8648 exports.BITLSHIFT = function(number, shift) { 8649 number = utils.parseNumber(number); 8650 shift = utils.parseNumber(shift); 8651 if (utils.anyIsError(number, shift)) { 8652 return error.value; 8653 } 8654 8655 // Return error if number is less than 0 8656 if (number < 0) { 8657 return error.num; 8658 } 8659 8660 // Return error if number is a non-integer 8661 if (Math.floor(number) !== number) { 8662 return error.num; 8663 } 8664 8665 // Return error if number is greater than (2^48)-1 8666 if (number > 281474976710655) { 8667 return error.num; 8668 } 8669 8670 // Return error if the absolute value of shift is greater than 53 8671 if (Math.abs(shift) > 53) { 8672 return error.num; 8673 } 8674 8675 // Return number shifted by shift bits to the left or to the right if 8676 // shift is negative 8677 return (shift >= 0) ? number << shift : number >> -shift; 8678 }; 8679 8680 exports.BITOR = function(number1, number2) { 8681 number1 = utils.parseNumber(number1); 8682 number2 = utils.parseNumber(number2); 8683 if (utils.anyIsError(number1, number2)) { 8684 return error.value; 8685 } 8686 8687 // Return error if either number is less than 0 8688 if (number1 < 0 || number2 < 0) { 8689 return error.num; 8690 } 8691 8692 // Return error if either number is a non-integer 8693 if (Math.floor(number1) !== number1 || Math.floor(number2) !== number2) { 8694 return error.num; 8695 } 8696 8697 // Return error if either number is greater than (2^48)-1 8698 if (number1 > 281474976710655 || number2 > 281474976710655) { 8699 return error.num; 8700 } 8701 8702 // Return bitwise OR of two numbers 8703 return number1 | number2; 8704 }; 8705 8706 exports.BITRSHIFT = function(number, shift) { 8707 number = utils.parseNumber(number); 8708 shift = utils.parseNumber(shift); 8709 if (utils.anyIsError(number, shift)) { 8710 return error.value; 8711 } 8712 8713 // Return error if number is less than 0 8714 if (number < 0) { 8715 return error.num; 8716 } 8717 8718 // Return error if number is a non-integer 8719 if (Math.floor(number) !== number) { 8720 return error.num; 8721 } 8722 8723 // Return error if number is greater than (2^48)-1 8724 if (number > 281474976710655) { 8725 return error.num; 8726 } 8727 8728 // Return error if the absolute value of shift is greater than 53 8729 if (Math.abs(shift) > 53) { 8730 return error.num; 8731 } 8732 8733 // Return number shifted by shift bits to the right or to the left if 8734 // shift is negative 8735 return (shift >= 0) ? number >> shift : number << -shift; 8736 }; 8737 8738 exports.BITXOR = function(number1, number2) { 8739 number1 = utils.parseNumber(number1); 8740 number2 = utils.parseNumber(number2); 8741 if (utils.anyIsError(number1, number2)) { 8742 return error.value; 8743 } 8744 8745 // Return error if either number is less than 0 8746 if (number1 < 0 || number2 < 0) { 8747 return error.num; 8748 } 8749 8750 // Return error if either number is a non-integer 8751 if (Math.floor(number1) !== number1 || Math.floor(number2) !== number2) { 8752 return error.num; 8753 } 8754 8755 // Return error if either number is greater than (2^48)-1 8756 if (number1 > 281474976710655 || number2 > 281474976710655) { 8757 return error.num; 8758 } 8759 8760 // Return bitwise XOR of two numbers 8761 return number1 ^ number2; 8762 }; 8763 8764 exports.COMPLEX = function(real, imaginary, suffix) { 8765 real = utils.parseNumber(real); 8766 imaginary = utils.parseNumber(imaginary); 8767 if (utils.anyIsError(real, imaginary)) { 8768 return real; 8769 } 8770 8771 // Set suffix 8772 suffix = (suffix === undefined) ? 'i' : suffix; 8773 8774 // Return error if suffix is neither "i" nor "j" 8775 if (suffix !== 'i' && suffix !== 'j') { 8776 return error.value; 8777 } 8778 8779 // Return complex number 8780 if (real === 0 && imaginary === 0) { 8781 return 0; 8782 } else if (real === 0) { 8783 return (imaginary === 1) ? suffix : imaginary.toString() + suffix; 8784 } else if (imaginary === 0) { 8785 return real.toString(); 8786 } else { 8787 var sign = (imaginary > 0) ? '+' : ''; 8788 return real.toString() + sign + ((imaginary === 1) ? suffix : imaginary.toString() + suffix); 8789 } 8790 }; 8791 8792 exports.CONVERT = function(number, from_unit, to_unit) { 8793 number = utils.parseNumber(number); 8794 if (number instanceof Error) { 8795 return number; 8796 } 8797 8798 // List of units supported by CONVERT and units defined by the 8799 // International System of Units 8800 // [Name, Symbol, Alternate symbols, Quantity, ISU, CONVERT, Conversion 8801 // ratio] 8802 var units = [ 8803 ["a.u. of action", "?", null, "action", false, false, 1.05457168181818e-34], 8804 ["a.u. of charge", "e", null, "electric_charge", false, false, 1.60217653141414e-19], 8805 ["a.u. of energy", "Eh", null, "energy", false, false, 4.35974417757576e-18], 8806 ["a.u. of length", "a?", null, "length", false, false, 5.29177210818182e-11], 8807 ["a.u. of mass", "m?", null, "mass", false, false, 9.10938261616162e-31], 8808 ["a.u. of time", "?/Eh", null, "time", false, false, 2.41888432650516e-17], 8809 ["admiralty knot", "admkn", null, "speed", false, true, 0.514773333], 8810 ["ampere", "A", null, "electric_current", true, false, 1], 8811 ["ampere per meter", "A/m", null, "magnetic_field_intensity", true, false, 1], 8812 ["Ã¥ngström", "à ", ["ang"], "length", false, true, 1e-10], 8813 ["are", "ar", null, "area", false, true, 100], 8814 ["astronomical unit", "ua", null, "length", false, false, 1.49597870691667e-11], 8815 ["bar", "bar", null, "pressure", false, false, 100000], 8816 ["barn", "b", null, "area", false, false, 1e-28], 8817 ["becquerel", "Bq", null, "radioactivity", true, false, 1], 8818 ["bit", "bit", ["b"], "information", false, true, 1], 8819 ["btu", "BTU", ["btu"], "energy", false, true, 1055.05585262], 8820 ["byte", "byte", null, "information", false, true, 8], 8821 ["candela", "cd", null, "luminous_intensity", true, false, 1], 8822 ["candela per square metre", "cd/m?", null, "luminance", true, false, 1], 8823 ["coulomb", "C", null, "electric_charge", true, false, 1], 8824 ["cubic Ã¥ngström", "ang3", ["ang^3"], "volume", false, true, 1e-30], 8825 ["cubic foot", "ft3", ["ft^3"], "volume", false, true, 0.028316846592], 8826 ["cubic inch", "in3", ["in^3"], "volume", false, true, 0.000016387064], 8827 ["cubic light-year", "ly3", ["ly^3"], "volume", false, true, 8.46786664623715e-47], 8828 ["cubic metre", "m?", null, "volume", true, true, 1], 8829 ["cubic mile", "mi3", ["mi^3"], "volume", false, true, 4168181825.44058], 8830 ["cubic nautical mile", "Nmi3", ["Nmi^3"], "volume", false, true, 6352182208], 8831 ["cubic Pica", "Pica3", ["Picapt3", "Pica^3", "Picapt^3"], "volume", false, true, 7.58660370370369e-8], 8832 ["cubic yard", "yd3", ["yd^3"], "volume", false, true, 0.764554857984], 8833 ["cup", "cup", null, "volume", false, true, 0.0002365882365], 8834 ["dalton", "Da", ["u"], "mass", false, false, 1.66053886282828e-27], 8835 ["day", "d", ["day"], "time", false, true, 86400], 8836 ["degree", "°", null, "angle", false, false, 0.0174532925199433], 8837 ["degrees Rankine", "Rank", null, "temperature", false, true, 0.555555555555556], 8838 ["dyne", "dyn", ["dy"], "force", false, true, 0.00001], 8839 ["electronvolt", "eV", ["ev"], "energy", false, true, 1.60217656514141], 8840 ["ell", "ell", null, "length", false, true, 1.143], 8841 ["erg", "erg", ["e"], "energy", false, true, 1e-7], 8842 ["farad", "F", null, "electric_capacitance", true, false, 1], 8843 ["fluid ounce", "oz", null, "volume", false, true, 0.0000295735295625], 8844 ["foot", "ft", null, "length", false, true, 0.3048], 8845 ["foot-pound", "flb", null, "energy", false, true, 1.3558179483314], 8846 ["gal", "Gal", null, "acceleration", false, false, 0.01], 8847 ["gallon", "gal", null, "volume", false, true, 0.003785411784], 8848 ["gauss", "G", ["ga"], "magnetic_flux_density", false, true, 1], 8849 ["grain", "grain", null, "mass", false, true, 0.0000647989], 8850 ["gram", "g", null, "mass", false, true, 0.001], 8851 ["gray", "Gy", null, "absorbed_dose", true, false, 1], 8852 ["gross registered ton", "GRT", ["regton"], "volume", false, true, 2.8316846592], 8853 ["hectare", "ha", null, "area", false, true, 10000], 8854 ["henry", "H", null, "inductance", true, false, 1], 8855 ["hertz", "Hz", null, "frequency", true, false, 1], 8856 ["horsepower", "HP", ["h"], "power", false, true, 745.69987158227], 8857 ["horsepower-hour", "HPh", ["hh", "hph"], "energy", false, true, 2684519.538], 8858 ["hour", "h", ["hr"], "time", false, true, 3600], 8859 ["imperial gallon (U.K.)", "uk_gal", null, "volume", false, true, 0.00454609], 8860 ["imperial hundredweight", "lcwt", ["uk_cwt", "hweight"], "mass", false, true, 50.802345], 8861 ["imperial quart (U.K)", "uk_qt", null, "volume", false, true, 0.0011365225], 8862 ["imperial ton", "brton", ["uk_ton", "LTON"], "mass", false, true, 1016.046909], 8863 ["inch", "in", null, "length", false, true, 0.0254], 8864 ["international acre", "uk_acre", null, "area", false, true, 4046.8564224], 8865 ["IT calorie", "cal", null, "energy", false, true, 4.1868], 8866 ["joule", "J", null, "energy", true, true, 1], 8867 ["katal", "kat", null, "catalytic_activity", true, false, 1], 8868 ["kelvin", "K", ["kel"], "temperature", true, true, 1], 8869 ["kilogram", "kg", null, "mass", true, true, 1], 8870 ["knot", "kn", null, "speed", false, true, 0.514444444444444], 8871 ["light-year", "ly", null, "length", false, true, 9460730472580800], 8872 ["litre", "L", ["l", "lt"], "volume", false, true, 0.001], 8873 ["lumen", "lm", null, "luminous_flux", true, false, 1], 8874 ["lux", "lx", null, "illuminance", true, false, 1], 8875 ["maxwell", "Mx", null, "magnetic_flux", false, false, 1e-18], 8876 ["measurement ton", "MTON", null, "volume", false, true, 1.13267386368], 8877 ["meter per hour", "m/h", ["m/hr"], "speed", false, true, 0.00027777777777778], 8878 ["meter per second", "m/s", ["m/sec"], "speed", true, true, 1], 8879 ["meter per second squared", "m?s??", null, "acceleration", true, false, 1], 8880 ["parsec", "pc", ["parsec"], "length", false, true, 30856775814671900], 8881 ["meter squared per second", "m?/s", null, "kinematic_viscosity", true, false, 1], 8882 ["metre", "m", null, "length", true, true, 1], 8883 ["miles per hour", "mph", null, "speed", false, true, 0.44704], 8884 ["millimetre of mercury", "mmHg", null, "pressure", false, false, 133.322], 8885 ["minute", "?", null, "angle", false, false, 0.000290888208665722], 8886 ["minute", "min", ["mn"], "time", false, true, 60], 8887 ["modern teaspoon", "tspm", null, "volume", false, true, 0.000005], 8888 ["mole", "mol", null, "amount_of_substance", true, false, 1], 8889 ["morgen", "Morgen", null, "area", false, true, 2500], 8890 ["n.u. of action", "?", null, "action", false, false, 1.05457168181818e-34], 8891 ["n.u. of mass", "m?", null, "mass", false, false, 9.10938261616162e-31], 8892 ["n.u. of speed", "c?", null, "speed", false, false, 299792458], 8893 ["n.u. of time", "?/(me?c??)", null, "time", false, false, 1.28808866778687e-21], 8894 ["nautical mile", "M", ["Nmi"], "length", false, true, 1852], 8895 ["newton", "N", null, "force", true, true, 1], 8896 ["Årsted", "Oe ", null, "magnetic_field_intensity", false, false, 79.5774715459477], 8897 ["ohm", "Ω", null, "electric_resistance", true, false, 1], 8898 ["ounce mass", "ozm", null, "mass", false, true, 0.028349523125], 8899 ["pascal", "Pa", null, "pressure", true, false, 1], 8900 ["pascal second", "Pa?s", null, "dynamic_viscosity", true, false, 1], 8901 ["pferdestärke", "PS", null, "power", false, true, 735.49875], 8902 ["phot", "ph", null, "illuminance", false, false, 0.0001], 8903 ["pica (1/6 inch)", "pica", null, "length", false, true, 0.00035277777777778], 8904 ["pica (1/72 inch)", "Pica", ["Picapt"], "length", false, true, 0.00423333333333333], 8905 ["poise", "P", null, "dynamic_viscosity", false, false, 0.1], 8906 ["pond", "pond", null, "force", false, true, 0.00980665], 8907 ["pound force", "lbf", null, "force", false, true, 4.4482216152605], 8908 ["pound mass", "lbm", null, "mass", false, true, 0.45359237], 8909 ["quart", "qt", null, "volume", false, true, 0.000946352946], 8910 ["radian", "rad", null, "angle", true, false, 1], 8911 ["second", "?", null, "angle", false, false, 0.00000484813681109536], 8912 ["second", "s", ["sec"], "time", true, true, 1], 8913 ["short hundredweight", "cwt", ["shweight"], "mass", false, true, 45.359237], 8914 ["siemens", "S", null, "electrical_conductance", true, false, 1], 8915 ["sievert", "Sv", null, "equivalent_dose", true, false, 1], 8916 ["slug", "sg", null, "mass", false, true, 14.59390294], 8917 ["square Ã¥ngström", "ang2", ["ang^2"], "area", false, true, 1e-20], 8918 ["square foot", "ft2", ["ft^2"], "area", false, true, 0.09290304], 8919 ["square inch", "in2", ["in^2"], "area", false, true, 0.00064516], 8920 ["square light-year", "ly2", ["ly^2"], "area", false, true, 8.95054210748189e+31], 8921 ["square meter", "m?", null, "area", true, true, 1], 8922 ["square mile", "mi2", ["mi^2"], "area", false, true, 2589988.110336], 8923 ["square nautical mile", "Nmi2", ["Nmi^2"], "area", false, true, 3429904], 8924 ["square Pica", "Pica2", ["Picapt2", "Pica^2", "Picapt^2"], "area", false, true, 0.00001792111111111], 8925 ["square yard", "yd2", ["yd^2"], "area", false, true, 0.83612736], 8926 ["statute mile", "mi", null, "length", false, true, 1609.344], 8927 ["steradian", "sr", null, "solid_angle", true, false, 1], 8928 ["stilb", "sb", null, "luminance", false, false, 0.0001], 8929 ["stokes", "St", null, "kinematic_viscosity", false, false, 0.0001], 8930 ["stone", "stone", null, "mass", false, true, 6.35029318], 8931 ["tablespoon", "tbs", null, "volume", false, true, 0.0000147868], 8932 ["teaspoon", "tsp", null, "volume", false, true, 0.00000492892], 8933 ["tesla", "T", null, "magnetic_flux_density", true, true, 1], 8934 ["thermodynamic calorie", "c", null, "energy", false, true, 4.184], 8935 ["ton", "ton", null, "mass", false, true, 907.18474], 8936 ["tonne", "t", null, "mass", false, false, 1000], 8937 ["U.K. pint", "uk_pt", null, "volume", false, true, 0.00056826125], 8938 ["U.S. bushel", "bushel", null, "volume", false, true, 0.03523907], 8939 ["U.S. oil barrel", "barrel", null, "volume", false, true, 0.158987295], 8940 ["U.S. pint", "pt", ["us_pt"], "volume", false, true, 0.000473176473], 8941 ["U.S. survey mile", "survey_mi", null, "length", false, true, 1609.347219], 8942 ["U.S. survey/statute acre", "us_acre", null, "area", false, true, 4046.87261], 8943 ["volt", "V", null, "voltage", true, false, 1], 8944 ["watt", "W", null, "power", true, true, 1], 8945 ["watt-hour", "Wh", ["wh"], "energy", false, true, 3600], 8946 ["weber", "Wb", null, "magnetic_flux", true, false, 1], 8947 ["yard", "yd", null, "length", false, true, 0.9144], 8948 ["year", "yr", null, "time", false, true, 31557600] 8949 ]; 8950 8951 // Binary prefixes 8952 // [Name, Prefix power of 2 value, Previx value, Abbreviation, Derived 8953 // from] 8954 var binary_prefixes = { 8955 Yi: ["yobi", 80, 1208925819614629174706176, "Yi", "yotta"], 8956 Zi: ["zebi", 70, 1180591620717411303424, "Zi", "zetta"], 8957 Ei: ["exbi", 60, 1152921504606846976, "Ei", "exa"], 8958 Pi: ["pebi", 50, 1125899906842624, "Pi", "peta"], 8959 Ti: ["tebi", 40, 1099511627776, "Ti", "tera"], 8960 Gi: ["gibi", 30, 1073741824, "Gi", "giga"], 8961 Mi: ["mebi", 20, 1048576, "Mi", "mega"], 8962 ki: ["kibi", 10, 1024, "ki", "kilo"] 8963 }; 8964 8965 // Unit prefixes 8966 // [Name, Multiplier, Abbreviation] 8967 var unit_prefixes = { 8968 Y: ["yotta", 1e+24, "Y"], 8969 Z: ["zetta", 1e+21, "Z"], 8970 E: ["exa", 1e+18, "E"], 8971 P: ["peta", 1e+15, "P"], 8972 T: ["tera", 1e+12, "T"], 8973 G: ["giga", 1e+09, "G"], 8974 M: ["mega", 1e+06, "M"], 8975 k: ["kilo", 1e+03, "k"], 8976 h: ["hecto", 1e+02, "h"], 8977 e: ["dekao", 1e+01, "e"], 8978 d: ["deci", 1e-01, "d"], 8979 c: ["centi", 1e-02, "c"], 8980 m: ["milli", 1e-03, "m"], 8981 u: ["micro", 1e-06, "u"], 8982 n: ["nano", 1e-09, "n"], 8983 p: ["pico", 1e-12, "p"], 8984 f: ["femto", 1e-15, "f"], 8985 a: ["atto", 1e-18, "a"], 8986 z: ["zepto", 1e-21, "z"], 8987 y: ["yocto", 1e-24, "y"] 8988 }; 8989 8990 // Initialize units and multipliers 8991 var from = null; 8992 var to = null; 8993 var base_from_unit = from_unit; 8994 var base_to_unit = to_unit; 8995 var from_multiplier = 1; 8996 var to_multiplier = 1; 8997 var alt; 8998 8999 // Lookup from and to units 9000 for (var i = 0; i < units.length; i++) { 9001 alt = (units[i][2] === null) ? [] : units[i][2]; 9002 if (units[i][1] === base_from_unit || alt.indexOf(base_from_unit) >= 0) { 9003 from = units[i]; 9004 } 9005 if (units[i][1] === base_to_unit || alt.indexOf(base_to_unit) >= 0) { 9006 to = units[i]; 9007 } 9008 } 9009 9010 // Lookup from prefix 9011 if (from === null) { 9012 var from_binary_prefix = binary_prefixes[from_unit.substring(0, 2)]; 9013 var from_unit_prefix = unit_prefixes[from_unit.substring(0, 1)]; 9014 9015 // Handle dekao unit prefix (only unit prefix with two characters) 9016 if (from_unit.substring(0, 2) === 'da') { 9017 from_unit_prefix = ["dekao", 1e+01, "da"]; 9018 } 9019 9020 // Handle binary prefixes first (so that 'Yi' is processed before 9021 // 'Y') 9022 if (from_binary_prefix) { 9023 from_multiplier = from_binary_prefix[2]; 9024 base_from_unit = from_unit.substring(2); 9025 } else if (from_unit_prefix) { 9026 from_multiplier = from_unit_prefix[1]; 9027 base_from_unit = from_unit.substring(from_unit_prefix[2].length); 9028 } 9029 9030 // Lookup from unit 9031 for (var j = 0; j < units.length; j++) { 9032 alt = (units[j][2] === null) ? [] : units[j][2]; 9033 if (units[j][1] === base_from_unit || alt.indexOf(base_from_unit) >= 0) { 9034 from = units[j]; 9035 } 9036 } 9037 } 9038 9039 // Lookup to prefix 9040 if (to === null) { 9041 var to_binary_prefix = binary_prefixes[to_unit.substring(0, 2)]; 9042 var to_unit_prefix = unit_prefixes[to_unit.substring(0, 1)]; 9043 9044 // Handle dekao unit prefix (only unit prefix with two characters) 9045 if (to_unit.substring(0, 2) === 'da') { 9046 to_unit_prefix = ["dekao", 1e+01, "da"]; 9047 } 9048 9049 // Handle binary prefixes first (so that 'Yi' is processed before 9050 // 'Y') 9051 if (to_binary_prefix) { 9052 to_multiplier = to_binary_prefix[2]; 9053 base_to_unit = to_unit.substring(2); 9054 } else if (to_unit_prefix) { 9055 to_multiplier = to_unit_prefix[1]; 9056 base_to_unit = to_unit.substring(to_unit_prefix[2].length); 9057 } 9058 9059 // Lookup to unit 9060 for (var k = 0; k < units.length; k++) { 9061 alt = (units[k][2] === null) ? [] : units[k][2]; 9062 if (units[k][1] === base_to_unit || alt.indexOf(base_to_unit) >= 0) { 9063 to = units[k]; 9064 } 9065 } 9066 } 9067 9068 // Return error if a unit does not exist 9069 if (from === null || to === null) { 9070 return error.na; 9071 } 9072 9073 // Return error if units represent different quantities 9074 if (from[3] !== to[3]) { 9075 return error.na; 9076 } 9077 9078 // Return converted number 9079 return number * from[6] * from_multiplier / (to[6] * to_multiplier); 9080 }; 9081 9082 exports.DEC2BIN = function(number, places) { 9083 number = utils.parseNumber(number); 9084 if (number instanceof Error) { 9085 return number; 9086 } 9087 9088 // Return error if number is not decimal, is lower than -512, or is 9089 // greater than 511 9090 if (!/^-?[0-9]{1,3}$/.test(number) || number < -512 || number > 511) { 9091 return error.num; 9092 } 9093 9094 // Ignore places and return a 10-character binary number if number is 9095 // negative 9096 if (number < 0) { 9097 return '1' + REPT('0', 9 - (512 + number).toString(2).length) + (512 + number).toString(2); 9098 } 9099 9100 // Convert decimal number to binary 9101 var result = parseInt(number, 10).toString(2); 9102 9103 // Return binary number using the minimum number of characters necessary 9104 // if places is undefined 9105 if (typeof places === 'undefined') { 9106 return result; 9107 } else { 9108 // Return error if places is nonnumeric 9109 if (isNaN(places)) { 9110 return error.value; 9111 } 9112 9113 // Return error if places is negative 9114 if (places < 0) { 9115 return error.num; 9116 } 9117 9118 // Truncate places in case it is not an integer 9119 places = Math.floor(places); 9120 9121 // Pad return value with leading 0s (zeros) if necessary (using 9122 // Underscore.string) 9123 return (places >= result.length) ? REPT('0', places - result.length) + result : error.num; 9124 } 9125 }; 9126 9127 exports.DEC2HEX = function(number, places) { 9128 number = utils.parseNumber(number); 9129 if (number instanceof Error) { 9130 return number; 9131 } 9132 9133 // Return error if number is not decimal, is lower than -549755813888, 9134 // or is greater than 549755813887 9135 if (!/^-?[0-9]{1,12}$/.test(number) || number < -549755813888 || number > 549755813887) { 9136 return error.num; 9137 } 9138 9139 // Ignore places and return a 10-character hexadecimal number if number 9140 // is negative 9141 if (number < 0) { 9142 return (1099511627776 + number).toString(16); 9143 } 9144 9145 // Convert decimal number to hexadecimal 9146 var result = parseInt(number, 10).toString(16); 9147 9148 // Return hexadecimal number using the minimum number of characters 9149 // necessary if places is undefined 9150 if (typeof places === 'undefined') { 9151 return result; 9152 } else { 9153 // Return error if places is nonnumeric 9154 if (isNaN(places)) { 9155 return error.value; 9156 } 9157 9158 // Return error if places is negative 9159 if (places < 0) { 9160 return error.num; 9161 } 9162 9163 // Truncate places in case it is not an integer 9164 places = Math.floor(places); 9165 9166 // Pad return value with leading 0s (zeros) if necessary (using 9167 // Underscore.string) 9168 return (places >= result.length) ? REPT('0', places - result.length) + result : error.num; 9169 } 9170 }; 9171 9172 exports.DEC2OCT = function(number, places) { 9173 number = utils.parseNumber(number); 9174 if (number instanceof Error) { 9175 return number; 9176 } 9177 9178 // Return error if number is not decimal, is lower than -549755813888, 9179 // or is greater than 549755813887 9180 if (!/^-?[0-9]{1,9}$/.test(number) || number < -536870912 || number > 536870911) { 9181 return error.num; 9182 } 9183 9184 // Ignore places and return a 10-character octal number if number is 9185 // negative 9186 if (number < 0) { 9187 return (1073741824 + number).toString(8); 9188 } 9189 9190 // Convert decimal number to octal 9191 var result = parseInt(number, 10).toString(8); 9192 9193 // Return octal number using the minimum number of characters necessary 9194 // if places is undefined 9195 if (typeof places === 'undefined') { 9196 return result; 9197 } else { 9198 // Return error if places is nonnumeric 9199 if (isNaN(places)) { 9200 return error.value; 9201 } 9202 9203 // Return error if places is negative 9204 if (places < 0) { 9205 return error.num; 9206 } 9207 9208 // Truncate places in case it is not an integer 9209 places = Math.floor(places); 9210 9211 // Pad return value with leading 0s (zeros) if necessary (using 9212 // Underscore.string) 9213 return (places >= result.length) ? REPT('0', places - result.length) + result : error.num; 9214 } 9215 }; 9216 9217 exports.DELTA = function(number1, number2) { 9218 // Set number2 to zero if undefined 9219 number2 = (number2 === undefined) ? 0 : number2; 9220 number1 = utils.parseNumber(number1); 9221 number2 = utils.parseNumber(number2); 9222 if (utils.anyIsError(number1, number2)) { 9223 return error.value; 9224 } 9225 9226 // Return delta 9227 return (number1 === number2) ? 1 : 0; 9228 }; 9229 9230 exports.ERF = function(lower_bound, upper_bound) { 9231 }; 9232 9233 exports.ERF.PRECISE = function() { 9234 }; 9235 9236 exports.ERFC = function(x) { 9237 }; 9238 9239 exports.ERFC.PRECISE = function() { 9240 }; 9241 9242 exports.GESTEP = function(number, step) { 9243 step = step || 0; 9244 number = utils.parseNumber(number); 9245 if (utils.anyIsError(step, number)) { 9246 return number; 9247 } 9248 9249 // Return delta 9250 return (number >= step) ? 1 : 0; 9251 }; 9252 9253 exports.HEX2BIN = function(number, places) { 9254 // Return error if number is not hexadecimal or contains more than ten 9255 // characters (10 digits) 9256 if (!/^[0-9A-Fa-f]{1,10}$/.test(number)) { 9257 return error.num; 9258 } 9259 9260 // Check if number is negative 9261 var negative = (number.length === 10 && number.substring(0, 1).toLowerCase() === 'f') ? true : false; 9262 9263 // Convert hexadecimal number to decimal 9264 var decimal = (negative) ? parseInt(number, 16) - 1099511627776 : parseInt(number, 16); 9265 9266 // Return error if number is lower than -512 or greater than 511 9267 if (decimal < -512 || decimal > 511) { 9268 return error.num; 9269 } 9270 9271 // Ignore places and return a 10-character binary number if number is 9272 // negative 9273 if (negative) { 9274 return '1' + REPT('0', 9 - (512 + decimal).toString(2).length) + (512 + decimal).toString(2); 9275 } 9276 9277 // Convert decimal number to binary 9278 var result = decimal.toString(2); 9279 9280 // Return binary number using the minimum number of characters necessary 9281 // if places is undefined 9282 if (places === undefined) { 9283 return result; 9284 } else { 9285 // Return error if places is nonnumeric 9286 if (isNaN(places)) { 9287 return error.value; 9288 } 9289 9290 // Return error if places is negative 9291 if (places < 0) { 9292 return error.num; 9293 } 9294 9295 // Truncate places in case it is not an integer 9296 places = Math.floor(places); 9297 9298 // Pad return value with leading 0s (zeros) if necessary (using 9299 // Underscore.string) 9300 return (places >= result.length) ? REPT('0', places - result.length) + result : error.num; 9301 } 9302 }; 9303 9304 exports.HEX2DEC = function(number) { 9305 // Return error if number is not hexadecimal or contains more than ten 9306 // characters (10 digits) 9307 if (!/^[0-9A-Fa-f]{1,10}$/.test(number)) { 9308 return error.num; 9309 } 9310 9311 // Convert hexadecimal number to decimal 9312 var decimal = parseInt(number, 16); 9313 9314 // Return decimal number 9315 return (decimal >= 549755813888) ? decimal - 1099511627776 : decimal; 9316 }; 9317 9318 exports.HEX2OCT = function(number, places) { 9319 // Return error if number is not hexadecimal or contains more than ten 9320 // characters (10 digits) 9321 if (!/^[0-9A-Fa-f]{1,10}$/.test(number)) { 9322 return error.num; 9323 } 9324 9325 // Convert hexadecimal number to decimal 9326 var decimal = parseInt(number, 16); 9327 9328 // Return error if number is positive and greater than 0x1fffffff 9329 // (536870911) 9330 if (decimal > 536870911 && decimal < 1098974756864) { 9331 return error.num; 9332 } 9333 9334 // Ignore places and return a 10-character octal number if number is 9335 // negative 9336 if (decimal >= 1098974756864) { 9337 return (decimal - 1098437885952).toString(8); 9338 } 9339 9340 // Convert decimal number to octal 9341 var result = decimal.toString(8); 9342 9343 // Return octal number using the minimum number of characters necessary 9344 // if places is undefined 9345 if (places === undefined) { 9346 return result; 9347 } else { 9348 // Return error if places is nonnumeric 9349 if (isNaN(places)) { 9350 return error.value; 9351 } 9352 9353 // Return error if places is negative 9354 if (places < 0) { 9355 return error.num; 9356 } 9357 9358 // Truncate places in case it is not an integer 9359 places = Math.floor(places); 9360 9361 // Pad return value with leading 0s (zeros) if necessary (using 9362 // Underscore.string) 9363 return (places >= result.length) ? REPT('0', places - result.length) + result : error.num; 9364 } 9365 }; 9366 9367 exports.IMABS = function(inumber) { 9368 // Lookup real and imaginary coefficients using exports.js 9369 // [http://formulajs.org] 9370 var x = exports.IMREAL(inumber); 9371 var y = exports.IMAGINARY(inumber); 9372 9373 // Return error if either coefficient is not a number 9374 if (utils.anyIsError(x, y)) { 9375 return error.value; 9376 } 9377 9378 // Return absolute value of complex number 9379 return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); 9380 }; 9381 9382 exports.IMAGINARY = function(inumber) { 9383 if (inumber === undefined || inumber === true || inumber === false) { 9384 return error.value; 9385 } 9386 9387 // Return 0 if inumber is equal to 0 9388 if (inumber === 0 || inumber === '0') { 9389 return 0; 9390 } 9391 9392 // Handle special cases 9393 if (['i', 'j'].indexOf(inumber) >= 0) { 9394 return 1; 9395 } 9396 9397 // Normalize imaginary coefficient 9398 inumber = inumber.replace('+i', '+1i').replace('-i', '-1i').replace('+j', '+1j').replace('-j', '-1j'); 9399 9400 // Lookup sign 9401 var plus = inumber.indexOf('+'); 9402 var minus = inumber.indexOf('-'); 9403 if (plus === 0) { 9404 plus = inumber.indexOf('+', 1); 9405 } 9406 9407 if (minus === 0) { 9408 minus = inumber.indexOf('-', 1); 9409 } 9410 9411 // Lookup imaginary unit 9412 var last = inumber.substring(inumber.length - 1, inumber.length); 9413 var unit = (last === 'i' || last === 'j'); 9414 9415 if (plus >= 0 || minus >= 0) { 9416 // Return error if imaginary unit is neither i nor j 9417 if (!unit) { 9418 return error.num; 9419 } 9420 9421 // Return imaginary coefficient of complex number 9422 if (plus >= 0) { 9423 return (isNaN(inumber.substring(0, plus)) || isNaN(inumber.substring(plus + 1, inumber.length - 1))) ? 9424 error.num : 9425 Number(inumber.substring(plus + 1, inumber.length - 1)); 9426 } else { 9427 return (isNaN(inumber.substring(0, minus)) || isNaN(inumber.substring(minus + 1, inumber.length - 1))) ? 9428 error.num : 9429 -Number(inumber.substring(minus + 1, inumber.length - 1)); 9430 } 9431 } else { 9432 if (unit) { 9433 return (isNaN(inumber.substring(0, inumber.length - 1))) ? error.num : inumber.substring(0, inumber.length - 1); 9434 } else { 9435 return (isNaN(inumber)) ? error.num : 0; 9436 } 9437 } 9438 }; 9439 9440 exports.IMARGUMENT = function(inumber) { 9441 // Lookup real and imaginary coefficients using exports.js 9442 // [http://formulajs.org] 9443 var x = exports.IMREAL(inumber); 9444 var y = exports.IMAGINARY(inumber); 9445 9446 // Return error if either coefficient is not a number 9447 if (utils.anyIsError(x, y)) { 9448 return error.value; 9449 } 9450 9451 // Return error if inumber is equal to zero 9452 if (x === 0 && y === 0) { 9453 return error.div0; 9454 } 9455 9456 // Return PI/2 if x is equal to zero and y is positive 9457 if (x === 0 && y > 0) { 9458 return Math.PI / 2; 9459 } 9460 9461 // Return -PI/2 if x is equal to zero and y is negative 9462 if (x === 0 && y < 0) { 9463 return -Math.PI / 2; 9464 } 9465 9466 // Return zero if x is negative and y is equal to zero 9467 if (y === 0 && x > 0) { 9468 return 0; 9469 } 9470 9471 // Return zero if x is negative and y is equal to zero 9472 if (y === 0 && x < 0) { 9473 return -Math.PI; 9474 } 9475 9476 // Return argument of complex number 9477 if (x > 0) { 9478 return Math.atan(y / x); 9479 } else if (x < 0 && y >= 0) { 9480 return Math.atan(y / x) + Math.PI; 9481 } else { 9482 return Math.atan(y / x) - Math.PI; 9483 } 9484 }; 9485 9486 exports.IMCONJUGATE = function(inumber) { 9487 // Lookup real and imaginary coefficients using exports.js 9488 // [http://formulajs.org] 9489 var x = exports.IMREAL(inumber); 9490 var y = exports.IMAGINARY(inumber); 9491 9492 if (utils.anyIsError(x, y)) { 9493 return error.value; 9494 } 9495 9496 // Lookup imaginary unit 9497 var unit = inumber.substring(inumber.length - 1); 9498 unit = (unit === 'i' || unit === 'j') ? unit : 'i'; 9499 9500 // Return conjugate of complex number 9501 return (y !== 0) ? exports.COMPLEX(x, -y, unit) : inumber; 9502 }; 9503 9504 exports.IMCOS = function(inumber) { 9505 // Lookup real and imaginary coefficients using exports.js 9506 // [http://formulajs.org] 9507 var x = exports.IMREAL(inumber); 9508 var y = exports.IMAGINARY(inumber); 9509 9510 if (utils.anyIsError(x, y)) { 9511 return error.value; 9512 } 9513 9514 // Lookup imaginary unit 9515 var unit = inumber.substring(inumber.length - 1); 9516 unit = (unit === 'i' || unit === 'j') ? unit : 'i'; 9517 9518 // Return cosine of complex number 9519 return exports.COMPLEX(Math.cos(x) * (Math.exp(y) + Math.exp(-y)) / 2, -Math.sin(x) * (Math.exp(y) - Math.exp(-y)) / 2, unit); 9520 }; 9521 9522 exports.IMCOSH = function(inumber) { 9523 // Lookup real and imaginary coefficients using exports.js 9524 // [http://formulajs.org] 9525 var x = exports.IMREAL(inumber); 9526 var y = exports.IMAGINARY(inumber); 9527 9528 if (utils.anyIsError(x, y)) { 9529 return error.value; 9530 } 9531 9532 // Lookup imaginary unit 9533 var unit = inumber.substring(inumber.length - 1); 9534 unit = (unit === 'i' || unit === 'j') ? unit : 'i'; 9535 9536 // Return hyperbolic cosine of complex number 9537 return exports.COMPLEX(Math.cos(y) * (Math.exp(x) + Math.exp(-x)) / 2, Math.sin(y) * (Math.exp(x) - Math.exp(-x)) / 2, unit); 9538 }; 9539 9540 exports.IMCOT = function(inumber) { 9541 // Lookup real and imaginary coefficients using Formula.js 9542 // [http://formulajs.org] 9543 var x = exports.IMREAL(inumber); 9544 var y = exports.IMAGINARY(inumber); 9545 9546 if (utils.anyIsError(x, y)) { 9547 return error.value; 9548 } 9549 9550 // Return cotangent of complex number 9551 return exports.IMDIV(exports.IMCOS(inumber), exports.IMSIN(inumber)); 9552 }; 9553 9554 exports.IMDIV = function(inumber1, inumber2) { 9555 // Lookup real and imaginary coefficients using Formula.js 9556 // [http://formulajs.org] 9557 var a = exports.IMREAL(inumber1); 9558 var b = exports.IMAGINARY(inumber1); 9559 var c = exports.IMREAL(inumber2); 9560 var d = exports.IMAGINARY(inumber2); 9561 9562 if (utils.anyIsError(a, b, c, d)) { 9563 return error.value; 9564 } 9565 9566 // Lookup imaginary unit 9567 var unit1 = inumber1.substring(inumber1.length - 1); 9568 var unit2 = inumber2.substring(inumber2.length - 1); 9569 var unit = 'i'; 9570 if (unit1 === 'j') { 9571 unit = 'j'; 9572 } else if (unit2 === 'j') { 9573 unit = 'j'; 9574 } 9575 9576 // Return error if inumber2 is null 9577 if (c === 0 && d === 0) { 9578 return error.num; 9579 } 9580 9581 // Return exponential of complex number 9582 var den = c * c + d * d; 9583 return exports.COMPLEX((a * c + b * d) / den, (b * c - a * d) / den, unit); 9584 }; 9585 9586 exports.IMEXP = function(inumber) { 9587 // Lookup real and imaginary coefficients using Formula.js 9588 // [http://formulajs.org] 9589 var x = exports.IMREAL(inumber); 9590 var y = exports.IMAGINARY(inumber); 9591 9592 if (utils.anyIsError(x, y)) { 9593 return error.value; 9594 } 9595 9596 // Lookup imaginary unit 9597 var unit = inumber.substring(inumber.length - 1); 9598 unit = (unit === 'i' || unit === 'j') ? unit : 'i'; 9599 9600 // Return exponential of complex number 9601 var e = Math.exp(x); 9602 return exports.COMPLEX(e * Math.cos(y), e * Math.sin(y), unit); 9603 }; 9604 9605 exports.IMLN = function(inumber) { 9606 // Lookup real and imaginary coefficients using Formula.js 9607 // [http://formulajs.org] 9608 var x = exports.IMREAL(inumber); 9609 var y = exports.IMAGINARY(inumber); 9610 9611 if (utils.anyIsError(x, y)) { 9612 return error.value; 9613 } 9614 9615 // Lookup imaginary unit 9616 var unit = inumber.substring(inumber.length - 1); 9617 unit = (unit === 'i' || unit === 'j') ? unit : 'i'; 9618 9619 // Return exponential of complex number 9620 return exports.COMPLEX(Math.log(Math.sqrt(x * x + y * y)), Math.atan(y / x), unit); 9621 }; 9622 9623 exports.IMLOG10 = function(inumber) { 9624 // Lookup real and imaginary coefficients using Formula.js 9625 // [http://formulajs.org] 9626 var x = exports.IMREAL(inumber); 9627 var y = exports.IMAGINARY(inumber); 9628 9629 if (utils.anyIsError(x, y)) { 9630 return error.value; 9631 } 9632 9633 // Lookup imaginary unit 9634 var unit = inumber.substring(inumber.length - 1); 9635 unit = (unit === 'i' || unit === 'j') ? unit : 'i'; 9636 9637 // Return exponential of complex number 9638 return exports.COMPLEX(Math.log(Math.sqrt(x * x + y * y)) / Math.log(10), Math.atan(y / x) / Math.log(10), unit); 9639 }; 9640 9641 exports.IMLOG2 = function(inumber) { 9642 // Lookup real and imaginary coefficients using Formula.js 9643 // [http://formulajs.org] 9644 var x = exports.IMREAL(inumber); 9645 var y = exports.IMAGINARY(inumber); 9646 9647 if (utils.anyIsError(x, y)) { 9648 return error.value; 9649 } 9650 9651 // Lookup imaginary unit 9652 var unit = inumber.substring(inumber.length - 1); 9653 unit = (unit === 'i' || unit === 'j') ? unit : 'i'; 9654 9655 // Return exponential of complex number 9656 return exports.COMPLEX(Math.log(Math.sqrt(x * x + y * y)) / Math.log(2), Math.atan(y / x) / Math.log(2), unit); 9657 }; 9658 9659 exports.IMPOWER = function(inumber, number) { 9660 number = utils.parseNumber(number); 9661 var x = exports.IMREAL(inumber); 9662 var y = exports.IMAGINARY(inumber); 9663 if (utils.anyIsError(number, x, y)) { 9664 return error.value; 9665 } 9666 9667 // Lookup imaginary unit 9668 var unit = inumber.substring(inumber.length - 1); 9669 unit = (unit === 'i' || unit === 'j') ? unit : 'i'; 9670 9671 // Calculate power of modulus 9672 var p = Math.pow(exports.IMABS(inumber), number); 9673 9674 // Calculate argument 9675 var t = exports.IMARGUMENT(inumber); 9676 9677 // Return exponential of complex number 9678 return exports.COMPLEX(p * Math.cos(number * t), p * Math.sin(number * t), unit); 9679 }; 9680 9681 exports.IMPRODUCT = function() { 9682 // Initialize result 9683 var result = arguments[0]; 9684 9685 // Loop on all numbers 9686 for (var i = 1; i < arguments.length; i++) { 9687 // Lookup coefficients of two complex numbers 9688 var a = exports.IMREAL(result); 9689 var b = exports.IMAGINARY(result); 9690 var c = exports.IMREAL(arguments[i]); 9691 var d = exports.IMAGINARY(arguments[i]); 9692 9693 if (utils.anyIsError(a, b, c, d)) { 9694 return error.value; 9695 } 9696 9697 // Complute product of two complex numbers 9698 result = exports.COMPLEX(a * c - b * d, a * d + b * c); 9699 } 9700 9701 // Return product of complex numbers 9702 return result; 9703 }; 9704 9705 exports.IMREAL = function(inumber) { 9706 if (inumber === undefined || inumber === true || inumber === false) { 9707 return error.value; 9708 } 9709 9710 // Return 0 if inumber is equal to 0 9711 if (inumber === 0 || inumber === '0') { 9712 return 0; 9713 } 9714 9715 // Handle special cases 9716 if (['i', '+i', '1i', '+1i', '-i', '-1i', 'j', '+j', '1j', '+1j', '-j', '-1j'].indexOf(inumber) >= 0) { 9717 return 0; 9718 } 9719 9720 // Lookup sign 9721 var plus = inumber.indexOf('+'); 9722 var minus = inumber.indexOf('-'); 9723 if (plus === 0) { 9724 plus = inumber.indexOf('+', 1); 9725 } 9726 if (minus === 0) { 9727 minus = inumber.indexOf('-', 1); 9728 } 9729 9730 // Lookup imaginary unit 9731 var last = inumber.substring(inumber.length - 1, inumber.length); 9732 var unit = (last === 'i' || last === 'j'); 9733 9734 if (plus >= 0 || minus >= 0) { 9735 // Return error if imaginary unit is neither i nor j 9736 if (!unit) { 9737 return error.num; 9738 } 9739 9740 // Return real coefficient of complex number 9741 if (plus >= 0) { 9742 return (isNaN(inumber.substring(0, plus)) || isNaN(inumber.substring(plus + 1, inumber.length - 1))) ? 9743 error.num : 9744 Number(inumber.substring(0, plus)); 9745 } else { 9746 return (isNaN(inumber.substring(0, minus)) || isNaN(inumber.substring(minus + 1, inumber.length - 1))) ? 9747 error.num : 9748 Number(inumber.substring(0, minus)); 9749 } 9750 } else { 9751 if (unit) { 9752 return (isNaN(inumber.substring(0, inumber.length - 1))) ? error.num : 0; 9753 } else { 9754 return (isNaN(inumber)) ? error.num : inumber; 9755 } 9756 } 9757 }; 9758 9759 exports.IMSEC = function(inumber) { 9760 // Return error if inumber is a logical value 9761 if (inumber === true || inumber === false) { 9762 return error.value; 9763 } 9764 9765 // Lookup real and imaginary coefficients using Formula.js 9766 // [http://formulajs.org] 9767 var x = exports.IMREAL(inumber); 9768 var y = exports.IMAGINARY(inumber); 9769 9770 if (utils.anyIsError(x, y)) { 9771 return error.value; 9772 } 9773 9774 // Return secant of complex number 9775 return exports.IMDIV('1', exports.IMCOS(inumber)); 9776 }; 9777 9778 exports.IMSECH = function(inumber) { 9779 // Lookup real and imaginary coefficients using Formula.js 9780 // [http://formulajs.org] 9781 var x = exports.IMREAL(inumber); 9782 var y = exports.IMAGINARY(inumber); 9783 9784 if (utils.anyIsError(x, y)) { 9785 return error.value; 9786 } 9787 9788 // Return hyperbolic secant of complex number 9789 return exports.IMDIV('1', exports.IMCOSH(inumber)); 9790 }; 9791 9792 exports.IMSIN = function(inumber) { 9793 // Lookup real and imaginary coefficients using Formula.js 9794 // [http://formulajs.org] 9795 var x = exports.IMREAL(inumber); 9796 var y = exports.IMAGINARY(inumber); 9797 9798 if (utils.anyIsError(x, y)) { 9799 return error.value; 9800 } 9801 9802 // Lookup imaginary unit 9803 var unit = inumber.substring(inumber.length - 1); 9804 unit = (unit === 'i' || unit === 'j') ? unit : 'i'; 9805 9806 // Return sine of complex number 9807 return exports.COMPLEX(Math.sin(x) * (Math.exp(y) + Math.exp(-y)) / 2, Math.cos(x) * (Math.exp(y) - Math.exp(-y)) / 2, unit); 9808 }; 9809 9810 exports.IMSINH = function(inumber) { 9811 // Lookup real and imaginary coefficients using Formula.js 9812 // [http://formulajs.org] 9813 var x = exports.IMREAL(inumber); 9814 var y = exports.IMAGINARY(inumber); 9815 9816 if (utils.anyIsError(x, y)) { 9817 return error.value; 9818 } 9819 9820 // Lookup imaginary unit 9821 var unit = inumber.substring(inumber.length - 1); 9822 unit = (unit === 'i' || unit === 'j') ? unit : 'i'; 9823 9824 // Return hyperbolic sine of complex number 9825 return exports.COMPLEX(Math.cos(y) * (Math.exp(x) - Math.exp(-x)) / 2, Math.sin(y) * (Math.exp(x) + Math.exp(-x)) / 2, unit); 9826 }; 9827 9828 exports.IMSQRT = function(inumber) { 9829 // Lookup real and imaginary coefficients using Formula.js 9830 // [http://formulajs.org] 9831 var x = exports.IMREAL(inumber); 9832 var y = exports.IMAGINARY(inumber); 9833 9834 if (utils.anyIsError(x, y)) { 9835 return error.value; 9836 } 9837 9838 // Lookup imaginary unit 9839 var unit = inumber.substring(inumber.length - 1); 9840 unit = (unit === 'i' || unit === 'j') ? unit : 'i'; 9841 9842 // Calculate power of modulus 9843 var s = Math.sqrt(exports.IMABS(inumber)); 9844 9845 // Calculate argument 9846 var t = exports.IMARGUMENT(inumber); 9847 9848 // Return exponential of complex number 9849 return exports.COMPLEX(s * Math.cos(t / 2), s * Math.sin(t / 2), unit); 9850 }; 9851 9852 exports.IMCSC = function (inumber) { 9853 // Return error if inumber is a logical value 9854 if (inumber === true || inumber === false) { 9855 return error.value; 9856 } 9857 9858 // Lookup real and imaginary coefficients using Formula.js 9859 // [http://formulajs.org] 9860 var x = exports.IMREAL(inumber); 9861 var y = exports.IMAGINARY(inumber); 9862 9863 // Return error if either coefficient is not a number 9864 if (utils.anyIsError(x, y)) { 9865 return error.num; 9866 } 9867 9868 // Return cosecant of complex number 9869 return exports.IMDIV('1', exports.IMSIN(inumber)); 9870 }; 9871 9872 exports.IMCSCH = function (inumber) { 9873 // Return error if inumber is a logical value 9874 if (inumber === true || inumber === false) { 9875 return error.value; 9876 } 9877 9878 // Lookup real and imaginary coefficients using Formula.js 9879 // [http://formulajs.org] 9880 var x = exports.IMREAL(inumber); 9881 var y = exports.IMAGINARY(inumber); 9882 9883 // Return error if either coefficient is not a number 9884 if (utils.anyIsError(x, y)) { 9885 return error.num; 9886 } 9887 9888 // Return hyperbolic cosecant of complex number 9889 return exports.IMDIV('1', exports.IMSINH(inumber)); 9890 }; 9891 9892 exports.IMSUB = function(inumber1, inumber2) { 9893 // Lookup real and imaginary coefficients using Formula.js 9894 // [http://formulajs.org] 9895 var a = this.IMREAL(inumber1); 9896 var b = this.IMAGINARY(inumber1); 9897 var c = this.IMREAL(inumber2); 9898 var d = this.IMAGINARY(inumber2); 9899 9900 if (utils.anyIsError(a, b, c, d)) { 9901 return error.value; 9902 } 9903 9904 // Lookup imaginary unit 9905 var unit1 = inumber1.substring(inumber1.length - 1); 9906 var unit2 = inumber2.substring(inumber2.length - 1); 9907 var unit = 'i'; 9908 if (unit1 === 'j') { 9909 unit = 'j'; 9910 } else if (unit2 === 'j') { 9911 unit = 'j'; 9912 } 9913 9914 // Return _ of two complex numbers 9915 return this.COMPLEX(a - c, b - d, unit); 9916 }; 9917 9918 exports.IMSUM = function() { 9919 var args = utils.flatten(arguments); 9920 9921 // Initialize result 9922 var result = args[0]; 9923 9924 // Loop on all numbers 9925 for (var i = 1; i < args.length; i++) { 9926 // Lookup coefficients of two complex numbers 9927 var a = this.IMREAL(result); 9928 var b = this.IMAGINARY(result); 9929 var c = this.IMREAL(args[i]); 9930 var d = this.IMAGINARY(args[i]); 9931 9932 if (utils.anyIsError(a, b, c, d)) { 9933 return error.value; 9934 } 9935 9936 // Complute product of two complex numbers 9937 result = this.COMPLEX(a + c, b + d); 9938 } 9939 9940 // Return sum of complex numbers 9941 return result; 9942 }; 9943 9944 exports.IMTAN = function(inumber) { 9945 // Return error if inumber is a logical value 9946 if (inumber === true || inumber === false) { 9947 return error.value; 9948 } 9949 9950 // Lookup real and imaginary coefficients using Formula.js 9951 // [http://formulajs.org] 9952 var x = exports.IMREAL(inumber); 9953 var y = exports.IMAGINARY(inumber); 9954 9955 if (utils.anyIsError(x, y)) { 9956 return error.value; 9957 } 9958 9959 // Return tangent of complex number 9960 return this.IMDIV(this.IMSIN(inumber), this.IMCOS(inumber)); 9961 }; 9962 9963 exports.OCT2BIN = function(number, places) { 9964 // Return error if number is not hexadecimal or contains more than ten 9965 // characters (10 digits) 9966 if (!/^[0-7]{1,10}$/.test(number)) { 9967 return error.num; 9968 } 9969 9970 // Check if number is negative 9971 var negative = (number.length === 10 && number.substring(0, 1) === '7') ? true : false; 9972 9973 // Convert octal number to decimal 9974 var decimal = (negative) ? parseInt(number, 8) - 1073741824 : parseInt(number, 8); 9975 9976 // Return error if number is lower than -512 or greater than 511 9977 if (decimal < -512 || decimal > 511) { 9978 return error.num; 9979 } 9980 9981 // Ignore places and return a 10-character binary number if number is 9982 // negative 9983 if (negative) { 9984 return '1' + REPT('0', 9 - (512 + decimal).toString(2).length) + (512 + decimal).toString(2); 9985 } 9986 9987 // Convert decimal number to binary 9988 var result = decimal.toString(2); 9989 9990 // Return binary number using the minimum number of characters necessary 9991 // if places is undefined 9992 if (typeof places === 'undefined') { 9993 return result; 9994 } else { 9995 // Return error if places is nonnumeric 9996 if (isNaN(places)) { 9997 return error.value; 9998 } 9999 10000 // Return error if places is negative 10001 if (places < 0) { 10002 return error.num; 10003 } 10004 10005 // Truncate places in case it is not an integer 10006 places = Math.floor(places); 10007 10008 // Pad return value with leading 0s (zeros) if necessary (using 10009 // Underscore.string) 10010 return (places >= result.length) ? REPT('0', places - result.length) + result : error.num; 10011 } 10012 }; 10013 10014 exports.OCT2DEC = function(number) { 10015 // Return error if number is not octal or contains more than ten 10016 // characters (10 digits) 10017 if (!/^[0-7]{1,10}$/.test(number)) { 10018 return error.num; 10019 } 10020 10021 // Convert octal number to decimal 10022 var decimal = parseInt(number, 8); 10023 10024 // Return decimal number 10025 return (decimal >= 536870912) ? decimal - 1073741824 : decimal; 10026 }; 10027 10028 exports.OCT2HEX = function(number, places) { 10029 // Return error if number is not octal or contains more than ten 10030 // characters (10 digits) 10031 if (!/^[0-7]{1,10}$/.test(number)) { 10032 return error.num; 10033 } 10034 10035 // Convert octal number to decimal 10036 var decimal = parseInt(number, 8); 10037 10038 // Ignore places and return a 10-character octal number if number is 10039 // negative 10040 if (decimal >= 536870912) { 10041 return 'ff' + (decimal + 3221225472).toString(16); 10042 } 10043 10044 // Convert decimal number to hexadecimal 10045 var result = decimal.toString(16); 10046 10047 // Return hexadecimal number using the minimum number of characters 10048 // necessary if places is undefined 10049 if (places === undefined) { 10050 return result; 10051 } else { 10052 // Return error if places is nonnumeric 10053 if (isNaN(places)) { 10054 return error.value; 10055 } 10056 10057 // Return error if places is negative 10058 if (places < 0) { 10059 return error.num; 10060 } 10061 10062 // Truncate places in case it is not an integer 10063 places = Math.floor(places); 10064 10065 // Pad return value with leading 0s (zeros) if necessary (using 10066 // Underscore.string) 10067 return (places >= result.length) ? REPT('0', places - result.length) + result : error.num; 10068 } 10069 }; 10070 10071 return exports; 10072 })(); 10073 10074 jexcel.methods.financial = (function() { 10075 var exports = {}; 10076 10077 function validDate(d) { 10078 return d && d.getTime && !isNaN(d.getTime()); 10079 } 10080 10081 function ensureDate(d) { 10082 return (d instanceof Date)?d:new Date(d); 10083 } 10084 10085 exports.ACCRINT = function(issue, first, settlement, rate, par, frequency, basis) { 10086 // Return error if either date is invalid 10087 issue = ensureDate(issue); 10088 first = ensureDate(first); 10089 settlement = ensureDate(settlement); 10090 if (!validDate(issue) || !validDate(first) || !validDate(settlement)) { 10091 return '#VALUE!'; 10092 } 10093 10094 // Return error if either rate or par are lower than or equal to zero 10095 if (rate <= 0 || par <= 0) { 10096 return '#NUM!'; 10097 } 10098 10099 // Return error if frequency is neither 1, 2, or 4 10100 if ([1, 2, 4].indexOf(frequency) === -1) { 10101 return '#NUM!'; 10102 } 10103 10104 // Return error if basis is neither 0, 1, 2, 3, or 4 10105 if ([0, 1, 2, 3, 4].indexOf(basis) === -1) { 10106 return '#NUM!'; 10107 } 10108 10109 // Return error if settlement is before or equal to issue 10110 if (settlement <= issue) { 10111 return '#NUM!'; 10112 } 10113 10114 // Set default values 10115 par = par || 0; 10116 basis = basis || 0; 10117 10118 // Compute accrued interest 10119 return par * rate * YEARFRAC(issue, settlement, basis); 10120 }; 10121 10122 exports.ACCRINTM = null; 10123 10124 exports.AMORDEGRC = null; 10125 10126 exports.AMORLINC = null; 10127 10128 exports.COUPDAYBS = null; 10129 10130 exports.COUPDAYS = null; 10131 10132 exports.COUPDAYSNC = null; 10133 10134 exports.COUPNCD = null; 10135 10136 exports.COUPNUM = null; 10137 10138 exports.COUPPCD = null; 10139 10140 exports.CUMIPMT = function(rate, periods, value, start, end, type) { 10141 // Credits: algorithm inspired by Apache OpenOffice 10142 // Credits: Hannes Stiebitzhofer for the translations of function and 10143 // variable names 10144 // Requires exports.FV() and exports.PMT() from exports.js 10145 // [http://stoic.com/exports/] 10146 10147 rate = utils.parseNumber(rate); 10148 periods = utils.parseNumber(periods); 10149 value = utils.parseNumber(value); 10150 if (utils.anyIsError(rate, periods, value)) { 10151 return error.value; 10152 } 10153 10154 // Return error if either rate, periods, or value are lower than or 10155 // equal to zero 10156 if (rate <= 0 || periods <= 0 || value <= 0) { 10157 return error.num; 10158 } 10159 10160 // Return error if start < 1, end < 1, or start > end 10161 if (start < 1 || end < 1 || start > end) { 10162 return error.num; 10163 } 10164 10165 // Return error if type is neither 0 nor 1 10166 if (type !== 0 && type !== 1) { 10167 return error.num; 10168 } 10169 10170 // Compute cumulative interest 10171 var payment = exports.PMT(rate, periods, value, 0, type); 10172 var interest = 0; 10173 10174 if (start === 1) { 10175 if (type === 0) { 10176 interest = -value; 10177 start++; 10178 } 10179 } 10180 10181 for (var i = start; i <= end; i++) { 10182 if (type === 1) { 10183 interest += exports.FV(rate, i - 2, payment, value, 1) - payment; 10184 } else { 10185 interest += exports.FV(rate, i - 1, payment, value, 0); 10186 } 10187 } 10188 interest *= rate; 10189 10190 // Return cumulative interest 10191 return interest; 10192 }; 10193 10194 exports.CUMPRINC = function(rate, periods, value, start, end, type) { 10195 // Credits: algorithm inspired by Apache OpenOffice 10196 // Credits: Hannes Stiebitzhofer for the translations of function and 10197 // variable names 10198 10199 rate = utils.parseNumber(rate); 10200 periods = utils.parseNumber(periods); 10201 value = utils.parseNumber(value); 10202 if (utils.anyIsError(rate, periods, value)) { 10203 return error.value; 10204 } 10205 10206 // Return error if either rate, periods, or value are lower than or 10207 // equal to zero 10208 if (rate <= 0 || periods <= 0 || value <= 0) { 10209 return error.num; 10210 } 10211 10212 // Return error if start < 1, end < 1, or start > end 10213 if (start < 1 || end < 1 || start > end) { 10214 return error.num; 10215 } 10216 10217 // Return error if type is neither 0 nor 1 10218 if (type !== 0 && type !== 1) { 10219 return error.num; 10220 } 10221 10222 // Compute cumulative principal 10223 var payment = exports.PMT(rate, periods, value, 0, type); 10224 var principal = 0; 10225 if (start === 1) { 10226 if (type === 0) { 10227 principal = payment + value * rate; 10228 } else { 10229 principal = payment; 10230 } 10231 start++; 10232 } 10233 for (var i = start; i <= end; i++) { 10234 if (type > 0) { 10235 principal += payment - (exports.FV(rate, i - 2, payment, value, 1) - payment) * rate; 10236 } else { 10237 principal += payment - exports.FV(rate, i - 1, payment, value, 0) * rate; 10238 } 10239 } 10240 10241 // Return cumulative principal 10242 return principal; 10243 }; 10244 10245 exports.DB = function(cost, salvage, life, period, month) { 10246 // Initialize month 10247 month = (month === undefined) ? 12 : month; 10248 10249 cost = utils.parseNumber(cost); 10250 salvage = utils.parseNumber(salvage); 10251 life = utils.parseNumber(life); 10252 period = utils.parseNumber(period); 10253 month = utils.parseNumber(month); 10254 if (utils.anyIsError(cost, salvage, life, period, month)) { 10255 return error.value; 10256 } 10257 10258 // Return error if any of the parameters is negative 10259 if (cost < 0 || salvage < 0 || life < 0 || period < 0) { 10260 return error.num; 10261 } 10262 10263 // Return error if month is not an integer between 1 and 12 10264 if ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].indexOf(month) === -1) { 10265 return error.num; 10266 } 10267 10268 // Return error if period is greater than life 10269 if (period > life) { 10270 return error.num; 10271 } 10272 10273 // Return 0 (zero) if salvage is greater than or equal to cost 10274 if (salvage >= cost) { 10275 return 0; 10276 } 10277 10278 // Rate is rounded to three decimals places 10279 var rate = (1 - Math.pow(salvage / cost, 1 / life)).toFixed(3); 10280 10281 // Compute initial depreciation 10282 var initial = cost * rate * month / 12; 10283 10284 // Compute total depreciation 10285 var total = initial; 10286 var current = 0; 10287 var ceiling = (period === life) ? life - 1 : period; 10288 for (var i = 2; i <= ceiling; i++) { 10289 current = (cost - total) * rate; 10290 total += current; 10291 } 10292 10293 // Depreciation for the first and last periods are special cases 10294 if (period === 1) { 10295 // First period 10296 return initial; 10297 } else if (period === life) { 10298 // Last period 10299 return (cost - total) * rate; 10300 } else { 10301 return current; 10302 } 10303 }; 10304 10305 exports.DDB = function(cost, salvage, life, period, factor) { 10306 // Initialize factor 10307 factor = (factor === undefined) ? 2 : factor; 10308 10309 cost = utils.parseNumber(cost); 10310 salvage = utils.parseNumber(salvage); 10311 life = utils.parseNumber(life); 10312 period = utils.parseNumber(period); 10313 factor = utils.parseNumber(factor); 10314 if (utils.anyIsError(cost, salvage, life, period, factor)) { 10315 return error.value; 10316 } 10317 10318 // Return error if any of the parameters is negative or if factor is 10319 // null 10320 if (cost < 0 || salvage < 0 || life < 0 || period < 0 || factor <= 0) { 10321 return error.num; 10322 } 10323 10324 // Return error if period is greater than life 10325 if (period > life) { 10326 return error.num; 10327 } 10328 10329 // Return 0 (zero) if salvage is greater than or equal to cost 10330 if (salvage >= cost) { 10331 return 0; 10332 } 10333 10334 // Compute depreciation 10335 var total = 0; 10336 var current = 0; 10337 for (var i = 1; i <= period; i++) { 10338 current = Math.min((cost - total) * (factor / life), (cost - salvage - total)); 10339 total += current; 10340 } 10341 10342 // Return depreciation 10343 return current; 10344 }; 10345 10346 exports.DISC = null; 10347 10348 exports.DOLLARDE = function(dollar, fraction) { 10349 // Credits: algorithm inspired by Apache OpenOffice 10350 10351 dollar = utils.parseNumber(dollar); 10352 fraction = utils.parseNumber(fraction); 10353 if (utils.anyIsError(dollar, fraction)) { 10354 return error.value; 10355 } 10356 10357 // Return error if fraction is negative 10358 if (fraction < 0) { 10359 return error.num; 10360 } 10361 10362 // Return error if fraction is greater than or equal to 0 and less than 10363 // 1 10364 if (fraction >= 0 && fraction < 1) { 10365 return error.div0; 10366 } 10367 10368 // Truncate fraction if it is not an integer 10369 fraction = parseInt(fraction, 10); 10370 10371 // Compute integer part 10372 var result = parseInt(dollar, 10); 10373 10374 // Add decimal part 10375 result += (dollar % 1) * Math.pow(10, Math.ceil(Math.log(fraction) / Math.LN10)) / fraction; 10376 10377 // Round result 10378 var power = Math.pow(10, Math.ceil(Math.log(fraction) / Math.LN2) + 1); 10379 result = Math.round(result * power) / power; 10380 10381 // Return converted dollar price 10382 return result; 10383 }; 10384 10385 exports.DOLLARFR = function(dollar, fraction) { 10386 // Credits: algorithm inspired by Apache OpenOffice 10387 10388 dollar = utils.parseNumber(dollar); 10389 fraction = utils.parseNumber(fraction); 10390 if (utils.anyIsError(dollar, fraction)) { 10391 return error.value; 10392 } 10393 10394 // Return error if fraction is negative 10395 if (fraction < 0) { 10396 return error.num; 10397 } 10398 10399 // Return error if fraction is greater than or equal to 0 and less than 10400 // 1 10401 if (fraction >= 0 && fraction < 1) { 10402 return error.div0; 10403 } 10404 10405 // Truncate fraction if it is not an integer 10406 fraction = parseInt(fraction, 10); 10407 10408 // Compute integer part 10409 var result = parseInt(dollar, 10); 10410 10411 // Add decimal part 10412 result += (dollar % 1) * Math.pow(10, -Math.ceil(Math.log(fraction) / Math.LN10)) * fraction; 10413 10414 // Return converted dollar price 10415 return result; 10416 }; 10417 10418 exports.DURATION = null; 10419 10420 exports.EFFECT = function(rate, periods) { 10421 rate = utils.parseNumber(rate); 10422 periods = utils.parseNumber(periods); 10423 if (utils.anyIsError(rate, periods)) { 10424 return error.value; 10425 } 10426 10427 // Return error if rate <=0 or periods < 1 10428 if (rate <= 0 || periods < 1) { 10429 return error.num; 10430 } 10431 10432 // Truncate periods if it is not an integer 10433 periods = parseInt(periods, 10); 10434 10435 // Return effective annual interest rate 10436 return Math.pow(1 + rate / periods, periods) - 1; 10437 }; 10438 10439 exports.FV = function(rate, periods, payment, value, type) { 10440 // Credits: algorithm inspired by Apache OpenOffice 10441 10442 value = value || 0; 10443 type = type || 0; 10444 10445 rate = utils.parseNumber(rate); 10446 periods = utils.parseNumber(periods); 10447 payment = utils.parseNumber(payment); 10448 value = utils.parseNumber(value); 10449 type = utils.parseNumber(type); 10450 if (utils.anyIsError(rate, periods, payment, value, type)) { 10451 return error.value; 10452 } 10453 10454 // Return future value 10455 var result; 10456 if (rate === 0) { 10457 result = value + payment * periods; 10458 } else { 10459 var term = Math.pow(1 + rate, periods); 10460 if (type === 1) { 10461 result = value * term + payment * (1 + rate) * (term - 1) / rate; 10462 } else { 10463 result = value * term + payment * (term - 1) / rate; 10464 } 10465 } 10466 return -result; 10467 }; 10468 10469 exports.FVSCHEDULE = function(principal, schedule) { 10470 principal = utils.parseNumber(principal); 10471 schedule = utils.parseNumberArray(utils.flatten(schedule)); 10472 if (utils.anyIsError(principal, schedule)) { 10473 return error.value; 10474 } 10475 10476 var n = schedule.length; 10477 var future = principal; 10478 10479 // Apply all interests in schedule 10480 for (var i = 0; i < n; i++) { 10481 // Apply scheduled interest 10482 future *= 1 + schedule[i]; 10483 } 10484 10485 // Return future value 10486 return future; 10487 }; 10488 10489 exports.INTRATE = null; 10490 10491 exports.IPMT = function(rate, period, periods, present, future, type) { 10492 // Credits: algorithm inspired by Apache OpenOffice 10493 10494 future = future || 0; 10495 type = type || 0; 10496 10497 rate = utils.parseNumber(rate); 10498 period = utils.parseNumber(period); 10499 periods = utils.parseNumber(periods); 10500 present = utils.parseNumber(present); 10501 future = utils.parseNumber(future); 10502 type = utils.parseNumber(type); 10503 if (utils.anyIsError(rate, period, periods, present, future, type)) { 10504 return error.value; 10505 } 10506 10507 // Compute payment 10508 var payment = exports.PMT(rate, periods, present, future, type); 10509 10510 // Compute interest 10511 var interest; 10512 if (period === 1) { 10513 if (type === 1) { 10514 interest = 0; 10515 } else { 10516 interest = -present; 10517 } 10518 } else { 10519 if (type === 1) { 10520 interest = exports.FV(rate, period - 2, payment, present, 1) - payment; 10521 } else { 10522 interest = exports.FV(rate, period - 1, payment, present, 0); 10523 } 10524 } 10525 10526 // Return interest 10527 return interest * rate; 10528 }; 10529 10530 exports.IRR = function(values, guess) { 10531 // Credits: algorithm inspired by Apache OpenOffice 10532 10533 guess = guess || 0; 10534 10535 values = utils.parseNumberArray(utils.flatten(values)); 10536 guess = utils.parseNumber(guess); 10537 if (utils.anyIsError(values, guess)) { 10538 return error.value; 10539 } 10540 10541 // Calculates the resulting amount 10542 var irrResult = function(values, dates, rate) { 10543 var r = rate + 1; 10544 var result = values[0]; 10545 for (var i = 1; i < values.length; i++) { 10546 result += values[i] / Math.pow(r, (dates[i] - dates[0]) / 365); 10547 } 10548 return result; 10549 }; 10550 10551 // Calculates the first derivation 10552 var irrResultDeriv = function(values, dates, rate) { 10553 var r = rate + 1; 10554 var result = 0; 10555 for (var i = 1; i < values.length; i++) { 10556 var frac = (dates[i] - dates[0]) / 365; 10557 result -= frac * values[i] / Math.pow(r, frac + 1); 10558 } 10559 return result; 10560 }; 10561 10562 // Initialize dates and check that values contains at least one positive 10563 // value and one negative value 10564 var dates = []; 10565 var positive = false; 10566 var negative = false; 10567 for (var i = 0; i < values.length; i++) { 10568 dates[i] = (i === 0) ? 0 : dates[i - 1] + 365; 10569 if (values[i] > 0) { 10570 positive = true; 10571 } 10572 if (values[i] < 0) { 10573 negative = true; 10574 } 10575 } 10576 10577 // Return error if values does not contain at least one positive value 10578 // and one negative value 10579 if (!positive || !negative) { 10580 return error.num; 10581 } 10582 10583 // Initialize guess and resultRate 10584 guess = (guess === undefined) ? 0.1 : guess; 10585 var resultRate = guess; 10586 10587 // Set maximum epsilon for end of iteration 10588 var epsMax = 1e-10; 10589 10590 // Implement Newton's method 10591 var newRate, epsRate, resultValue; 10592 var contLoop = true; 10593 do { 10594 resultValue = irrResult(values, dates, resultRate); 10595 newRate = resultRate - resultValue / irrResultDeriv(values, dates, resultRate); 10596 epsRate = Math.abs(newRate - resultRate); 10597 resultRate = newRate; 10598 contLoop = (epsRate > epsMax) && (Math.abs(resultValue) > epsMax); 10599 } while (contLoop); 10600 10601 // Return internal rate of return 10602 return resultRate; 10603 }; 10604 10605 exports.ISPMT = function(rate, period, periods, value) { 10606 rate = utils.parseNumber(rate); 10607 period = utils.parseNumber(period); 10608 periods = utils.parseNumber(periods); 10609 value = utils.parseNumber(value); 10610 if (utils.anyIsError(rate, period, periods, value)) { 10611 return error.value; 10612 } 10613 10614 // Return interest 10615 return value * rate * (period / periods - 1); 10616 }; 10617 10618 exports.MDURATION = null; 10619 10620 exports.MIRR = function(values, finance_rate, reinvest_rate) { 10621 values = utils.parseNumberArray(utils.flatten(values)); 10622 finance_rate = utils.parseNumber(finance_rate); 10623 reinvest_rate = utils.parseNumber(reinvest_rate); 10624 if (utils.anyIsError(values, finance_rate, reinvest_rate)) { 10625 return error.value; 10626 } 10627 10628 // Initialize number of values 10629 var n = values.length; 10630 10631 // Lookup payments (negative values) and incomes (positive values) 10632 var payments = []; 10633 var incomes = []; 10634 for (var i = 0; i < n; i++) { 10635 if (values[i] < 0) { 10636 payments.push(values[i]); 10637 } else { 10638 incomes.push(values[i]); 10639 } 10640 } 10641 10642 // Return modified internal rate of return 10643 var num = -exports.NPV(reinvest_rate, incomes) * Math.pow(1 + reinvest_rate, n - 1); 10644 var den = exports.NPV(finance_rate, payments) * (1 + finance_rate); 10645 return Math.pow(num / den, 1 / (n - 1)) - 1; 10646 }; 10647 10648 exports.NOMINAL = function(rate, periods) { 10649 rate = utils.parseNumber(rate); 10650 periods = utils.parseNumber(periods); 10651 if (utils.anyIsError(rate, periods)) { 10652 return error.value; 10653 } 10654 10655 // Return error if rate <=0 or periods < 1 10656 if (rate <= 0 || periods < 1) { 10657 return error.num; 10658 } 10659 10660 // Truncate periods if it is not an integer 10661 periods = parseInt(periods, 10); 10662 10663 // Return nominal annual interest rate 10664 return (Math.pow(rate + 1, 1 / periods) - 1) * periods; 10665 }; 10666 10667 exports.NPER = function(rate, payment, present, future, type) { 10668 type = (type === undefined) ? 0 : type; 10669 future = (future === undefined) ? 0 : future; 10670 10671 rate = utils.parseNumber(rate); 10672 payment = utils.parseNumber(payment); 10673 present = utils.parseNumber(present); 10674 future = utils.parseNumber(future); 10675 type = utils.parseNumber(type); 10676 if (utils.anyIsError(rate, payment, present, future, type)) { 10677 return error.value; 10678 } 10679 10680 // Return number of periods 10681 var num = payment * (1 + rate * type) - future * rate; 10682 var den = (present * rate + payment * (1 + rate * type)); 10683 return Math.log(num / den) / Math.log(1 + rate); 10684 }; 10685 10686 exports.NPV = function() { 10687 var args = utils.parseNumberArray(utils.flatten(arguments)); 10688 if (args instanceof Error) { 10689 return args; 10690 } 10691 10692 // Lookup rate 10693 var rate = args[0]; 10694 10695 // Initialize net present value 10696 var value = 0; 10697 10698 // Loop on all values 10699 for (var j = 1; j < args.length; j++) { 10700 value += args[j] / Math.pow(1 + rate, j); 10701 } 10702 10703 // Return net present value 10704 return value; 10705 }; 10706 10707 exports.ODDFPRICE = null; 10708 10709 exports.ODDFYIELD = null; 10710 10711 exports.ODDLPRICE = null; 10712 10713 exports.ODDLYIELD = null; 10714 10715 exports.PDURATION = function(rate, present, future) { 10716 rate = utils.parseNumber(rate); 10717 present = utils.parseNumber(present); 10718 future = utils.parseNumber(future); 10719 if (utils.anyIsError(rate, present, future)) { 10720 return error.value; 10721 } 10722 10723 // Return error if rate <=0 10724 if (rate <= 0) { 10725 return error.num; 10726 } 10727 10728 // Return number of periods 10729 return (Math.log(future) - Math.log(present)) / Math.log(1 + rate); 10730 }; 10731 10732 exports.PMT = function(rate, periods, present, future, type) { 10733 // Credits: algorithm inspired by Apache OpenOffice 10734 10735 future = future || 0; 10736 type = type || 0; 10737 10738 rate = utils.parseNumber(rate); 10739 periods = utils.parseNumber(periods); 10740 present = utils.parseNumber(present); 10741 future = utils.parseNumber(future); 10742 type = utils.parseNumber(type); 10743 if (utils.anyIsError(rate, periods, present, future, type)) { 10744 return error.value; 10745 } 10746 10747 // Return payment 10748 var result; 10749 if (rate === 0) { 10750 result = (present + future) / periods; 10751 } else { 10752 var term = Math.pow(1 + rate, periods); 10753 if (type === 1) { 10754 result = (future * rate / (term - 1) + present * rate / (1 - 1 / term)) / (1 + rate); 10755 } else { 10756 result = future * rate / (term - 1) + present * rate / (1 - 1 / term); 10757 } 10758 } 10759 return -result; 10760 }; 10761 10762 exports.PPMT = function(rate, period, periods, present, future, type) { 10763 future = future || 0; 10764 type = type || 0; 10765 10766 rate = utils.parseNumber(rate); 10767 periods = utils.parseNumber(periods); 10768 present = utils.parseNumber(present); 10769 future = utils.parseNumber(future); 10770 type = utils.parseNumber(type); 10771 if (utils.anyIsError(rate, periods, present, future, type)) { 10772 return error.value; 10773 } 10774 10775 return exports.PMT(rate, periods, present, future, type) - exports.IPMT(rate, period, periods, present, future, type); 10776 }; 10777 10778 exports.PRICE = null; 10779 10780 exports.PRICEDISC = null; 10781 10782 exports.PRICEMAT = null; 10783 10784 exports.PV = function(rate, periods, payment, future, type) { 10785 future = future || 0; 10786 type = type || 0; 10787 10788 rate = utils.parseNumber(rate); 10789 periods = utils.parseNumber(periods); 10790 payment = utils.parseNumber(payment); 10791 future = utils.parseNumber(future); 10792 type = utils.parseNumber(type); 10793 if (utils.anyIsError(rate, periods, payment, future, type)) { 10794 return error.value; 10795 } 10796 10797 // Return present value 10798 if (rate === 0) { 10799 return -payment * periods - future; 10800 } else { 10801 return (((1 - Math.pow(1 + rate, periods)) / rate) * payment * (1 + rate * type) - future) / Math.pow(1 + rate, periods); 10802 } 10803 }; 10804 10805 exports.RATE = function(periods, payment, present, future, type, guess) { 10806 // Credits: rabugento 10807 10808 guess = (guess === undefined) ? 0.01 : guess; 10809 future = (future === undefined) ? 0 : future; 10810 type = (type === undefined) ? 0 : type; 10811 10812 periods = utils.parseNumber(periods); 10813 payment = utils.parseNumber(payment); 10814 present = utils.parseNumber(present); 10815 future = utils.parseNumber(future); 10816 type = utils.parseNumber(type); 10817 guess = utils.parseNumber(guess); 10818 if (utils.anyIsError(periods, payment, present, future, type, guess)) { 10819 return error.value; 10820 } 10821 10822 // Set maximum epsilon for end of iteration 10823 var epsMax = 1e-6; 10824 10825 // Set maximum number of iterations 10826 var iterMax = 100; 10827 var iter = 0; 10828 var close = false; 10829 var rate = guess; 10830 10831 while (iter < iterMax && !close) { 10832 var t1 = Math.pow(rate + 1, periods); 10833 var t2 = Math.pow(rate + 1, periods - 1); 10834 10835 var f1 = future + t1 * present + payment * (t1 - 1) * (rate * type + 1) / rate; 10836 var f2 = periods * t2 * present - payment * (t1 - 1) *(rate * type + 1) / Math.pow(rate,2); 10837 var f3 = periods * payment * t2 * (rate * type + 1) / rate + payment * (t1 - 1) * type / rate; 10838 10839 var newRate = rate - f1 / (f2 + f3); 10840 10841 if (Math.abs(newRate - rate) < epsMax) close = true; 10842 iter++ 10843 rate = newRate; 10844 } 10845 10846 if (!close) return Number.NaN + rate; 10847 return rate; 10848 }; 10849 10850 // TODO 10851 exports.RECEIVED = null; 10852 10853 exports.RRI = function(periods, present, future) { 10854 periods = utils.parseNumber(periods); 10855 present = utils.parseNumber(present); 10856 future = utils.parseNumber(future); 10857 if (utils.anyIsError(periods, present, future)) { 10858 return error.value; 10859 } 10860 10861 // Return error if periods or present is equal to 0 (zero) 10862 if (periods === 0 || present === 0) { 10863 return error.num; 10864 } 10865 10866 // Return equivalent interest rate 10867 return Math.pow(future / present, 1 / periods) - 1; 10868 }; 10869 10870 exports.SLN = function(cost, salvage, life) { 10871 cost = utils.parseNumber(cost); 10872 salvage = utils.parseNumber(salvage); 10873 life = utils.parseNumber(life); 10874 if (utils.anyIsError(cost, salvage, life)) { 10875 return error.value; 10876 } 10877 10878 // Return error if life equal to 0 (zero) 10879 if (life === 0) { 10880 return error.num; 10881 } 10882 10883 // Return straight-line depreciation 10884 return (cost - salvage) / life; 10885 }; 10886 10887 exports.SYD = function(cost, salvage, life, period) { 10888 // Return error if any of the parameters is not a number 10889 cost = utils.parseNumber(cost); 10890 salvage = utils.parseNumber(salvage); 10891 life = utils.parseNumber(life); 10892 period = utils.parseNumber(period); 10893 if (utils.anyIsError(cost, salvage, life, period)) { 10894 return error.value; 10895 } 10896 10897 // Return error if life equal to 0 (zero) 10898 if (life === 0) { 10899 return error.num; 10900 } 10901 10902 // Return error if period is lower than 1 or greater than life 10903 if (period < 1 || period > life) { 10904 return error.num; 10905 } 10906 10907 // Truncate period if it is not an integer 10908 period = parseInt(period, 10); 10909 10910 // Return straight-line depreciation 10911 return ((cost - salvage) * (life - period + 1) * 2) / (life * (life + 1)); 10912 }; 10913 10914 exports.TBILLEQ = function(settlement, maturity, discount) { 10915 settlement = utils.parseDate(settlement); 10916 maturity = utils.parseDate(maturity); 10917 discount = utils.parseNumber(discount); 10918 if (utils.anyIsError(settlement, maturity, discount)) { 10919 return error.value; 10920 } 10921 10922 // Return error if discount is lower than or equal to zero 10923 if (discount <= 0) { 10924 return error.num; 10925 } 10926 10927 // Return error if settlement is greater than maturity 10928 if (settlement > maturity) { 10929 return error.num; 10930 } 10931 10932 // Return error if maturity is more than one year after settlement 10933 if (maturity - settlement > 365 * 24 * 60 * 60 * 1000) { 10934 return error.num; 10935 } 10936 10937 // Return bond-equivalent yield 10938 return (365 * discount) / (360 - discount * DAYS360(settlement, maturity, false)); 10939 }; 10940 10941 exports.TBILLPRICE = function(settlement, maturity, discount) { 10942 settlement = utils.parseDate(settlement); 10943 maturity = utils.parseDate(maturity); 10944 discount = utils.parseNumber(discount); 10945 if (utils.anyIsError(settlement, maturity, discount)) { 10946 return error.value; 10947 } 10948 10949 // Return error if discount is lower than or equal to zero 10950 if (discount <= 0) { 10951 return error.num; 10952 } 10953 10954 // Return error if settlement is greater than maturity 10955 if (settlement > maturity) { 10956 return error.num; 10957 } 10958 10959 // Return error if maturity is more than one year after settlement 10960 if (maturity - settlement > 365 * 24 * 60 * 60 * 1000) { 10961 return error.num; 10962 } 10963 10964 // Return bond-equivalent yield 10965 return 100 * (1 - discount * DAYS360(settlement, maturity, false) / 360); 10966 }; 10967 10968 exports.TBILLYIELD = function(settlement, maturity, price) { 10969 settlement = utils.parseDate(settlement); 10970 maturity = utils.parseDate(maturity); 10971 price = utils.parseNumber(price); 10972 if (utils.anyIsError(settlement, maturity, price)) { 10973 return error.value; 10974 } 10975 10976 // Return error if price is lower than or equal to zero 10977 if (price <= 0) { 10978 return error.num; 10979 } 10980 10981 // Return error if settlement is greater than maturity 10982 if (settlement > maturity) { 10983 return error.num; 10984 } 10985 10986 // Return error if maturity is more than one year after settlement 10987 if (maturity - settlement > 365 * 24 * 60 * 60 * 1000) { 10988 return error.num; 10989 } 10990 10991 // Return bond-equivalent yield 10992 return (100 - price) * 360 / (price * DAYS360(settlement, maturity, false)); 10993 }; 10994 10995 exports.VDB = null; 10996 10997 exports.XIRR = function(values, dates, guess) { 10998 // Credits: algorithm inspired by Apache OpenOffice 10999 11000 values = utils.parseNumberArray(utils.flatten(values)); 11001 dates = utils.parseDateArray(utils.flatten(dates)); 11002 guess = utils.parseNumber(guess); 11003 if (utils.anyIsError(values, dates, guess)) { 11004 return error.value; 11005 } 11006 11007 // Calculates the resulting amount 11008 var irrResult = function(values, dates, rate) { 11009 var r = rate + 1; 11010 var result = values[0]; 11011 for (var i = 1; i < values.length; i++) { 11012 result += values[i] / Math.pow(r, DAYS(dates[i], dates[0]) / 365); 11013 } 11014 return result; 11015 }; 11016 11017 // Calculates the first derivation 11018 var irrResultDeriv = function(values, dates, rate) { 11019 var r = rate + 1; 11020 var result = 0; 11021 for (var i = 1; i < values.length; i++) { 11022 var frac = DAYS(dates[i], dates[0]) / 365; 11023 result -= frac * values[i] / Math.pow(r, frac + 1); 11024 } 11025 return result; 11026 }; 11027 11028 // Check that values contains at least one positive value and one 11029 // negative value 11030 var positive = false; 11031 var negative = false; 11032 for (var i = 0; i < values.length; i++) { 11033 if (values[i] > 0) { 11034 positive = true; 11035 } 11036 if (values[i] < 0) { 11037 negative = true; 11038 } 11039 } 11040 11041 // Return error if values does not contain at least one positive value 11042 // and one negative value 11043 if (!positive || !negative) { 11044 return error.num; 11045 } 11046 11047 // Initialize guess and resultRate 11048 guess = guess || 0.1; 11049 var resultRate = guess; 11050 11051 // Set maximum epsilon for end of iteration 11052 var epsMax = 1e-10; 11053 11054 // Implement Newton's method 11055 var newRate, epsRate, resultValue; 11056 var contLoop = true; 11057 do { 11058 resultValue = irrResult(values, dates, resultRate); 11059 newRate = resultRate - resultValue / irrResultDeriv(values, dates, resultRate); 11060 epsRate = Math.abs(newRate - resultRate); 11061 resultRate = newRate; 11062 contLoop = (epsRate > epsMax) && (Math.abs(resultValue) > epsMax); 11063 } while (contLoop); 11064 11065 // Return internal rate of return 11066 return resultRate; 11067 }; 11068 11069 exports.XNPV = function(rate, values, dates) { 11070 rate = utils.parseNumber(rate); 11071 values = utils.parseNumberArray(utils.flatten(values)); 11072 dates = utils.parseDateArray(utils.flatten(dates)); 11073 if (utils.anyIsError(rate, values, dates)) { 11074 return error.value; 11075 } 11076 11077 var result = 0; 11078 for (var i = 0; i < values.length; i++) { 11079 result += values[i] / Math.pow(1 + rate, DAYS(dates[i], dates[0]) / 365); 11080 } 11081 return result; 11082 }; 11083 11084 exports.YIELD = null; 11085 11086 exports.YIELDDISC = null; 11087 11088 exports.YIELDMAT = null; 11089 11090 return exports; 11091 })(); 11092 11093 jexcel.methods.information = (function() { 11094 var exports = {}; 11095 exports.CELL = null; 11096 11097 exports.ERROR = {}; 11098 exports.ERROR.TYPE = function(error_val) { 11099 switch (error_val) { 11100 case error.nil: return 1; 11101 case error.div0: return 2; 11102 case error.value: return 3; 11103 case error.ref: return 4; 11104 case error.name: return 5; 11105 case error.num: return 6; 11106 case error.na: return 7; 11107 case error.data: return 8; 11108 } 11109 return error.na; 11110 }; 11111 11112 exports.INFO = null; 11113 11114 exports.ISBLANK = function(value) { 11115 return value === null; 11116 }; 11117 11118 exports.ISBINARY = function (number) { 11119 return (/^[01]{1,10}$/).test(number); 11120 }; 11121 11122 exports.ISERR = function(value) { 11123 return ([error.value, error.ref, error.div0, error.num, error.name, error.nil]).indexOf(value) >= 0 || 11124 (typeof value === 'number' && (isNaN(value) || !isFinite(value))); 11125 }; 11126 11127 exports.ISERROR = function(value) { 11128 return exports.ISERR(value) || value === error.na; 11129 }; 11130 11131 exports.ISEVEN = function(number) { 11132 return (Math.floor(Math.abs(number)) & 1) ? false : true; 11133 }; 11134 11135 // TODO 11136 exports.ISFORMULA = null; 11137 11138 exports.ISLOGICAL = function(value) { 11139 return value === true || value === false; 11140 }; 11141 11142 exports.ISNA = function(value) { 11143 return value === error.na; 11144 }; 11145 11146 exports.ISNONTEXT = function(value) { 11147 return typeof(value) !== 'string'; 11148 }; 11149 11150 exports.ISNUMBER = function(value) { 11151 return typeof(value) === 'number' && !isNaN(value) && isFinite(value); 11152 }; 11153 11154 exports.ISODD = function(number) { 11155 return (Math.floor(Math.abs(number)) & 1) ? true : false; 11156 }; 11157 11158 exports.ISREF = null; 11159 11160 exports.ISTEXT = function(value) { 11161 return typeof(value) === 'string'; 11162 }; 11163 11164 exports.N = function(value) { 11165 if (this.ISNUMBER(value)) { 11166 return value; 11167 } 11168 if (value instanceof Date) { 11169 return value.getTime(); 11170 } 11171 if (value === true) { 11172 return 1; 11173 } 11174 if (value === false) { 11175 return 0; 11176 } 11177 if (this.ISERROR(value)) { 11178 return value; 11179 } 11180 return 0; 11181 }; 11182 11183 exports.NA = function() { 11184 return error.na; 11185 }; 11186 11187 exports.SHEET = null; 11188 11189 exports.SHEETS = null; 11190 11191 exports.TYPE = function(value) { 11192 if (this.ISNUMBER(value)) { 11193 return 1; 11194 } 11195 if (this.ISTEXT(value)) { 11196 return 2; 11197 } 11198 if (this.ISLOGICAL(value)) { 11199 return 4; 11200 } 11201 if (this.ISERROR(value)) { 11202 return 16; 11203 } 11204 if (Array.isArray(value)) { 11205 return 64; 11206 } 11207 }; 11208 11209 return exports; 11210 })(); 11211 11212 jexcel.methods.logical = (function() { 11213 var exports = {}; 11214 11215 exports.AND = function() { 11216 var args = utils.flatten(arguments); 11217 var result = true; 11218 for (var i = 0; i < args.length; i++) { 11219 if (!args[i]) { 11220 result = false; 11221 } 11222 } 11223 return result; 11224 }; 11225 11226 exports.CHOOSE = function() { 11227 if (arguments.length < 2) { 11228 return error.na; 11229 } 11230 11231 var index = arguments[0]; 11232 if (index < 1 || index > 254) { 11233 return error.value; 11234 } 11235 11236 if (arguments.length < index + 1) { 11237 return error.value; 11238 } 11239 11240 return arguments[index]; 11241 }; 11242 11243 exports.FALSE = function() { 11244 return false; 11245 }; 11246 11247 exports.IF = function(test, then_value, otherwise_value) { 11248 return test ? then_value : otherwise_value; 11249 }; 11250 11251 exports.IFERROR = function(value, valueIfError) { 11252 if (ISERROR(value)) { 11253 return valueIfError; 11254 } 11255 return value; 11256 }; 11257 11258 exports.IFNA = function(value, value_if_na) { 11259 return value === error.na ? value_if_na : value; 11260 }; 11261 11262 exports.NOT = function(logical) { 11263 return !logical; 11264 }; 11265 11266 exports.OR = function() { 11267 var args = utils.flatten(arguments); 11268 var result = false; 11269 for (var i = 0; i < args.length; i++) { 11270 if (args[i]) { 11271 result = true; 11272 } 11273 } 11274 return result; 11275 }; 11276 11277 exports.TRUE = function() { 11278 return true; 11279 }; 11280 11281 exports.XOR = function() { 11282 var args = utils.flatten(arguments); 11283 var result = 0; 11284 for (var i = 0; i < args.length; i++) { 11285 if (args[i]) { 11286 result++; 11287 } 11288 } 11289 return (Math.floor(Math.abs(result)) & 1) ? true : false; 11290 }; 11291 11292 exports.SWITCH = function() { 11293 var result; 11294 if (arguments.length > 0) { 11295 var targetValue = arguments[0]; 11296 var argc = arguments.length - 1; 11297 var switchCount = Math.floor(argc / 2); 11298 var switchSatisfied = false; 11299 var defaultClause = argc % 2 === 0 ? null : arguments[arguments.length - 1]; 11300 11301 if (switchCount) { 11302 for (var index = 0; index < switchCount; index++) { 11303 if (targetValue === arguments[index * 2 + 1]) { 11304 result = arguments[index * 2 + 2]; 11305 switchSatisfied = true; 11306 break; 11307 } 11308 } 11309 } 11310 11311 if (!switchSatisfied && defaultClause) { 11312 result = defaultClause; 11313 } 11314 } 11315 11316 return result; 11317 }; 11318 11319 return exports; 11320 })(); 11321 11322 jexcel.methods.math = (function() { 11323 var exports = {}; 11324 11325 exports.ABS = function(number) { 11326 number = utils.parseNumber(number); 11327 if (number instanceof Error) { 11328 return number; 11329 } 11330 return Math.abs(utils.parseNumber(number)); 11331 }; 11332 11333 exports.ACOS = function(number) { 11334 number = utils.parseNumber(number); 11335 if (number instanceof Error) { 11336 return number; 11337 } 11338 return Math.acos(number); 11339 }; 11340 11341 exports.ACOSH = function(number) { 11342 number = utils.parseNumber(number); 11343 if (number instanceof Error) { 11344 return number; 11345 } 11346 return Math.log(number + Math.sqrt(number * number - 1)); 11347 }; 11348 11349 exports.ACOT = function(number) { 11350 number = utils.parseNumber(number); 11351 if (number instanceof Error) { 11352 return number; 11353 } 11354 return Math.atan(1 / number); 11355 }; 11356 11357 exports.ACOTH = function(number) { 11358 number = utils.parseNumber(number); 11359 if (number instanceof Error) { 11360 return number; 11361 } 11362 return 0.5 * Math.log((number + 1) / (number - 1)); 11363 }; 11364 11365 exports.AGGREGATE = null 11366 11367 exports.ARABIC = function(text) { 11368 // Credits: Rafa? Kukawski 11369 if (!/^M*(?:D?C{0,3}|C[MD])(?:L?X{0,3}|X[CL])(?:V?I{0,3}|I[XV])$/.test(text)) { 11370 return error.value; 11371 } 11372 var r = 0; 11373 text.replace(/[MDLV]|C[MD]?|X[CL]?|I[XV]?/g, function(i) { 11374 r += { 11375 M: 1000, 11376 CM: 900, 11377 D: 500, 11378 CD: 400, 11379 C: 100, 11380 XC: 90, 11381 L: 50, 11382 XL: 40, 11383 X: 10, 11384 IX: 9, 11385 V: 5, 11386 IV: 4, 11387 I: 1 11388 }[i]; 11389 }); 11390 return r; 11391 }; 11392 11393 exports.ASIN = function(number) { 11394 number = utils.parseNumber(number); 11395 if (number instanceof Error) { 11396 return number; 11397 } 11398 return Math.asin(number); 11399 }; 11400 11401 exports.ASINH = function(number) { 11402 number = utils.parseNumber(number); 11403 if (number instanceof Error) { 11404 return number; 11405 } 11406 return Math.log(number + Math.sqrt(number * number + 1)); 11407 }; 11408 11409 exports.ATAN = function(number) { 11410 number = utils.parseNumber(number); 11411 if (number instanceof Error) { 11412 return number; 11413 } 11414 return Math.atan(number); 11415 }; 11416 11417 exports.ATAN2 = function(number_x, number_y) { 11418 number_x = utils.parseNumber(number_x); 11419 number_y = utils.parseNumber(number_y); 11420 if (utils.anyIsError(number_x, number_y)) { 11421 return error.value; 11422 } 11423 return Math.atan2(number_x, number_y); 11424 }; 11425 11426 exports.ATANH = function(number) { 11427 number = utils.parseNumber(number); 11428 if (number instanceof Error) { 11429 return number; 11430 } 11431 return Math.log((1 + number) / (1 - number)) / 2; 11432 }; 11433 11434 exports.BASE = function(number, radix, min_length) { 11435 min_length = min_length || 0; 11436 11437 number = utils.parseNumber(number); 11438 radix = utils.parseNumber(radix); 11439 min_length = utils.parseNumber(min_length); 11440 if (utils.anyIsError(number, radix, min_length)) { 11441 return error.value; 11442 } 11443 min_length = (min_length === undefined) ? 0 : min_length; 11444 var result = number.toString(radix); 11445 return new Array(Math.max(min_length + 1 - result.length, 0)).join('0') + result; 11446 }; 11447 11448 exports.CEILING = function(number, significance, mode) { 11449 significance = (significance === undefined) ? 1 : significance; 11450 mode = (mode === undefined) ? 0 : mode; 11451 11452 number = utils.parseNumber(number); 11453 significance = utils.parseNumber(significance); 11454 mode = utils.parseNumber(mode); 11455 if (utils.anyIsError(number, significance, mode)) { 11456 return error.value; 11457 } 11458 if (significance === 0) { 11459 return 0; 11460 } 11461 11462 significance = Math.abs(significance); 11463 if (number >= 0) { 11464 return Math.ceil(number / significance) * significance; 11465 } else { 11466 if (mode === 0) { 11467 return -1 * Math.floor(Math.abs(number) / significance) * significance; 11468 } else { 11469 return -1 * Math.ceil(Math.abs(number) / significance) * significance; 11470 } 11471 } 11472 }; 11473 11474 exports.CEILING.MATH = exports.CEILING; 11475 11476 exports.CEILING.PRECISE = exports.CEILING; 11477 11478 exports.COMBIN = function(number, number_chosen) { 11479 number = utils.parseNumber(number); 11480 number_chosen = utils.parseNumber(number_chosen); 11481 if (utils.anyIsError(number, number_chosen)) { 11482 return error.value; 11483 } 11484 return exports.FACT(number) / (exports.FACT(number_chosen) * exports.FACT(number - number_chosen)); 11485 }; 11486 11487 exports.COMBINA = function(number, number_chosen) { 11488 number = utils.parseNumber(number); 11489 number_chosen = utils.parseNumber(number_chosen); 11490 if (utils.anyIsError(number, number_chosen)) { 11491 return error.value; 11492 } 11493 return (number === 0 && number_chosen === 0) ? 1 : exports.COMBIN(number + number_chosen - 1, number - 1); 11494 }; 11495 11496 exports.COS = function(number) { 11497 number = utils.parseNumber(number); 11498 if (number instanceof Error) { 11499 return number; 11500 } 11501 return Math.cos(number); 11502 }; 11503 11504 exports.COSH = function(number) { 11505 number = utils.parseNumber(number); 11506 if (number instanceof Error) { 11507 return number; 11508 } 11509 return (Math.exp(number) + Math.exp(-number)) / 2; 11510 }; 11511 11512 exports.COT = function(number) { 11513 number = utils.parseNumber(number); 11514 if (number instanceof Error) { 11515 return number; 11516 } 11517 return 1 / Math.tan(number); 11518 }; 11519 11520 exports.COTH = function(number) { 11521 number = utils.parseNumber(number); 11522 if (number instanceof Error) { 11523 return number; 11524 } 11525 var e2 = Math.exp(2 * number); 11526 return (e2 + 1) / (e2 - 1); 11527 }; 11528 11529 exports.CSC = function(number) { 11530 number = utils.parseNumber(number); 11531 if (number instanceof Error) { 11532 return number; 11533 } 11534 return 1 / Math.sin(number); 11535 }; 11536 11537 exports.CSCH = function(number) { 11538 number = utils.parseNumber(number); 11539 if (number instanceof Error) { 11540 return number; 11541 } 11542 return 2 / (Math.exp(number) - Math.exp(-number)); 11543 }; 11544 11545 exports.DECIMAL = function(number, radix) { 11546 if (arguments.length < 1) { 11547 return error.value; 11548 } 11549 11550 11551 return parseInt(number, radix); 11552 }; 11553 11554 exports.DEGREES = function(number) { 11555 number = utils.parseNumber(number); 11556 if (number instanceof Error) { 11557 return number; 11558 } 11559 return number * 180 / Math.PI; 11560 }; 11561 11562 exports.EVEN = function(number) { 11563 number = utils.parseNumber(number); 11564 if (number instanceof Error) { 11565 return number; 11566 } 11567 return exports.CEILING(number, -2, -1); 11568 }; 11569 11570 exports.EXP = Math.exp; 11571 11572 var MEMOIZED_FACT = []; 11573 exports.FACT = function(number) { 11574 number = utils.parseNumber(number); 11575 if (number instanceof Error) { 11576 return number; 11577 } 11578 var n = Math.floor(number); 11579 if (n === 0 || n === 1) { 11580 return 1; 11581 } else if (MEMOIZED_FACT[n] > 0) { 11582 return MEMOIZED_FACT[n]; 11583 } else { 11584 MEMOIZED_FACT[n] = exports.FACT(n - 1) * n; 11585 return MEMOIZED_FACT[n]; 11586 } 11587 }; 11588 11589 exports.FACTDOUBLE = function(number) { 11590 number = utils.parseNumber(number); 11591 if (number instanceof Error) { 11592 return number; 11593 } 11594 var n = Math.floor(number); 11595 if (n <= 0) { 11596 return 1; 11597 } else { 11598 return n * exports.FACTDOUBLE(n - 2); 11599 } 11600 }; 11601 11602 exports.FLOOR = function(number, significance, mode) { 11603 significance = (significance === undefined) ? 1 : significance; 11604 mode = (mode === undefined) ? 0 : mode; 11605 11606 number = utils.parseNumber(number); 11607 significance = utils.parseNumber(significance); 11608 mode = utils.parseNumber(mode); 11609 if (utils.anyIsError(number, significance, mode)) { 11610 return error.value; 11611 } 11612 if (significance === 0) { 11613 return 0; 11614 } 11615 11616 significance = Math.abs(significance); 11617 if (number >= 0) { 11618 return Math.floor(number / significance) * significance; 11619 } else { 11620 if (mode === 0) { 11621 return -1 * Math.ceil(Math.abs(number) / significance) * significance; 11622 } else { 11623 return -1 * Math.floor(Math.abs(number) / significance) * significance; 11624 } 11625 } 11626 }; 11627 11628 exports.FLOOR.MATH = exports.FLOOR; 11629 11630 exports.GCD = null; 11631 11632 exports.INT = function(number) { 11633 number = utils.parseNumber(number); 11634 if (number instanceof Error) { 11635 return number; 11636 } 11637 return Math.floor(number); 11638 }; 11639 11640 exports.LCM = function() { 11641 // Credits: Jonas Raoni Soares Silva 11642 var o = utils.parseNumberArray(utils.flatten(arguments)); 11643 if (o instanceof Error) { 11644 return o; 11645 } 11646 for (var i, j, n, d, r = 1; 11647 (n = o.pop()) !== undefined;) { 11648 while (n > 1) { 11649 if (n % 2) { 11650 for (i = 3, j = Math.floor(Math.sqrt(n)); i <= j && n % i; i += 2) { 11651 //empty 11652 } 11653 d = (i <= j) ? i : n; 11654 } else { 11655 d = 2; 11656 } 11657 for (n /= d, r *= d, i = o.length; i; 11658 (o[--i] % d) === 0 && (o[i] /= d) === 1 && o.splice(i, 1)) { 11659 //empty 11660 } 11661 } 11662 } 11663 return r; 11664 }; 11665 11666 exports.LN = function(number) { 11667 number = utils.parseNumber(number); 11668 if (number instanceof Error) { 11669 return number; 11670 } 11671 return Math.log(number); 11672 }; 11673 11674 exports.LOG = function(number, base) { 11675 number = utils.parseNumber(number); 11676 base = (base === undefined) ? 10 : utils.parseNumber(base); 11677 11678 if (utils.anyIsError(number, base)) { 11679 return error.value; 11680 } 11681 11682 return Math.log(number) / Math.log(base); 11683 }; 11684 11685 exports.LOG10 = function(number) { 11686 number = utils.parseNumber(number); 11687 if (number instanceof Error) { 11688 return number; 11689 } 11690 return Math.log(number) / Math.log(10); 11691 }; 11692 11693 exports.MDETERM = null; 11694 11695 exports.MINVERSE = null; 11696 11697 exports.MMULT = null; 11698 11699 exports.MOD = function(dividend, divisor) { 11700 dividend = utils.parseNumber(dividend); 11701 divisor = utils.parseNumber(divisor); 11702 if (utils.anyIsError(dividend, divisor)) { 11703 return error.value; 11704 } 11705 if (divisor === 0) { 11706 return error.div0; 11707 } 11708 var modulus = Math.abs(dividend % divisor); 11709 return (divisor > 0) ? modulus : -modulus; 11710 }; 11711 11712 exports.MROUND = function(number, multiple) { 11713 number = utils.parseNumber(number); 11714 multiple = utils.parseNumber(multiple); 11715 if (utils.anyIsError(number, multiple)) { 11716 return error.value; 11717 } 11718 if (number * multiple < 0) { 11719 return error.num; 11720 } 11721 11722 return Math.round(number / multiple) * multiple; 11723 }; 11724 11725 exports.MULTINOMIAL = function() { 11726 var args = utils.parseNumberArray(utils.flatten(arguments)); 11727 if (args instanceof Error) { 11728 return args; 11729 } 11730 var sum = 0; 11731 var divisor = 1; 11732 for (var i = 0; i < args.length; i++) { 11733 sum += args[i]; 11734 divisor *= exports.FACT(args[i]); 11735 } 11736 return exports.FACT(sum) / divisor; 11737 }; 11738 11739 exports.MUNIT = null; 11740 11741 exports.ODD = function(number) { 11742 number = utils.parseNumber(number); 11743 if (number instanceof Error) { 11744 return number; 11745 } 11746 var temp = Math.ceil(Math.abs(number)); 11747 temp = (temp & 1) ? temp : temp + 1; 11748 return (number > 0) ? temp : -temp; 11749 }; 11750 11751 exports.PI = function() { 11752 return Math.PI; 11753 }; 11754 11755 exports.POWER = function(number, power) { 11756 number = utils.parseNumber(number); 11757 power = utils.parseNumber(power); 11758 if (utils.anyIsError(number, power)) { 11759 return error.value; 11760 } 11761 var result = Math.pow(number, power); 11762 if (isNaN(result)) { 11763 return error.num; 11764 } 11765 11766 return result; 11767 }; 11768 11769 exports.PRODUCT = function() { 11770 var args = utils.parseNumberArray(utils.flatten(arguments)); 11771 if (args instanceof Error) { 11772 return args; 11773 } 11774 var result = 1; 11775 for (var i = 0; i < args.length; i++) { 11776 result *= args[i]; 11777 } 11778 return result; 11779 }; 11780 11781 exports.QUOTIENT = function(numerator, denominator) { 11782 numerator = utils.parseNumber(numerator); 11783 denominator = utils.parseNumber(denominator); 11784 if (utils.anyIsError(numerator, denominator)) { 11785 return error.value; 11786 } 11787 return parseInt(numerator / denominator, 10); 11788 }; 11789 11790 exports.RADIANS = function(number) { 11791 number = utils.parseNumber(number); 11792 if (number instanceof Error) { 11793 return number; 11794 } 11795 return number * Math.PI / 180; 11796 }; 11797 11798 exports.RAND = function() { 11799 return Math.random(); 11800 }; 11801 11802 exports.RANDBETWEEN = function(bottom, top) { 11803 bottom = utils.parseNumber(bottom); 11804 top = utils.parseNumber(top); 11805 if (utils.anyIsError(bottom, top)) { 11806 return error.value; 11807 } 11808 // Creative Commons Attribution 3.0 License 11809 // Copyright (c) 2012 eqcode 11810 return bottom + Math.ceil((top - bottom + 1) * Math.random()) - 1; 11811 }; 11812 11813 exports.ROMAN = null; 11814 11815 exports.ROUND = function(number, digits) { 11816 number = utils.parseNumber(number); 11817 digits = utils.parseNumber(digits); 11818 if (utils.anyIsError(number, digits)) { 11819 return error.value; 11820 } 11821 return Math.round(number * Math.pow(10, digits)) / Math.pow(10, digits); 11822 }; 11823 11824 exports.ROUNDDOWN = function(number, digits) { 11825 number = utils.parseNumber(number); 11826 digits = utils.parseNumber(digits); 11827 if (utils.anyIsError(number, digits)) { 11828 return error.value; 11829 } 11830 var sign = (number > 0) ? 1 : -1; 11831 return sign * (Math.floor(Math.abs(number) * Math.pow(10, digits))) / Math.pow(10, digits); 11832 }; 11833 11834 exports.ROUNDUP = function(number, digits) { 11835 number = utils.parseNumber(number); 11836 digits = utils.parseNumber(digits); 11837 if (utils.anyIsError(number, digits)) { 11838 return error.value; 11839 } 11840 var sign = (number > 0) ? 1 : -1; 11841 return sign * (Math.ceil(Math.abs(number) * Math.pow(10, digits))) / Math.pow(10, digits); 11842 }; 11843 11844 exports.SEC = function(number) { 11845 number = utils.parseNumber(number); 11846 if (number instanceof Error) { 11847 return number; 11848 } 11849 return 1 / Math.cos(number); 11850 }; 11851 11852 exports.SECH = function(number) { 11853 number = utils.parseNumber(number); 11854 if (number instanceof Error) { 11855 return number; 11856 } 11857 return 2 / (Math.exp(number) + Math.exp(-number)); 11858 }; 11859 11860 exports.SERIESSUM = function(x, n, m, coefficients) { 11861 x = utils.parseNumber(x); 11862 n = utils.parseNumber(n); 11863 m = utils.parseNumber(m); 11864 coefficients = utils.parseNumberArray(coefficients); 11865 if (utils.anyIsError(x, n, m, coefficients)) { 11866 return error.value; 11867 } 11868 var result = coefficients[0] * Math.pow(x, n); 11869 for (var i = 1; i < coefficients.length; i++) { 11870 result += coefficients[i] * Math.pow(x, n + i * m); 11871 } 11872 return result; 11873 }; 11874 11875 exports.SIGN = function(number) { 11876 number = utils.parseNumber(number); 11877 if (number instanceof Error) { 11878 return number; 11879 } 11880 if (number < 0) { 11881 return -1; 11882 } else if (number === 0) { 11883 return 0; 11884 } else { 11885 return 1; 11886 } 11887 }; 11888 11889 exports.SIN = function(number) { 11890 number = utils.parseNumber(number); 11891 if (number instanceof Error) { 11892 return number; 11893 } 11894 return Math.sin(number); 11895 }; 11896 11897 exports.SINH = function(number) { 11898 number = utils.parseNumber(number); 11899 if (number instanceof Error) { 11900 return number; 11901 } 11902 return (Math.exp(number) - Math.exp(-number)) / 2; 11903 }; 11904 11905 exports.SQRT = function(number) { 11906 number = utils.parseNumber(number); 11907 if (number instanceof Error) { 11908 return number; 11909 } 11910 if (number < 0) { 11911 return error.num; 11912 } 11913 return Math.sqrt(number); 11914 }; 11915 11916 exports.SQRTPI = function(number) { 11917 number = utils.parseNumber(number); 11918 if (number instanceof Error) { 11919 return number; 11920 } 11921 return Math.sqrt(number * Math.PI); 11922 }; 11923 11924 exports.SUBTOTAL = null; 11925 11926 exports.ADD = function (num1, num2) { 11927 if (arguments.length !== 2) { 11928 return error.na; 11929 } 11930 11931 num1 = utils.parseNumber(num1); 11932 num2 = utils.parseNumber(num2); 11933 if (utils.anyIsError(num1, num2)) { 11934 return error.value; 11935 } 11936 11937 return num1 + num2; 11938 }; 11939 11940 exports.MINUS = function (num1, num2) { 11941 if (arguments.length !== 2) { 11942 return error.na; 11943 } 11944 11945 num1 = utils.parseNumber(num1); 11946 num2 = utils.parseNumber(num2); 11947 if (utils.anyIsError(num1, num2)) { 11948 return error.value; 11949 } 11950 11951 return num1 - num2; 11952 }; 11953 11954 exports.DIVIDE = function (dividend, divisor) { 11955 if (arguments.length !== 2) { 11956 return error.na; 11957 } 11958 11959 dividend = utils.parseNumber(dividend); 11960 divisor = utils.parseNumber(divisor); 11961 if (utils.anyIsError(dividend, divisor)) { 11962 return error.value; 11963 } 11964 11965 if (divisor === 0) { 11966 return error.div0; 11967 } 11968 11969 return dividend / divisor; 11970 }; 11971 11972 exports.MULTIPLY = function (factor1, factor2) { 11973 if (arguments.length !== 2) { 11974 return error.na; 11975 } 11976 11977 factor1 = utils.parseNumber(factor1); 11978 factor2 = utils.parseNumber(factor2); 11979 if (utils.anyIsError(factor1, factor2)) { 11980 return error.value; 11981 } 11982 11983 return factor1 * factor2; 11984 }; 11985 11986 exports.GTE = function (num1, num2) { 11987 if (arguments.length !== 2) { 11988 return error.na; 11989 } 11990 11991 num1 = utils.parseNumber(num1); 11992 num2 = utils.parseNumber(num2); 11993 if (utils.anyIsError(num1, num2)) { 11994 return error.error; 11995 } 11996 11997 return num1 >= num2; 11998 }; 11999 12000 exports.LT = function (num1, num2) { 12001 if (arguments.length !== 2) { 12002 return error.na; 12003 } 12004 12005 num1 = utils.parseNumber(num1); 12006 num2 = utils.parseNumber(num2); 12007 if (utils.anyIsError(num1, num2)) { 12008 return error.error; 12009 } 12010 12011 return num1 < num2; 12012 }; 12013 12014 exports.LTE = function (num1, num2) { 12015 if (arguments.length !== 2) { 12016 return error.na; 12017 } 12018 12019 num1 = utils.parseNumber(num1); 12020 num2 = utils.parseNumber(num2); 12021 if (utils.anyIsError(num1, num2)) { 12022 return error.error; 12023 } 12024 12025 return num1 <= num2; 12026 }; 12027 12028 exports.EQ = function (value1, value2) { 12029 if (arguments.length !== 2) { 12030 return error.na; 12031 } 12032 12033 return value1 === value2; 12034 }; 12035 12036 exports.NE = function (value1, value2) { 12037 if (arguments.length !== 2) { 12038 return error.na; 12039 } 12040 12041 return value1 !== value2; 12042 }; 12043 12044 exports.POW = function (base, exponent) { 12045 if (arguments.length !== 2) { 12046 return error.na; 12047 } 12048 12049 base = utils.parseNumber(base); 12050 exponent = utils.parseNumber(exponent); 12051 if (utils.anyIsError(base, exponent)) { 12052 return error.error; 12053 } 12054 12055 return exports.POWER(base, exponent); 12056 }; 12057 12058 exports.SUM = function() { 12059 var result = 0; 12060 var argsKeys = Object.keys(arguments); 12061 for (var i = 0; i < argsKeys.length; ++i) { 12062 var elt = arguments[argsKeys[i]]; 12063 if (typeof elt === 'number') { 12064 result += elt; 12065 } else if (typeof elt === 'string') { 12066 var parsed = parseFloat(elt); 12067 !isNaN(parsed) && (result += parsed); 12068 } else if (Array.isArray(elt)) { 12069 result += exports.SUM.apply(null, elt); 12070 } 12071 } 12072 return result; 12073 }; 12074 12075 exports.SUMIF = function(range, criteria) { 12076 range = utils.parseNumberArray(utils.flatten(range)); 12077 if (range instanceof Error) { 12078 return range; 12079 } 12080 var result = 0; 12081 for (var i = 0; i < range.length; i++) { 12082 result += (eval(range[i] + criteria)) ? range[i] : 0; // jshint ignore:line 12083 } 12084 return result; 12085 }; 12086 12087 exports.SUMIFS = function() { 12088 var args = utils.argsToArray(arguments); 12089 var range = utils.parseNumberArray(utils.flatten(args.shift())); 12090 if (range instanceof Error) { 12091 return range; 12092 } 12093 var criteria = args; 12094 12095 var n_range_elements = range.length; 12096 var n_criterias = criteria.length; 12097 12098 var result = 0; 12099 for (var i = 0; i < n_range_elements; i++) { 12100 var el = range[i]; 12101 var condition = ''; 12102 for (var c = 0; c < n_criterias; c++) { 12103 condition += el + criteria[c]; 12104 if (c !== n_criterias - 1) { 12105 condition += '&&'; 12106 } 12107 } 12108 if (eval(condition)) { // jshint ignore:line 12109 result += el; 12110 } 12111 } 12112 return result; 12113 }; 12114 12115 exports.SUMPRODUCT = null; 12116 12117 exports.SUMSQ = function() { 12118 var numbers = utils.parseNumberArray(utils.flatten(arguments)); 12119 if (numbers instanceof Error) { 12120 return numbers; 12121 } 12122 var result = 0; 12123 var length = numbers.length; 12124 for (var i = 0; i < length; i++) { 12125 result += (ISNUMBER(numbers[i])) ? numbers[i] * numbers[i] : 0; 12126 } 12127 return result; 12128 }; 12129 12130 exports.SUMX2MY2 = function(array_x, array_y) { 12131 array_x = utils.parseNumberArray(utils.flatten(array_x)); 12132 array_y = utils.parseNumberArray(utils.flatten(array_y)); 12133 if (utils.anyIsError(array_x, array_y)) { 12134 return error.value; 12135 } 12136 var result = 0; 12137 for (var i = 0; i < array_x.length; i++) { 12138 result += array_x[i] * array_x[i] - array_y[i] * array_y[i]; 12139 } 12140 return result; 12141 }; 12142 12143 exports.SUMX2PY2 = function(array_x, array_y) { 12144 array_x = utils.parseNumberArray(utils.flatten(array_x)); 12145 array_y = utils.parseNumberArray(utils.flatten(array_y)); 12146 if (utils.anyIsError(array_x, array_y)) { 12147 return error.value; 12148 } 12149 var result = 0; 12150 array_x = utils.parseNumberArray(utils.flatten(array_x)); 12151 array_y = utils.parseNumberArray(utils.flatten(array_y)); 12152 for (var i = 0; i < array_x.length; i++) { 12153 result += array_x[i] * array_x[i] + array_y[i] * array_y[i]; 12154 } 12155 return result; 12156 }; 12157 12158 exports.SUMXMY2 = function(array_x, array_y) { 12159 array_x = utils.parseNumberArray(utils.flatten(array_x)); 12160 array_y = utils.parseNumberArray(utils.flatten(array_y)); 12161 if (utils.anyIsError(array_x, array_y)) { 12162 return error.value; 12163 } 12164 var result = 0; 12165 array_x = utils.flatten(array_x); 12166 array_y = utils.flatten(array_y); 12167 for (var i = 0; i < array_x.length; i++) { 12168 result += Math.pow(array_x[i] - array_y[i], 2); 12169 } 12170 return result; 12171 }; 12172 12173 exports.TAN = function(number) { 12174 number = utils.parseNumber(number); 12175 if (number instanceof Error) { 12176 return number; 12177 } 12178 return Math.tan(number); 12179 }; 12180 12181 exports.TANH = function(number) { 12182 number = utils.parseNumber(number); 12183 if (number instanceof Error) { 12184 return number; 12185 } 12186 var e2 = Math.exp(2 * number); 12187 return (e2 - 1) / (e2 + 1); 12188 }; 12189 12190 exports.TRUNC = function(number, digits) { 12191 digits = (digits === undefined) ? 0 : digits; 12192 number = utils.parseNumber(number); 12193 digits = utils.parseNumber(digits); 12194 if (utils.anyIsError(number, digits)) { 12195 return error.value; 12196 } 12197 var sign = (number > 0) ? 1 : -1; 12198 return sign * (Math.floor(Math.abs(number) * Math.pow(10, digits))) / Math.pow(10, digits); 12199 }; 12200 12201 return exports; 12202 })(); 12203 12204 jexcel.methods.misc = (function() { 12205 var exports = {}; 12206 12207 exports.UNIQUE = function () { 12208 var result = []; 12209 for (var i = 0; i < arguments.length; ++i) { 12210 var hasElement = false; 12211 var element = arguments[i]; 12212 12213 // Check if we've already seen this element. 12214 for (var j = 0; j < result.length; ++j) { 12215 hasElement = result[j] === element; 12216 if (hasElement) { break; } 12217 } 12218 12219 // If we did not find it, add it to the result. 12220 if (!hasElement) { 12221 result.push(element); 12222 } 12223 } 12224 return result; 12225 }; 12226 12227 exports.FLATTEN = utils.flatten; 12228 12229 exports.ARGS2ARRAY = function () { 12230 return Array.prototype.slice.call(arguments, 0); 12231 }; 12232 12233 exports.REFERENCE = function (context, reference) { 12234 try { 12235 var path = reference.split('.'); 12236 var result = context; 12237 for (var i = 0; i < path.length; ++i) { 12238 var step = path[i]; 12239 if (step[step.length - 1] === ']') { 12240 var opening = step.indexOf('['); 12241 var index = step.substring(opening + 1, step.length - 1); 12242 result = result[step.substring(0, opening)][index]; 12243 } else { 12244 result = result[step]; 12245 } 12246 } 12247 return result; 12248 } catch (error) {} 12249 }; 12250 12251 exports.JOIN = function (array, separator) { 12252 return array.join(separator); 12253 }; 12254 12255 exports.NUMBERS = function () { 12256 var possibleNumbers = utils.flatten(arguments); 12257 return possibleNumbers.filter(function (el) { 12258 return typeof el === 'number'; 12259 }); 12260 }; 12261 12262 exports.NUMERAL = null; 12263 12264 return exports; 12265 })(); 12266 12267 jexcel.methods.text = (function() { 12268 var exports = {}; 12269 12270 exports.ASC = null; 12271 12272 exports.BAHTTEXT = null; 12273 12274 exports.CHAR = function(number) { 12275 number = utils.parseNumber(number); 12276 if (number instanceof Error) { 12277 return number; 12278 } 12279 return String.fromCharCode(number); 12280 }; 12281 12282 exports.CLEAN = function(text) { 12283 text = text || ''; 12284 var re = /[\0-\x1F]/g; 12285 return text.replace(re, ""); 12286 }; 12287 12288 exports.CODE = function(text) { 12289 text = text || ''; 12290 return text.charCodeAt(0); 12291 }; 12292 12293 exports.CONCATENATE = function() { 12294 var args = utils.flatten(arguments); 12295 12296 var trueFound = 0; 12297 while ((trueFound = args.indexOf(true)) > -1) { 12298 args[trueFound] = 'TRUE'; 12299 } 12300 12301 var falseFound = 0; 12302 while ((falseFound = args.indexOf(false)) > -1) { 12303 args[falseFound] = 'FALSE'; 12304 } 12305 12306 return args.join(''); 12307 }; 12308 12309 exports.DBCS = null; 12310 12311 exports.DOLLAR = null; 12312 12313 exports.EXACT = function(text1, text2) { 12314 return text1 === text2; 12315 }; 12316 12317 exports.FIND = function(find_text, within_text, position) { 12318 position = (position === undefined) ? 0 : position; 12319 return within_text ? within_text.indexOf(find_text, position - 1) + 1 : null; 12320 }; 12321 12322 exports.FIXED = null; 12323 12324 exports.HTML2TEXT = function (value) { 12325 var result = ''; 12326 12327 if (value) { 12328 if (value instanceof Array) { 12329 value.forEach(function (line) { 12330 if (result !== '') { 12331 result += '\n'; 12332 } 12333 result += (line.replace(/<(?:.|\n)*?>/gm, '')); 12334 }); 12335 } else { 12336 result = value.replace(/<(?:.|\n)*?>/gm, ''); 12337 } 12338 } 12339 12340 return result; 12341 }; 12342 12343 exports.LEFT = function(text, number) { 12344 number = (number === undefined) ? 1 : number; 12345 number = utils.parseNumber(number); 12346 if (number instanceof Error || typeof text !== 'string') { 12347 return error.value; 12348 } 12349 return text ? text.substring(0, number) : null; 12350 }; 12351 12352 exports.LEN = function(text) { 12353 if (arguments.length === 0) { 12354 return error.error; 12355 } 12356 12357 if (typeof text === 'string') { 12358 return text ? text.length : 0; 12359 } 12360 12361 if (text.length) { 12362 return text.length; 12363 } 12364 12365 return error.value; 12366 }; 12367 12368 exports.LOWER = function(text) { 12369 if (typeof text !== 'string') { 12370 return error.value; 12371 } 12372 return text ? text.toLowerCase() : text; 12373 }; 12374 12375 exports.MID = function(text, start, number) { 12376 start = utils.parseNumber(start); 12377 number = utils.parseNumber(number); 12378 if (utils.anyIsError(start, number) || typeof text !== 'string') { 12379 return number; 12380 } 12381 12382 var begin = start - 1; 12383 var end = begin + number; 12384 12385 return text.substring(begin, end); 12386 }; 12387 12388 exports.NUMBERVALUE = null; 12389 12390 exports.PRONETIC = null; 12391 12392 exports.PROPER = function(text) { 12393 if (text === undefined || text.length === 0) { 12394 return error.value; 12395 } 12396 if (text === true) { 12397 text = 'TRUE'; 12398 } 12399 if (text === false) { 12400 text = 'FALSE'; 12401 } 12402 if (isNaN(text) && typeof text === 'number') { 12403 return error.value; 12404 } 12405 if (typeof text === 'number') { 12406 text = '' + text; 12407 } 12408 12409 return text.replace(/\w\S*/g, function(txt) { 12410 return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); 12411 }); 12412 }; 12413 12414 exports.REGEXEXTRACT = function (text, regular_expression) { 12415 var match = text.match(new RegExp(regular_expression)); 12416 return match ? (match[match.length > 1 ? match.length - 1 : 0]) : null; 12417 }; 12418 12419 exports.REGEXMATCH = function (text, regular_expression, full) { 12420 var match = text.match(new RegExp(regular_expression)); 12421 return full ? match : !!match; 12422 }; 12423 12424 exports.REGEXREPLACE = function (text, regular_expression, replacement) { 12425 return text.replace(new RegExp(regular_expression), replacement); 12426 }; 12427 12428 exports.REPLACE = function(text, position, length, new_text) { 12429 position = utils.parseNumber(position); 12430 length = utils.parseNumber(length); 12431 if (utils.anyIsError(position, length) || 12432 typeof text !== 'string' || 12433 typeof new_text !== 'string') { 12434 return error.value; 12435 } 12436 return text.substr(0, position - 1) + new_text + text.substr(position - 1 + length); 12437 }; 12438 12439 exports.REPT = function(text, number) { 12440 number = utils.parseNumber(number); 12441 if (number instanceof Error) { 12442 return number; 12443 } 12444 return new Array(number + 1).join(text); 12445 }; 12446 12447 exports.RIGHT = function(text, number) { 12448 number = (number === undefined) ? 1 : number; 12449 number = utils.parseNumber(number); 12450 if (number instanceof Error) { 12451 return number; 12452 } 12453 return text ? text.substring(text.length - number) : null; 12454 }; 12455 12456 exports.SEARCH = function(find_text, within_text, position) { 12457 var foundAt; 12458 if (typeof find_text !== 'string' || typeof within_text !== 'string') { 12459 return error.value; 12460 } 12461 position = (position === undefined) ? 0 : position; 12462 foundAt = within_text.toLowerCase().indexOf(find_text.toLowerCase(), position - 1)+1; 12463 return (foundAt === 0)?error.value:foundAt; 12464 }; 12465 12466 exports.SPLIT = function (text, separator) { 12467 return text.split(separator); 12468 }; 12469 12470 exports.SUBSTITUTE = function(text, old_text, new_text, occurrence) { 12471 if (!text || !old_text || !new_text) { 12472 return text; 12473 } else if (occurrence === undefined) { 12474 return text.replace(new RegExp(old_text, 'g'), new_text); 12475 } else { 12476 var index = 0; 12477 var i = 0; 12478 while (text.indexOf(old_text, index) > 0) { 12479 index = text.indexOf(old_text, index + 1); 12480 i++; 12481 if (i === occurrence) { 12482 return text.substring(0, index) + new_text + text.substring(index + old_text.length); 12483 } 12484 } 12485 } 12486 }; 12487 12488 exports.T = function(value) { 12489 return (typeof value === "string") ? value : ''; 12490 }; 12491 12492 exports.TEXT = null; 12493 12494 exports.TRIM = function(text) { 12495 if (typeof text !== 'string') { 12496 return error.value; 12497 } 12498 return text.replace(/ +/g, ' ').trim(); 12499 }; 12500 12501 exports.UNICHAR = exports.CHAR; 12502 12503 exports.UNICODE = exports.CODE; 12504 12505 exports.UPPER = function(text) { 12506 if (typeof text !== 'string') { 12507 return error.value; 12508 } 12509 return text.toUpperCase(); 12510 }; 12511 12512 exports.VALUE = null; 12513 12514 return exports; 12515 })(); 12516 12517 jexcel.methods.stats = (function() { 12518 var exports = {}; 12519 12520 var SQRT2PI = 2.5066282746310002; 12521 12522 exports.AVEDEV = null; 12523 12524 exports.AVERAGE = function() { 12525 var range = utils.numbers(utils.flatten(arguments)); 12526 var n = range.length; 12527 var sum = 0; 12528 var count = 0; 12529 for (var i = 0; i < n; i++) { 12530 sum += range[i]; 12531 count += 1; 12532 } 12533 return sum / count; 12534 }; 12535 12536 exports.AVERAGEA = function() { 12537 var range = utils.flatten(arguments); 12538 var n = range.length; 12539 var sum = 0; 12540 var count = 0; 12541 for (var i = 0; i < n; i++) { 12542 var el = range[i]; 12543 if (typeof el === 'number') { 12544 sum += el; 12545 } 12546 if (el === true) { 12547 sum++; 12548 } 12549 if (el !== null) { 12550 count++; 12551 } 12552 } 12553 return sum / count; 12554 }; 12555 12556 exports.AVERAGEIF = function(range, criteria, average_range) { 12557 average_range = average_range || range; 12558 range = utils.flatten(range); 12559 average_range = utils.parseNumberArray(utils.flatten(average_range)); 12560 if (average_range instanceof Error) { 12561 return average_range; 12562 } 12563 var average_count = 0; 12564 var result = 0; 12565 for (var i = 0; i < range.length; i++) { 12566 if (eval(range[i] + criteria)) { // jshint ignore:line 12567 result += average_range[i]; 12568 average_count++; 12569 } 12570 } 12571 return result / average_count; 12572 }; 12573 12574 exports.AVERAGEIFS = null; 12575 12576 exports.COUNT = function() { 12577 return utils.numbers(utils.flatten(arguments)).length; 12578 }; 12579 12580 exports.COUNTA = function() { 12581 var range = utils.flatten(arguments); 12582 return range.length - exports.COUNTBLANK(range); 12583 }; 12584 12585 exports.COUNTIN = function (range, value) { 12586 var result = 0; 12587 for (var i = 0; i < range.length; i++) { 12588 if (range[i] === value) { 12589 result++; 12590 } 12591 } 12592 return result; 12593 }; 12594 12595 exports.COUNTBLANK = function() { 12596 var range = utils.flatten(arguments); 12597 var blanks = 0; 12598 var element; 12599 for (var i = 0; i < range.length; i++) { 12600 element = range[i]; 12601 if (element === null || element === '') { 12602 blanks++; 12603 } 12604 } 12605 return blanks; 12606 }; 12607 12608 exports.COUNTIF = function(range, criteria) { 12609 range = utils.flatten(range); 12610 if (!/[<>=!]/.test(criteria)) { 12611 criteria = '=="' + criteria + '"'; 12612 } 12613 var matches = 0; 12614 for (var i = 0; i < range.length; i++) { 12615 if (typeof range[i] !== 'string') { 12616 if (eval(range[i] + criteria)) { // jshint ignore:line 12617 matches++; 12618 } 12619 } else { 12620 if (eval('"' + range[i] + '"' + criteria)) { // jshint ignore:line 12621 matches++; 12622 } 12623 } 12624 } 12625 return matches; 12626 }; 12627 12628 exports.COUNTIFS = function() { 12629 var args = utils.argsToArray(arguments); 12630 var results = new Array(utils.flatten(args[0]).length); 12631 for (var i = 0; i < results.length; i++) { 12632 results[i] = true; 12633 } 12634 for (i = 0; i < args.length; i += 2) { 12635 var range = utils.flatten(args[i]); 12636 var criteria = args[i + 1]; 12637 if (!/[<>=!]/.test(criteria)) { 12638 criteria = '=="' + criteria + '"'; 12639 } 12640 for (var j = 0; j < range.length; j++) { 12641 if (typeof range[j] !== 'string') { 12642 results[j] = results[j] && eval(range[j] + criteria); // jshint ignore:line 12643 } else { 12644 results[j] = results[j] && eval('"' + range[j] + '"' + criteria); // jshint ignore:line 12645 } 12646 } 12647 } 12648 var result = 0; 12649 for (i = 0; i < results.length; i++) { 12650 if (results[i]) { 12651 result++; 12652 } 12653 } 12654 return result; 12655 }; 12656 12657 exports.COUNTUNIQUE = function () { 12658 return UNIQUE.apply(null, utils.flatten(arguments)).length; 12659 }; 12660 12661 exports.FISHER = function(x) { 12662 x = utils.parseNumber(x); 12663 if (x instanceof Error) { 12664 return x; 12665 } 12666 return Math.log((1 + x) / (1 - x)) / 2; 12667 }; 12668 12669 exports.FISHERINV = function(y) { 12670 y = utils.parseNumber(y); 12671 if (y instanceof Error) { 12672 return y; 12673 } 12674 var e2y = Math.exp(2 * y); 12675 return (e2y - 1) / (e2y + 1); 12676 }; 12677 12678 exports.FREQUENCY = function(data, bins) { 12679 data = utils.parseNumberArray(utils.flatten(data)); 12680 bins = utils.parseNumberArray(utils.flatten(bins)); 12681 if (utils.anyIsError(data, bins)) { 12682 return error.value; 12683 } 12684 var n = data.length; 12685 var b = bins.length; 12686 var r = []; 12687 for (var i = 0; i <= b; i++) { 12688 r[i] = 0; 12689 for (var j = 0; j < n; j++) { 12690 if (i === 0) { 12691 if (data[j] <= bins[0]) { 12692 r[0] += 1; 12693 } 12694 } else if (i < b) { 12695 if (data[j] > bins[i - 1] && data[j] <= bins[i]) { 12696 r[i] += 1; 12697 } 12698 } else if (i === b) { 12699 if (data[j] > bins[b - 1]) { 12700 r[b] += 1; 12701 } 12702 } 12703 } 12704 } 12705 return r; 12706 }; 12707 12708 exports.LARGE = function(range, k) { 12709 range = utils.parseNumberArray(utils.flatten(range)); 12710 k = utils.parseNumber(k); 12711 if (utils.anyIsError(range, k)) { 12712 return range; 12713 } 12714 return range.sort(function(a, b) { 12715 return b - a; 12716 })[k - 1]; 12717 }; 12718 12719 exports.MAX = function() { 12720 var range = utils.numbers(utils.flatten(arguments)); 12721 return (range.length === 0) ? 0 : Math.max.apply(Math, range); 12722 }; 12723 12724 exports.MAXA = function() { 12725 var range = utils.arrayValuesToNumbers(utils.flatten(arguments)); 12726 return (range.length === 0) ? 0 : Math.max.apply(Math, range); 12727 }; 12728 12729 exports.MIN = function() { 12730 var range = utils.numbers(utils.flatten(arguments)); 12731 return (range.length === 0) ? 0 : Math.min.apply(Math, range); 12732 }; 12733 12734 exports.MINA = function() { 12735 var range = utils.arrayValuesToNumbers(utils.flatten(arguments)); 12736 return (range.length === 0) ? 0 : Math.min.apply(Math, range); 12737 }; 12738 12739 exports.MODE = {}; 12740 12741 exports.MODE.MULT = function() { 12742 // Credits: Roönaän 12743 var range = utils.parseNumberArray(utils.flatten(arguments)); 12744 if (range instanceof Error) { 12745 return range; 12746 } 12747 var n = range.length; 12748 var count = {}; 12749 var maxItems = []; 12750 var max = 0; 12751 var currentItem; 12752 12753 for (var i = 0; i < n; i++) { 12754 currentItem = range[i]; 12755 count[currentItem] = count[currentItem] ? count[currentItem] + 1 : 1; 12756 if (count[currentItem] > max) { 12757 max = count[currentItem]; 12758 maxItems = []; 12759 } 12760 if (count[currentItem] === max) { 12761 maxItems[maxItems.length] = currentItem; 12762 } 12763 } 12764 return maxItems; 12765 }; 12766 12767 exports.MODE.SNGL = function() { 12768 var range = utils.parseNumberArray(utils.flatten(arguments)); 12769 if (range instanceof Error) { 12770 return range; 12771 } 12772 return exports.MODE.MULT(range).sort(function(a, b) { 12773 return a - b; 12774 })[0]; 12775 }; 12776 12777 exports.PERCENTILE = {}; 12778 12779 exports.PERCENTILE.EXC = function(array, k) { 12780 array = utils.parseNumberArray(utils.flatten(array)); 12781 k = utils.parseNumber(k); 12782 if (utils.anyIsError(array, k)) { 12783 return error.value; 12784 } 12785 array = array.sort(function(a, b) { 12786 { 12787 return a - b; 12788 } 12789 }); 12790 var n = array.length; 12791 if (k < 1 / (n + 1) || k > 1 - 1 / (n + 1)) { 12792 return error.num; 12793 } 12794 var l = k * (n + 1) - 1; 12795 var fl = Math.floor(l); 12796 return utils.cleanFloat((l === fl) ? array[l] : array[fl] + (l - fl) * (array[fl + 1] - array[fl])); 12797 }; 12798 12799 exports.PERCENTILE.INC = function(array, k) { 12800 array = utils.parseNumberArray(utils.flatten(array)); 12801 k = utils.parseNumber(k); 12802 if (utils.anyIsError(array, k)) { 12803 return error.value; 12804 } 12805 array = array.sort(function(a, b) { 12806 return a - b; 12807 }); 12808 var n = array.length; 12809 var l = k * (n - 1); 12810 var fl = Math.floor(l); 12811 return utils.cleanFloat((l === fl) ? array[l] : array[fl] + (l - fl) * (array[fl + 1] - array[fl])); 12812 }; 12813 12814 exports.PERCENTRANK = {}; 12815 12816 exports.PERCENTRANK.EXC = function(array, x, significance) { 12817 significance = (significance === undefined) ? 3 : significance; 12818 array = utils.parseNumberArray(utils.flatten(array)); 12819 x = utils.parseNumber(x); 12820 significance = utils.parseNumber(significance); 12821 if (utils.anyIsError(array, x, significance)) { 12822 return error.value; 12823 } 12824 array = array.sort(function(a, b) { 12825 return a - b; 12826 }); 12827 var uniques = UNIQUE.apply(null, array); 12828 var n = array.length; 12829 var m = uniques.length; 12830 var power = Math.pow(10, significance); 12831 var result = 0; 12832 var match = false; 12833 var i = 0; 12834 while (!match && i < m) { 12835 if (x === uniques[i]) { 12836 result = (array.indexOf(uniques[i]) + 1) / (n + 1); 12837 match = true; 12838 } else if (x >= uniques[i] && (x < uniques[i + 1] || i === m - 1)) { 12839 result = (array.indexOf(uniques[i]) + 1 + (x - uniques[i]) / (uniques[i + 1] - uniques[i])) / (n + 1); 12840 match = true; 12841 } 12842 i++; 12843 } 12844 return Math.floor(result * power) / power; 12845 }; 12846 12847 exports.PERCENTRANK.INC = function(array, x, significance) { 12848 significance = (significance === undefined) ? 3 : significance; 12849 array = utils.parseNumberArray(utils.flatten(array)); 12850 x = utils.parseNumber(x); 12851 significance = utils.parseNumber(significance); 12852 if (utils.anyIsError(array, x, significance)) { 12853 return error.value; 12854 } 12855 array = array.sort(function(a, b) { 12856 return a - b; 12857 }); 12858 var uniques = UNIQUE.apply(null, array); 12859 var n = array.length; 12860 var m = uniques.length; 12861 var power = Math.pow(10, significance); 12862 var result = 0; 12863 var match = false; 12864 var i = 0; 12865 while (!match && i < m) { 12866 if (x === uniques[i]) { 12867 result = array.indexOf(uniques[i]) / (n - 1); 12868 match = true; 12869 } else if (x >= uniques[i] && (x < uniques[i + 1] || i === m - 1)) { 12870 result = (array.indexOf(uniques[i]) + (x - uniques[i]) / (uniques[i + 1] - uniques[i])) / (n - 1); 12871 match = true; 12872 } 12873 i++; 12874 } 12875 return Math.floor(result * power) / power; 12876 }; 12877 12878 exports.PERMUT = function(number, number_chosen) { 12879 number = utils.parseNumber(number); 12880 number_chosen = utils.parseNumber(number_chosen); 12881 if (utils.anyIsError(number, number_chosen)) { 12882 return error.value; 12883 } 12884 return FACT(number) / FACT(number - number_chosen); 12885 }; 12886 12887 exports.PERMUTATIONA = function(number, number_chosen) { 12888 number = utils.parseNumber(number); 12889 number_chosen = utils.parseNumber(number_chosen); 12890 if (utils.anyIsError(number, number_chosen)) { 12891 return error.value; 12892 } 12893 return Math.pow(number, number_chosen); 12894 }; 12895 12896 exports.PHI = function(x) { 12897 x = utils.parseNumber(x); 12898 if (x instanceof Error) { 12899 return error.value; 12900 } 12901 return Math.exp(-0.5 * x * x) / SQRT2PI; 12902 }; 12903 12904 exports.PROB = function(range, probability, lower, upper) { 12905 if (lower === undefined) { 12906 return 0; 12907 } 12908 upper = (upper === undefined) ? lower : upper; 12909 12910 range = utils.parseNumberArray(utils.flatten(range)); 12911 probability = utils.parseNumberArray(utils.flatten(probability)); 12912 lower = utils.parseNumber(lower); 12913 upper = utils.parseNumber(upper); 12914 if (utils.anyIsError(range, probability, lower, upper)) { 12915 return error.value; 12916 } 12917 12918 if (lower === upper) { 12919 return (range.indexOf(lower) >= 0) ? probability[range.indexOf(lower)] : 0; 12920 } 12921 12922 var sorted = range.sort(function(a, b) { 12923 return a - b; 12924 }); 12925 var n = sorted.length; 12926 var result = 0; 12927 for (var i = 0; i < n; i++) { 12928 if (sorted[i] >= lower && sorted[i] <= upper) { 12929 result += probability[range.indexOf(sorted[i])]; 12930 } 12931 } 12932 return result; 12933 }; 12934 12935 exports.QUARTILE = {}; 12936 12937 exports.QUARTILE.EXC = function(range, quart) { 12938 range = utils.parseNumberArray(utils.flatten(range)); 12939 quart = utils.parseNumber(quart); 12940 if (utils.anyIsError(range, quart)) { 12941 return error.value; 12942 } 12943 switch (quart) { 12944 case 1: 12945 return exports.PERCENTILE.EXC(range, 0.25); 12946 case 2: 12947 return exports.PERCENTILE.EXC(range, 0.5); 12948 case 3: 12949 return exports.PERCENTILE.EXC(range, 0.75); 12950 default: 12951 return error.num; 12952 } 12953 }; 12954 12955 exports.QUARTILE.INC = function(range, quart) { 12956 range = utils.parseNumberArray(utils.flatten(range)); 12957 quart = utils.parseNumber(quart); 12958 if (utils.anyIsError(range, quart)) { 12959 return error.value; 12960 } 12961 switch (quart) { 12962 case 1: 12963 return exports.PERCENTILE.INC(range, 0.25); 12964 case 2: 12965 return exports.PERCENTILE.INC(range, 0.5); 12966 case 3: 12967 return exports.PERCENTILE.INC(range, 0.75); 12968 default: 12969 return error.num; 12970 } 12971 }; 12972 12973 exports.RANK = {}; 12974 12975 exports.RANK.AVG = function(number, range, order) { 12976 number = utils.parseNumber(number); 12977 range = utils.parseNumberArray(utils.flatten(range)); 12978 if (utils.anyIsError(number, range)) { 12979 return error.value; 12980 } 12981 range = utils.flatten(range); 12982 order = order || false; 12983 var sort = (order) ? function(a, b) { 12984 return a - b; 12985 } : function(a, b) { 12986 return b - a; 12987 }; 12988 range = range.sort(sort); 12989 12990 var length = range.length; 12991 var count = 0; 12992 for (var i = 0; i < length; i++) { 12993 if (range[i] === number) { 12994 count++; 12995 } 12996 } 12997 12998 return (count > 1) ? (2 * range.indexOf(number) + count + 1) / 2 : range.indexOf(number) + 1; 12999 }; 13000 13001 exports.RANK.EQ = function(number, range, order) { 13002 number = utils.parseNumber(number); 13003 range = utils.parseNumberArray(utils.flatten(range)); 13004 if (utils.anyIsError(number, range)) { 13005 return error.value; 13006 } 13007 order = order || false; 13008 var sort = (order) ? function(a, b) { 13009 return a - b; 13010 } : function(a, b) { 13011 return b - a; 13012 }; 13013 range = range.sort(sort); 13014 return range.indexOf(number) + 1; 13015 }; 13016 13017 exports.RSQ = function(data_x, data_y) { // no need to flatten here, PEARSON will take care of that 13018 data_x = utils.parseNumberArray(utils.flatten(data_x)); 13019 data_y = utils.parseNumberArray(utils.flatten(data_y)); 13020 if (utils.anyIsError(data_x, data_y)) { 13021 return error.value; 13022 } 13023 return Math.pow(exports.PEARSON(data_x, data_y), 2); 13024 }; 13025 13026 exports.SMALL = function(range, k) { 13027 range = utils.parseNumberArray(utils.flatten(range)); 13028 k = utils.parseNumber(k); 13029 if (utils.anyIsError(range, k)) { 13030 return range; 13031 } 13032 return range.sort(function(a, b) { 13033 return a - b; 13034 })[k - 1]; 13035 }; 13036 13037 exports.STANDARDIZE = function(x, mean, sd) { 13038 x = utils.parseNumber(x); 13039 mean = utils.parseNumber(mean); 13040 sd = utils.parseNumber(sd); 13041 if (utils.anyIsError(x, mean, sd)) { 13042 return error.value; 13043 } 13044 return (x - mean) / sd; 13045 }; 13046 13047 exports.STDEV = {}; 13048 13049 exports.STDEV.P = function() { 13050 var v = exports.VAR.P.apply(this, arguments); 13051 return Math.sqrt(v); 13052 }; 13053 13054 exports.STDEV.S = function() { 13055 var v = exports.VAR.S.apply(this, arguments); 13056 return Math.sqrt(v); 13057 }; 13058 13059 exports.STDEVA = function() { 13060 var v = exports.VARA.apply(this, arguments); 13061 return Math.sqrt(v); 13062 }; 13063 13064 exports.STDEVPA = function() { 13065 var v = exports.VARPA.apply(this, arguments); 13066 return Math.sqrt(v); 13067 }; 13068 13069 exports.VAR = {}; 13070 13071 exports.VAR.P = function() { 13072 var range = utils.numbers(utils.flatten(arguments)); 13073 var n = range.length; 13074 var sigma = 0; 13075 var mean = exports.AVERAGE(range); 13076 for (var i = 0; i < n; i++) { 13077 sigma += Math.pow(range[i] - mean, 2); 13078 } 13079 return sigma / n; 13080 }; 13081 13082 exports.VAR.S = function() { 13083 var range = utils.numbers(utils.flatten(arguments)); 13084 var n = range.length; 13085 var sigma = 0; 13086 var mean = exports.AVERAGE(range); 13087 for (var i = 0; i < n; i++) { 13088 sigma += Math.pow(range[i] - mean, 2); 13089 } 13090 return sigma / (n - 1); 13091 }; 13092 13093 exports.VARA = function() { 13094 var range = utils.flatten(arguments); 13095 var n = range.length; 13096 var sigma = 0; 13097 var count = 0; 13098 var mean = exports.AVERAGEA(range); 13099 for (var i = 0; i < n; i++) { 13100 var el = range[i]; 13101 if (typeof el === 'number') { 13102 sigma += Math.pow(el - mean, 2); 13103 } else if (el === true) { 13104 sigma += Math.pow(1 - mean, 2); 13105 } else { 13106 sigma += Math.pow(0 - mean, 2); 13107 } 13108 13109 if (el !== null) { 13110 count++; 13111 } 13112 } 13113 return sigma / (count - 1); 13114 }; 13115 13116 exports.VARPA = function() { 13117 var range = utils.flatten(arguments); 13118 var n = range.length; 13119 var sigma = 0; 13120 var count = 0; 13121 var mean = exports.AVERAGEA(range); 13122 for (var i = 0; i < n; i++) { 13123 var el = range[i]; 13124 if (typeof el === 'number') { 13125 sigma += Math.pow(el - mean, 2); 13126 } else if (el === true) { 13127 sigma += Math.pow(1 - mean, 2); 13128 } else { 13129 sigma += Math.pow(0 - mean, 2); 13130 } 13131 13132 if (el !== null) { 13133 count++; 13134 } 13135 } 13136 return sigma / count; 13137 }; 13138 13139 exports.WEIBULL = {}; 13140 13141 exports.WEIBULL.DIST = function(x, alpha, beta, cumulative) { 13142 x = utils.parseNumber(x); 13143 alpha = utils.parseNumber(alpha); 13144 beta = utils.parseNumber(beta); 13145 if (utils.anyIsError(x, alpha, beta)) { 13146 return error.value; 13147 } 13148 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); 13149 }; 13150 13151 exports.Z = {}; 13152 13153 exports.Z.TEST = function(range, x, sd) { 13154 range = utils.parseNumberArray(utils.flatten(range)); 13155 x = utils.parseNumber(x); 13156 if (utils.anyIsError(range, x)) { 13157 return error.value; 13158 } 13159 13160 sd = sd || exports.STDEV.S(range); 13161 var n = range.length; 13162 return 1 - exports.NORM.S.DIST((exports.AVERAGE(range) - x) / (sd / Math.sqrt(n)), true); 13163 }; 13164 13165 return exports; 13166 })(); 13167 13168 for (var i = 0; i < Object.keys(jexcel.methods).length; i++) { 13169 var methods = jexcel.methods[Object.keys(jexcel.methods)[i]]; 13170 for (var j = 0; j < Object.keys(methods).length; j++) { 13171 if (typeof(methods[Object.keys(methods)[j]]) == 'function') { 13172 window[Object.keys(methods)[j]] = methods[Object.keys(methods)[j]]; 13173 } else { 13174 window[Object.keys(methods)[j]] = function() { 13175 return Object.keys(methods)[j] + 'Not implemented'; 13176 } 13177 } 13178 } 13179 } 13180 13181 return jexcel; 13182})));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.