diff --git a/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor.xml b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor.xml new file mode 100644 index 0000000000000000000000000000000000000000..b68162a656d9429574d0dd1eb4b32078a239771d --- /dev/null +++ b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor.xml @@ -0,0 +1,26 @@ +<?xml version="1.0"?> +<ZopeData> + <record id="1" aka="AAAAAAAAAAE="> + <pickle> + <global name="Folder" module="OFS.Folder"/> + </pickle> + <pickle> + <dictionary> + <item> + <key> <string>_objects</string> </key> + <value> + <tuple/> + </value> + </item> + <item> + <key> <string>id</string> </key> + <value> <string>erp5_jexcel_editor</string> </value> + </item> + <item> + <key> <string>title</string> </key> + <value> <string>ERP5 Jexcel Editor</string> </value> + </item> + </dictionary> + </pickle> + </record> +</ZopeData> diff --git a/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/WebSection_getJexcelEditorPrecacheManifestList.py b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/WebSection_getJexcelEditorPrecacheManifestList.py new file mode 100644 index 0000000000000000000000000000000000000000..5958013d197329a30c97e4d2ffa70c14e89f38f2 --- /dev/null +++ b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/WebSection_getJexcelEditorPrecacheManifestList.py @@ -0,0 +1,18 @@ +url_list = [ + "renderjs.js", + "rsvp.js", + "jio.js", + "domsugar.js", + "jsuites/jsuites.js", + "jsuites/jsuites.css", + "jexcel/jexcel.js", + "jexcel/jexcel.css", + "jexcel.gadget.html", + "jexcel.gadget.js", + "complements.css", + "fx.png", + "fonts.ttf", + "icons.css", +] + +return url_list diff --git a/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/WebSection_getJexcelEditorPrecacheManifestList.xml b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/WebSection_getJexcelEditorPrecacheManifestList.xml new file mode 100644 index 0000000000000000000000000000000000000000..9e68c64e0929b8ea9d9a829d061bf3e5aa26fc89 --- /dev/null +++ b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/WebSection_getJexcelEditorPrecacheManifestList.xml @@ -0,0 +1,62 @@ +<?xml version="1.0"?> +<ZopeData> + <record id="1" aka="AAAAAAAAAAE="> + <pickle> + <global name="PythonScript" module="Products.PythonScripts.PythonScript"/> + </pickle> + <pickle> + <dictionary> + <item> + <key> <string>Script_magic</string> </key> + <value> <int>3</int> </value> + </item> + <item> + <key> <string>_bind_names</string> </key> + <value> + <object> + <klass> + <global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/> + </klass> + <tuple/> + <state> + <dictionary> + <item> + <key> <string>_asgns</string> </key> + <value> + <dictionary> + <item> + <key> <string>name_container</string> </key> + <value> <string>container</string> </value> + </item> + <item> + <key> <string>name_context</string> </key> + <value> <string>context</string> </value> + </item> + <item> + <key> <string>name_m_self</string> </key> + <value> <string>script</string> </value> + </item> + <item> + <key> <string>name_subpath</string> </key> + <value> <string>traverse_subpath</string> </value> + </item> + </dictionary> + </value> + </item> + </dictionary> + </state> + </object> + </value> + </item> + <item> + <key> <string>_params</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>id</string> </key> + <value> <string>WebSection_getJexcelEditorPrecacheManifestList</string> </value> + </item> + </dictionary> + </pickle> + </record> +</ZopeData> diff --git a/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/complements.css.css b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/complements.css.css new file mode 100644 index 0000000000000000000000000000000000000000..e97708a0c2511c489772796f249ccba8c84b56cb --- /dev/null +++ b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/complements.css.css @@ -0,0 +1,84 @@ +div.jexcel_formula img{ + display: inline-block; + width: 12px; + height: 12px; + margin-left: 4px; + margin-right: 4px; +} + +input.jexcel_formula { + margin-left: 2px; + width: calc(100% - 77px); + position:sticky; + top:0px; + z-index:0; + display: inline-block; +} + +div.jexcel-filter > * { + display: inline-block; +} + +input.cell_input { + display: inline-block; + width: 35px; +} + +input.jexcel_search { + width: 40px; +} + +.jexcel_toolbar select.minimize { + width: 90px; +} + +td { + cursor: cell; +} + +div.jexcel_toolbar div.jcolor-content td { + cursor: crosshair; +} + +input.readonly { + cursor: not-allowed; +} + +body > div.spreadsheet.jexcel_tabs > div:nth-child(1) { + position : absolute; + bottom: 0; +} + +.add_delete { + position : absolute; + bottom : 3px; + right : 10px; +} + +.add_delete > i { + cursor : pointer; + margin-left : 10px; +} + +.jexcel_tab.jexcel_container.fullscreen.with-toolbar { + position: absolute; + top: 0; +} + +.jexcel_tabs .jexcel_tab_link, .jexcel_filter > div { + font-family: arial; +} + +.jexcel_tab_link.selected { + cursor: alias; +} + +:focus { + outline: none; +} + +@media (max-width: 1150px){ + div.jexcel_filter { + display: none; + } +} diff --git a/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/complements.css.xml b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/complements.css.xml new file mode 100644 index 0000000000000000000000000000000000000000..559b190f9f5b123ca1c3da7d95590b9a4e691d07 --- /dev/null +++ b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/complements.css.xml @@ -0,0 +1,28 @@ +<?xml version="1.0"?> +<ZopeData> + <record id="1" aka="AAAAAAAAAAE="> + <pickle> + <global name="File" module="OFS.Image"/> + </pickle> + <pickle> + <dictionary> + <item> + <key> <string>__name__</string> </key> + <value> <string>complements.css</string> </value> + </item> + <item> + <key> <string>content_type</string> </key> + <value> <string>text/css</string> </value> + </item> + <item> + <key> <string>precondition</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>title</string> </key> + <value> <string></string> </value> + </item> + </dictionary> + </pickle> + </record> +</ZopeData> diff --git a/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/fonts.ttf.ttf b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/fonts.ttf.ttf new file mode 100644 index 0000000000000000000000000000000000000000..8f07978740cecc6083137cb36262a096f6a7df76 Binary files /dev/null and b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/fonts.ttf.ttf differ diff --git a/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/fonts.ttf.xml b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/fonts.ttf.xml new file mode 100644 index 0000000000000000000000000000000000000000..d57ff36d118c7f14d7885edd5385d23f3b66c7a1 --- /dev/null +++ b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/fonts.ttf.xml @@ -0,0 +1,28 @@ +<?xml version="1.0"?> +<ZopeData> + <record id="1" aka="AAAAAAAAAAE="> + <pickle> + <global name="File" module="OFS.Image"/> + </pickle> + <pickle> + <dictionary> + <item> + <key> <string>__name__</string> </key> + <value> <string>fonts.ttf</string> </value> + </item> + <item> + <key> <string>content_type</string> </key> + <value> <string>font/ttf</string> </value> + </item> + <item> + <key> <string>precondition</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>title</string> </key> + <value> <string></string> </value> + </item> + </dictionary> + </pickle> + </record> +</ZopeData> diff --git a/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/fx.png.png b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/fx.png.png new file mode 100644 index 0000000000000000000000000000000000000000..9c2a02f4d84044523ad791c8daf956874c0628af Binary files /dev/null and b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/fx.png.png differ diff --git a/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/fx.png.xml b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/fx.png.xml new file mode 100644 index 0000000000000000000000000000000000000000..46922e690078c390c5c4ce47bcee9b353863ce8c --- /dev/null +++ b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/fx.png.xml @@ -0,0 +1,28 @@ +<?xml version="1.0"?> +<ZopeData> + <record id="1" aka="AAAAAAAAAAE="> + <pickle> + <global name="File" module="OFS.Image"/> + </pickle> + <pickle> + <dictionary> + <item> + <key> <string>__name__</string> </key> + <value> <string>fx.png</string> </value> + </item> + <item> + <key> <string>content_type</string> </key> + <value> <string>image/png</string> </value> + </item> + <item> + <key> <string>precondition</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>title</string> </key> + <value> <string></string> </value> + </item> + </dictionary> + </pickle> + </record> +</ZopeData> diff --git a/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/icons.css.css b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/icons.css.css new file mode 100644 index 0000000000000000000000000000000000000000..4dd83990251293279e9a4f1ffcdf10b47d3ca6b9 --- /dev/null +++ b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/icons.css.css @@ -0,0 +1,20 @@ +@font-face { + font-family: 'Material Icons'; + font-style: normal; + font-weight: 400; + src: url(fonts.ttf) format('truetype'); +} + +.material-icons { + font-family: 'Material Icons'; + font-weight: normal; + font-style: normal; + font-size: 24px; + line-height: 1; + letter-spacing: normal; + text-transform: none; + display: inline-block; + white-space: nowrap; + word-wrap: normal; + direction: ltr; +} diff --git a/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/icons.css.xml b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/icons.css.xml new file mode 100644 index 0000000000000000000000000000000000000000..933a79037d5cb69700d45dd89c0ca237322e59cf --- /dev/null +++ b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/icons.css.xml @@ -0,0 +1,28 @@ +<?xml version="1.0"?> +<ZopeData> + <record id="1" aka="AAAAAAAAAAE="> + <pickle> + <global name="File" module="OFS.Image"/> + </pickle> + <pickle> + <dictionary> + <item> + <key> <string>__name__</string> </key> + <value> <string>icons.css</string> </value> + </item> + <item> + <key> <string>content_type</string> </key> + <value> <string>text/css</string> </value> + </item> + <item> + <key> <string>precondition</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>title</string> </key> + <value> <string></string> </value> + </item> + </dictionary> + </pickle> + </record> +</ZopeData> diff --git a/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jexcel.gadget.html.html b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jexcel.gadget.html.html new file mode 100644 index 0000000000000000000000000000000000000000..e27a252c8279f2900371f071957dcaa18c0fa055 --- /dev/null +++ b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jexcel.gadget.html.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>Jexcel</title> + + <script src="rsvp.js"></script> + <script src="renderjs.js"></script> + <script src="jio.js"></script> + + <script src="domsugar.js"></script> + + <script src="jexcel/jexcel.js"></script> + <script src="jsuites/jsuites.js"></script> + <link rel="stylesheet" href="jexcel/jexcel.css" type="text/css" /> + <link rel="stylesheet" href="jsuites/jsuites.css" type="text/css" /> + <link rel="stylesheet" href="complements.css" type="text/css" /> + + <link rel="stylesheet" href="icons.css" type="text/css" /> + + <script src="jexcel.gadget.js"></script> + + </head> + <body> + <div class="spreadsheet"></div> + </body> +</html> diff --git a/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jexcel.gadget.html.xml b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jexcel.gadget.html.xml new file mode 100644 index 0000000000000000000000000000000000000000..fb1b432a9a5a69306470dd7a9975ff36ff29fd72 --- /dev/null +++ b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jexcel.gadget.html.xml @@ -0,0 +1,28 @@ +<?xml version="1.0"?> +<ZopeData> + <record id="1" aka="AAAAAAAAAAE="> + <pickle> + <global name="File" module="OFS.Image"/> + </pickle> + <pickle> + <dictionary> + <item> + <key> <string>__name__</string> </key> + <value> <string>jexcel.gadget.html</string> </value> + </item> + <item> + <key> <string>content_type</string> </key> + <value> <string>text/html</string> </value> + </item> + <item> + <key> <string>precondition</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>title</string> </key> + <value> <string></string> </value> + </item> + </dictionary> + </pickle> + </record> +</ZopeData> diff --git a/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jexcel.gadget.js.js b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jexcel.gadget.js.js new file mode 100644 index 0000000000000000000000000000000000000000..701f4d81b247dd0a4ceb5f84944d2f53ea9a6772 --- /dev/null +++ b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jexcel.gadget.js.js @@ -0,0 +1,1122 @@ +/*jslint nomen: true, indent: 2, maxlen: 80, unparam: true */ +/*global window, rJS, RSVP, jexcel, domsugar, document, alert, +prompt, confirm, navigator*/ +(function (window, rJS, jexcel, domsugar, document, alert, + prompt, confirm, navigator) { + "use strict"; + + function isMobileDevice() { + return (window.orientation !== undefined) || + (navigator.userAgent.indexOf('IEMobile') !== -1); + } + + function format(node, level) { + var indentBefore = new Array(level + 2).join(' '), + indentAfter, + textNode, + i; + level += 1; + indentAfter = new Array(level - 1).join(' '); + for (i = 0; i < node.children.length; i += 1) { + textNode = document.createTextNode('\n' + indentBefore); + node.insertBefore(textNode, node.children[i]); + format(node.children[i], level); + if (node.lastElementChild === node.children[i]) { + textNode = document.createTextNode('\n' + indentAfter); + node.appendChild(textNode); + } + } + return node; + } + + function beautifyHTMLString(str) { + var div = domsugar("div", {html: str.trim()}); + return format(div, 0).innerHTML; + } + + function createElementFromHTML(html_string) { + var div = domsugar("div", {html: html_string.trim()}); + return div.children; + } + + function columnLetterToNumber(str) { + // convert a column letter to its corresponding column number : + // A -> 0, B -> 1, AA -> 26, AB -> 27 ... + var out = 0, len = str.length, pos = len; + while (--pos > -1) { + out += (str.charCodeAt(pos) - 64) * Math.pow(26, len - 1 - pos); + } + return out - 1; + } + + function numberToColumnLetter(i) { + // convert a column number to its corresponding column letter : + // 0 -> A, 1 -> B, 26 -> AA, 27 -> AB ... + return (i >= 26 ? numberToColumnLetter((i / 26 >> 0) - 1) : '') + + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[i % 26 >> 0]; + } + + function getCoordinatesFromCell(cell) { + var x = Number(cell.dataset.x), + y = Number(cell.dataset.y) + 1; + return numberToColumnLetter(x) + y.toString(); + } + + function fireDoubleClick(element) { + var clickEvent = document.createEvent('MouseEvents'); + clickEvent.initEvent('dblclick', true, true); + element.dispatchEvent(clickEvent); + } + + function getCurrentSheet(gadget) { + var worksheet = gadget.element.querySelector('.selected') + .getAttribute('data-spreadsheet'); + return gadget.element.querySelector('.spreadsheet').jexcel[worksheet]; + } + + function buildSelectOptions() { + var frag = document.createDocumentFragment(), + formulas; + formulas = ["SUM", "MIN", "MAX", "COUNT", "AVERAGE", "FLOOR", "ABS", + "SQRT", "ISEVEN", "ISODD", "TODAY", "UPPER", "LOWER", "TRUNC", + "TYPE", "TRIM", "SIN", "COS", "TAN", "ARCSIN", "ARCCOS", + "ARCTAN", "ROUND", "RAND", "RANDBETWEEN", "RADIANS", "POWER", + "PI", "PHI", "MOD", "LEN", "LN", "LOG", "LOG10", "FACT", "TRUE", + "FALSE", "AND", "OR", "XOR", "EVEN", "ODD", "EXP", + "CONCATENATE", "BITAND", "BITOR", "BIN2DEC", "BIN2HEX", + "BIN2OCT", "DEC2BIN", "DEC2HEX", "DEC2OCT", "HEX2BIN", + "HEX2DEC", "HEX2OCT", "NOT", "OCT2BIN", "OCT2DEC", "OCT2HEX", + "PRODUCT", "QUOTIENT", "COLUMN", "ROW", "CELL"].sort(); + frag.appendChild( + domsugar("option", {text: "FORMULA", "class": "formula_option"}) + ); + formulas.forEach(function (value) { + frag.appendChild( + domsugar("option", + {"class": "formula_option", text: value + "()", value: value} + ) + ); + }); + return frag; + } + + function setupTable(gadget, element) { + var filter = element.querySelector(".jexcel_filter"), + formula_div = domsugar("div", {"class": "jexcel_formula"}), + img = domsugar("img", {src: "fx.png"}), + formula_input = domsugar("input", {"class": "jexcel_formula"}), + cell_input = domsugar("input", {"class": "cell_input"}), + options = buildSelectOptions(), + select = domsugar("select", {"class": "minimize"}), + buttons; + element.querySelector("table.jexcel tr").childNodes.forEach(function (td) { + td.style.textAlign = "center"; + }); + element.querySelector(".jexcel_toolbar").appendChild(filter); + element.querySelector("select.jexcel_toolbar_item") + .classList.add("minimize"); + formula_div.appendChild(img); + formula_div.appendChild(formula_input); + element.querySelector("div.jexcel_toolbar").parentNode + .insertBefore(formula_div, + element.querySelector("div.jexcel_toolbar").nextSibling); + formula_input.onfocus = gadget.triggerOnFocusFormulaInput.bind(gadget); + formula_input.oninput = function () { + return gadget.triggerOnInputFormulaInput(cell_input, this); + }; + cell_input.onfocus = gadget.triggerOnFocusCellInput.bind(gadget); + cell_input.onkeypress = function (event) { + return gadget.triggerOnKeyPressCellInput(event, cell_input); + }; + formula_div.insertBefore(cell_input, img); + select.appendChild(options); + select.onchange = function () { + var dropdown = this; + return gadget.triggerOnChangeSelect(dropdown, formula_input); + }; + element.querySelector(".jexcel_toolbar").insertBefore(select, filter); + gadget.element.querySelectorAll(".jexcel_tab_link") + .forEach(function (tab, i) { + if (i === 0) { + gadget.state.selectedTabLink = tab; + } + tab.title = "Click to rename when selected"; + }); + buttons = domsugar("div", {"class": "add_delete"}); + buttons.appendChild(element.querySelector("i[title='Add table']")); + buttons.appendChild(element.querySelector("i[title='Delete table']")); + gadget.element.querySelector(".spreadsheet.jexcel_tabs") + .appendChild(buttons); + gadget.state.newSheet = false; + } + + function bindEvents(gadget, sheet) { + sheet.onevent = gadget.triggerOnEventSheet.bind(gadget); + sheet.onselection = gadget.triggerOnSelectionSheet.bind(gadget); + sheet.oneditionend = gadget.triggerOnEditionEndSheet.bind(gadget); + return sheet; + } + + function getConfigListFromTables(gadget, list) { + var configs = [], + dict, + tmp; + list.forEach(function (table, i) { + dict = {}; + dict = Object.assign(jexcel.createFromTable(table), + gadget.state.template); + if (table.classList.contains("jexcel") && + !table.classList.contains("jSheet")) { + if (Array.from(table.querySelectorAll("td")).filter(function (td) { + return td.hasAttribute("cache"); + }).length === 0) { + tmp = JSON.parse(table.dataset.config); + dict.columns = tmp.columns; + dict.data = tmp.data; + } + } + dict.sheetName = table.title || "Sheet " + (i + 1); + configs.push(dict); + }); + return configs; + } + + function getTemplate(gadget) { + var toolbar_dict = gadget.state.toolbar_dict, + list = [], + dict, + res; + dict = { + options: gadget.state.options, + undo: { + type: 'i', + content: 'undo', + onclick: gadget.triggerUndo.bind(gadget), + tooltip: "Undo" + }, + redo: { + type: 'i', + content: 'redo', + onclick: gadget.triggerRedo.bind(gadget), + tooltip: "Redo" + }, + merge: { + type: 'i', + content: 'table_chart', + onclick: gadget.triggerMerge.bind(gadget), + tooltip: "Merge" + }, + unmerge: { + type: 'i', + content: 'close', + onclick: gadget.triggerUnmerge.bind(gadget), + tooltip: "Unmerge" + }, + destroy_merge: { + type: 'i', + content: 'cancel', + onclick: gadget.triggerDestroyMerge.bind(gadget), + tooltip: "Unmerge all" + }, + font_style: { + type: 'select', + k: 'font-family', + v: ['Arial', 'Comic Sans MS', 'Verdana', 'Calibri', 'Tahoma', + 'Helvetica', 'DejaVu Sans', 'Times New Roman', 'Georgia', + 'Antiqua'] + }, + font_size: { + type: 'select', + k: 'font-size', + v: ['8px', '10px', '12px', '14px', '16px', '18px', '20px', + '22px', '24px', '26px', '28px', '30px', '34px', '38px', + '42px', '46px', '50px'] + }, + text_align_left: { + type: 'i', + content: 'format_align_left', + k: 'text-align', + v: 'left', + tooltip: "Align left" + }, + text_align_center: { + type: 'i', + content: 'format_align_center', + k: 'text-align', + v: 'center', + tooltip: "Align center" + }, + text_align_right: { + type: 'i', + content: 'format_align_right', + k: 'text-align', + v: 'right', + tooltip: "Align right" + }, + text_align_justify: { + type: 'i', + content: 'format_align_justify', + k: 'text-align', + v: 'justify', + tooltip: "Align justify" + }, + vertical_align_top: { + type: 'i', + content: 'vertical_align_top', + k: 'vertical-align', + v: 'top', + tooltip: "Align top" + }, + vertical_align_middle: { + type: 'i', + content: 'vertical_align_center', + k: 'vertical-align', + v: 'middle', + tooltip: "Align center" + }, + vertical_align_bottom: { + type: 'i', + content: 'vertical_align_bottom', + k: 'vertical-align', + v: 'bottom', + tooltip: "Align bottom" + }, + style_bold: { + type: 'i', + content: 'format_bold', + k: 'font-weight', + v: 'bold', + tooltip: "Bold" + }, + style_underlined: { + type: 'i', + content: 'format_underlined', + k: 'text-decoration', + v: 'underline', + tooltip: "Underline" + }, + style_italic: { + type: 'i', + content: 'format_italic', + k: 'font-style', + v: 'italic', + tooltip: "Italic" + }, + text_color: { + type: 'color', + content: 'format_color_text', + k: 'color', + tooltip: "Text color" + }, + background_color: { + type: 'color', + content: 'format_color_fill', + k: 'background-color', + tooltip: "Background color" + }, + add: { + type: "i", + content: "add", + onclick: gadget.triggerAddSheet.bind(gadget), + tooltip: "Add table" + }, + remove: { + type: "i", + content: "delete", + onclick: gadget.triggerDeleteSheet.bind(gadget), + tooltip: "Delete table" + }, + add_row: { + type: "i", + content: "playlist_add", + onclick: gadget.triggerAddRow.bind(gadget), + tooltip: "Add row at the end" + }, + delete_row: { + type: "i", + content: "delete_sweep", + onclick: gadget.triggerDeleteRow.bind(gadget), + tooltip: "Delete last row" + }, + add_column: { + type: "i", + content: "exposure_plus_1", + onclick: gadget.triggerAddColumn.bind(gadget), + tooltip: "Add column at the end" + }, + delete_column: { + type: "i", + content: "exposure_neg_1", + onclick: gadget.triggerDeleteColumn.bind(gadget), + tooltip: "Delete last column" + }, + dimensions: { + type: "i", + content: "photo_size_select_small", + onclick: gadget.triggerNewDimensions.bind(gadget), + tooltip: "Resize table" + }, + contextMenu: function (obj, x, y) { + var items = []; + gadget.state.obj = obj; + gadget.state.x = x; + gadget.state.y = y; + if (y === null) { + // Insert a new column + items.push({ + title: obj.options.text.insertANewColumnBefore, + onclick: gadget.triggerInsertNewColumnBefore.bind(gadget) + }); + items.push({ + title: obj.options.text.insertANewColumnAfter, + onclick: gadget.triggerInsertNewColumnAfter.bind(gadget) + }); + // Delete a column + items.push({ + title: obj.options.text.deleteSelectedColumns, + onclick: gadget.triggerDeleteSelectedColumns.bind(gadget) + }); + // Rename column + items.push({ + title: obj.options.text.renameThisColumn, + onclick: gadget.triggerRenameColumn.bind(gadget) + }); + // Sorting + items.push({ type: 'line' }); + items.push({ + title: obj.options.text.orderAscending, + onclick: gadget.triggerOrderAscending.bind(gadget) + }); + items.push({ + title: obj.options.text.orderDescending, + onclick: gadget.triggerOrderDescending.bind(gadget) + }); + } else { + // Insert new row + items.push({ + title: obj.options.text.insertANewRowBefore, + onclick: gadget.triggerInsertNewRowBefore.bind(gadget) + }); + items.push({ + title: obj.options.text.insertANewRowAfter, + onclick: gadget.triggerInsertNewRowAfter.bind(gadget) + }); + items.push({ + title: obj.options.text.deleteSelectedRows, + onclick: gadget.triggerDeleteSelectedRows.bind(gadget) + }); + } + if (x) { + items.push({type: 'line'}); + items.push({ + title: "Set column type: Text", + onclick: function () { + return gadget.triggerChangeType("text"); + } + }); + items.push({ + title: "Set column type: Image", + onclick: function () { + return gadget.triggerChangeType("image"); + } + }); + items.push({ + title: "Set column type: HTML", + onclick: function () { + return gadget.triggerChangeType("html"); + } + }); + items.push({ + title: "Set column type: Checkbox", + onclick: function () { + var child = domsugar("input", {type: "checkbox", name: "c" + x}); + return gadget.triggerChangeType("checkbox", child); + } + }); + items.push({ + title: "Set column type: Calendar", + onclick: function () { + return gadget.triggerChangeType("calendar"); + } + }); + items.push({ + title: "Set column type: Color", + onclick: function () { + return gadget.triggerChangeType("color", null, "square"); + } + }); + } + return items; + } + }; + if (toolbar_dict.hasOwnProperty("undo_redo") && toolbar_dict.undo_redo) { + list.push(dict.undo, dict.redo); + } + if (toolbar_dict.hasOwnProperty("add") && toolbar_dict.add) { + list.push(dict.add, dict.remove); + } + if (toolbar_dict.hasOwnProperty("merge") && toolbar_dict.merge) { + list.push(dict.merge, dict.unmerge, dict.destroy_merge); + } + if (toolbar_dict.hasOwnProperty("text_font") && toolbar_dict.text_font) { + list.push(dict.font_style, dict.font_size, dict.style_bold, + dict.style_italic, dict.style_underlined); + } + if (toolbar_dict.hasOwnProperty("text_position") && + toolbar_dict.text_position) { + list.push(dict.text_align_left, dict.text_align_center, + dict.text_align_right, dict.text_align_justify, + dict.vertical_align_top, dict.vertical_align_middle, + dict.vertical_align_bottom); + } + if (toolbar_dict.hasOwnProperty("color_picker") && + toolbar_dict.color_picker) { + list.push(dict.text_color, dict.background_color); + } + if (isMobileDevice()) { + list.push({ + type: "i", + content: "photo_library", + onclick: function (sheet, instance) { + return gadget.triggerChangeTypeInToolbar(sheet, instance, "image"); + } + }); + list.push({ + type: "i", + content: "format_size", + onclick: function (sheet, instance) { + return gadget.triggerChangeTypeInToolbar(sheet, instance, "text"); + } + }); + list.push({ + type: "i", + content: "format_paint", + onclick: function (sheet, instance) { + return gadget.triggerChangeTypeInToolbar(sheet, instance, + "color", null, "square"); + } + }); + list.push({ + type: "i", + content: "format_list_bulleted", + onclick: function (sheet, instance) { + return gadget.triggerChangeTypeInToolbar(sheet, instance, "html"); + } + }); + list.push({ + type: "i", + content: "calendar_today", + onclick: function (sheet, instance) { + return gadget.triggerChangeTypeInToolbar(sheet, instance, "calendar"); + } + }); + list.push({ + type: "i", + content: "check_box", + onclick: function (sheet, instance) { + var child = domsugar("input", {type: "checkbox"}); + return gadget.triggerChangeTypeInToolbar(sheet, instance, + "checkbox", child); + } + }); + } + if (toolbar_dict.hasOwnProperty("add_delete_row_column") && + toolbar_dict.add_delete_row_column) { + list.push(dict.add_row, dict.delete_row, + dict.add_column, dict.delete_column, dict.dimensions); + } + res = Object.assign({}, dict.options); + res.toolbar = list; + res.contextMenu = dict.contextMenu; + return res; + } + + function setHistoryType(instance, action, column, old_type, new_type) { + instance.setHistory({ + action: action, + column: column, + oldType: old_type, + newType: new_type + }); + } + + rJS(window) + + .setState({ + saveConfig: false, + newSheet: false, + updateSelection: true, + toolbar_dict: { + undo_redo: true, + add: true, + merge: true, + text_font: true, + text_position: true, + color_picker: true, + add_delete_row_column: true + }, + options: { + minDimensions: [26, 100], + defaultColWidth: 100, + defaultColAlign: "left", + allowExport: true, + columnSorting: true, + columnDrag: true, + columnResize: true, + rowResize: true, + rowDrag: true, + editable: true, + allowInsertRow: true, + allowManualInsertRow: true, + allowInsertColumn: true, + allowManualInsertColumn: true, + allowDeleteRow: true, + allowRenameColumn: true, + selectionCopy: true, + search: true, + fullscreen: true, + autoIncrement: true, + parseFormulas: true, + wordWrap: true + } + }) + + .declareAcquiredMethod("notifyChange", "notifyChange") + + .declareMethod("render", function (options) { + return this.changeState(options); + }) + + .declareMethod('getContent', function () { + var gadget = this, + form_data = {}, + res = "", + sheets = gadget.element.querySelector('.spreadsheet').jexcel, + tab_links = gadget.element.querySelectorAll('.jexcel_tab_link'); + if (this.state.editable) { + sheets.forEach(function (sheet, i) { + var table = sheet.el.querySelector("table.jexcel").cloneNode(true), + config = sheet.getConfig(), + dict; + dict = { + columns: config.columns, + data: config.data + }; + table.dataset.config = JSON.stringify(dict); + table.title = tab_links[i].textContent; + table.border = "1px"; + table.querySelector("colgroup col").remove(); + table.querySelector("tr").remove(); + table.querySelectorAll("td.jexcel_row").forEach(function (td) { + td.remove(); + }); + table.querySelectorAll('tr').forEach(function (tr, y) { + tr.childNodes.forEach(function (td, x) { + if (td.textContent !== "") { + var value = sheet.getValueFromCoords(x, y); + if (value[0] === "=") { + td.dataset.formula = value; + td.setAttribute('formula', value); + } + } + }); + }); + res += table.outerHTML; + }); + form_data[this.state.key] = beautifyHTMLString(res); + } + return form_data; + }) + + .onStateChange(function (modification_dict) { + var gadget = this, tabs, i, + toolbar_config, + toolbar_events_config, + nodes, + data_list, + selected; + if (modification_dict.hasOwnProperty('newSheet') && + modification_dict.newSheet) { + tabs = (gadget.element.querySelectorAll(".jexcel_container")); + return setupTable(gadget, tabs[tabs.length - 1]); + } + if (modification_dict.hasOwnProperty('value')) { + toolbar_config = getTemplate(gadget); + toolbar_events_config = bindEvents(gadget, toolbar_config); + if (gadget.state.value === "") { + toolbar_events_config.sheetName = "Table 1"; + jexcel.tabs(gadget.element.querySelector(".spreadsheet"), + [toolbar_events_config] + ); + gadget.state.tables = + [gadget.element.querySelector("div.jexcel_content > table")]; + gadget.element + .querySelectorAll(".jexcel_container") + .forEach(function (tab) { + return setupTable(gadget, tab); + }); + } else { + gadget.state.tables = []; + nodes = createElementFromHTML(gadget.state.value); + for (i = 0; i < nodes.length; i += 1) { + gadget.state.tables[i] = nodes[i]; + } + data_list = getConfigListFromTables(gadget, gadget.state.tables); + data_list.forEach(function (dict) { + Object.assign(dict, toolbar_events_config); + }); + jexcel.tabs(gadget.element.querySelector(".spreadsheet"), data_list); + gadget.element.querySelectorAll(".jexcel_container") + .forEach(function (tab) { + return setupTable(gadget, tab); + }); + selected = gadget.element + .querySelector('.jexcel_tab_link.selected'); + selected.classList.remove("selected"); + gadget.element.querySelector('.jexcel_tab_link') + .classList.add("selected"); + gadget.element.querySelectorAll(".jexcel_container") + .forEach(function (tab, i) { + if (i === 0) { + tab.style.display = "block"; + } else { + tab.style.display = "none"; + } + }); + } + } + }) + + .declareJob("deferNotifyChange", function () { + return this.notifyChange(); + }) + + .declareJob("triggerAddRow", function (sheet, instance) { + instance.insertRow(); + }) + + .declareJob("triggerDeleteRow", function (sheet, instance) { + instance.deleteRow(instance.options.data.length - 1, 1); + }) + + .declareJob("triggerInsertNewColumnBefore", function () { + var state = this.state; + state.obj.insertColumn(1, parseInt(state.x, 10), 1); + }) + + .declareJob("triggerInsertNewColumnAfter", function () { + var state = this.state; + state.obj.insertColumn(1, parseInt(state.x, 10), 0); + }) + + .declareJob("triggerDeleteSelectedColumns", function () { + var state = this.state; + state.obj.deleteColumn(state.obj.getSelectedColumns().length ? + undefined : parseInt(state.x, 10)); + }) + + .declareJob("triggerRenameColumn", function () { + var state = this.state; + state.obj.setHeader(state.x); + }) + + .declareJob("triggerOrderAscending", function () { + var state = this.state; + state.obj.orderBy(state.x, 0); + }) + + .declareJob("triggerOrderDescending", function () { + var state = this.state; + state.obj.orderBy(state.x, 1); + }) + + .declareJob("triggerInsertNewRowBefore", function () { + var state = this.state; + state.obj.insertRow(1, parseInt(state.y, 10), 1); + }) + + .declareJob("triggerInsertNewRowAfter", function () { + var state = this.state; + state.obj.insertRow(1, parseInt(state.y, 10)); + }) + + .declareJob("triggerDeleteSelectedRows", function () { + var state = this.state; + state.obj.deleteRow(state.obj.getSelectedRows().length ? + undefined : parseInt(state.y, 10)); + }) + + .declareJob("triggerAddColumn", function (sheet, instance) { + instance.insertColumn(); + }) + + .declareJob("triggerDeleteColumn", function (sheet, instance) { + instance.deleteColumn(instance.options.columns.length - 1, 1); + }) + + .declareJob("triggerUndo", function (sheet, instance) { + instance.undo(); + }) + + .declareJob("triggerRedo", function (sheet, instance) { + instance.redo(); + }) + + .declareJob("triggerMerge", function (sheet, instance) { + var cell = sheet.querySelector("td.highlight"), + selected = instance.getJson(true), + colspan = Object.keys(selected[0]).length, + rowspan = selected.length, + coor = getCoordinatesFromCell(cell); + instance.setMerge(coor, colspan, rowspan); + }) + + .declareJob("triggerUnmerge", function (sheet, instance) { + var cell = document.querySelector("td.highlight-selected"), + coor = getCoordinatesFromCell(cell); + instance.removeMerge(coor); + }) + + .declareJob("triggerDestroyMerge", function (sheet, instance) { + instance.destroyMerged(); + }) + + .declareJob("triggerChangeType", function (type, child, render) { + var state = this.state, + x = parseInt(state.x, 10), + y = state.y !== null ? parseInt(state.y, 10) : 0, + cell, + column, + array; + state.obj.updateSelectionFromCoords(x, y, x, y); + cell = state.obj.el.querySelector("td.highlight-selected"); + if (state.obj.options.columns[x].type !== type) { + column = state.obj.el + .querySelectorAll("td[data-x='" + x + "']"); + array = Array.from(column); + array.shift(); + setHistoryType(state.obj, "beginChangeType", + x, + state.obj.options.columns[x].type, + type); + array.forEach(function (cell) { + state.obj.setValue(getCoordinatesFromCell(cell), ""); + cell.innerHTML = ""; + if (child) { + cell.appendChild(child.cloneNode()); + } + }); + setHistoryType(state.obj, "endChangeType", + x, + state.obj.options.columns[x].type, + type); + state.obj.options.columns[x].type = type; + if (render) { + state.obj.options.columns[x].render = render; + } + fireDoubleClick(cell); + } + }) + + .declareJob("triggerChangeTypeInToolbar", + function (sheet, instance, type, child, render) { + var cell = sheet.querySelector("td.highlight-selected"), + x, + column, + array; + x = cell ? parseInt(cell.dataset.x, 10) : null; + if (cell && instance.options.columns[x].type !== type) { + column = sheet.querySelectorAll("td[data-x='" + x + "']"); + array = Array.from(column); + array.shift(); + setHistoryType(instance, "beginChangeType", + x, + instance.options.columns[x].type, + type); + array.forEach(function (cell) { + instance.setValue(getCoordinatesFromCell(cell), ""); + cell.innerHTML = ""; + if (child) { + cell.appendChild(child.cloneNode()); + } + }); + setHistoryType(instance, "endChangeType", + x, + instance.options.columns[x].type, + type); + instance.options.columns[x].type = type; + if (render) { + instance.options.columns[x].render = render; + } + fireDoubleClick(cell); + } + }) + + .declareJob("triggerNewDimensions", function (sheet, instance) { + var r = prompt("Number of rows :", instance.options.data.length), + c = prompt("Number of columns :", instance.options.columns.length); + if (c > 0 && r > 0) { + instance.setHistory({action: "beginResizeTable"}); + if (c > instance.options.columns.length) { + while (instance.options.columns.length < c) { + instance.insertColumn(); + } + } else { + while (instance.options.columns.length > c) { + instance.deleteColumn(instance.options.columns.length - 1, 1); + } + } + if (r > instance.options.data.length) { + while (instance.options.data.length < r) { + instance.insertRow(); + } + } else { + while (instance.options.data.length > r) { + instance.deleteRow(instance.options.data.length - 1, 1); + } + } + instance.setHistory({action: "endResizeTable"}); + } + }) + + .declareJob("triggerAddSheet", function () { + var gadget = this, + tabs = gadget.element.querySelectorAll(".jexcel_tab_link"), + dict1, + dict2; + if (tabs.length === 16) { + alert("Can't add tables anymore."); + } else { + dict1 = getTemplate(gadget); + dict1.sheetName = "Table " + + (gadget.element.querySelector('.spreadsheet').jexcel.length + 1); + dict2 = bindEvents(gadget, dict1); + jexcel.tabs(gadget.element.querySelector(".spreadsheet"), [dict2]); + gadget.element + .querySelectorAll('.jexcel_container')[gadget.element.querySelector('.spreadsheet').jexcel.length - 1] + .querySelectorAll("td[data-x][data-y]") + .forEach(function (td) { + td.style.textAlign = "left"; + }); + gadget.deferNotifyChange(); + return gadget.changeState({newSheet: true}) + .push(function () { + gadget.state.selectedTabLink = gadget.element + .querySelector(".jexcel_tab_link.selected"); + }); + } + }) + + .declareJob("triggerDeleteSheet", function (sheet, instance) { + var gadget = this, + tab_link = gadget.element + .querySelector('.jexcel_tab_link.selected'), + index = tab_link.getAttribute("data-spreadsheet"), + to_remove, + sheets; + if (confirm("Delete this table ?")) { + if (gadget.element.querySelector('.spreadsheet').jexcel.length > 1) { + tab_link.remove(); + gadget.element.querySelectorAll(".jexcel_container") + .forEach(function (tab) { + if (tab.style.display === "block") { + to_remove = tab; + } + }); + to_remove.remove(); + gadget.element.querySelector('.spreadsheet').jexcel.splice(index, 1); + sheets = gadget.element.querySelectorAll('.jexcel_container'); + sheets[sheets.length - 1].style.display = "block"; + gadget.element.querySelectorAll('.jexcel_tab_link') + .forEach(function (tab, i) { + if (i === sheets.length - 1) { + tab.classList.add("selected"); + } + tab.dataset.spreadsheet = i; + tab.textContent = tab.textContent.substring(0, 5) === "Table" ? + "Table " + (i + 1) : tab.textContent; + }); + } else { + gadget.element.querySelector('.jexcel_tab_link') + .textContent = "Table 1"; + sheet.querySelector("input.jexcel_formula").value = ""; + instance.setData(new Array(instance.options.columns.length) + .fill(0, instance.options.columns.length - 1, + new Array(instance.options.data.length) + .fill(0, instance.options.data.length - 1, ""))); + instance.options.columns.forEach(function (column) { + column.type = "text"; + }); + } + gadget.deferNotifyChange(); + gadget.state.selectedTabLink = gadget.element + .querySelector(".jexcel_tab_link.selected"); + } + }) + + .declareJob("triggerOnFocusFormulaInput", function () { + var gadget = this, + worksheet = gadget.element.querySelector('.selected') + .getAttribute('data-spreadsheet'); + gadget.element.querySelector('.spreadsheet').jexcel[worksheet] + .resetSelection(true); + }) + + .declareJob("triggerOnInputFormulaInput", + function (cell_input, formula_input) { + var gadget = this, + worksheet = gadget.element.querySelector('.selected') + .getAttribute('data-spreadsheet'), + instance = gadget.element.querySelector('.spreadsheet') + .jexcel[worksheet], + e = formula_input.value, + numbers = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]; + if (e[0] === "=" && e[e.length - 1] !== ")") { + if (numbers.includes(e[e.length - 1])) { + instance.setValue(cell_input.value, e); + } + } else { + instance.setValue(cell_input.value, e); + } + }) + + .declareJob("triggerOnFocusCellInput", function () { + var gadget = this, + worksheet = gadget.element.querySelector('.selected') + .getAttribute('data-spreadsheet'); + gadget.element.querySelector('.spreadsheet').jexcel[worksheet] + .resetSelection(true); + }) + + .declareJob("triggerOnKeyPressCellInput", function (event, input) { + var gadget = this, + worksheet, + x, + y, + ys; + if (event.keyCode === 13) { + worksheet = gadget.element.querySelector('.selected') + .getAttribute('data-spreadsheet'); + y = input.value.match(/(\d+)/)[0]; + x = columnLetterToNumber(input.value + .substring(0, input.value.length - y.length) + ); + ys = parseInt(y, 10) - 1; + gadget.element.querySelector('.spreadsheet').jexcel[worksheet] + .updateSelectionFromCoords(x, ys, x, ys); + } + }) + + .declareJob("triggerOnChangeSelect", function (dropdown, formula_input) { + var gadget = this, + sheet = getCurrentSheet(gadget), + cell = sheet.el.querySelector("td.highlight-selected"), + x, + y, + value, + currentValue; + if (cell && sheet.options.columns[Number(cell.dataset.x)] + .type === "text") { + x = Number(cell.dataset.x); + y = Number(cell.dataset.y); + currentValue = sheet.getValueFromCoords(x, y); + if (currentValue === "" || currentValue[0] !== "=") { + value = "=" + + dropdown.options[dropdown.selectedIndex].value + + "(" + currentValue + ")"; + } else { + value = "=" + + dropdown.options[dropdown.selectedIndex].value + + "(" + currentValue.substring(1, currentValue.length) + ")"; + } + sheet.setValueFromCoords(x, y, value); + formula_input.value = value; + } + dropdown.selectedIndex = 0; + }) + + .declareJob("triggerOnEventSheet", function (event) { + var gadget = this, + exluded_events = ["onload", "onfocus", "onblur", "onselection"]; + if (!exluded_events.includes(event)) { + if ((["onchangestyle", "onchange", "onbeforechange"].includes(event) && + gadget.state.saveConfig) || + !["onchangestyle", "onchange", "onbeforechange"].includes(event)) { + gadget.deferNotifyChange(); + } + } + }) + + .declareJob("triggerOnSelectionSheet", function () { + var gadget = this, + instance = getCurrentSheet(gadget), + tab = gadget.element + .querySelectorAll(".jexcel_container")[gadget.element + .querySelector("div.jexcel_tab_link.selected") + .getAttribute("data-spreadsheet")], + cell = tab.querySelector("td.highlight-selected"), + cell_input = tab.querySelector("input.cell_input"), + formula = tab.querySelector("input.jexcel_formula"), + x, + y; + cell_input.value = getCoordinatesFromCell(cell); + x = Number(cell.dataset.x); + y = Number(cell.dataset.y); + formula.value = ["text", "calendar", "checkbox", "color"] + .includes(instance.options.columns[x].type) ? + instance.getValueFromCoords(x, y) : ""; + if (instance.options.columns[x].type === "text") { + formula.readOnly = false; + formula.classList.remove("readonly"); + } else { + formula.readOnly = true; + formula.classList.add("readonly"); + } + }) + + .declareJob("triggerOnEditionEndSheet", function (cell, x, y, value) { + var gadget = this, + numbers, + worksheet, + tab; + if (value) { + if (value[0] === "=" && value[value.length - 1] !== ")") { + numbers = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]; + if (numbers.includes(value[value.length - 1])) { + worksheet = gadget.element.querySelector('.selected') + .getAttribute('data-spreadsheet'); + tab = gadget.element.querySelector('.spreadsheet') + .jexcel[worksheet]; + tab.setValueFromCoords(x, y, value); + } else { + cell.textContent = value; + } + } + } + }) + + .onEvent("input", function (ev) { + var gadget = this, sheet, formula, td; + sheet = getCurrentSheet(gadget); + td = sheet.el.querySelector("td.highlight-selected"); + if (td && ev.target === td.childNodes[0]) { + formula = sheet.el.querySelector("input.jexcel_formula"); + formula.value = ev.target.value; + } + }, false, false) + + .onEvent("click", function (ev) { + var gadget = this, name; + gadget.state.saveConfig = true; + if (ev.target.classList.contains("jexcel_tab_link")) { + if (ev.target === gadget.state.selectedTabLink) { + name = prompt("Table name :", ev.target.textContent); + gadget.state.selectedTabLink.textContent = name !== null ? + name : gadget.state.selectedTabLink.textContent; + gadget.deferNotifyChange(); + } + gadget.state.selectedTabLink = ev.target; + } + }, false, false); + +}(window, rJS, jexcel, domsugar, document, alert, prompt, confirm, navigator)); \ No newline at end of file diff --git a/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jexcel.gadget.js.xml b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jexcel.gadget.js.xml new file mode 100644 index 0000000000000000000000000000000000000000..5c8cbcd107f645a56268e56cbeaabeb3384420ef --- /dev/null +++ b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jexcel.gadget.js.xml @@ -0,0 +1,28 @@ +<?xml version="1.0"?> +<ZopeData> + <record id="1" aka="AAAAAAAAAAE="> + <pickle> + <global name="File" module="OFS.Image"/> + </pickle> + <pickle> + <dictionary> + <item> + <key> <string>__name__</string> </key> + <value> <string>jexcel.gadget.js</string> </value> + </item> + <item> + <key> <string>content_type</string> </key> + <value> <string>application/javascript</string> </value> + </item> + <item> + <key> <string>precondition</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>title</string> </key> + <value> <string></string> </value> + </item> + </dictionary> + </pickle> + </record> +</ZopeData> diff --git a/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jexcel.xml b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jexcel.xml new file mode 100644 index 0000000000000000000000000000000000000000..4075101f339a97ef2cb163fdc1ca551d6c407a9f --- /dev/null +++ b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jexcel.xml @@ -0,0 +1,26 @@ +<?xml version="1.0"?> +<ZopeData> + <record id="1" aka="AAAAAAAAAAE="> + <pickle> + <global name="Folder" module="OFS.Folder"/> + </pickle> + <pickle> + <dictionary> + <item> + <key> <string>_objects</string> </key> + <value> + <tuple/> + </value> + </item> + <item> + <key> <string>id</string> </key> + <value> <string>jexcel</string> </value> + </item> + <item> + <key> <string>title</string> </key> + <value> <string></string> </value> + </item> + </dictionary> + </pickle> + </record> +</ZopeData> diff --git a/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jexcel/jexcel.css.css b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jexcel/jexcel.css.css new file mode 100644 index 0000000000000000000000000000000000000000..e848aa2b56053bf636ba40b3b1ed9342168f1bd9 --- /dev/null +++ b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jexcel/jexcel.css.css @@ -0,0 +1,740 @@ +:root { + --jexcel-border-color:#000; +} + +.jexcel_container { + display:inline-block; + padding-right:2px; + box-sizing: border-box; + overscroll-behavior: contain; + padding-bottom: 33px; +} + +.jexcel_container.fullscreen { + position:fixed; + top:38px; + left:0px; + width:100%; + height:100%; + z-index:21; +} + +.jexcel_container.fullscreen .jexcel_content { + overflow:auto; + width:100%; + height:96.5%; + background-color:#ffffff; +} + +.jexcel_container.fullscreen.with-toolbar { + height: calc(100% - 46px); +} + +.jexcel_content { + display:inline-block; + box-sizing: border-box; + padding-right:3px; + padding-bottom:3px; + position:relative; + scrollbar-width: thin; + scrollbar-color: #666 transparent; +} + +@supports (-moz-appearance:none) { + .jexcel_content { padding-right:10px; } +} + +.jexcel_content::-webkit-scrollbar { + width: 5px; + height: 5px; +} + +.jexcel_content::-webkit-scrollbar-track { + background: #eee; +} + +.jexcel_content::-webkit-scrollbar-thumb { + background: #666; +} + +.jexcel { + border-collapse: separate; + table-layout: fixed; + white-space: nowrap; + empty-cells: show; + border: 0px; + background-color: #fff; + width: 0; + + /*border-top: 1px solid transparent; + border-left: 1px solid transparent; + border-right: 1px solid #ccc; + border-bottom: 1px solid #ccc;*/ +} + +.jexcel > thead > tr > td +{ + border-top: 1px solid #ccc; + border-left: 1px solid #ccc; + border-right: 1px solid transparent; + border-bottom: 1px solid transparent; + background-color: #f3f3f3; + padding: 2px; + cursor: pointer; + box-sizing: border-box; + overflow: hidden; + position: -webkit-sticky; + position: sticky; + top: 0; + z-index:2; +} + +.with-toolbar .jexcel > thead > tr > td +{ + /**top:42px;**/ +} + +.jexcel > thead > tr > td.dragging +{ + background-color:#fff; + opacity:0.5; +} + +.jexcel > thead > tr > td.selected +{ + background-color:#dcdcdc; +} + +.jexcel > thead > tr > td.arrow-up +{ + background-repeat:no-repeat; + background-position:center right 5px; + background-image: url("data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath d='M7 14l5-5 5 5H7z' fill='gray'/%3E%3C/svg%3E"); + text-decoration:underline; +} + +.jexcel > thead > tr > td.arrow-down +{ + background-repeat:no-repeat; + background-position:center right 5px; + background-image: url("data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath d='M7 10l5 5 5-5H7z' fill='gray'/%3E%3C/svg%3E"); + text-decoration:underline; +} + +.jexcel > tbody > tr > td:first-child +{ + position:sticky; + left:0; + background-color:#f3f3f3; + text-align:center; +} + +.jexcel > tbody.resizable > tr > td:first-child::before +{ + content:'\00a0'; + width:100%; + height:3px; + position:absolute; + bottom:0px; + left:0px; + cursor:row-resize; +} + +.jexcel > tbody.draggable > tr > td:first-child::after +{ + content:'\00a0'; + width:3px; + height:100%; + position:absolute; + top:0px; + right:0px; + cursor:move; +} + +.jexcel > tbody > tr.dragging > td +{ + background-color:#eee; + opacity:0.5; +} + +.jexcel > tbody > tr > td +{ + border-top:1px solid #ccc; + border-left:1px solid #ccc; + border-right:1px solid transparent; + border-bottom:1px solid transparent; + padding:4px; + white-space: nowrap; + box-sizing: border-box; + line-height:1em; +} + +.jexcel > tbody > tr > td:last-child +{ + overflow:hidden; +} + +.jexcel > tbody > tr > td > img +{ + display:inline-block; + max-width:100px; +} + +.jexcel > tbody > tr > td.readonly +{ + color:rgba(0,0,0,0.3) +} +.jexcel > tbody > tr.selected > td:first-child +{ + background-color:#dcdcdc; +} +.jexcel > tbody > tr > td > select, +.jexcel > tbody > tr > td > input, +.jexcel > tbody > tr > td > textarea +{ + border:0px; + border-radius:0px; + outline:0px; + width:100%; + margin:0px; + padding:0px; + background-color:transparent; + box-sizing: border-box; +} + +.jexcel > tbody > tr > td > textarea +{ + resize: none; + padding-top:6px !important; +} + +.jexcel > tbody > tr > td > input[type=checkbox] +{ + width:12px; + margin-top:2px; +} +.jexcel > tbody > tr > td > input[type=radio] +{ + width:12px; + margin-top:2px; +} + +.jexcel > tbody > tr > td > select +{ + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-repeat: no-repeat; + background-position-x: 100%; + background-position-y: 40%; + background-image: url(data:image/svg+xml;base64,PHN2ZyBmaWxsPSdibGFjaycgaGVpZ2h0PScyNCcgdmlld0JveD0nMCAwIDI0IDI0JyB3aWR0aD0nMjQnIHhtbG5zPSdodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2Zyc+PHBhdGggZD0nTTcgMTBsNSA1IDUtNXonLz48cGF0aCBkPSdNMCAwaDI0djI0SDB6JyBmaWxsPSdub25lJy8+PC9zdmc+); +} + +.jexcel > tbody > tr > td.jexcel_dropdown +{ + background-repeat: no-repeat; + background-position:top 50% right 5px; + background-image: url("data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath d='M7 10l5 5 5-5H7z' fill='lightgray'/%3E%3C/svg%3E"); + text-overflow: ellipsis; + overflow-x:hidden; +} + +.jexcel > tbody > tr > td.jexcel_dropdown.jexcel_comments +{ + background:url("data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath d='M7 10l5 5 5-5H7z' fill='lightgray'/%3E%3C/svg%3E") top 50% right 5px no-repeat, url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFuGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTAxLTMxVDE4OjU1OjA4WiIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0wMS0zMVQxODo1NTowOFoiIHhtcDpNb2RpZnlEYXRlPSIyMDE5LTAxLTMxVDE4OjU1OjA4WiIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDphMTlhZDJmOC1kMDI2LTI1NDItODhjOS1iZTRkYjkyMmQ0MmQiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDpkOGI5NDUyMS00ZjEwLWQ5NDktYjUwNC0wZmU1N2I3Nzk1MDEiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDplMzdjYmE1ZS1hYTMwLWNkNDUtYTAyNS1lOWYxZjk2MzUzOGUiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDplMzdjYmE1ZS1hYTMwLWNkNDUtYTAyNS1lOWYxZjk2MzUzOGUiIHN0RXZ0OndoZW49IjIwMTktMDEtMzFUMTg6NTU6MDhaIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmExOWFkMmY4LWQwMjYtMjU0Mi04OGM5LWJlNGRiOTIyZDQyZCIgc3RFdnQ6d2hlbj0iMjAxOS0wMS0zMVQxODo1NTowOFoiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4En6MDAAAAX0lEQVQYlX3KOw6AIBBAwS32RpJADXfx0pTET+ERZJ8F8RODFtONsG0QAoh0CSDM82dqodaBdQXnfoLZQM7gPai+wjNNE8R4pTuAYNZSKZASqL7CMy0LxNgJp30fKYUDi3+vIqb/+rUAAAAASUVORK5CYII=') top right no-repeat; +} + +.jexcel > tbody > tr > td > .color +{ + width:90%; + height:10px; + margin:auto; +} + +.jexcel > tbody > tr > td > a { + text-decoration: underline; +} + +.jexcel > tbody > tr > td.highlight > a { + color: blue; + cursor: pointer; +} + +.jexcel > tfoot > tr > td +{ + border-top: 1px solid #ccc; + border-left: 1px solid #ccc; + border-right: 1px solid transparent; + border-bottom: 1px solid transparent; + background-color: #f3f3f3; + padding: 2px; + cursor: pointer; + box-sizing: border-box; + overflow: hidden; +} + +.jexcel .highlight { + background-color:rgba(0,0,0,0.05); +} + +.jexcel .highlight-top { + border-top:1px solid #000; /* var(--jexcel-border-color);*/ + box-shadow: 0px -1px #ccc; +} + +.jexcel .highlight-left { + border-left:1px solid #000; /* var(--jexcel-border-color);*/ + box-shadow: -1px 0px #ccc; +} + +.jexcel .highlight-right { + border-right:1px solid #000; /* var(--jexcel-border-color);*/ +} + +.jexcel .highlight-bottom { + border-bottom:1px solid #000; /* var(--jexcel-border-color);*/ +} + +.jexcel .highlight-top.highlight-left { + box-shadow: -1px -1px #ccc; + -webkit-box-shadow: -1px -1px #ccc; + -moz-box-shadow: -1px -1px #ccc; +} + +.jexcel .highlight-selected +{ + background-color:rgba(0,0,0,0.0); +} +.jexcel .selection +{ + background-color:rgba(0,0,0,0.05); +} +.jexcel .selection-left +{ + border-left:1px dotted #000; +} +.jexcel .selection-right +{ + border-right:1px dotted #000; +} +.jexcel .selection-top +{ + border-top:1px dotted #000; +} +.jexcel .selection-bottom +{ + border-bottom:1px dotted #000; +} +.jexcel_corner +{ + position:absolute; + background-color: rgb(0, 0, 0); + height: 1px; + width: 1px; + border: 1px solid rgb(255, 255, 255); + top:-2000px; + left:-2000px; + cursor:crosshair; + box-sizing: initial; + z-index:30; + padding: 2px; +} + +.jexcel .editor +{ + outline:0px solid transparent; + overflow:visible; + white-space: nowrap; + text-align:left; + padding:0px; + box-sizing: border-box; + overflow:visible !important; +} + +.jexcel .editor > input +{ + padding-left:4px; +} + +.jexcel .editor .jupload +{ + position:fixed; + top:100%; + z-index:40; + user-select:none; + -webkit-font-smoothing: antialiased; + font-size: .875rem; + letter-spacing: .2px; + -webkit-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: 0 8px 10px 1px rgba(0,0,0,0.14), 0 3px 14px 2px rgba(0,0,0,0.12), 0 5px 5px -3px rgba(0,0,0,0.2); + box-shadow: 0 8px 10px 1px rgba(0,0,0,0.14), 0 3px 14px 2px rgba(0,0,0,0.12), 0 5px 5px -3px rgba(0,0,0,0.2); + padding:10px; + background-color:#fff; + width:300px; + min-height:225px; + margin-top:2px; +} + +.jexcel .editor .jupload img +{ + width:100%; + height:auto; +} + +.jexcel .editor .jexcel_richtext +{ + position:fixed; + top:100%; + z-index:40; + user-select:none; + -webkit-font-smoothing: antialiased; + font-size: .875rem; + letter-spacing: .2px; + -webkit-box-shadow: 0 8px 10px 1px rgba(0,0,0,0.14), 0 3px 14px 2px rgba(0,0,0,0.12), 0 5px 5px -3px rgba(0,0,0,0.2); + box-shadow: 0 8px 10px 1px rgba(0,0,0,0.14), 0 3px 14px 2px rgba(0,0,0,0.12), 0 5px 5px -3px rgba(0,0,0,0.2); + padding:10px; + background-color:#fff; + min-width:280px; + max-width:310px; + margin-top:2px; + text-align:left; +} + +.jexcel .editor .jclose:after +{ + position:absolute; + top:0; + right:0; + margin:10px; + content:'close'; + font-family:'Material icons'; + font-size:24px; + width:24px; + height:24px; + line-height:24px; + cursor:pointer; + text-shadow: 0px 0px 5px #fff; +} + +.jexcel, .jexcel td, .jexcel_corner +{ + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-user-drag: none; + -khtml-user-drag: none; + -moz-user-drag: none; + -o-user-drag: none; + user-drag: none; +} + +.jexcel_textarea +{ + position:absolute; + top:-999px; + left:-999px; + width:1px; + height:1px; +} +.jexcel .dragline +{ + position:absolute; +} +.jexcel .dragline div +{ + position:relative; + top:-6px; + height:5px; + width:22px; +} +.jexcel .dragline div:hover +{ + cursor:move; +} + +.jexcel .onDrag +{ + background-color:rgba(0,0,0,0.6); +} + +.jexcel .error +{ + border:1px solid red; +} + +.jexcel thead td.resizing +{ + border-right-style:dotted !important; + border-right-color:red !important; +} + +.jexcel tbody tr.resizing > td +{ + border-bottom-style:dotted !important; + border-bottom-color:red !important; +} + +.jexcel tbody td.resizing +{ + border-right-style:dotted !important; + border-right-color:red !important; +} + +.jexcel .jdropdown-header +{ + border:0px !important; + outline:none !important; + width:100% !important; + height:100% !important; + padding:0px !important; + padding-left:8px !important; +} + +.jexcel .jdropdown-container +{ + margin-top:1px; +} + +.jexcel .jdropdown-container-header { + padding: 0px; + margin: 0px; + height: inherit; +} + +.jexcel .jdropdown-picker +{ + border:0px !important; + padding:0px !important; + width:inherit; + height:inherit; +} + +.jexcel .jexcel_comments +{ + background:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFuGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTAxLTMxVDE4OjU1OjA4WiIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0wMS0zMVQxODo1NTowOFoiIHhtcDpNb2RpZnlEYXRlPSIyMDE5LTAxLTMxVDE4OjU1OjA4WiIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDphMTlhZDJmOC1kMDI2LTI1NDItODhjOS1iZTRkYjkyMmQ0MmQiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDpkOGI5NDUyMS00ZjEwLWQ5NDktYjUwNC0wZmU1N2I3Nzk1MDEiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDplMzdjYmE1ZS1hYTMwLWNkNDUtYTAyNS1lOWYxZjk2MzUzOGUiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDplMzdjYmE1ZS1hYTMwLWNkNDUtYTAyNS1lOWYxZjk2MzUzOGUiIHN0RXZ0OndoZW49IjIwMTktMDEtMzFUMTg6NTU6MDhaIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmExOWFkMmY4LWQwMjYtMjU0Mi04OGM5LWJlNGRiOTIyZDQyZCIgc3RFdnQ6d2hlbj0iMjAxOS0wMS0zMVQxODo1NTowOFoiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4En6MDAAAAX0lEQVQYlX3KOw6AIBBAwS32RpJADXfx0pTET+ERZJ8F8RODFtONsG0QAoh0CSDM82dqodaBdQXnfoLZQM7gPai+wjNNE8R4pTuAYNZSKZASqL7CMy0LxNgJp30fKYUDi3+vIqb/+rUAAAAASUVORK5CYII='); + background-repeat: no-repeat; + background-position: top right; +} + +.jexcel .sp-replacer +{ + margin: 2px; + border:0px; +} + +.jexcel > thead > tr.jexcel_filter > td > input +{ + border:0px; + width:100%; + outline:none; +} + +.jexcel_about { + float: right; + font-size: 0.7em; + padding: 2px; + text-transform: uppercase; + letter-spacing: 1px; + display: none; +} +.jexcel_about a { + color: #ccc; + text-decoration: none; +} + +.jexcel_about img { + display: none; +} + +.jexcel_filter +{ + display:flex; + justify-content:space-between; + /**margin-bottom:4px;**/ +} + +.jexcel_filter > div +{ + padding:4px; + align-items:center; +} + +.jexcel_pagination +{ + display:flex; + justify-content:space-between; + align-items:center; +} + +.jexcel_pagination > div +{ + display:flex; + padding:10px; +} + +.jexcel_pagination > div:last-child +{ + padding-right:10px; + padding-top:10px; +} + +.jexcel_pagination > div > div +{ + text-align:center; + width:36px; + height:36px; + line-height:34px; + border:1px solid #ccc; + box-sizing: border-box; + margin-left:2px; + cursor:pointer; +} + +.jexcel_page +{ + font-size:0.8em; +} + +.jexcel_page_selected +{ + font-weight:bold; + background-color:#f3f3f3; +} + +.jexcel_toolbar +{ + display:flex; + background-color:#f3f3f3; + border:1px solid #ccc; + padding:4px; + margin:0px 2px 1px 1px; + position:sticky; + top:0px; + z-index:21; +} + +.jexcel_toolbar:empty +{ + display:none; +} + +.jexcel_toolbar i.jexcel_toolbar_item +{ + width:24px; + height:24px; + padding:4px; + cursor:pointer; + display:inline-block; +} + +.jexcel_toolbar i.jexcel_toolbar_item:hover +{ + background-color:#ddd; +} + +.jexcel_toolbar select.jexcel_toolbar_item +{ + margin-left:2px; + margin-right:2px; + display:inline-block; + border:0px; + background-color:transparent; + padding-right:10px; +} + +.jexcel .dragging-left +{ + background-repeat: no-repeat; + background-position:top 50% left 0px; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M14 7l-5 5 5 5V7z'/%3E%3Cpath fill='none' d='M24 0v24H0V0h24z'/%3E%3C/svg%3E"); +} + +.jexcel .dragging-right +{ + background-repeat: no-repeat; + background-position:top 50% right 0px; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M10 17l5-5-5-5v10z'/%3E%3Cpath fill='none' d='M0 24V0h24v24H0z'/%3E%3C/svg%3E"); +} + +.jexcel_tabs .jexcel_tab +{ + display:none; +} + +.jexcel_tabs .jexcel_tab_link +{ + display:inline-block; + padding:5px; + margin-right:2px; + margin-bottom:2px; + background-color:#f3f3f3; + cursor:pointer; +} + +.jexcel_tabs .jexcel_tab_link.selected +{ + background-color:#ddd; +} + +.jexcel_hidden_index > tbody > tr > td:first-child, +.jexcel_hidden_index > thead > tr > td:first-child, +.jexcel_hidden_index > colgroup > col:first-child +{ + display:none; +} + + + +.jexcel .jrating { + display: inline-flex; +} +.jexcel .jrating > div { + zoom: 0.55; +} + +.jexcel .copying-top { + border-top:1px dashed #000; +} + +.jexcel .copying-left { + border-left:1px dashed #000; +} + +.jexcel .copying-right { + border-right:1px dashed #000; +} + +.jexcel .copying-bottom { + border-bottom:1px dashed #000; +} + +.jexcel .jexcel_column_filter { + background-repeat: no-repeat; + background-position: top 50% right 5px; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='gray' width='18px' height='18px'%3E%3Cpath d='M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E"); + text-overflow: ellipsis; + overflow: hidden; + padding: 0px; + padding-left: 6px; + padding-right: 20px; +} + +.jexcel thead .jexcel_freezed { + left: 0px; + z-index: 3 !important; + box-shadow: 2px 0px 2px 0.2px #ccc !important; + -webkit-box-shadow: 2px 0px 2px 0.2px #ccc !important; + -moz-box-shadow: 2px 0px 2px 0.2px #ccc !important; +} + +.jexcel tbody .jexcel_freezed { + position: relative; + background-color: #fff; + box-shadow: 1px 1px 1px 1px #ccc !important; + -webkit-box-shadow: 2px 4px 4px 0.1px #ccc !important; + -moz-box-shadow: 2px 4px 4px 0.1px #ccc !important; +} \ No newline at end of file diff --git a/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jexcel/jexcel.css.xml b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jexcel/jexcel.css.xml new file mode 100644 index 0000000000000000000000000000000000000000..f75a3e9e7d952cb070c60564dae7e7311b3509c1 --- /dev/null +++ b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jexcel/jexcel.css.xml @@ -0,0 +1,28 @@ +<?xml version="1.0"?> +<ZopeData> + <record id="1" aka="AAAAAAAAAAE="> + <pickle> + <global name="File" module="OFS.Image"/> + </pickle> + <pickle> + <dictionary> + <item> + <key> <string>__name__</string> </key> + <value> <string>jexcel.css</string> </value> + </item> + <item> + <key> <string>content_type</string> </key> + <value> <string>text/css</string> </value> + </item> + <item> + <key> <string>precondition</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>title</string> </key> + <value> <string></string> </value> + </item> + </dictionary> + </pickle> + </record> +</ZopeData> diff --git a/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jexcel/jexcel.js.js b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jexcel/jexcel.js.js new file mode 100644 index 0000000000000000000000000000000000000000..2f303e77621aaf41bd5dda5601e69c8e42e9fc77 --- /dev/null +++ b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jexcel/jexcel.js.js @@ -0,0 +1,14031 @@ +/** + * jExcel v4.2.1 + * + * Modified version for Jexcel editor + * + * Author: Paul Hodel <paul.hodel@gmail.com> + * Website: https://bossanova.uk/jexcel/ + * Description: Create amazing web based spreadsheets. + * + * This software is distribute under MIT License + */ + + if (! jSuites && typeof(require) === 'function') { + var jSuites = require('jsuites'); + require('jsuites/dist/jsuites.css'); +} + +;(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + global.jexcel = factory(); +}(this, (function () { + + 'use strict'; + + // Jexcel core object + + var jexcel = (function(el, options) { + // Create jexcel object + var obj = {}; + obj.options = {}; + + if (! (el instanceof Element || el instanceof HTMLDocument)) { + console.error('JEXCEL: el is not a valid DOM element'); + return false; + } else if (el.tagName == 'TABLE') { + if (options = jexcel.createFromTable(el, options)) { + var div = document.createElement('div'); + el.parentNode.insertBefore(div, el); + el.remove(); + el = div; + } else { + console.error('JEXCEL: el is not a valid DOM element'); + return false; + } + } + + // Loading default configuration + var defaults = { + // External data + url:null, + // Data + data:null, + // Copy behavior + copyCompatibility:false, + root:null, + // Rows and columns definitions + rows:[], + columns:[], + // Deprected legacy options + colHeaders:[], + colWidths:[], + colAlignments:[], + nestedHeaders:null, + // Column width that is used by default + defaultColWidth:50, + defaultColAlign:'center', + // Spare rows and columns + minSpareRows:0, + minSpareCols:0, + // Minimal table dimensions + minDimensions:[0,0], + // Allow Export + allowExport:true, + // @type {boolean} - Include the header titles on download + includeHeadersOnDownload:false, + // @type {boolean} - Include the header titles on copy + includeHeadersOnCopy:false, + // Allow column sorting + columnSorting:true, + // Allow column dragging + columnDrag:false, + // Allow column resizing + columnResize:true, + // Allow row resizing + rowResize:false, + // Allow row dragging + rowDrag:true, + // Allow table edition + editable:true, + // Allow new rows + allowInsertRow:true, + // Allow new rows + allowManualInsertRow:true, + // Allow new columns + allowInsertColumn:true, + // Allow new rows + allowManualInsertColumn:true, + // Allow row delete + allowDeleteRow:true, + // Allow deleting of all rows + allowDeletingAllRows:false, + // Allow column delete + allowDeleteColumn:true, + // Allow rename column + allowRenameColumn:true, + // Allow comments + allowComments:false, + // Global wrap + wordWrap:false, + // Image options + imageOptions: null, + // CSV source + csv:null, + // Filename + csvFileName:'jexcel', + // Consider first line as header + csvHeaders:true, + // Delimiters + csvDelimiter:',', + // First row as header + parseTableFirstRowAsHeader:false, + parseTableAutoCellType:false, + // Disable corner selection + selectionCopy:true, + // Merged cells + mergeCells:{}, + // Create toolbar + toolbar:null, + // Allow search + search:false, + // Create pagination + pagination:false, + paginationOptions:null, + // Full screen + fullscreen:false, + // Lazy loading + lazyLoading:false, + loadingSpin:false, + // Table overflow + tableOverflow:false, + tableHeight:'300px', + tableWidth:null, + // Meta + meta: null, + // Style + style:null, + // Execute formulas + parseFormulas:true, + autoIncrement:true, + autoCasting:true, + // Security + secureFormulas:true, + stripHTML:true, + // Filters + filters:false, + footers:null, + // Event handles + onundo:null, + onredo:null, + onload:null, + onchange:null, + onbeforechange:null, + onafterchanges:null, + onbeforeinsertrow: null, + oninsertrow:null, + onbeforeinsertcolumn: null, + oninsertcolumn:null, + onbeforedeleterow:null, + ondeleterow:null, + onbeforedeletecolumn:null, + ondeletecolumn:null, + onmoverow:null, + onmovecolumn:null, + onresizerow:null, + onresizecolumn:null, + onsort:null, + onselection:null, + oncopy:null, + onpaste:null, + onbeforepaste:null, + onmerge:null, + onfocus:null, + onblur:null, + onchangeheader:null, + oneditionstart:null, + oneditionend:null, + onchangestyle:null, + onchangemeta:null, + onchangepage:null, + onbeforesave:null, + onsave:null, + // Global event dispatcher + onevent:null, + // Persistance + persistance:false, + // Customize any cell behavior + updateTable:null, + // Detach the HTML table when calling updateTable + detachForUpdates: false, + freezeColumns:null, + // Texts + text:{ + noRecordsFound: 'No records found', + showingPage: 'Showing page {0} of {1} entries', + show: 'Show ', + search: 'Search', + entries: ' entries', + columnName: 'Column name', + insertANewColumnBefore: 'Insert a new column before', + insertANewColumnAfter: 'Insert a new column after', + deleteSelectedColumns: 'Delete selected columns', + renameThisColumn: 'Rename this column', + orderAscending: 'Order ascending', + orderDescending: 'Order descending', + insertANewRowBefore: 'Insert a new row before', + insertANewRowAfter: 'Insert a new row after', + deleteSelectedRows: 'Delete selected rows', + editComments: 'Edit comments', + addComments: 'Add comments', + comments: 'Comments', + clearComments: 'Clear comments', + copy: 'Copy...', + paste: 'Paste...', + saveAs: 'Save as...', + about: 'About', + areYouSureToDeleteTheSelectedRows: 'Are you sure to delete the selected rows?', + areYouSureToDeleteTheSelectedColumns: 'Are you sure to delete the selected columns?', + thisActionWillDestroyAnyExistingMergedCellsAreYouSure: 'This action will destroy any existing merged cells. Are you sure?', + thisActionWillClearYourSearchResultsAreYouSure: 'This action will clear your search results. Are you sure?', + thereIsAConflictWithAnotherMergedCell: 'There is a conflict with another merged cell', + invalidMergeProperties: 'Invalid merged properties', + cellAlreadyMerged: 'Cell already merged', + noCellsSelected: 'No cells selected', + }, + // About message + about:"jExcel CE Spreadsheet\nVersion 4.2.0\nAuthor: Paul Hodel <paul.hodel@gmail.com>\nWebsite: https://bossanova.uk/jexcel/v3", + }; + + // Loading initial configuration from user + for (var property in defaults) { + if (options && options.hasOwnProperty(property)) { + if (property === 'text') { + obj.options[property] = defaults[property]; + for (var textKey in options[property]) { + if (options[property].hasOwnProperty(textKey)){ + obj.options[property][textKey] = options[property][textKey]; + } + } + } else { + obj.options[property] = options[property]; + } + } else { + obj.options[property] = defaults[property]; + } + } + + // Global elements + obj.el = el; + obj.corner = null; + obj.contextMenu = null; + obj.textarea = null; + obj.ads = null; + obj.content = null; + obj.table = null; + obj.thead = null; + obj.tbody = null; + obj.rows = []; + obj.results = null; + obj.searchInput = null; + obj.toolbar = null; + obj.pagination = null; + obj.pageNumber = null; + obj.headerContainer = null; + obj.colgroupContainer = null; + + // Containers + obj.headers = []; + obj.records = []; + obj.history = []; + obj.formula = []; + obj.colgroup = []; + obj.selection = []; + obj.highlighted = []; + obj.selectedCell = null; + obj.selectedContainer = null; + obj.style = []; + obj.data = null; + obj.filter = null; + obj.filters = []; + + // Internal controllers + obj.cursor = null; + obj.historyIndex = -1; + obj.ignoreEvents = false; + obj.ignoreHistory = false; + obj.edition = null; + obj.hashString = null; + obj.resizing = null; + obj.dragging = null; + + // Lazy loading + if (obj.options.lazyLoading == true && (obj.options.tableOverflow == false && obj.options.fullscreen == false)) { + console.error('JEXCEL: The lazyloading only works when tableOverflow = yes or fullscreen = yes'); + obj.options.lazyLoading = false; + } + + /** + * Activate/Disable fullscreen + * use programmatically : table.fullscreen(); or table.fullscreen(true); or table.fullscreen(false); + * @Param {boolean} activate + */ + obj.fullscreen = function(activate) { + // If activate not defined, get reverse options.fullscreen + if (activate == null) { + activate = ! obj.options.fullscreen; + } + + // If change + if (obj.options.fullscreen != activate) { + obj.options.fullscreen = activate; + + // Test LazyLoading conflict + if (activate == true) { + el.classList.add('fullscreen'); + } else { + el.classList.remove('fullscreen'); + } + } + } + + /** + * Trigger events + */ + obj.dispatch = function(event) { + // Dispatch events + if (! obj.ignoreEvents) { + // Call global event + if (typeof(obj.options.onevent) == 'function') { + var ret = obj.options.onevent.apply(this, arguments); + } + // Call specific events + if (typeof(obj.options[event]) == 'function') { + var ret = obj.options[event].apply(this, Array.prototype.slice.call(arguments, 1)); + } + } + + // Persistance + if (event == 'onafterchanges' && obj.options.persistance) { + var url = obj.options.persistance == true ? obj.options.url : obj.options.persistance; + var data = obj.prepareJson(arguments[2]); + obj.save(url, data); + } + + return ret; + } + + /** + * Prepare the jexcel table + * + * @Param config + */ + obj.prepareTable = function() { + // Loading initial data from remote sources + var results = []; + + // Number of columns + var size = obj.options.columns.length; + + if (obj.options.data && typeof(obj.options.data[0]) !== 'undefined') { + // Data keys + var keys = Object.keys(obj.options.data[0]); + + if (keys.length > size) { + size = keys.length; + } + } + + // Minimal dimensions + if (obj.options.minDimensions[0] > size) { + size = obj.options.minDimensions[0]; + } + + // Requests + var multiple = []; + + // Preparations + for (var i = 0; i < size; i++) { + // Deprected options. You should use only columns + if (! obj.options.colHeaders[i]) { + obj.options.colHeaders[i] = ''; + } + if (! obj.options.colWidths[i]) { + obj.options.colWidths[i] = obj.options.defaultColWidth; + } + if (! obj.options.colAlignments[i]) { + obj.options.colAlignments[i] = obj.options.defaultColAlign; + } + + // Default column description + if (! obj.options.columns[i]) { + obj.options.columns[i] = { type:'text' }; + } else if (! obj.options.columns[i].type) { + obj.options.columns[i].type = 'text'; + } + if (! obj.options.columns[i].name) { + obj.options.columns[i].name = keys && keys[i] ? keys[i] : i; + } + if (! obj.options.columns[i].source) { + obj.options.columns[i].source = []; + } + if (! obj.options.columns[i].options) { + obj.options.columns[i].options = []; + } + if (! obj.options.columns[i].editor) { + obj.options.columns[i].editor = null; + } + if (! obj.options.columns[i].allowEmpty) { + obj.options.columns[i].allowEmpty = false; + } + if (! obj.options.columns[i].title) { + obj.options.columns[i].title = obj.options.colHeaders[i] ? obj.options.colHeaders[i] : ''; + } + if (! obj.options.columns[i].width) { + obj.options.columns[i].width = obj.options.colWidths[i] ? obj.options.colWidths[i] : obj.options.defaultColWidth; + } + if (! obj.options.columns[i].align) { + obj.options.columns[i].align = obj.options.colAlignments[i] ? obj.options.colAlignments[i] : 'center'; + } + + // Pre-load initial source for json autocomplete + if (obj.options.columns[i].type == 'autocomplete' || obj.options.columns[i].type == 'dropdown') { + // if remote content + if (obj.options.columns[i].url) { + multiple.push(jSuites.ajax({ + url: obj.options.columns[i].url, + index: i, + method: 'GET', + dataType: 'json', + multiple: multiple, + success: function(data) { + var source = []; + for (var i = 0; i < data.length; i++) { + obj.options.columns[this.index].source.push(data[i]); + } + }, + complete: function() { + obj.createTable(); + } + })); + } + } else if (obj.options.columns[i].type == 'calendar') { + // Default format for date columns + if (! obj.options.columns[i].options.format) { + obj.options.columns[i].options.format = 'DD/MM/YYYY'; + } + } + } + + // On complete + if (! multiple.length) { + obj.createTable(); + } + } + + obj.createTable = function() { + // Elements + obj.table = document.createElement('table'); + obj.thead = document.createElement('thead'); + obj.tbody = document.createElement('tbody'); + + // Create headers controllers + obj.headers = []; + obj.colgroup = []; + + // Create table container + obj.content = document.createElement('div'); + obj.content.classList.add('jexcel_content'); + obj.content.onscroll = function(e) { + obj.scrollControls(e); + } + obj.content.onwheel = function(e) { + obj.wheelControls(e); + } + + // Create toolbar object + obj.toolbar = document.createElement('div'); + obj.toolbar.classList.add('jexcel_toolbar'); + + // Search + var searchContainer = document.createElement('div'); + var searchText = document.createTextNode((obj.options.text.search) + ': '); + obj.searchInput = document.createElement('input'); + obj.searchInput.classList.add('jexcel_search'); + searchContainer.appendChild(searchText); + searchContainer.appendChild(obj.searchInput); + obj.searchInput.onfocus = function() { + obj.resetSelection(); + } + + // Pagination select option + var paginationUpdateContainer = document.createElement('div'); + + if (obj.options.pagination > 0 && obj.options.paginationOptions && obj.options.paginationOptions.length > 0) { + obj.paginationDropdown = document.createElement('select'); + obj.paginationDropdown.classList.add('jexcel_pagination_dropdown'); + obj.paginationDropdown.onchange = function() { + obj.options.pagination = parseInt(this.value); + obj.page(0); + } + + for (var i = 0; i < obj.options.paginationOptions.length; i++) { + var temp = document.createElement('option'); + temp.value = obj.options.paginationOptions[i]; + temp.innerHTML = obj.options.paginationOptions[i]; + obj.paginationDropdown.appendChild(temp); + } + + paginationUpdateContainer.appendChild(document.createTextNode(obj.options.text.show)); + paginationUpdateContainer.appendChild(obj.paginationDropdown); + paginationUpdateContainer.appendChild(document.createTextNode(obj.options.text.entries)); + } + + // Filter and pagination container + var filter = document.createElement('div'); + filter.classList.add('jexcel_filter'); + filter.appendChild(paginationUpdateContainer); + filter.appendChild(searchContainer); + + // Colsgroup + obj.colgroupContainer = document.createElement('colgroup'); + var tempCol = document.createElement('col'); + tempCol.setAttribute('width', '50'); + obj.colgroupContainer.appendChild(tempCol); + + // Nested + if (obj.options.nestedHeaders && obj.options.nestedHeaders.length > 0) { + // Flexible way to handle nestedheaders + if (obj.options.nestedHeaders[0] && obj.options.nestedHeaders[0][0]) { + for (var j = 0; j < obj.options.nestedHeaders.length; j++) { + obj.thead.appendChild(obj.createNestedHeader(obj.options.nestedHeaders[j])); + } + } else { + obj.thead.appendChild(obj.createNestedHeader(obj.options.nestedHeaders)); + } + } + + // Row + obj.headerContainer = document.createElement('tr'); + var tempCol = document.createElement('td'); + tempCol.classList.add('jexcel_selectall'); + obj.headerContainer.appendChild(tempCol); + + for (var i = 0; i < obj.options.columns.length; i++) { + // Create header + obj.createCellHeader(i); + // Append cell to the container + obj.headerContainer.appendChild(obj.headers[i]); + obj.colgroupContainer.appendChild(obj.colgroup[i]); + } + + obj.thead.appendChild(obj.headerContainer); + + // Filters + if (obj.options.filters == true) { + obj.filter = document.createElement('tr'); + var td = document.createElement('td'); + obj.filter.appendChild(td); + + for (var i = 0; i < obj.options.columns.length; i++) { + var td = document.createElement('td'); + td.innerHTML = ' '; + td.setAttribute('data-x', i); + td.className = 'jexcel_column_filter'; + obj.filter.appendChild(td); + } + + obj.thead.appendChild(obj.filter); + } + + // Content table + obj.table = document.createElement('table'); + obj.table.classList.add('jexcel'); + obj.table.setAttribute('cellpadding', '0'); + obj.table.setAttribute('cellspacing', '0'); + obj.table.setAttribute('unselectable', 'yes'); + //obj.table.setAttribute('onselectstart', 'return false'); + obj.table.appendChild(obj.colgroupContainer); + obj.table.appendChild(obj.thead); + obj.table.appendChild(obj.tbody); + + // Spreadsheet corner + obj.corner = document.createElement('div'); + obj.corner.className = 'jexcel_corner'; + obj.corner.setAttribute('unselectable', 'on'); + obj.corner.setAttribute('onselectstart', 'return false'); + + if (obj.options.selectionCopy == false) { + obj.corner.style.display = 'none'; + } + + // Textarea helper + obj.textarea = document.createElement('textarea'); + obj.textarea.className = 'jexcel_textarea'; + obj.textarea.id = 'jexcel_textarea'; + obj.textarea.tabIndex = '-1'; + + // Contextmenu container + obj.contextMenu = document.createElement('div'); + obj.contextMenu.className = 'jexcel_contextmenu'; + + // Create element + jSuites.contextmenu(obj.contextMenu, { + onclick:function() { + obj.contextMenu.contextmenu.close(false); + } + }); + + // Powered by jExcel + var ads = document.createElement('a'); + ads.setAttribute('href', 'https://bossanova.uk/jexcel/'); + obj.ads = document.createElement('div'); + obj.ads.className = 'jexcel_about'; + try { + if (typeof(sessionStorage) !== "undefined" && ! sessionStorage.getItem('jexcel')) { + sessionStorage.setItem('jexcel', true); + var img = document.createElement('img'); + img.src = '//bossanova.uk/jexcel/logo.png'; + ads.appendChild(img); + } + } catch (exception) { + } + var span = document.createElement('span'); + span.innerHTML = 'Jexcel spreadsheet'; + ads.appendChild(span); + obj.ads.appendChild(ads); + + // Create table container TODO: frozen columns + var container = document.createElement('div'); + container.classList.add('jexcel_table'); + + // Pagination + obj.pagination = document.createElement('div'); + obj.pagination.classList.add('jexcel_pagination'); + var paginationInfo = document.createElement('div'); + var paginationPages = document.createElement('div'); + obj.pagination.appendChild(paginationInfo); + obj.pagination.appendChild(paginationPages); + + // Hide pagination if not in use + if (! obj.options.pagination) { + obj.pagination.style.display = 'none'; + } + + // Append containers to the table + if (obj.options.search == true) { + el.appendChild(filter); + } + + // Elements + obj.content.appendChild(obj.table); + obj.content.appendChild(obj.corner); + obj.content.appendChild(obj.textarea); + + el.appendChild(obj.toolbar); + el.appendChild(obj.content); + el.appendChild(obj.pagination); + el.appendChild(obj.contextMenu); + el.appendChild(obj.ads); + el.classList.add('jexcel_container'); + + // Create toolbar + if (obj.options.toolbar && obj.options.toolbar.length) { + obj.createToolbar(); + } + + // Fullscreen + if (obj.options.fullscreen == true) { + el.classList.add('fullscreen'); + } else { + // Overflow + if (obj.options.tableOverflow == true) { + if (obj.options.tableHeight) { + obj.content.style['overflow-y'] = 'auto'; + obj.content.style.maxHeight = obj.options.tableHeight; + } + if (obj.options.tableWidth) { + obj.content.style['overflow-x'] = 'auto'; + obj.content.style.width = obj.options.tableWidth; + } + } + } + + // With toolbars + if (obj.options.tableOverflow != true && obj.options.toolbar) { + el.classList.add('with-toolbar'); + } + + // Actions + if (obj.options.columnDrag == true) { + obj.thead.classList.add('draggable'); + } + if (obj.options.columnResize == true) { + obj.thead.classList.add('resizable'); + } + if (obj.options.rowDrag == true) { + obj.tbody.classList.add('draggable'); + } + if (obj.options.rowResize == true) { + obj.tbody.classList.add('resizable'); + } + + // Load data + obj.setData(); + + // Style + if (obj.options.style) { + obj.setStyle(obj.options.style, null, null, 1, 1); + } + } + + /** + * Refresh the data + * + * @return void + */ + obj.refresh = function() { + if (obj.options.url) { + // Loading + if (obj.options.loadingSpin == true) { + jSuites.loading.show(); + } + + jSuites.ajax({ + url: obj.options.url, + method: 'GET', + dataType: 'json', + success: function(result) { + // Data + obj.options.data = (result.data) ? result.data : result; + // Prepare table + obj.setData(); + // Hide spin + if (obj.options.loadingSpin == true) { + jSuites.loading.hide(); + } + } + }); + } else { + obj.setData(); + } + } + + /** + * Set data + * + * @param array data In case no data is sent, default is reloaded + * @return void + */ + obj.setData = function(data) { + // Update data + if (data) { + if (typeof(data) == 'string') { + data = JSON.parse(data); + } + + obj.options.data = data; + } + + // Data + if (! obj.options.data) { + obj.options.data = []; + } + + // Prepare data + if (obj.options.data && obj.options.data[0]) { + if (! Array.isArray(obj.options.data[0])) { + var data = []; + for (var j = 0; j < obj.options.data.length; j++) { + var row = []; + for (var i = 0; i < obj.options.columns.length; i++) { + row[i] = obj.options.data[j][obj.options.columns[i].name]; + } + data.push(row); + } + + obj.options.data = data; + } + } + + // Adjust minimal dimensions + var j = 0; + var i = 0; + var size_i = obj.options.columns.length; + var size_j = obj.options.data.length; + var min_i = obj.options.minDimensions[0]; + var min_j = obj.options.minDimensions[1]; + var max_i = min_i > size_i ? min_i : size_i; + var max_j = min_j > size_j ? min_j : size_j; + + for (j = 0; j < max_j; j++) { + for (i = 0; i < max_i; i++) { + if (obj.options.data[j] == undefined) { + obj.options.data[j] = []; + } + + if (obj.options.data[j][i] == undefined) { + obj.options.data[j][i] = ''; + } + } + } + + // Reset containers + obj.rows = []; + obj.results = null; + obj.records = []; + obj.history = []; + + // Reset internal controllers + obj.historyIndex = -1; + + // Reset data + obj.tbody.innerHTML = ''; + + // Lazy loading + if (obj.options.lazyLoading == true) { + // Load only 100 records + var startNumber = 0 + var finalNumber = obj.options.data.length < 100 ? obj.options.data.length : 100; + + if (obj.options.pagination) { + obj.options.pagination = false; + console.error('JEXCEL: Pagination will be disable due the lazyLoading'); + } + } else if (obj.options.pagination) { + // Pagination + if (! obj.pageNumber) { + obj.pageNumber = 0; + } + var quantityPerPage = obj.options.pagination; + startNumber = (obj.options.pagination * obj.pageNumber); + finalNumber = (obj.options.pagination * obj.pageNumber) + obj.options.pagination; + + if (obj.options.data.length < finalNumber) { + finalNumber = obj.options.data.length; + } + } else { + var startNumber = 0; + var finalNumber = obj.options.data.length; + } + + // Append nodes to the HTML + for (j = 0; j < obj.options.data.length; j++) { + // Create row + var tr = obj.createRow(j, obj.options.data[j]); + // Append line to the table + if (j >= startNumber && j < finalNumber) { + obj.tbody.appendChild(tr); + } + } + + if (obj.options.lazyLoading == true) { + // Do not create pagination with lazyloading activated + } else if (obj.options.pagination) { + obj.updatePagination(); + } + + // Merge cells + if (obj.options.mergeCells) { + var keys = Object.keys(obj.options.mergeCells); + for (var i = 0; i < keys.length; i++) { + var num = obj.options.mergeCells[keys[i]]; + obj.setMerge(keys[i], num[0], num[1], 1); + } + } + + // Updata table with custom configurations if applicable + obj.updateTable(); + + // Onload + obj.dispatch('onload', el, obj); + } + + /** + * Get the whole table data + * + * @param bool get highlighted cells only + * @return array data + */ + obj.getData = function(highlighted, dataOnly) { + // Control vars + var dataset = []; + var px = 0; + var py = 0; + + // Data type + var dataType = dataOnly == true || obj.options.copyCompatibility == false ? true : false; + + // Column and row length + var x = obj.options.columns.length + var y = obj.options.data.length + + // Go through the columns to get the data + for (var j = 0; j < y; j++) { + px = 0; + for (var i = 0; i < x; i++) { + // Cell selected or fullset + if (! highlighted || obj.records[j][i].classList.contains('highlight')) { + // Get value + if (! dataset[py]) { + dataset[py] = []; + } + if (! dataType) { + dataset[py][px] = obj.records[j][i].innerHTML; + } else { + dataset[py][px] = obj.options.data[j][i]; + } + px++; + } + } + if (px > 0) { + py++; + } + } + + return dataset; + } + + /** + * Get the whole table data + * + * @param integer row number + * @return string value + */ + obj.getJson = function(highlighted) { + // Control vars + var data = []; + + // Column and row length + var x = obj.options.columns.length + var y = obj.options.data.length + + // Go through the columns to get the data + for (var j = 0; j < y; j++) { + var row = null; + for (var i = 0; i < x; i++) { + if (! highlighted || obj.records[j][i].classList.contains('highlight')) { + if (row == null) { + row = {}; + } + if (! obj.options.columns[i].name) { + obj.options.columns[i].name = i; + } + row[obj.options.columns[i].name] = obj.options.data[j][i]; + } + } + + if (row != null) { + data.push(row); + } + } + + return data; + } + + /** + * Prepare JSON in the correct format + */ + obj.prepareJson = function(data) { + var rows = []; + for (var i = 0; i < data.length; i++) { + var x = data[i].x; + var y = data[i].y; + var k = obj.options.columns[x].name ? obj.options.columns[x].name : x; + + // Create row + if (! rows[y]) { + rows[y] = { + row: y, + data: {}, + }; + } + rows[y].data[k] = data[i].newValue; + } + + // Filter rows + return rows.filter(function (el) { + return el != null; + }); + } + + /** + * Post json to a remote server + */ + obj.save = function(url, data) { + // Parse anything in the data before sending to the server + var ret = obj.dispatch('onbeforesave', el, obj, data); +console.log(ret); + if (ret) { + var data = ret; + } else { + if (ret === false) { + return false; + } + } + + // Remove update + jSuites.ajax({ + url: url, + method: 'POST', + dataType: 'json', + data: { data: JSON.stringify(data) }, + success: function(result) { + // Event + obj.dispatch('onsave', el, obj, data); + } + }); + } + + /** + * Get a row data by rowNumber + */ + obj.getRowData = function(rowNumber) { + return obj.options.data[rowNumber]; + } + + /** + * Set a row data by rowNumber + */ + obj.setRowData = function(rowNumber, data) { + for (var i = 0; i < obj.headers.length; i++) { + // Update cell + var columnName = jexcel.getColumnNameFromId([ i, rowNumber ]); + // Set value + if (data[i] != null) { + obj.setValue(columnName, data[i]); + } + } + } + + /** + * Get a column data by columnNumber + */ + obj.getColumnData = function(columnNumber) { + var dataset = []; + // Go through the rows to get the data + for (var j = 0; j < obj.options.data.length; j++) { + dataset.push(obj.options.data[j][columnNumber]); + } + return dataset; + } + + /** + * Set a column data by colNumber + */ + obj.setColumnData = function(colNumber, data) { + for (var j = 0; j < obj.rows.length; j++) { + // Update cell + var columnName = jexcel.getColumnNameFromId([ colNumber, j ]); + // Set value + if (data[j] != null) { + obj.setValue(columnName, data[j]); + } + } + } + + /** + * Create row + */ + obj.createRow = function(j, data) { + // Create container + if (! obj.records[j]) { + obj.records[j] = []; + } + // Default data + if (! data) { + var data = obj.options.data[j]; + } + // New line of data to be append in the table + obj.rows[j] = document.createElement('tr'); + obj.rows[j].setAttribute('data-y', j); + // Definitions + if (obj.options.rows[j]) { + if (obj.options.rows[j].height) { + obj.rows[j].style.height = obj.options.rows[j].height; + } + } + // Row number label + var td = document.createElement('td'); + td.innerHTML = parseInt(j + 1); + td.setAttribute('data-y', j); + td.className = 'jexcel_row'; + obj.rows[j].appendChild(td); + + // Data columns + for (var i = 0; i < obj.options.columns.length; i++) { + // New column of data to be append in the line + obj.records[j][i] = obj.createCell(i, j, data[i]); + // Add column to the row + obj.rows[j].appendChild(obj.records[j][i]); + } + + // Add row to the table body + return obj.rows[j]; + } + + obj.parseValue = function(i, j, value) { + if ((''+value).substr(0,1) == '=' && obj.options.parseFormulas == true) { + value = obj.executeFormula(value, i, j) + } + if (obj.options.columns[i].mask) { + var decimal = obj.options.columns[i].decimal || '.'; + value = '' + jSuites.mask.run(value, obj.options.columns[i].mask, decimal); + } + + return value; + } + + /** + * Create cell + */ + obj.createCell = function(i, j, value) { + // Create cell and properties + var td = document.createElement('td'); + td.setAttribute('data-x', i); + td.setAttribute('data-y', j); + + // Security + if ((''+value).substr(0,1) == '=' && obj.options.secureFormulas == true) { + var val = secureFormula(value); + if (val != value) { + // Update the data container + value = val; + } + } + + // Custom column + if (obj.options.columns[i].editor) { + if (obj.options.stripHTML === false || obj.options.columns[i].stripHTML === false) { + td.innerHTML = value; + } else { + td.innerText = value; + } + if (typeof(obj.options.columns[i].editor.createCell) == 'function') { + td = obj.options.columns[i].editor.createCell(td); + } + } else { + // Hidden column + if (obj.options.columns[i].type == 'hidden') { + td.style.display = 'none'; + td.innerText = value; + } else if (obj.options.columns[i].type == 'checkbox' || obj.options.columns[i].type == 'radio') { + // Create input + var element = document.createElement('input'); + element.type = obj.options.columns[i].type; + element.name = 'c' + i; + element.checked = (value == 1 || value == true || value == 'true') ? true : false; + element.onclick = function() { + obj.setValue(td, this.checked); + } + + if (obj.options.columns[i].readOnly == true || obj.options.editable == false) { + element.setAttribute('disabled', 'disabled'); + } + + // Append to the table + td.appendChild(element); + // Make sure the values are correct + obj.options.data[j][i] = element.checked; + } else if (obj.options.columns[i].type == 'calendar') { + // Try formatted date + var formatted = jSuites.calendar.extractDateFromString(value, obj.options.columns[i].options.format); + // Create calendar cell + td.innerText = jSuites.calendar.getDateString(formatted ? formatted : value, obj.options.columns[i].options.format); + } else if (obj.options.columns[i].type == 'dropdown' || obj.options.columns[i].type == 'autocomplete') { + // Create dropdown cell + td.classList.add('jexcel_dropdown'); + td.innerText = obj.getDropDownValue(i, value); + } else if (obj.options.columns[i].type == 'color') { + if (obj.options.columns[i].render == 'square') { + var color = document.createElement('div'); + color.className = 'color'; + color.style.backgroundColor = value; + td.appendChild(color); + } else { + td.style.color = value; + td.innerText = value; + } + } else if (obj.options.columns[i].type == 'image') { + if (value && value.substr(0, 10) == 'data:image') { + var img = document.createElement('img'); + img.src = value; + td.appendChild(img); + } + } else { + if (obj.options.columns[i].type == 'html') { + td.innerHTML = stripScript(obj.parseValue(i, j, value)); + } else { + if (obj.options.stripHTML === false || obj.options.columns[i].stripHTML === false) { + td.innerHTML = stripScript(obj.parseValue(i, j, value)); + } else { + td.innerText = obj.parseValue(i, j, value); + } + } + } + } + + // Readonly + if (obj.options.columns[i].readOnly == true) { + td.className = 'readonly'; + } + + // Text align + var colAlign = obj.options.columns[i].align ? obj.options.columns[i].align : 'center'; + td.style.textAlign = colAlign; + + // Wrap option + if (obj.options.columns[i].wordWrap != false && (obj.options.wordWrap == true || obj.options.columns[i].wordWrap == true || td.innerHTML.length > 200)) { + td.style.whiteSpace = 'pre-wrap'; + } + + // Overflow + if (i > 0) { + if (value || td.innerHTML) { + obj.records[j][i-1].style.overflow = 'hidden'; + } else { + if (i == obj.options.columns.length - 1) { + td.style.overflow = 'hidden'; + } + } + } + + return td; + } + + obj.createCellHeader = function(colNumber) { + // Create col global control + var colWidth = obj.options.columns[colNumber].width ? obj.options.columns[colNumber].width : obj.options.defaultColWidth; + var colAlign = obj.options.columns[colNumber].align ? obj.options.columns[colNumber].align : obj.options.defaultColAlign; + + // Create header cell + obj.headers[colNumber] = document.createElement('td'); + obj.headers[colNumber].innerText = obj.options.columns[colNumber].title ? obj.options.columns[colNumber].title : jexcel.getColumnName(colNumber); + obj.headers[colNumber].setAttribute('data-x', colNumber); + obj.headers[colNumber].style.textAlign = colAlign; + if (obj.options.columns[colNumber].title) { + obj.headers[colNumber].setAttribute('title', obj.options.columns[colNumber].title); + } + + // Width control + obj.colgroup[colNumber] = document.createElement('col'); + obj.colgroup[colNumber].setAttribute('width', colWidth); + + // Hidden column + if (obj.options.columns[colNumber].type == 'hidden') { + obj.headers[colNumber].style.display = 'none'; + obj.colgroup[colNumber].style.display = 'none'; + } + } + + /** + * Update a nested header title + */ + obj.updateNestedHeader = function(x, y, title) { + if (obj.options.nestedHeaders[y][x].title) { + obj.options.nestedHeaders[y][x].title = title; + obj.options.nestedHeaders[y].element.children[x+1].innerText = title; + } + } + + /** + * Create a nested header object + */ + obj.createNestedHeader = function(nestedInformation) { + var tr = document.createElement('tr'); + tr.classList.add('jexcel_nested'); + var td = document.createElement('td'); + tr.appendChild(td); + // Element + nestedInformation.element = tr; + + var headerIndex = 0; + for (var i = 0; i < nestedInformation.length; i++) { + // Default values + if (! nestedInformation[i].colspan) { + nestedInformation[i].colspan = 1; + } + if (! nestedInformation[i].align) { + nestedInformation[i].align = 'center'; + } + if (! nestedInformation[i].title) { + nestedInformation[i].title = ''; + } + + // Number of columns + var numberOfColumns = nestedInformation[i].colspan; + + // Classes container + var column = []; + // Header classes for this cell + for (var x = 0; x < numberOfColumns; x++) { + if (obj.options.columns[headerIndex] && obj.options.columns[headerIndex].type == 'hidden') { + numberOfColumns++; + } + column.push(headerIndex); + headerIndex++; + } + + // Created the nested cell + var td = document.createElement('td'); + td.setAttribute('data-column', column.join(',')); + td.setAttribute('colspan', nestedInformation[i].colspan); + td.setAttribute('align', nestedInformation[i].align); + td.innerText = nestedInformation[i].title; + tr.appendChild(td); + } + + return tr; + } + + /** + * Create toolbar + */ + obj.createToolbar = function(toolbar) { + if (toolbar) { + obj.options.toolbar = toolbar; + } else { + var toolbar = obj.options.toolbar; + } + + for (var i = 0; i < toolbar.length; i++) { + if (toolbar[i].type == 'i') { + var toolbarItem = document.createElement('i'); + toolbarItem.classList.add('jexcel_toolbar_item'); + toolbarItem.classList.add('material-icons'); + toolbarItem.setAttribute('data-k', toolbar[i].k); + toolbarItem.setAttribute('data-v', toolbar[i].v); + // Tooltip + if (toolbar[i].tooltip) { + toolbarItem.setAttribute('title', toolbar[i].tooltip); + } + // Handle click + if (toolbar[i].onclick && typeof(toolbar[i].onclick)) { + toolbarItem.onclick = (function (a) { + var b = a; + return function () { + toolbar[b].onclick(el, obj, this); + }; + })(i); + } else { + toolbarItem.onclick = function() { + var k = this.getAttribute('data-k'); + var v = this.getAttribute('data-v'); + obj.setStyle(obj.highlighted, k, v); + } + } + // Append element + toolbarItem.innerText = toolbar[i].content; + obj.toolbar.appendChild(toolbarItem); + } else if (toolbar[i].type == 'select') { + var toolbarItem = document.createElement('select'); + toolbarItem.classList.add('jexcel_toolbar_item'); + toolbarItem.setAttribute('data-k', toolbar[i].k); + // Tooltip + if (toolbar[i].tooltip) { + toolbarItem.setAttribute('title', toolbar[i].tooltip); + } + // Handle onchange + if (toolbar[i].onchange && typeof(toolbar[i].onchange)) { + toolbarItem.onchange = toolbar[i].onchange; + } else { + toolbarItem.onchange = function() { + var k = this.getAttribute('data-k'); + obj.setStyle(obj.highlighted, k, this.value); + } + } + // Add options to the dropdown + for(var j = 0; j < toolbar[i].v.length; j++) { + var toolbarDropdownOption = document.createElement('option'); + toolbarDropdownOption.value = toolbar[i].v[j]; + toolbarDropdownOption.innerText = toolbar[i].v[j]; + toolbarItem.appendChild(toolbarDropdownOption); + } + obj.toolbar.appendChild(toolbarItem); + } else if (toolbar[i].type == 'color') { + var toolbarItem = document.createElement('i'); + toolbarItem.classList.add('jexcel_toolbar_item'); + toolbarItem.classList.add('material-icons'); + toolbarItem.setAttribute('data-k', toolbar[i].k); + toolbarItem.setAttribute('data-v', ''); + // Tooltip + if (toolbar[i].tooltip) { + toolbarItem.setAttribute('title', toolbar[i].tooltip); + } + obj.toolbar.appendChild(toolbarItem); + toolbarItem.onclick = function() { + this.color.open(); + } + toolbarItem.innerText = toolbar[i].content; + jSuites.color(toolbarItem, { + onchange:function(o, v) { + var k = o.getAttribute('data-k'); + obj.setStyle(obj.highlighted, k, v); + } + }); + } + } + } + + /** + * Merge cells + * @param cellName + * @param colspan + * @param rowspan + * @param ignoreHistoryAndEvents + */ + obj.setMerge = function(cellName, colspan, rowspan, ignoreHistoryAndEvents) { + var test = false; + + if (! cellName) { + if (! obj.highlighted.length) { + alert(obj.options.text.noCellsSelected); + return null; + } else { + var x1 = parseInt(obj.highlighted[0].getAttribute('data-x')); + var y1 = parseInt(obj.highlighted[0].getAttribute('data-y')); + var x2 = parseInt(obj.highlighted[obj.highlighted.length-1].getAttribute('data-x')); + var y2 = parseInt(obj.highlighted[obj.highlighted.length-1].getAttribute('data-y')); + var cellName = jexcel.getColumnNameFromId([ x1, y1 ]); + var colspan = (x2 - x1) + 1; + var rowspan = (y2 - y1) + 1; + } + } + + var cell = jexcel.getIdFromColumnName(cellName, true); + + if (obj.options.mergeCells[cellName]) { + if (obj.records[cell[1]][cell[0]].getAttribute('data-merged')) { + test = obj.options.text.cellAlreadyMerged; + } + } else if ((! colspan || colspan < 2) && (! rowspan || rowspan < 2)) { + test = obj.options.text.invalidMergeProperties; + } else { + var cells = []; + for (var j = cell[1]; j < cell[1] + rowspan; j++) { + for (var i = cell[0]; i < cell[0] + colspan; i++) { + var columnName = jexcel.getColumnNameFromId([i, j]); + if (obj.records[j][i].getAttribute('data-merged')) { + test = obj.options.text.thereIsAConflictWithAnotherMergedCell; + } + } + } + } + + if (test) { + alert(test); + } else { + // Add property + if (colspan > 1) { + obj.records[cell[1]][cell[0]].setAttribute('colspan', colspan); + } else { + colspan = 1; + } + if (rowspan > 1) { + obj.records[cell[1]][cell[0]].setAttribute('rowspan', rowspan); + } else { + rowspan = 1; + } + // Keep links to the existing nodes + obj.options.mergeCells[cellName] = [ colspan, rowspan, [] ]; + // Mark cell as merged + obj.records[cell[1]][cell[0]].setAttribute('data-merged', 'true'); + // Overflow + obj.records[cell[1]][cell[0]].style.overflow = 'hidden'; + // History data + var data = []; + // Adjust the nodes + for (var y = cell[1]; y < cell[1] + rowspan; y++) { + for (var x = cell[0]; x < cell[0] + colspan; x++) { + if (! (cell[0] == x && cell[1] == y)) { + data.push(obj.options.data[y][x]); + obj.updateCell(x, y, '', true); + obj.options.mergeCells[cellName][2].push(obj.records[y][x]); + obj.records[y][x].style.display = 'none'; + obj.records[y][x] = obj.records[cell[1]][cell[0]]; + } + } + } + // In the initialization is not necessary keep the history + //obj.updateSelection(obj.records[cell[1]][cell[0]]); + + if (! ignoreHistoryAndEvents) { + obj.setHistory({ + action:'setMerge', + column:cellName, + colspan:colspan, + rowspan:rowspan, + data:data, + }); + + obj.dispatch('onmerge', el, cellName, colspan, rowspan); + } + } + } + + /** + * Merge cells + * @param cellName + * @param colspan + * @param rowspan + * @param ignoreHistoryAndEvents + */ + obj.getMerge = function(cellName) { + var data = {}; + if (cellName) { + if (obj.options.mergeCells[cellName]) { + data = [ obj.options.mergeCells[cellName][0], obj.options.mergeCells[cellName][1] ]; + } else { + data = null; + } + } else { + if (obj.options.mergeCells) { + var mergedCells = obj.options.mergeCells; + var keys = Object.keys(obj.options.mergeCells); + for (var i = 0; i < keys.length; i++) { + data[keys[i]] = [ obj.options.mergeCells[keys[i]][0], obj.options.mergeCells[keys[i]][1] ]; + } + } + } + + return data; + } + + /** + * Remove merge by cellname + * @param cellName + */ + obj.removeMerge = function(cellName, data, keepOptions) { + if (obj.options.mergeCells[cellName]) { + var cell = jexcel.getIdFromColumnName(cellName, true); + obj.records[cell[1]][cell[0]].removeAttribute('colspan'); + obj.records[cell[1]][cell[0]].removeAttribute('rowspan'); + obj.records[cell[1]][cell[0]].removeAttribute('data-merged'); + var info = obj.options.mergeCells[cellName]; + + var index = 0; + for (var j = 0; j < info[1]; j++) { + for (var i = 0; i < info[0]; i++) { + if (j > 0 || i > 0) { + obj.records[cell[1]+j][cell[0]+i] = info[2][index]; + obj.records[cell[1]+j][cell[0]+i].style.display = ''; + // Recover data + if (data && data[index]) { + obj.updateCell(cell[0]+i, cell[1]+j, data[index]); + } + index++; + } + } + } + + // Update selection + obj.updateSelection(obj.records[cell[1]][cell[0]], obj.records[cell[1]+j-1][cell[0]+i-1]); + + if (! keepOptions) { + delete(obj.options.mergeCells[cellName]); + } + } + } + + /** + * Remove all merged cells + */ + obj.destroyMerged = function(keepOptions) { + // Remove any merged cells + if (obj.options.mergeCells) { + var mergedCells = obj.options.mergeCells; + var keys = Object.keys(obj.options.mergeCells); + for (var i = 0; i < keys.length; i++) { + obj.removeMerge(keys[i], null, keepOptions); + } + } + } + + /** + * Is column merged + */ + obj.isColMerged = function(x, insertBefore) { + var cols = []; + // Remove any merged cells + if (obj.options.mergeCells) { + var keys = Object.keys(obj.options.mergeCells); + for (var i = 0; i < keys.length; i++) { + var info = jexcel.getIdFromColumnName(keys[i], true); + var colspan = obj.options.mergeCells[keys[i]][0]; + var x1 = info[0]; + var x2 = info[0] + (colspan > 1 ? colspan - 1 : 0); + + if (insertBefore == null) { + if ((x1 <= x && x2 >= x)) { + cols.push(keys[i]); + } + } else { + if (insertBefore) { + if ((x1 < x && x2 >= x)) { + cols.push(keys[i]); + } + } else { + if ((x1 <= x && x2 > x)) { + cols.push(keys[i]); + } + } + } + } + } + + return cols; + } + + /** + * Is rows merged + */ + obj.isRowMerged = function(y, insertBefore) { + var rows = []; + // Remove any merged cells + if (obj.options.mergeCells) { + var keys = Object.keys(obj.options.mergeCells); + for (var i = 0; i < keys.length; i++) { + var info = jexcel.getIdFromColumnName(keys[i], true); + var rowspan = obj.options.mergeCells[keys[i]][1]; + var y1 = info[1]; + var y2 = info[1] + (rowspan > 1 ? rowspan - 1 : 0); + + if (insertBefore == null) { + if ((y1 <= y && y2 >= y)) { + rows.push(keys[i]); + } + } else { + if (insertBefore) { + if ((y1 < y && y2 >= y)) { + rows.push(keys[i]); + } + } else { + if ((y1 <= y && y2 > y)) { + rows.push(keys[i]); + } + } + } + } + } + + return rows; + } + + /** + * Open the column filter + */ + obj.openFilter = function(columnId) { + if (! obj.options.filters) { + console.log('JEXCEL: filters not enabled.'); + } else { + // Make sure is integer + columnId = parseInt(columnId); + // Reset selection + obj.resetSelection(); + // Load options + var options = []; + for (var j = 0; j < obj.options.data.length; j++) { + var k = obj.options.data[j][columnId]; + var v = obj.records[j][columnId].innerHTML; + if (k && v) { + options[k] = v; + } + } + var keys = Object.keys(options); + var optionsFiltered = []; + for (var j = 0; j < keys.length; j++) { + optionsFiltered.push({ id: keys[j], name: options[keys[j]] }); + } + + // Create dropdown + var div = document.createElement('div'); + obj.filter.children[columnId + 1].innerHTML = ''; + obj.filter.children[columnId + 1].appendChild(div); + obj.filter.children[columnId + 1].style.paddingLeft = '0px'; + obj.filter.children[columnId + 1].style.paddingRight = '0px'; + obj.filter.children[columnId + 1].style.overflow = 'initial'; + + // Dynamic dropdown + jSuites.dropdown(div, { + data: optionsFiltered, + multiple: true, + autocomplete: true, + opened: true, + value: obj.filters[columnId] ? obj.filters[columnId] : '', + width:'100%', + position: (obj.options.tableOverflow == true || obj.options.fullscreen == true) ? true : false, + onclose: function(o) { + obj.resetFilters(); + obj.filters[columnId] = o.dropdown.getValue(true); + obj.filter.children[columnId + 1].innerHTML = o.dropdown.getText(); + obj.filter.children[columnId + 1].style.paddingLeft = ''; + obj.filter.children[columnId + 1].style.paddingRight = ''; + obj.filter.children[columnId + 1].style.overflow = ''; + obj.closeFilter(columnId); + } + }); + } + } + + obj.resetFilters = function() { + if (obj.options.filters) { + for (var i = 0; i < obj.filter.children.length; i++) { + obj.filter.children[i].innerHTML = ' '; + obj.filters[i] = null; + } + } + } + + obj.closeFilter = function(columnId) { + if (! columnId) { + for (var i = 0; i < obj.filter.children.length; i++) { + if (obj.filters[i]) { + columnId = i; + } + } + } + + // Search filter + var search = function(query, x, y) { + for (var i = 0; i < query.length; i++) { + if ((''+obj.options.data[y][x]).search(query[i]) >= 0 || + (''+obj.records[y][x].innerHTML).search(query[i]) >= 0) { + return true; + } + } + return false; + } + + var query = obj.filters[columnId]; + obj.results = []; + for (var j = 0; j < obj.options.data.length; j++) { + if (search(query, columnId, j)) { + obj.results.push(j); + } + } + if (! obj.results.length) { + obj.results = null; + } + + obj.updateResult(); + } + + /** + * Open the editor + * + * @param object cell + * @return void + */ + obj.openEditor = function(cell, empty, e) { + // Get cell position + var y = cell.getAttribute('data-y'); + var x = cell.getAttribute('data-x'); + + // On edition start + obj.dispatch('oneditionstart', el, cell, x, y); + + // Overflow + if (x > 0) { + obj.records[y][x-1].style.overflow = 'hidden'; + } + + // Create editor + var createEditor = function(type) { + // Cell information + var info = cell.getBoundingClientRect(); + + // Create dropdown + var editor = document.createElement(type); + editor.style.width = (info.width) + 'px'; + editor.style.height = (info.height - 2) + 'px'; + editor.style.minHeight = (info.height - 2) + 'px'; + + // Edit cell + cell.classList.add('editor'); + cell.innerHTML = ''; + cell.appendChild(editor); + + return editor; + } + + // Readonly + if (cell.classList.contains('readonly') == true) { + // Do nothing + } else { + // Holder + obj.edition = [ obj.records[y][x], obj.records[y][x].innerHTML, x, y ]; + + // If there is a custom editor for it + if (obj.options.columns[x].editor) { + // Custom editors + obj.options.columns[x].editor.openEditor(cell, el, empty, e); + } else { + // Native functions + if (obj.options.columns[x].type == 'hidden') { + // Do nothing + } else if (obj.options.columns[x].type == 'checkbox' || obj.options.columns[x].type == 'radio') { + // Get value + var value = cell.children[0].checked ? false : true; + // Toogle value + obj.setValue(cell, value); + // Do not keep edition open + obj.edition = null; + } else if (obj.options.columns[x].type == 'dropdown' || obj.options.columns[x].type == 'autocomplete') { + // Get current value + var value = obj.options.data[y][x]; + + // Create dropdown + if (typeof(obj.options.columns[x].filter) == 'function') { + var source = obj.options.columns[x].filter(el, cell, x, y, obj.options.columns[x].source); + } else { + var source = obj.options.columns[x].source; + } + + // Create editor + var editor = createEditor('div'); + var options = { + data: source, + multiple: obj.options.columns[x].multiple ? true : false, + autocomplete: obj.options.columns[x].autocomplete || obj.options.columns[x].type == 'autocomplete' ? true : false, + opened:true, + value: obj.options.columns[x].multiple ? value.split(';') : value, + width:'100%', + height:editor.style.minHeight, + position: (obj.options.tableOverflow == true || obj.options.fullscreen == true) ? true : false, + onclose:function() { + obj.closeEditor(cell, true); + } + }; + if (obj.options.columns[x].options && obj.options.columns[x].options.type) { + options.type = obj.options.columns[x].options.type; + } + jSuites.dropdown(editor, options); + } else if (obj.options.columns[x].type == 'calendar' || obj.options.columns[x].type == 'color') { + // Value + var value = obj.options.data[y][x]; + // Create editor + var editor = createEditor('input'); + editor.value = value; + + if (obj.options.tableOverflow == true || obj.options.fullscreen == true) { + obj.options.columns[x].options.position = true; + } + obj.options.columns[x].options.value = obj.options.data[y][x]; + obj.options.columns[x].options.opened = true; + obj.options.columns[x].options.onclose = function(el, value) { + obj.closeEditor(cell, true); + } + // Current value + if (obj.options.columns[x].type == 'color') { + jSuites.color(editor, obj.options.columns[x].options); + } else { + jSuites.calendar(editor, obj.options.columns[x].options); + } + // Focus on editor + editor.focus(); + } else if (obj.options.columns[x].type == 'html') { + var value = obj.options.data[y][x]; + // Create editor + var editor = createEditor('div'); + editor.style.position = 'relative'; + var div = document.createElement('div'); + div.classList.add('jexcel_richtext'); + editor.appendChild(div); + jSuites.editor(div, { + focus: true, + value: value, + }); + const rect = cell.getBoundingClientRect(); + const rectContent = div.getBoundingClientRect(); + if (window.innerHeight < rect.bottom + rectContent.height) { + div.style.top = (rect.top - (rectContent.height + 2)) + 'px'; + } else { + div.style.top = (rect.top) + 'px'; + } + } else if (obj.options.columns[x].type == 'image') { + // Value + var img = cell.children[0]; + // Create editor + var editor = createEditor('div'); + editor.style.position = 'relative'; + var div = document.createElement('div'); + div.classList.add('jclose'); + if (img && img.src) { + div.appendChild(img); + } + editor.appendChild(div); + jSuites.image(div, obj.options.imageOptions); + const rect = cell.getBoundingClientRect(); + const rectContent = div.getBoundingClientRect(); + if (window.innerHeight < rect.bottom + rectContent.height) { + div.style.top = (rect.top - (rectContent.height + 2)) + 'px'; + } else { + div.style.top = (rect.top) + 'px'; + } + } else { + // Value + var value = empty == true ? '' : obj.options.data[y][x]; + + // Basic editor + if (obj.options.columns[x].wordWrap != false && (obj.options.wordWrap == true || obj.options.columns[x].wordWrap == true)) { + var editor = createEditor('textarea'); + } else { + var editor = createEditor('input'); + // Mask + if (obj.options.columns[x].mask) { + editor.setAttribute('data-mask', obj.options.columns[x].mask); + } + } + + editor.onblur = function() { + obj.closeEditor(cell, true); + }; + editor.focus(); + editor.value = value; + } + } + } + } + + /** + * Close the editor and save the information + * + * @param object cell + * @param boolean save + * @return void + */ + obj.closeEditor = function(cell, save) { + var x = parseInt(cell.getAttribute('data-x')); + var y = parseInt(cell.getAttribute('data-y')); + + // Get cell properties + if (save == true) { + // If custom editor + if (obj.options.columns[x].editor) { + // Custom editor + var value = obj.options.columns[x].editor.closeEditor(cell, save); + } else { + // Native functions + if (obj.options.columns[x].type == 'checkbox' || obj.options.columns[x].type == 'radio' || obj.options.columns[x].type == 'hidden') { + // Do nothing + } else if (obj.options.columns[x].type == 'dropdown' || obj.options.columns[x].type == 'autocomplete') { + var value = cell.children[0].dropdown.close(true); + } else if (obj.options.columns[x].type == 'calendar') { + var value = cell.children[0].calendar.close(true); + } else if (obj.options.columns[x].type == 'color') { + var value = cell.children[0].color.close(true); + } else if (obj.options.columns[x].type == 'html') { + var value = cell.children[0].children[0].editor.getData(); + } else if (obj.options.columns[x].type == 'image') { + var img = cell.children[0].children[0].children[0]; + var value = img && img.tagName == 'IMG' ? img.src : ''; + } else if (obj.options.columns[x].type == 'numeric') { + var value = cell.children[0].value; + if (value.substr(0,1) != '=') { + if (value == '') { + value = obj.options.columns[x].allowEmpty ? '' : 0; + } + } + cell.children[0].onblur = null; + } else { + var value = cell.children[0].value; + cell.children[0].onblur = null; + } + } + + // Ignore changes if the value is the same + if (obj.options.data[y][x] == value) { + cell.innerHTML = obj.edition[1]; + } else { + obj.setValue(cell, value); + } + } else { + if (obj.options.columns[x].editor) { + // Custom editor + obj.options.columns[x].editor.closeEditor(cell, save); + } else { + if (obj.options.columns[x].type == 'dropdown' || obj.options.columns[x].type == 'autocomplete') { + cell.children[0].dropdown.close(true); + } else if (obj.options.columns[x].type == 'calendar') { + cell.children[0].calendar.close(true); + } else if (obj.options.columns[x].type == 'color') { + cell.children[0].color.close(true); + } else { + cell.children[0].onblur = null; + } + } + + // Restore value + cell.innerHTML = obj.edition && obj.edition[1] ? obj.edition[1] : ''; + } + + // On edition end + obj.dispatch('oneditionend', el, cell, x, y, value, save); + + // Remove editor class + cell.classList.remove('editor'); + + // Finish edition + obj.edition = null; + } + + /** + * Get the cell object + * + * @param object cell + * @return string value + */ + obj.getCell = function(cell) { + // Convert in case name is excel liked ex. A10, BB92 + cell = jexcel.getIdFromColumnName(cell, true); + var x = cell[0]; + var y = cell[1]; + + return obj.records[y][x]; + } + + /** + * Get the cell object from coords + * + * @param object cell + * @return string value + */ + obj.getCellFromCoords = function(x, y) { + return obj.records[y][x]; + } + + /** + * Get label + * + * @param object cell + * @return string value + */ + obj.getLabel = function(cell) { + // Convert in case name is excel liked ex. A10, BB92 + cell = jexcel.getIdFromColumnName(cell, true); + var x = cell[0]; + var y = cell[1]; + + return obj.records[y][x].innerHTML; + } + + /** + * Get labelfrom coords + * + * @param object cell + * @return string value + */ + obj.getLabelFromCoords = function(x, y) { + return obj.records[y][x].innerHTML; + } + + /** + * Get the value from a cell + * + * @param object cell + * @return string value + */ + obj.getValue = function(cell, processedValue) { + if (typeof(cell) == 'object') { + var x = cell.getAttribute('data-x'); + var y = cell.getAttribute('data-y'); + } else { + cell = jexcel.getIdFromColumnName(cell, true); + var x = cell[0]; + var y = cell[1]; + } + + var value = null; + + if (x != null && y != null) { + if (obj.records[y] && obj.records[y][x] && (processedValue || obj.options.copyCompatibility == true)) { + value = obj.records[y][x].innerHTML; + } else { + if (obj.options.data[y] && obj.options.data[y][x] != 'undefined') { + value = obj.options.data[y][x]; + } + } + } + + return value; + } + + /** + * Get the value from a coords + * + * @param int x + * @param int y + * @return string value + */ + obj.getValueFromCoords = function(x, y, processedValue) { + var value = null; + + if (x != null && y != null) { + if ((obj.records[y] && obj.records[y][x]) && processedValue || obj.options.copyCompatibility == true) { + value = obj.records[y][x].innerHTML; + } else { + if (obj.options.data[y] && obj.options.data[y][x] != 'undefined') { + value = obj.options.data[y][x]; + } + } + } + + return value; + } + + /** + * Set a cell value + * + * @param mixed cell destination cell + * @param string value value + * @return void + */ + obj.setValue = function(cell, value, force) { + var records = []; + + if (typeof(cell) == 'string') { + var columnId = jexcel.getIdFromColumnName(cell, true); + var x = columnId[0]; + var y = columnId[1]; + + // Update cell + records.push(obj.updateCell(x, y, value, force)); + + // Update all formulas in the chain + obj.updateFormulaChain(x, y, records); + } else { + var x = null; + var y = null; + if (cell && cell.getAttribute) { + var x = cell.getAttribute('data-x'); + var y = cell.getAttribute('data-y'); + } + + // Update cell + if (x != null && y != null) { + records.push(obj.updateCell(x, y, value, force)); + + // Update all formulas in the chain + obj.updateFormulaChain(x, y, records); + } else { + var keys = Object.keys(cell); + if (keys.length > 0) { + for (var i = 0; i < keys.length; i++) { + if (typeof(cell[i]) == 'string') { + var columnId = jexcel.getIdFromColumnName(cell[i], true); + var x = columnId[0]; + var y = columnId[1]; + } else { + if (cell[i].x != null && cell[i].y != null) { + var x = cell[i].x; + var y = cell[i].y; + // Flexible setup + if (cell[i].newValue != null) { + value = cell[i].newValue; + } else if (cell[i].value != null) { + value = cell[i].value; + } + } else { + var x = cell[i].getAttribute('data-x'); + var y = cell[i].getAttribute('data-y'); + } + } + + // Update cell + if (x != null && y != null) { + records.push(obj.updateCell(x, y, value, force)); + + // Update all formulas in the chain + obj.updateFormulaChain(x, y, records); + } + } + } + } + } + + // Update history + obj.setHistory({ + action:'setValue', + records:records, + selection:obj.selectedCell, + }); + + // Update table with custom configurations if applicable + obj.updateTable(); + + // On after changes + obj.onafterchanges(el, records); + } + + /** + * Set a cell value based on coordinates + * + * @param int x destination cell + * @param int y destination cell + * @param string value + * @return void + */ + obj.setValueFromCoords = function(x, y, value, force) { + var records = []; + records.push(obj.updateCell(x, y, value, force)); + + // Update all formulas in the chain + obj.updateFormulaChain(x, y, records); + + // Update history + obj.setHistory({ + action:'setValue', + records:records, + selection:obj.selectedCell, + }); + + // Update table with custom configurations if applicable + obj.updateTable(); + + // On after changes + obj.onafterchanges(el, records); + } + + /** + * Toogle + */ + obj.setCheckRadioValue = function() { + var records = []; + var keys = Object.keys(obj.highlighted); + for (var i = 0; i < keys.length; i++) { + var x = obj.highlighted[i].getAttribute('data-x'); + var y = obj.highlighted[i].getAttribute('data-y'); + + if (obj.options.columns[x].type == 'checkbox' || obj.options.columns[x].type == 'radio') { + // Update cell + records.push(obj.updateCell(x, y, ! obj.options.data[y][x])); + } + } + + if (records.length) { + // Update history + obj.setHistory({ + action:'setValue', + records:records, + selection:obj.selectedCell, + }); + + // On after changes + obj.onafterchanges(el, records); + } + } + + /** + * Strip tags + */ + var stripScript = function(a) { + var b = new Option; + b.innerHTML = a; + var c = null; + for (a = b.getElementsByTagName('script'); c=a[0];) c.parentNode.removeChild(c); + return b.innerHTML; + } + + /** + * Update cell content + * + * @param object cell + * @return void + */ + obj.updateCell = function(x, y, value, force) { + // Changing value depending on the column type + if (obj.records[y][x].classList.contains('readonly') == true && ! force) { + // Do nothing + var record = { + x: x, + y: y, + col: x, + row: y + } + } else { + // Security + if ((''+value).substr(0,1) == '=' && obj.options.secureFormulas == true) { + var val = secureFormula(value); + if (val != value) { + // Update the data container + value = val; + } + } + + // On change + var val = obj.dispatch('onbeforechange', el, obj.records[y][x], x, y, value); + + // If you return something this will overwrite the value + if (val != undefined) { + value = val; + } + + if (obj.options.columns[x].editor && typeof(obj.options.columns[x].editor.updateCell) == 'function') { + value = obj.options.columns[x].editor.updateCell(obj.records[y][x], value, force); + } + + // History format + var record = { + x: x, + y: y, + col: x, + row: y, + newValue: value, + oldValue: obj.options.data[y][x], + } + + if (obj.options.columns[x].editor) { + // Update data and cell + obj.options.data[y][x] = value; + } else { + // Native functions + if (obj.options.columns[x].type == 'checkbox' || obj.options.columns[x].type == 'radio') { + // Unchecked all options + if (obj.options.columns[x].type == 'radio') { + for (var j = 0; j < obj.options.data.length; j++) { + obj.options.data[j][x] = false; + } + } + + // Update data and cell + obj.records[y][x].children[0].checked = (value == 1 || value == true || value == 'true' || value == 'TRUE') ? true : false; + obj.options.data[y][x] = obj.records[y][x].children[0].checked; + } else if (obj.options.columns[x].type == 'dropdown' || obj.options.columns[x].type == 'autocomplete') { + // Update data and cell + obj.options.data[y][x] = value; + obj.records[y][x].innerText = obj.getDropDownValue(x, value); + } else if (obj.options.columns[x].type == 'calendar') { + // Update calendar + var formatted = jSuites.calendar.extractDateFromString(value, obj.options.columns[x].options.format); + // Update data and cell + obj.options.data[y][x] = value; + obj.records[y][x].innerText = jSuites.calendar.getDateString(formatted ? formatted : value, obj.options.columns[x].options.format); + } else if (obj.options.columns[x].type == 'color') { + // Update color + obj.options.data[y][x] = value; + // Render + if (obj.options.columns[x].render == 'square') { + var color = document.createElement('div'); + color.className = 'color'; + color.style.backgroundColor = value; + obj.records[y][x].innerText = ''; + obj.records[y][x].appendChild(color); + } else { + obj.records[y][x].style.color = value; + obj.records[y][x].innerText = value; + } + } else if (obj.options.columns[x].type == 'image') { + value = ''+value; + obj.options.data[y][x] = value; + obj.records[y][x].innerHTML = ''; + if (value && value.substr(0, 10) == 'data:image') { + var img = document.createElement('img'); + img.src = value; + obj.records[y][x].appendChild(img); + } + } else { + // Update data and cell + obj.options.data[y][x] = value; + // Label + if (obj.options.columns[x].type == 'html') { + obj.records[y][x].innerHTML = stripScript(obj.parseValue(x, y, value)); + } else { + if (obj.options.stripHTML === false || obj.options.columns[x].stripHTML === false) { + obj.records[y][x].innerHTML = stripScript(obj.parseValue(x, y, value)); + } else { + obj.records[y][x].innerText = obj.parseValue(x, y, value); + } + } + // Handle big text inside a cell + if (obj.options.columns[x].wordWrap != false && (obj.options.wordWrap == true || obj.options.columns[x].wordWrap == true || obj.records[y][x].innerHTML.length > 200)) { + obj.records[y][x].style.whiteSpace = 'pre-wrap'; + } else { + obj.records[y][x].style.whiteSpace = ''; + } + } + } + + // Overflow + if (x > 0) { + if (value) { + obj.records[y][x-1].style.overflow = 'hidden'; + } else { + obj.records[y][x-1].style.overflow = ''; + } + } + + // On change + obj.dispatch('onchange', el, (obj.records[y] && obj.records[y][x] ? obj.records[y][x] : null), x, y, value, record.oldValue); + } + + return record; + } + + /** + * Helper function to copy data using the corner icon + */ + obj.copyData = function(o, d) { + // Get data from all selected cells + var data = obj.getData(true, true); + + // Selected cells + var h = obj.selectedContainer; + + // Cells + var x1 = parseInt(o.getAttribute('data-x')); + var y1 = parseInt(o.getAttribute('data-y')); + var x2 = parseInt(d.getAttribute('data-x')); + var y2 = parseInt(d.getAttribute('data-y')); + + // Records + var records = []; + var breakControl = false; + + if (h[0] == x1) { + // Vertical copy + if (y1 < h[1]) { + var rowNumber = y1 - h[1]; + } else { + var rowNumber = 1; + } + var colNumber = 0; + } else { + if (x1 < h[0]) { + var colNumber = x1 - h[0]; + } else { + var colNumber = 1; + } + var rowNumber = 0; + } + + // Copy data procedure + var posx = 0; + var posy = 0; + + for (var j = y1; j <= y2; j++) { + // Skip hidden rows + if (obj.rows[j] && obj.rows[j].style.display == 'none') { + continue; + } + + // Controls + if (data[posy] == undefined) { + posy = 0; + } + posx = 0; + + // Data columns + if (h[0] != x1) { + if (x1 < h[0]) { + var colNumber = x1 - h[0]; + } else { + var colNumber = 1; + } + } + // Data columns + for (var i = x1; i <= x2; i++) { + // Update non-readonly + if (obj.records[j][i] && ! obj.records[j][i].classList.contains('readonly') && obj.records[j][i].style.display != 'none' && breakControl == false) { + // Stop if contains value + if (! obj.selection.length) { + if (obj.options.data[j][i] != '') { + breakControl = true; + continue; + } + } + + // Column + if (data[posy] == undefined) { + posx = 0; + } else if (data[posy][posx] == undefined) { + posx = 0; + } + + // Value + var value = data[posy][posx]; + + if (value && ! data[1] && obj.options.autoIncrement == true) { + if (obj.options.columns[i].type == 'text' || obj.options.columns[i].type == 'number') { + if ((''+value).substr(0,1) == '=') { + var tokens = value.match(/([A-Z]+[0-9]+)/g); + + if (tokens) { + var affectedTokens = []; + for (var index = 0; index < tokens.length; index++) { + var position = jexcel.getIdFromColumnName(tokens[index], 1); + position[0] += colNumber; + position[1] += rowNumber; + if (position[1] < 0) { + position[1] = 0; + } + var token = jexcel.getColumnNameFromId([position[0], position[1]]); + + if (token != tokens[index]) { + affectedTokens[tokens[index]] = token; + } + } + // Update formula + if (affectedTokens) { + value = obj.updateFormula(value, affectedTokens) + } + } + } else { + if (value == Number(value)) { + value = Number(value) + rowNumber; + } + } + } else if (obj.options.columns[i].type == 'calendar') { + var date = new Date(value); + date.setDate(date.getDate() + rowNumber); + value = date.getFullYear() + '-' + jexcel.doubleDigitFormat(parseInt(date.getMonth() + 1)) + '-' + jexcel.doubleDigitFormat(date.getDate()) + ' ' + '00:00:00'; + } + } + + records.push(obj.updateCell(i, j, value)); + + // Update all formulas in the chain + obj.updateFormulaChain(i, j, records); + } + posx++; + if (h[0] != x1) { + colNumber++; + } + } + posy++; + rowNumber++; + } + + // Update history + obj.setHistory({ + action:'setValue', + records:records, + selection:obj.selectedCell, + }); + + // Update table with custom configuration if applicable + obj.updateTable(); + + // On after changes + obj.onafterchanges(el, records); + } + + /** + * Refresh current selection + */ + obj.refreshSelection = function() { + if (obj.selectedCell) { + obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); + } + } + + /** + * Move coords to A1 in case ovelaps with an excluded cell + */ + obj.conditionalSelectionUpdate = function(type, o, d) { + if (type == 1) { + if (obj.selectedCell && ((o >= obj.selectedCell[1] && o <= obj.selectedCell[3]) || (d >= obj.selectedCell[1] && d <= obj.selectedCell[3]))) { + obj.resetSelection(); + return; + } + } else { + if (obj.selectedCell && ((o >= obj.selectedCell[0] && o <= obj.selectedCell[2]) || (d >= obj.selectedCell[0] && d <= obj.selectedCell[2]))) { + obj.resetSelection(); + return; + } + } + } + + /** + * Clear table selection + */ + obj.resetSelection = function(blur) { + // Remove style + if (! obj.highlighted.length) { + var previousStatus = 0; + } else { + var previousStatus = 1; + + for (var i = 0; i < obj.highlighted.length; i++) { + obj.highlighted[i].classList.remove('highlight'); + obj.highlighted[i].classList.remove('highlight-left'); + obj.highlighted[i].classList.remove('highlight-right'); + obj.highlighted[i].classList.remove('highlight-top'); + obj.highlighted[i].classList.remove('highlight-bottom'); + obj.highlighted[i].classList.remove('highlight-selected'); + + var px = parseInt(obj.highlighted[i].getAttribute('data-x')); + var py = parseInt(obj.highlighted[i].getAttribute('data-y')); + + // Check for merged cells + if (obj.highlighted[i].getAttribute('data-merged')) { + var colspan = parseInt(obj.highlighted[i].getAttribute('colspan')); + var rowspan = parseInt(obj.highlighted[i].getAttribute('rowspan')); + var ux = colspan > 0 ? px + (colspan - 1) : px; + var uy = rowspan > 0 ? py + (rowspan - 1): py; + } else { + var ux = px; + var uy = py; + } + + // Remove selected from headers + for (var j = px; j <= ux; j++) { + if (obj.headers[j]) { + obj.headers[j].classList.remove('selected'); + } + } + + // Remove selected from rows + for (var j = py; j <= uy; j++) { + if (obj.rows[j]) { + obj.rows[j].classList.remove('selected'); + } + } + } + } + + // Reset highlighed cells + obj.highlighted = []; + + // Reset + obj.selectedCell = null; + + // Hide corner + obj.corner.style.top = '-2000px'; + obj.corner.style.left = '-2000px'; + + if (blur == true && previousStatus == 1) { + obj.dispatch('onblur', el); + } + + return previousStatus; + } + + /** + * Update selection based on two cells + */ + obj.updateSelection = function(el1, el2, origin) { + var x1 = el1.getAttribute('data-x'); + var y1 = el1.getAttribute('data-y'); + if (el2) { + var x2 = el2.getAttribute('data-x'); + var y2 = el2.getAttribute('data-y'); + } else { + var x2 = x1; + var y2 = y1; + } + + obj.updateSelectionFromCoords(x1, y1, x2, y2, origin); + } + + /** + * Update selection from coords + */ + obj.updateSelectionFromCoords = function(x1, y1, x2, y2, origin) { + // Reset Selection + var updated = null; + var previousState = obj.resetSelection(); + + // Same element + if (x2 == null) { + x2 = x1; + } + if (y2 == null) { + y2 = y1; + } + + // Selection must be within the existing data + if (x1 >= obj.headers.length) { + x1 = obj.headers.length - 1; + } + if (y1 >= obj.rows.length) { + y1 = obj.rows.length - 1; + } + if (x2 >= obj.headers.length) { + x2 = obj.headers.length - 1; + } + if (y2 >= obj.rows.length) { + y2 = obj.rows.length - 1; + } + + // Keep selected cell + obj.selectedCell = [x1, y1, x2, y2]; + + // Select cells + if (x1 != null) { + // Add selected cell + if (obj.records[y1][x1]) { + obj.records[y1][x1].classList.add('highlight-selected'); + } + + // Origin & Destination + if (parseInt(x1) < parseInt(x2)) { + var px = parseInt(x1); + var ux = parseInt(x2); + } else { + var px = parseInt(x2); + var ux = parseInt(x1); + } + + if (parseInt(y1) < parseInt(y2)) { + var py = parseInt(y1); + var uy = parseInt(y2); + } else { + var py = parseInt(y2); + var uy = parseInt(y1); + } + + // Verify merged columns + for (var i = px; i <= ux; i++) { + for (var j = py; j <= uy; j++) { + if (obj.records[j][i] && obj.records[j][i].getAttribute('data-merged')) { + var x = parseInt(obj.records[j][i].getAttribute('data-x')); + var y = parseInt(obj.records[j][i].getAttribute('data-y')); + var colspan = parseInt(obj.records[j][i].getAttribute('colspan')); + var rowspan = parseInt(obj.records[j][i].getAttribute('rowspan')); + + if (colspan > 1) { + if (x < px) { + px = x; + } + if (x + colspan > ux) { + ux = x + colspan - 1; + } + } + + if (rowspan) { + if (y < py) { + py = y; + + } + if (y + rowspan > uy) { + uy = y + rowspan - 1; + } + } + } + } + } + + // Limits + var borderLeft = null; + var borderRight = null; + var borderTop = null; + var borderBottom = null; + + // Vertical limits + for (var j = py; j <= uy; j++) { + if (obj.rows[j].style.display != 'none') { + if (borderTop == null) { + borderTop = j; + } + borderBottom = j; + } + } + + // Redefining styles + for (var i = px; i <= ux; i++) { + for (var j = py; j <= uy; j++) { + if (obj.rows[j].style.display != 'none' && obj.records[j][i].style.display != 'none') { + obj.records[j][i].classList.add('highlight'); + obj.highlighted.push(obj.records[j][i]); + } + } + + // Horizontal limits + if (obj.options.columns[i].type != 'hidden') { + if (borderLeft == null) { + borderLeft = i; + } + borderRight = i; + } + } + + // Create borders + if (! borderLeft) { + borderLeft = 0; + } + if (! borderRight) { + borderRight = 0; + } + for (var i = borderLeft; i <= borderRight; i++) { + if (obj.options.columns[i].type != 'hidden') { + // Top border + if (obj.records[borderTop][i]) { + obj.records[borderTop][i].classList.add('highlight-top'); + } + // Bottom border + if (obj.records[borderBottom][i]) { + obj.records[borderBottom][i].classList.add('highlight-bottom'); + } + // Add selected from headers + obj.headers[i].classList.add('selected'); + } + } + + for (var j = borderTop; j <= borderBottom; j++) { + if (obj.rows[j].style.display != 'none') { + // Left border + obj.records[j][borderLeft].classList.add('highlight-left'); + // Right border + obj.records[j][borderRight].classList.add('highlight-right'); + // Add selected from rows + obj.rows[j].classList.add('selected'); + } + } + + obj.selectedContainer = [ borderLeft, borderTop, borderRight, borderBottom ]; + } + + // Handle events + if (previousState == 0) { + obj.dispatch('onfocus', el); + + obj.removeCopyingSelection(); + } + + obj.dispatch('onselection', el, borderLeft, borderTop, borderRight, borderBottom, origin); + + // Find corner cell + obj.updateCornerPosition(); + } + + /** + * Remove copy selection + * + * @return void + */ + obj.removeCopySelection = function() { + // Remove current selection + for (var i = 0; i < obj.selection.length; i++) { + obj.selection[i].classList.remove('selection'); + obj.selection[i].classList.remove('selection-left'); + obj.selection[i].classList.remove('selection-right'); + obj.selection[i].classList.remove('selection-top'); + obj.selection[i].classList.remove('selection-bottom'); + } + + obj.selection = []; + } + + /** + * Update copy selection + * + * @param int x, y + * @return void + */ + obj.updateCopySelection = function(x3, y3) { + // Remove selection + obj.removeCopySelection(); + + // Get elements first and last + var x1 = obj.selectedContainer[0]; + var y1 = obj.selectedContainer[1]; + var x2 = obj.selectedContainer[2]; + var y2 = obj.selectedContainer[3]; + + if (x3 != null && y3 != null) { + if (x3 - x2 > 0) { + var px = parseInt(x2) + 1; + var ux = parseInt(x3); + } else { + var px = parseInt(x3); + var ux = parseInt(x1) - 1; + } + + if (y3 - y2 > 0) { + var py = parseInt(y2) + 1; + var uy = parseInt(y3); + } else { + var py = parseInt(y3); + var uy = parseInt(y1) - 1; + } + + if (ux - px <= uy - py) { + var px = parseInt(x1); + var ux = parseInt(x2); + } else { + var py = parseInt(y1); + var uy = parseInt(y2); + } + + for (var j = py; j <= uy; j++) { + for (var i = px; i <= ux; i++) { + if (obj.records[j][i] && obj.rows[j].style.display != 'none' && obj.records[j][i].style.display != 'none') { + obj.records[j][i].classList.add('selection'); + obj.records[py][i].classList.add('selection-top'); + obj.records[uy][i].classList.add('selection-bottom'); + obj.records[j][px].classList.add('selection-left'); + obj.records[j][ux].classList.add('selection-right'); + + // Persist selected elements + obj.selection.push(obj.records[j][i]); + } + } + } + } + } + + /** + * Update corner position + * + * @return void + */ + obj.updateCornerPosition = function() { + // If any selected cells + if (! obj.highlighted.length) { + obj.corner.style.top = '-2000px'; + obj.corner.style.left = '-2000px'; + } else { + // Get last cell + var last = obj.highlighted[obj.highlighted.length-1]; + + const contentRect = obj.content.getBoundingClientRect(); + var x1 = contentRect.left; + var y1 = contentRect.top; + + const lastRect = last.getBoundingClientRect(); + var x2 = lastRect.left; + var y2 = lastRect.top; + var w2 = lastRect.width; + var h2 = lastRect.height; + + var x = (x2 - x1) + obj.content.scrollLeft + w2 - 4; + var y = (y2 - y1) + obj.content.scrollTop + h2 - 4; + + // Place the corner in the correct place + obj.corner.style.top = y + 'px'; + obj.corner.style.left = x + 'px'; + + if (obj.options.freezeColumns) { + var width = obj.getFreezeWidth(); + if (x2 - x1 + w2 < width) { + obj.corner.style.display = 'none'; + } else { + obj.corner.style.display = ''; + } + } else { + obj.corner.style.display = ''; + } + } + } + + /** + * Update scroll position based on the selection + */ + obj.updateScroll = function(direction) { + // jExcel Container information + const contentRect = obj.content.getBoundingClientRect(); + var x1 = contentRect.left; + var y1 = contentRect.top; + var w1 = contentRect.width; + var h1 = contentRect.height; + + // Direction Left or Up + var reference = obj.records[obj.selectedCell[3]][obj.selectedCell[2]]; + + // Reference + const referenceRect = reference.getBoundingClientRect(); + var x2 = referenceRect.left; + var y2 = referenceRect.top; + var w2 = referenceRect.width; + var h2 = referenceRect.height; + + // Direction + if (direction == 0 || direction == 1) { + var x = (x2 - x1) + obj.content.scrollLeft; + var y = (y2 - y1) + obj.content.scrollTop - 2; + } else { + var x = (x2 - x1) + obj.content.scrollLeft + w2; + var y = (y2 - y1) + obj.content.scrollTop + h2; + } + + // Top position check + if (y > (obj.content.scrollTop + 30) && y < (obj.content.scrollTop + h1)) { + // In the viewport + } else { + // Out of viewport + if (y < obj.content.scrollTop + 30) { + obj.content.scrollTop = y - h2; + } else { + obj.content.scrollTop = y - (h1 - 2); + } + } + + // Freeze columns? + var freezed = obj.getFreezeWidth(); + + // Left position check - TODO: change that to the bottom border of the element + if (x > (obj.content.scrollLeft + freezed) && x < (obj.content.scrollLeft + w1)) { + // In the viewport + } else { + // Out of viewport + if (x < obj.content.scrollLeft + 30) { + obj.content.scrollLeft = x; + if (obj.content.scrollLeft < 50) { + obj.content.scrollLeft = 0; + } + } else if (x < obj.content.scrollLeft + freezed) { + obj.content.scrollLeft = x - freezed - 1; + } else { + obj.content.scrollLeft = x - (w1 - 20); + } + } + } + + /** + * Get the column width + * + * @param int column column number (first column is: 0) + * @return int current width + */ + obj.getWidth = function(column) { + if (! column) { + // Get all headers + var data = []; + for (var i = 0; i < obj.headers.length; i++) { + data.push(obj.options.columns[i].width); + } + } else { + // In case the column is an object + if (typeof(column) == 'object') { + column = $(column).getAttribute('data-x'); + } + + data = obj.colgroup[column].getAttribute('width') + } + + return data; + } + + + /** + * Set the column width + * + * @param int column number (first column is: 0) + * @param int new column width + * @param int old column width + */ + obj.setWidth = function (column, width, oldWidth) { + if (width) { + if (Array.isArray(column)) { + // Oldwidth + if (! oldWidth) { + var oldWidth = []; + } + // Set width + for (var i = 0; i < column.length; i++) { + if (! oldWidth[i]) { + oldWidth[i] = obj.colgroup[column[i]].getAttribute('width'); + } + var w = Array.isArray(width) && width[i] ? width[i] : width; + obj.colgroup[column[i]].setAttribute('width', w); + obj.options.columns[column[i]].width = w; + } + } else { + // Oldwidth + if (! oldWidth) { + oldWidth = obj.colgroup[column].getAttribute('width'); + } + // Set width + obj.colgroup[column].setAttribute('width', width); + obj.options.columns[column].width = width; + } + + // Keeping history of changes + obj.setHistory({ + action:'setWidth', + column:column, + oldValue:oldWidth, + newValue:width, + }); + + // On resize column + obj.dispatch('onresizecolumn', el, column, width, oldWidth); + + // Update corner position + obj.updateCornerPosition(); + } + } + + /** + * Set the row height + * + * @param row - row number (first row is: 0) + * @param height - new row height + * @param oldHeight - old row height + */ + obj.setHeight = function (row, height, oldHeight) { + if (height > 0) { + // In case the column is an object + if (typeof(row) == 'object') { + row = row.getAttribute('data-y'); + } + + // Oldwidth + if (! oldHeight) { + oldHeight = obj.rows[row].getAttribute('height'); + + if (! oldHeight) { + var rect = obj.rows[row].getBoundingClientRect(); + oldHeight = rect.height; + } + } + + // Integer + height = parseInt(height); + + // Set width + obj.rows[row].style.height = height + 'px'; + + // Keep options updated + if (! obj.options.rows[row]) { + obj.options.rows[row] = {}; + } + obj.options.rows[row].height = height + 'px'; + + // Keeping history of changes + obj.setHistory({ + action:'setHeight', + row:row, + oldValue:oldHeight, + newValue:height, + }); + + // On resize column + obj.dispatch('onresizerow', el, row, height, oldHeight); + + // Update corner position + obj.updateCornerPosition(); + } + } + + /** + * Get the row height + * + * @param row - row number (first row is: 0) + * @return height - current row height + */ + obj.getHeight = function(row) { + if (! row) { + // Get height of all rows + var data = []; + for (var j = 0; j < obj.rows.length; j++) { + var h = obj.rows[j].style.height; + if (h) { + data[j] = h; + } + } + } else { + // In case the row is an object + if (typeof(row) == 'object') { + row = $(row).getAttribute('data-y'); + } + + var data = obj.rows[row].style.height; + } + + return data; + } + + obj.setFooter = function(data) { + if (data) { + obj.options.footers = data; + } + + if (obj.options.footers) { + if (! obj.tfoot) { + obj.tfoot = document.createElement('tfoot'); + obj.table.appendChild(obj.tfoot); + } + + for (var j = 0; j < obj.options.footers.length; j++) { + if (obj.tfoot.children[j]) { + var tr = obj.tfoot.children[j]; + } else { + var tr = document.createElement('tr'); + var td = document.createElement('td'); + tr.appendChild(td); + obj.tfoot.appendChild(tr); + } + for (var i = 0; i < obj.headers.length; i++) { + if (! obj.options.footers[j][i]) { + obj.options.footers[j][i] = ''; + } + if (obj.tfoot.children[j].children[i+1]) { + var td = obj.tfoot.children[j].children[i+1]; + } else { + var td = document.createElement('td'); + tr.appendChild(td); + + // Text align + var colAlign = obj.options.columns[i].align ? obj.options.columns[i].align : 'center'; + td.style.textAlign = colAlign; + } + td.innerText = obj.parseValue(i, j, obj.options.footers[j][i]); + } + } + } + } + + /** + * Get the column title + * + * @param column - column number (first column is: 0) + * @param title - new column title + */ + obj.getHeader = function(column) { + return obj.headers[column].innerText; + } + + /** + * Set the column title + * + * @param column - column number (first column is: 0) + * @param title - new column title + */ + obj.setHeader = function(column, newValue) { + if (obj.headers[column]) { + var oldValue = obj.headers[column].innerText; + + if (! newValue) { + newValue = prompt(obj.options.text.columnName, oldValue) + } + + if (newValue) { + obj.headers[column].innerText = newValue; + // Keep the title property + obj.headers[column].setAttribute('title', newValue); + // Update title + obj.options.columns[column].title = newValue; + } + + obj.setHistory({ + action: 'setHeader', + column: column, + oldValue: oldValue, + newValue: newValue + }); + + // On onchange header + obj.dispatch('onchangeheader', el, column, oldValue, newValue); + } + } + + /** + * Get the headers + * + * @param asArray + * @return mixed + */ + obj.getHeaders = function (asArray) { + var title = []; + + for (var i = 0; i < obj.headers.length; i++) { + title.push(obj.getHeader(i)); + } + + return asArray ? title : title.join(obj.options.csvDelimiter); + } + + /** + * Get meta information from cell(s) + * + * @return integer + */ + obj.getMeta = function(cell, key) { + if (! cell) { + return obj.options.meta; + } else { + if (key) { + return obj.options.meta[cell] && obj.options.meta[cell][key] ? obj.options.meta[cell][key] : null; + } else { + return obj.options.meta[cell] ? obj.options.meta[cell] : null; + } + } + } + + /** + * Set meta information to cell(s) + * + * @return integer + */ + obj.setMeta = function(o, k, v) { + if (! obj.options.meta) { + obj.options.meta = {} + } + + if (k && v) { + // Set data value + if (! obj.options.meta[o]) { + obj.options.meta[o] = {}; + } + obj.options.meta[o][k] = v; + } else { + // Apply that for all cells + var keys = Object.keys(o); + for (var i = 0; i < keys.length; i++) { + if (! obj.options.meta[keys[i]]) { + obj.options.meta[keys[i]] = {}; + } + + var prop = Object.keys(o[keys[i]]); + for (var j = 0; j < prop.length; j++) { + obj.options.meta[keys[i]][prop[j]] = o[keys[i]][prop[j]]; + } + } + } + + obj.dispatch('onchangemeta', el, o, k, v); + } + + /** + * Update meta information + * + * @return integer + */ + obj.updateMeta = function(affectedCells) { + if (obj.options.meta) { + var newMeta = {}; + var keys = Object.keys(obj.options.meta); + for (var i = 0; i < keys.length; i++) { + if (affectedCells[keys[i]]) { + newMeta[affectedCells[keys[i]]] = obj.options.meta[keys[i]]; + } else { + newMeta[keys[i]] = obj.options.meta[keys[i]]; + } + } + // Update meta information + obj.options.meta = newMeta; + } + } + + /** + * Get style information from cell(s) + * + * @return integer + */ + obj.getStyle = function(cell, key) { + // Cell + if (! cell) { + // Control vars + var data = {}; + + // Column and row length + var x = obj.options.data[0].length; + var y = obj.options.data.length; + + // Go through the columns to get the data + for (var j = 0; j < y; j++) { + for (var i = 0; i < x; i++) { + // Value + var v = key ? obj.records[j][i].style[key] : obj.records[j][i].getAttribute('style'); + + // Any meta data for this column? + if (v) { + // Column name + var k = jexcel.getColumnNameFromId([i, j]); + // Value + data[k] = v; + } + } + } + + return data; + } else { + cell = jexcel.getIdFromColumnName(cell, true); + + return key ? obj.records[cell[1]][cell[0]].style[key] : obj.records[cell[1]][cell[0]].getAttribute('style'); + } + }, + + obj.resetStyle = function(o, ignoreHistoryAndEvents) { + var keys = Object.keys(o); + for (var i = 0; i < keys.length; i++) { + // Position + var cell = jexcel.getIdFromColumnName(keys[i], true); + if (obj.records[cell[1]] && obj.records[cell[1]][cell[0]]) { + obj.records[cell[1]][cell[0]].setAttribute('style', ''); + } + } + obj.setStyle(o, null, null, null, ignoreHistoryAndEvents); + } + + /** + * Set meta information to cell(s) + * + * @return integer + */ + obj.setStyle = function(o, k, v, force, ignoreHistoryAndEvents) { + var newValue = {}; + var oldValue = {}; + + // Apply style + var applyStyle = function(cellId, key, value) { + // Position + var cell = jexcel.getIdFromColumnName(cellId, true); + + if (obj.records[cell[1]] && obj.records[cell[1]][cell[0]]) { + // Current value + var currentValue = obj.records[cell[1]][cell[0]].style[key]; + + // Change layout + if (currentValue == value && ! force) { + value = ''; + obj.records[cell[1]][cell[0]].style[key] = ''; + } else { + obj.records[cell[1]][cell[0]].style[key] = value; + } + + // History + if (! oldValue[cellId]) { + oldValue[cellId] = []; + } + if (! newValue[cellId]) { + newValue[cellId] = []; + } + + oldValue[cellId].push([key + ':' + currentValue]); + newValue[cellId].push([key + ':' + value]); + } + } + + if (k && v) { + // Get object from string + if (typeof(o) == 'string') { + applyStyle(o, k, v); + } else { + // Avoid duplications + var oneApplication = []; + // Apply that for all cells + for (var i = 0; i < o.length; i++) { + var x = o[i].getAttribute('data-x'); + var y = o[i].getAttribute('data-y'); + var cellName = jexcel.getColumnNameFromId([x, y]); + // This happens when is a merged cell + if (! oneApplication[cellName]) { + applyStyle(cellName, k, v); + oneApplication[cellName] = true; + } + } + } + } else { + var keys = Object.keys(o); + for (var i = 0; i < keys.length; i++) { + var style = o[keys[i]]; + if (typeof(style) == 'string') { + style = style.split(';'); + } + for (var j = 0; j < style.length; j++) { + if (typeof(style[j]) == 'string') { + style[j] = style[j].split(':'); + } + // Apply value + if (style[j][0].trim()) { + applyStyle(keys[i], style[j][0].trim(), style[j][1]); + } + } + } + } + + var keys = Object.keys(oldValue); + for (var i = 0; i < keys.length; i++) { + oldValue[keys[i]] = oldValue[keys[i]].join(';'); + } + var keys = Object.keys(newValue); + for (var i = 0; i < keys.length; i++) { + newValue[keys[i]] = newValue[keys[i]].join(';'); + } + + if (! ignoreHistoryAndEvents) { + // Keeping history of changes + obj.setHistory({ + action: 'setStyle', + oldValue: oldValue, + newValue: newValue, + }); + } + + obj.dispatch('onchangestyle', el, o, k, v); + } + + /** + * Get cell comments, null cell for all + */ + obj.getComments = function(cell, withAuthor) { + if (cell) { + if (typeof(cell) == 'string') { + var cell = jexcel.getIdFromColumnName(cell, true); + } + + if (withAuthor) { + return [obj.records[cell[1]][cell[0]].getAttribute('title'), obj.records[cell[1]][cell[0]].getAttribute('author')]; + } else { + return obj.records[cell[1]][cell[0]].getAttribute('title') || ''; + } + } else { + var data = {}; + for (var j = 0; j < obj.options.data.length; j++) { + for (var i = 0; i < obj.options.columns.length; i++) { + var comments = obj.records[j][i].getAttribute('title'); + if (comments) { + var cell = jexcel.getColumnNameFromId([i, j]); + data[cell] = comments; + } + } + } + return data; + } + } + + /** + * Set cell comments + */ + obj.setComments = function(cellId, comments, author) { + if (typeof(cellId) == 'string') { + var cell = jexcel.getIdFromColumnName(cellId, true); + } else { + var cell = cellId; + } + + // Keep old value + var title = obj.records[cell[1]][cell[0]].getAttribute('title'); + var author = obj.records[cell[1]][cell[0]].getAttribute('data-author'); + var oldValue = [ title, author ]; + + // Set new values + obj.records[cell[1]][cell[0]].setAttribute('title', comments ? comments : ''); + obj.records[cell[1]][cell[0]].setAttribute('data-author', author ? author : ''); + + // Remove class if there is no comment + if (comments) { + obj.records[cell[1]][cell[0]].classList.add('jexcel_comments'); + } else { + obj.records[cell[1]][cell[0]].classList.remove('jexcel_comments'); + } + + // Save history + obj.setHistory({ + action:'setComments', + column: cellId, + newValue: [ comments, author ], + oldValue: oldValue, + }); + } + + /** + * Get table config information + */ + obj.getConfig = function() { + var options = obj.options; + options.style = obj.getStyle(); + options.mergeCells = obj.getMerge(); + options.comments = obj.getComments(); + + return options; + } + + /** + * Sort data and reload table + */ + obj.orderBy = function(column, order) { + if (column >= 0) { + // Merged cells + if (Object.keys(obj.options.mergeCells).length > 0) { + if (! confirm(obj.options.text.thisActionWillDestroyAnyExistingMergedCellsAreYouSure)) { + return false; + } else { + // Remove merged cells + obj.destroyMerged(); + } + } + + // Direction + if (order == null) { + order = obj.headers[column].classList.contains('arrow-down') ? 1 : 0; + } else { + order = order ? 1 : 0; + } + + // Filter + Array.prototype.orderBy = function(p, o) { + return this.slice(0).sort(function(a, b) { + var valueA = a[p]; + var valueB = b[p]; + + if (! o) { + return (valueA == '' && valueB != '') ? 1 : (valueA != '' && valueB == '') ? -1 : (valueA > valueB) ? 1 : (valueA < valueB) ? -1 : 0; + } else { + return (valueA == '' && valueB != '') ? 1 : (valueA != '' && valueB == '') ? -1 : (valueA > valueB) ? -1 : (valueA < valueB) ? 1 : 0; + } + }); + } + + // Test order + var temp = []; + if (obj.options.columns[column].type == 'number' || obj.options.columns[column].type == 'percentage' || obj.options.columns[column].type == 'autonumber' || obj.options.columns[column].type == 'color') { + for (var j = 0; j < obj.options.data.length; j++) { + temp[j] = [ j, Number(obj.options.data[j][column]) ]; + } + } else if (obj.options.columns[column].type == 'calendar' || obj.options.columns[column].type == 'checkbox' || obj.options.columns[column].type == 'radio') { + for (var j = 0; j < obj.options.data.length; j++) { + temp[j] = [ j, obj.options.data[j][column] ]; + } + } else { + for (var j = 0; j < obj.options.data.length; j++) { + temp[j] = [ j, obj.records[j][column].innerText.toLowerCase() ]; + } + } + temp = temp.orderBy(1, order); + + // Save history + var newValue = []; + for (var j = 0; j < temp.length; j++) { + newValue[j] = temp[j][0]; + } + + // Save history + obj.setHistory({ + action: 'orderBy', + rows: newValue, + column: column, + order: order, + }); + + // Update order + obj.updateOrderArrow(column, order); + obj.updateOrder(newValue); + + // On sort event + obj.dispatch('onsort', el, column, order); + + return true; + } + } + + /** + * Update order arrow + */ + obj.updateOrderArrow = function(column, order) { + // Remove order + for (var i = 0; i < obj.headers.length; i++) { + obj.headers[i].classList.remove('arrow-up'); + obj.headers[i].classList.remove('arrow-down'); + } + + // No order specified then toggle order + if (order) { + obj.headers[column].classList.add('arrow-up'); + } else { + obj.headers[column].classList.add('arrow-down'); + } + } + + /** + * Update rows position + */ + obj.updateOrder = function(rows) { + // History + var data = [] + for (var j = 0; j < rows.length; j++) { + data[j] = obj.options.data[rows[j]]; + } + obj.options.data = data; + + var data = [] + for (var j = 0; j < rows.length; j++) { + data[j] = obj.records[rows[j]]; + } + obj.records = data; + + var data = [] + for (var j = 0; j < rows.length; j++) { + data[j] = obj.rows[rows[j]]; + } + obj.rows = data; + + // Update references + obj.updateTableReferences(); + + // Redo search + if (obj.results && obj.results.length) { + if (obj.searchInput.value) { + obj.search(obj.searchInput.value); + } else { + obj.closeFilter(); + } + } else { + // Create page + obj.results = null; + obj.pageNumber = 0; + + if (obj.options.pagination > 0) { + obj.page(0); + } else if (obj.options.lazyLoading == true) { + obj.loadPage(0); + } else { + for (var j = 0; j < obj.rows.length; j++) { + obj.tbody.appendChild(obj.rows[j]); + } + } + } + } + + /** + * Move row + * + * @return void + */ + obj.moveRow = function(o, d, ignoreDom) { + if (Object.keys(obj.options.mergeCells).length > 0) { + if (o > d) { + var insertBefore = 1; + } else { + var insertBefore = 0; + } + + if (obj.isRowMerged(o).length || obj.isRowMerged(d, insertBefore).length) { + if (! confirm(obj.options.text.thisActionWillDestroyAnyExistingMergedCellsAreYouSure)) { + return false; + } else { + obj.destroyMerged(); + } + } + } + + if (obj.options.search == true) { + if (obj.results && obj.results.length != obj.rows.length) { + if (confirm(obj.options.text.thisActionWillClearYourSearchResultsAreYouSure)) { + obj.resetSearch(); + } else { + return false; + } + } + + obj.results = null; + } + + if (! ignoreDom) { + if (Array.prototype.indexOf.call(obj.tbody.children, obj.rows[d]) >= 0) { + if (o > d) { + obj.tbody.insertBefore(obj.rows[o], obj.rows[d]); + } else { + obj.tbody.insertBefore(obj.rows[o], obj.rows[d].nextSibling); + } + } else { + obj.tbody.removeChild(obj.rows[o]); + } + } + + // Place references in the correct position + obj.rows.splice(d, 0, obj.rows.splice(o, 1)[0]); + obj.records.splice(d, 0, obj.records.splice(o, 1)[0]); + obj.options.data.splice(d, 0, obj.options.data.splice(o, 1)[0]); + + // Respect pagination + if (obj.options.pagination > 0 && obj.tbody.children.length != obj.options.pagination) { + obj.page(obj.pageNumber); + } + + // Keeping history of changes + obj.setHistory({ + action:'moveRow', + oldValue: o, + newValue: d, + }); + + // Update table references + obj.updateTableReferences(); + + // Events + obj.dispatch('onmoverow', el, o, d); + } + + /** + * Insert a new row + * + * @param mixed - number of blank lines to be insert or a single array with the data of the new row + * @param rowNumber + * @param insertBefore + * @return void + */ + obj.insertRow = function(mixed, rowNumber, insertBefore) { + // Configuration + if (obj.options.allowInsertRow == true) { + // Records + var records = []; + + // Data to be insert + var data = []; + + // The insert could be lead by number of rows or the array of data + if (mixed > 0) { + var numOfRows = mixed; + } else { + var numOfRows = 1; + + if (mixed) { + data = mixed; + } + } + + // Direction + var insertBefore = insertBefore ? true : false; + + // Current column number + var lastRow = obj.options.data.length - 1; + + if (rowNumber == undefined || rowNumber >= parseInt(lastRow) || rowNumber < 0) { + rowNumber = lastRow; + } + + // Onbeforeinsertrow + if (obj.dispatch('onbeforeinsertrow', el, rowNumber, numOfRows, insertBefore) === false) { + console.log('onbeforeinsertrow returned false'); + + return false; + } + + // Merged cells + if (Object.keys(obj.options.mergeCells).length > 0) { + if (obj.isRowMerged(rowNumber, insertBefore).length) { + if (! confirm(obj.options.text.thisActionWillDestroyAnyExistingMergedCellsAreYouSure)) { + return false; + } else { + obj.destroyMerged(); + } + } + } + + // Clear any search + if (obj.options.search == true) { + if (obj.results && obj.results.length != obj.rows.length) { + if (confirm(obj.options.text.thisActionWillClearYourSearchResultsAreYouSure)) { + obj.resetSearch(); + } else { + return false; + } + } + + obj.results = null; + } + + // Insertbefore + var rowIndex = (! insertBefore) ? rowNumber + 1 : rowNumber; + + // Keep the current data + var currentRecords = obj.records.splice(rowIndex); + var currentData = obj.options.data.splice(rowIndex); + var currentRows = obj.rows.splice(rowIndex); + + // Adding lines + var rowRecords = []; + var rowData = []; + var rowNode = []; + + for (var row = rowIndex; row < (numOfRows + rowIndex); row++) { + // Push data to the data container + obj.options.data[row] = []; + for (var col = 0; col < obj.options.columns.length; col++) { + obj.options.data[row][col] = data[col] ? data[col] : ''; + } + // Create row + var tr = obj.createRow(row, obj.options.data[row]); + // Append node + if (currentRows[0]) { + if (Array.prototype.indexOf.call(obj.tbody.children, currentRows[0]) >= 0) { + obj.tbody.insertBefore(tr, currentRows[0]); + } + } else { + if (Array.prototype.indexOf.call(obj.tbody.children, obj.rows[rowNumber]) >= 0) { + obj.tbody.appendChild(tr); + } + } + // Record History + rowRecords.push(obj.records[row]); + rowData.push(obj.options.data[row]); + rowNode.push(tr); + } + + // Copy the data back to the main data + Array.prototype.push.apply(obj.records, currentRecords); + Array.prototype.push.apply(obj.options.data, currentData); + Array.prototype.push.apply(obj.rows, currentRows); + + // Respect pagination + if (obj.options.pagination > 0) { + obj.page(obj.pageNumber); + } + + // Keep history + obj.setHistory({ + action: 'insertRow', + rowNumber: rowNumber, + numOfRows: numOfRows, + insertBefore: insertBefore, + rowRecords: rowRecords, + rowData: rowData, + rowNode: rowNode, + }); + + // Remove table references + obj.updateTableReferences(); + + // Events + obj.dispatch('oninsertrow', el, rowNumber, numOfRows, rowRecords, insertBefore); + } + } + + /** + * Delete a row by number + * + * @param integer rowNumber - row number to be excluded + * @param integer numOfRows - number of lines + * @return void + */ + obj.deleteRow = function(rowNumber, numOfRows) { + // Global Configuration + if (obj.options.allowDeleteRow == true) { + if (obj.options.allowDeletingAllRows == true || obj.options.data.length > 1) { + // Delete row definitions + if (rowNumber == undefined) { + var number = obj.getSelectedRows(); + + if (! number[0]) { + rowNumber = obj.options.data.length - 1; + numOfRows = 1; + } else { + rowNumber = parseInt(number[0].getAttribute('data-y')); + numOfRows = number.length; + } + } + + // Last column + var lastRow = obj.options.data.length - 1; + + if (rowNumber == undefined || rowNumber > lastRow || rowNumber < 0) { + rowNumber = lastRow; + } + + if (! numOfRows) { + numOfRows = 1; + } + + // Do not delete more than the number of recoreds + if (rowNumber + numOfRows >= obj.options.data.length) { + numOfRows = obj.options.data.length - rowNumber; + } + + // Onbeforedeleterow + if (obj.dispatch('onbeforedeleterow', el, rowNumber, numOfRows) === false) { + console.log('onbeforedeleterow returned false'); + return false; + } + + if (parseInt(rowNumber) > -1) { + // Merged cells + var mergeExists = false; + if (Object.keys(obj.options.mergeCells).length > 0) { + for (var row = rowNumber; row < rowNumber + numOfRows; row++) { + if (obj.isRowMerged(row, false).length) { + mergeExists = true; + } + } + } + if (mergeExists) { + if (! confirm(obj.options.text.thisActionWillDestroyAnyExistingMergedCellsAreYouSure)) { + return false; + } else { + obj.destroyMerged(); + } + } + + // Clear any search + if (obj.options.search == true) { + if (obj.results && obj.results.length != obj.rows.length) { + if (confirm(obj.options.text.thisActionWillClearYourSearchResultsAreYouSure)) { + obj.resetSearch(); + } else { + return false; + } + } + + obj.results = null; + } + + // Remove node + for (var row = rowNumber; row < rowNumber + numOfRows; row++) { + if (Array.prototype.indexOf.call(obj.tbody.children, obj.rows[row]) >= 0) { + obj.rows[row].className = ''; + obj.rows[row].parentNode.removeChild(obj.rows[row]); + } + } + + // Remove data + var rowRecords = obj.records.splice(rowNumber, numOfRows); + var rowData = obj.options.data.splice(rowNumber, numOfRows); + var rowNode = obj.rows.splice(rowNumber, numOfRows); + + // Respect pagination + if (obj.options.pagination > 0 && obj.tbody.children.length != obj.options.pagination) { + obj.page(obj.pageNumber); + } + + // Remove selection + obj.conditionalSelectionUpdate(1, rowNumber, (rowNumber + numOfRows) - 1); + + // Keep history + obj.setHistory({ + action: 'deleteRow', + rowNumber: rowNumber, + numOfRows: numOfRows, + insertBefore: 1, + rowRecords: rowRecords, + rowData: rowData, + rowNode: rowNode + }); + + // Remove table references + obj.updateTableReferences(); + + // Events + obj.dispatch('ondeleterow', el, rowNumber, numOfRows, rowRecords); + } + } else { + console.error('JEXCEL. It is not possible to delete the last row'); + } + } + } + + + /** + * Move column + * + * @return void + */ + obj.moveColumn = function(o, d) { + if (Object.keys(obj.options.mergeCells).length > 0) { + if (o > d) { + var insertBefore = 1; + } else { + var insertBefore = 0; + } + + if (obj.isColMerged(o).length || obj.isColMerged(d, insertBefore).length) { + if (! confirm(obj.options.text.thisActionWillDestroyAnyExistingMergedCellsAreYouSure)) { + return false; + } else { + obj.destroyMerged(); + } + } + } + + var o = parseInt(o); + var d = parseInt(d); + + if (o > d) { + obj.headerContainer.insertBefore(obj.headers[o], obj.headers[d]); + obj.colgroupContainer.insertBefore(obj.colgroup[o], obj.colgroup[d]); + + for (var j = 0; j < obj.rows.length; j++) { + obj.rows[j].insertBefore(obj.records[j][o], obj.records[j][d]); + } + } else { + obj.headerContainer.insertBefore(obj.headers[o], obj.headers[d].nextSibling); + obj.colgroupContainer.insertBefore(obj.colgroup[o], obj.colgroup[d].nextSibling); + + for (var j = 0; j < obj.rows.length; j++) { + obj.rows[j].insertBefore(obj.records[j][o], obj.records[j][d].nextSibling); + } + } + + obj.options.columns.splice(d, 0, obj.options.columns.splice(o, 1)[0]); + obj.headers.splice(d, 0, obj.headers.splice(o, 1)[0]); + obj.colgroup.splice(d, 0, obj.colgroup.splice(o, 1)[0]); + + for (var j = 0; j < obj.rows.length; j++) { + obj.options.data[j].splice(d, 0, obj.options.data[j].splice(o, 1)[0]); + obj.records[j].splice(d, 0, obj.records[j].splice(o, 1)[0]); + } + + // Update footers position + if (obj.options.footers) { + for (var j = 0; j < obj.options.footers.length; j++) { + obj.options.footers[j].splice(d, 0, obj.options.footers[j].splice(o, 1)[0]); + } + } + + // Keeping history of changes + obj.setHistory({ + action:'moveColumn', + oldValue: o, + newValue: d, + }); + + // Update table references + obj.updateTableReferences(); + + // Events + obj.dispatch('onmovecolumn', el, o, d); + } + + /** + * Insert a new column + * + * @param mixed - num of columns to be added or data to be added in one single column + * @param int columnNumber - number of columns to be created + * @param bool insertBefore + * @param object properties - column properties + * @return void + */ + obj.insertColumn = function(mixed, columnNumber, insertBefore, properties) { + // Configuration + if (obj.options.allowInsertColumn == true) { + // Records + var records = []; + + // Data to be insert + var data = []; + + // The insert could be lead by number of rows or the array of data + if (mixed > 0) { + var numOfColumns = mixed; + } else { + var numOfColumns = 1; + + if (mixed) { + data = mixed; + } + } + + // Direction + var insertBefore = insertBefore ? true : false; + + // Current column number + var lastColumn = obj.options.columns.length - 1; + + // Confirm position + if (columnNumber == undefined || columnNumber >= parseInt(lastColumn) || columnNumber < 0) { + columnNumber = lastColumn; + } + + // Onbeforeinsertcolumn + if (obj.dispatch('onbeforeinsertcolumn', el, columnNumber, numOfColumns, insertBefore) === false) { + console.log('onbeforeinsertcolumn returned false'); + + return false; + } + + // Merged cells + if (Object.keys(obj.options.mergeCells).length > 0) { + if (obj.isColMerged(columnNumber, insertBefore).length) { + if (! confirm(obj.options.text.thisActionWillDestroyAnyExistingMergedCellsAreYouSure)) { + return false; + } else { + obj.destroyMerged(); + } + } + } + + // Create default properties + if (! properties) { + properties = []; + } + + for (var i = 0; i < numOfColumns; i++) { + if (! properties[i]) { + properties[i] = { type:'text', source:[], options:[], width:obj.options.defaultColWidth, align:obj.options.defaultColAlign }; + } + } + + // Insert before + var columnIndex = (! insertBefore) ? columnNumber + 1 : columnNumber; + obj.options.columns = jexcel.injectArray(obj.options.columns, columnIndex, properties); + + // Open space in the containers + var currentHeaders = obj.headers.splice(columnIndex); + var currentColgroup = obj.colgroup.splice(columnIndex); + + // History + var historyHeaders = []; + var historyColgroup = []; + var historyRecords = []; + var historyData = []; + var historyFooters = []; + + // Add new headers + for (var col = columnIndex; col < (numOfColumns + columnIndex); col++) { + obj.createCellHeader(col); + obj.headerContainer.insertBefore(obj.headers[col], obj.headerContainer.children[col+1]); + obj.colgroupContainer.insertBefore(obj.colgroup[col], obj.colgroupContainer.children[col+1]); + + historyHeaders.push(obj.headers[col]); + historyColgroup.push(obj.colgroup[col]); + } + + // Add new footer cells + if (obj.options.footers) { + for (var j = 0; j < obj.options.footers.length; j++) { + historyFooters[j] = []; + for (var i = 0; i < numOfColumns; i++) { + historyFooters[j].push(''); + } + obj.options.footers[j].splice(columnIndex, 0, historyFooters[j]); + } + } + + // Adding visual columns + for (var row = 0; row < obj.options.data.length; row++) { + // Keep the current data + var currentData = obj.options.data[row].splice(columnIndex); + var currentRecord = obj.records[row].splice(columnIndex); + + // History + historyData[row] = []; + historyRecords[row] = []; + + for (var col = columnIndex; col < (numOfColumns + columnIndex); col++) { + // New value + var value = data[row] ? data[row] : ''; + obj.options.data[row][col] = value; + // New cell + var td = obj.createCell(col, row, obj.options.data[row][col]); + obj.records[row][col] = td; + // Add cell to the row + if (obj.rows[row]) { + obj.rows[row].insertBefore(td, obj.rows[row].children[col+1]); + } + + // Record History + historyData[row].push(value); + historyRecords[row].push(td); + } + + // Copy the data back to the main data + Array.prototype.push.apply(obj.options.data[row], currentData); + Array.prototype.push.apply(obj.records[row], currentRecord); + } + + Array.prototype.push.apply(obj.headers, currentHeaders); + Array.prototype.push.apply(obj.colgroup, currentColgroup); + + // Adjust nested headers + if (obj.options.nestedHeaders && obj.options.nestedHeaders.length > 0) { + // Flexible way to handle nestedheaders + if (obj.options.nestedHeaders[0] && obj.options.nestedHeaders[0][0]) { + for (var j = 0; j < obj.options.nestedHeaders.length; j++) { + var colspan = parseInt(obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan) + numOfColumns; + obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan = colspan; + obj.thead.children[j].children[obj.thead.children[j].children.length-1].setAttribute('colspan', colspan); + } + } else { + var colspan = parseInt(obj.options.nestedHeaders[0].colspan) + numOfColumns; + obj.options.nestedHeaders[0].colspan = colspan; + obj.thead.children[0].children[obj.thead.children[0].children.length-1].setAttribute('colspan', colspan); + } + } + + // Keep history + obj.setHistory({ + action: 'insertColumn', + columnNumber:columnNumber, + numOfColumns:numOfColumns, + insertBefore:insertBefore, + columns:properties, + headers:historyHeaders, + colgroup:historyColgroup, + records:historyRecords, + footers:historyFooters, + data:historyData, + }); + + // Remove table references + obj.updateTableReferences(); + + // Events + obj.dispatch('oninsertcolumn', el, columnNumber, numOfColumns, historyRecords, insertBefore); + + obj.el.querySelector("table.jexcel tr").childNodes.forEach(function (td) { + td.style.textAlign = "center"; + }); + } + } + + /** + * Delete a column by number + * + * @param integer columnNumber - reference column to be excluded + * @param integer numOfColumns - number of columns to be excluded from the reference column + * @return void + */ + obj.deleteColumn = function(columnNumber, numOfColumns) { + // Global Configuration + if (obj.options.allowDeleteColumn == true) { + if (obj.headers.length > 1) { + // Delete column definitions + if (columnNumber == undefined) { + var number = obj.getSelectedColumns(true); + + if (! number.length) { + // Remove last column + columnNumber = obj.headers.length - 1; + numOfColumns = 1; + } else { + // Remove selected + columnNumber = parseInt(number[0]); + numOfColumns = parseInt(number.length); + } + } + + // Lasat column + var lastColumn = obj.options.data[0].length - 1; + + if (columnNumber == undefined || columnNumber > lastColumn || columnNumber < 0) { + columnNumber = lastColumn; + } + + // Minimum of columns to be delete is 1 + if (! numOfColumns) { + numOfColumns = 1; + } + + // Can't delete more than the limit of the table + if (numOfColumns > obj.options.data[0].length - columnNumber) { + numOfColumns = obj.options.data[0].length - columnNumber; + } + + // onbeforedeletecolumn + if (obj.dispatch('onbeforedeletecolumn', el, columnNumber, numOfColumns) === false) { + console.log('onbeforedeletecolumn returned false'); + return false; + } + + // Can't remove the last column + if (parseInt(columnNumber) > -1) { + // Merged cells + var mergeExists = false; + if (Object.keys(obj.options.mergeCells).length > 0) { + for (var col = columnNumber; col < columnNumber + numOfColumns; col++) { + if (obj.isColMerged(col, false).length) { + mergeExists = true; + } + } + } + if (mergeExists) { + if (! confirm(obj.options.text.thisActionWillDestroyAnyExistingMergedCellsAreYouSure)) { + return false; + } else { + obj.destroyMerged(); + } + } + + // Delete the column properties + var columns = obj.options.columns.splice(columnNumber, numOfColumns); + + for (var col = columnNumber; col < columnNumber + numOfColumns; col++) { + obj.colgroup[col].className = ''; + obj.headers[col].className = ''; + obj.colgroup[col].parentNode.removeChild(obj.colgroup[col]); + obj.headers[col].parentNode.removeChild(obj.headers[col]); + } + + var historyHeaders = obj.headers.splice(columnNumber, numOfColumns); + var historyColgroup = obj.colgroup.splice(columnNumber, numOfColumns); + var historyRecords = []; + var historyData = []; + var historyFooters = []; + + for (var row = 0; row < obj.options.data.length; row++) { + for (var col = columnNumber; col < columnNumber + numOfColumns; col++) { + obj.records[row][col].className = ''; + obj.records[row][col].parentNode.removeChild(obj.records[row][col]); + } + } + + // Delete headers + for (var row = 0; row < obj.options.data.length; row++) { + // History + historyData[row] = obj.options.data[row].splice(columnNumber, numOfColumns); + historyRecords[row] = obj.records[row].splice(columnNumber, numOfColumns); + } + + // Delete footers + if (obj.options.footers) { + for (var row = 0; row < obj.options.footers.length; row++) { + historyFooters[row] = obj.options.footers[row].splice(columnNumber, numOfColumns); + } + } + + // Remove selection + obj.conditionalSelectionUpdate(0, columnNumber, (columnNumber + numOfColumns) - 1); + + // Adjust nested headers + if (obj.options.nestedHeaders && obj.options.nestedHeaders.length > 0) { + // Flexible way to handle nestedheaders + if (obj.options.nestedHeaders[0] && obj.options.nestedHeaders[0][0]) { + for (var j = 0; j < obj.options.nestedHeaders.length; j++) { + var colspan = parseInt(obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan) - numOfColumns; + obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan = colspan; + obj.thead.children[j].children[obj.thead.children[j].children.length-1].setAttribute('colspan', colspan); + } + } else { + var colspan = parseInt(obj.options.nestedHeaders[0].colspan) - numOfColumns; + obj.options.nestedHeaders[0].colspan = colspan; + obj.thead.children[0].children[obj.thead.children[0].children.length-1].setAttribute('colspan', colspan); + } + } + + // Keeping history of changes + obj.setHistory({ + action:'deleteColumn', + columnNumber:columnNumber, + numOfColumns:numOfColumns, + insertBefore: 1, + columns:columns, + headers:historyHeaders, + colgroup:historyColgroup, + records:historyRecords, + footers:historyFooters, + data:historyData, + }); + + // Update table references + obj.updateTableReferences(); + + // Delete + obj.dispatch('ondeletecolumn', el, columnNumber, numOfColumns, historyRecords); + } + } else { + console.error('JEXCEL. It is not possible to delete the last column'); + } + } + } + + /** + * Get seleted rows numbers + * + * @return array + */ + obj.getSelectedRows = function(asIds) { + var rows = []; + // Get all selected rows + for (var j = 0; j < obj.rows.length; j++) { + if (obj.rows[j].classList.contains('selected')) { + if (asIds) { + rows.push(j); + } else { + rows.push(obj.rows[j]); + } + } + } + + return rows; + }, + + /** + * Get seleted column numbers + * + * @return array + */ + obj.getSelectedColumns = function() { + var cols = []; + // Get all selected cols + for (var i = 0; i < obj.headers.length; i++) { + if (obj.headers[i].classList.contains('selected')) { + cols.push(i); + } + } + + return cols; + } + + /** + * Get highlighted + * + * @return array + */ + obj.getHighlighted = function() { + return obj.highlighted; + } + + /** + * Update cell references + * + * @return void + */ + obj.updateTableReferences = function() { + // Update headers + for (var i = 0; i < obj.headers.length; i++) { + var x = obj.headers[i].getAttribute('data-x'); + + if (x != i) { + // Update coords + obj.headers[i].setAttribute('data-x', i); + // Title + if (! obj.headers[i].getAttribute('title')) { + obj.headers[i].innerHTML = jexcel.getColumnName(i); + } + } + } + + // Update all rows + for (var j = 0; j < obj.rows.length; j++) { + if (obj.rows[j]) { + var y = obj.rows[j].getAttribute('data-y'); + + if (y != j) { + // Update coords + obj.rows[j].setAttribute('data-y', j); + obj.rows[j].children[0].setAttribute('data-y', j); + // Row number + obj.rows[j].children[0].innerHTML = j + 1; + } + } + } + + // Regular cells affected by this change + var affectedTokens = []; + var mergeCellUpdates = []; + + // Update cell + var updatePosition = function(x,y,i,j) { + if (x != i) { + obj.records[j][i].setAttribute('data-x', i); + } + if (y != j) { + obj.records[j][i].setAttribute('data-y', j); + } + + // Other updates + if (x != i || y != j) { + var columnIdFrom = jexcel.getColumnNameFromId([x, y]); + var columnIdTo = jexcel.getColumnNameFromId([i, j]); + affectedTokens[columnIdFrom] = columnIdTo; + } + } + + for (var j = 0; j < obj.records.length; j++) { + for (var i = 0; i < obj.records[0].length; i++) { + if (obj.records[j][i]) { + // Current values + var x = obj.records[j][i].getAttribute('data-x'); + var y = obj.records[j][i].getAttribute('data-y'); + + // Update column + if (obj.records[j][i].getAttribute('data-merged')) { + var columnIdFrom = jexcel.getColumnNameFromId([x, y]); + var columnIdTo = jexcel.getColumnNameFromId([i, j]); + if (mergeCellUpdates[columnIdFrom] == null) { + if (columnIdFrom == columnIdTo) { + mergeCellUpdates[columnIdFrom] = false; + } else { + var totalX = parseInt(i - x); + var totalY = parseInt(j - y); + mergeCellUpdates[columnIdFrom] = [ columnIdTo, totalX, totalY ]; + } + } + } else { + updatePosition(x,y,i,j); + } + } + } + } + + // Update merged if applicable + var keys = Object.keys(mergeCellUpdates); + if (keys.length) { + for (var i = 0; i < keys.length; i++) { + if (mergeCellUpdates[keys[i]]) { + var info = jexcel.getIdFromColumnName(keys[i], true) + var x = info[0]; + var y = info[1]; + updatePosition(x,y,x + mergeCellUpdates[keys[i]][1],y + mergeCellUpdates[keys[i]][2]); + + var columnIdFrom = keys[i]; + var columnIdTo = mergeCellUpdates[keys[i]][0]; + for (var j = 0; j < obj.options.mergeCells[columnIdFrom][2].length; j++) { + var x = parseInt(obj.options.mergeCells[columnIdFrom][2][j].getAttribute('data-x')); + var y = parseInt(obj.options.mergeCells[columnIdFrom][2][j].getAttribute('data-y')); + obj.options.mergeCells[columnIdFrom][2][j].setAttribute('data-x', x + mergeCellUpdates[keys[i]][1]); + obj.options.mergeCells[columnIdFrom][2][j].setAttribute('data-y', y + mergeCellUpdates[keys[i]][2]); + } + + obj.options.mergeCells[columnIdTo] = obj.options.mergeCells[columnIdFrom]; + delete(obj.options.mergeCells[columnIdFrom]); + } + } + } + + // Update formulas + obj.updateFormulas(affectedTokens); + + // Update meta data + obj.updateMeta(affectedTokens); + + // Refresh selection + obj.refreshSelection(); + + // Update table with custom configuration if applicable + obj.updateTable(); + } + + /** + * Custom settings for the cells + */ + obj.updateTable = function() { + // Check for spare + if (obj.options.minSpareRows > 0) { + var numBlankRows = 0; + for (var j = obj.rows.length - 1; j >= 0; j--) { + var test = false; + for (var i = 0; i < obj.headers.length; i++) { + if (obj.options.data[j][i]) { + test = true; + } + } + if (test) { + break; + } else { + numBlankRows++; + } + } + + if (obj.options.minSpareRows - numBlankRows > 0) { + obj.insertRow(obj.options.minSpareRows - numBlankRows) + } + } + + if (obj.options.minSpareCols > 0) { + var numBlankCols = 0; + for (var i = obj.headers.length - 1; i >= 0 ; i--) { + var test = false; + for (var j = 0; j < obj.rows.length; j++) { + if (obj.options.data[j][i]) { + test = true; + } + } + if (test) { + break; + } else { + numBlankCols++; + } + } + + if (obj.options.minSpareCols - numBlankCols > 0) { + obj.insertColumn(obj.options.minSpareCols - numBlankCols) + } + } + + // Customizations by the developer + if (typeof(obj.options.updateTable) == 'function') { + if (obj.options.detachForUpdates) { + el.removeChild(obj.content); + } + + for (var j = 0; j < obj.rows.length; j++) { + for (var i = 0; i < obj.headers.length; i++) { + obj.options.updateTable(el, obj.records[j][i], i, j, obj.options.data[j][i], obj.records[j][i].innerText, jexcel.getColumnNameFromId([i, j])); + } + } + + if (obj.options.detachForUpdates) { + el.insertBefore(obj.content, obj.pagination); + } + } + + // Update footers + if (obj.options.footers) { + obj.setFooter(); + } + + // Update corner position + setTimeout(function() { + obj.updateCornerPosition(); + },0); + } + + /** + * Show row + */ + obj.showRow = function(rowNumber) { + obj.rows[rowNumber].style.display = ''; + } + + /** + * Hide row + */ + obj.hideRow = function(rowNumber) { + obj.rows[rowNumber].style.display = 'none'; + } + + /** + * Show column + */ + obj.showColumn = function(colNumber) { + obj.headers[colNumber].style.display = ''; + obj.colgroup[colNumber].style.display = ''; + for (var j = 0; j < obj.options.data.length; j++) { + obj.records[j][colNumber].style.display = ''; + } + } + + /** + * Hide column + */ + obj.hideColumn = function(colNumber) { + obj.headers[colNumber].style.display = 'none'; + obj.colgroup[colNumber].style.display = 'none'; + for (var j = 0; j < obj.options.data.length; j++) { + obj.records[j][colNumber].style.display = 'none'; + } + } + + /** + * Show index column + */ + obj.showIndex = function() { + obj.table.classList.remove('jexcel_hidden_index'); + } + + /** + * Hide index column + */ + obj.hideIndex = function() { + obj.table.classList.add('jexcel_hidden_index'); + } + + /** + * Update all related cells in the chain + */ + var chainLoopProtection = []; + + obj.updateFormulaChain = function(x, y, records) { + var cellId = jexcel.getColumnNameFromId([x, y]); + if (obj.formula[cellId] && obj.formula[cellId].length > 0) { + if (chainLoopProtection[cellId]) { + obj.records[y][x].innerHTML = '#ERROR'; + obj.formula[cellId] = ''; + } else { + // Protection + chainLoopProtection[cellId] = true; + + for (var i = 0; i < obj.formula[cellId].length; i++) { + var cell = jexcel.getIdFromColumnName(obj.formula[cellId][i], true); + // Update cell + var value = ''+obj.options.data[cell[1]][cell[0]]; + if (value.substr(0,1) == '=') { + records.push(obj.updateCell(cell[0], cell[1], value, true)); + } else { + // No longer a formula, remove from the chain + Object.keys(obj.formula)[i] = null; + } + obj.updateFormulaChain(cell[0], cell[1], records); + } + } + } + + chainLoopProtection = []; + } + + /** + * Update formulas + */ + obj.updateFormulas = function(referencesToUpdate) { + // Update formulas + for (var j = 0; j < obj.options.data.length; j++) { + for (var i = 0; i < obj.options.data[0].length; i++) { + var value = '' + obj.options.data[j][i]; + // Is formula + if (value.substr(0,1) == '=') { + // Replace tokens + var newFormula = obj.updateFormula(value, referencesToUpdate); + if (newFormula != value) { + obj.options.data[j][i] = newFormula; + } + } + } + } + + // Update formula chain + var formula = []; + var keys = Object.keys(obj.formula); + for (var j = 0; j < keys.length; j++) { + // Current key and values + var key = keys[j]; + var value = obj.formula[key]; + // Update key + if (referencesToUpdate[key]) { + key = referencesToUpdate[key]; + } + // Update values + formula[key] = []; + for (var i = 0; i < value.length; i++) { + var letter = value[i]; + if (referencesToUpdate[letter]) { + letter = referencesToUpdate[letter]; + } + formula[key].push(letter); + } + } + obj.formula = formula; + } + + /** + * Update formula + */ + obj.updateFormula = function(formula, referencesToUpdate) { + var testLetter = /[A-Z]/; + var testNumber = /[0-9]/; + + var newFormula = ''; + var letter = null; + var number = null; + var token = ''; + + for (var index = 0; index < formula.length; index++) { + if (testLetter.exec(formula[index])) { + letter = 1; + number = 0; + token += formula[index]; + } else if (testNumber.exec(formula[index])) { + number = letter ? 1 : 0; + token += formula[index]; + } else { + if (letter && number) { + token = referencesToUpdate[token] ? referencesToUpdate[token] : token; + } + newFormula += token; + newFormula += formula[index]; + letter = 0; + number = 0; + token = ''; + } + } + + if (token) { + if (letter && number) { + token = referencesToUpdate[token] ? referencesToUpdate[token] : token; + } + newFormula += token; + } + + return newFormula; + } + + /** + * Secure formula + */ + var secureFormula = function(oldValue) { + var newValue = ''; + var inside = 0; + + for (var i = 0; i < oldValue.length; i++) { + if (oldValue[i] == '"') { + if (inside == 0) { + inside = 1; + } else { + inside = 0; + } + } + + if (inside == 1) { + newValue += oldValue[i]; + } else { + newValue += oldValue[i].toUpperCase(); + } + } + + return newValue; + } + + /** + * Parse formulas + */ + obj.executeFormula = function(expression, x, y) { + + var formulaResults = []; + var formulaLoopProtection = []; + + // Execute formula with loop protection + var execute = function(expression, x, y) { + // Parent column identification + var parentId = jexcel.getColumnNameFromId([x, y]); + + // Code protection + if (formulaLoopProtection[parentId]) { + console.error('Reference loop detected'); + return '#ERROR'; + } + + formulaLoopProtection[parentId] = true; + + // Convert range tokens + var tokensUpdate = function(tokens) { + for (var index = 0; index < tokens.length; index++) { + var f = []; + var token = tokens[index].split(':'); + var e1 = jexcel.getIdFromColumnName(token[0], true); + var e2 = jexcel.getIdFromColumnName(token[1], true); + + if (e1[0] <= e2[0]) { + var x1 = e1[0]; + var x2 = e2[0]; + } else { + var x1 = e2[0]; + var x2 = e1[0]; + } + + if (e1[1] <= e2[1]) { + var y1 = e1[1]; + var y2 = e2[1]; + } else { + var y1 = e2[1]; + var y2 = e1[1]; + } + + for (var j = y1; j <= y2; j++) { + for (var i = x1; i <= x2; i++) { + f.push(jexcel.getColumnNameFromId([i, j])); + } + } + + expression = expression.replace(tokens[index], f.join(',')); + } + } + + var tokens = expression.match(/([A-Z]+[0-9]+)\:([A-Z]+[0-9]+)/g); + if (tokens && tokens.length) { + tokensUpdate(tokens); + } + + // String + var evalstring = ''; + + // Get tokens + var tokens = expression.match(/([A-Z]+[0-9]+)/g); + + // Direct self-reference protection + if (tokens && tokens.indexOf(parentId) > -1) { + console.error('Self Reference detected'); + return '#ERROR'; + } else { + if (tokens) { + for (var i = 0; i < tokens.length; i++) { + // Keep chain + if (! obj.formula[tokens[i]]) { + obj.formula[tokens[i]] = []; + } + // Is already in the register + if (obj.formula[tokens[i]].indexOf(parentId) < 0) { + obj.formula[tokens[i]].push(parentId); + } + + // Do not calculate again + if (eval('typeof(' + tokens[i] + ') == "undefined"')) { + // Coords + var position = jexcel.getIdFromColumnName(tokens[i], 1); + // Get value + if (typeof(obj.options.data[position[1]]) != 'undefined' && typeof(obj.options.data[position[1]][position[0]]) != 'undefined') { + var value = obj.options.data[position[1]][position[0]]; + } else { + var value = ''; + } + // Get column data + if ((''+value).substr(0,1) == '=') { + if (formulaResults[tokens[i]]) { + value = formulaResults[tokens[i]]; + } else { + value = execute(value, position[0], position[1]); + formulaResults[tokens[i]] = value; + } + } + // Type! + if ((''+value).trim() == '') { + // Null + evalstring += "var " + tokens[i] + " = null;"; + } else { + if (value == Number(value) && obj.options.autoCasting == true) { + // Number + evalstring += "var " + tokens[i] + " = " + Number(value) + ";"; + } else { + // Trying any formatted number + var number = obj.parseNumber(value, position[0]) + if (obj.options.autoCasting == true && number) { + // Render as number + evalstring += "var " + tokens[i] + " = " + number + ";"; + } else { + // Render as string + evalstring += "var " + tokens[i] + " = '" + value + "';"; + } + } + } + } + } + } + + // Convert formula to javascript + try { + evalstring += "function COLUMN() { return parseInt(x) + 1; }; function ROW() { return parseInt(y) + 1; }; function CELL() { return parentId; }; function TABLE() { return obj; }; function VALUE(col, row) { return obj.records[row-1][col-1].innerHTML; }; function THISROWCELL(col) { var id = jexcel.getIdFromColumnName(col+(parseInt(y)+1), true); return obj.records[id[1]][id[0]].innerHTML; }"; + + var res = eval(evalstring + expression.substr(1)); + } catch (e) { + var res = '#ERROR'; + } + + return res; + } + } + + return execute(expression, x, y); + } + + /** + * Trying to extract a number from a string + */ + obj.parseNumber = function(value, columnNumber) { + // Decimal point + var decimal = columnNumber && obj.options.columns[columnNumber].decimal ? obj.options.columns[columnNumber].decimal : '.'; + + // Parse both parts of the number + var number = ('' + value); + number = number.split(decimal); + number[0] = number[0].match(/[+-]?[0-9]/g); + if (number[0]) { + number[0] = number[0].join(''); + } + if (number[1]) { + number[1] = number[1].match(/[0-9]*/g).join(''); + } + + // Is a valid number + if (number[0] && Number(number[0]) >= 0) { + if (! number[1]) { + var value = Number(number[0] + '.00'); + } else { + var value = Number(number[0] + '.' + number[1]); + } + } else { + var value = null; + } + + return value; + } + + /** + * Get row number + */ + obj.row = function(cell) { + } + + /** + * Get col number + */ + obj.col = function(cell) { + } + + obj.up = function(shiftKey, ctrlKey) { + if (shiftKey) { + if (obj.selectedCell[3] > 0) { + obj.up.visible(1, ctrlKey ? 0 : 1) + } + } else { + if (obj.selectedCell[1] > 0) { + obj.up.visible(0, ctrlKey ? 0 : 1) + } + obj.selectedCell[2] = obj.selectedCell[0]; + obj.selectedCell[3] = obj.selectedCell[1]; + } + + // Update selection + obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); + + // Change page + if (obj.options.lazyLoading == true) { + if (obj.selectedCell[1] == 0 || obj.selectedCell[3] == 0) { + obj.loadPage(0); + obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); + } else { + if (obj.loadValidation()) { + obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); + } else { + var item = parseInt(obj.tbody.firstChild.getAttribute('data-y')); + if (obj.selectedCell[1] - item < 30) { + obj.loadUp(); + obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); + } + } + } + } else if (obj.options.pagination > 0) { + var pageNumber = obj.whichPage(obj.selectedCell[3]); + if (pageNumber != obj.pageNumber) { + obj.page(pageNumber); + } + } + + obj.updateScroll(1); + } + + obj.up.visible = function(group, direction) { + if (group == 0) { + var x = parseInt(obj.selectedCell[0]); + var y = parseInt(obj.selectedCell[1]); + } else { + var x = parseInt(obj.selectedCell[2]); + var y = parseInt(obj.selectedCell[3]); + } + + if (direction == 0) { + for (var j = 0; j < y; j++) { + if (obj.records[j][x].style.display != 'none' && obj.rows[j].style.display != 'none') { + y = j; + break; + } + } + } else { + y = obj.up.get(x, y); + } + + if (group == 0) { + obj.selectedCell[0] = x; + obj.selectedCell[1] = y; + } else { + obj.selectedCell[2] = x; + obj.selectedCell[3] = y; + } + } + + obj.up.get = function(x, y) { + var x = parseInt(x); + var y = parseInt(y); + for (var j = (y - 1); j >= 0; j--) { + if (obj.records[j][x].style.display != 'none' && obj.rows[j].style.display != 'none') { + if (obj.records[j][x].getAttribute('data-merged')) { + if (obj.records[j][x] == obj.records[y][x]) { + continue; + } + } + y = j; + break; + } + } + + return y; + } + + obj.down = function(shiftKey, ctrlKey) { + if (shiftKey) { + if (obj.selectedCell[3] < obj.records.length - 1) { + obj.down.visible(1, ctrlKey ? 0 : 1) + } + } else { + if (obj.selectedCell[1] < obj.records.length - 1) { + obj.down.visible(0, ctrlKey ? 0 : 1) + } + obj.selectedCell[2] = obj.selectedCell[0]; + obj.selectedCell[3] = obj.selectedCell[1]; + } + + obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); + + // Change page + if (obj.options.lazyLoading == true) { + if ((obj.selectedCell[1] == obj.records.length - 1 || obj.selectedCell[3] == obj.records.length - 1)) { + obj.loadPage(-1); + obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); + } else { + if (obj.loadValidation()) { + obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); + } else { + var item = parseInt(obj.tbody.lastChild.getAttribute('data-y')); + if (item - obj.selectedCell[3] < 30) { + obj.loadDown(); + obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); + } + } + } + } else if (obj.options.pagination > 0) { + var pageNumber = obj.whichPage(obj.selectedCell[3]); + if (pageNumber != obj.pageNumber) { + obj.page(pageNumber); + } + } + + obj.updateScroll(3); + } + + obj.down.visible = function(group, direction) { + if (group == 0) { + var x = parseInt(obj.selectedCell[0]); + var y = parseInt(obj.selectedCell[1]); + } else { + var x = parseInt(obj.selectedCell[2]); + var y = parseInt(obj.selectedCell[3]); + } + + if (direction == 0) { + for (var j = obj.rows.length - 1; j > y; j--) { + if (obj.records[j][x].style.display != 'none' && obj.rows[j].style.display != 'none') { + y = j; + break; + } + } + } else { + y = obj.down.get(x, y); + } + + if (group == 0) { + obj.selectedCell[0] = x; + obj.selectedCell[1] = y; + } else { + obj.selectedCell[2] = x; + obj.selectedCell[3] = y; + } + } + + obj.down.get = function(x, y) { + var x = parseInt(x); + var y = parseInt(y); + for (var j = (y + 1); j < obj.rows.length; j++) { + if (obj.records[j][x].style.display != 'none' && obj.rows[j].style.display != 'none') { + if (obj.records[j][x].getAttribute('data-merged')) { + if (obj.records[j][x] == obj.records[y][x]) { + continue; + } + } + y = j; + break; + } + } + + return y; + } + + obj.right = function(shiftKey, ctrlKey) { + if (shiftKey) { + if (obj.selectedCell[2] < obj.headers.length - 1) { + obj.right.visible(1, ctrlKey ? 0 : 1) + } + } else { + if (obj.selectedCell[0] < obj.headers.length - 1) { + obj.right.visible(0, ctrlKey ? 0 : 1) + } + obj.selectedCell[2] = obj.selectedCell[0]; + obj.selectedCell[3] = obj.selectedCell[1]; + } + + obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); + obj.updateScroll(2); + } + + obj.right.visible = function(group, direction) { + if (group == 0) { + var x = parseInt(obj.selectedCell[0]); + var y = parseInt(obj.selectedCell[1]); + } else { + var x = parseInt(obj.selectedCell[2]); + var y = parseInt(obj.selectedCell[3]); + } + + if (direction == 0) { + for (var i = obj.headers.length - 1; i > x; i--) { + if (obj.records[y][i].style.display != 'none') { + x = i; + break; + } + } + } else { + x = obj.right.get(x, y); + } + + if (group == 0) { + obj.selectedCell[0] = x; + obj.selectedCell[1] = y; + } else { + obj.selectedCell[2] = x; + obj.selectedCell[3] = y; + } + } + + obj.right.get = function(x, y) { + var x = parseInt(x); + var y = parseInt(y); + + for (var i = (x + 1); i < obj.headers.length; i++) { + if (obj.records[y][i].style.display != 'none') { + if (obj.records[y][i].getAttribute('data-merged')) { + if (obj.records[y][i] == obj.records[y][x]) { + continue; + } + } + x = i; + break; + } + } + + return x; + } + + obj.left = function(shiftKey, ctrlKey) { + if (shiftKey) { + if (obj.selectedCell[2] > 0) { + obj.left.visible(1, ctrlKey ? 0 : 1) + } + } else { + if (obj.selectedCell[0] > 0) { + obj.left.visible(0, ctrlKey ? 0 : 1) + } + obj.selectedCell[2] = obj.selectedCell[0]; + obj.selectedCell[3] = obj.selectedCell[1]; + } + + obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); + obj.updateScroll(0); + } + + obj.left.visible = function(group, direction) { + if (group == 0) { + var x = parseInt(obj.selectedCell[0]); + var y = parseInt(obj.selectedCell[1]); + } else { + var x = parseInt(obj.selectedCell[2]); + var y = parseInt(obj.selectedCell[3]); + } + + if (direction == 0) { + for (var i = 0; i < x; i++) { + if (obj.records[y][i].style.display != 'none') { + x = i; + break; + } + } + } else { + x = obj.left.get(x, y); + } + + if (group == 0) { + obj.selectedCell[0] = x; + obj.selectedCell[1] = y; + } else { + obj.selectedCell[2] = x; + obj.selectedCell[3] = y; + } + } + + obj.left.get = function(x, y) { + var x = parseInt(x); + var y = parseInt(y); + for (var i = (x - 1); i >= 0; i--) { + if (obj.records[y][i].style.display != 'none') { + if (obj.records[y][i].getAttribute('data-merged')) { + if (obj.records[y][i] == obj.records[y][x]) { + continue; + } + } + x = i; + break; + } + } + + return x; + } + + obj.first = function(shiftKey, ctrlKey) { + if (shiftKey) { + if (ctrlKey) { + obj.selectedCell[3] = 0; + } else { + obj.left.visible(1, 0); + } + } else { + if (ctrlKey) { + obj.selectedCell[1] = 0; + } else { + obj.left.visible(0, 0); + } + obj.selectedCell[2] = obj.selectedCell[0]; + obj.selectedCell[3] = obj.selectedCell[1]; + } + + // Change page + if (obj.options.lazyLoading == true && (obj.selectedCell[1] == 0 || obj.selectedCell[3] == 0)) { + obj.loadPage(0); + } else if (obj.options.pagination > 0) { + var pageNumber = obj.whichPage(obj.selectedCell[3]); + if (pageNumber != obj.pageNumber) { + obj.page(pageNumber); + } + } + + obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); + obj.updateScroll(1); + } + + obj.last = function(shiftKey, ctrlKey) { + if (shiftKey) { + if (ctrlKey) { + obj.selectedCell[3] = obj.records.length - 1; + } else { + obj.right.visible(1, 0); + } + } else { + if (ctrlKey) { + obj.selectedCell[1] = obj.records.length - 1; + } else { + obj.right.visible(0, 0); + } + obj.selectedCell[2] = obj.selectedCell[0]; + obj.selectedCell[3] = obj.selectedCell[1]; + } + + // Change page + if (obj.options.lazyLoading == true && (obj.selectedCell[1] == obj.records.length - 1 || obj.selectedCell[3] == obj.records.length - 1)) { + obj.loadPage(-1); + } else if (obj.options.pagination > 0) { + var pageNumber = obj.whichPage(obj.selectedCell[3]); + if (pageNumber != obj.pageNumber) { + obj.page(pageNumber); + } + } + + obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); + obj.updateScroll(3); + } + + obj.selectAll = function() { + if (! obj.selectedCell) { + obj.selectedCell = []; + } + + obj.selectedCell[0] = 0; + obj.selectedCell[1] = 0; + obj.selectedCell[2] = obj.headers.length - 1; + obj.selectedCell[3] = obj.records.length - 1; + + obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]); + } + + /** + * Go to a page in a lazyLoading + */ + obj.loadPage = function(pageNumber) { + // Search + if (obj.options.search == true && obj.results) { + var results = obj.results; + } else { + var results = obj.rows; + } + + // Per page + var quantityPerPage = 100; + + // pageNumber + if (pageNumber == null || pageNumber == -1) { + // Last page + pageNumber = Math.ceil(results.length / quantityPerPage); + } + + var startRow = (pageNumber * quantityPerPage); + var finalRow = (pageNumber * quantityPerPage) + quantityPerPage; + if (finalRow > results.length) { + finalRow = results.length; + } + startRow = finalRow - 100; + if (startRow < 0) { + startRow = 0; + } + + // Appeding items + for (var j = startRow; j < finalRow; j++) { + if (obj.options.search == true && obj.results) { + obj.tbody.appendChild(obj.rows[results[j]]); + } else { + obj.tbody.appendChild(obj.rows[j]); + } + + if (obj.tbody.children.length > quantityPerPage) { + obj.tbody.removeChild(obj.tbody.firstChild); + } + } + } + + obj.loadUp = function() { + // Search + if (obj.options.search == true && obj.results) { + var results = obj.results; + } else { + var results = obj.rows; + } + var test = 0; + if (results.length > 100) { + // Get the first element in the page + var item = parseInt(obj.tbody.firstChild.getAttribute('data-y')); + if (obj.options.search == true && obj.results) { + item = results.indexOf(item); + } + if (item > 0) { + for (var j = 0; j < 30; j++) { + item = item - 1; + if (item > -1) { + if (obj.options.search == true && obj.results) { + obj.tbody.insertBefore(obj.rows[results[item]], obj.tbody.firstChild); + } else { + obj.tbody.insertBefore(obj.rows[item], obj.tbody.firstChild); + } + if (obj.tbody.children.length > 100) { + obj.tbody.removeChild(obj.tbody.lastChild); + test = 1; + } + } + } + } + } + return test; + } + + obj.loadDown = function() { + // Search + if (obj.options.search == true && obj.results) { + var results = obj.results; + } else { + var results = obj.rows; + } + var test = 0; + if (results.length > 100) { + // Get the last element in the page + var item = parseInt(obj.tbody.lastChild.getAttribute('data-y')); + if (obj.options.search == true && obj.results) { + item = results.indexOf(item); + } + if (item < obj.rows.length - 1) { + for (var j = 0; j <= 30; j++) { + if (item < results.length) { + if (obj.options.search == true && obj.results) { + obj.tbody.appendChild(obj.rows[results[item]]); + } else { + obj.tbody.appendChild(obj.rows[item]); + } + if (obj.tbody.children.length > 100) { + obj.tbody.removeChild(obj.tbody.firstChild); + test = 1; + } + } + item = item + 1; + } + } + } + + return test; + } + + obj.loadValidation = function() { + if (obj.selectedCell) { + var currentPage = parseInt(obj.tbody.firstChild.getAttribute('data-y')) / 100; + var selectedPage = parseInt(obj.selectedCell[3] / 100); + var totalPages = parseInt(obj.rows.length / 100); + + if (currentPage != selectedPage && selectedPage <= totalPages) { + if (! Array.prototype.indexOf.call(obj.tbody.children, obj.rows[obj.selectedCell[3]])) { + obj.loadPage(selectedPage); + return true; + } + } + } + + return false; + } + + /** + * Reset search + */ + obj.resetSearch = function() { + obj.searchInput.value = ''; + obj.search(''); + obj.results = null; + } + + /** + * Search + */ + obj.search = function(query) { + // Query + if (query) { + var query = query.toLowerCase(); + } + + // Reset any filter + if (obj.options.filters) { + obj.resetFilters(); + } + + // Reset selection + obj.resetSelection(); + + // Total of results + obj.pageNumber = 0; + obj.results = []; + + if (query) { + // Search filter + var search = function(item, query, index) { + for (var i = 0; i < item.length; i++) { + if ((''+item[i]).toLowerCase().search(query) >= 0 || + (''+obj.records[index][i].innerHTML).toLowerCase().search(query) >= 0) { + return true; + } + } + return false; + } + + // Result + var addToResult = function(k) { + if (obj.results.indexOf(k) == -1) { + obj.results.push(k); + } + } + + // Filter + var data = obj.options.data.filter(function(v, k) { + if (search(v, query, k)) { + // Merged rows found + var rows = obj.isRowMerged(k); + if (rows.length) { + for (var i = 0; i < rows.length; i++) { + var row = jexcel.getIdFromColumnName(rows[i], true); + for (var j = 0; j < obj.options.mergeCells[rows[i]][1]; j++) { + addToResult(row[1]+j); + } + } + } else { + // Normal row found + addToResult(k); + } + return true; + } else { + return false; + } + }); + } else { + obj.results = null; + } + + return obj.updateResult(); + } + + obj.updateResult = function() { + var total = 0; + var index = 0; + + // Page 1 + if (obj.options.lazyLoading == true) { + total = 100; + } else if (obj.options.pagination > 0) { + total = obj.options.pagination; + } else { + if (obj.results) { + total = obj.results.length; + } else { + total = obj.rows.length; + } + } + + // Reset current nodes + while (obj.tbody.firstChild) { + obj.tbody.removeChild(obj.tbody.firstChild); + } + + // Hide all records from the table + for (var j = 0; j < obj.rows.length; j++) { + if (! obj.results || obj.results.indexOf(j) > -1) { + if (index < total) { + obj.tbody.appendChild(obj.rows[j]); + index++; + } + obj.rows[j].style.display = ''; + } else { + obj.rows[j].style.display = 'none'; + } + } + + // Update pagination + if (obj.options.pagination > 0) { + obj.updatePagination(); + } + + obj.updateCornerPosition(); + + return total; + } + + /** + * Which page the cell is + */ + obj.whichPage = function(cell) { + // Search + if (obj.options.search == true && obj.results) { + cell = obj.results.indexOf(cell); + } + + return (Math.ceil((parseInt(cell) + 1) / parseInt(obj.options.pagination))) - 1; + } + + /** + * Go to page + */ + obj.page = function(pageNumber) { + var oldPage = obj.pageNumber; + + // Search + if (obj.options.search == true && obj.results) { + var results = obj.results; + } else { + var results = obj.rows; + } + + // Per page + var quantityPerPage = parseInt(obj.options.pagination); + + // pageNumber + if (pageNumber == null || pageNumber == -1) { + // Last page + pageNumber = Math.ceil(results.length / quantityPerPage); + } + + // Page number + obj.pageNumber = pageNumber; + + var startRow = (pageNumber * quantityPerPage); + var finalRow = (pageNumber * quantityPerPage) + quantityPerPage; + if (finalRow > results.length) { + finalRow = results.length; + } + if (startRow < 0) { + startRow = 0; + } + + // Reset container + while (obj.tbody.firstChild) { + obj.tbody.removeChild(obj.tbody.firstChild); + } + + // Appeding items + for (var j = startRow; j < finalRow; j++) { + if (obj.options.search == true && obj.results) { + obj.tbody.appendChild(obj.rows[results[j]]); + } else { + obj.tbody.appendChild(obj.rows[j]); + } + } + + if (obj.options.pagination > 0) { + obj.updatePagination(); + } + + // Update corner position + obj.updateCornerPosition(); + + // Events + obj.dispatch('onchangepage', el, pageNumber, oldPage); + } + + /** + * Update the pagination + */ + obj.updatePagination = function() { + // Reset container + obj.pagination.children[0].innerHTML = ''; + obj.pagination.children[1].innerHTML = ''; + + // Start pagination + if (obj.options.pagination) { + // Searchable + if (obj.options.search == true && obj.results) { + var results = obj.results.length; + } else { + var results = obj.rows.length; + } + + if (! results) { + // No records found + obj.pagination.children[0].innerHTML = obj.options.text.noRecordsFound; + } else { + // Pagination container + var quantyOfPages = Math.ceil(results / obj.options.pagination); + + if (obj.pageNumber < 6) { + var startNumber = 1; + var finalNumber = quantyOfPages < 10 ? quantyOfPages : 10; + } else if (quantyOfPages - obj.pageNumber < 5) { + var startNumber = quantyOfPages - 9; + var finalNumber = quantyOfPages; + if (startNumber < 1) { + startNumber = 1; + } + } else { + var startNumber = obj.pageNumber - 4; + var finalNumber = obj.pageNumber + 5; + } + + // First + if (startNumber > 1) { + var paginationItem = document.createElement('div'); + paginationItem.className = 'jexcel_page'; + paginationItem.innerHTML = '<'; + paginationItem.title = 1; + obj.pagination.children[1].appendChild(paginationItem); + } + + // Get page links + for (var i = startNumber; i <= finalNumber; i++) { + var paginationItem = document.createElement('div'); + paginationItem.className = 'jexcel_page'; + paginationItem.innerHTML = i; + obj.pagination.children[1].appendChild(paginationItem); + + if (obj.pageNumber == (i-1)) { + paginationItem.classList.add('jexcel_page_selected'); + } + } + + // Last + if (finalNumber < quantyOfPages) { + var paginationItem = document.createElement('div'); + paginationItem.className = 'jexcel_page'; + paginationItem.innerHTML = '>'; + paginationItem.title = quantyOfPages; + obj.pagination.children[1].appendChild(paginationItem); + } + + // Text + var format = function(format) { + var args = Array.prototype.slice.call(arguments, 1); + return format.replace(/{(\d+)}/g, function(match, number) { + return typeof args[number] != 'undefined' + ? args[number] + : match + ; + }); + }; + + obj.pagination.children[0].innerHTML = format(obj.options.text.showingPage, obj.pageNumber + 1, quantyOfPages) + } + } + } + + /** + * Download CSV table + * + * @return null + */ + obj.download = function(includeHeaders) { + if (obj.options.allowExport == false) { + console.error('Export not allowed'); + } else { + // Data + var data = ''; + if (includeHeaders == true || obj.options.includeHeadersOnDownload == true) { + data += obj.getHeaders(); + data += "\r\n"; + } + + // Get data + data += obj.copy(false, obj.options.csvDelimiter, true); + + // Download element + var blob = new Blob(["\uFEFF"+data], {type: 'text/csv;charset=utf-8;'}); + + // IE Compatibility + if (window.navigator && window.navigator.msSaveOrOpenBlob) { + window.navigator.msSaveOrOpenBlob(blob, options.csvFileName + '.csv'); + } else { + // Download element + var pom = document.createElement('a'); + var url = URL.createObjectURL(blob); + pom.href = url; + pom.setAttribute('download', obj.options.csvFileName + '.csv'); + document.body.appendChild(pom); + pom.click(); + pom.parentNode.removeChild(pom); + } + } + } + + /** + * Initializes a new history record for undo/redo + * + * @return null + */ + obj.setHistory = function(changes) { + if (obj.ignoreHistory != true) { + // Increment and get the current history index + var index = ++obj.historyIndex; + + // Slice the array to discard undone changes + obj.history = (obj.history = obj.history.slice(0, index + 1)); + + // Keep history + obj.history[index] = changes; + } + } + + /** + * Copy method + * + * @param bool highlighted - Get only highlighted cells + * @param delimiter - \t default to keep compatibility with excel + * @return string value + */ + obj.copy = function(highlighted, delimiter, returnData) { + if (! delimiter) { + delimiter = "\t"; + } + + // Controls + var col = []; + var colLabel = []; + var row = []; + var rowLabel = []; + var x = obj.options.data[0].length + var y = obj.options.data.length + var tmp = ''; + + // Headers + if (obj.options.includeHeadersOnCopy == true) { + if (obj.options.copyCompatibility == true) { + strLabel.push(obj.getHeaders(true).join(delimiter)); + } else { + str.push(obj.getHeaders(true).join(delimiter)); + } + } + + // Reset container + obj.style = []; + + // Go through the columns to get the data + for (var j = 0; j < y; j++) { + col = []; + colLabel = []; + + for (var i = 0; i < x; i++) { + // If cell is highlighted + if (! highlighted || obj.records[j][i].classList.contains('highlight')) { + // Values + var value = obj.options.data[j][i]; + if (value.match && (value.match(/,/g) || value.match(/\n/) || value.match(/\"/))) { + value = value.replace(new RegExp('"', 'g'), '""'); + value = '"' + value + '"'; + } + col.push(value); + + // Labels + if (obj.options.columns[i].type == 'checkbox' || obj.options.columns[i].type == 'radio') { + var label = value; + } else { + var label = obj.records[j][i].innerHTML; + if (label.match && (label.match(/,/g) || label.match(/\n/) || label.match(/\"/))) { + // Scape double quotes + label = label.replace(new RegExp('"', 'g'), '""'); + label = '"' + label + '"'; + } + } + colLabel.push(label); + + // Get style + tmp = obj.records[j][i].getAttribute('style'); + tmp = tmp.replace('display: none;', ''); + obj.style.push(tmp ? tmp : ''); + } + } + + if (col.length) { + row.push(col.join(delimiter)); + } + if (colLabel.length) { + rowLabel.push(colLabel.join(delimiter)); + } + } + + // Final string + var str = row.join("\r\n"); + var strLabel = rowLabel.join("\r\n"); + + // Create a hidden textarea to copy the values + if (! returnData) { + if (obj.options.copyCompatibility == true) { + obj.textarea.value = strLabel; + } else { + obj.textarea.value = str; + } + obj.textarea.select(); + document.execCommand("copy"); + } + + // Keep data + if (obj.options.copyCompatibility == true) { + obj.data = strLabel; + } else { + obj.data = str; + } + // Keep non visible information + obj.hashString = obj.hash(obj.data); + + // Any exiting border should go + obj.removeCopyingSelection(); + + // Border + if (obj.highlighted) { + for (var i = 0; i < obj.highlighted.length; i++) { + obj.highlighted[i].classList.add('copying'); + if (obj.highlighted[i].classList.contains('highlight-left')) { + obj.highlighted[i].classList.add('copying-left'); + } + if (obj.highlighted[i].classList.contains('highlight-right')) { + obj.highlighted[i].classList.add('copying-right'); + } + if (obj.highlighted[i].classList.contains('highlight-top')) { + obj.highlighted[i].classList.add('copying-top'); + } + if (obj.highlighted[i].classList.contains('highlight-bottom')) { + obj.highlighted[i].classList.add('copying-bottom'); + } + } + } + + // Paste event + obj.dispatch('oncopy', el, obj.options.copyCompatibility == true ? rowLabel : row, obj.hashString); + + return obj.data; + } + + /** + * jExcel paste method + * + * @param integer row number + * @return string value + */ + obj.paste = function(x, y, data) { + // Paste filter + var ret = obj.dispatch('onbeforepaste', el, data, x, y); + + if (ret === false) { + return false; + } else if (ret) { + var data = ret; + } + + // Controls + var hash = obj.hash(data); + var style = (hash == obj.hashString) ? obj.style : null; + + // Depending on the behavior + if (obj.options.copyCompatibility == true && hash == obj.hashString) { + var data = obj.data; + } + + // Split new line + var data = obj.parseCSV(data, "\t"); + + if (x != null && y != null && data) { + // Records + var i = 0; + var j = 0; + var records = []; + var newStyle = {}; + var oldStyle = {}; + var styleIndex = 0; + + // Index + var colIndex = parseInt(x); + var rowIndex = parseInt(y); + var row = null; + + // Go through the columns to get the data + while (row = data[j]) { + i = 0; + colIndex = parseInt(x); + + while (row[i] != null) { + // Update and keep history + var record = obj.updateCell(colIndex, rowIndex, row[i]); + // Keep history + records.push(record); + // Update all formulas in the chain + obj.updateFormulaChain(colIndex, rowIndex, records); + // Style + if (style && style[styleIndex]) { + var columnName = jexcel.getColumnNameFromId([colIndex, rowIndex]); + newStyle[columnName] = style[styleIndex]; + oldStyle[columnName] = obj.getStyle(columnName); + obj.records[rowIndex][colIndex].setAttribute('style', style[styleIndex]); + styleIndex++ + } + i++; + if (row[i] != null) { + if (colIndex >= obj.headers.length - 1) { + obj.insertColumn(); + } + colIndex = obj.right.get(colIndex, rowIndex); + } + } + + j++; + if (data[j]) { + if (rowIndex >= obj.rows.length-1) { + obj.insertRow(); + } + rowIndex = obj.down.get(x, rowIndex); + } + } + + // Select the new cells + obj.updateSelectionFromCoords(x, y, colIndex, rowIndex); + + // Update history + obj.setHistory({ + action:'setValue', + records:records, + selection:obj.selectedCell, + newStyle:newStyle, + oldStyle:oldStyle, + }); + + // Update table + obj.updateTable(); + + // Paste event + obj.dispatch('onpaste', el, data); + + // On after changes + obj.onafterchanges(el, records); + } + + obj.removeCopyingSelection(); + } + + /** + * Remove copying border + */ + obj.removeCopyingSelection = function() { + var copying = document.querySelectorAll('.jexcel .copying'); + for (var i = 0; i < copying.length; i++) { + copying[i].classList.remove('copying'); + copying[i].classList.remove('copying-left'); + copying[i].classList.remove('copying-right'); + copying[i].classList.remove('copying-top'); + copying[i].classList.remove('copying-bottom'); + } + } + + /** + * Process row + */ + obj.historyProcessRow = function(type, historyRecord) { + var rowIndex = (! historyRecord.insertBefore) ? historyRecord.rowNumber + 1 : historyRecord.rowNumber; + + if (obj.options.search == true) { + if (obj.results && obj.results.length != obj.rows.length) { + obj.resetSearch(); + } + } + + // Remove row + if (type == 1) { + var numOfRows = historyRecord.numOfRows; + // Remove nodes + for (var j = rowIndex; j < (numOfRows + rowIndex); j++) { + obj.rows[j].parentNode.removeChild(obj.rows[j]); + } + // Remove references + obj.records.splice(rowIndex, numOfRows); + obj.options.data.splice(rowIndex, numOfRows); + obj.rows.splice(rowIndex, numOfRows); + + obj.conditionalSelectionUpdate(1, rowIndex, (numOfRows + rowIndex) - 1); + } else { + // Insert data + obj.records = jexcel.injectArray(obj.records, rowIndex, historyRecord.rowRecords); + obj.options.data = jexcel.injectArray(obj.options.data, rowIndex, historyRecord.rowData); + obj.rows = jexcel.injectArray(obj.rows, rowIndex, historyRecord.rowNode); + // Insert nodes + var index = 0 + for (var j = rowIndex; j < (historyRecord.numOfRows + rowIndex); j++) { + obj.tbody.insertBefore(historyRecord.rowNode[index], obj.tbody.children[j]); + index++; + } + } + + // Respect pagination + if (obj.options.pagination > 0) { + obj.page(obj.pageNumber); + } + + obj.updateTableReferences(); + } + + /** + * Process column + */ + obj.historyProcessColumn = function(type, historyRecord) { + var columnIndex = (! historyRecord.insertBefore) ? historyRecord.columnNumber + 1 : historyRecord.columnNumber; + + // Remove column + if (type == 1) { + var numOfColumns = historyRecord.numOfColumns; + + obj.options.columns.splice(columnIndex, numOfColumns); + for (var i = columnIndex; i < (numOfColumns + columnIndex); i++) { + obj.headers[i].parentNode.removeChild(obj.headers[i]); + obj.colgroup[i].parentNode.removeChild(obj.colgroup[i]); + } + obj.headers.splice(columnIndex, numOfColumns); + obj.colgroup.splice(columnIndex, numOfColumns); + for (var j = 0; j < historyRecord.data.length; j++) { + for (var i = columnIndex; i < (numOfColumns + columnIndex); i++) { + obj.records[j][i].parentNode.removeChild(obj.records[j][i]); + } + obj.records[j].splice(columnIndex, numOfColumns); + obj.options.data[j].splice(columnIndex, numOfColumns); + } + // Process footers + if (obj.options.footers) { + for (var j = 0; j < obj.options.footers.length; j++) { + obj.options.footers[j].splice(columnIndex, numOfColumns); + } + } + } else { + // Insert data + obj.options.columns = jexcel.injectArray(obj.options.columns, columnIndex, historyRecord.columns); + obj.headers = jexcel.injectArray(obj.headers, columnIndex, historyRecord.headers); + obj.colgroup = jexcel.injectArray(obj.colgroup, columnIndex, historyRecord.colgroup); + + var index = 0 + for (var i = columnIndex; i < (historyRecord.numOfColumns + columnIndex); i++) { + obj.headerContainer.insertBefore(historyRecord.headers[index], obj.headerContainer.children[i+1]); + obj.colgroupContainer.insertBefore(historyRecord.colgroup[index], obj.colgroupContainer.children[i+1]); + index++; + } + + for (var j = 0; j < historyRecord.data.length; j++) { + obj.options.data[j] = jexcel.injectArray(obj.options.data[j], columnIndex, historyRecord.data[j]); + obj.records[j] = jexcel.injectArray(obj.records[j], columnIndex, historyRecord.records[j]); + var index = 0 + for (var i = columnIndex; i < (historyRecord.numOfColumns + columnIndex); i++) { + obj.rows[j].insertBefore(historyRecord.records[j][index], obj.rows[j].children[i+1]); + index++; + } + } + // Process footers + if (obj.options.footers) { + for (var j = 0; j < obj.options.footers.length; j++) { + obj.options.footers[j] = jexcel.injectArray(obj.options.footers[j], columnIndex, historyRecord.footers[j]); + } + } + } + + // Adjust nested headers + if (obj.options.nestedHeaders && obj.options.nestedHeaders.length > 0) { + // Flexible way to handle nestedheaders + if (obj.options.nestedHeaders[0] && obj.options.nestedHeaders[0][0]) { + for (var j = 0; j < obj.options.nestedHeaders.length; j++) { + if (type == 1) { + var colspan = parseInt(obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan) - historyRecord.numOfColumns; + } else { + var colspan = parseInt(obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan) + historyRecord.numOfColumns; + } + obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan = colspan; + obj.thead.children[j].children[obj.thead.children[j].children.length-1].setAttribute('colspan', colspan); + } + } else { + if (type == 1) { + var colspan = parseInt(obj.options.nestedHeaders[0].colspan) - historyRecord.numOfColumns; + } else { + var colspan = parseInt(obj.options.nestedHeaders[0].colspan) + historyRecord.numOfColumns; + } + obj.options.nestedHeaders[0].colspan = colspan; + obj.thead.children[0].children[obj.thead.children[0].children.length-1].setAttribute('colspan', colspan); + } + } + + obj.updateTableReferences(); + } + + /** + * Undo last action + */ + obj.undo = function() { + // Ignore events and history + var ignoreEvents = obj.ignoreEvents ? true : false; + var ignoreHistory = obj.ignoreHistory ? true : false; + + obj.ignoreEvents = true; + obj.ignoreHistory = true; + + // Records + var records = []; + + // Update cells + if (obj.historyIndex >= 0) { + // History + var historyRecord = obj.history[obj.historyIndex--]; + + if (historyRecord.action === 'endResizeTable') { + while (obj.history[obj.historyIndex].action !== "beginResizeTable") { + obj.undo(); + } + obj.undo(); + } else if (historyRecord.action == 'endChangeType') { + obj.options.columns[historyRecord.column].type = historyRecord.oldType; + while (obj.history[obj.historyIndex].action !== "beginChangeType") { + obj.undo(); + } + obj.undo(); + } else if (historyRecord.action == 'insertRow') { + obj.historyProcessRow(1, historyRecord); + } else if (historyRecord.action == 'deleteRow') { + obj.historyProcessRow(0, historyRecord); + } else if (historyRecord.action == 'insertColumn') { + obj.historyProcessColumn(1, historyRecord); + } else if (historyRecord.action == 'deleteColumn') { + obj.historyProcessColumn(0, historyRecord); + } else if (historyRecord.action == 'moveRow') { + obj.moveRow(historyRecord.newValue, historyRecord.oldValue); + } else if (historyRecord.action == 'moveColumn') { + obj.moveColumn(historyRecord.newValue, historyRecord.oldValue); + } else if (historyRecord.action == 'setMerge') { + obj.removeMerge(historyRecord.column, historyRecord.data); + } else if (historyRecord.action == 'setStyle') { + obj.setStyle(historyRecord.oldValue, null, null, 1); + } else if (historyRecord.action == 'setWidth') { + obj.setWidth(historyRecord.column, historyRecord.oldValue); + } else if (historyRecord.action == 'setHeight') { + obj.setHeight(historyRecord.row, historyRecord.oldValue); + } else if (historyRecord.action == 'setHeader') { + obj.setHeader(historyRecord.column, historyRecord.oldValue); + } else if (historyRecord.action == 'setComments') { + obj.setComments(historyRecord.column, historyRecord.oldValue[0], historyRecord.oldValue[1]); + } else if (historyRecord.action == 'orderBy') { + var rows = []; + for (var j = 0; j < historyRecord.rows.length; j++) { + rows[historyRecord.rows[j]] = j; + } + obj.updateOrderArrow(historyRecord.column, historyRecord.order ? 0 : 1); + obj.updateOrder(rows); + } else if (historyRecord.action == 'setValue') { + // Redo for changes in cells + for (var i = 0; i < historyRecord.records.length; i++) { + records.push({ + x: historyRecord.records[i].x, + y: historyRecord.records[i].y, + newValue: historyRecord.records[i].oldValue, + }); + + if (historyRecord.oldStyle) { + obj.resetStyle(historyRecord.oldStyle); + } + } + // Update records + obj.setValue(records); + + // Update selection + if (historyRecord.selection) { + obj.updateSelectionFromCoords(historyRecord.selection[0], historyRecord.selection[1], historyRecord.selection[2], historyRecord.selection[3]); + } + } + } + obj.ignoreEvents = ignoreEvents; + obj.ignoreHistory = ignoreHistory; + + // Events + obj.dispatch('onundo', el, historyRecord); + } + + /** + * Redo previously undone action + */ + obj.redo = function() { + // Ignore events and history + var ignoreEvents = obj.ignoreEvents ? true : false; + var ignoreHistory = obj.ignoreHistory ? true : false; + + obj.ignoreEvents = true; + obj.ignoreHistory = true; + + // Records + var records = []; + + // Update cells + if (obj.historyIndex < obj.history.length - 1) { + // History + var historyRecord = obj.history[++obj.historyIndex]; + if (historyRecord.action == 'beginResizeTable') { + while (obj.history[obj.historyIndex].action !== "endResizeTable") { + obj.redo(); + } + obj.redo(); + } else if (historyRecord.action == 'beginChangeType') { + obj.options.columns[historyRecord.column].type = historyRecord.newType; + while (obj.history[obj.historyIndex].action !== "endChangeType") { + obj.redo(); + } + obj.redo(); + } else if (historyRecord.action == 'insertRow') { + obj.historyProcessRow(0, historyRecord); + } else if (historyRecord.action == 'deleteRow') { + obj.historyProcessRow(1, historyRecord); + } else if (historyRecord.action == 'insertColumn') { + obj.historyProcessColumn(0, historyRecord); + } else if (historyRecord.action == 'deleteColumn') { + obj.historyProcessColumn(1, historyRecord); + } else if (historyRecord.action == 'moveRow') { + obj.moveRow(historyRecord.oldValue, historyRecord.newValue); + } else if (historyRecord.action == 'moveColumn') { + obj.moveColumn(historyRecord.oldValue, historyRecord.newValue); + } else if (historyRecord.action == 'setMerge') { + obj.setMerge(historyRecord.column, historyRecord.colspan, historyRecord.rowspan, 1); + } else if (historyRecord.action == 'setStyle') { + obj.setStyle(historyRecord.newValue, null, null, 1); + } else if (historyRecord.action == 'setWidth') { + obj.setWidth(historyRecord.column, historyRecord.newValue); + } else if (historyRecord.action == 'setHeight') { + obj.setHeight(historyRecord.row, historyRecord.newValue); + } else if (historyRecord.action == 'setHeader') { + obj.setHeader(historyRecord.column, historyRecord.newValue); + } else if (historyRecord.action == 'setComments') { + obj.setComments(historyRecord.column, historyRecord.newValue[0], historyRecord.newValue[1]); + } else if (historyRecord.action == 'orderBy') { + obj.updateOrderArrow(historyRecord.column, historyRecord.order); + obj.updateOrder(historyRecord.rows); + } else if (historyRecord.action == 'setValue') { + obj.setValue(historyRecord.records); + // Redo for changes in cells + for (var i = 0; i < historyRecord.records.length; i++) { + if (historyRecord.oldStyle) { + obj.resetStyle(historyRecord.newStyle); + } + } + // Update selection + if (historyRecord.selection) { + obj.updateSelectionFromCoords(historyRecord.selection[0], historyRecord.selection[1], historyRecord.selection[2], historyRecord.selection[3]); + } + } + } + obj.ignoreEvents = ignoreEvents; + obj.ignoreHistory = ignoreHistory; + + // Events + obj.dispatch('onredo', el, historyRecord); + } + + /** + * Get dropdown value from key + */ + obj.getDropDownValue = function(column, key) { + var value = []; + + if (obj.options.columns[column] && obj.options.columns[column].source) { + // Create array from source + var combo = []; + var source = obj.options.columns[column].source; + + for (var i = 0; i < source.length; i++) { + if (typeof(source[i]) == 'object') { + combo[source[i].id] = source[i].name; + } else { + combo[source[i]] = source[i]; + } + } + + // Garante single multiple compatibily + var keys = ('' + key).split(';') + + for (var i = 0; i < keys.length; i++) { + if (combo[keys[i]]) { + value.push(combo[keys[i]]); + } + } + } else { + console.error('Invalid column'); + } + + return (value.length > 0) ? value.join('; ') : ''; + } + + /** + * From starckoverflow contributions + */ + obj.parseCSV = function(str, delimiter) { + // Remove last line break + str = str.replace(/\r?\n$|\r$|\n$/g, ""); + // Last caracter is the delimiter + if (str.charCodeAt(str.length-1) == 9) { + str += "\0"; + } + // user-supplied delimeter or default comma + delimiter = (delimiter || ","); + + var arr = []; + var quote = false; // true means we're inside a quoted field + // iterate over each character, keep track of current row and column (of the returned array) + for (var row = 0, col = 0, c = 0; c < str.length; c++) { + var cc = str[c], nc = str[c+1]; + arr[row] = arr[row] || []; + arr[row][col] = arr[row][col] || ''; + + // If the current character is a quotation mark, and we're inside a quoted field, and the next character is also a quotation mark, add a quotation mark to the current column and skip the next character + if (cc == '"' && quote && nc == '"') { arr[row][col] += cc; ++c; continue; } + + // If it's just one quotation mark, begin/end quoted field + if (cc == '"') { quote = !quote; continue; } + + // If it's a comma and we're not in a quoted field, move on to the next column + if (cc == delimiter && !quote) { ++col; continue; } + + // If it's a newline (CRLF) and we're not in a quoted field, skip the next character and move on to the next row and move to column 0 of that new row + if (cc == '\r' && nc == '\n' && !quote) { ++row; col = 0; ++c; continue; } + + // If it's a newline (LF or CR) and we're not in a quoted field, move on to the next row and move to column 0 of that new row + if (cc == '\n' && !quote) { ++row; col = 0; continue; } + if (cc == '\r' && !quote) { ++row; col = 0; continue; } + + // Otherwise, append the current character to the current column + arr[row][col] += cc; + } + return arr; + } + + obj.hash = function(str) { + var hash = 0, i, chr; + + if (str.length === 0) { + return hash; + } else { + for (i = 0; i < str.length; i++) { + chr = str.charCodeAt(i); + hash = ((hash << 5) - hash) + chr; + hash |= 0; + } + } + return hash; + } + + obj.onafterchanges = function(el, records) { + // Events + obj.dispatch('onafterchanges', el, records); + } + + obj.destroy = function() { + jexcel.destroy(el); + } + + /** + * Initialization method + */ + obj.init = function() { + jexcel.current = obj; + + // Build handlers + if (typeof(jexcel.build) == 'function') { + if (obj.options.root) { + jexcel.build(obj.options.root); + } else { + jexcel.build(document); + jexcel.build = null; + } + } + + // Event + el.setAttribute('tabindex', 1); + el.addEventListener('focus', function(e) { + if (jexcel.current && ! obj.selectedCell) { + obj.updateSelectionFromCoords(0,0,0,0); + obj.left(); + } + }); + + // Load the table data based on an CSV file + if (obj.options.csv) { + // Loading + if (obj.options.loadingSpin == true) { + jSuites.loading.show(); + } + + // Load CSV file + jSuites.ajax({ + url: obj.options.csv, + method: 'GET', + dataType: 'text', + success: function(result) { + // Convert data + var newData = obj.parseCSV(result, obj.options.csvDelimiter) + + // Headers + if (obj.options.csvHeaders == true && newData.length > 0) { + var headers = newData.shift(); + for(var i = 0; i < headers.length; i++) { + if (! obj.options.columns[i]) { + obj.options.columns[i] = { type:'text', align:obj.options.defaultColAlign, width:obj.options.defaultColWidth }; + } + // Precedence over pre-configurated titles + if (typeof obj.options.columns[i].title === 'undefined') { + obj.options.columns[i].title = headers[i]; + } + } + } + // Data + obj.options.data = newData; + // Prepare table + obj.prepareTable(); + // Hide spin + if (obj.options.loadingSpin == true) { + jSuites.loading.hide(); + } + } + }); + } else if (obj.options.url) { + // Loading + if (obj.options.loadingSpin == true) { + jSuites.loading.show(); + } + + jSuites.ajax({ + url: obj.options.url, + method: 'GET', + dataType: 'json', + success: function(result) { + // Data + obj.options.data = (result.data) ? result.data : result; + // Prepare table + obj.prepareTable(); + // Hide spin + if (obj.options.loadingSpin == true) { + jSuites.loading.hide(); + } + } + }); + } else { + // Prepare table + obj.prepareTable(); + } + } + + // Context menu + if (options && options.contextMenu != null) { + obj.options.contextMenu = options.contextMenu; + } else { + obj.options.contextMenu = function(el, x, y, e) { + var items = []; + + if (y == null) { + // Insert a new column + if (obj.options.allowInsertColumn == true) { + items.push({ + title:obj.options.text.insertANewColumnBefore, + onclick:function() { + obj.insertColumn(1, parseInt(x), 1); + } + }); + } + + if (obj.options.allowInsertColumn == true) { + items.push({ + title:obj.options.text.insertANewColumnAfter, + onclick:function() { + obj.insertColumn(1, parseInt(x), 0); + } + }); + } + + // Delete a column + if (obj.options.allowDeleteColumn == true) { + items.push({ + title:obj.options.text.deleteSelectedColumns, + onclick:function() { + obj.deleteColumn(obj.getSelectedColumns().length ? undefined : parseInt(x)); + } + }); + } + + // Rename column + if (obj.options.allowRenameColumn == true) { + items.push({ + title:obj.options.text.renameThisColumn, + onclick:function() { + obj.setHeader(x); + } + }); + } + + // Sorting + if (obj.options.columnSorting == true) { + // Line + items.push({ type:'line' }); + + items.push({ + title:obj.options.text.orderAscending, + onclick:function() { + obj.orderBy(x, 0); + } + }); + items.push({ + title:obj.options.text.orderDescending, + onclick:function() { + obj.orderBy(x, 1); + } + }); + } + } else { + // Insert new row + if (obj.options.allowInsertRow == true) { + items.push({ + title:obj.options.text.insertANewRowBefore, + onclick:function() { + obj.insertRow(1, parseInt(y), 1); + } + }); + + items.push({ + title:obj.options.text.insertANewRowAfter, + onclick:function() { + obj.insertRow(1, parseInt(y)); + } + }); + } + + if (obj.options.allowDeleteRow == true) { + items.push({ + title:obj.options.text.deleteSelectedRows, + onclick:function() { + obj.deleteRow(obj.getSelectedRows().length ? undefined : parseInt(y)); + } + }); + } + + if (x) { + if (obj.options.allowComments == true) { + items.push({ type:'line' }); + + var title = obj.records[y][x].getAttribute('title') || ''; + + items.push({ + title: title ? obj.options.text.editComments : obj.options.text.addComments, + onclick:function() { + var comment = prompt(obj.options.text.comments, title); + if (comment) { + obj.setComments([ x, y ], comment); + } + } + }); + + if (title) { + items.push({ + title:obj.options.text.clearComments, + onclick:function() { + obj.setComments([ x, y ], ''); + } + }); + } + } + } + } + + // Line + items.push({ type:'line' }); + + // Copy + items.push({ + title:obj.options.text.copy, + shortcut:'Ctrl + C', + onclick:function() { + obj.copy(true); + } + }); + + // Paste + if (navigator && navigator.clipboard) { + items.push({ + title:obj.options.text.paste, + shortcut:'Ctrl + V', + onclick:function() { + if (obj.selectedCell) { + navigator.clipboard.readText().then(function(text) { + if (text) { + jexcel.current.paste(obj.selectedCell[0], obj.selectedCell[1], text); + } + }); + } + } + }); + } + + // Save + if (obj.options.allowExport) { + items.push({ + title: obj.options.text.saveAs, + shortcut: 'Ctrl + S', + onclick: function () { + obj.download(); + } + }); + } + + // About + if (obj.options.about) { + items.push({ + title:obj.options.text.about, + onclick:function() { + alert(obj.options.about); + } + }); + } + + return items; + } + } + + obj.scrollControls = function(e) { + obj.wheelControls(); + + if (obj.options.freezeColumns > 0 && obj.content.scrollLeft != scrollLeft) { + obj.updateFreezePosition(); + } + + // Close editor + if (obj.options.lazyLoading == true || obj.options.tableOverflow == true) { + if (obj.edition && e.target.className.substr(0,9) != 'jdropdown') { + obj.closeEditor(obj.edition[0], true); + } + } + } + + obj.wheelControls = function(e) { + if (obj.options.lazyLoading == true) { + if (jexcel.timeControlLoading == null) { + jexcel.timeControlLoading = setTimeout(function() { + if (obj.content.scrollTop + obj.content.clientHeight >= obj.content.scrollHeight) { + if (obj.loadDown()) { + if (obj.content.scrollTop + obj.content.clientHeight > obj.content.scrollHeight - 10) { + obj.content.scrollTop = obj.content.scrollTop - obj.content.clientHeight; + } + obj.updateCornerPosition(); + } + } else if (obj.content.scrollTop <= obj.content.clientHeight) { + if (obj.loadUp()) { + if (obj.content.scrollTop < 10) { + obj.content.scrollTop = obj.content.scrollTop + obj.content.clientHeight; + } + obj.updateCornerPosition(); + } + } + + jexcel.timeControlLoading = null; + }, 100); + } + } + } + + // Get width of all freezed cells together + obj.getFreezeWidth = function() { + var width = 0; + if (obj.options.freezeColumns > 0) { + for (var i = 0; i < obj.options.freezeColumns; i++) { + width += parseInt(obj.options.columns[i].width); + } + } + return width; + } + + var scrollLeft = 0; + + obj.updateFreezePosition = function() { + scrollLeft = obj.content.scrollLeft; + var width = 0; + if (scrollLeft > 50) { + for (var i = 0; i < obj.options.freezeColumns; i++) { + if (i > 0) { + width += parseInt(obj.options.columns[i-1].width); + } + obj.headers[i].classList.add('jexcel_freezed'); + obj.headers[i].style.left = width + 'px'; + for (var j = 0; j < obj.rows.length; j++) { + if (obj.rows[j] && obj.records[j][i]) { + var shifted = (scrollLeft + (i > 0 ? obj.records[j][i-1].style.width : 0)) - 51 + 'px'; + obj.records[j][i].classList.add('jexcel_freezed'); + obj.records[j][i].style.left = shifted; + } + } + } + } else { + for (var i = 0; i < obj.options.freezeColumns; i++) { + obj.headers[i].classList.remove('jexcel_freezed'); + obj.headers[i].style.left = ''; + for (var j = 0; j < obj.rows.length; j++) { + if (obj.records[j][i]) { + obj.records[j][i].classList.remove('jexcel_freezed'); + obj.records[j][i].style.left = ''; + } + } + } + } + + // Place the corner in the correct place + obj.updateCornerPosition(); + } + + el.addEventListener("DOMMouseScroll", obj.wheelControls); + el.addEventListener("mousewheel", obj.wheelControls); + + el.jexcel = obj; + + obj.init(); + + return obj; + }); + + jexcel.current = null; + jexcel.timeControl = null; + jexcel.timeControlLoading = null; + + jexcel.destroy = function(element, destroyEventHandlers) { + if (element.jexcel) { + var root = element.jexcel.options.root ? element.jexcel.options.root : document; + element.removeEventListener("DOMMouseScroll", element.jexcel.scrollControls); + element.removeEventListener("mousewheel", element.jexcel.scrollControls); + element.jexcel = null; + element.innerHTML = ''; + + if (destroyEventHandlers) { + root.removeEventListener("mouseup", jexcel.mouseUpControls); + root.removeEventListener("mousedown", jexcel.mouseDownControls); + root.removeEventListener("mousemove", jexcel.mouseMoveControls); + root.removeEventListener("mouseover", jexcel.mouseOverControls); + root.removeEventListener("dblclick", jexcel.doubleClickControls); + root.removeEventListener("paste", jexcel.pasteControls); + root.removeEventListener("contextmenu", jexcel.contextMenuControls); + root.removeEventListener("touchstart", jexcel.touchStartControls); + root.removeEventListener("touchend", jexcel.touchEndControls); + root.removeEventListener("touchcancel", jexcel.touchEndControls); + document.removeEventListener("keydown", jexcel.keyDownControls); + jexcel = null; + } + } + } + + jexcel.build = function(root) { + root.addEventListener("mouseup", jexcel.mouseUpControls); + root.addEventListener("mousedown", jexcel.mouseDownControls); + root.addEventListener("mousemove", jexcel.mouseMoveControls); + root.addEventListener("mouseover", jexcel.mouseOverControls); + root.addEventListener("dblclick", jexcel.doubleClickControls); + root.addEventListener("paste", jexcel.pasteControls); + root.addEventListener("contextmenu", jexcel.contextMenuControls); + root.addEventListener("touchstart", jexcel.touchStartControls); + root.addEventListener("touchend", jexcel.touchEndControls); + root.addEventListener("touchcancel", jexcel.touchEndControls); + root.addEventListener("touchmove", jexcel.touchEndControls); + document.addEventListener("keydown", jexcel.keyDownControls); + } + + /** + * Events + */ + jexcel.keyDownControls = function(e) { + if (jexcel.current) { + if (jexcel.current.edition) { + if (e.which == 27) { + // Escape + if (jexcel.current.edition) { + // Exit without saving + jexcel.current.closeEditor(jexcel.current.edition[0], false); + } + e.preventDefault(); + } else if (e.which == 13) { + // Enter + if (jexcel.current.options.columns[jexcel.current.edition[2]].type == 'calendar') { + jexcel.current.closeEditor(jexcel.current.edition[0], true); + } else if (jexcel.current.options.columns[jexcel.current.edition[2]].type == 'dropdown' || + jexcel.current.options.columns[jexcel.current.edition[2]].type == 'autocomplete') { + // Do nothing + } else { + // Alt enter -> do not close editor + if ((jexcel.current.options.wordWrap == true || + jexcel.current.options.columns[jexcel.current.edition[2]].wordWrap == true || + jexcel.current.options.data[jexcel.current.edition[3]][jexcel.current.edition[2]].length > 200) && e.altKey) { + // Add new line to the editor + var editorTextarea = jexcel.current.edition[0].children[0]; + var editorValue = jexcel.current.edition[0].children[0].value; + var editorIndexOf = editorTextarea.selectionStart; + editorValue = editorValue.slice(0, editorIndexOf) + "\n" + editorValue.slice(editorIndexOf); + editorTextarea.value = editorValue; + editorTextarea.focus(); + editorTextarea.selectionStart = editorIndexOf + 1; + editorTextarea.selectionEnd = editorIndexOf + 1; + } else { + jexcel.current.edition[0].children[0].blur(); + } + } + } else if (e.which == 9) { + // Tab + if (jexcel.current.options.columns[jexcel.current.edition[2]].type == 'calendar') { + jexcel.current.closeEditor(jexcel.current.edition[0], true); + } else { + jexcel.current.edition[0].children[0].blur(); + } + } + } + + if (! jexcel.current.edition && jexcel.current.selectedCell) { + // Which key + if (e.which == 37) { + jexcel.current.left(e.shiftKey, e.ctrlKey); + e.preventDefault(); + } else if (e.which == 39) { + jexcel.current.right(e.shiftKey, e.ctrlKey); + e.preventDefault(); + } else if (e.which == 38) { + jexcel.current.up(e.shiftKey, e.ctrlKey); + e.preventDefault(); + } else if (e.which == 40) { + jexcel.current.down(e.shiftKey, e.ctrlKey); + e.preventDefault(); + } else if (e.which == 36) { + jexcel.current.first(e.shiftKey, e.ctrlKey); + e.preventDefault(); + } else if (e.which == 35) { + jexcel.current.last(e.shiftKey, e.ctrlKey); + e.preventDefault(); + } else if (e.which == 32) { + if (jexcel.current.options.editable == true) { + jexcel.current.setCheckRadioValue(); + } + e.preventDefault(); + } else if (e.which == 46) { + // Delete + if (jexcel.current.options.editable == true) { + if (jexcel.current.selectedRow) { + if (jexcel.current.options.allowDeleteRow == true) { + if (confirm(jexcel.current.options.text.areYouSureToDeleteTheSelectedRows)) { + jexcel.current.deleteRow(); + } + } + } else if (jexcel.current.selectedHeader) { + if (jexcel.current.options.allowDeleteColumn == true) { + if (confirm(jexcel.current.options.text.areYouSureToDeleteTheSelectedColumns)) { + jexcel.current.deleteColumn(); + } + } + } else { + // Change value + jexcel.current.setValue(jexcel.current.highlighted, ''); + } + } + } else if (e.which == 13) { + // Move cursor + if (e.shiftKey) { + jexcel.current.up(); + } else { + if (jexcel.current.options.allowInsertRow == true) { + if (jexcel.current.options.allowManualInsertRow == true) { + if (jexcel.current.selectedCell[1] == jexcel.current.options.data.length - 1) { + // New record in case selectedCell in the last row + jexcel.current.insertRow(); + } + } + } + + jexcel.current.down(); + } + e.preventDefault(); + } else if (e.which == 9) { + // Tab + if (e.shiftKey) { + jexcel.current.left(); + } else { + if (jexcel.current.options.allowInsertColumn == true) { + if (jexcel.current.options.allowManualInsertColumn == true) { + if (jexcel.current.selectedCell[0] == jexcel.current.options.data[0].length - 1) { + // New record in case selectedCell in the last column + jexcel.current.insertColumn(); + } + } + } + + jexcel.current.right(); + } + e.preventDefault(); + } else { + if ((e.ctrlKey || e.metaKey) && ! e.shiftKey) { + if (e.which == 65) { + // Ctrl + A + jexcel.current.selectAll(); + e.preventDefault(); + } else if (e.which == 83) { + // Ctrl + S + jexcel.current.download(); + e.preventDefault(); + } else if (e.which == 89) { + // Ctrl + Y + jexcel.current.redo(); + e.preventDefault(); + } else if (e.which == 90) { + // Ctrl + Z + jexcel.current.undo(); + e.preventDefault(); + } else if (e.which == 67) { + // Ctrl + C + jexcel.current.copy(true); + e.preventDefault(); + } else if (e.which == 67) { + // Ctrl + C + jexcel.current.copy(true); + e.preventDefault(); + } else if (e.which == 88) { + // Ctrl + X + if (jexcel.current.options.editable == true) { + jexcel.cutControls(); + } else { + jexcel.copyControls(); + } + e.preventDefault(); + } else if (e.which == 86) { + // Ctrl + V + jexcel.pasteControls(); + } + } else { + if (jexcel.current.selectedCell) { + if (jexcel.current.options.editable == true) { + var rowId = jexcel.current.selectedCell[1]; + var columnId = jexcel.current.selectedCell[0]; + + // If is not readonly + if (jexcel.current.options.columns[columnId].type != 'readonly') { + // Characters able to start a edition + if (e.keyCode == 32) { + // Space + if (jexcel.current.options.columns[columnId].type == 'checkbox' || + jexcel.current.options.columns[columnId].type == 'radio') { + e.preventDefault(); + } else { + // Start edition + jexcel.current.openEditor(jexcel.current.records[rowId][columnId], true); + } + } else if (e.keyCode == 113) { + // Start edition with current content F2 + jexcel.current.openEditor(jexcel.current.records[rowId][columnId], false); + } else if ((e.keyCode == 8) || + (e.keyCode >= 48 && e.keyCode <= 57) || + (e.keyCode >= 96 && e.keyCode <= 111) || + (e.keyCode == 187) || + (e.keyCode == 189) || + ((String.fromCharCode(e.keyCode) == e.key || String.fromCharCode(e.keyCode).toLowerCase() == e.key.toLowerCase()) && jexcel.validLetter(String.fromCharCode(e.keyCode)))) { + // Start edition + jexcel.current.openEditor(jexcel.current.records[rowId][columnId], true); + // Prevent entries in the calendar + if (jexcel.current.options.columns[columnId].type == 'calendar') { + e.preventDefault(); + } + } + } + } + } + } + } + } else { + if (e.target.classList.contains('jexcel_search')) { + if (jexcel.timeControl) { + clearTimeout(jexcel.timeControl); + } + + jexcel.timeControl = setTimeout(function() { + jexcel.current.search(e.target.value); + }, 200); + } + } + } + } + + jexcel.isMouseAction = false; + + jexcel.mouseDownControls = function(e) { + e = e || window.event; + if (e.buttons) { + var mouseButton = e.buttons; + } else if (e.button) { + var mouseButton = e.button; + } else { + var mouseButton = e.which; + } + + // Get elements + var jexcelTable = jexcel.getElement(e.target); + + if (jexcelTable[0]) { + if (jexcel.current != jexcelTable[0].jexcel) { + if (jexcel.current) { + if (jexcel.current.edition) { + jexcel.current.closeEditor(jexcel.current.edition[0], true); + } + jexcel.current.resetSelection(); + } + jexcel.current = jexcelTable[0].jexcel; + } + } else { + if (jexcel.current) { + if (jexcel.current.edition) { + jexcel.current.closeEditor(jexcel.current.edition[0], true); + } + + jexcel.current.resetSelection(true); + jexcel.current = null; + } + } + + if (jexcel.current && mouseButton == 1) { + if (e.target.classList.contains('jexcel_selectall')) { + if (jexcel.current) { + jexcel.current.selectAll(); + } + } else if (e.target.classList.contains('jexcel_corner')) { + if (jexcel.current.options.editable == true) { + jexcel.current.selectedCorner = true; + } + } else { + // Header found + if (jexcelTable[1] == 1) { + var columnId = e.target.getAttribute('data-x'); + if (columnId) { + // Update cursor + var info = e.target.getBoundingClientRect(); + if (jexcel.current.options.columnResize == true && info.width - e.offsetX < 6) { + // Resize helper + jexcel.current.resizing = { + mousePosition: e.pageX, + column: columnId, + width: info.width, + }; + + // Border indication + jexcel.current.headers[columnId].classList.add('resizing'); + for (var j = 0; j < jexcel.current.records.length; j++) { + if (jexcel.current.records[j][columnId]) { + jexcel.current.records[j][columnId].classList.add('resizing'); + } + } + } else if (jexcel.current.options.columnDrag == true && info.height - e.offsetY < 6) { + if (jexcel.current.isColMerged(columnId).length) { + console.error('JEXCEL: This column is part of a merged cell.'); + } else { + // Reset selection + jexcel.current.resetSelection(); + // Drag helper + jexcel.current.dragging = { + element: e.target, + column:columnId, + destination:columnId, + }; + // Border indication + jexcel.current.headers[columnId].classList.add('dragging'); + for (var j = 0; j < jexcel.current.records.length; j++) { + if (jexcel.current.records[j][columnId]) { + jexcel.current.records[j][columnId].classList.add('dragging'); + } + } + } + } else { + if (jexcel.current.selectedHeader && (e.shiftKey || e.ctrlKey)) { + var o = jexcel.current.selectedHeader; + var d = columnId; + } else { + // Press to rename + if (jexcel.current.selectedHeader == columnId && jexcel.current.options.allowRenameColumn == true) { + jexcel.timeControl = setTimeout(function() { + jexcel.current.setHeader(columnId); + }, 800); + } + + // Keep track of which header was selected first + jexcel.current.selectedHeader = columnId; + + // Update selection single column + var o = columnId; + var d = columnId; + } + + // Update selection + jexcel.current.updateSelectionFromCoords(o, 0, d, jexcel.current.options.data.length - 1); + } + } else { + if (e.target.parentNode.classList.contains('jexcel_nested')) { + if (e.target.getAttribute('data-column')) { + var column = e.target.getAttribute('data-column').split(','); + var c1 = parseInt(column[0]); + var c2 = parseInt(column[column.length-1]); + } else { + var c1 = 0; + var c2 = jexcel.current.options.columns.length - 1; + } + jexcel.current.updateSelectionFromCoords(c1, 0, c2, jexcel.current.options.data.length - 1); + } + } + } else { + jexcel.current.selectedHeader = false; + } + + // Body found + if (jexcelTable[1] == 2) { + var rowId = e.target.getAttribute('data-y'); + + if (e.target.classList.contains('jexcel_row')) { + var info = e.target.getBoundingClientRect(); + if (jexcel.current.options.rowResize == true && info.height - e.offsetY < 6) { + // Resize helper + jexcel.current.resizing = { + element: e.target.parentNode, + mousePosition: e.pageY, + row: rowId, + height: info.height, + }; + // Border indication + e.target.parentNode.classList.add('resizing'); + } else if (jexcel.current.options.rowDrag == true && info.width - e.offsetX < 6) { + if (jexcel.current.isRowMerged(rowId).length) { + console.error('JEXCEL: This row is part of a merged cell'); + } else if (jexcel.current.options.search == true && jexcel.current.results) { + console.error('JEXCEL: Please clear your search before perform this action'); + } else { + // Reset selection + jexcel.current.resetSelection(); + // Drag helper + jexcel.current.dragging = { + element: e.target.parentNode, + row:rowId, + destination:rowId, + }; + // Border indication + e.target.parentNode.classList.add('dragging'); + } + } else { + if (jexcel.current.selectedRow && (e.shiftKey || e.ctrlKey)) { + var o = jexcel.current.selectedRow; + var d = rowId; + } else { + // Keep track of which header was selected first + jexcel.current.selectedRow = rowId; + + // Update selection single column + var o = rowId; + var d = rowId; + } + + // Update selection + jexcel.current.updateSelectionFromCoords(0, o, jexcel.current.options.data[0].length - 1, d); + } + } else { + // Jclose + if (e.target.classList.contains('jclose') && e.target.clientWidth - e.offsetX < 50 && e.offsetY < 50) { + jexcel.current.closeEditor(jexcel.current.edition[0], true); + } else { + var getCellCoords = function(element) { + var x = element.getAttribute('data-x'); + var y = element.getAttribute('data-y'); + if (x && y) { + return [x, y]; + } else { + if (element.parentNode) { + return getCellCoords(element.parentNode); + } + } + }; + + var position = getCellCoords(e.target); + if (position) { + + var columnId = position[0]; + var rowId = position[1]; + // Close edition + if (jexcel.current.edition) { + if (jexcel.current.edition[2] != columnId || jexcel.current.edition[3] != rowId) { + jexcel.current.closeEditor(jexcel.current.edition[0], true); + } + } + + if (! jexcel.current.edition) { + // Update cell selection + if (e.shiftKey) { + jexcel.current.updateSelectionFromCoords(jexcel.current.selectedCell[0], jexcel.current.selectedCell[1], columnId, rowId); + } else { + jexcel.current.updateSelectionFromCoords(columnId, rowId); + } + } + + // No full row selected + jexcel.current.selectedHeader = null; + jexcel.current.selectedRow = null; + } + } + } + } else { + jexcel.current.selectedRow = false; + } + + // Pagination + if (e.target.classList.contains('jexcel_page')) { + if (e.target.innerText == '<') { + jexcel.current.page(0); + } else if (e.target.innerText == '>') { + jexcel.current.page(e.target.getAttribute('title') - 1); + } else { + jexcel.current.page(e.target.innerText - 1); + } + } + } + + if (jexcel.current.edition) { + jexcel.isMouseAction = false; + } else { + jexcel.isMouseAction = true; + } + } else { + jexcel.isMouseAction = false; + } + } + + jexcel.mouseUpControls = function(e) { + if (jexcel.current) { + // Update cell size + if (jexcel.current.resizing) { + // Columns to be updated + if (jexcel.current.resizing.column) { + // New width + var newWidth = jexcel.current.colgroup[jexcel.current.resizing.column].getAttribute('width'); + // Columns + var columns = jexcel.current.getSelectedColumns(); + if (columns.length > 1) { + var currentWidth = []; + for (var i = 0; i < columns.length; i++) { + currentWidth.push(parseInt(jexcel.current.colgroup[columns[i]].getAttribute('width'))); + } + // Previous width + var index = columns.indexOf(parseInt(jexcel.current.resizing.column)); + currentWidth[index] = jexcel.current.resizing.width; + jexcel.current.setWidth(columns, newWidth, currentWidth); + } else { + jexcel.current.setWidth(jexcel.current.resizing.column, newWidth, jexcel.current.resizing.width); + } + // Remove border + jexcel.current.headers[jexcel.current.resizing.column].classList.remove('resizing'); + for (var j = 0; j < jexcel.current.records.length; j++) { + if (jexcel.current.records[j][jexcel.current.resizing.column]) { + jexcel.current.records[j][jexcel.current.resizing.column].classList.remove('resizing'); + } + } + } else { + // Remove Class + jexcel.current.rows[jexcel.current.resizing.row].children[0].classList.remove('resizing'); + var newHeight = jexcel.current.rows[jexcel.current.resizing.row].getAttribute('height'); + jexcel.current.setHeight(jexcel.current.resizing.row, newHeight, jexcel.current.resizing.height); + // Remove border + jexcel.current.resizing.element.classList.remove('resizing'); + } + // Reset resizing helper + jexcel.current.resizing = null; + } else if (jexcel.current.dragging) { + // Reset dragging helper + if (jexcel.current.dragging) { + if (jexcel.current.dragging.column) { + // Target + var columnId = e.target.getAttribute('data-x'); + // Remove move style + jexcel.current.headers[jexcel.current.dragging.column].classList.remove('dragging'); + for (var j = 0; j < jexcel.current.rows.length; j++) { + if (jexcel.current.records[j][jexcel.current.dragging.column]) { + jexcel.current.records[j][jexcel.current.dragging.column].classList.remove('dragging'); + } + } + for (var i = 0; i < jexcel.current.headers.length; i++) { + jexcel.current.headers[i].classList.remove('dragging-left'); + jexcel.current.headers[i].classList.remove('dragging-right'); + } + // Update position + if (columnId) { + if (jexcel.current.dragging.column != jexcel.current.dragging.destination) { + jexcel.current.moveColumn(jexcel.current.dragging.column, jexcel.current.dragging.destination); + } + } + } else { + if (jexcel.current.dragging.element.nextSibling) { + var position = parseInt(jexcel.current.dragging.element.nextSibling.getAttribute('data-y')); + if (jexcel.current.dragging.row < position) { + position -= 1; + } + } else { + var position = parseInt(jexcel.current.dragging.element.previousSibling.getAttribute('data-y')); + } + if (jexcel.current.dragging.row != position) { + jexcel.current.moveRow(jexcel.current.dragging.row, position, true); + } + jexcel.current.dragging.element.classList.remove('dragging'); + } + jexcel.current.dragging = null; + } + } else { + // Close any corner selection + if (jexcel.current.selectedCorner) { + jexcel.current.selectedCorner = false; + + // Data to be copied + if (jexcel.current.selection.length > 0) { + // Copy data + jexcel.current.copyData(jexcel.current.selection[0], jexcel.current.selection[jexcel.current.selection.length - 1]); + + // Remove selection + jexcel.current.removeCopySelection(); + } + } + } + } + + // Clear any time control + if (jexcel.timeControl) { + clearTimeout(jexcel.timeControl); + jexcel.timeControl = null; + } + + // Mouse up + jexcel.isMouseAction = false; + } + + // Mouse move controls + jexcel.mouseMoveControls = function(e) { + e = e || window.event; + if (e.buttons) { + var mouseButton = e.buttons; + } else if (e.button) { + var mouseButton = e.button; + } else { + var mouseButton = e.which; + } + + if (! mouseButton) { + jexcel.isMouseAction = false; + } + + if (jexcel.current) { + if (jexcel.isMouseAction == true) { + // Resizing is ongoing + if (jexcel.current.resizing) { + if (jexcel.current.resizing.column) { + var width = e.pageX - jexcel.current.resizing.mousePosition; + + if (jexcel.current.resizing.width + width > 0) { + var tempWidth = jexcel.current.resizing.width + width; + jexcel.current.colgroup[jexcel.current.resizing.column].setAttribute('width', tempWidth); + + jexcel.current.updateCornerPosition(); + } + } else { + var height = e.pageY - jexcel.current.resizing.mousePosition; + + if (jexcel.current.resizing.height + height > 0) { + var tempHeight = jexcel.current.resizing.height + height; + jexcel.current.rows[jexcel.current.resizing.row].setAttribute('height', tempHeight); + + jexcel.current.updateCornerPosition(); + } + } + } + } else { + var x = e.target.getAttribute('data-x'); + var y = e.target.getAttribute('data-y'); + var rect = e.target.getBoundingClientRect(); + + if (jexcel.current.cursor) { + jexcel.current.cursor.style.cursor = ''; + jexcel.current.cursor = null; + } + + if (e.target.parentNode.parentNode && e.target.parentNode.parentNode.className) { + if (e.target.parentNode.parentNode.classList.contains('resizable')) { + if (e.target && x && ! y && (rect.width - (e.clientX - rect.left) < 6)) { + jexcel.current.cursor = e.target; + jexcel.current.cursor.style.cursor = 'col-resize'; + } else if (e.target && ! x && y && (rect.height - (e.clientY - rect.top) < 6)) { + jexcel.current.cursor = e.target; + jexcel.current.cursor.style.cursor = 'row-resize'; + } + } + + if (e.target.parentNode.parentNode.classList.contains('draggable')) { + if (e.target && ! x && y && (rect.width - (e.clientX - rect.left) < 6)) { + jexcel.current.cursor = e.target; + jexcel.current.cursor.style.cursor = 'move'; + } else if (e.target && x && ! y && (rect.height - (e.clientY - rect.top) < 6)) { + jexcel.current.cursor = e.target; + jexcel.current.cursor.style.cursor = 'move'; + } + } + } + } + } + } + + jexcel.mouseOverControls = function(e) { + e = e || window.event; + if (e.buttons) { + var mouseButton = e.buttons; + } else if (e.button) { + var mouseButton = e.button; + } else { + var mouseButton = e.which; + } + + if (! mouseButton) { + jexcel.isMouseAction = false; + } + + if (jexcel.current && jexcel.isMouseAction == true) { + // Get elements + var jexcelTable = jexcel.getElement(e.target); + + if (jexcelTable[0]) { + // Avoid cross reference + if (jexcel.current != jexcelTable[0].jexcel) { + if (jexcel.current) { + return false; + } + } + + var columnId = e.target.getAttribute('data-x'); + var rowId = e.target.getAttribute('data-y'); + + if (jexcel.current.dragging) { + if (jexcel.current.dragging.column) { + if (columnId) { + if (jexcel.current.isColMerged(columnId).length) { + console.error('JEXCEL: This column is part of a merged cell.'); + } else { + for (var i = 0; i < jexcel.current.headers.length; i++) { + jexcel.current.headers[i].classList.remove('dragging-left'); + jexcel.current.headers[i].classList.remove('dragging-right'); + } + + if (jexcel.current.dragging.column == columnId) { + jexcel.current.dragging.destination = parseInt(columnId); + } else { + if (e.target.clientWidth / 2 > e.offsetX) { + if (jexcel.current.dragging.column < columnId) { + jexcel.current.dragging.destination = parseInt(columnId) - 1; + } else { + jexcel.current.dragging.destination = parseInt(columnId); + } + jexcel.current.headers[columnId].classList.add('dragging-left'); + } else { + if (jexcel.current.dragging.column < columnId) { + jexcel.current.dragging.destination = parseInt(columnId); + } else { + jexcel.current.dragging.destination = parseInt(columnId) + 1; + } + jexcel.current.headers[columnId].classList.add('dragging-right'); + } + } + } + } + } else { + if (rowId) { + if (jexcel.current.isRowMerged(rowId).length) { + console.error('JEXCEL: This row is part of a merged cell.'); + } else { + var target = (e.target.clientHeight / 2 > e.offsetY) ? e.target.parentNode.nextSibling : e.target.parentNode; + e.target.parentNode.parentNode.insertBefore(jexcel.current.dragging.element, target); + } + } + } + } else if (jexcel.current.resizing) { + } else { + // Header found + if (jexcelTable[1] == 1) { + if (jexcel.current.selectedHeader) { + var columnId = e.target.getAttribute('data-x'); + var o = jexcel.current.selectedHeader; + var d = columnId; + // Update selection + jexcel.current.updateSelectionFromCoords(o, 0, d, jexcel.current.options.data.length - 1); + } + } + + // Body found + if (jexcelTable[1] == 2) { + if (e.target.classList.contains('jexcel_row')) { + if (jexcel.current.selectedRow) { + var o = jexcel.current.selectedRow; + var d = rowId; + // Update selection + jexcel.current.updateSelectionFromCoords(0, o, jexcel.current.options.data[0].length - 1, d); + } + } else { + // Do not select edtion is in progress + if (! jexcel.current.edition) { + if (columnId && rowId) { + if (jexcel.current.selectedCorner) { + jexcel.current.updateCopySelection(columnId, rowId); + } else { + if (jexcel.current.selectedCell) { + jexcel.current.updateSelectionFromCoords(jexcel.current.selectedCell[0], jexcel.current.selectedCell[1], columnId, rowId); + } + } + } + } + } + } + } + } + } + + // Clear any time control + if (jexcel.timeControl) { + clearTimeout(jexcel.timeControl); + jexcel.timeControl = null; + } + } + + /** + * Double click event handler: controls the double click in the corner, cell edition or column re-ordering. + */ + jexcel.doubleClickControls = function(e) { + // Jexcel is selected + if (jexcel.current) { + // Corner action + if (e.target.classList.contains('jexcel_corner')) { + // Any selected cells + if (jexcel.current.highlighted.length > 0) { + // Copy from this + var x1 = jexcel.current.highlighted[0].getAttribute('data-x'); + var y1 = parseInt(jexcel.current.highlighted[jexcel.current.highlighted.length - 1].getAttribute('data-y')) + 1; + // Until this + var x2 = jexcel.current.highlighted[jexcel.current.highlighted.length - 1].getAttribute('data-x'); + var y2 = jexcel.current.records.length - 1 + // Execute copy + jexcel.current.copyData(jexcel.current.records[y1][x1], jexcel.current.records[y2][x2]); + } + } else if (e.target.classList.contains('jexcel_column_filter')) { + // Column + var columnId = e.target.getAttribute('data-x'); + // Open filter + jexcel.current.openFilter(columnId); + + } else { + // Get table + var jexcelTable = jexcel.getElement(e.target); + + // Double click over header + if (jexcelTable[1] == 1 && jexcel.current.options.columnSorting == true) { + // Check valid column header coords + var columnId = e.target.getAttribute('data-x'); + if (columnId) { + jexcel.current.orderBy(columnId); + } + } + + // Double click over body + if (jexcelTable[1] == 2 && jexcel.current.options.editable == true) { + if (! jexcel.current.edition) { + var getCellCoords = function(element) { + if (element.parentNode) { + var x = element.getAttribute('data-x'); + var y = element.getAttribute('data-y'); + if (x && y) { + return element; + } else { + return getCellCoords(element.parentNode); + } + } + } + var cell = getCellCoords(e.target); + if (cell && cell.classList.contains('highlight')) { + jexcel.current.openEditor(cell); + } + } + } + } + } + } + + jexcel.copyControls = function(e) { + if (jexcel.current && jexcel.copyControls.enabled) { + if (! jexcel.current.edition) { + jexcel.current.copy(true); + } + } + } + + jexcel.cutControls = function(e) { + if (jexcel.current) { + if (! jexcel.current.edition) { + jexcel.current.copy(true); + if (jexcel.current.options.editable == true) { + jexcel.current.setValue(jexcel.current.highlighted, ''); + } + } + } + } + + jexcel.pasteControls = function(e) { + if (jexcel.current && jexcel.current.selectedCell) { + if (! jexcel.current.edition) { + if (jexcel.current.options.editable == true) { + if (e && e.clipboardData) { + jexcel.current.paste(jexcel.current.selectedCell[0], jexcel.current.selectedCell[1], e.clipboardData.getData('text')); + e.preventDefault(); + } else if (window.clipboardData) { + jexcel.current.paste(jexcel.current.selectedCell[0], jexcel.current.selectedCell[1], window.clipboardData.getData('text')); + } + } + } + } + } + + jexcel.contextMenuControls = function(e) { + e = e || window.event; + if ("buttons" in e) { + var mouseButton = e.buttons; + } else { + var mouseButton = e.which || e.button; + } + + if (jexcel.current) { + if (jexcel.current.edition) { + e.preventDefault(); + } else if (jexcel.current.options.contextMenu) { + jexcel.current.contextMenu.contextmenu.close(); + + if (jexcel.current) { + var x = e.target.getAttribute('data-x'); + var y = e.target.getAttribute('data-y'); + + if (x || y) { + // Table found + var items = jexcel.current.options.contextMenu(jexcel.current, x, y, e); + // The id is depending on header and body + jexcel.current.contextMenu.contextmenu.open(e, items); + // Avoid the real one + e.preventDefault(); + } + } + } + } + } + + jexcel.touchStartControls = function(e) { + var jexcelTable = jexcel.getElement(e.target); + + if (jexcelTable[0]) { + if (jexcel.current != jexcelTable[0].jexcel) { + if (jexcel.current) { + jexcel.current.resetSelection(); + } + jexcel.current = jexcelTable[0].jexcel; + } + } else { + if (jexcel.current) { + jexcel.current.resetSelection(); + jexcel.current = null; + } + } + + if (jexcel.current) { + if (! jexcel.current.edition) { + var columnId = e.target.getAttribute('data-x'); + var rowId = e.target.getAttribute('data-y'); + + if (columnId && rowId) { + jexcel.current.updateSelectionFromCoords(columnId, rowId); + + jexcel.timeControl = setTimeout(function() { + // Keep temporary reference to the element + if (jexcel.current.options.columns[columnId].type == 'color') { + jexcel.tmpElement = null; + } else { + jexcel.tmpElement = e.target; + } + jexcel.current.openEditor(e.target, false, e); + }, 500); + } + } + } + } + + jexcel.touchEndControls = function(e) { + // Clear any time control + if (jexcel.timeControl) { + clearTimeout(jexcel.timeControl); + jexcel.timeControl = null; + // Element + if (jexcel.tmpElement && jexcel.tmpElement.children[0].tagName == 'INPUT') { + jexcel.tmpElement.children[0].focus(); + } + jexcel.tmpElement = null; + } + } + + /** + * Jexcel extensions + */ + + jexcel.tabs = function(tabs, result) { + var instances = []; + // Create tab container + if (! tabs.classList.contains('jexcel_tabs')) { + tabs.innerHTML = ''; + tabs.classList.add('jexcel_tabs') + tabs.jexcel = []; + + var div = document.createElement('div'); + var headers = tabs.appendChild(div); + var div = document.createElement('div'); + var content = tabs.appendChild(div); + } else { + var headers = tabs.children[0]; + var content = tabs.children[1]; + } + + var spreadsheet = [] + var link = []; + for (var i = 0; i < result.length; i++) { + // Spreadsheet container + spreadsheet[i] = document.createElement('div'); + spreadsheet[i].classList.add('jexcel_tab'); + var worksheet = jexcel(spreadsheet[i], result[i]); + content.appendChild(spreadsheet[i]); + instances[i] = tabs.jexcel.push(worksheet); + + // Tab link + link[i] = document.createElement('div'); + link[i].classList.add('jexcel_tab_link'); + link[i].setAttribute('data-spreadsheet', tabs.jexcel.length-1); + link[i].innerHTML = result[i].sheetName; + link[i].onclick = function() { + for (var j = 0; j < headers.children.length; j++) { + headers.children[j].classList.remove('selected'); + content.children[j].style.display = 'none'; + } + var i = this.getAttribute('data-spreadsheet'); + content.children[i].style.display = 'block'; + headers.children[i].classList.add('selected') + } + headers.appendChild(link[i]); + } + + // First tab + for (var j = 0; j < headers.children.length; j++) { + headers.children[j].classList.remove('selected'); + content.children[j].style.display = 'none'; + } + headers.children[headers.children.length - 1].classList.add('selected'); + content.children[headers.children.length - 1].style.display = 'block'; + + return instances; + } + + // Compability to older versions + jexcel.createTabs = jexcel.tabs; + + jexcel.fromSpreadsheet = function(file, __callback) { + var convert = function(workbook) { + var spreadsheets = []; + workbook.SheetNames.forEach(function(sheetName) { + var spreadsheet = {}; + spreadsheet.rows = []; + spreadsheet.columns = []; + spreadsheet.data = []; + spreadsheet.style = {}; + spreadsheet.sheetName = sheetName; + + // Column widths + var temp = workbook.Sheets[sheetName]['!cols']; + if (temp && temp.length) { + for (var i = 0; i < temp.length; i++) { + spreadsheet.columns[i] = {}; + if (temp[i] && temp[i].wpx) { + spreadsheet.columns[i].width = temp[i].wpx + 'px'; + } + } + } + // Rows heights + var temp = workbook.Sheets[sheetName]['!rows']; + if (temp && temp.length) { + for (var i = 0; i < temp.length; i++) { + if (temp[i] && temp[i].hpx) { + spreadsheet.rows[i] = {}; + spreadsheet.rows[i].height = temp[i].hpx + 'px'; + } + } + } + // Merge cells + var temp = workbook.Sheets[sheetName]['!merges']; + if (temp && temp.length > 0) { + spreadsheet.mergeCells = []; + for (var i = 0; i < temp.length; i++) { + var x1 = temp[i].s.c; + var y1 = temp[i].s.r; + var x2 = temp[i].e.c; + var y2 = temp[i].e.r; + var key = jexcel.getColumnNameFromId([x1,y1]); + spreadsheet.mergeCells[key] = [ x2-x1+1, y2-y1+1 ]; + } + } + // Data container + var max_x = 0; + var max_y = 0; + var temp = Object.keys(workbook.Sheets[sheetName]); + for (var i = 0; i < temp.length; i++) { + if (temp[i].substr(0,1) != '!') { + var cell = workbook.Sheets[sheetName][temp[i]]; + var info = jexcel.getIdFromColumnName(temp[i], true); + if (! spreadsheet.data[info[1]]) { + spreadsheet.data[info[1]] = []; + } + spreadsheet.data[info[1]][info[0]] = cell.f ? '=' + cell.f : cell.w; + if (max_x < info[0]) { + max_x = info[0]; + } + if (max_y < info[1]) { + max_y = info[1]; + } + // Style + if (cell.style && Object.keys(cell.style).length > 0) { + spreadsheet.style[temp[i]] = cell.style; + } + if (cell.s && cell.s.fgColor) { + if (spreadsheet.style[temp[i]]) { + spreadsheet.style[temp[i]] += ';'; + } + spreadsheet.style[temp[i]] += 'background-color:#' + cell.s.fgColor.rgb; + } + } + } + var numColumns = spreadsheet.columns; + for (var j = 0; j <= max_y; j++) { + for (var i = 0; i <= max_x; i++) { + if (! spreadsheet.data[j]) { + spreadsheet.data[j] = []; + } + if (! spreadsheet.data[j][i]) { + if (numColumns < i) { + spreadsheet.data[j][i] = ''; + } + } + } + } + spreadsheets.push(spreadsheet); + }); + + return spreadsheets; + } + + var oReq; + oReq = new XMLHttpRequest(); + oReq.open("GET", file, true); + + if(typeof Uint8Array !== 'undefined') { + oReq.responseType = "arraybuffer"; + oReq.onload = function(e) { + var arraybuffer = oReq.response; + var data = new Uint8Array(arraybuffer); + var wb = XLSX.read(data, {type:"array", cellFormula:true, cellStyles:true }); + __callback(convert(wb)) + }; + } else { + oReq.setRequestHeader("Accept-Charset", "x-user-defined"); + oReq.onreadystatechange = function() { if(oReq.readyState == 4 && oReq.status == 200) { + var ff = convertResponseBodyToText(oReq.responseBody); + var wb = XLSX.read(ff, {type:"binary", cellFormula:true, cellStyles:true }); + __callback(convert(wb)) + }}; + } + + oReq.send(); + } + + /** + * Valid international letter + */ + + jexcel.validLetter = function (text) { + var regex = /([\u0041-\u005A\u0061-\u007A\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC-\u0400-\u04FF']+)/g; + return text.match(regex) ? 1 : 0; + } + + /** + * Helper injectArray + */ + jexcel.injectArray = function(o, idx, arr) { + return o.slice(0, idx).concat(arr).concat(o.slice(idx)); + } + + /** + * Get letter based on a number + * + * @param integer i + * @return string letter + */ + jexcel.getColumnName = function(i) { + var letter = ''; + if (i > 701) { + letter += String.fromCharCode(64 + parseInt(i / 676)); + letter += String.fromCharCode(64 + parseInt((i % 676) / 26)); + } else if (i > 25) { + letter += String.fromCharCode(64 + parseInt(i / 26)); + } + letter += String.fromCharCode(65 + (i % 26)); + + return letter; + } + + /** + * Convert excel like column to jexcel id + * + * @param string id + * @return string id + */ + jexcel.getIdFromColumnName = function (id, arr) { + // Get the letters + var t = /^[a-zA-Z]+/.exec(id); + + if (t) { + // Base 26 calculation + var code = 0; + for (var i = 0; i < t[0].length; i++) { + code += parseInt(t[0].charCodeAt(i) - 64) * Math.pow(26, (t[0].length - 1 - i)); + } + code--; + // Make sure jexcel starts on zero + if (code < 0) { + code = 0; + } + + // Number + var number = parseInt(/[0-9]+$/.exec(id)); + if (number > 0) { + number--; + } + + if (arr == true) { + id = [ code, number ]; + } else { + id = code + '-' + number; + } + } + + return id; + } + + /** + * Convert jexcel id to excel like column name + * + * @param string id + * @return string id + */ + jexcel.getColumnNameFromId = function (cellId) { + if (! Array.isArray(cellId)) { + cellId = cellId.split('-'); + } + + return jexcel.getColumnName(parseInt(cellId[0])) + (parseInt(cellId[1]) + 1); + } + + /** + * Verify element inside jexcel table + * + * @param string id + * @return string id + */ + jexcel.getElement = function(element) { + var jexcelSection = 0; + var jexcelElement = 0; + + function path (element) { + if (element.className) { + if (element.classList.contains('jexcel_container')) { + jexcelElement = element; + } + } + + if (element.tagName == 'THEAD') { + jexcelSection = 1; + } else if (element.tagName == 'TBODY') { + jexcelSection = 2; + } + + if (element.parentNode) { + if (! jexcelElement) { + path(element.parentNode); + } + } + } + + path(element); + + return [ jexcelElement, jexcelSection ]; + } + + jexcel.doubleDigitFormat = function(v) { + v = ''+v; + if (v.length == 1) { + v = '0'+v; + } + return v; + } + + jexcel.createFromTable = function(el, options) { + if (el.tagName != 'TABLE') { + console.log('Element is not a table'); + } else { + // Configuration + if (! options) { + options = {}; + } + options.columns = []; + options.data = []; + + // Colgroup + var colgroup = el.querySelectorAll('colgroup > col'); + if (colgroup.length) { + // Get column width + for (var i = 0; i < colgroup.length; i++) { + var width = colgroup[i].style.width; + if (! width) { + var width = colgroup[i].getAttribute('width'); + } + // Set column width + if (width) { + if (! options.columns[i]) { + options.columns[i] = {} + } + options.columns[i].width = width; + } + } + } + + // Parse header + var parseHeader = function(header) { + // Get width information + var info = header.getBoundingClientRect(); + var width = info.width > 50 ? info.width : 50; + + // Create column option + if (! options.columns[i]) { + options.columns[i] = {}; + } + if (header.getAttribute('data-celltype')) { + options.columns[i].type = header.getAttribute('data-celltype'); + } else { + options.columns[i].type = 'text'; + } + options.columns[i].width = width + 'px'; + options.columns[i].title = header.innerText; + options.columns[i].align = header.style.textAlign || 'center'; + } + + // Headers + var headers = el.querySelectorAll('thead > tr'); + if (headers.length) { + // Get the last row in the thead + headers = headers[headers.length-1].children; + // Go though the headers + for (var i = 0; i < headers.length; i++) { + parseHeader(headers[i]); + } + } + + // Content + var rowNumber = 0; + var mergeCells = {}; + var rows = {}; + var style = {}; + + var content = el.querySelectorAll('table > tr, tbody tr'); + for (var j = 0; j < content.length; j++) { + options.data[rowNumber] = []; + if (options.parseTableFirstRowAsHeader == true && j == 0) { + for (var i = 0; i < content[j].children.length; i++) { + parseHeader(content[j].children[i]); + } + } else { + for (var i = 0; i < content[j].children.length; i++) { + // WickedGrid formula compatibility + var value = content[j].children[i].getAttribute('formula'); + if (!value) { + value = content[j].children[i].getAttribute('data-formula'); + } + if (value) { + if (value.substr(0,1) != '=') { + value = '=' + value; + } + } else { + var value = content[j].children[i].innerText; + } + options.data[rowNumber].push(value); + + // Key + var cellName = jexcel.getColumnNameFromId([ i, j ]); + + // Merged cells + var mergedColspan = parseInt(content[j].children[i].getAttribute('colspan')) || 0; + var mergedRowspan = parseInt(content[j].children[i].getAttribute('rowspan')) || 0; + if (mergedColspan || mergedRowspan) { + mergeCells[cellName] = [ mergedColspan || 1, mergedRowspan || 1 ]; + } + + // Avoid problems with hidden cells + if (s = content[j].children[i].style && content[j].children[i].style.display == 'none') { + content[j].children[i].style.display = ''; + } + // Get style + var s = content[j].children[i].getAttribute('style'); + if (s) { + style[cellName] = s; + } + // Bold + if (content[j].children[i].classList.contains('styleBold') || content[j].children[i].classList.contains('style-bold')) { + if (style[cellName]) { + style[cellName] += '; font-weight:bold;'; + } else { + style[cellName] = 'font-weight:bold;'; + } + } + // Italic + if (content[j].children[i].classList.contains('styleItalics') || content[j].children[i].classList.contains('style-italics')) { + if (style[cellName]) { + style[cellName] += '; font-style: italic;'; + } else { + style[cellName] = 'font-style: italic;'; + } + } + // Underlined + if (content[j].children[i].classList.contains('styleUnderline') || content[j].children[i].classList.contains('style-underline')) { + if (style[cellName]) { + style[cellName] += '; text-decoration: underline;'; + } else { + style[cellName] = 'text-decoration: underline;'; + } + } + // Line through + if (content[j].children[i].classList.contains('style-line-through')) { + if (style[cellName]) { + style[cellName] += '; text-decoration: line-through;'; + } else { + style[cellName] = 'text-decoration: line-through;'; + } + } + // Align left + if (content[j].children[i].classList.contains('styleLeft')) { + if (style[cellName]) { + style[cellName] += '; text-align: left;'; + } else { + style[cellName] = 'text-align: left;'; + } + } + // Align right + if (content[j].children[i].classList.contains('styleRight')) { + if (style[cellName]) { + style[cellName] += '; text-align: right;'; + } else { + style[cellName] = 'text-align: right;'; + } + } + } + + // Row Height + if (content[j].style && content[j].style.height) { + rows[j] = { height: content[j].style.height }; + } + + // Index + rowNumber++; + } + } + + // Style + if (Object.keys(style).length > 0) { + options.style = style; + } + // Merged + if (Object.keys(mergeCells).length > 0) { + options.mergeCells = mergeCells; + } + // Row height + if (Object.keys(rows).length > 0) { + options.rows = rows; + } + + // TODO: data-hiddencolumns="3,4" + + // I guess in terms the better column type + if (options.parseTableAutoCellType == true) { + var pattern = []; + for (var i = 0; i < options.columns.length; i++) { + var test = true; + var testCalendar = true; + pattern[i] = []; + for (var j = 0; j < options.data.length; j++) { + var value = options.data[j][i]; + if (! pattern[i][value]) { + pattern[i][value] = 0; + } + pattern[i][value]++; + if (value.length > 25) { + test = false; + } + if (value.length == 10) { + if (! (value.substr(4,1) == '-' && value.substr(7,1) == '-')) { + testCalendar = false; + } + } else { + testCalendar = false; + } + } + + var keys = Object.keys(pattern[i]).length; + if (testCalendar) { + options.columns[i].type = 'calendar'; + } else if (test == true && keys > 1 && keys <= parseInt(options.data.length * 0.1)) { + options.columns[i].type = 'dropdown'; + options.columns[i].source = Object.keys(pattern[i]); + } + } + } + + return options; + } + } + + /** + * Jquery Support + */ + if (typeof(jQuery) != 'undefined') { + (function($){ + $.fn.jexcel = function(method) { + var spreadsheetContainer = $(this).get(0); + if (! spreadsheetContainer.jexcel) { + return jexcel($(this).get(0), arguments[0]); + } else { + return spreadsheetContainer.jexcel[method].apply(this, Array.prototype.slice.call( arguments, 1 )); + } + }; + + })(jQuery); + } + + + // Based on sutoiku work (https://github.com/sutoiku) + + var error = (function() { + var exports = {}; + + exports.nil = new Error('#NULL!'); + exports.div0 = new Error('#DIV/0!'); + exports.value = new Error('#VALUE!'); + exports.ref = new Error('#REF!'); + exports.name = new Error('#NAME?'); + exports.num = new Error('#NUM!'); + exports.na = new Error('#N/A'); + exports.error = new Error('#ERROR!'); + exports.data = new Error('#GETTING_DATA'); + + return exports; + })(); + + var utils = (function() { + var exports = {}; + + exports.flattenShallow = function(array) { + if (!array || !array.reduce) { + return array; + } + + return array.reduce(function(a, b) { + var aIsArray = Array.isArray(a); + var bIsArray = Array.isArray(b); + + if (aIsArray && bIsArray) { + return a.concat(b); + } + if (aIsArray) { + a.push(b); + + return a; + } + if (bIsArray) { + return [ a ].concat(b); + } + + return [ a, b ]; + }); + }; + + exports.isFlat = function(array) { + if (!array) { + return false; + } + + for (var i = 0; i < array.length; ++i) { + if (Array.isArray(array[i])) { + return false; + } + } + + return true; + }; + + exports.flatten = function() { + var result = exports.argsToArray.apply(null, arguments); + + while (!exports.isFlat(result)) { + result = exports.flattenShallow(result); + } + + return result; + }; + + exports.argsToArray = function(args) { + var result = []; + + exports.arrayEach(args, function(value) { + result.push(value); + }); + + return result; + }; + + exports.numbers = function() { + var possibleNumbers = this.flatten.apply(null, arguments); + return possibleNumbers.filter(function(el) { + return typeof el === 'number'; + }); + }; + + exports.cleanFloat = function(number) { + var power = 1e14; + return Math.round(number * power) / power; + }; + + exports.parseBool = function(bool) { + if (typeof bool === 'boolean') { + return bool; + } + + if (bool instanceof Error) { + return bool; + } + + if (typeof bool === 'number') { + return bool !== 0; + } + + if (typeof bool === 'string') { + var up = bool.toUpperCase(); + if (up === 'TRUE') { + return true; + } + + if (up === 'FALSE') { + return false; + } + } + + if (bool instanceof Date && !isNaN(bool)) { + return true; + } + + return error.value; + }; + + exports.parseNumber = function(string) { + if (string === undefined || string === '') { + return error.value; + } + if (!isNaN(string)) { + return parseFloat(string); + } + + return error.value; + }; + + exports.parseNumberArray = function(arr) { + var len; + + if (!arr || (len = arr.length) === 0) { + return error.value; + } + + var parsed; + + while (len--) { + parsed = exports.parseNumber(arr[len]); + if (parsed === error.value) { + return parsed; + } + arr[len] = parsed; + } + + return arr; + }; + + exports.parseMatrix = function(matrix) { + var n; + + if (!matrix || (n = matrix.length) === 0) { + return error.value; + } + var pnarr; + + for (var i = 0; i < matrix.length; i++) { + pnarr = exports.parseNumberArray(matrix[i]); + matrix[i] = pnarr; + + if (pnarr instanceof Error) { + return pnarr; + } + } + + return matrix; + }; + + var d1900 = new Date(Date.UTC(1900, 0, 1)); + exports.parseDate = function(date) { + if (!isNaN(date)) { + if (date instanceof Date) { + return new Date(date); + } + var d = parseInt(date, 10); + if (d < 0) { + return error.num; + } + if (d <= 60) { + return new Date(d1900.getTime() + (d - 1) * 86400000); + } + return new Date(d1900.getTime() + (d - 2) * 86400000); + } + if (typeof date === 'string') { + date = new Date(date); + if (!isNaN(date)) { + return date; + } + } + return error.value; + }; + + exports.parseDateArray = function(arr) { + var len = arr.length; + var parsed; + while (len--) { + parsed = this.parseDate(arr[len]); + if (parsed === error.value) { + return parsed; + } + arr[len] = parsed; + } + return arr; + }; + + exports.anyIsError = function() { + var n = arguments.length; + while (n--) { + if (arguments[n] instanceof Error) { + return true; + } + } + return false; + }; + + exports.arrayValuesToNumbers = function(arr) { + var n = arr.length; + var el; + while (n--) { + el = arr[n]; + if (typeof el === 'number') { + continue; + } + if (el === true) { + arr[n] = 1; + continue; + } + if (el === false) { + arr[n] = 0; + continue; + } + if (typeof el === 'string') { + var number = this.parseNumber(el); + if (number instanceof Error) { + arr[n] = 0; + } else { + arr[n] = number; + } + } + } + return arr; + }; + + exports.rest = function(array, idx) { + idx = idx || 1; + if (!array || typeof array.slice !== 'function') { + return array; + } + return array.slice(idx); + }; + + exports.initial = function(array, idx) { + idx = idx || 1; + if (!array || typeof array.slice !== 'function') { + return array; + } + return array.slice(0, array.length - idx); + }; + + exports.arrayEach = function(array, iteratee) { + var index = -1, length = array.length; + + while (++index < length) { + if (iteratee(array[index], index, array) === false) { + break; + } + } + + return array; + }; + + exports.transpose = function(matrix) { + if (!matrix) { + return error.value; + } + + return matrix[0].map(function(col, i) { + return matrix.map(function(row) { + return row[i]; + }); + }); + }; + + return exports; + })(); + + jexcel.methods = {}; + + jexcel.methods.datetime = (function() { + var exports = {}; + + var d1900 = new Date(1900, 0, 1); + var WEEK_STARTS = [ + undefined, + 0, + 1, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + 1, + 2, + 3, + 4, + 5, + 6, + 0 + ]; + var WEEK_TYPES = [ + [], + [1, 2, 3, 4, 5, 6, 7], + [7, 1, 2, 3, 4, 5, 6], + [6, 0, 1, 2, 3, 4, 5], + [], + [], + [], + [], + [], + [], + [], + [7, 1, 2, 3, 4, 5, 6], + [6, 7, 1, 2, 3, 4, 5], + [5, 6, 7, 1, 2, 3, 4], + [4, 5, 6, 7, 1, 2, 3], + [3, 4, 5, 6, 7, 1, 2], + [2, 3, 4, 5, 6, 7, 1], + [1, 2, 3, 4, 5, 6, 7] + ]; + var WEEKEND_TYPES = [ + [], + [6, 0], + [0, 1], + [1, 2], + [2, 3], + [3, 4], + [4, 5], + [5, 6], + undefined, + undefined, + undefined, [0, 0], + [1, 1], + [2, 2], + [3, 3], + [4, 4], + [5, 5], + [6, 6] + ]; + + exports.DATE = function(year, month, day) { + year = utils.parseNumber(year); + month = utils.parseNumber(month); + day = utils.parseNumber(day); + if (utils.anyIsError(year, month, day)) { + return error.value; + } + if (year < 0 || month < 0 || day < 0) { + return error.num; + } + var date = new Date(year, month - 1, day); + return date; + }; + + exports.DATEVALUE = function(date_text) { + if (typeof date_text !== 'string') { + return error.value; + } + var date = Date.parse(date_text); + if (isNaN(date)) { + return error.value; + } + if (date <= -2203891200000) { + return (date - d1900) / 86400000 + 1; + } + return (date - d1900) / 86400000 + 2; + }; + + exports.DAY = function(serial_number) { + var date = utils.parseDate(serial_number); + if (date instanceof Error) { + return date; + } + return date.getDate(); + }; + + exports.DAYS = function(end_date, start_date) { + end_date = utils.parseDate(end_date); + start_date = utils.parseDate(start_date); + if (end_date instanceof Error) { + return end_date; + } + if (start_date instanceof Error) { + return start_date; + } + return serial(end_date) - serial(start_date); + }; + + exports.DAYS360 = function(start_date, end_date, method) { + }; + + exports.EDATE = function(start_date, months) { + start_date = utils.parseDate(start_date); + if (start_date instanceof Error) { + return start_date; + } + if (isNaN(months)) { + return error.value; + } + months = parseInt(months, 10); + start_date.setMonth(start_date.getMonth() + months); + return serial(start_date); + }; + + exports.EOMONTH = function(start_date, months) { + start_date = utils.parseDate(start_date); + if (start_date instanceof Error) { + return start_date; + } + if (isNaN(months)) { + return error.value; + } + months = parseInt(months, 10); + return serial(new Date(start_date.getFullYear(), start_date.getMonth() + months + 1, 0)); + }; + + exports.HOUR = function(serial_number) { + serial_number = utils.parseDate(serial_number); + if (serial_number instanceof Error) { + return serial_number; + } + return serial_number.getHours(); + }; + + exports.INTERVAL = function(second) { + if (typeof second !== 'number' && typeof second !== 'string') { + return error.value; + } else { + second = parseInt(second, 10); + } + + var year = Math.floor(second/946080000); + second = second%946080000; + var month = Math.floor(second/2592000); + second = second%2592000; + var day = Math.floor(second/86400); + second = second%86400; + + var hour = Math.floor(second/3600); + second = second%3600; + var min = Math.floor(second/60); + second = second%60; + var sec = second; + + year = (year > 0) ? year + 'Y' : ''; + month = (month > 0) ? month + 'M' : ''; + day = (day > 0) ? day + 'D' : ''; + hour = (hour > 0) ? hour + 'H' : ''; + min = (min > 0) ? min + 'M' : ''; + sec = (sec > 0) ? sec + 'S' : ''; + + return 'P' + year + month + day + 'T' + hour + min + sec; + }; + + exports.ISOWEEKNUM = function(date) { + date = utils.parseDate(date); + if (date instanceof Error) { + return date; + } + + date.setHours(0, 0, 0); + date.setDate(date.getDate() + 4 - (date.getDay() || 7)); + var yearStart = new Date(date.getFullYear(), 0, 1); + return Math.ceil((((date - yearStart) / 86400000) + 1) / 7); + }; + + exports.MINUTE = function(serial_number) { + serial_number = utils.parseDate(serial_number); + if (serial_number instanceof Error) { + return serial_number; + } + return serial_number.getMinutes(); + }; + + exports.MONTH = function(serial_number) { + serial_number = utils.parseDate(serial_number); + if (serial_number instanceof Error) { + return serial_number; + } + return serial_number.getMonth() + 1; + }; + + exports.NETWORKDAYS = function(start_date, end_date, holidays) { + }; + + exports.NETWORKDAYS.INTL = function(start_date, end_date, weekend, holidays) { + }; + + exports.NOW = function() { + return new Date(); + }; + + exports.SECOND = function(serial_number) { + serial_number = utils.parseDate(serial_number); + if (serial_number instanceof Error) { + return serial_number; + } + return serial_number.getSeconds(); + }; + + exports.TIME = function(hour, minute, second) { + hour = utils.parseNumber(hour); + minute = utils.parseNumber(minute); + second = utils.parseNumber(second); + if (utils.anyIsError(hour, minute, second)) { + return error.value; + } + if (hour < 0 || minute < 0 || second < 0) { + return error.num; + } + return (3600 * hour + 60 * minute + second) / 86400; + }; + + exports.TIMEVALUE = function(time_text) { + time_text = utils.parseDate(time_text); + if (time_text instanceof Error) { + return time_text; + } + return (3600 * time_text.getHours() + 60 * time_text.getMinutes() + time_text.getSeconds()) / 86400; + }; + + exports.TODAY = function() { + return new Date(); + }; + + exports.WEEKDAY = function(serial_number, return_type) { + serial_number = utils.parseDate(serial_number); + if (serial_number instanceof Error) { + return serial_number; + } + if (return_type === undefined) { + return_type = 1; + } + var day = serial_number.getDay(); + return WEEK_TYPES[return_type][day]; + }; + + exports.WEEKNUM = function(serial_number, return_type) { + }; + + exports.WORKDAY = function(start_date, days, holidays) { + }; + + exports.WORKDAY.INTL = function(start_date, days, weekend, holidays) { + }; + + exports.YEAR = function(serial_number) { + serial_number = utils.parseDate(serial_number); + if (serial_number instanceof Error) { + return serial_number; + } + return serial_number.getFullYear(); + }; + + function isLeapYear(year) { + return new Date(year, 1, 29).getMonth() === 1; + } + + exports.YEARFRAC = function(start_date, end_date, basis) { + }; + + function serial(date) { + var addOn = (date > -2203891200000)?2:1; + return (date - d1900) / 86400000 + addOn; + } + + return exports; + })(); + + jexcel.methods.database = (function() { + var exports = {}; + + function compact(array) { + if (!array) { + return array; + } + var result = []; + for (var i = 0; i < array.length; ++i) { + if (!array[i]) { + continue; + } + result.push(array[i]); + } + return result; + } + + exports.FINDFIELD = function(database, title) { + var index = null; + for (var i = 0; i < database.length; i++) { + if (database[i][0] === title) { + index = i; + break; + } + } + + // Return error if the input field title is incorrect + if (index == null) { + return error.value; + } + return index; + }; + + function findResultIndex(database, criterias) { + var matches = {}; + for (var i = 1; i < database[0].length; ++i) { + matches[i] = true; + } + var maxCriteriaLength = criterias[0].length; + for (i = 1; i < criterias.length; ++i) { + if (criterias[i].length > maxCriteriaLength) { + maxCriteriaLength = criterias[i].length; + } + } + + for (var k = 1; k < database.length; ++k) { + for (var l = 1; l < database[k].length; ++l) { + var currentCriteriaResult = false; + var hasMatchingCriteria = false; + for (var j = 0; j < criterias.length; ++j) { + var criteria = criterias[j]; + if (criteria.length < maxCriteriaLength) { + continue; + } + + var criteriaField = criteria[0]; + if (database[k][0] !== criteriaField) { + continue; + } + hasMatchingCriteria = true; + for (var p = 1; p < criteria.length; ++p) { + currentCriteriaResult = currentCriteriaResult + || eval(database[k][l] + criteria[p]); // jshint + // ignore:line + } + } + if (hasMatchingCriteria) { + matches[l] = matches[l] && currentCriteriaResult; + } + } + } + + var result = []; + for (var n = 0; n < database[0].length; ++n) { + if (matches[n]) { + result.push(n - 1); + } + } + return result; + } + + // Database functions + exports.DAVERAGE = function(database, field, criteria) { + // Return error if field is not a number and not a string + if (isNaN(field) && (typeof field !== "string")) { + return error.value; + } + var resultIndexes = findResultIndex(database, criteria); + var targetFields = []; + if (typeof field === "string") { + var index = exports.FINDFIELD(database, field); + targetFields = utils.rest(database[index]); + } else { + targetFields = utils.rest(database[field]); + } + var sum = 0; + for (var i = 0; i < resultIndexes.length; i++) { + sum += targetFields[resultIndexes[i]]; + } + return resultIndexes.length === 0 ? error.div0 : sum / resultIndexes.length; + }; + + exports.DCOUNT = function(database, field, criteria) { + }; + + exports.DCOUNTA = function(database, field, criteria) { + }; + + exports.DGET = function(database, field, criteria) { + // Return error if field is not a number and not a string + if (isNaN(field) && (typeof field !== "string")) { + return error.value; + } + var resultIndexes = findResultIndex(database, criteria); + var targetFields = []; + if (typeof field === "string") { + var index = exports.FINDFIELD(database, field); + targetFields = utils.rest(database[index]); + } else { + targetFields = utils.rest(database[field]); + } + // Return error if no record meets the criteria + if (resultIndexes.length === 0) { + return error.value; + } + // Returns the #NUM! error value because more than one record meets the + // criteria + if (resultIndexes.length > 1) { + return error.num; + } + + return targetFields[resultIndexes[0]]; + }; + + exports.DMAX = function(database, field, criteria) { + // Return error if field is not a number and not a string + if (isNaN(field) && (typeof field !== "string")) { + return error.value; + } + var resultIndexes = findResultIndex(database, criteria); + var targetFields = []; + if (typeof field === "string") { + var index = exports.FINDFIELD(database, field); + targetFields = utils.rest(database[index]); + } else { + targetFields = utils.rest(database[field]); + } + var maxValue = targetFields[resultIndexes[0]]; + for (var i = 1; i < resultIndexes.length; i++) { + if (maxValue < targetFields[resultIndexes[i]]) { + maxValue = targetFields[resultIndexes[i]]; + } + } + return maxValue; + }; + + exports.DMIN = function(database, field, criteria) { + // Return error if field is not a number and not a string + if (isNaN(field) && (typeof field !== "string")) { + return error.value; + } + var resultIndexes = findResultIndex(database, criteria); + var targetFields = []; + if (typeof field === "string") { + var index = exports.FINDFIELD(database, field); + targetFields = utils.rest(database[index]); + } else { + targetFields = utils.rest(database[field]); + } + var minValue = targetFields[resultIndexes[0]]; + for (var i = 1; i < resultIndexes.length; i++) { + if (minValue > targetFields[resultIndexes[i]]) { + minValue = targetFields[resultIndexes[i]]; + } + } + return minValue; + }; + + exports.DPRODUCT = function(database, field, criteria) { + // Return error if field is not a number and not a string + if (isNaN(field) && (typeof field !== "string")) { + return error.value; + } + var resultIndexes = findResultIndex(database, criteria); + var targetFields = []; + if (typeof field === "string") { + var index = exports.FINDFIELD(database, field); + targetFields = utils.rest(database[index]); + } else { + targetFields = utils.rest(database[field]); + } + var targetValues = []; + for (var i = 0; i < resultIndexes.length; i++) { + targetValues[i] = targetFields[resultIndexes[i]]; + } + targetValues = compact(targetValues); + var result = 1; + for (i = 0; i < targetValues.length; i++) { + result *= targetValues[i]; + } + return result; + }; + + exports.DSTDEV = function(database, field, criteria) { + }; + + exports.DSTDEVP = function(database, field, criteria) { + }; + + exports.DSUM = function(database, field, criteria) { + }; + + exports.DVAR = function(database, field, criteria) { + }; + + exports.DVARP = function(database, field, criteria) { + }; + + exports.MATCH = function(lookupValue, lookupArray, matchType) { + if (!lookupValue && !lookupArray) { + return error.na; + } + if (arguments.length === 2) { + matchType = 1; + } + if (!(lookupArray instanceof Array)) { + return error.na; + } + if (matchType !== -1 && matchType !== 0 && matchType !== 1) { + return error.na; + } + + var index; + var indexValue; + + for (var idx = 0; idx < lookupArray.length; idx++) { + if (matchType === 1) { + if (lookupArray[idx] === lookupValue) { + return idx + 1; + } else if (lookupArray[idx] < lookupValue) { + if (!indexValue) { + index = idx + 1; + indexValue = lookupArray[idx]; + } else if (lookupArray[idx] > indexValue) { + index = idx + 1; + indexValue = lookupArray[idx]; + } + } + } else if (matchType === 0) { + if (typeof lookupValue === 'string') { + lookupValue = lookupValue.replace(/\?/g, '.'); + if (lookupArray[idx].toLowerCase().match(lookupValue.toLowerCase())) { + return idx + 1; + } + } else { + if (lookupArray[idx] === lookupValue) { + return idx + 1; + } + } + } else if (matchType === -1) { + if (lookupArray[idx] === lookupValue) { + return idx + 1; + } else if (lookupArray[idx] > lookupValue) { + if (!indexValue) { + index = idx + 1; + indexValue = lookupArray[idx]; + } else if (lookupArray[idx] < indexValue) { + index = idx + 1; + indexValue = lookupArray[idx]; + } + } + } + } + + return index ? index : error.na; + }; + + return exports; + })(); + + jexcel.methods.engineering = (function() { + var exports = {}; + + function isValidBinaryNumber(number) { + return (/^[01]{1,10}$/).test(number); + } + + exports.BESSELI = function(x, n) { + }; + + exports.BESSELJ = function(x, n) { + }; + + exports.BESSELK = function(x, n) { + }; + + exports.BESSELY = function(x, n) { + }; + + exports.BIN2DEC = function(number) { + // Return error if number is not binary or contains more than 10 + // characters (10 digits) + if (!isValidBinaryNumber(number)) { + return error.num; + } + + // Convert binary number to decimal + var result = parseInt(number, 2); + + // Handle negative numbers + var stringified = number.toString(); + if (stringified.length === 10 && stringified.substring(0, 1) === '1') { + return parseInt(stringified.substring(1), 2) - 512; + } else { + return result; + } + }; + + exports.BIN2HEX = function(number, places) { + // Return error if number is not binary or contains more than 10 + // characters (10 digits) + if (!isValidBinaryNumber(number)) { + return error.num; + } + + // Ignore places and return a 10-character hexadecimal number if number + // is negative + var stringified = number.toString(); + if (stringified.length === 10 && stringified.substring(0, 1) === '1') { + return (1099511627264 + parseInt(stringified.substring(1), 2)).toString(16); + } + + // Convert binary number to hexadecimal + var result = parseInt(number, 2).toString(16); + + // Return hexadecimal number using the minimum number of characters + // necessary if places is undefined + if (places === undefined) { + return result; + } else { + // Return error if places is nonnumeric + if (isNaN(places)) { + return error.value; + } + + // Return error if places is negative + if (places < 0) { + return error.num; + } + + // Truncate places in case it is not an integer + places = Math.floor(places); + + // Pad return value with leading 0s (zeros) if necessary (using + // Underscore.string) + return (places >= result.length) ? REPT('0', places - result.length) + result : error.num; + } + }; + + exports.BIN2OCT = function(number, places) { + // Return error if number is not binary or contains more than 10 + // characters (10 digits) + if (!isValidBinaryNumber(number)) { + return error.num; + } + + // Ignore places and return a 10-character octal number if number is + // negative + var stringified = number.toString(); + if (stringified.length === 10 && stringified.substring(0, 1) === '1') { + return (1073741312 + parseInt(stringified.substring(1), 2)).toString(8); + } + + // Convert binary number to octal + var result = parseInt(number, 2).toString(8); + + // Return octal number using the minimum number of characters necessary + // if places is undefined + if (places === undefined) { + return result; + } else { + // Return error if places is nonnumeric + if (isNaN(places)) { + return error.value; + } + + // Return error if places is negative + if (places < 0) { + return error.num; + } + + // Truncate places in case it is not an integer + places = Math.floor(places); + + // Pad return value with leading 0s (zeros) if necessary (using + // Underscore.string) + return (places >= result.length) ? REPT('0', places - result.length) + result : error.num; + } + }; + + exports.BITAND = function(number1, number2) { + // Return error if either number is a non-numeric value + number1 = utils.parseNumber(number1); + number2 = utils.parseNumber(number2); + if (utils.anyIsError(number1, number2)) { + return error.value; + } + + // Return error if either number is less than 0 + if (number1 < 0 || number2 < 0) { + return error.num; + } + + // Return error if either number is a non-integer + if (Math.floor(number1) !== number1 || Math.floor(number2) !== number2) { + return error.num; + } + + // Return error if either number is greater than (2^48)-1 + if (number1 > 281474976710655 || number2 > 281474976710655) { + return error.num; + } + + // Return bitwise AND of two numbers + return number1 & number2; + }; + + exports.BITLSHIFT = function(number, shift) { + number = utils.parseNumber(number); + shift = utils.parseNumber(shift); + if (utils.anyIsError(number, shift)) { + return error.value; + } + + // Return error if number is less than 0 + if (number < 0) { + return error.num; + } + + // Return error if number is a non-integer + if (Math.floor(number) !== number) { + return error.num; + } + + // Return error if number is greater than (2^48)-1 + if (number > 281474976710655) { + return error.num; + } + + // Return error if the absolute value of shift is greater than 53 + if (Math.abs(shift) > 53) { + return error.num; + } + + // Return number shifted by shift bits to the left or to the right if + // shift is negative + return (shift >= 0) ? number << shift : number >> -shift; + }; + + exports.BITOR = function(number1, number2) { + number1 = utils.parseNumber(number1); + number2 = utils.parseNumber(number2); + if (utils.anyIsError(number1, number2)) { + return error.value; + } + + // Return error if either number is less than 0 + if (number1 < 0 || number2 < 0) { + return error.num; + } + + // Return error if either number is a non-integer + if (Math.floor(number1) !== number1 || Math.floor(number2) !== number2) { + return error.num; + } + + // Return error if either number is greater than (2^48)-1 + if (number1 > 281474976710655 || number2 > 281474976710655) { + return error.num; + } + + // Return bitwise OR of two numbers + return number1 | number2; + }; + + exports.BITRSHIFT = function(number, shift) { + number = utils.parseNumber(number); + shift = utils.parseNumber(shift); + if (utils.anyIsError(number, shift)) { + return error.value; + } + + // Return error if number is less than 0 + if (number < 0) { + return error.num; + } + + // Return error if number is a non-integer + if (Math.floor(number) !== number) { + return error.num; + } + + // Return error if number is greater than (2^48)-1 + if (number > 281474976710655) { + return error.num; + } + + // Return error if the absolute value of shift is greater than 53 + if (Math.abs(shift) > 53) { + return error.num; + } + + // Return number shifted by shift bits to the right or to the left if + // shift is negative + return (shift >= 0) ? number >> shift : number << -shift; + }; + + exports.BITXOR = function(number1, number2) { + number1 = utils.parseNumber(number1); + number2 = utils.parseNumber(number2); + if (utils.anyIsError(number1, number2)) { + return error.value; + } + + // Return error if either number is less than 0 + if (number1 < 0 || number2 < 0) { + return error.num; + } + + // Return error if either number is a non-integer + if (Math.floor(number1) !== number1 || Math.floor(number2) !== number2) { + return error.num; + } + + // Return error if either number is greater than (2^48)-1 + if (number1 > 281474976710655 || number2 > 281474976710655) { + return error.num; + } + + // Return bitwise XOR of two numbers + return number1 ^ number2; + }; + + exports.COMPLEX = function(real, imaginary, suffix) { + real = utils.parseNumber(real); + imaginary = utils.parseNumber(imaginary); + if (utils.anyIsError(real, imaginary)) { + return real; + } + + // Set suffix + suffix = (suffix === undefined) ? 'i' : suffix; + + // Return error if suffix is neither "i" nor "j" + if (suffix !== 'i' && suffix !== 'j') { + return error.value; + } + + // Return complex number + if (real === 0 && imaginary === 0) { + return 0; + } else if (real === 0) { + return (imaginary === 1) ? suffix : imaginary.toString() + suffix; + } else if (imaginary === 0) { + return real.toString(); + } else { + var sign = (imaginary > 0) ? '+' : ''; + return real.toString() + sign + ((imaginary === 1) ? suffix : imaginary.toString() + suffix); + } + }; + + exports.CONVERT = function(number, from_unit, to_unit) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + + // List of units supported by CONVERT and units defined by the + // International System of Units + // [Name, Symbol, Alternate symbols, Quantity, ISU, CONVERT, Conversion + // ratio] + var units = [ + ["a.u. of action", "?", null, "action", false, false, 1.05457168181818e-34], + ["a.u. of charge", "e", null, "electric_charge", false, false, 1.60217653141414e-19], + ["a.u. of energy", "Eh", null, "energy", false, false, 4.35974417757576e-18], + ["a.u. of length", "a?", null, "length", false, false, 5.29177210818182e-11], + ["a.u. of mass", "m?", null, "mass", false, false, 9.10938261616162e-31], + ["a.u. of time", "?/Eh", null, "time", false, false, 2.41888432650516e-17], + ["admiralty knot", "admkn", null, "speed", false, true, 0.514773333], + ["ampere", "A", null, "electric_current", true, false, 1], + ["ampere per meter", "A/m", null, "magnetic_field_intensity", true, false, 1], + ["ångström", "Å", ["ang"], "length", false, true, 1e-10], + ["are", "ar", null, "area", false, true, 100], + ["astronomical unit", "ua", null, "length", false, false, 1.49597870691667e-11], + ["bar", "bar", null, "pressure", false, false, 100000], + ["barn", "b", null, "area", false, false, 1e-28], + ["becquerel", "Bq", null, "radioactivity", true, false, 1], + ["bit", "bit", ["b"], "information", false, true, 1], + ["btu", "BTU", ["btu"], "energy", false, true, 1055.05585262], + ["byte", "byte", null, "information", false, true, 8], + ["candela", "cd", null, "luminous_intensity", true, false, 1], + ["candela per square metre", "cd/m?", null, "luminance", true, false, 1], + ["coulomb", "C", null, "electric_charge", true, false, 1], + ["cubic ångström", "ang3", ["ang^3"], "volume", false, true, 1e-30], + ["cubic foot", "ft3", ["ft^3"], "volume", false, true, 0.028316846592], + ["cubic inch", "in3", ["in^3"], "volume", false, true, 0.000016387064], + ["cubic light-year", "ly3", ["ly^3"], "volume", false, true, 8.46786664623715e-47], + ["cubic metre", "m?", null, "volume", true, true, 1], + ["cubic mile", "mi3", ["mi^3"], "volume", false, true, 4168181825.44058], + ["cubic nautical mile", "Nmi3", ["Nmi^3"], "volume", false, true, 6352182208], + ["cubic Pica", "Pica3", ["Picapt3", "Pica^3", "Picapt^3"], "volume", false, true, 7.58660370370369e-8], + ["cubic yard", "yd3", ["yd^3"], "volume", false, true, 0.764554857984], + ["cup", "cup", null, "volume", false, true, 0.0002365882365], + ["dalton", "Da", ["u"], "mass", false, false, 1.66053886282828e-27], + ["day", "d", ["day"], "time", false, true, 86400], + ["degree", "°", null, "angle", false, false, 0.0174532925199433], + ["degrees Rankine", "Rank", null, "temperature", false, true, 0.555555555555556], + ["dyne", "dyn", ["dy"], "force", false, true, 0.00001], + ["electronvolt", "eV", ["ev"], "energy", false, true, 1.60217656514141], + ["ell", "ell", null, "length", false, true, 1.143], + ["erg", "erg", ["e"], "energy", false, true, 1e-7], + ["farad", "F", null, "electric_capacitance", true, false, 1], + ["fluid ounce", "oz", null, "volume", false, true, 0.0000295735295625], + ["foot", "ft", null, "length", false, true, 0.3048], + ["foot-pound", "flb", null, "energy", false, true, 1.3558179483314], + ["gal", "Gal", null, "acceleration", false, false, 0.01], + ["gallon", "gal", null, "volume", false, true, 0.003785411784], + ["gauss", "G", ["ga"], "magnetic_flux_density", false, true, 1], + ["grain", "grain", null, "mass", false, true, 0.0000647989], + ["gram", "g", null, "mass", false, true, 0.001], + ["gray", "Gy", null, "absorbed_dose", true, false, 1], + ["gross registered ton", "GRT", ["regton"], "volume", false, true, 2.8316846592], + ["hectare", "ha", null, "area", false, true, 10000], + ["henry", "H", null, "inductance", true, false, 1], + ["hertz", "Hz", null, "frequency", true, false, 1], + ["horsepower", "HP", ["h"], "power", false, true, 745.69987158227], + ["horsepower-hour", "HPh", ["hh", "hph"], "energy", false, true, 2684519.538], + ["hour", "h", ["hr"], "time", false, true, 3600], + ["imperial gallon (U.K.)", "uk_gal", null, "volume", false, true, 0.00454609], + ["imperial hundredweight", "lcwt", ["uk_cwt", "hweight"], "mass", false, true, 50.802345], + ["imperial quart (U.K)", "uk_qt", null, "volume", false, true, 0.0011365225], + ["imperial ton", "brton", ["uk_ton", "LTON"], "mass", false, true, 1016.046909], + ["inch", "in", null, "length", false, true, 0.0254], + ["international acre", "uk_acre", null, "area", false, true, 4046.8564224], + ["IT calorie", "cal", null, "energy", false, true, 4.1868], + ["joule", "J", null, "energy", true, true, 1], + ["katal", "kat", null, "catalytic_activity", true, false, 1], + ["kelvin", "K", ["kel"], "temperature", true, true, 1], + ["kilogram", "kg", null, "mass", true, true, 1], + ["knot", "kn", null, "speed", false, true, 0.514444444444444], + ["light-year", "ly", null, "length", false, true, 9460730472580800], + ["litre", "L", ["l", "lt"], "volume", false, true, 0.001], + ["lumen", "lm", null, "luminous_flux", true, false, 1], + ["lux", "lx", null, "illuminance", true, false, 1], + ["maxwell", "Mx", null, "magnetic_flux", false, false, 1e-18], + ["measurement ton", "MTON", null, "volume", false, true, 1.13267386368], + ["meter per hour", "m/h", ["m/hr"], "speed", false, true, 0.00027777777777778], + ["meter per second", "m/s", ["m/sec"], "speed", true, true, 1], + ["meter per second squared", "m?s??", null, "acceleration", true, false, 1], + ["parsec", "pc", ["parsec"], "length", false, true, 30856775814671900], + ["meter squared per second", "m?/s", null, "kinematic_viscosity", true, false, 1], + ["metre", "m", null, "length", true, true, 1], + ["miles per hour", "mph", null, "speed", false, true, 0.44704], + ["millimetre of mercury", "mmHg", null, "pressure", false, false, 133.322], + ["minute", "?", null, "angle", false, false, 0.000290888208665722], + ["minute", "min", ["mn"], "time", false, true, 60], + ["modern teaspoon", "tspm", null, "volume", false, true, 0.000005], + ["mole", "mol", null, "amount_of_substance", true, false, 1], + ["morgen", "Morgen", null, "area", false, true, 2500], + ["n.u. of action", "?", null, "action", false, false, 1.05457168181818e-34], + ["n.u. of mass", "m?", null, "mass", false, false, 9.10938261616162e-31], + ["n.u. of speed", "c?", null, "speed", false, false, 299792458], + ["n.u. of time", "?/(me?c??)", null, "time", false, false, 1.28808866778687e-21], + ["nautical mile", "M", ["Nmi"], "length", false, true, 1852], + ["newton", "N", null, "force", true, true, 1], + ["œrsted", "Oe ", null, "magnetic_field_intensity", false, false, 79.5774715459477], + ["ohm", "Ω", null, "electric_resistance", true, false, 1], + ["ounce mass", "ozm", null, "mass", false, true, 0.028349523125], + ["pascal", "Pa", null, "pressure", true, false, 1], + ["pascal second", "Pa?s", null, "dynamic_viscosity", true, false, 1], + ["pferdestärke", "PS", null, "power", false, true, 735.49875], + ["phot", "ph", null, "illuminance", false, false, 0.0001], + ["pica (1/6 inch)", "pica", null, "length", false, true, 0.00035277777777778], + ["pica (1/72 inch)", "Pica", ["Picapt"], "length", false, true, 0.00423333333333333], + ["poise", "P", null, "dynamic_viscosity", false, false, 0.1], + ["pond", "pond", null, "force", false, true, 0.00980665], + ["pound force", "lbf", null, "force", false, true, 4.4482216152605], + ["pound mass", "lbm", null, "mass", false, true, 0.45359237], + ["quart", "qt", null, "volume", false, true, 0.000946352946], + ["radian", "rad", null, "angle", true, false, 1], + ["second", "?", null, "angle", false, false, 0.00000484813681109536], + ["second", "s", ["sec"], "time", true, true, 1], + ["short hundredweight", "cwt", ["shweight"], "mass", false, true, 45.359237], + ["siemens", "S", null, "electrical_conductance", true, false, 1], + ["sievert", "Sv", null, "equivalent_dose", true, false, 1], + ["slug", "sg", null, "mass", false, true, 14.59390294], + ["square ångström", "ang2", ["ang^2"], "area", false, true, 1e-20], + ["square foot", "ft2", ["ft^2"], "area", false, true, 0.09290304], + ["square inch", "in2", ["in^2"], "area", false, true, 0.00064516], + ["square light-year", "ly2", ["ly^2"], "area", false, true, 8.95054210748189e+31], + ["square meter", "m?", null, "area", true, true, 1], + ["square mile", "mi2", ["mi^2"], "area", false, true, 2589988.110336], + ["square nautical mile", "Nmi2", ["Nmi^2"], "area", false, true, 3429904], + ["square Pica", "Pica2", ["Picapt2", "Pica^2", "Picapt^2"], "area", false, true, 0.00001792111111111], + ["square yard", "yd2", ["yd^2"], "area", false, true, 0.83612736], + ["statute mile", "mi", null, "length", false, true, 1609.344], + ["steradian", "sr", null, "solid_angle", true, false, 1], + ["stilb", "sb", null, "luminance", false, false, 0.0001], + ["stokes", "St", null, "kinematic_viscosity", false, false, 0.0001], + ["stone", "stone", null, "mass", false, true, 6.35029318], + ["tablespoon", "tbs", null, "volume", false, true, 0.0000147868], + ["teaspoon", "tsp", null, "volume", false, true, 0.00000492892], + ["tesla", "T", null, "magnetic_flux_density", true, true, 1], + ["thermodynamic calorie", "c", null, "energy", false, true, 4.184], + ["ton", "ton", null, "mass", false, true, 907.18474], + ["tonne", "t", null, "mass", false, false, 1000], + ["U.K. pint", "uk_pt", null, "volume", false, true, 0.00056826125], + ["U.S. bushel", "bushel", null, "volume", false, true, 0.03523907], + ["U.S. oil barrel", "barrel", null, "volume", false, true, 0.158987295], + ["U.S. pint", "pt", ["us_pt"], "volume", false, true, 0.000473176473], + ["U.S. survey mile", "survey_mi", null, "length", false, true, 1609.347219], + ["U.S. survey/statute acre", "us_acre", null, "area", false, true, 4046.87261], + ["volt", "V", null, "voltage", true, false, 1], + ["watt", "W", null, "power", true, true, 1], + ["watt-hour", "Wh", ["wh"], "energy", false, true, 3600], + ["weber", "Wb", null, "magnetic_flux", true, false, 1], + ["yard", "yd", null, "length", false, true, 0.9144], + ["year", "yr", null, "time", false, true, 31557600] + ]; + + // Binary prefixes + // [Name, Prefix power of 2 value, Previx value, Abbreviation, Derived + // from] + var binary_prefixes = { + Yi: ["yobi", 80, 1208925819614629174706176, "Yi", "yotta"], + Zi: ["zebi", 70, 1180591620717411303424, "Zi", "zetta"], + Ei: ["exbi", 60, 1152921504606846976, "Ei", "exa"], + Pi: ["pebi", 50, 1125899906842624, "Pi", "peta"], + Ti: ["tebi", 40, 1099511627776, "Ti", "tera"], + Gi: ["gibi", 30, 1073741824, "Gi", "giga"], + Mi: ["mebi", 20, 1048576, "Mi", "mega"], + ki: ["kibi", 10, 1024, "ki", "kilo"] + }; + + // Unit prefixes + // [Name, Multiplier, Abbreviation] + var unit_prefixes = { + Y: ["yotta", 1e+24, "Y"], + Z: ["zetta", 1e+21, "Z"], + E: ["exa", 1e+18, "E"], + P: ["peta", 1e+15, "P"], + T: ["tera", 1e+12, "T"], + G: ["giga", 1e+09, "G"], + M: ["mega", 1e+06, "M"], + k: ["kilo", 1e+03, "k"], + h: ["hecto", 1e+02, "h"], + e: ["dekao", 1e+01, "e"], + d: ["deci", 1e-01, "d"], + c: ["centi", 1e-02, "c"], + m: ["milli", 1e-03, "m"], + u: ["micro", 1e-06, "u"], + n: ["nano", 1e-09, "n"], + p: ["pico", 1e-12, "p"], + f: ["femto", 1e-15, "f"], + a: ["atto", 1e-18, "a"], + z: ["zepto", 1e-21, "z"], + y: ["yocto", 1e-24, "y"] + }; + + // Initialize units and multipliers + var from = null; + var to = null; + var base_from_unit = from_unit; + var base_to_unit = to_unit; + var from_multiplier = 1; + var to_multiplier = 1; + var alt; + + // Lookup from and to units + for (var i = 0; i < units.length; i++) { + alt = (units[i][2] === null) ? [] : units[i][2]; + if (units[i][1] === base_from_unit || alt.indexOf(base_from_unit) >= 0) { + from = units[i]; + } + if (units[i][1] === base_to_unit || alt.indexOf(base_to_unit) >= 0) { + to = units[i]; + } + } + + // Lookup from prefix + if (from === null) { + var from_binary_prefix = binary_prefixes[from_unit.substring(0, 2)]; + var from_unit_prefix = unit_prefixes[from_unit.substring(0, 1)]; + + // Handle dekao unit prefix (only unit prefix with two characters) + if (from_unit.substring(0, 2) === 'da') { + from_unit_prefix = ["dekao", 1e+01, "da"]; + } + + // Handle binary prefixes first (so that 'Yi' is processed before + // 'Y') + if (from_binary_prefix) { + from_multiplier = from_binary_prefix[2]; + base_from_unit = from_unit.substring(2); + } else if (from_unit_prefix) { + from_multiplier = from_unit_prefix[1]; + base_from_unit = from_unit.substring(from_unit_prefix[2].length); + } + + // Lookup from unit + for (var j = 0; j < units.length; j++) { + alt = (units[j][2] === null) ? [] : units[j][2]; + if (units[j][1] === base_from_unit || alt.indexOf(base_from_unit) >= 0) { + from = units[j]; + } + } + } + + // Lookup to prefix + if (to === null) { + var to_binary_prefix = binary_prefixes[to_unit.substring(0, 2)]; + var to_unit_prefix = unit_prefixes[to_unit.substring(0, 1)]; + + // Handle dekao unit prefix (only unit prefix with two characters) + if (to_unit.substring(0, 2) === 'da') { + to_unit_prefix = ["dekao", 1e+01, "da"]; + } + + // Handle binary prefixes first (so that 'Yi' is processed before + // 'Y') + if (to_binary_prefix) { + to_multiplier = to_binary_prefix[2]; + base_to_unit = to_unit.substring(2); + } else if (to_unit_prefix) { + to_multiplier = to_unit_prefix[1]; + base_to_unit = to_unit.substring(to_unit_prefix[2].length); + } + + // Lookup to unit + for (var k = 0; k < units.length; k++) { + alt = (units[k][2] === null) ? [] : units[k][2]; + if (units[k][1] === base_to_unit || alt.indexOf(base_to_unit) >= 0) { + to = units[k]; + } + } + } + + // Return error if a unit does not exist + if (from === null || to === null) { + return error.na; + } + + // Return error if units represent different quantities + if (from[3] !== to[3]) { + return error.na; + } + + // Return converted number + return number * from[6] * from_multiplier / (to[6] * to_multiplier); + }; + + exports.DEC2BIN = function(number, places) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + + // Return error if number is not decimal, is lower than -512, or is + // greater than 511 + if (!/^-?[0-9]{1,3}$/.test(number) || number < -512 || number > 511) { + return error.num; + } + + // Ignore places and return a 10-character binary number if number is + // negative + if (number < 0) { + return '1' + REPT('0', 9 - (512 + number).toString(2).length) + (512 + number).toString(2); + } + + // Convert decimal number to binary + var result = parseInt(number, 10).toString(2); + + // Return binary number using the minimum number of characters necessary + // if places is undefined + if (typeof places === 'undefined') { + return result; + } else { + // Return error if places is nonnumeric + if (isNaN(places)) { + return error.value; + } + + // Return error if places is negative + if (places < 0) { + return error.num; + } + + // Truncate places in case it is not an integer + places = Math.floor(places); + + // Pad return value with leading 0s (zeros) if necessary (using + // Underscore.string) + return (places >= result.length) ? REPT('0', places - result.length) + result : error.num; + } + }; + + exports.DEC2HEX = function(number, places) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + + // Return error if number is not decimal, is lower than -549755813888, + // or is greater than 549755813887 + if (!/^-?[0-9]{1,12}$/.test(number) || number < -549755813888 || number > 549755813887) { + return error.num; + } + + // Ignore places and return a 10-character hexadecimal number if number + // is negative + if (number < 0) { + return (1099511627776 + number).toString(16); + } + + // Convert decimal number to hexadecimal + var result = parseInt(number, 10).toString(16); + + // Return hexadecimal number using the minimum number of characters + // necessary if places is undefined + if (typeof places === 'undefined') { + return result; + } else { + // Return error if places is nonnumeric + if (isNaN(places)) { + return error.value; + } + + // Return error if places is negative + if (places < 0) { + return error.num; + } + + // Truncate places in case it is not an integer + places = Math.floor(places); + + // Pad return value with leading 0s (zeros) if necessary (using + // Underscore.string) + return (places >= result.length) ? REPT('0', places - result.length) + result : error.num; + } + }; + + exports.DEC2OCT = function(number, places) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + + // Return error if number is not decimal, is lower than -549755813888, + // or is greater than 549755813887 + if (!/^-?[0-9]{1,9}$/.test(number) || number < -536870912 || number > 536870911) { + return error.num; + } + + // Ignore places and return a 10-character octal number if number is + // negative + if (number < 0) { + return (1073741824 + number).toString(8); + } + + // Convert decimal number to octal + var result = parseInt(number, 10).toString(8); + + // Return octal number using the minimum number of characters necessary + // if places is undefined + if (typeof places === 'undefined') { + return result; + } else { + // Return error if places is nonnumeric + if (isNaN(places)) { + return error.value; + } + + // Return error if places is negative + if (places < 0) { + return error.num; + } + + // Truncate places in case it is not an integer + places = Math.floor(places); + + // Pad return value with leading 0s (zeros) if necessary (using + // Underscore.string) + return (places >= result.length) ? REPT('0', places - result.length) + result : error.num; + } + }; + + exports.DELTA = function(number1, number2) { + // Set number2 to zero if undefined + number2 = (number2 === undefined) ? 0 : number2; + number1 = utils.parseNumber(number1); + number2 = utils.parseNumber(number2); + if (utils.anyIsError(number1, number2)) { + return error.value; + } + + // Return delta + return (number1 === number2) ? 1 : 0; + }; + + exports.ERF = function(lower_bound, upper_bound) { + }; + + exports.ERF.PRECISE = function() { + }; + + exports.ERFC = function(x) { + }; + + exports.ERFC.PRECISE = function() { + }; + + exports.GESTEP = function(number, step) { + step = step || 0; + number = utils.parseNumber(number); + if (utils.anyIsError(step, number)) { + return number; + } + + // Return delta + return (number >= step) ? 1 : 0; + }; + + exports.HEX2BIN = function(number, places) { + // Return error if number is not hexadecimal or contains more than ten + // characters (10 digits) + if (!/^[0-9A-Fa-f]{1,10}$/.test(number)) { + return error.num; + } + + // Check if number is negative + var negative = (number.length === 10 && number.substring(0, 1).toLowerCase() === 'f') ? true : false; + + // Convert hexadecimal number to decimal + var decimal = (negative) ? parseInt(number, 16) - 1099511627776 : parseInt(number, 16); + + // Return error if number is lower than -512 or greater than 511 + if (decimal < -512 || decimal > 511) { + return error.num; + } + + // Ignore places and return a 10-character binary number if number is + // negative + if (negative) { + return '1' + REPT('0', 9 - (512 + decimal).toString(2).length) + (512 + decimal).toString(2); + } + + // Convert decimal number to binary + var result = decimal.toString(2); + + // Return binary number using the minimum number of characters necessary + // if places is undefined + if (places === undefined) { + return result; + } else { + // Return error if places is nonnumeric + if (isNaN(places)) { + return error.value; + } + + // Return error if places is negative + if (places < 0) { + return error.num; + } + + // Truncate places in case it is not an integer + places = Math.floor(places); + + // Pad return value with leading 0s (zeros) if necessary (using + // Underscore.string) + return (places >= result.length) ? REPT('0', places - result.length) + result : error.num; + } + }; + + exports.HEX2DEC = function(number) { + // Return error if number is not hexadecimal or contains more than ten + // characters (10 digits) + if (!/^[0-9A-Fa-f]{1,10}$/.test(number)) { + return error.num; + } + + // Convert hexadecimal number to decimal + var decimal = parseInt(number, 16); + + // Return decimal number + return (decimal >= 549755813888) ? decimal - 1099511627776 : decimal; + }; + + exports.HEX2OCT = function(number, places) { + // Return error if number is not hexadecimal or contains more than ten + // characters (10 digits) + if (!/^[0-9A-Fa-f]{1,10}$/.test(number)) { + return error.num; + } + + // Convert hexadecimal number to decimal + var decimal = parseInt(number, 16); + + // Return error if number is positive and greater than 0x1fffffff + // (536870911) + if (decimal > 536870911 && decimal < 1098974756864) { + return error.num; + } + + // Ignore places and return a 10-character octal number if number is + // negative + if (decimal >= 1098974756864) { + return (decimal - 1098437885952).toString(8); + } + + // Convert decimal number to octal + var result = decimal.toString(8); + + // Return octal number using the minimum number of characters necessary + // if places is undefined + if (places === undefined) { + return result; + } else { + // Return error if places is nonnumeric + if (isNaN(places)) { + return error.value; + } + + // Return error if places is negative + if (places < 0) { + return error.num; + } + + // Truncate places in case it is not an integer + places = Math.floor(places); + + // Pad return value with leading 0s (zeros) if necessary (using + // Underscore.string) + return (places >= result.length) ? REPT('0', places - result.length) + result : error.num; + } + }; + + exports.IMABS = function(inumber) { + // Lookup real and imaginary coefficients using exports.js + // [http://formulajs.org] + var x = exports.IMREAL(inumber); + var y = exports.IMAGINARY(inumber); + + // Return error if either coefficient is not a number + if (utils.anyIsError(x, y)) { + return error.value; + } + + // Return absolute value of complex number + return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); + }; + + exports.IMAGINARY = function(inumber) { + if (inumber === undefined || inumber === true || inumber === false) { + return error.value; + } + + // Return 0 if inumber is equal to 0 + if (inumber === 0 || inumber === '0') { + return 0; + } + + // Handle special cases + if (['i', 'j'].indexOf(inumber) >= 0) { + return 1; + } + + // Normalize imaginary coefficient + inumber = inumber.replace('+i', '+1i').replace('-i', '-1i').replace('+j', '+1j').replace('-j', '-1j'); + + // Lookup sign + var plus = inumber.indexOf('+'); + var minus = inumber.indexOf('-'); + if (plus === 0) { + plus = inumber.indexOf('+', 1); + } + + if (minus === 0) { + minus = inumber.indexOf('-', 1); + } + + // Lookup imaginary unit + var last = inumber.substring(inumber.length - 1, inumber.length); + var unit = (last === 'i' || last === 'j'); + + if (plus >= 0 || minus >= 0) { + // Return error if imaginary unit is neither i nor j + if (!unit) { + return error.num; + } + + // Return imaginary coefficient of complex number + if (plus >= 0) { + return (isNaN(inumber.substring(0, plus)) || isNaN(inumber.substring(plus + 1, inumber.length - 1))) ? + error.num : + Number(inumber.substring(plus + 1, inumber.length - 1)); + } else { + return (isNaN(inumber.substring(0, minus)) || isNaN(inumber.substring(minus + 1, inumber.length - 1))) ? + error.num : + -Number(inumber.substring(minus + 1, inumber.length - 1)); + } + } else { + if (unit) { + return (isNaN(inumber.substring(0, inumber.length - 1))) ? error.num : inumber.substring(0, inumber.length - 1); + } else { + return (isNaN(inumber)) ? error.num : 0; + } + } + }; + + exports.IMARGUMENT = function(inumber) { + // Lookup real and imaginary coefficients using exports.js + // [http://formulajs.org] + var x = exports.IMREAL(inumber); + var y = exports.IMAGINARY(inumber); + + // Return error if either coefficient is not a number + if (utils.anyIsError(x, y)) { + return error.value; + } + + // Return error if inumber is equal to zero + if (x === 0 && y === 0) { + return error.div0; + } + + // Return PI/2 if x is equal to zero and y is positive + if (x === 0 && y > 0) { + return Math.PI / 2; + } + + // Return -PI/2 if x is equal to zero and y is negative + if (x === 0 && y < 0) { + return -Math.PI / 2; + } + + // Return zero if x is negative and y is equal to zero + if (y === 0 && x > 0) { + return 0; + } + + // Return zero if x is negative and y is equal to zero + if (y === 0 && x < 0) { + return -Math.PI; + } + + // Return argument of complex number + if (x > 0) { + return Math.atan(y / x); + } else if (x < 0 && y >= 0) { + return Math.atan(y / x) + Math.PI; + } else { + return Math.atan(y / x) - Math.PI; + } + }; + + exports.IMCONJUGATE = function(inumber) { + // Lookup real and imaginary coefficients using exports.js + // [http://formulajs.org] + var x = exports.IMREAL(inumber); + var y = exports.IMAGINARY(inumber); + + if (utils.anyIsError(x, y)) { + return error.value; + } + + // Lookup imaginary unit + var unit = inumber.substring(inumber.length - 1); + unit = (unit === 'i' || unit === 'j') ? unit : 'i'; + + // Return conjugate of complex number + return (y !== 0) ? exports.COMPLEX(x, -y, unit) : inumber; + }; + + exports.IMCOS = function(inumber) { + // Lookup real and imaginary coefficients using exports.js + // [http://formulajs.org] + var x = exports.IMREAL(inumber); + var y = exports.IMAGINARY(inumber); + + if (utils.anyIsError(x, y)) { + return error.value; + } + + // Lookup imaginary unit + var unit = inumber.substring(inumber.length - 1); + unit = (unit === 'i' || unit === 'j') ? unit : 'i'; + + // Return cosine of complex number + return exports.COMPLEX(Math.cos(x) * (Math.exp(y) + Math.exp(-y)) / 2, -Math.sin(x) * (Math.exp(y) - Math.exp(-y)) / 2, unit); + }; + + exports.IMCOSH = function(inumber) { + // Lookup real and imaginary coefficients using exports.js + // [http://formulajs.org] + var x = exports.IMREAL(inumber); + var y = exports.IMAGINARY(inumber); + + if (utils.anyIsError(x, y)) { + return error.value; + } + + // Lookup imaginary unit + var unit = inumber.substring(inumber.length - 1); + unit = (unit === 'i' || unit === 'j') ? unit : 'i'; + + // Return hyperbolic cosine of complex number + return exports.COMPLEX(Math.cos(y) * (Math.exp(x) + Math.exp(-x)) / 2, Math.sin(y) * (Math.exp(x) - Math.exp(-x)) / 2, unit); + }; + + exports.IMCOT = function(inumber) { + // Lookup real and imaginary coefficients using Formula.js + // [http://formulajs.org] + var x = exports.IMREAL(inumber); + var y = exports.IMAGINARY(inumber); + + if (utils.anyIsError(x, y)) { + return error.value; + } + + // Return cotangent of complex number + return exports.IMDIV(exports.IMCOS(inumber), exports.IMSIN(inumber)); + }; + + exports.IMDIV = function(inumber1, inumber2) { + // Lookup real and imaginary coefficients using Formula.js + // [http://formulajs.org] + var a = exports.IMREAL(inumber1); + var b = exports.IMAGINARY(inumber1); + var c = exports.IMREAL(inumber2); + var d = exports.IMAGINARY(inumber2); + + if (utils.anyIsError(a, b, c, d)) { + return error.value; + } + + // Lookup imaginary unit + var unit1 = inumber1.substring(inumber1.length - 1); + var unit2 = inumber2.substring(inumber2.length - 1); + var unit = 'i'; + if (unit1 === 'j') { + unit = 'j'; + } else if (unit2 === 'j') { + unit = 'j'; + } + + // Return error if inumber2 is null + if (c === 0 && d === 0) { + return error.num; + } + + // Return exponential of complex number + var den = c * c + d * d; + return exports.COMPLEX((a * c + b * d) / den, (b * c - a * d) / den, unit); + }; + + exports.IMEXP = function(inumber) { + // Lookup real and imaginary coefficients using Formula.js + // [http://formulajs.org] + var x = exports.IMREAL(inumber); + var y = exports.IMAGINARY(inumber); + + if (utils.anyIsError(x, y)) { + return error.value; + } + + // Lookup imaginary unit + var unit = inumber.substring(inumber.length - 1); + unit = (unit === 'i' || unit === 'j') ? unit : 'i'; + + // Return exponential of complex number + var e = Math.exp(x); + return exports.COMPLEX(e * Math.cos(y), e * Math.sin(y), unit); + }; + + exports.IMLN = function(inumber) { + // Lookup real and imaginary coefficients using Formula.js + // [http://formulajs.org] + var x = exports.IMREAL(inumber); + var y = exports.IMAGINARY(inumber); + + if (utils.anyIsError(x, y)) { + return error.value; + } + + // Lookup imaginary unit + var unit = inumber.substring(inumber.length - 1); + unit = (unit === 'i' || unit === 'j') ? unit : 'i'; + + // Return exponential of complex number + return exports.COMPLEX(Math.log(Math.sqrt(x * x + y * y)), Math.atan(y / x), unit); + }; + + exports.IMLOG10 = function(inumber) { + // Lookup real and imaginary coefficients using Formula.js + // [http://formulajs.org] + var x = exports.IMREAL(inumber); + var y = exports.IMAGINARY(inumber); + + if (utils.anyIsError(x, y)) { + return error.value; + } + + // Lookup imaginary unit + var unit = inumber.substring(inumber.length - 1); + unit = (unit === 'i' || unit === 'j') ? unit : 'i'; + + // Return exponential of complex number + return exports.COMPLEX(Math.log(Math.sqrt(x * x + y * y)) / Math.log(10), Math.atan(y / x) / Math.log(10), unit); + }; + + exports.IMLOG2 = function(inumber) { + // Lookup real and imaginary coefficients using Formula.js + // [http://formulajs.org] + var x = exports.IMREAL(inumber); + var y = exports.IMAGINARY(inumber); + + if (utils.anyIsError(x, y)) { + return error.value; + } + + // Lookup imaginary unit + var unit = inumber.substring(inumber.length - 1); + unit = (unit === 'i' || unit === 'j') ? unit : 'i'; + + // Return exponential of complex number + return exports.COMPLEX(Math.log(Math.sqrt(x * x + y * y)) / Math.log(2), Math.atan(y / x) / Math.log(2), unit); + }; + + exports.IMPOWER = function(inumber, number) { + number = utils.parseNumber(number); + var x = exports.IMREAL(inumber); + var y = exports.IMAGINARY(inumber); + if (utils.anyIsError(number, x, y)) { + return error.value; + } + + // Lookup imaginary unit + var unit = inumber.substring(inumber.length - 1); + unit = (unit === 'i' || unit === 'j') ? unit : 'i'; + + // Calculate power of modulus + var p = Math.pow(exports.IMABS(inumber), number); + + // Calculate argument + var t = exports.IMARGUMENT(inumber); + + // Return exponential of complex number + return exports.COMPLEX(p * Math.cos(number * t), p * Math.sin(number * t), unit); + }; + + exports.IMPRODUCT = function() { + // Initialize result + var result = arguments[0]; + + // Loop on all numbers + for (var i = 1; i < arguments.length; i++) { + // Lookup coefficients of two complex numbers + var a = exports.IMREAL(result); + var b = exports.IMAGINARY(result); + var c = exports.IMREAL(arguments[i]); + var d = exports.IMAGINARY(arguments[i]); + + if (utils.anyIsError(a, b, c, d)) { + return error.value; + } + + // Complute product of two complex numbers + result = exports.COMPLEX(a * c - b * d, a * d + b * c); + } + + // Return product of complex numbers + return result; + }; + + exports.IMREAL = function(inumber) { + if (inumber === undefined || inumber === true || inumber === false) { + return error.value; + } + + // Return 0 if inumber is equal to 0 + if (inumber === 0 || inumber === '0') { + return 0; + } + + // Handle special cases + if (['i', '+i', '1i', '+1i', '-i', '-1i', 'j', '+j', '1j', '+1j', '-j', '-1j'].indexOf(inumber) >= 0) { + return 0; + } + + // Lookup sign + var plus = inumber.indexOf('+'); + var minus = inumber.indexOf('-'); + if (plus === 0) { + plus = inumber.indexOf('+', 1); + } + if (minus === 0) { + minus = inumber.indexOf('-', 1); + } + + // Lookup imaginary unit + var last = inumber.substring(inumber.length - 1, inumber.length); + var unit = (last === 'i' || last === 'j'); + + if (plus >= 0 || minus >= 0) { + // Return error if imaginary unit is neither i nor j + if (!unit) { + return error.num; + } + + // Return real coefficient of complex number + if (plus >= 0) { + return (isNaN(inumber.substring(0, plus)) || isNaN(inumber.substring(plus + 1, inumber.length - 1))) ? + error.num : + Number(inumber.substring(0, plus)); + } else { + return (isNaN(inumber.substring(0, minus)) || isNaN(inumber.substring(minus + 1, inumber.length - 1))) ? + error.num : + Number(inumber.substring(0, minus)); + } + } else { + if (unit) { + return (isNaN(inumber.substring(0, inumber.length - 1))) ? error.num : 0; + } else { + return (isNaN(inumber)) ? error.num : inumber; + } + } + }; + + exports.IMSEC = function(inumber) { + // Return error if inumber is a logical value + if (inumber === true || inumber === false) { + return error.value; + } + + // Lookup real and imaginary coefficients using Formula.js + // [http://formulajs.org] + var x = exports.IMREAL(inumber); + var y = exports.IMAGINARY(inumber); + + if (utils.anyIsError(x, y)) { + return error.value; + } + + // Return secant of complex number + return exports.IMDIV('1', exports.IMCOS(inumber)); + }; + + exports.IMSECH = function(inumber) { + // Lookup real and imaginary coefficients using Formula.js + // [http://formulajs.org] + var x = exports.IMREAL(inumber); + var y = exports.IMAGINARY(inumber); + + if (utils.anyIsError(x, y)) { + return error.value; + } + + // Return hyperbolic secant of complex number + return exports.IMDIV('1', exports.IMCOSH(inumber)); + }; + + exports.IMSIN = function(inumber) { + // Lookup real and imaginary coefficients using Formula.js + // [http://formulajs.org] + var x = exports.IMREAL(inumber); + var y = exports.IMAGINARY(inumber); + + if (utils.anyIsError(x, y)) { + return error.value; + } + + // Lookup imaginary unit + var unit = inumber.substring(inumber.length - 1); + unit = (unit === 'i' || unit === 'j') ? unit : 'i'; + + // Return sine of complex number + return exports.COMPLEX(Math.sin(x) * (Math.exp(y) + Math.exp(-y)) / 2, Math.cos(x) * (Math.exp(y) - Math.exp(-y)) / 2, unit); + }; + + exports.IMSINH = function(inumber) { + // Lookup real and imaginary coefficients using Formula.js + // [http://formulajs.org] + var x = exports.IMREAL(inumber); + var y = exports.IMAGINARY(inumber); + + if (utils.anyIsError(x, y)) { + return error.value; + } + + // Lookup imaginary unit + var unit = inumber.substring(inumber.length - 1); + unit = (unit === 'i' || unit === 'j') ? unit : 'i'; + + // Return hyperbolic sine of complex number + return exports.COMPLEX(Math.cos(y) * (Math.exp(x) - Math.exp(-x)) / 2, Math.sin(y) * (Math.exp(x) + Math.exp(-x)) / 2, unit); + }; + + exports.IMSQRT = function(inumber) { + // Lookup real and imaginary coefficients using Formula.js + // [http://formulajs.org] + var x = exports.IMREAL(inumber); + var y = exports.IMAGINARY(inumber); + + if (utils.anyIsError(x, y)) { + return error.value; + } + + // Lookup imaginary unit + var unit = inumber.substring(inumber.length - 1); + unit = (unit === 'i' || unit === 'j') ? unit : 'i'; + + // Calculate power of modulus + var s = Math.sqrt(exports.IMABS(inumber)); + + // Calculate argument + var t = exports.IMARGUMENT(inumber); + + // Return exponential of complex number + return exports.COMPLEX(s * Math.cos(t / 2), s * Math.sin(t / 2), unit); + }; + + exports.IMCSC = function (inumber) { + // Return error if inumber is a logical value + if (inumber === true || inumber === false) { + return error.value; + } + + // Lookup real and imaginary coefficients using Formula.js + // [http://formulajs.org] + var x = exports.IMREAL(inumber); + var y = exports.IMAGINARY(inumber); + + // Return error if either coefficient is not a number + if (utils.anyIsError(x, y)) { + return error.num; + } + + // Return cosecant of complex number + return exports.IMDIV('1', exports.IMSIN(inumber)); + }; + + exports.IMCSCH = function (inumber) { + // Return error if inumber is a logical value + if (inumber === true || inumber === false) { + return error.value; + } + + // Lookup real and imaginary coefficients using Formula.js + // [http://formulajs.org] + var x = exports.IMREAL(inumber); + var y = exports.IMAGINARY(inumber); + + // Return error if either coefficient is not a number + if (utils.anyIsError(x, y)) { + return error.num; + } + + // Return hyperbolic cosecant of complex number + return exports.IMDIV('1', exports.IMSINH(inumber)); + }; + + exports.IMSUB = function(inumber1, inumber2) { + // Lookup real and imaginary coefficients using Formula.js + // [http://formulajs.org] + var a = this.IMREAL(inumber1); + var b = this.IMAGINARY(inumber1); + var c = this.IMREAL(inumber2); + var d = this.IMAGINARY(inumber2); + + if (utils.anyIsError(a, b, c, d)) { + return error.value; + } + + // Lookup imaginary unit + var unit1 = inumber1.substring(inumber1.length - 1); + var unit2 = inumber2.substring(inumber2.length - 1); + var unit = 'i'; + if (unit1 === 'j') { + unit = 'j'; + } else if (unit2 === 'j') { + unit = 'j'; + } + + // Return _ of two complex numbers + return this.COMPLEX(a - c, b - d, unit); + }; + + exports.IMSUM = function() { + var args = utils.flatten(arguments); + + // Initialize result + var result = args[0]; + + // Loop on all numbers + for (var i = 1; i < args.length; i++) { + // Lookup coefficients of two complex numbers + var a = this.IMREAL(result); + var b = this.IMAGINARY(result); + var c = this.IMREAL(args[i]); + var d = this.IMAGINARY(args[i]); + + if (utils.anyIsError(a, b, c, d)) { + return error.value; + } + + // Complute product of two complex numbers + result = this.COMPLEX(a + c, b + d); + } + + // Return sum of complex numbers + return result; + }; + + exports.IMTAN = function(inumber) { + // Return error if inumber is a logical value + if (inumber === true || inumber === false) { + return error.value; + } + + // Lookup real and imaginary coefficients using Formula.js + // [http://formulajs.org] + var x = exports.IMREAL(inumber); + var y = exports.IMAGINARY(inumber); + + if (utils.anyIsError(x, y)) { + return error.value; + } + + // Return tangent of complex number + return this.IMDIV(this.IMSIN(inumber), this.IMCOS(inumber)); + }; + + exports.OCT2BIN = function(number, places) { + // Return error if number is not hexadecimal or contains more than ten + // characters (10 digits) + if (!/^[0-7]{1,10}$/.test(number)) { + return error.num; + } + + // Check if number is negative + var negative = (number.length === 10 && number.substring(0, 1) === '7') ? true : false; + + // Convert octal number to decimal + var decimal = (negative) ? parseInt(number, 8) - 1073741824 : parseInt(number, 8); + + // Return error if number is lower than -512 or greater than 511 + if (decimal < -512 || decimal > 511) { + return error.num; + } + + // Ignore places and return a 10-character binary number if number is + // negative + if (negative) { + return '1' + REPT('0', 9 - (512 + decimal).toString(2).length) + (512 + decimal).toString(2); + } + + // Convert decimal number to binary + var result = decimal.toString(2); + + // Return binary number using the minimum number of characters necessary + // if places is undefined + if (typeof places === 'undefined') { + return result; + } else { + // Return error if places is nonnumeric + if (isNaN(places)) { + return error.value; + } + + // Return error if places is negative + if (places < 0) { + return error.num; + } + + // Truncate places in case it is not an integer + places = Math.floor(places); + + // Pad return value with leading 0s (zeros) if necessary (using + // Underscore.string) + return (places >= result.length) ? REPT('0', places - result.length) + result : error.num; + } + }; + + exports.OCT2DEC = function(number) { + // Return error if number is not octal or contains more than ten + // characters (10 digits) + if (!/^[0-7]{1,10}$/.test(number)) { + return error.num; + } + + // Convert octal number to decimal + var decimal = parseInt(number, 8); + + // Return decimal number + return (decimal >= 536870912) ? decimal - 1073741824 : decimal; + }; + + exports.OCT2HEX = function(number, places) { + // Return error if number is not octal or contains more than ten + // characters (10 digits) + if (!/^[0-7]{1,10}$/.test(number)) { + return error.num; + } + + // Convert octal number to decimal + var decimal = parseInt(number, 8); + + // Ignore places and return a 10-character octal number if number is + // negative + if (decimal >= 536870912) { + return 'ff' + (decimal + 3221225472).toString(16); + } + + // Convert decimal number to hexadecimal + var result = decimal.toString(16); + + // Return hexadecimal number using the minimum number of characters + // necessary if places is undefined + if (places === undefined) { + return result; + } else { + // Return error if places is nonnumeric + if (isNaN(places)) { + return error.value; + } + + // Return error if places is negative + if (places < 0) { + return error.num; + } + + // Truncate places in case it is not an integer + places = Math.floor(places); + + // Pad return value with leading 0s (zeros) if necessary (using + // Underscore.string) + return (places >= result.length) ? REPT('0', places - result.length) + result : error.num; + } + }; + + return exports; + })(); + + jexcel.methods.financial = (function() { + var exports = {}; + + function validDate(d) { + return d && d.getTime && !isNaN(d.getTime()); + } + + function ensureDate(d) { + return (d instanceof Date)?d:new Date(d); + } + + exports.ACCRINT = function(issue, first, settlement, rate, par, frequency, basis) { + // Return error if either date is invalid + issue = ensureDate(issue); + first = ensureDate(first); + settlement = ensureDate(settlement); + if (!validDate(issue) || !validDate(first) || !validDate(settlement)) { + return '#VALUE!'; + } + + // Return error if either rate or par are lower than or equal to zero + if (rate <= 0 || par <= 0) { + return '#NUM!'; + } + + // Return error if frequency is neither 1, 2, or 4 + if ([1, 2, 4].indexOf(frequency) === -1) { + return '#NUM!'; + } + + // Return error if basis is neither 0, 1, 2, 3, or 4 + if ([0, 1, 2, 3, 4].indexOf(basis) === -1) { + return '#NUM!'; + } + + // Return error if settlement is before or equal to issue + if (settlement <= issue) { + return '#NUM!'; + } + + // Set default values + par = par || 0; + basis = basis || 0; + + // Compute accrued interest + return par * rate * YEARFRAC(issue, settlement, basis); + }; + + exports.ACCRINTM = null; + + exports.AMORDEGRC = null; + + exports.AMORLINC = null; + + exports.COUPDAYBS = null; + + exports.COUPDAYS = null; + + exports.COUPDAYSNC = null; + + exports.COUPNCD = null; + + exports.COUPNUM = null; + + exports.COUPPCD = null; + + exports.CUMIPMT = function(rate, periods, value, start, end, type) { + // Credits: algorithm inspired by Apache OpenOffice + // Credits: Hannes Stiebitzhofer for the translations of function and + // variable names + // Requires exports.FV() and exports.PMT() from exports.js + // [http://stoic.com/exports/] + + rate = utils.parseNumber(rate); + periods = utils.parseNumber(periods); + value = utils.parseNumber(value); + if (utils.anyIsError(rate, periods, value)) { + return error.value; + } + + // Return error if either rate, periods, or value are lower than or + // equal to zero + if (rate <= 0 || periods <= 0 || value <= 0) { + return error.num; + } + + // Return error if start < 1, end < 1, or start > end + if (start < 1 || end < 1 || start > end) { + return error.num; + } + + // Return error if type is neither 0 nor 1 + if (type !== 0 && type !== 1) { + return error.num; + } + + // Compute cumulative interest + var payment = exports.PMT(rate, periods, value, 0, type); + var interest = 0; + + if (start === 1) { + if (type === 0) { + interest = -value; + start++; + } + } + + for (var i = start; i <= end; i++) { + if (type === 1) { + interest += exports.FV(rate, i - 2, payment, value, 1) - payment; + } else { + interest += exports.FV(rate, i - 1, payment, value, 0); + } + } + interest *= rate; + + // Return cumulative interest + return interest; + }; + + exports.CUMPRINC = function(rate, periods, value, start, end, type) { + // Credits: algorithm inspired by Apache OpenOffice + // Credits: Hannes Stiebitzhofer for the translations of function and + // variable names + + rate = utils.parseNumber(rate); + periods = utils.parseNumber(periods); + value = utils.parseNumber(value); + if (utils.anyIsError(rate, periods, value)) { + return error.value; + } + + // Return error if either rate, periods, or value are lower than or + // equal to zero + if (rate <= 0 || periods <= 0 || value <= 0) { + return error.num; + } + + // Return error if start < 1, end < 1, or start > end + if (start < 1 || end < 1 || start > end) { + return error.num; + } + + // Return error if type is neither 0 nor 1 + if (type !== 0 && type !== 1) { + return error.num; + } + + // Compute cumulative principal + var payment = exports.PMT(rate, periods, value, 0, type); + var principal = 0; + if (start === 1) { + if (type === 0) { + principal = payment + value * rate; + } else { + principal = payment; + } + start++; + } + for (var i = start; i <= end; i++) { + if (type > 0) { + principal += payment - (exports.FV(rate, i - 2, payment, value, 1) - payment) * rate; + } else { + principal += payment - exports.FV(rate, i - 1, payment, value, 0) * rate; + } + } + + // Return cumulative principal + return principal; + }; + + exports.DB = function(cost, salvage, life, period, month) { + // Initialize month + month = (month === undefined) ? 12 : month; + + cost = utils.parseNumber(cost); + salvage = utils.parseNumber(salvage); + life = utils.parseNumber(life); + period = utils.parseNumber(period); + month = utils.parseNumber(month); + if (utils.anyIsError(cost, salvage, life, period, month)) { + return error.value; + } + + // Return error if any of the parameters is negative + if (cost < 0 || salvage < 0 || life < 0 || period < 0) { + return error.num; + } + + // Return error if month is not an integer between 1 and 12 + if ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].indexOf(month) === -1) { + return error.num; + } + + // Return error if period is greater than life + if (period > life) { + return error.num; + } + + // Return 0 (zero) if salvage is greater than or equal to cost + if (salvage >= cost) { + return 0; + } + + // Rate is rounded to three decimals places + var rate = (1 - Math.pow(salvage / cost, 1 / life)).toFixed(3); + + // Compute initial depreciation + var initial = cost * rate * month / 12; + + // Compute total depreciation + var total = initial; + var current = 0; + var ceiling = (period === life) ? life - 1 : period; + for (var i = 2; i <= ceiling; i++) { + current = (cost - total) * rate; + total += current; + } + + // Depreciation for the first and last periods are special cases + if (period === 1) { + // First period + return initial; + } else if (period === life) { + // Last period + return (cost - total) * rate; + } else { + return current; + } + }; + + exports.DDB = function(cost, salvage, life, period, factor) { + // Initialize factor + factor = (factor === undefined) ? 2 : factor; + + cost = utils.parseNumber(cost); + salvage = utils.parseNumber(salvage); + life = utils.parseNumber(life); + period = utils.parseNumber(period); + factor = utils.parseNumber(factor); + if (utils.anyIsError(cost, salvage, life, period, factor)) { + return error.value; + } + + // Return error if any of the parameters is negative or if factor is + // null + if (cost < 0 || salvage < 0 || life < 0 || period < 0 || factor <= 0) { + return error.num; + } + + // Return error if period is greater than life + if (period > life) { + return error.num; + } + + // Return 0 (zero) if salvage is greater than or equal to cost + if (salvage >= cost) { + return 0; + } + + // Compute depreciation + var total = 0; + var current = 0; + for (var i = 1; i <= period; i++) { + current = Math.min((cost - total) * (factor / life), (cost - salvage - total)); + total += current; + } + + // Return depreciation + return current; + }; + + exports.DISC = null; + + exports.DOLLARDE = function(dollar, fraction) { + // Credits: algorithm inspired by Apache OpenOffice + + dollar = utils.parseNumber(dollar); + fraction = utils.parseNumber(fraction); + if (utils.anyIsError(dollar, fraction)) { + return error.value; + } + + // Return error if fraction is negative + if (fraction < 0) { + return error.num; + } + + // Return error if fraction is greater than or equal to 0 and less than + // 1 + if (fraction >= 0 && fraction < 1) { + return error.div0; + } + + // Truncate fraction if it is not an integer + fraction = parseInt(fraction, 10); + + // Compute integer part + var result = parseInt(dollar, 10); + + // Add decimal part + result += (dollar % 1) * Math.pow(10, Math.ceil(Math.log(fraction) / Math.LN10)) / fraction; + + // Round result + var power = Math.pow(10, Math.ceil(Math.log(fraction) / Math.LN2) + 1); + result = Math.round(result * power) / power; + + // Return converted dollar price + return result; + }; + + exports.DOLLARFR = function(dollar, fraction) { + // Credits: algorithm inspired by Apache OpenOffice + + dollar = utils.parseNumber(dollar); + fraction = utils.parseNumber(fraction); + if (utils.anyIsError(dollar, fraction)) { + return error.value; + } + + // Return error if fraction is negative + if (fraction < 0) { + return error.num; + } + + // Return error if fraction is greater than or equal to 0 and less than + // 1 + if (fraction >= 0 && fraction < 1) { + return error.div0; + } + + // Truncate fraction if it is not an integer + fraction = parseInt(fraction, 10); + + // Compute integer part + var result = parseInt(dollar, 10); + + // Add decimal part + result += (dollar % 1) * Math.pow(10, -Math.ceil(Math.log(fraction) / Math.LN10)) * fraction; + + // Return converted dollar price + return result; + }; + + exports.DURATION = null; + + exports.EFFECT = function(rate, periods) { + rate = utils.parseNumber(rate); + periods = utils.parseNumber(periods); + if (utils.anyIsError(rate, periods)) { + return error.value; + } + + // Return error if rate <=0 or periods < 1 + if (rate <= 0 || periods < 1) { + return error.num; + } + + // Truncate periods if it is not an integer + periods = parseInt(periods, 10); + + // Return effective annual interest rate + return Math.pow(1 + rate / periods, periods) - 1; + }; + + exports.FV = function(rate, periods, payment, value, type) { + // Credits: algorithm inspired by Apache OpenOffice + + value = value || 0; + type = type || 0; + + rate = utils.parseNumber(rate); + periods = utils.parseNumber(periods); + payment = utils.parseNumber(payment); + value = utils.parseNumber(value); + type = utils.parseNumber(type); + if (utils.anyIsError(rate, periods, payment, value, type)) { + return error.value; + } + + // Return future value + var result; + if (rate === 0) { + result = value + payment * periods; + } else { + var term = Math.pow(1 + rate, periods); + if (type === 1) { + result = value * term + payment * (1 + rate) * (term - 1) / rate; + } else { + result = value * term + payment * (term - 1) / rate; + } + } + return -result; + }; + + exports.FVSCHEDULE = function(principal, schedule) { + principal = utils.parseNumber(principal); + schedule = utils.parseNumberArray(utils.flatten(schedule)); + if (utils.anyIsError(principal, schedule)) { + return error.value; + } + + var n = schedule.length; + var future = principal; + + // Apply all interests in schedule + for (var i = 0; i < n; i++) { + // Apply scheduled interest + future *= 1 + schedule[i]; + } + + // Return future value + return future; + }; + + exports.INTRATE = null; + + exports.IPMT = function(rate, period, periods, present, future, type) { + // Credits: algorithm inspired by Apache OpenOffice + + future = future || 0; + type = type || 0; + + rate = utils.parseNumber(rate); + period = utils.parseNumber(period); + periods = utils.parseNumber(periods); + present = utils.parseNumber(present); + future = utils.parseNumber(future); + type = utils.parseNumber(type); + if (utils.anyIsError(rate, period, periods, present, future, type)) { + return error.value; + } + + // Compute payment + var payment = exports.PMT(rate, periods, present, future, type); + + // Compute interest + var interest; + if (period === 1) { + if (type === 1) { + interest = 0; + } else { + interest = -present; + } + } else { + if (type === 1) { + interest = exports.FV(rate, period - 2, payment, present, 1) - payment; + } else { + interest = exports.FV(rate, period - 1, payment, present, 0); + } + } + + // Return interest + return interest * rate; + }; + + exports.IRR = function(values, guess) { + // Credits: algorithm inspired by Apache OpenOffice + + guess = guess || 0; + + values = utils.parseNumberArray(utils.flatten(values)); + guess = utils.parseNumber(guess); + if (utils.anyIsError(values, guess)) { + return error.value; + } + + // Calculates the resulting amount + var irrResult = function(values, dates, rate) { + var r = rate + 1; + var result = values[0]; + for (var i = 1; i < values.length; i++) { + result += values[i] / Math.pow(r, (dates[i] - dates[0]) / 365); + } + return result; + }; + + // Calculates the first derivation + var irrResultDeriv = function(values, dates, rate) { + var r = rate + 1; + var result = 0; + for (var i = 1; i < values.length; i++) { + var frac = (dates[i] - dates[0]) / 365; + result -= frac * values[i] / Math.pow(r, frac + 1); + } + return result; + }; + + // Initialize dates and check that values contains at least one positive + // value and one negative value + var dates = []; + var positive = false; + var negative = false; + for (var i = 0; i < values.length; i++) { + dates[i] = (i === 0) ? 0 : dates[i - 1] + 365; + if (values[i] > 0) { + positive = true; + } + if (values[i] < 0) { + negative = true; + } + } + + // Return error if values does not contain at least one positive value + // and one negative value + if (!positive || !negative) { + return error.num; + } + + // Initialize guess and resultRate + guess = (guess === undefined) ? 0.1 : guess; + var resultRate = guess; + + // Set maximum epsilon for end of iteration + var epsMax = 1e-10; + + // Implement Newton's method + var newRate, epsRate, resultValue; + var contLoop = true; + do { + resultValue = irrResult(values, dates, resultRate); + newRate = resultRate - resultValue / irrResultDeriv(values, dates, resultRate); + epsRate = Math.abs(newRate - resultRate); + resultRate = newRate; + contLoop = (epsRate > epsMax) && (Math.abs(resultValue) > epsMax); + } while (contLoop); + + // Return internal rate of return + return resultRate; + }; + + exports.ISPMT = function(rate, period, periods, value) { + rate = utils.parseNumber(rate); + period = utils.parseNumber(period); + periods = utils.parseNumber(periods); + value = utils.parseNumber(value); + if (utils.anyIsError(rate, period, periods, value)) { + return error.value; + } + + // Return interest + return value * rate * (period / periods - 1); + }; + + exports.MDURATION = null; + + exports.MIRR = function(values, finance_rate, reinvest_rate) { + values = utils.parseNumberArray(utils.flatten(values)); + finance_rate = utils.parseNumber(finance_rate); + reinvest_rate = utils.parseNumber(reinvest_rate); + if (utils.anyIsError(values, finance_rate, reinvest_rate)) { + return error.value; + } + + // Initialize number of values + var n = values.length; + + // Lookup payments (negative values) and incomes (positive values) + var payments = []; + var incomes = []; + for (var i = 0; i < n; i++) { + if (values[i] < 0) { + payments.push(values[i]); + } else { + incomes.push(values[i]); + } + } + + // Return modified internal rate of return + var num = -exports.NPV(reinvest_rate, incomes) * Math.pow(1 + reinvest_rate, n - 1); + var den = exports.NPV(finance_rate, payments) * (1 + finance_rate); + return Math.pow(num / den, 1 / (n - 1)) - 1; + }; + + exports.NOMINAL = function(rate, periods) { + rate = utils.parseNumber(rate); + periods = utils.parseNumber(periods); + if (utils.anyIsError(rate, periods)) { + return error.value; + } + + // Return error if rate <=0 or periods < 1 + if (rate <= 0 || periods < 1) { + return error.num; + } + + // Truncate periods if it is not an integer + periods = parseInt(periods, 10); + + // Return nominal annual interest rate + return (Math.pow(rate + 1, 1 / periods) - 1) * periods; + }; + + exports.NPER = function(rate, payment, present, future, type) { + type = (type === undefined) ? 0 : type; + future = (future === undefined) ? 0 : future; + + rate = utils.parseNumber(rate); + payment = utils.parseNumber(payment); + present = utils.parseNumber(present); + future = utils.parseNumber(future); + type = utils.parseNumber(type); + if (utils.anyIsError(rate, payment, present, future, type)) { + return error.value; + } + + // Return number of periods + var num = payment * (1 + rate * type) - future * rate; + var den = (present * rate + payment * (1 + rate * type)); + return Math.log(num / den) / Math.log(1 + rate); + }; + + exports.NPV = function() { + var args = utils.parseNumberArray(utils.flatten(arguments)); + if (args instanceof Error) { + return args; + } + + // Lookup rate + var rate = args[0]; + + // Initialize net present value + var value = 0; + + // Loop on all values + for (var j = 1; j < args.length; j++) { + value += args[j] / Math.pow(1 + rate, j); + } + + // Return net present value + return value; + }; + + exports.ODDFPRICE = null; + + exports.ODDFYIELD = null; + + exports.ODDLPRICE = null; + + exports.ODDLYIELD = null; + + exports.PDURATION = function(rate, present, future) { + rate = utils.parseNumber(rate); + present = utils.parseNumber(present); + future = utils.parseNumber(future); + if (utils.anyIsError(rate, present, future)) { + return error.value; + } + + // Return error if rate <=0 + if (rate <= 0) { + return error.num; + } + + // Return number of periods + return (Math.log(future) - Math.log(present)) / Math.log(1 + rate); + }; + + exports.PMT = function(rate, periods, present, future, type) { + // Credits: algorithm inspired by Apache OpenOffice + + future = future || 0; + type = type || 0; + + rate = utils.parseNumber(rate); + periods = utils.parseNumber(periods); + present = utils.parseNumber(present); + future = utils.parseNumber(future); + type = utils.parseNumber(type); + if (utils.anyIsError(rate, periods, present, future, type)) { + return error.value; + } + + // Return payment + var result; + if (rate === 0) { + result = (present + future) / periods; + } else { + var term = Math.pow(1 + rate, periods); + if (type === 1) { + result = (future * rate / (term - 1) + present * rate / (1 - 1 / term)) / (1 + rate); + } else { + result = future * rate / (term - 1) + present * rate / (1 - 1 / term); + } + } + return -result; + }; + + exports.PPMT = function(rate, period, periods, present, future, type) { + future = future || 0; + type = type || 0; + + rate = utils.parseNumber(rate); + periods = utils.parseNumber(periods); + present = utils.parseNumber(present); + future = utils.parseNumber(future); + type = utils.parseNumber(type); + if (utils.anyIsError(rate, periods, present, future, type)) { + return error.value; + } + + return exports.PMT(rate, periods, present, future, type) - exports.IPMT(rate, period, periods, present, future, type); + }; + + exports.PRICE = null; + + exports.PRICEDISC = null; + + exports.PRICEMAT = null; + + exports.PV = function(rate, periods, payment, future, type) { + future = future || 0; + type = type || 0; + + rate = utils.parseNumber(rate); + periods = utils.parseNumber(periods); + payment = utils.parseNumber(payment); + future = utils.parseNumber(future); + type = utils.parseNumber(type); + if (utils.anyIsError(rate, periods, payment, future, type)) { + return error.value; + } + + // Return present value + if (rate === 0) { + return -payment * periods - future; + } else { + return (((1 - Math.pow(1 + rate, periods)) / rate) * payment * (1 + rate * type) - future) / Math.pow(1 + rate, periods); + } + }; + + exports.RATE = function(periods, payment, present, future, type, guess) { + // Credits: rabugento + + guess = (guess === undefined) ? 0.01 : guess; + future = (future === undefined) ? 0 : future; + type = (type === undefined) ? 0 : type; + + periods = utils.parseNumber(periods); + payment = utils.parseNumber(payment); + present = utils.parseNumber(present); + future = utils.parseNumber(future); + type = utils.parseNumber(type); + guess = utils.parseNumber(guess); + if (utils.anyIsError(periods, payment, present, future, type, guess)) { + return error.value; + } + + // Set maximum epsilon for end of iteration + var epsMax = 1e-6; + + // Set maximum number of iterations + var iterMax = 100; + var iter = 0; + var close = false; + var rate = guess; + + while (iter < iterMax && !close) { + var t1 = Math.pow(rate + 1, periods); + var t2 = Math.pow(rate + 1, periods - 1); + + var f1 = future + t1 * present + payment * (t1 - 1) * (rate * type + 1) / rate; + var f2 = periods * t2 * present - payment * (t1 - 1) *(rate * type + 1) / Math.pow(rate,2); + var f3 = periods * payment * t2 * (rate * type + 1) / rate + payment * (t1 - 1) * type / rate; + + var newRate = rate - f1 / (f2 + f3); + + if (Math.abs(newRate - rate) < epsMax) close = true; + iter++ + rate = newRate; + } + + if (!close) return Number.NaN + rate; + return rate; + }; + + // TODO + exports.RECEIVED = null; + + exports.RRI = function(periods, present, future) { + periods = utils.parseNumber(periods); + present = utils.parseNumber(present); + future = utils.parseNumber(future); + if (utils.anyIsError(periods, present, future)) { + return error.value; + } + + // Return error if periods or present is equal to 0 (zero) + if (periods === 0 || present === 0) { + return error.num; + } + + // Return equivalent interest rate + return Math.pow(future / present, 1 / periods) - 1; + }; + + exports.SLN = function(cost, salvage, life) { + cost = utils.parseNumber(cost); + salvage = utils.parseNumber(salvage); + life = utils.parseNumber(life); + if (utils.anyIsError(cost, salvage, life)) { + return error.value; + } + + // Return error if life equal to 0 (zero) + if (life === 0) { + return error.num; + } + + // Return straight-line depreciation + return (cost - salvage) / life; + }; + + exports.SYD = function(cost, salvage, life, period) { + // Return error if any of the parameters is not a number + cost = utils.parseNumber(cost); + salvage = utils.parseNumber(salvage); + life = utils.parseNumber(life); + period = utils.parseNumber(period); + if (utils.anyIsError(cost, salvage, life, period)) { + return error.value; + } + + // Return error if life equal to 0 (zero) + if (life === 0) { + return error.num; + } + + // Return error if period is lower than 1 or greater than life + if (period < 1 || period > life) { + return error.num; + } + + // Truncate period if it is not an integer + period = parseInt(period, 10); + + // Return straight-line depreciation + return ((cost - salvage) * (life - period + 1) * 2) / (life * (life + 1)); + }; + + exports.TBILLEQ = function(settlement, maturity, discount) { + settlement = utils.parseDate(settlement); + maturity = utils.parseDate(maturity); + discount = utils.parseNumber(discount); + if (utils.anyIsError(settlement, maturity, discount)) { + return error.value; + } + + // Return error if discount is lower than or equal to zero + if (discount <= 0) { + return error.num; + } + + // Return error if settlement is greater than maturity + if (settlement > maturity) { + return error.num; + } + + // Return error if maturity is more than one year after settlement + if (maturity - settlement > 365 * 24 * 60 * 60 * 1000) { + return error.num; + } + + // Return bond-equivalent yield + return (365 * discount) / (360 - discount * DAYS360(settlement, maturity, false)); + }; + + exports.TBILLPRICE = function(settlement, maturity, discount) { + settlement = utils.parseDate(settlement); + maturity = utils.parseDate(maturity); + discount = utils.parseNumber(discount); + if (utils.anyIsError(settlement, maturity, discount)) { + return error.value; + } + + // Return error if discount is lower than or equal to zero + if (discount <= 0) { + return error.num; + } + + // Return error if settlement is greater than maturity + if (settlement > maturity) { + return error.num; + } + + // Return error if maturity is more than one year after settlement + if (maturity - settlement > 365 * 24 * 60 * 60 * 1000) { + return error.num; + } + + // Return bond-equivalent yield + return 100 * (1 - discount * DAYS360(settlement, maturity, false) / 360); + }; + + exports.TBILLYIELD = function(settlement, maturity, price) { + settlement = utils.parseDate(settlement); + maturity = utils.parseDate(maturity); + price = utils.parseNumber(price); + if (utils.anyIsError(settlement, maturity, price)) { + return error.value; + } + + // Return error if price is lower than or equal to zero + if (price <= 0) { + return error.num; + } + + // Return error if settlement is greater than maturity + if (settlement > maturity) { + return error.num; + } + + // Return error if maturity is more than one year after settlement + if (maturity - settlement > 365 * 24 * 60 * 60 * 1000) { + return error.num; + } + + // Return bond-equivalent yield + return (100 - price) * 360 / (price * DAYS360(settlement, maturity, false)); + }; + + exports.VDB = null; + + exports.XIRR = function(values, dates, guess) { + // Credits: algorithm inspired by Apache OpenOffice + + values = utils.parseNumberArray(utils.flatten(values)); + dates = utils.parseDateArray(utils.flatten(dates)); + guess = utils.parseNumber(guess); + if (utils.anyIsError(values, dates, guess)) { + return error.value; + } + + // Calculates the resulting amount + var irrResult = function(values, dates, rate) { + var r = rate + 1; + var result = values[0]; + for (var i = 1; i < values.length; i++) { + result += values[i] / Math.pow(r, DAYS(dates[i], dates[0]) / 365); + } + return result; + }; + + // Calculates the first derivation + var irrResultDeriv = function(values, dates, rate) { + var r = rate + 1; + var result = 0; + for (var i = 1; i < values.length; i++) { + var frac = DAYS(dates[i], dates[0]) / 365; + result -= frac * values[i] / Math.pow(r, frac + 1); + } + return result; + }; + + // Check that values contains at least one positive value and one + // negative value + var positive = false; + var negative = false; + for (var i = 0; i < values.length; i++) { + if (values[i] > 0) { + positive = true; + } + if (values[i] < 0) { + negative = true; + } + } + + // Return error if values does not contain at least one positive value + // and one negative value + if (!positive || !negative) { + return error.num; + } + + // Initialize guess and resultRate + guess = guess || 0.1; + var resultRate = guess; + + // Set maximum epsilon for end of iteration + var epsMax = 1e-10; + + // Implement Newton's method + var newRate, epsRate, resultValue; + var contLoop = true; + do { + resultValue = irrResult(values, dates, resultRate); + newRate = resultRate - resultValue / irrResultDeriv(values, dates, resultRate); + epsRate = Math.abs(newRate - resultRate); + resultRate = newRate; + contLoop = (epsRate > epsMax) && (Math.abs(resultValue) > epsMax); + } while (contLoop); + + // Return internal rate of return + return resultRate; + }; + + exports.XNPV = function(rate, values, dates) { + rate = utils.parseNumber(rate); + values = utils.parseNumberArray(utils.flatten(values)); + dates = utils.parseDateArray(utils.flatten(dates)); + if (utils.anyIsError(rate, values, dates)) { + return error.value; + } + + var result = 0; + for (var i = 0; i < values.length; i++) { + result += values[i] / Math.pow(1 + rate, DAYS(dates[i], dates[0]) / 365); + } + return result; + }; + + exports.YIELD = null; + + exports.YIELDDISC = null; + + exports.YIELDMAT = null; + + return exports; + })(); + + jexcel.methods.information = (function() { + var exports = {}; + exports.CELL = null; + + exports.ERROR = {}; + exports.ERROR.TYPE = function(error_val) { + switch (error_val) { + case error.nil: return 1; + case error.div0: return 2; + case error.value: return 3; + case error.ref: return 4; + case error.name: return 5; + case error.num: return 6; + case error.na: return 7; + case error.data: return 8; + } + return error.na; + }; + + exports.INFO = null; + + exports.ISBLANK = function(value) { + return value === null; + }; + + exports.ISBINARY = function (number) { + return (/^[01]{1,10}$/).test(number); + }; + + exports.ISERR = function(value) { + return ([error.value, error.ref, error.div0, error.num, error.name, error.nil]).indexOf(value) >= 0 || + (typeof value === 'number' && (isNaN(value) || !isFinite(value))); + }; + + exports.ISERROR = function(value) { + return exports.ISERR(value) || value === error.na; + }; + + exports.ISEVEN = function(number) { + return (Math.floor(Math.abs(number)) & 1) ? false : true; + }; + + // TODO + exports.ISFORMULA = null; + + exports.ISLOGICAL = function(value) { + return value === true || value === false; + }; + + exports.ISNA = function(value) { + return value === error.na; + }; + + exports.ISNONTEXT = function(value) { + return typeof(value) !== 'string'; + }; + + exports.ISNUMBER = function(value) { + return typeof(value) === 'number' && !isNaN(value) && isFinite(value); + }; + + exports.ISODD = function(number) { + return (Math.floor(Math.abs(number)) & 1) ? true : false; + }; + + exports.ISREF = null; + + exports.ISTEXT = function(value) { + return typeof(value) === 'string'; + }; + + exports.N = function(value) { + if (this.ISNUMBER(value)) { + return value; + } + if (value instanceof Date) { + return value.getTime(); + } + if (value === true) { + return 1; + } + if (value === false) { + return 0; + } + if (this.ISERROR(value)) { + return value; + } + return 0; + }; + + exports.NA = function() { + return error.na; + }; + + exports.SHEET = null; + + exports.SHEETS = null; + + exports.TYPE = function(value) { + if (this.ISNUMBER(value)) { + return 1; + } + if (this.ISTEXT(value)) { + return 2; + } + if (this.ISLOGICAL(value)) { + return 4; + } + if (this.ISERROR(value)) { + return 16; + } + if (Array.isArray(value)) { + return 64; + } + }; + + return exports; + })(); + + jexcel.methods.logical = (function() { + var exports = {}; + + exports.AND = function() { + var args = utils.flatten(arguments); + var result = true; + for (var i = 0; i < args.length; i++) { + if (!args[i]) { + result = false; + } + } + return result; + }; + + exports.CHOOSE = function() { + if (arguments.length < 2) { + return error.na; + } + + var index = arguments[0]; + if (index < 1 || index > 254) { + return error.value; + } + + if (arguments.length < index + 1) { + return error.value; + } + + return arguments[index]; + }; + + exports.FALSE = function() { + return false; + }; + + exports.IF = function(test, then_value, otherwise_value) { + return test ? then_value : otherwise_value; + }; + + exports.IFERROR = function(value, valueIfError) { + if (ISERROR(value)) { + return valueIfError; + } + return value; + }; + + exports.IFNA = function(value, value_if_na) { + return value === error.na ? value_if_na : value; + }; + + exports.NOT = function(logical) { + return !logical; + }; + + exports.OR = function() { + var args = utils.flatten(arguments); + var result = false; + for (var i = 0; i < args.length; i++) { + if (args[i]) { + result = true; + } + } + return result; + }; + + exports.TRUE = function() { + return true; + }; + + exports.XOR = function() { + var args = utils.flatten(arguments); + var result = 0; + for (var i = 0; i < args.length; i++) { + if (args[i]) { + result++; + } + } + return (Math.floor(Math.abs(result)) & 1) ? true : false; + }; + + exports.SWITCH = function() { + var result; + if (arguments.length > 0) { + var targetValue = arguments[0]; + var argc = arguments.length - 1; + var switchCount = Math.floor(argc / 2); + var switchSatisfied = false; + var defaultClause = argc % 2 === 0 ? null : arguments[arguments.length - 1]; + + if (switchCount) { + for (var index = 0; index < switchCount; index++) { + if (targetValue === arguments[index * 2 + 1]) { + result = arguments[index * 2 + 2]; + switchSatisfied = true; + break; + } + } + } + + if (!switchSatisfied && defaultClause) { + result = defaultClause; + } + } + + return result; + }; + + return exports; + })(); + + jexcel.methods.math = (function() { + var exports = {}; + + exports.ABS = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + return Math.abs(utils.parseNumber(number)); + }; + + exports.ACOS = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + return Math.acos(number); + }; + + exports.ACOSH = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + return Math.log(number + Math.sqrt(number * number - 1)); + }; + + exports.ACOT = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + return Math.atan(1 / number); + }; + + exports.ACOTH = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + return 0.5 * Math.log((number + 1) / (number - 1)); + }; + + exports.AGGREGATE = null + + exports.ARABIC = function(text) { + // Credits: Rafa? Kukawski + if (!/^M*(?:D?C{0,3}|C[MD])(?:L?X{0,3}|X[CL])(?:V?I{0,3}|I[XV])$/.test(text)) { + return error.value; + } + var r = 0; + text.replace(/[MDLV]|C[MD]?|X[CL]?|I[XV]?/g, function(i) { + r += { + M: 1000, + CM: 900, + D: 500, + CD: 400, + C: 100, + XC: 90, + L: 50, + XL: 40, + X: 10, + IX: 9, + V: 5, + IV: 4, + I: 1 + }[i]; + }); + return r; + }; + + exports.ASIN = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + return Math.asin(number); + }; + + exports.ASINH = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + return Math.log(number + Math.sqrt(number * number + 1)); + }; + + exports.ATAN = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + return Math.atan(number); + }; + + exports.ATAN2 = function(number_x, number_y) { + number_x = utils.parseNumber(number_x); + number_y = utils.parseNumber(number_y); + if (utils.anyIsError(number_x, number_y)) { + return error.value; + } + return Math.atan2(number_x, number_y); + }; + + exports.ATANH = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + return Math.log((1 + number) / (1 - number)) / 2; + }; + + exports.BASE = function(number, radix, min_length) { + min_length = min_length || 0; + + number = utils.parseNumber(number); + radix = utils.parseNumber(radix); + min_length = utils.parseNumber(min_length); + if (utils.anyIsError(number, radix, min_length)) { + return error.value; + } + min_length = (min_length === undefined) ? 0 : min_length; + var result = number.toString(radix); + return new Array(Math.max(min_length + 1 - result.length, 0)).join('0') + result; + }; + + exports.CEILING = function(number, significance, mode) { + significance = (significance === undefined) ? 1 : significance; + mode = (mode === undefined) ? 0 : mode; + + number = utils.parseNumber(number); + significance = utils.parseNumber(significance); + mode = utils.parseNumber(mode); + if (utils.anyIsError(number, significance, mode)) { + return error.value; + } + if (significance === 0) { + return 0; + } + + significance = Math.abs(significance); + if (number >= 0) { + return Math.ceil(number / significance) * significance; + } else { + if (mode === 0) { + return -1 * Math.floor(Math.abs(number) / significance) * significance; + } else { + return -1 * Math.ceil(Math.abs(number) / significance) * significance; + } + } + }; + + exports.CEILING.MATH = exports.CEILING; + + exports.CEILING.PRECISE = exports.CEILING; + + exports.COMBIN = function(number, number_chosen) { + number = utils.parseNumber(number); + number_chosen = utils.parseNumber(number_chosen); + if (utils.anyIsError(number, number_chosen)) { + return error.value; + } + return exports.FACT(number) / (exports.FACT(number_chosen) * exports.FACT(number - number_chosen)); + }; + + exports.COMBINA = function(number, number_chosen) { + number = utils.parseNumber(number); + number_chosen = utils.parseNumber(number_chosen); + if (utils.anyIsError(number, number_chosen)) { + return error.value; + } + return (number === 0 && number_chosen === 0) ? 1 : exports.COMBIN(number + number_chosen - 1, number - 1); + }; + + exports.COS = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + return Math.cos(number); + }; + + exports.COSH = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + return (Math.exp(number) + Math.exp(-number)) / 2; + }; + + exports.COT = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + return 1 / Math.tan(number); + }; + + exports.COTH = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + var e2 = Math.exp(2 * number); + return (e2 + 1) / (e2 - 1); + }; + + exports.CSC = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + return 1 / Math.sin(number); + }; + + exports.CSCH = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + return 2 / (Math.exp(number) - Math.exp(-number)); + }; + + exports.DECIMAL = function(number, radix) { + if (arguments.length < 1) { + return error.value; + } + + + return parseInt(number, radix); + }; + + exports.DEGREES = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + return number * 180 / Math.PI; + }; + + exports.EVEN = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + return exports.CEILING(number, -2, -1); + }; + + exports.EXP = Math.exp; + + var MEMOIZED_FACT = []; + exports.FACT = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + var n = Math.floor(number); + if (n === 0 || n === 1) { + return 1; + } else if (MEMOIZED_FACT[n] > 0) { + return MEMOIZED_FACT[n]; + } else { + MEMOIZED_FACT[n] = exports.FACT(n - 1) * n; + return MEMOIZED_FACT[n]; + } + }; + + exports.FACTDOUBLE = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + var n = Math.floor(number); + if (n <= 0) { + return 1; + } else { + return n * exports.FACTDOUBLE(n - 2); + } + }; + + exports.FLOOR = function(number, significance, mode) { + significance = (significance === undefined) ? 1 : significance; + mode = (mode === undefined) ? 0 : mode; + + number = utils.parseNumber(number); + significance = utils.parseNumber(significance); + mode = utils.parseNumber(mode); + if (utils.anyIsError(number, significance, mode)) { + return error.value; + } + if (significance === 0) { + return 0; + } + + significance = Math.abs(significance); + if (number >= 0) { + return Math.floor(number / significance) * significance; + } else { + if (mode === 0) { + return -1 * Math.ceil(Math.abs(number) / significance) * significance; + } else { + return -1 * Math.floor(Math.abs(number) / significance) * significance; + } + } + }; + + exports.FLOOR.MATH = exports.FLOOR; + + exports.GCD = null; + + exports.INT = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + return Math.floor(number); + }; + + exports.LCM = function() { + // Credits: Jonas Raoni Soares Silva + var o = utils.parseNumberArray(utils.flatten(arguments)); + if (o instanceof Error) { + return o; + } + for (var i, j, n, d, r = 1; + (n = o.pop()) !== undefined;) { + while (n > 1) { + if (n % 2) { + for (i = 3, j = Math.floor(Math.sqrt(n)); i <= j && n % i; i += 2) { + //empty + } + d = (i <= j) ? i : n; + } else { + d = 2; + } + for (n /= d, r *= d, i = o.length; i; + (o[--i] % d) === 0 && (o[i] /= d) === 1 && o.splice(i, 1)) { + //empty + } + } + } + return r; + }; + + exports.LN = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + return Math.log(number); + }; + + exports.LOG = function(number, base) { + number = utils.parseNumber(number); + base = (base === undefined) ? 10 : utils.parseNumber(base); + + if (utils.anyIsError(number, base)) { + return error.value; + } + + return Math.log(number) / Math.log(base); + }; + + exports.LOG10 = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + return Math.log(number) / Math.log(10); + }; + + exports.MDETERM = null; + + exports.MINVERSE = null; + + exports.MMULT = null; + + exports.MOD = function(dividend, divisor) { + dividend = utils.parseNumber(dividend); + divisor = utils.parseNumber(divisor); + if (utils.anyIsError(dividend, divisor)) { + return error.value; + } + if (divisor === 0) { + return error.div0; + } + var modulus = Math.abs(dividend % divisor); + return (divisor > 0) ? modulus : -modulus; + }; + + exports.MROUND = function(number, multiple) { + number = utils.parseNumber(number); + multiple = utils.parseNumber(multiple); + if (utils.anyIsError(number, multiple)) { + return error.value; + } + if (number * multiple < 0) { + return error.num; + } + + return Math.round(number / multiple) * multiple; + }; + + exports.MULTINOMIAL = function() { + var args = utils.parseNumberArray(utils.flatten(arguments)); + if (args instanceof Error) { + return args; + } + var sum = 0; + var divisor = 1; + for (var i = 0; i < args.length; i++) { + sum += args[i]; + divisor *= exports.FACT(args[i]); + } + return exports.FACT(sum) / divisor; + }; + + exports.MUNIT = null; + + exports.ODD = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + var temp = Math.ceil(Math.abs(number)); + temp = (temp & 1) ? temp : temp + 1; + return (number > 0) ? temp : -temp; + }; + + exports.PI = function() { + return Math.PI; + }; + + exports.POWER = function(number, power) { + number = utils.parseNumber(number); + power = utils.parseNumber(power); + if (utils.anyIsError(number, power)) { + return error.value; + } + var result = Math.pow(number, power); + if (isNaN(result)) { + return error.num; + } + + return result; + }; + + exports.PRODUCT = function() { + var args = utils.parseNumberArray(utils.flatten(arguments)); + if (args instanceof Error) { + return args; + } + var result = 1; + for (var i = 0; i < args.length; i++) { + result *= args[i]; + } + return result; + }; + + exports.QUOTIENT = function(numerator, denominator) { + numerator = utils.parseNumber(numerator); + denominator = utils.parseNumber(denominator); + if (utils.anyIsError(numerator, denominator)) { + return error.value; + } + return parseInt(numerator / denominator, 10); + }; + + exports.RADIANS = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + return number * Math.PI / 180; + }; + + exports.RAND = function() { + return Math.random(); + }; + + exports.RANDBETWEEN = function(bottom, top) { + bottom = utils.parseNumber(bottom); + top = utils.parseNumber(top); + if (utils.anyIsError(bottom, top)) { + return error.value; + } + // Creative Commons Attribution 3.0 License + // Copyright (c) 2012 eqcode + return bottom + Math.ceil((top - bottom + 1) * Math.random()) - 1; + }; + + exports.ROMAN = null; + + exports.ROUND = function(number, digits) { + number = utils.parseNumber(number); + digits = utils.parseNumber(digits); + if (utils.anyIsError(number, digits)) { + return error.value; + } + return Math.round(number * Math.pow(10, digits)) / Math.pow(10, digits); + }; + + exports.ROUNDDOWN = function(number, digits) { + number = utils.parseNumber(number); + digits = utils.parseNumber(digits); + if (utils.anyIsError(number, digits)) { + return error.value; + } + var sign = (number > 0) ? 1 : -1; + return sign * (Math.floor(Math.abs(number) * Math.pow(10, digits))) / Math.pow(10, digits); + }; + + exports.ROUNDUP = function(number, digits) { + number = utils.parseNumber(number); + digits = utils.parseNumber(digits); + if (utils.anyIsError(number, digits)) { + return error.value; + } + var sign = (number > 0) ? 1 : -1; + return sign * (Math.ceil(Math.abs(number) * Math.pow(10, digits))) / Math.pow(10, digits); + }; + + exports.SEC = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + return 1 / Math.cos(number); + }; + + exports.SECH = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + return 2 / (Math.exp(number) + Math.exp(-number)); + }; + + exports.SERIESSUM = function(x, n, m, coefficients) { + x = utils.parseNumber(x); + n = utils.parseNumber(n); + m = utils.parseNumber(m); + coefficients = utils.parseNumberArray(coefficients); + if (utils.anyIsError(x, n, m, coefficients)) { + return error.value; + } + var result = coefficients[0] * Math.pow(x, n); + for (var i = 1; i < coefficients.length; i++) { + result += coefficients[i] * Math.pow(x, n + i * m); + } + return result; + }; + + exports.SIGN = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + if (number < 0) { + return -1; + } else if (number === 0) { + return 0; + } else { + return 1; + } + }; + + exports.SIN = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + return Math.sin(number); + }; + + exports.SINH = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + return (Math.exp(number) - Math.exp(-number)) / 2; + }; + + exports.SQRT = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + if (number < 0) { + return error.num; + } + return Math.sqrt(number); + }; + + exports.SQRTPI = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + return Math.sqrt(number * Math.PI); + }; + + exports.SUBTOTAL = null; + + exports.ADD = function (num1, num2) { + if (arguments.length !== 2) { + return error.na; + } + + num1 = utils.parseNumber(num1); + num2 = utils.parseNumber(num2); + if (utils.anyIsError(num1, num2)) { + return error.value; + } + + return num1 + num2; + }; + + exports.MINUS = function (num1, num2) { + if (arguments.length !== 2) { + return error.na; + } + + num1 = utils.parseNumber(num1); + num2 = utils.parseNumber(num2); + if (utils.anyIsError(num1, num2)) { + return error.value; + } + + return num1 - num2; + }; + + exports.DIVIDE = function (dividend, divisor) { + if (arguments.length !== 2) { + return error.na; + } + + dividend = utils.parseNumber(dividend); + divisor = utils.parseNumber(divisor); + if (utils.anyIsError(dividend, divisor)) { + return error.value; + } + + if (divisor === 0) { + return error.div0; + } + + return dividend / divisor; + }; + + exports.MULTIPLY = function (factor1, factor2) { + if (arguments.length !== 2) { + return error.na; + } + + factor1 = utils.parseNumber(factor1); + factor2 = utils.parseNumber(factor2); + if (utils.anyIsError(factor1, factor2)) { + return error.value; + } + + return factor1 * factor2; + }; + + exports.GTE = function (num1, num2) { + if (arguments.length !== 2) { + return error.na; + } + + num1 = utils.parseNumber(num1); + num2 = utils.parseNumber(num2); + if (utils.anyIsError(num1, num2)) { + return error.error; + } + + return num1 >= num2; + }; + + exports.LT = function (num1, num2) { + if (arguments.length !== 2) { + return error.na; + } + + num1 = utils.parseNumber(num1); + num2 = utils.parseNumber(num2); + if (utils.anyIsError(num1, num2)) { + return error.error; + } + + return num1 < num2; + }; + + exports.LTE = function (num1, num2) { + if (arguments.length !== 2) { + return error.na; + } + + num1 = utils.parseNumber(num1); + num2 = utils.parseNumber(num2); + if (utils.anyIsError(num1, num2)) { + return error.error; + } + + return num1 <= num2; + }; + + exports.EQ = function (value1, value2) { + if (arguments.length !== 2) { + return error.na; + } + + return value1 === value2; + }; + + exports.NE = function (value1, value2) { + if (arguments.length !== 2) { + return error.na; + } + + return value1 !== value2; + }; + + exports.POW = function (base, exponent) { + if (arguments.length !== 2) { + return error.na; + } + + base = utils.parseNumber(base); + exponent = utils.parseNumber(exponent); + if (utils.anyIsError(base, exponent)) { + return error.error; + } + + return exports.POWER(base, exponent); + }; + + exports.SUM = function() { + var result = 0; + var argsKeys = Object.keys(arguments); + for (var i = 0; i < argsKeys.length; ++i) { + var elt = arguments[argsKeys[i]]; + if (typeof elt === 'number') { + result += elt; + } else if (typeof elt === 'string') { + var parsed = parseFloat(elt); + !isNaN(parsed) && (result += parsed); + } else if (Array.isArray(elt)) { + result += exports.SUM.apply(null, elt); + } + } + return result; + }; + + exports.SUMIF = function(range, criteria) { + range = utils.parseNumberArray(utils.flatten(range)); + if (range instanceof Error) { + return range; + } + var result = 0; + for (var i = 0; i < range.length; i++) { + result += (eval(range[i] + criteria)) ? range[i] : 0; // jshint ignore:line + } + return result; + }; + + exports.SUMIFS = function() { + var args = utils.argsToArray(arguments); + var range = utils.parseNumberArray(utils.flatten(args.shift())); + if (range instanceof Error) { + return range; + } + var criteria = args; + + var n_range_elements = range.length; + var n_criterias = criteria.length; + + var result = 0; + for (var i = 0; i < n_range_elements; i++) { + var el = range[i]; + var condition = ''; + for (var c = 0; c < n_criterias; c++) { + condition += el + criteria[c]; + if (c !== n_criterias - 1) { + condition += '&&'; + } + } + if (eval(condition)) { // jshint ignore:line + result += el; + } + } + return result; + }; + + exports.SUMPRODUCT = null; + + exports.SUMSQ = function() { + var numbers = utils.parseNumberArray(utils.flatten(arguments)); + if (numbers instanceof Error) { + return numbers; + } + var result = 0; + var length = numbers.length; + for (var i = 0; i < length; i++) { + result += (ISNUMBER(numbers[i])) ? numbers[i] * numbers[i] : 0; + } + return result; + }; + + exports.SUMX2MY2 = function(array_x, array_y) { + array_x = utils.parseNumberArray(utils.flatten(array_x)); + array_y = utils.parseNumberArray(utils.flatten(array_y)); + if (utils.anyIsError(array_x, array_y)) { + return error.value; + } + var result = 0; + for (var i = 0; i < array_x.length; i++) { + result += array_x[i] * array_x[i] - array_y[i] * array_y[i]; + } + return result; + }; + + exports.SUMX2PY2 = function(array_x, array_y) { + array_x = utils.parseNumberArray(utils.flatten(array_x)); + array_y = utils.parseNumberArray(utils.flatten(array_y)); + if (utils.anyIsError(array_x, array_y)) { + return error.value; + } + var result = 0; + array_x = utils.parseNumberArray(utils.flatten(array_x)); + array_y = utils.parseNumberArray(utils.flatten(array_y)); + for (var i = 0; i < array_x.length; i++) { + result += array_x[i] * array_x[i] + array_y[i] * array_y[i]; + } + return result; + }; + + exports.SUMXMY2 = function(array_x, array_y) { + array_x = utils.parseNumberArray(utils.flatten(array_x)); + array_y = utils.parseNumberArray(utils.flatten(array_y)); + if (utils.anyIsError(array_x, array_y)) { + return error.value; + } + var result = 0; + array_x = utils.flatten(array_x); + array_y = utils.flatten(array_y); + for (var i = 0; i < array_x.length; i++) { + result += Math.pow(array_x[i] - array_y[i], 2); + } + return result; + }; + + exports.TAN = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + return Math.tan(number); + }; + + exports.TANH = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + var e2 = Math.exp(2 * number); + return (e2 - 1) / (e2 + 1); + }; + + exports.TRUNC = function(number, digits) { + digits = (digits === undefined) ? 0 : digits; + number = utils.parseNumber(number); + digits = utils.parseNumber(digits); + if (utils.anyIsError(number, digits)) { + return error.value; + } + var sign = (number > 0) ? 1 : -1; + return sign * (Math.floor(Math.abs(number) * Math.pow(10, digits))) / Math.pow(10, digits); + }; + + return exports; + })(); + + jexcel.methods.misc = (function() { + var exports = {}; + + exports.UNIQUE = function () { + var result = []; + for (var i = 0; i < arguments.length; ++i) { + var hasElement = false; + var element = arguments[i]; + + // Check if we've already seen this element. + for (var j = 0; j < result.length; ++j) { + hasElement = result[j] === element; + if (hasElement) { break; } + } + + // If we did not find it, add it to the result. + if (!hasElement) { + result.push(element); + } + } + return result; + }; + + exports.FLATTEN = utils.flatten; + + exports.ARGS2ARRAY = function () { + return Array.prototype.slice.call(arguments, 0); + }; + + exports.REFERENCE = function (context, reference) { + try { + var path = reference.split('.'); + var result = context; + for (var i = 0; i < path.length; ++i) { + var step = path[i]; + if (step[step.length - 1] === ']') { + var opening = step.indexOf('['); + var index = step.substring(opening + 1, step.length - 1); + result = result[step.substring(0, opening)][index]; + } else { + result = result[step]; + } + } + return result; + } catch (error) {} + }; + + exports.JOIN = function (array, separator) { + return array.join(separator); + }; + + exports.NUMBERS = function () { + var possibleNumbers = utils.flatten(arguments); + return possibleNumbers.filter(function (el) { + return typeof el === 'number'; + }); + }; + + exports.NUMERAL = null; + + return exports; + })(); + + jexcel.methods.text = (function() { + var exports = {}; + + exports.ASC = null; + + exports.BAHTTEXT = null; + + exports.CHAR = function(number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + return String.fromCharCode(number); + }; + + exports.CLEAN = function(text) { + text = text || ''; + var re = /[\0-\x1F]/g; + return text.replace(re, ""); + }; + + exports.CODE = function(text) { + text = text || ''; + return text.charCodeAt(0); + }; + + exports.CONCATENATE = function() { + var args = utils.flatten(arguments); + + var trueFound = 0; + while ((trueFound = args.indexOf(true)) > -1) { + args[trueFound] = 'TRUE'; + } + + var falseFound = 0; + while ((falseFound = args.indexOf(false)) > -1) { + args[falseFound] = 'FALSE'; + } + + return args.join(''); + }; + + exports.DBCS = null; + + exports.DOLLAR = null; + + exports.EXACT = function(text1, text2) { + return text1 === text2; + }; + + exports.FIND = function(find_text, within_text, position) { + position = (position === undefined) ? 0 : position; + return within_text ? within_text.indexOf(find_text, position - 1) + 1 : null; + }; + + exports.FIXED = null; + + exports.HTML2TEXT = function (value) { + var result = ''; + + if (value) { + if (value instanceof Array) { + value.forEach(function (line) { + if (result !== '') { + result += '\n'; + } + result += (line.replace(/<(?:.|\n)*?>/gm, '')); + }); + } else { + result = value.replace(/<(?:.|\n)*?>/gm, ''); + } + } + + return result; + }; + + exports.LEFT = function(text, number) { + number = (number === undefined) ? 1 : number; + number = utils.parseNumber(number); + if (number instanceof Error || typeof text !== 'string') { + return error.value; + } + return text ? text.substring(0, number) : null; + }; + + exports.LEN = function(text) { + if (arguments.length === 0) { + return error.error; + } + + if (typeof text === 'string') { + return text ? text.length : 0; + } + + if (text.length) { + return text.length; + } + + return error.value; + }; + + exports.LOWER = function(text) { + if (typeof text !== 'string') { + return error.value; + } + return text ? text.toLowerCase() : text; + }; + + exports.MID = function(text, start, number) { + start = utils.parseNumber(start); + number = utils.parseNumber(number); + if (utils.anyIsError(start, number) || typeof text !== 'string') { + return number; + } + + var begin = start - 1; + var end = begin + number; + + return text.substring(begin, end); + }; + + exports.NUMBERVALUE = null; + + exports.PRONETIC = null; + + exports.PROPER = function(text) { + if (text === undefined || text.length === 0) { + return error.value; + } + if (text === true) { + text = 'TRUE'; + } + if (text === false) { + text = 'FALSE'; + } + if (isNaN(text) && typeof text === 'number') { + return error.value; + } + if (typeof text === 'number') { + text = '' + text; + } + + return text.replace(/\w\S*/g, function(txt) { + return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); + }); + }; + + exports.REGEXEXTRACT = function (text, regular_expression) { + var match = text.match(new RegExp(regular_expression)); + return match ? (match[match.length > 1 ? match.length - 1 : 0]) : null; + }; + + exports.REGEXMATCH = function (text, regular_expression, full) { + var match = text.match(new RegExp(regular_expression)); + return full ? match : !!match; + }; + + exports.REGEXREPLACE = function (text, regular_expression, replacement) { + return text.replace(new RegExp(regular_expression), replacement); + }; + + exports.REPLACE = function(text, position, length, new_text) { + position = utils.parseNumber(position); + length = utils.parseNumber(length); + if (utils.anyIsError(position, length) || + typeof text !== 'string' || + typeof new_text !== 'string') { + return error.value; + } + return text.substr(0, position - 1) + new_text + text.substr(position - 1 + length); + }; + + exports.REPT = function(text, number) { + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + return new Array(number + 1).join(text); + }; + + exports.RIGHT = function(text, number) { + number = (number === undefined) ? 1 : number; + number = utils.parseNumber(number); + if (number instanceof Error) { + return number; + } + return text ? text.substring(text.length - number) : null; + }; + + exports.SEARCH = function(find_text, within_text, position) { + var foundAt; + if (typeof find_text !== 'string' || typeof within_text !== 'string') { + return error.value; + } + position = (position === undefined) ? 0 : position; + foundAt = within_text.toLowerCase().indexOf(find_text.toLowerCase(), position - 1)+1; + return (foundAt === 0)?error.value:foundAt; + }; + + exports.SPLIT = function (text, separator) { + return text.split(separator); + }; + + exports.SUBSTITUTE = function(text, old_text, new_text, occurrence) { + if (!text || !old_text || !new_text) { + return text; + } else if (occurrence === undefined) { + return text.replace(new RegExp(old_text, 'g'), new_text); + } else { + var index = 0; + var i = 0; + while (text.indexOf(old_text, index) > 0) { + index = text.indexOf(old_text, index + 1); + i++; + if (i === occurrence) { + return text.substring(0, index) + new_text + text.substring(index + old_text.length); + } + } + } + }; + + exports.T = function(value) { + return (typeof value === "string") ? value : ''; + }; + + exports.TEXT = null; + + exports.TRIM = function(text) { + if (typeof text !== 'string') { + return error.value; + } + return text.replace(/ +/g, ' ').trim(); + }; + + exports.UNICHAR = exports.CHAR; + + exports.UNICODE = exports.CODE; + + exports.UPPER = function(text) { + if (typeof text !== 'string') { + return error.value; + } + return text.toUpperCase(); + }; + + exports.VALUE = null; + + return exports; + })(); + + jexcel.methods.stats = (function() { + var exports = {}; + + var SQRT2PI = 2.5066282746310002; + + exports.AVEDEV = null; + + exports.AVERAGE = function() { + var range = utils.numbers(utils.flatten(arguments)); + var n = range.length; + var sum = 0; + var count = 0; + for (var i = 0; i < n; i++) { + sum += range[i]; + count += 1; + } + return sum / count; + }; + + exports.AVERAGEA = function() { + var range = utils.flatten(arguments); + var n = range.length; + var sum = 0; + var count = 0; + for (var i = 0; i < n; i++) { + var el = range[i]; + if (typeof el === 'number') { + sum += el; + } + if (el === true) { + sum++; + } + if (el !== null) { + count++; + } + } + return sum / count; + }; + + exports.AVERAGEIF = function(range, criteria, average_range) { + average_range = average_range || range; + range = utils.flatten(range); + average_range = utils.parseNumberArray(utils.flatten(average_range)); + if (average_range instanceof Error) { + return average_range; + } + var average_count = 0; + var result = 0; + for (var i = 0; i < range.length; i++) { + if (eval(range[i] + criteria)) { // jshint ignore:line + result += average_range[i]; + average_count++; + } + } + return result / average_count; + }; + + exports.AVERAGEIFS = null; + + exports.COUNT = function() { + return utils.numbers(utils.flatten(arguments)).length; + }; + + exports.COUNTA = function() { + var range = utils.flatten(arguments); + return range.length - exports.COUNTBLANK(range); + }; + + exports.COUNTIN = function (range, value) { + var result = 0; + for (var i = 0; i < range.length; i++) { + if (range[i] === value) { + result++; + } + } + return result; + }; + + exports.COUNTBLANK = function() { + var range = utils.flatten(arguments); + var blanks = 0; + var element; + for (var i = 0; i < range.length; i++) { + element = range[i]; + if (element === null || element === '') { + blanks++; + } + } + return blanks; + }; + + exports.COUNTIF = function(range, criteria) { + range = utils.flatten(range); + if (!/[<>=!]/.test(criteria)) { + criteria = '=="' + criteria + '"'; + } + var matches = 0; + for (var i = 0; i < range.length; i++) { + if (typeof range[i] !== 'string') { + if (eval(range[i] + criteria)) { // jshint ignore:line + matches++; + } + } else { + if (eval('"' + range[i] + '"' + criteria)) { // jshint ignore:line + matches++; + } + } + } + return matches; + }; + + exports.COUNTIFS = function() { + var args = utils.argsToArray(arguments); + var results = new Array(utils.flatten(args[0]).length); + for (var i = 0; i < results.length; i++) { + results[i] = true; + } + for (i = 0; i < args.length; i += 2) { + var range = utils.flatten(args[i]); + var criteria = args[i + 1]; + if (!/[<>=!]/.test(criteria)) { + criteria = '=="' + criteria + '"'; + } + for (var j = 0; j < range.length; j++) { + if (typeof range[j] !== 'string') { + results[j] = results[j] && eval(range[j] + criteria); // jshint ignore:line + } else { + results[j] = results[j] && eval('"' + range[j] + '"' + criteria); // jshint ignore:line + } + } + } + var result = 0; + for (i = 0; i < results.length; i++) { + if (results[i]) { + result++; + } + } + return result; + }; + + exports.COUNTUNIQUE = function () { + return UNIQUE.apply(null, utils.flatten(arguments)).length; + }; + + exports.FISHER = function(x) { + x = utils.parseNumber(x); + if (x instanceof Error) { + return x; + } + return Math.log((1 + x) / (1 - x)) / 2; + }; + + exports.FISHERINV = function(y) { + y = utils.parseNumber(y); + if (y instanceof Error) { + return y; + } + var e2y = Math.exp(2 * y); + return (e2y - 1) / (e2y + 1); + }; + + exports.FREQUENCY = function(data, bins) { + data = utils.parseNumberArray(utils.flatten(data)); + bins = utils.parseNumberArray(utils.flatten(bins)); + if (utils.anyIsError(data, bins)) { + return error.value; + } + var n = data.length; + var b = bins.length; + var r = []; + for (var i = 0; i <= b; i++) { + r[i] = 0; + for (var j = 0; j < n; j++) { + if (i === 0) { + if (data[j] <= bins[0]) { + r[0] += 1; + } + } else if (i < b) { + if (data[j] > bins[i - 1] && data[j] <= bins[i]) { + r[i] += 1; + } + } else if (i === b) { + if (data[j] > bins[b - 1]) { + r[b] += 1; + } + } + } + } + return r; + }; + + exports.LARGE = function(range, k) { + range = utils.parseNumberArray(utils.flatten(range)); + k = utils.parseNumber(k); + if (utils.anyIsError(range, k)) { + return range; + } + return range.sort(function(a, b) { + return b - a; + })[k - 1]; + }; + + exports.MAX = function() { + var range = utils.numbers(utils.flatten(arguments)); + return (range.length === 0) ? 0 : Math.max.apply(Math, range); + }; + + exports.MAXA = function() { + var range = utils.arrayValuesToNumbers(utils.flatten(arguments)); + return (range.length === 0) ? 0 : Math.max.apply(Math, range); + }; + + exports.MIN = function() { + var range = utils.numbers(utils.flatten(arguments)); + return (range.length === 0) ? 0 : Math.min.apply(Math, range); + }; + + exports.MINA = function() { + var range = utils.arrayValuesToNumbers(utils.flatten(arguments)); + return (range.length === 0) ? 0 : Math.min.apply(Math, range); + }; + + exports.MODE = {}; + + exports.MODE.MULT = function() { + // Credits: Roönaän + var range = utils.parseNumberArray(utils.flatten(arguments)); + if (range instanceof Error) { + return range; + } + var n = range.length; + var count = {}; + var maxItems = []; + var max = 0; + var currentItem; + + for (var i = 0; i < n; i++) { + currentItem = range[i]; + count[currentItem] = count[currentItem] ? count[currentItem] + 1 : 1; + if (count[currentItem] > max) { + max = count[currentItem]; + maxItems = []; + } + if (count[currentItem] === max) { + maxItems[maxItems.length] = currentItem; + } + } + return maxItems; + }; + + exports.MODE.SNGL = function() { + var range = utils.parseNumberArray(utils.flatten(arguments)); + if (range instanceof Error) { + return range; + } + return exports.MODE.MULT(range).sort(function(a, b) { + return a - b; + })[0]; + }; + + exports.PERCENTILE = {}; + + exports.PERCENTILE.EXC = function(array, k) { + array = utils.parseNumberArray(utils.flatten(array)); + k = utils.parseNumber(k); + if (utils.anyIsError(array, k)) { + return error.value; + } + array = array.sort(function(a, b) { + { + return a - b; + } + }); + var n = array.length; + if (k < 1 / (n + 1) || k > 1 - 1 / (n + 1)) { + return error.num; + } + var l = k * (n + 1) - 1; + var fl = Math.floor(l); + return utils.cleanFloat((l === fl) ? array[l] : array[fl] + (l - fl) * (array[fl + 1] - array[fl])); + }; + + exports.PERCENTILE.INC = function(array, k) { + array = utils.parseNumberArray(utils.flatten(array)); + k = utils.parseNumber(k); + if (utils.anyIsError(array, k)) { + return error.value; + } + array = array.sort(function(a, b) { + return a - b; + }); + var n = array.length; + var l = k * (n - 1); + var fl = Math.floor(l); + return utils.cleanFloat((l === fl) ? array[l] : array[fl] + (l - fl) * (array[fl + 1] - array[fl])); + }; + + exports.PERCENTRANK = {}; + + exports.PERCENTRANK.EXC = function(array, x, significance) { + significance = (significance === undefined) ? 3 : significance; + array = utils.parseNumberArray(utils.flatten(array)); + x = utils.parseNumber(x); + significance = utils.parseNumber(significance); + if (utils.anyIsError(array, x, significance)) { + return error.value; + } + array = array.sort(function(a, b) { + return a - b; + }); + var uniques = UNIQUE.apply(null, array); + var n = array.length; + var m = uniques.length; + var power = Math.pow(10, significance); + var result = 0; + var match = false; + var i = 0; + while (!match && i < m) { + if (x === uniques[i]) { + result = (array.indexOf(uniques[i]) + 1) / (n + 1); + match = true; + } else if (x >= uniques[i] && (x < uniques[i + 1] || i === m - 1)) { + result = (array.indexOf(uniques[i]) + 1 + (x - uniques[i]) / (uniques[i + 1] - uniques[i])) / (n + 1); + match = true; + } + i++; + } + return Math.floor(result * power) / power; + }; + + exports.PERCENTRANK.INC = function(array, x, significance) { + significance = (significance === undefined) ? 3 : significance; + array = utils.parseNumberArray(utils.flatten(array)); + x = utils.parseNumber(x); + significance = utils.parseNumber(significance); + if (utils.anyIsError(array, x, significance)) { + return error.value; + } + array = array.sort(function(a, b) { + return a - b; + }); + var uniques = UNIQUE.apply(null, array); + var n = array.length; + var m = uniques.length; + var power = Math.pow(10, significance); + var result = 0; + var match = false; + var i = 0; + while (!match && i < m) { + if (x === uniques[i]) { + result = array.indexOf(uniques[i]) / (n - 1); + match = true; + } else if (x >= uniques[i] && (x < uniques[i + 1] || i === m - 1)) { + result = (array.indexOf(uniques[i]) + (x - uniques[i]) / (uniques[i + 1] - uniques[i])) / (n - 1); + match = true; + } + i++; + } + return Math.floor(result * power) / power; + }; + + exports.PERMUT = function(number, number_chosen) { + number = utils.parseNumber(number); + number_chosen = utils.parseNumber(number_chosen); + if (utils.anyIsError(number, number_chosen)) { + return error.value; + } + return FACT(number) / FACT(number - number_chosen); + }; + + exports.PERMUTATIONA = function(number, number_chosen) { + number = utils.parseNumber(number); + number_chosen = utils.parseNumber(number_chosen); + if (utils.anyIsError(number, number_chosen)) { + return error.value; + } + return Math.pow(number, number_chosen); + }; + + exports.PHI = function(x) { + x = utils.parseNumber(x); + if (x instanceof Error) { + return error.value; + } + return Math.exp(-0.5 * x * x) / SQRT2PI; + }; + + exports.PROB = function(range, probability, lower, upper) { + if (lower === undefined) { + return 0; + } + upper = (upper === undefined) ? lower : upper; + + range = utils.parseNumberArray(utils.flatten(range)); + probability = utils.parseNumberArray(utils.flatten(probability)); + lower = utils.parseNumber(lower); + upper = utils.parseNumber(upper); + if (utils.anyIsError(range, probability, lower, upper)) { + return error.value; + } + + if (lower === upper) { + return (range.indexOf(lower) >= 0) ? probability[range.indexOf(lower)] : 0; + } + + var sorted = range.sort(function(a, b) { + return a - b; + }); + var n = sorted.length; + var result = 0; + for (var i = 0; i < n; i++) { + if (sorted[i] >= lower && sorted[i] <= upper) { + result += probability[range.indexOf(sorted[i])]; + } + } + return result; + }; + + exports.QUARTILE = {}; + + exports.QUARTILE.EXC = function(range, quart) { + range = utils.parseNumberArray(utils.flatten(range)); + quart = utils.parseNumber(quart); + if (utils.anyIsError(range, quart)) { + return error.value; + } + switch (quart) { + case 1: + return exports.PERCENTILE.EXC(range, 0.25); + case 2: + return exports.PERCENTILE.EXC(range, 0.5); + case 3: + return exports.PERCENTILE.EXC(range, 0.75); + default: + return error.num; + } + }; + + exports.QUARTILE.INC = function(range, quart) { + range = utils.parseNumberArray(utils.flatten(range)); + quart = utils.parseNumber(quart); + if (utils.anyIsError(range, quart)) { + return error.value; + } + switch (quart) { + case 1: + return exports.PERCENTILE.INC(range, 0.25); + case 2: + return exports.PERCENTILE.INC(range, 0.5); + case 3: + return exports.PERCENTILE.INC(range, 0.75); + default: + return error.num; + } + }; + + exports.RANK = {}; + + exports.RANK.AVG = function(number, range, order) { + number = utils.parseNumber(number); + range = utils.parseNumberArray(utils.flatten(range)); + if (utils.anyIsError(number, range)) { + return error.value; + } + range = utils.flatten(range); + order = order || false; + var sort = (order) ? function(a, b) { + return a - b; + } : function(a, b) { + return b - a; + }; + range = range.sort(sort); + + var length = range.length; + var count = 0; + for (var i = 0; i < length; i++) { + if (range[i] === number) { + count++; + } + } + + return (count > 1) ? (2 * range.indexOf(number) + count + 1) / 2 : range.indexOf(number) + 1; + }; + + exports.RANK.EQ = function(number, range, order) { + number = utils.parseNumber(number); + range = utils.parseNumberArray(utils.flatten(range)); + if (utils.anyIsError(number, range)) { + return error.value; + } + order = order || false; + var sort = (order) ? function(a, b) { + return a - b; + } : function(a, b) { + return b - a; + }; + range = range.sort(sort); + return range.indexOf(number) + 1; + }; + + exports.RSQ = function(data_x, data_y) { // no need to flatten here, PEARSON will take care of that + data_x = utils.parseNumberArray(utils.flatten(data_x)); + data_y = utils.parseNumberArray(utils.flatten(data_y)); + if (utils.anyIsError(data_x, data_y)) { + return error.value; + } + return Math.pow(exports.PEARSON(data_x, data_y), 2); + }; + + exports.SMALL = function(range, k) { + range = utils.parseNumberArray(utils.flatten(range)); + k = utils.parseNumber(k); + if (utils.anyIsError(range, k)) { + return range; + } + return range.sort(function(a, b) { + return a - b; + })[k - 1]; + }; + + exports.STANDARDIZE = function(x, mean, sd) { + x = utils.parseNumber(x); + mean = utils.parseNumber(mean); + sd = utils.parseNumber(sd); + if (utils.anyIsError(x, mean, sd)) { + return error.value; + } + return (x - mean) / sd; + }; + + exports.STDEV = {}; + + exports.STDEV.P = function() { + var v = exports.VAR.P.apply(this, arguments); + return Math.sqrt(v); + }; + + exports.STDEV.S = function() { + var v = exports.VAR.S.apply(this, arguments); + return Math.sqrt(v); + }; + + exports.STDEVA = function() { + var v = exports.VARA.apply(this, arguments); + return Math.sqrt(v); + }; + + exports.STDEVPA = function() { + var v = exports.VARPA.apply(this, arguments); + return Math.sqrt(v); + }; + + exports.VAR = {}; + + exports.VAR.P = function() { + var range = utils.numbers(utils.flatten(arguments)); + var n = range.length; + var sigma = 0; + var mean = exports.AVERAGE(range); + for (var i = 0; i < n; i++) { + sigma += Math.pow(range[i] - mean, 2); + } + return sigma / n; + }; + + exports.VAR.S = function() { + var range = utils.numbers(utils.flatten(arguments)); + var n = range.length; + var sigma = 0; + var mean = exports.AVERAGE(range); + for (var i = 0; i < n; i++) { + sigma += Math.pow(range[i] - mean, 2); + } + return sigma / (n - 1); + }; + + exports.VARA = function() { + var range = utils.flatten(arguments); + var n = range.length; + var sigma = 0; + var count = 0; + var mean = exports.AVERAGEA(range); + for (var i = 0; i < n; i++) { + var el = range[i]; + if (typeof el === 'number') { + sigma += Math.pow(el - mean, 2); + } else if (el === true) { + sigma += Math.pow(1 - mean, 2); + } else { + sigma += Math.pow(0 - mean, 2); + } + + if (el !== null) { + count++; + } + } + return sigma / (count - 1); + }; + + exports.VARPA = function() { + var range = utils.flatten(arguments); + var n = range.length; + var sigma = 0; + var count = 0; + var mean = exports.AVERAGEA(range); + for (var i = 0; i < n; i++) { + var el = range[i]; + if (typeof el === 'number') { + sigma += Math.pow(el - mean, 2); + } else if (el === true) { + sigma += Math.pow(1 - mean, 2); + } else { + sigma += Math.pow(0 - mean, 2); + } + + if (el !== null) { + count++; + } + } + return sigma / count; + }; + + exports.WEIBULL = {}; + + exports.WEIBULL.DIST = function(x, alpha, beta, cumulative) { + x = utils.parseNumber(x); + alpha = utils.parseNumber(alpha); + beta = utils.parseNumber(beta); + if (utils.anyIsError(x, alpha, beta)) { + return error.value; + } + return (cumulative) ? 1 - Math.exp(-Math.pow(x / beta, alpha)) : Math.pow(x, alpha - 1) * Math.exp(-Math.pow(x / beta, alpha)) * alpha / Math.pow(beta, alpha); + }; + + exports.Z = {}; + + exports.Z.TEST = function(range, x, sd) { + range = utils.parseNumberArray(utils.flatten(range)); + x = utils.parseNumber(x); + if (utils.anyIsError(range, x)) { + return error.value; + } + + sd = sd || exports.STDEV.S(range); + var n = range.length; + return 1 - exports.NORM.S.DIST((exports.AVERAGE(range) - x) / (sd / Math.sqrt(n)), true); + }; + + return exports; + })(); + + jexcel.methods.utils = (function() { + var exports = {}; + + exports.PROGRESS = function(p, c) { + var color = c ? c : 'red'; + var value = p ? p : '0'; + + return '<div style="width:' + value + '%;height:4px;background-color:' + color + ';margin-top:1px;"></div>'; + }; + + exports.RATING = function(v) { + var html = '<div class="jrating">'; + for (var i = 0; i < 5; i++) { + if (i < v) { + html += '<div class="jrating-selected"></div>'; + } else { + html += '<div></div>'; + } + } + html += '</div>'; + return html; + } + + return exports; + })(); + + for (var i = 0; i < Object.keys(jexcel.methods).length; i++) { + var methods = jexcel.methods[Object.keys(jexcel.methods)[i]]; + for (var j = 0; j < Object.keys(methods).length; j++) { + if (typeof(methods[Object.keys(methods)[j]]) == 'function') { + window[Object.keys(methods)[j]] = methods[Object.keys(methods)[j]]; + } else { + window[Object.keys(methods)[j]] = function() { + return Object.keys(methods)[j] + 'Not implemented'; + } + } + } + } + + return jexcel; +}))); \ No newline at end of file diff --git a/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jexcel/jexcel.js.xml b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jexcel/jexcel.js.xml new file mode 100644 index 0000000000000000000000000000000000000000..06094cf5020b3e189c97faa82754f0e17ead3a13 --- /dev/null +++ b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jexcel/jexcel.js.xml @@ -0,0 +1,28 @@ +<?xml version="1.0"?> +<ZopeData> + <record id="1" aka="AAAAAAAAAAE="> + <pickle> + <global name="File" module="OFS.Image"/> + </pickle> + <pickle> + <dictionary> + <item> + <key> <string>__name__</string> </key> + <value> <string>jexcel.js</string> </value> + </item> + <item> + <key> <string>content_type</string> </key> + <value> <string>application/javascript</string> </value> + </item> + <item> + <key> <string>precondition</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>title</string> </key> + <value> <string></string> </value> + </item> + </dictionary> + </pickle> + </record> +</ZopeData> diff --git a/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jsuites.xml b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jsuites.xml new file mode 100644 index 0000000000000000000000000000000000000000..fdafdb7edba4d238f6cd265924036864a5692587 --- /dev/null +++ b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jsuites.xml @@ -0,0 +1,26 @@ +<?xml version="1.0"?> +<ZopeData> + <record id="1" aka="AAAAAAAAAAE="> + <pickle> + <global name="Folder" module="OFS.Folder"/> + </pickle> + <pickle> + <dictionary> + <item> + <key> <string>_objects</string> </key> + <value> + <tuple/> + </value> + </item> + <item> + <key> <string>id</string> </key> + <value> <string>jsuites</string> </value> + </item> + <item> + <key> <string>title</string> </key> + <value> <string></string> </value> + </item> + </dictionary> + </pickle> + </record> +</ZopeData> diff --git a/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jsuites/jsuites.css.css b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jsuites/jsuites.css.css new file mode 100644 index 0000000000000000000000000000000000000000..2d5f8b871f4b2fad5bd46d34a2cc8d400c9e4902 --- /dev/null +++ b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jsuites/jsuites.css.css @@ -0,0 +1,3274 @@ + +/** + * (c) jSuites Javascript Web Components + * + * Author: Paul Hodel <paul.hodel@gmail.com> + * Website: https://bossanova.uk/jsuites/ + * Description: Create amazing web based applications. + * + * MIT License + * + */ + +/** General **/ + +:root { + --button-color: #298BA8; + --active-color: #007aff; +} + +.jdragging { + opacity:0.2; + filter: alpha(opacity=20); +} + +.jupload { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAACXBIWXMAAAsTAAALEwEAmpwYAAA7emlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxMzggNzkuMTU5ODI0LCAyMDE2LzA5LzE0LTAxOjA5OjAxICAgICAgICAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgICAgICAgICAgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIgogICAgICAgICAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgICAgICAgICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIj4KICAgICAgICAgPHhtcDpDcmVhdG9yVG9vbD5BZG9iZSBQaG90b3Nob3AgQ0MgMjAxNyAoV2luZG93cyk8L3htcDpDcmVhdG9yVG9vbD4KICAgICAgICAgPHhtcDpDcmVhdGVEYXRlPjIwMTctMTItMzFUMjI6NDM6MzBaPC94bXA6Q3JlYXRlRGF0ZT4KICAgICAgICAgPHhtcDpNb2RpZnlEYXRlPjIwMTgtMDEtMDFUMjA6MDM6MjNaPC94bXA6TW9kaWZ5RGF0ZT4KICAgICAgICAgPHhtcDpNZXRhZGF0YURhdGU+MjAxOC0wMS0wMVQyMDowMzoyM1o8L3htcDpNZXRhZGF0YURhdGU+CiAgICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2UvcG5nPC9kYzpmb3JtYXQ+CiAgICAgICAgIDxwaG90b3Nob3A6Q29sb3JNb2RlPjM8L3Bob3Rvc2hvcDpDb2xvck1vZGU+CiAgICAgICAgIDx4bXBNTTpJbnN0YW5jZUlEPnhtcC5paWQ6ZDE3NGVkY2UtNWZkNC0wODRkLTkzNzAtZDRhODVmMzdkNjdiPC94bXBNTTpJbnN0YW5jZUlEPgogICAgICAgICA8eG1wTU06RG9jdW1lbnRJRD54bXAuZGlkOjBmYzcxZDEyLTg1MGUtY2E0NS05MzE4LWRhYjI5YWZhMmQ4MjwveG1wTU06RG9jdW1lbnRJRD4KICAgICAgICAgPHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD54bXAuZGlkOjBmYzcxZDEyLTg1MGUtY2E0NS05MzE4LWRhYjI5YWZhMmQ4MjwveG1wTU06T3JpZ2luYWxEb2N1bWVudElEPgogICAgICAgICA8eG1wTU06SGlzdG9yeT4KICAgICAgICAgICAgPHJkZjpTZXE+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPmNyZWF0ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0Omluc3RhbmNlSUQ+eG1wLmlpZDowZmM3MWQxMi04NTBlLWNhNDUtOTMxOC1kYWIyOWFmYTJkODI8L3N0RXZ0Omluc3RhbmNlSUQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDp3aGVuPjIwMTctMTItMzFUMjI6NDM6MzBaPC9zdEV2dDp3aGVuPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6c29mdHdhcmVBZ2VudD5BZG9iZSBQaG90b3Nob3AgQ0MgMjAxNyAoV2luZG93cyk8L3N0RXZ0OnNvZnR3YXJlQWdlbnQ+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5zYXZlZDwvc3RFdnQ6YWN0aW9uPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6aW5zdGFuY2VJRD54bXAuaWlkOjk2MTA5Y2Y0LTEwM2ItNTc0Ny1hZWE3LWI5NDMxOWY0NWFkZjwvc3RFdnQ6aW5zdGFuY2VJRD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OndoZW4+MjAxOC0wMS0wMVQyMDowMjoyMFo8L3N0RXZ0OndoZW4+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpzb2Z0d2FyZUFnZW50PkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE3IChXaW5kb3dzKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmNoYW5nZWQ+Lzwvc3RFdnQ6Y2hhbmdlZD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPnNhdmVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDppbnN0YW5jZUlEPnhtcC5paWQ6ZDE3NGVkY2UtNWZkNC0wODRkLTkzNzAtZDRhODVmMzdkNjdiPC9zdEV2dDppbnN0YW5jZUlEPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6d2hlbj4yMDE4LTAxLTAxVDIwOjAzOjIzWjwvc3RFdnQ6d2hlbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnNvZnR3YXJlQWdlbnQ+QWRvYmUgUGhvdG9zaG9wIENDIDIwMTcgKFdpbmRvd3MpPC9zdEV2dDpzb2Z0d2FyZUFnZW50PgogICAgICAgICAgICAgICAgICA8c3RFdnQ6Y2hhbmdlZD4vPC9zdEV2dDpjaGFuZ2VkPgogICAgICAgICAgICAgICA8L3JkZjpsaT4KICAgICAgICAgICAgPC9yZGY6U2VxPgogICAgICAgICA8L3htcE1NOkhpc3Rvcnk+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgICAgIDx0aWZmOlhSZXNvbHV0aW9uPjcyMDAwMC8xMDAwMDwvdGlmZjpYUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6WVJlc29sdXRpb24+NzIwMDAwLzEwMDAwPC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpSZXNvbHV0aW9uVW5pdD4yPC90aWZmOlJlc29sdXRpb25Vbml0PgogICAgICAgICA8ZXhpZjpDb2xvclNwYWNlPjY1NTM1PC9leGlmOkNvbG9yU3BhY2U+CiAgICAgICAgIDxleGlmOlBpeGVsWERpbWVuc2lvbj44MDwvZXhpZjpQaXhlbFhEaW1lbnNpb24+CiAgICAgICAgIDxleGlmOlBpeGVsWURpbWVuc2lvbj44MDwvZXhpZjpQaXhlbFlEaW1lbnNpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIAo8P3hwYWNrZXQgZW5kPSJ3Ij8+tc8AsgAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAGjUlEQVR42uycW2xURRjHf91SoEIBUQpYI8VSVBDExniLERNUJBpQVEDFqE1AEYkX0JCC0YjgBdEYDOIFeVEEFRBilKBogg+KiQoV5Q6Khla8lrZRBLo+zLdazs7ZM2d3zsJu55/sy3dmz5797Vy+y8wWxONxnNJXzCFwAB1AB9ABdHIAHUAH0AF0Cq929fX1ufbMA4HLgSqgHOgPdJJrfwPbgb3AJuBD4KsoH6agrq4uF6CVAncC4wVYGP0ALAVeAva0NYDFwOPARKBzhvc6ArwG1AC/toU5cDSwFXjAAjyAQmACsAOozneATwLLgdMM2/8jLxN1AxYBC/J1CL8F3BjQZhOwCqgFvgd+AuJAT6ACOBMYCVwUcJ91siDlDcC3gRtSXF8IzAe+M7xfOXC3TAOFPm3WA0NzAeDp8oUGASeKrUF60R6Zn6an6CnTgI1pfnYFMAcY43N9GTDueAR4sQylK8RvS0dzgYcsPc9U4Bmfa9XA4uMF4GXypUdkeJ8Z0nNs6hbgdZ9rPcK6OLZX4XbisH5iAd68COABvAHc63NtwbHsgf2B94BKC/faAFwY8Zy8ErhWYx8EbM42wAuAj4ETAtptllh1N9AiC0t/YLCnXV9xT6JUJ6AOKPHYX5HIJ2sAK4BvgQ4+17fJ5LxKIgudzgBGySS/3sAPtKWZwCyPrQk4ydQxtwFwH9BbY4/LIvBEyF5RIF8iG+oC/K7xEUfLEI98EVnsA28/MCQkPIDmLMIDOCDOu1fXZGMVrgJu19gbZCKuJTek62mV2QD4XIoFZT+5o10a2ylRA6wCLtXYp8uikUv6Webr1uoOdIwS4HiNbS/wFLmnZuCgx9Y+hVdhBeBwjW1+BF9uFvBqFiDGDW3a0Cus+gADPLYWVB7PpiaJn5YYZjMigles6W2NskJH0gN1RZ1EJcyWJnvi0hpUbSQK9dRwaDGIqtIGeLbG9o3lnveCT2YmCogVPqvwdpOIKB2A7TU2W+XCiQEZkSggTvGxl8m0NNc2wDKNrcXCF5mASoUFaQYw2xK8Iv4vyvtpmsTxGQHsLPPQLp9frI+FYftyiPY1liAeBs5DZYNS/XgjgTXpApwq4Gaj0k86DbK4YISBmOlwjreaw+8CzvEDJa7bojAATwU+QtUQSg0Wlr4WFwxT2Z4Ta1GZ9Md8rlcDY00ADpFfZViID7/D8oJxrCACPCIjQ6elqOK8ryM9GPg64AM+B1YAW1BJ0r9M/aaQC0YYiAWWne0FQFf0dZnngdsgOaHaXVySLj43XSY3zCRVNclSz9NpTgQRywfAVRr7WcDWmKaxDl4TKuU+LkN4kyOEF1XEMk5GmFf3eOfAKcD5moY/ypy4OsMHeTjDBSPMcF5o8X4NPs70eKAoMYSLJIDuoOl5AwRiJroJWIIq1BwM8DcLDFyPpoAESTEqN/m0aVYlQF1RtRPviB2ZWEQeRJ//Gm0BHqhttieTutJVgkrGBu0FbEbtvmoMgNjLErxEL3yH5L01oxIA79O8aTVqj7ENmWSpGw1DwhZUJTBIf1ieGlZoAFbGxG3pESLIjkolBsMXaVNC9qWrnZTFZJh69SV283v5oHrNlNA9JvGfV0scL+3cm1Q7iaFPKNY6XimTD0fFwuWaRrscqyR11Hgqh2IkZ5gPy7LtdLRKNX7gnzHgkMdYGDIx0FZUrrHVxVBHBLzDup/jlaSrNbbdMWCn5sIAxyspsrlZY18TQ3/mYoxjdpQmkFx8OgKsjAHvat4wlFZZVyce1diWA80x4DOf7Ma8LD+kaYhmGvLZ0ovoa0LP0mpZ1qXXq4Fzs/igiSRBY8BrH3bq0Ca6HlWt82od6iTBfyn9Lj6+Xx2q2nYwSz3Q9FhrE/ZSVX66BPjU51q/RLCR6IEH0KfCe8sQL85SmNRo+Ioa3tgU8Ga2jtS8RaUdPj7gTtQRqS/yfLHogErf+6Xykk52ekOTESm67AbUrvuyPARXAtyPSvz6wduK5myx7pzIcPy3N4D6Z4w3pc0O1IbybAwrWyqSubZUOsYw4DrU4Ro/bUEd3m4wAZjoie8bPtARVNkvVwAWypxu6gqtRW0u0i6kqU4qDUEVUirasANdQ8BhoVSbizai/uRmbhsEtxp1lCPwpJXpWbmBqD++uTWPQ7xm1NaVJeIomzmvIQ8bdpO0zpWozea9Aibf41WHgd9QhaJtqAPia4FfQnv/7l98M5P79zYH0AF0AB1AJwfQAXQAHUCnNPTvAKemiFU6nb96AAAAAElFTkSuQmCC); + background-repeat: no-repeat; + background-size: 100px; + background-position: center; + background-color: rgba(230, 230, 230, 0.1); + border: 1px dotted #eee; + cursor: pointer; + box-sizing: border-box; + width:100%; + min-height:180px; +} + +.jremove { + opacity: 0.2; + filter: alpha(opacity=20); +} + +/** Animations **/ +.fade-in { + animation: fade-in 2s forwards; +} + +.fade-out { + animation: fade-out 1s forwards; +} + +.slide-left-in { + animation: slide-left-in 0.4s forwards; +} + +.slide-left-out { + animation: slide-left-out 0.4s forwards; +} + +.slide-right-in { + animation: slide-right-in 0.4s forwards; +} + +.slide-right-out { + animation: slide-right-out 0.4s forwards; +} + +.slide-top-in { + animation: slide-top-in 0.4s forwards; +} + +.slide-top-out { + animation: slide-top-out 0.2s forwards; +} + +.slide-bottom-in { + animation: slide-bottom-in 0.4s forwards; +} + +.slide-bottom-out { + animation: slide-bottom-out 0.1s forwards; +} + +/** Fadein and Fadeout **/ +@keyframes fade-in { + 0% { opacity: 0; } + 100% { opacity: 100; } +} + +@-webkit-keyframes fade-in { + 0% { opacity: 0; } + 100% { opacity: 100; } +} + +@keyframes fade-out { + 0% { opacity: 100; } + 100% { opacity: 0; } +} + +@-webkit-keyframes fade-out { + 0% { opacity: 100; } + 100% { opacity: 0; } +} + +/** Keyframes Left to Right **/ +@keyframes slide-left-in { + 0% { transform: translateX(-100%); } + 100% { transform: translateX(0%); } +} + +@-webkit-keyframes slide-left-in { + 0% { transform: translateX(-100%); } + 100% { -webkit-transform: translateX(0%); } +} + +@keyframes slide-left-out { + 0% { transform: translateX(0%); } + 100% { transform: translateX(-100%); } +} + +@-webkit-keyframes slide-left-out { + 0% { -webkit-transform: translateX(0%); } + 100% { -webkit-transform: translateX(-100%); } +} + +/** Keyframes Right to Left **/ +@keyframes slide-right-in { + 0% { transform: translateX(100%); } + 100% { transform: translateX(0%); } +} + +@-webkit-keyframes slide-right-in +{ + 0% { transform: translateX(100%); } + 100% { -webkit-transform: translateX(0%); } +} + +@keyframes slide-right-out { + 0% { transform: translateX(0%); } + 100% { transform: translateX(100%); } +} + +@-webkit-keyframes slide-right-out { + 0% { -webkit-transform: translateX(0%); } + 100% { -webkit-transform: translateX(100%); } +} + +/** Keyframes Top to Bottom **/ +@keyframes slide-top-in { + 0% { transform: translateY(-100%); } + 100% { transform: translateY(0%); } +} + +@-webkit-keyframes slide-top-in { + 0% { transform: translateY(-100%); } + 100% { -webkit-transform: translateY(0%); } +} + +@keyframes slide-top-out { + 0% { transform: translateY(0%); } + 100% { transform: translateY(-100%); } +} + +@-webkit-keyframes slide-top-out { + 0% { -webkit-transform: translateY(0%); } + 100% { -webkit-transform: translateY(-100%); } +} + +/** Keyframes Bottom to Top **/ +@keyframes slide-bottom-in { + 0% { transform: translateY(100%); } + 100% { transform: translateY(0%); } +} + +@-webkit-keyframes slide-bottom-in { + 0% { transform: translateY(100%); } + 100% { -webkit-transform: translateY(0%); } +} + +@keyframes slide-bottom-out { + 0% { transform: translateY(0%); } + 100% { transform: translateY(100%); } +} + +@-webkit-keyframes slide-bottom-out { + 0% { -webkit-transform: translateY(0%); } + 100% { -webkit-transform: translateY(100%); } +} + + +@supports (-webkit-overflow-scrolling: touch) { + .app .options input:checked:before { + top:-12px; + } +} + +@-webkit-keyframes spin { + from { + -webkit-transform:rotate(0deg); + } + to { + -webkit-transform:rotate(360deg); + } +} + +@keyframes spin { + from { + transform:rotate(0deg); + } + to { + transform:rotate(360deg); + } +} + +.unselectable { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.jbutton { + padding:4px; + padding-left:10px; + padding-right:20px; + border:1px solid #ddd; + display:inline-flex; + border-radius:4px; + cursor:pointer; + line-height: 24px; + -webkit-font-smoothing: antialiased; +} + +.jbutton.blue { + border:1px solid transparent; + color:#fff; + background-color:#1a73e8; +} + +.jbutton::before { + content: attr(data-icon); + width: 24px; + height: 24px; + font-size: 24px; + line-height: 24px; + font-family: 'Material icons'; + color: #999; + margin-right:2px; +} + +.jbutton.blue::before { + color: #fff; +} + +.jbutton:hover { + background-color: #eee; +} + +.jbutton.blue:hover { + background-color: #1a73e8; + opacity: 0.8; +} + +/** + * Date & Datetime picker v1.0.1 + * Author: paul.hodel@gmail.com + * https://github.com/paulhodel/jtools + */ + +.jcalendar { + position:relative; + z-index:9000; + display:none; + box-sizing:border-box; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-tap-highlight-color: rgba(0,0,0,0); + -webkit-tap-highlight-color: transparent; + min-width:280px; +} + +.jcalendar-focus { + display:block; +} + +.jcalendar-backdrop { + position:fixed; + top:0px; + left:0px; + z-index:9000; + min-width:100%; + min-height:100%; + background-color:rgba(0,0,0,0.5); + border:0px; + padding:0px; + display:none; +} + +.jcalendar-container { + position:relative; + box-sizing:border-box; +} + +.jcalendar-content { + position:absolute; + z-index:9001; + -webkit-box-shadow: 1px 1px 5px 0px rgba(0,0,0,0.39); + -moz-box-shadow: 1px 1px 5px 0px rgba(0,0,0,0.39); + box-shadow: 1px 1px 5px 0px rgba(0,0,0,0.39); + background-color:#fff; +} + +.jcalendar-table > table { + width:100%; + background-color:#fff; +} + +.jcalendar-table > table > tbody td { + box-sizing:border-box; + cursor:pointer; + padding:9px; + font-size:0.9em; +} + +.jcalendar-table > table > thead { + cursor:pointer; +} + +.jcalendar-header { + text-align:center; +} + +.jcalendar-header span { + margin-right:4px; + font-size:1.1em; + font-weight:bold; +} + +.jcalendar-prev { + cursor:pointer; + background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M15.41 16.59L10.83 12l4.58-4.59L14 6l-6 6 6 6 1.41-1.41z' fill='%23000' /%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3C/svg%3E"); + background-position:center; + background-repeat:no-repeat; +} + +.jcalendar-next { + cursor:pointer; + background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z' fill='%23000' /%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3C/svg%3E"); + background-position:center; + background-repeat:no-repeat; +} + +.jcalendar-weekday { + font-weight: 600; + background-color: #fcfcfc; + padding: 14px; +} + +.jcalendar thead td { + padding:10px; + height:40px; +} + +.jcalendar tfoot td { + padding:10px; +} + +.jcalendar-months td, .jcalendar-years td { + height:24px; +} + +.jcalendar-input { + padding-right:18px; + background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='gray'%3E%3Cpath d='M20 3h-1V1h-2v2H7V1H5v2H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 18H4V8h16v13z'/%3E%3Cpath fill='none' d='M0 0h24v24H0z'/%3E%3C/svg%3E"); + background-position:top 50% right 5px; + background-repeat:no-repeat; + box-sizing: border-box; +} + +.jcalendar-done { + -webkit-box-shadow: 1px 1px 5px 0px rgba(0,0,0,0.39); + -moz-box-shadow: 1px 1px 5px 0px rgba(0,0,0,0.39); + box-shadow: 1px 1px 5px 0px rgba(0,0,0,0.39); + background-color:#fff; +} + +.jcalendar-update { + border:1px solid #ccc; + background-color:#fff; + border-radius:4px; + padding:5px; + width:100%; +} + +.jcalendar select { + width:55px; + display:inline-block; + border:0px; + padding:4px; + text-align:center; + font-size:1.1em; + user-select:none; + margin-right:10px; +} + +.jcalendar select:first-child +{ + margin-right:2px; +} + +.jcalendar-selected { + background-color:#eee; +} + +.jcalendar-reset, .jcalendar-confirm { + text-transform:uppercase; + cursor:pointer; + color: var(--active-color); +} + +.jcalendar-controls { + padding:15px; + + -webkit-box-sizing: border-box; + box-sizing: border-box; + vertical-align:middle; + + display: -webkit-box; + display: -moz-box; + display: -ms-flexbox; + display: -webkit-flex; + display: flex; + + -webkit-flex-flow: row wrap; + justify-content: space-between; + align-items:center; + + border-bottom:1px solid #ddd; +} + +.jcalendar-controls div { + font-weight:bold; +} + +.jcalendar-fullsize { + position:fixed; + width:100%; + top:0px; + left:0px; +} + +.jcalendar-fullsize .jcalendar-content +{ + position:fixed; + width:100%; + left:0px; + bottom:0px; +} + +.jcalendar-focus.jcalendar-fullsize .jcalendar-backdrop { + display:block; +} + +.jcalendar-sunday { + color: red; +} +.jcalendar-disabled { + color: #ccc; +} + +.jcolor { + position: absolute; + display: none; + outline: none; +} + +.jcolor-content { + position: absolute; + left: 0px; + z-index: 9000; + user-select: none; + -webkit-font-smoothing: antialiased; + font-size: .875rem; + letter-spacing: .2px; + -webkit-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: 0 8px 10px 1px rgba(0,0,0,0.14), 0 3px 14px 2px rgba(0,0,0,0.12), 0 5px 5px -3px rgba(0,0,0,0.2); + box-shadow: 0 8px 10px 1px rgba(0,0,0,0.14), 0 3px 14px 2px rgba(0,0,0,0.12), 0 5px 5px -3px rgba(0,0,0,0.2); + background-color:#fff; + box-sizing: border-box; +} + +.jcolor-content table { + padding: 7px; + box-sizing: border-box; +} + +.jcolor-focus { + display:block; +} + +.jcolor table { + width:100%; + height:100%; +} + +.jcolor td { + border:2px solid #fff; +} + +.jcolor-selected { + border:2px solid #000 !important; + background-repeat:no-repeat; + background-size: cover; + background-position:0 0; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3Cpath d='M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z' fill='white'/%3E%3C/svg%3E"); +} + +.jcolor-fullscreen { + position: fixed; + bottom: 0px; + width:100%; + max-height:250px; + border-radius: 0px; + box-sizing: border-box; +} + +.jcolor-close { + display: none; + font-size: 1em; + color: var(--active-color); + text-transform: uppercase; + text-align: right; + padding: 15px; + font-weight: bold; + -webkit-box-shadow: 1px 0px 1px 0px rgba(0,0,0,0.39); + -moz-box-shadow: 1px 0px 1px 0px rgba(0,0,0,0.39); + box-shadow: 1px 0px 1px 0px rgba(0,0,0,0.39); + width: 100%; + box-sizing: border-box; +} + +.jcolor-fullscreen .jcolor-close { + display: block; +} + +.jcolor-backdrop { + position: fixed; + top: 0px; + left: 0px; + min-width: 100%; + min-height: 100%; + background-color: rgba(0,0,0,0.5); + border: 0px; + padding: 0px; + z-index: 8000; + display: none; + + -webkit-touch-callout: none; /* iOS Safari */ + -webkit-user-select: none; /* Safari */ + -khtml-user-select: none; /* Konqueror HTML */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* Internet Explorer/Edge */ + user-select: none; /* Non-prefixed version, currently + supported by Chrome and Opera */ +} + +/** + * Contextmenu v1.0.1 + * Author: paul.hodel@gmail.com + * https://github.com/paulhodel/jsuites + */ + +.jcontextmenu { + position:fixed; + z-index:10000; + background:#fff; + color: #555; + font-size: 11px; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + -webkit-box-shadow: 2px 2px 2px 0px rgba(143, 144, 145, 1); + -moz-box-shadow: 2px 2px 2px 0px rgba(143, 144, 145, 1); + box-shadow: 2px 2px 2px 0px rgba(143, 144, 145, 1); + border: 1px solid #C6C6C6; + padding: 0px; + padding-top:4px; + padding-bottom:4px; + margin:0px; + outline:none; + display:none; +} + +.jcontextmenu.jcontextmenu-focus { + display:inline-block; +} + +.jcontextmenu > div { + box-sizing: border-box; + display: block; + padding: 8px 8px 8px 28px; + width: 250px; + position: relative; + cursor: default; + font-size: 11px; + font-family:sans-serif; +} + +.jcontextmenu > div a { + color: #555; + text-decoration: none; +} + +.jcontextmenu > div span { + float: right; + margin-right:10px; +} + +.jcontextmenu .contextmenu-disabled { + color: #a1a192; +} + +.jcontextmenu > div:not(.contextmenu-line):hover { + background: #ebebeb; +} + +.jcontextmenu > div.contextmenu-line { + border-top: 1px solid #e9e9e9; + margin-top:5px; + padding:2px; +} + +.jcontextmenu hr { + border: 1px solid #e9e9e9; + border-bottom: 0; + margin-top:5px; + margin-bottom:5px; +} + +/** + * Dialog v1.0.1 + * Author: paul.hodel@gmail.com + * https://github.com/paulhodel/jtools + */ + +.jdialog +{ + position:fixed; + left: 0; + top: 0; + width: 100%; + min-height:100%; + z-index:10000; + + background-color:rgba(0,0,0,0.5); + -webkit-transition-duration: .1s; + transition-duration: .1s; + display: flex; + -ms-flex-align: center; + -webkit-align-items: center; + -webkit-box-align: center; + + align-items: center; +} + +.jdialog .jdialog-container +{ + width:100%; + max-width:280px; + box-sizing: border-box; + margin:0 auto; + vertical-align:middle; + background-color:#fff; + border-radius:10px; + + opacity: 0; + transition: opacity .2s; + -webkit-transition: opacity .2s; +} + +.jdialog .jdialog-header +{ + padding:10px; + text-align:center; +} + +.jdialog .jdialog-header .jdialog-title +{ + font-size:1em; + font-weight:bold; + padding:8px; +} + +.jdialog .jdialog-header .jdialog-message +{ + padding: 4px; +} + +.jdialog .jdialog-footer +{ + border-top: 1px solid #ccc; + display: flex; + flex-wrap: wrap; + align-content: stretch; + align-items: center; + text-align: center; +} + +.jdialog .jdialog-footer > div +{ + padding:4px; + border-left:1px solid #ccc; + text-align:center; + width:100%; +} +.jdialog .jdialog-footer > div:first-child +{ + border-left:0px; +} + +.jdialog .jdialog-footer input +{ + border: 0px; + margin: 0px; + background-color: transparent; + color: var(--active-color); + width: 100%; + outline: none; +} + +/** + * (c) 2013 jDropdown + * http://www.github.com/paulhodel/jdropdown + * + * @author: Paul Hodel <paul.hodel@gmail.com> + * @description: Custom dropdowns + */ + +.jdropdown +{ + cursor:pointer; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + box-sizing: border-box; + background:#fff; + -webkit-tap-highlight-color: transparent; + display: inline-block; +} + +.jdropdown-header::placeholder +{ + color:#000; +} + +.jdropdown-backdrop +{ + position:fixed; + top:0px; + left:0px; + min-width:100%; + min-height:100%; + background-color:rgba(0,0,0,0.5); + border:0px; + padding:0px; + z-index:8000; + display:none; +} + +.jdropdown-focus +{ + position:relative; +} + +.jdropdown-focus .jdropdown-container +{ + transform: translate3d(0,0,0); +} + +.jdropdown-focus .jdropdown-header +{ + outline:auto 5px -webkit-focus-ring-color; +} + +.jdropdown-container-header +{ + padding:0px; + margin:0px; + position:relative; +} + +.jdropdown-header +{ + width:100%; + appearance: none; + background-repeat: no-repeat; + background-position:top 50% right 5px; + background-image: url("data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath d='M7 10l5 5 5-5H7z' fill='gray'/%3E%3C/svg%3E"); + text-overflow: ellipsis; + cursor:pointer; + box-sizing: border-box; + -webkit-appearance: none; + -moz-appearance: none; + padding-right:24px; +} + +.jdropdown-insert-button +{ + font-size: 1.4em; + text-transform: uppercase; + position:absolute; + right: 30px; + top: 4px; + display:none; +} + +.jdropdown-container +{ + min-width: inherit; + transform: translate3d(-10000px,0,0); + position:absolute; + z-index:9001; +} + +.jdropdown-close +{ + display:none; + font-size:1em; + color: var(--active-color); + text-transform:uppercase; + text-align:right; + padding:15px; + font-weight:bold; +} + +.jdropdown-content +{ + min-width:inherit; + margin:0px; + box-sizing:border-box; +} + +.jdropdown-content:empty +{ +} + +.jdropdown-item +{ + white-space: nowrap; + text-align: left; + text-overflow: ellipsis; + overflow-x: hidden; + color: #000; + display: flex; + align-items: center; +} + +.jdropdown-image +{ + margin-right:10px; + width: 32px; + height: 32px; + border-radius:20px; +} + +.jdropdown-image-small +{ + width:24px; + height:24px; +} + +.jdropdown-title +{ + font-size: 0.7em; + text-overflow: ellipsis; + overflow-x: hidden; + display: block; +} + +/** Default visual **/ + +.jdropdown-default .jdropdown-header +{ + border:1px solid #ccc; + padding:5px; + padding-left:10px; + padding-right:16px; +} + +.jdropdown-default .jdropdown-container +{ + background-color:#fff; +} + +.jdropdown-default.jdropdown-focus.jdropdown-insert .jdropdown-header { + padding-right:50px; +} + +.jdropdown-default.jdropdown-focus.jdropdown-insert .jdropdown-insert-button { + display:block; +} + +.jdropdown-default .jdropdown-content +{ + min-width:inherit; + border:1px solid #8fb1e3; + margin:0px; + background-color:#fff; + box-sizing:border-box; + min-height:10px; + max-height:215px; + overflow-y:auto; +} + +.jdropdown-default .jdropdown-item +{ + padding:4px; + padding-left:8px; + padding-right:40px; +} + +.jdropdown-default .jdropdown-item:hover +{ + background-color:#1f93ff; + color:#fff; +} + +.jdropdown-default .jdropdown-cursor +{ + background-color:#1f93ff; + color:#fff; +} + +.jdropdown-default .jdropdown-selected +{ + background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMCAwaDI0djI0SDB6IiBmaWxsPSJub25lIiAvPjxwYXRoIGQ9Ik05IDE2LjE3TDQuODMgMTJsLTEuNDIgMS40MUw5IDE5IDIxIDdsLTEuNDEtMS40MXoiIGZpbGw9IndoaXRlIiAvPjwvc3ZnPgo='); + background-repeat:no-repeat; + background-position:top 50% right 5px; + background-color:#1f93ff; + color:#fff; +} + +.jdropdown-default .jdropdown-group +{ + margin-top:5px; +} + +.jdropdown-default .jdropdown-group .jdropdown-item +{ + padding-left:16px; +} + +.jdropdown-default .jdropdown-group-name +{ + padding-left:8px; + font-weight:bold; +} + + +/** Default render for mobile **/ + +.jdropdown-picker.jdropdown-focus .jdropdown-header { + outline: none; +} + +.jdropdown-picker.jdropdown-focus .jdropdown-backdrop { + display:block; +} + +.jdropdown-picker .jdropdown-container +{ + position:fixed; + bottom:0px; + left:0px; + border-bottom:1px solid #e6e6e8; + width:100%; + background-color:#fff; + box-sizing: border-box; +} + +.jdropdown-picker .jdropdown-close +{ + -webkit-box-shadow: 0px -1px 5px 0px rgba(0,0,0,0.39); + -moz-box-shadow: 0px -1px 5px 0px rgba(0,0,0,0.39); + box-shadow: 0px -1px 5px 0px rgba(0,0,0,0.39); + background-color:#fff; + display:block; +} + +.jdropdown-picker .jdropdown-content +{ + overflow-y:scroll; + height:280px; + background-color:#fafafa; + border-top:1px solid #e6e6e8; +} + +.jdropdown-picker .jdropdown-group-name +{ + font-size: 1em; + text-transform: uppercase; + padding-top:10px; + padding-bottom:10px; + display: block; + border-bottom: 1px solid #e6e6e8; + padding-left:20px; + padding-right:20px; + text-align:center; + font-weight:bold; +} + +.jdropdown-picker .jdropdown-item +{ + font-size: 1em; + text-transform: uppercase; + padding-top:10px; + padding-bottom:10px; + border-bottom: 1px solid #e6e6e8; + padding-left:20px; + padding-right:20px; +} + +.jdropdown-picker .jdropdown-selected +{ + background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMCAwaDI0djI0SDB6IiBmaWxsPSJub25lIiAvPjxwYXRoIGQ9Ik05IDE2LjE3TDQuODMgMTJsLTEuNDIgMS40MUw5IDE5IDIxIDdsLTEuNDEtMS40MXoiIGZpbGw9IndoaXRlIiAvPjwvc3ZnPgo='); + background-repeat:no-repeat; + background-position:top 50% right 15px; + background-color:#1f93ff; + color:#fff; +} + +.jdropdown-picker .jdropdown-cursor +{ + background-color:#1f93ff; + color:#fff; +} + +/** Default render for mobile searchbar **/ + +.jdropdown-searchbar.jdropdown-focus +{ + position:fixed; + top:0px; + left:0px; + width:100%; + height:100%; + background-color:#fafafa; + padding:0px; + z-index:9001; + overflow-y:scroll; +} + +.jdropdown-searchbar.jdropdown-focus .jdropdown-container-header +{ + padding:10px; + background-color:#fff; + box-shadow: 0 1px 2px rgba(0,0,0,.1); +} + +.jdropdown-searchbar.jdropdown-focus .jdropdown-header +{ + border: 0px; + background-repeat: no-repeat; + background-position-x: 0%; + background-position-y: 40%; + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMTUuNSAxNGgtLjc5bC0uMjgtLjI3QzE1LjQxIDEyLjU5IDE2IDExLjExIDE2IDkuNSAxNiA1LjkxIDEzLjA5IDMgOS41IDNTMyA1LjkxIDMgOS41IDUuOTEgMTYgOS41IDE2YzEuNjEgMCAzLjA5LS41OSA0LjIzLTEuNTdsLjI3LjI4di43OWw1IDQuOTlMMjAuNDkgMTlsLTQuOTktNXptLTYgMEM3LjAxIDE0IDUgMTEuOTkgNSA5LjVTNy4wMSA1IDkuNSA1IDE0IDcuMDEgMTQgOS41IDExLjk5IDE0IDkuNSAxNHoiIGZpbGw9IiNlNmU2ZTgiLz48cGF0aCBkPSJNMCAwaDI0djI0SDB6IiBmaWxsPSJub25lIi8+PC9zdmc+); + padding-left: 30px !important; + padding-right: 60px !important; + outline: none; +} + +.jdropdown-searchbar.jdropdown-focus .jdropdown-close +{ + display:block; +} + +.jdropdown-searchbar .jdropdown-container +{ + width:100%; +} + +.jdropdown-searchbar .jdropdown-close +{ + position:fixed; + top:0px; + right:0px; +} + +.jdropdown-searchbar .jdropdown-content +{ + margin-top:10px; +} + +.jdropdown-searchbar .jdropdown-group +{ + margin-top:10px; + margin-bottom:15px; + background-color:#fff; +} + +.jdropdown-searchbar .jdropdown-group-name +{ + border-top: 1px solid #e6e6e8; + border-bottom: 1px solid #e6e6e8; + padding:10px; + padding-left:12px; + font-weight:bold; +} + +.jdropdown-searchbar .jdropdown-group-arrow +{ + float:right; + width:24px; + height:24px; + background-repeat:no-repeat; +} + +.jdropdown-searchbar .jdropdown-group-arrow-down +{ + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNNy40MSA4LjU5TDEyIDEzLjE3bDQuNTktNC41OEwxOCAxMGwtNiA2LTYtNiAxLjQxLTEuNDF6Ii8+PHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMGgyNHYyNEgwVjB6Ii8+PC9zdmc+); +} + +.jdropdown-searchbar .jdropdown-group-arrow-up +{ + background-image: url(data:image/svg+xml;base64,CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTcuNDEgMTUuNDFMMTIgMTAuODNsNC41OSA0LjU4TDE4IDE0bC02LTYtNiA2eiIvPjxwYXRoIGQ9Ik0wIDBoMjR2MjRIMHoiIGZpbGw9Im5vbmUiLz48L3N2Zz4=); +} + +.jdropdown-searchbar .jdropdown-item +{ + padding-top:10px; + padding-bottom:10px; + border-bottom: 1px solid #e6e6e8; + padding-left:15px; + padding-right:40px; + background-color:#fff; + font-size:0.9em; +} + +.jdropdown-searchbar .jdropdown-description { + text-overflow: ellipsis; + overflow: hidden; + max-width: calc(100% - 20px); +} + +.jdropdown-searchbar .jdropdown-content > .jdropdown-item:first-child +{ + border-top: 1px solid #e6e6e8; +} + +.jdropdown-searchbar .jdropdown-selected +{ + background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMCAwaDI0djI0SDB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTkgMTYuMTdMNC44MyAxMmwtMS40MiAxLjQxTDkgMTkgMjEgN2wtMS40MS0xLjQxeiIgZmlsbD0iIzAwN2FmZiIvPjwvc3ZnPg=='); + background-repeat:no-repeat; + background-position:top 50% right 15px; +} + +/** List render **/ + +.jdropdown-list +{ +} + +.jdropdown-list .jdropdown-container +{ + display:block; +} + +.jdropdown-list .jdropdown-header +{ + display:none; +} + +.jdropdown-list .jdropdown-group +{ + background-color:#fff; +} + +.jdropdown-list .jdropdown-group-name +{ + border-bottom: 1px solid #e6e6e8; + padding-top:10px; + padding-bottom:10px; + font-weight:bold; +} + +.jdropdown-list .jdropdown-item +{ + padding-top:10px; + padding-bottom:10px; + border-bottom: 1px solid #e6e6e8; + padding-left:10px; + padding-right:40px; + background-color:#fff; +} + +.jdropdown-list .jdropdown-selected +{ + background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMCAwaDI0djI0SDB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTkgMTYuMTdMNC44MyAxMmwtMS40MiAxLjQxTDkgMTkgMjEgN2wtMS40MS0xLjQxeiIgZmlsbD0iIzAwN2FmZiIvPjwvc3ZnPg=='); + background-repeat:no-repeat; + background-position:top 50% right 10px; +} + +@media only screen and (max-device-width : 800px) +{ + .jdropdown-list + { + width:100% !important; + border:0px; + padding:0px; + } + + .jdropdown-list .jdropdown-container + { + min-width:100%; + } +} + +.app .jdropdown-item { + text-transform:uppercase; +} + +/** + * (c) jTools Text Editor + * https://github.com/paulhodel/jtools + * + * @author: Paul Hodel <paul.hodel@gmail.com> + * @description: Inline richtext editor + */ + + .jeditor-container { + border:1px solid #ccc; +} + +.jeditor-dragging { + border:1px dashed #000; +} + +.jeditor-container.jeditor-padding { + padding:10px; +} + +.jeditor { + outline:none; + word-break: break-word; + +} + +.jeditor-container.jeditor-padding .jeditor { + min-height:120px; + margin-bottom:10px; + padding:10px; +} + +.jeditor-toolbar { + cursor: pointer; + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-align-items: center; + align-items: center; + border: none; + -webkit-border-radius: 2px; + border-radius: 2px; + -webkit-box-shadow: 0 4px 5px 0 rgba(0,0,0,0.14), 0 1px 10px 0 rgba(0,0,0,0.12), 0 2px 4px -1px rgba(0,0,0,0.2); + box-shadow: 0 4px 5px 0 rgba(0,0,0,0.14), 0 1px 10px 0 rgba(0,0,0,0.12), 0 2px 4px -1px rgba(0,0,0,0.2); + margin: 0px; + margin-bottom:5px; + padding: 5px; + white-space: nowrap; + border-radius:2px; +} + +.jeditor-toolbar .jeditor-toolbar-group { + background-color:#f3f3f3; +} + +.jeditor-toolbar div { + padding:4px; +} + +.jeditor-toolbar div.jeditor-toolbar-button { + padding-left:20px; + padding-right:20px; +} + +.jeditor-toolbar div { + -webkit-user-select: none; /* Chrome all / Safari all */ + -moz-user-select: none; /* Firefox all */ + -ms-user-select: none; /* IE 10+ */ + user-select: none; /* Likely future */ +} + +.jeditor-toolbar div i { + display:block; + font-size:20px; + width:20px; + height:20px; + line-height:20px; + color:#777; +} + +.jeditor-toolbar select { + border:0px; + background-color:transparent; + padding:0px; + padding-left:5px; + padding-right:5px; + display:block; + height:32px; + outline:none; + border-radius:4px; + margin:1px; +} + +.jeditor-toolbar .jtoolbar-divisor { + width: 2px; + height: 28px; + padding: 0px; + margin: 4px; + background-color: #f2f2f2; +} + +.jeditor-toolbar select option { + background-color:#fff; + padding:20px; +} + +.jeditor-toolbar div:hover, .toolbar select:hover { + background-color:#f2f2f2; + border-radius:2px; +} + +.jeditor-toolbar div:hover i { + color:#222; +} + +.jeditor-toolbar div.selected { + background-color:#cecece; +} + +.jeditor-thumbs { + display:inline-block; + margin-right:5px; +} + +.jeditor-thumbs-container { + margin-top:15px; +} + +.jeditor-thumbs .close { + position:absolute; + height:1px; +} + +.jeditor-thumbs .close i { + color:#fff; + text-shadow: 0px 0px 2px #000; + position:relative; + top:5px; + left:72px; +} + +.jeditor-thumbs img { + border:1px solid #eee; + width:100px; + height:100px; +} + +.jeditor-users { + position:absolute; + border:1px solid #ccc; + background-color:#fff; + padding-top:5px; + padding-bottom:5px; + max-height:220px; + overflow-y:scroll; +} + +.jeditor-users > div +{ + display:flex; + align-items:center; + min-width:220px; + padding:10px; + padding-top:5px; + padding-bottom:5px; +} + +.jeditor-users > div:hover +{ + background-color:#efefef; +} + +.jeditor-users > div img +{ + width:24px; + height:24px; + border-radius:12px; + margin-right:5px; +} + +/** Snippet **/ + +.snippet { + margin-top:15px; + cursor:pointer; + border: 1px solid #eee; + position:relative; +} + +.snippet:focus { + outline: none; +} + +.snippet img { + width:25%; + max-width:120px; + float:left; + margin-right:10px; + margin-bottom:10px; +} + +.snippet .snippet-title { + margin-top:15px; + font-size:1.4em; +} + +.snippet .snippet-description { + margin-top:5px; + font-size:1em; +} + +.snippet .snippet-host { + margin-top:10px; + margin-bottom:10px; + text-transform:uppercase; + font-size:0.8em; + color:#777; + text-align:right; +} + +.snippet .snippet-url { + display:none; +} + +.jeditor .snippet:after { + content:''; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E"); + position:absolute; + top:0; + right:0; + margin:14px; + font-size:24px; + width:24px; + height:24px; + cursor:pointer; + text-shadow: 0px 0px 5px #fff; +} + +.snippet div, .snippet img { + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -o-user-select: none; + user-select: none; + + -webkit-user-drag: none; + -khtml-user-drag: none; + -moz-user-drag: none; + -o-user-drag: none; +} + +.jeditor img { + border:2px solid transparent; + box-sizing: border-box; +} + +.jeditor img:focus { + border:2px solid #0096FD; +} + +.jeditor .pdf { + background-image: url("data:image/svg+xml,%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 512 512' style='enable-background:new 0 0 512 512;' xml:space='preserve'%3E%3Cpath style='fill:%23C30B15;' d='M511.344,274.266C511.77,268.231,512,262.143,512,256C512,114.615,397.385,0,256,0S0,114.615,0,256 c0,117.769,79.53,216.949,187.809,246.801L511.344,274.266z'/%3E%3Cpath style='fill:%2385080E;' d='M511.344,274.266L314.991,77.913L119.096,434.087l68.714,68.714C209.522,508.787,232.385,512,256,512 C391.243,512,501.976,407.125,511.344,274.266z'/%3E%3Cpolygon style='fill:%23FFFFFF;' points='278.328,333.913 255.711,77.913 119.096,77.913 119.096,311.652 '/%3E%3Cpolygon style='fill:%23E8E6E6;' points='392.904,311.652 392.904,155.826 337.252,133.565 314.991,77.913 255.711,77.913 256.067,333.913 '/%3E%3Cpolygon style='fill:%23FFFFFF;' points='314.991,155.826 314.991,77.913 392.904,155.826 '/%3E%3Crect x='119.096' y='311.652' style='fill:%23FC0F1A;' width='273.809' height='122.435'/%3E%3Cg%3E%3Cpath style='fill:%23FFFFFF;' d='M204.871,346.387c13.547,0,21.341,6.659,21.341,18.465c0,12.412-7.795,19.601-21.341,19.601h-9.611 v14.909h-13.471v-52.975L204.871,346.387L204.871,346.387z M195.26,373.858h8.93c5.904,0,9.308-2.952,9.308-8.552 c0-5.525-3.406-8.324-9.308-8.324h-8.93V373.858z'/%3E%3Cpath style='fill:%23FFFFFF;' d='M257.928,346.387c16.649,0,28.152,10.746,28.152,26.487c0,15.666-11.655,26.488-28.683,26.488 h-22.25v-52.975H257.928z M248.619,388.615h9.611c8.249,0,14.151-6.357,14.151-15.665c0-9.384-6.205-15.817-14.757-15.817h-9.006 V388.615z'/%3E%3Cpath style='fill:%23FFFFFF;' d='M308.563,356.982v12.26h23.763v10.596h-23.763v19.525h-13.471v-52.975h39.277v10.595h-25.806 V356.982z'/%3E%3C/g%3E%3C/svg%3E%0A"); + background-repeat: no-repeat; + background-size: cover; + width:60px; + height:60px; +} + + + +/** + * (c) jLoading + * https://github.com/paulhodel/jtools + * + * @author: Paul Hodel <paul.hodel@gmail.com> + * @description: Page loading spin + */ + +.jloading { + position:fixed; + z-index:10001; + width:100%; + left:0; + right:0; + top:0; + bottom:0; + background-color: rgba(0,0,0,0.7); +} + +.jloading::after { + content:''; + display:block; + margin:0 auto; + margin-top:50vh; + width:40px; + height:40px; + border-style:solid; + border-color:white; + border-top-color:transparent; + border-width:4px; + border-radius:50%; + -webkit-animation: spin .8s linear infinite; + animation: spin .8s linear infinite; +} + +.jloading.spin { + background-color:transparent; +} + +.jloading.spin::after { + margin:0 auto; + margin-top:80px; + border-color:#aaa; + border-top-color:transparent; +} + + +.jlogin { + width: 100%; + box-sizing: border-box; + margin:0 auto; + vertical-align:middle; + border-radius: 5px; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + font-size:1em; +} + +.jlogin-fullscreen { + position:absolute; + top:0px; + left:0px; + width:100%; + height:100%; + background-color:#fff; + z-index:9000; +} + +.jlogin > form { + max-width:480px; + padding:20px; + margin: 0 auto; +} + +.jlogin > form > div { + margin-bottom:10px; +} + +.jlogin label { + display:block; +} + +.jlogin > form > div > input { + width:100%; + outline:none; + padding:15px; + margin:0px; +} + +.jlogin input[type='checkbox'] { + float:left; +} + +.jlogin input[type='button'] { + padding:10px; + background-color: var(--button-color); + border: 1px solid var(--button-color); + color: #fff; + cursor:pointer; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +.jlogin span +{ + margin:0 auto; +} +.jlogin img +{ + text-align:center; + max-width:220px; +} + +.jlogin .jlogin-logo +{ + text-align:center; + padding:20px; +} + +.jlogin .captcha +{ + width:100%; + margin-top:4px; + margin-bottom:4px; + border:1px solid #ccc; + display:block; +} + +.jlogin .facebookButton +{ + padding:10px; + background-color: var(--button-color); + border: 1px solid var(--button-color); + color: #fff; + text-align:center; + background-repeat:no-repeat; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24px' height='24px'%3E%3Cpath d='M19,3H5C3.895,3,3,3.895,3,5v14c0,1.105,0.895,2,2,2h7.621v-6.961h-2.343v-2.725h2.343V9.309 c0-2.324,1.421-3.591,3.495-3.591c0.699-0.002,1.397,0.034,2.092,0.105v2.43h-1.428c-1.13,0-1.35,0.534-1.35,1.322v1.735h2.7 l-0.351,2.725h-2.365V21H19c1.105,0,2-0.895,2-2V5C21,3.895,20.105,3,19,3z' fill='white'/%3E%3C/svg%3E%0A"); + background-position:10px 40%; + cursor:pointer; +} + +.jlogin .rememberButton +{ + padding:10px; + display:none; +} + +.jlogin .requestButton, .jlogin .cancelButton, .jlogin .newProfileButton +{ + padding:20px; + padding-bottom:0px; + text-align:center; + cursor:pointer; +} + +.jlogin-captcha img { + min-width:280px; +} + +.jlogin-loading:before { + content: ""; + display: block; + position: fixed; + top: 0px; + left: 0px; + width: 200px; + height: 4px; + background-color: var(--button-color); + animation: loading 2s linear infinite; +} + +@keyframes loading { + from { left: 0px; width: 25%; } + 25% { width: 25%; } + 50% { width: 50%; } + 75% { left: 75%; } + 100% { left: 100%; } + to { left: 100%; } +} + +@media only screen and (max-device-width : 800px) +{ + .jsuites * { + -webkit-tap-highlight-color: transparent; + -webkit-touch-callout: none; + } + + html.jsuites { + height:100%; + } + + .jsuites body { + margin:0px; + padding:0px; + width:100%; + height:100%; + min-height:100%; + line-height:1.4; + font-family:-apple-system,SF UI Text,Helvetica Neue,Helvetica,Arial,sans-serif !important; + background-color:#fff; + } + + .jsuites input, .jsuites select, .jsuites textarea, .jsuites div { + font-family:-apple-system,SF UI Text,Helvetica Neue,Helvetica,Arial,sans-serif !important; + } +} + +.japp +{ + height:100%; + box-sizing: border-box; + overflow:scroll; + background-color:#fafafa; +} + +.jwarning::before { + content:'No internet connection'; + background-color:red; + color:#fff; + padding:2px; + position:fixed; + top:0px; + left:0px; + z-index:20000; + width:100%; + font-size:0.55em; + text-align:center; +} + +/* +.japp .activeHighLight +{ + color:var(--active-color); +} + +.japp .active a, .japp .active span, .japp .active i +{ + color:var(--active-color); +} + +.japp .no-padding +{ + padding:0px; +} + +.japp .action +{ + color:var(--active-color); + cursor:pointer; +} + + +.japp i.action +{ + position:absolute; + right:0px; + margin-right:15px; + cursor:pointer; +} +*/ + +.japp button { + border-radius: 4px; + width: 100%; + border: 1px solid var(--active-color); + color: var(--active-color); + background-color: transparent; + padding: 4px; + outline: none; +} + +.japp button:active { + border: 1px solid #000; + color: #000; +} + +.japp .red { + color: red; + border-color: 1px solid red; +} + +.japp .link { + color: var(--active-color); + cursor: pointer; +} + +.japp .uppercase { + text-transform: uppercase; +} + +/** Navbar **/ + +.japp .navbar { + position:fixed; + top: 0; + width: 100%; + z-index: 500; + margin: 0; + -webkit-backface-visibility: hidden; + -webkit-transform: translate3d(0,0,0); + -webkit-box-sizing: border-box; + backface-visibility: hidden; + box-sizing: border-box; + transform: translate3d(0,0,0); + height: 44px; + background-color: #fff; + box-shadow: 0 1px 2px rgba(0,0,0,.1); +} + +.jwarning .navbar { + top:16px; +} + +.japp .navbar-container { + padding: 0 8px; + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + -webkit-box-sizing: border-box; + box-sizing: border-box; + vertical-align:middle; + + display: flex; + + -webkit-flex-flow: row wrap; + justify-content: space-between; + align-items:center; +} + +.japp .navbar-container div { + text-align:center; +} + +.japp .navbar-container div.title { + display: flex; +} + +.japp .navbar-container div.title div +{ + line-height:24px; +} + +.japp .navbar-container div.icon { + width:24px; +} + +.japp .navbar-container div i { + display:block; +} + +.japp .navbar-container div img { + height:28px; + display:block; +} + +.japp .navbar-container div.title div img { + border-radius:12px; + width:24px; + height:24px; + margin-right:6px; +} + +.japp .navbar .icon { + color:var(--active-color); +} + +.japp .title { + font-size:1.2em; +} + +.japp .block { + padding:15px; + color: #6d6d72; + font-size:0.9em; + box-sizing: border-box; +} + +.japp .block-title { + text-transform: uppercase; + color: #6d6d72; + margin: 25px 15px 10px; + line-height: 17px; + position: relative; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + line-height: 1; + font-size:0.9em; +} + +.japp .block-strong { + background-color:#fff; + color: #000; +} + +.japp .block-border { + border-top:1px solid #ddd; + border-bottom:1px solid #ddd; +} + +.japp .block-footer { + text-transform: uppercase; + padding:15px; + color: #6d6d72; + font-size:0.7em; +} + +.japp .block-collapse { + max-height:100px; + overflow-y:hidden; +} + +.japp .block-instruction { + text-transform: uppercase; + padding:15px; + color: #6d6d72; + font-size:0.7em; + text-align:center; +} + +.japp .pages { + display: flex; + flex-wrap: nowrap; + align-items: flex-start; + height: 100%; +} + +.japp .page { + padding-top: 45px; + padding-bottom: 45px; + min-height: 100%; + min-width: 100%; + box-sizing: border-box; + overflow-y: auto; + /*will-change: scroll-position; + -webkit-overflow-scrolling: touch; + -webkit-transform: translateZ(0px); + -webkit-transform: translate3d(0,0,0); + -webkit-perspective: 1000;*/ +} + +.warning .page { + padding-top: 60px; +} + +.japp .progressbar-container { + margin: 15px; + margin-left: 0px; + margin-right: 0px; + margin-top: 5px; +} + +.japp .progressbar { + width:100%; + border:1px solid var(--active-color); + border-radius:1px; +} + +.japp .progressbar-title { + margin-bottom:2px; + text-transform:uppercase; + display:flex; + justify-content:space-between; +} + +.japp .progressbar div { + background-color:var(--active-color); + height:4px; + width:0%; +} + +.japp .panel { + position:fixed; + top: 0; + left: 0; + margin: 0; + width:60vw; + max-width:220px; + height:100vh; + background: #fff; + z-index: 1003; + -webkit-box-sizing: border-box; + box-sizing: border-box; + box-shadow: 0 -1px 2px rgba(0,0,0,.1); + padding:4px; +} + +.japp .panel-left { + -webkit-box-shadow: 0px 2px 15px -5px rgba(0, 0, 0, 0.7); + box-shadow: 0px 2px 15px -5px rgba(0, 0, 0, 0.7); +} + +.japp .panel a { + color:var(--active-color); +} + +/** Toolbar **/ + +.japp .jtoolbar +{ + position:fixed; + bottom: 0; + margin: 0; + left: 0; + width: 100%; + background: #f7f7f8; + z-index: 500; + -webkit-box-sizing: border-box; + box-sizing: border-box; + box-shadow: 0 -1px 2px rgba(0,0,0,.1); + padding:4px; +} + +.japp .jtoolbar > div +{ + -webkit-box-sizing: border-box; + box-sizing: border-box; + vertical-align:middle; + + display: -webkit-box; + display: -moz-box; + display: -ms-flexbox; + display: -webkit-flex; + display: flex; + + -webkit-flex-flow: row wrap; + justify-content: space-evenly; + align-items:center; + width: 100%; + cursor:pointer; +} + +.japp .jtoolbar > div > div +{ + text-align:center; + flex:1; + margin:auto; +} + +.japp .jtoolbar a +{ + text-decoration:none; + display:inline-block; +} + +.japp .jtoolbar i +{ + color:#929292; +} + +.japp .jtoolbar span +{ + font-size:0.7em; + display:block; + color:#929292; +} + +.japp .jtoolbar .selected a, +.japp .jtoolbar .selected i, +.japp .jtoolbar .selected span +{ + color:var(--active-color); +} + + +/** Options **/ + +.japp .options +{ + background-color:#fff; + border-top:1px solid #e6e6e8; + border-bottom:1px solid #e6e6e8; + padding-left:15px; + margin-top:10px; +} + +.japp .options:empty +{ + display:none; +} + +.japp .options .option +{ + padding-top:10px; + padding-bottom:10px; + text-transform:uppercase; + font-size:1em; + border-bottom:1px solid #e6e6e8; + overflow-x:hidden; + + -webkit-box-sizing: border-box; + box-sizing: border-box; + vertical-align:middle; + + display: -webkit-box; + display: -moz-box; + display: -ms-flexbox; + display: -webkit-flex; + display: flex; + + -webkit-flex-flow: row nowrap; + justify-content: space-between; + align-items:center; + flex-wrap:nowrap; +} + +.japp .options .option:last-child +{ + border-bottom:0px; +} + +.japp .options label +{ + width:100%; +} + +.japp .options input[type='text'], .japp .options textarea +{ + outline:none; + border:1px solid transparent; + font-size:1em; + margin:0px; + padding:0px; + width:100%; + box-sizing:border-box; +} + +.japp .options textarea +{ + height:100px; +} + +.japp .options input[type='checkbox'], .japp .options input[type='radio'] +{ + visibility: hidden; +} + +.japp .options input[type='checkbox'] ~ i, .japp .options input[type='radio'] ~ i +{ + background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMCAwaDI0djI0SDB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTkgMTYuMTdMNC44MyAxMmwtMS40MiAxLjQxTDkgMTkgMjEgN2wtMS40MS0xLjQxeiIgZmlsbD0iIzAwN2FmZiIvPjwvc3ZnPg=='); + background-repeat: no-repeat; + content:''; + visibility: hidden; + margin-right:10px; + line-height:24px; + width:24px; + height:24px; +} + +.japp .options input[type='checkbox']:checked ~ i, .japp .options input[type='radio']:checked ~ i +{ + visibility: visible; +} + +.japp .options .jdropdown { + display: block; +} + +.japp .options .jdropdown .jdropdown-header { + border: 0px; + border-bottom: 1px solid #e6e6e8; + padding: 15px; + text-transform: uppercase; +} + +.japp .options .jdropdown:last-child .jdropdown-header { + border-bottom: 0px; +} + +.japp .options .jdropdown-searchbar.jdropdown-focus .jdropdown-header { + border:0px; + padding: 5px; +} + +.japp .options .icon { + float:left; + margin-right:10px; + max-width:40px; + max-height:40px; + border-radius:20px; + color:#929292; +} + +.japp .options .option .option-actions { + display:flex; + transform: translateX(100%); + width:0px; +} + +.japp .options .option .option-actions > div { + padding-right:5px; +} + +.japp .options .option .option-actions > div > i { + width:40px; + height:40px; + color:#fff; + font-size:24px; + line-height:40px; + text-align:center; + border-radius:30px; + background-color:red; +} + +.japp .options .option .option-actions.small > div > i +{ + color:#000; + width:24px; + height:24px; + font-size:24px; + line-height:24px; + border-radius:24px; + background-color:transparent; +} + +.japp .options .option-title { + display:block; + padding-top:10px; + padding-bottom:10px; + text-transform:uppercase; + font-size:1em; + vertical-align:center; +} + +.japp .options .option-title::after { + content:'\e313'; + font-family: 'material icons'; + font-size:24px; + margin-right:10px; + float:right; + width:24px; + height:24px; + line-height:24px; +} + +.japp .options .option-title.selected::after +{ + content:'\e316'; +} + +.japp .options .option-title.selected ~ div { + display:block; +} + +.japp .options .option-group { + border-top:1px solid #e6e6e8; + background-color:#fff; + display:none; +} + +.japp .options .option-link:after { + content:'\e315'; + font-family: 'material icons'; + color:var(--active-color); + font-size:24px; + margin-right:10px; + float:right; + width:24px; + height:24px; + line-height:24px; + display:block; +} + +.japp .options .option-header { + padding-left:10px; + padding-right:10px; + flex-grow:10; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.japp .options .option .option-name { + font-size:1em; + text-overflow: ellipsis; + width: 100%; + overflow: hidden; +} + +.japp .options .option .option-small { + font-size:0.7em; + color: #6d6d72; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + display:block; +} + +.japp .options .option .option-image img { + width:40px; + height:40px; + border-radius:20px; + display:block; +} + +.japp .options .option .option-image-small img { + width:24px; + height:auto; + border-radius:50px; + display:block; +} + +.japp .options .option .option-image .option-badge { + position: absolute; + background-color: red; + color: #fff; + width: 16px; + height: 16px; + line-height: 16px; + border-radius: 16px; + text-align: center; + font-size: 7px; + margin: 28px; +} + +.japp .options .option .option-image-small .option-badge { + position: absolute; + background-color: red; + color: #fff; + width: 16px; + height: 16px; + line-height: 16px; + border-radius: 16px; + text-align: center; + font-size: 7px; + margin: 12px; +} + +.japp .options .option .option-badge.solid { + background-color: red; + color: red; +} + +.japp .options .option .option-badge.solid-green { + background-color: green; + color: green; +} + +.japp .options .option .option-badge:empty { + display:none; +} + +.japp .options .option .option-date { + float:right; + font-size:0.6em; + color:#888; + margin-right:15px; + white-space: nowrap; +} + +.japp .options .option .option-right { + margin-right:15px; +} + +.japp .options .option .option-badget { + background-color:red; + border-radius:12px; + width:24px; + height:24px; + line-height:24px; + font-size:0.7em; + color:#fff; + text-align:center; +} + +.japp .options .option .option-badget:empty { + display:none; +} + +/** Badge **/ + +.japp .jbadge +{ + position:relative; + display:inline-block; + top:-12px; + left:-12px; +} + +.japp .jbadge > div +{ + background-color:red; + color:#fff; + width:16px; + height:16px; + line-height:16px; + border-radius:16px; + text-align:center; + font-size:8px; + position:absolute; +} + +.japp .jbadge > div:empty +{ + display:none; +} + +/** Picker **/ + +.japp .picker +{ + background-color:#fff; + border-top:1px solid #e6e6e8; + border-bottom:1px solid #e6e6e8; + padding-left:15px; + margin-top:10px; + position:absolute; + bottom:0px; + width:100%; + z-index:700; +} + +.japp .picker .picker-options +{ + -webkit-mask-image: linear-gradient(to bottom, rgba(0,0,0,1), rgba(0,0,0,.2)), linear-gradient(to top, rgba(0,0,0,1), rgba(0,0,0,.2)); + -webkit-mask-size: 100% 50%; + -webkit-mask-repeat: no-repeat; + -webkit-mask-position: center bottom, top center; + overflow-y:scroll; + padding-top:100px; + padding-bottom:100px; +} + +.japp .picker label +{ + display: block; + padding-top: 10px; + padding-bottom: 10px; +} + +.japp .picker .picker-selected +{ + border-top:1px solid #e6e6e8; + border-bottom:1px solid #e6e6e8; +} + +.japp .picker input +{ + display:none; +} + +/** Topsearch **/ + +.japp .top-search { + margin:0px; + padding:10px; + border-bottom:1px solid #ddd; +} + +.japp .top-search input { + background-color: #e6e6e6; + border: 0px; + border-radius: 4px; + outline: none; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='gray'%3E%3Cpath d='M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: 5px center; + padding-left: 32px; + width: 100%; +} + +.japp .searchbar-container i { + position:absolute; + margin-top:5px; + margin-left:8px; + color:#bbb; +} + +/** Range **/ + +.japp .range +{ + -webkit-appearance: none; + margin: 18px 0; + width: 100%; +} + +.japp .range:focus +{ + outline: none; +} + +.japp .range::-webkit-slider-runnable-track +{ + width: 100%; + height: 4px; + cursor: pointer; + animation: 0.2s; + background: #ccc; + border-radius: 1.3px; +} + +.japp .range::-webkit-slider-thumb +{ + -webkit-appearance:none; + appearance:none; + width:24px; + height:24px; + background:#fff; + cursor:pointer; + border-radius:24px; + margin-top:-10px; + border:0px; + box-shadow:0px 1px 3px 1px #bbb; +} + +.japp .range::-moz-range-track +{ + width:100%; + cursor:pointer; + animation:0.2s; + background:#ccc; + border-radius:1.3px; +} + +.japp .range::-moz-range-thumb +{ + width:24px; + height:24px; + background:#fff; + cursor:pointer; + border-radius:24px; + border:0px; + box-shadow:0px 1px 3px 1px #bbb; +} + +.japp .range::-ms-track +{ + width: 100%; + cursor: pointer; + animation: 0.2s; + background: transparent; + border-color: transparent; + border-width: 16px 0; + color: transparent; + border-radius:1.3px; +} + +.japp .range::-ms-fill-lower +{ + background:#ccc; + height:5px; +} + +.japp .range::-ms-fill-upper +{ + background:#ccc; + height:5px; +} + +.japp .range::-ms-thumb { + width:24px; + height:24px; + background:#fff; + cursor:pointer; + border-radius:24px; + border:0px; + box-shadow:0px 1px 3px 1px #bbb; +} + +.japp .range:focus::-ms-fill-lower +{ + background: #ccc; +} + +.japp .range:focus::-ms-fill-upper +{ + background: #ccc; +} + + +/** Actionsheet **/ + +.jactionsheet +{ + position:fixed; + left: 0; + bottom: 0; + width: 100%; + min-height:100%; + z-index:10000; + + background-color:rgba(0,0,0,0.5); + -webkit-transition-duration: .2s; + transition-duration: .2s; + display: flex; + -ms-flex-align: baseline; + -webkit-align-items: baseline; + -webkit-box-align: baseline; + + align-items: baseline; +} + +.jactionsheet .jactionsheet-content +{ + width:100%; + align-self: flex-end; +} + +.jactionsheet .jactionsheet-title +{ + font-size:1em; + font-weight:bold; + padding:8px; +} + +.jactionsheet .jactionsheet-message +{ + padding:4px; +} + +.jactionsheet .jactionsheet-group +{ + box-sizing: border-box; + margin:10px; + background-color:#fff; + border-radius:5px; +} + +.jactionsheet .jactionsheet-group > div +{ + font-size:1.4em; + font-weight:bold; + padding:4px; + border-top:1px solid #ccc; + text-align:center; + width:100%; +} +.jactionsheet .jactionsheet-group > div:first-child +{ + border-top:0px; +} + +.jactionsheet .jactionsheet-group input +{ + border:0px; + margin:0px; + background-color:transparent; + color:var(--active-color); + width:100%; + outline:none; + font-size:1em; +} + +.jactionsheet .jactionsheet-group input.jactionsheet-cancel +{ + color:red; +} + +/** Placeholders **/ + +.japp .options input::-webkit-input-placeholder +{ + text-transform:uppercase; +} + +.japp .options input::-moz-placeholder +{ + text-transform:uppercase; +} + +.japp .options input:-ms-input-placeholder +{ + text-transform:uppercase; +} + +.japp .options input:-moz-placeholder +{ + text-transform:uppercase; +} + +::placeholder +{ + color: #cccccc; + opacity: 1; +} + +:-ms-input-placeholder +{ + color: #cccccc; +} + +::-ms-input-placeholder +{ + color: #cccccc; +} + +::-webkit-input-placeholder +{ + color: #cccccc; +} + + +.jmodal { + position:fixed; + top:50%; + left:50%; + width:60%; + height:60%; + -webkit-box-shadow: 0 2px 10px rgba(0,0,0,.2); + -moz-box-shadow: 0 2px 10px rgba(0,0,0,.2); + border:1px solid #ccc; + background-color:#fff; + transform: translate(-50%, -50%); + box-sizing: border-box; + padding-top:50px; + z-index:9002; + border-radius:5px; +} + +.jmodal:before { + position:absolute; + top:0; + left:0; + width:100%; + content:attr(title); + padding:15px; + box-sizing: border-box; + background: #e3e3e3; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ededed', endColorstr='#e3e3e3'); + background: -webkit-gradient(linear, left top, left bottom, from(#ededed), to(#e3e3e3)); + background: -moz-linear-gradient(top, #ededed, #e3e3e3); + font-size:1.2em; +} + +.jmodal_content { + padding:20px; + overflow-y:auto; + max-height:100%; + box-sizing: border-box; +} +.jmodal.no-title { + padding-top:0px; +} + +.jmodal.no-title:before { + display:none; +} + +.jmodal:after { + content:''; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E"); + position:absolute; + top:0; + right:0; + margin:14px; + font-size:24px; + width:24px; + height:24px; + cursor:pointer; + text-shadow: 0px 0px 5px #fff; +} + +.jmodal_fullscreen { + width: 100% !important; + height: 100% !important; + top: 0px; + left: 0px; + transform: none; + border-radius: 0px; +} + + +.jmodal_backdrop { + position: fixed; + top: 0px; + left: 0px; + min-width: 100%; + min-height: 100%; + background-color: rgba(0,0,0,0.5); + border: 0px; + padding: 0px; + z-index: 8000; + display: none; + + -webkit-touch-callout: none; /* iOS Safari */ + -webkit-user-select: none; /* Safari */ + -khtml-user-select: none; /* Konqueror HTML */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* Internet Explorer/Edge */ + user-select: none; /* Non-prefixed version, currently + supported by Chrome and Opera */ +} + + +.jnotification { + position: fixed; + z-index: 10000; + -webkit-box-sizing: border-box; + box-sizing: border-box; + padding: 10px; +} + +.jnotification-container { + -webkit-box-shadow: 0px 2px 15px -5px rgba(0, 0, 0, 0.7); + box-shadow: 0px 2px 15px -5px rgba(0, 0, 0, 0.7); + padding: 12px; + border-radius: 8px; +} + +.jnotification-close { + content: ''; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='gray'%3E%3Cpath d='M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E"); + font-size: 20px; + width: 20px; + height: 20px; + cursor: pointer; +} + +.jnotification-title { + font-weight: bold; +} + +.jnotification-header { + display: flex; + padding-bottom: 5px; +} + +.jnotification-header:empty { + display: none; +} + +.jnotification-image { + margin-right: 5px; +} + +.jnotification-image:empty { + display: none; +} + +.jnotification-image img { + width: 24px; +} + +.jnotification-name { + text-transform: uppercase; + font-size: 0.9em; + flex: 1; + letter-spacing: 0.1em; +} + +@media (min-width: 320px) and (max-width: 800px) { + .jnotification { + top: 0px; + width: 100%; + } + .jnotification-container { + background: rgba(255,255,255,0.95); + border: 1px solid #eee; + } +} + +@media (min-width: 801px) { + .jnotification { + bottom: 0px; + } + .jnotification-container { + background-color: #000; + background: rgba(92,92,92,1); + background: linear-gradient(0deg, rgba(92,92,92,1) 0%, rgba(77,77,77,1) 100%); + color: #fff; + width: 320px; + margin: 30px; + padding: 20px; + } +} + +.jnotification-header { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; + -webkit-box-align: center; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; +} + +.jrating { + display:flex; +} +.jrating > div { + width:24px; + height:24px; + line-height:24px; + background-image: url("data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M22 9.24l-7.19-.62L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21 12 17.27 18.18 21l-1.63-7.03L22 9.24zM12 15.4l-3.76 2.27 1-4.28-3.32-2.88 4.38-.38L12 6.1l1.71 4.04 4.38.38-3.32 2.88 1 4.28L12 15.4z' fill='gray'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E"); +} + +.jrating .jrating-over { + background-image: url("data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='black'%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3Cpath d='M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E"); + opacity: 0.7; +} + +.jrating .jrating-selected { + background-image: url("data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='red'%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3Cpath d='M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E"); +} + + +/** + * (c) Image slider + * https://github.com/paulhodel/jtools + * + * @author: Paul Hodel <paul.hodel@gmail.com> + * @description: Image Slider + */ + +.jslider { + margin-top:10px; + margin-bottom:10px; +} + +.jslider-container img { + width:60px; + margin-right:5px; + margin-bottom:5px; +} + +.jslider-attach { + cursor:pointer; +} + +.jslider-left::before { + position: fixed; + left: 10px; + content:'arrow_back_ios'; + color: #fff; + width: 30px; + height: 30px; + font-family: 'Material Icons'; + font-size: 30px; + text-shadow: 0px 0px 0px #000; + text-align: center; +} + +.jslider-right::after { + position: fixed; + right: 5px; + content: 'arrow_forward_ios'; + color: #fff; + width: 30px; + height: 30px; + font-family: 'Material Icons'; + font-size: 30px; + text-shadow: 0px 0px 0px #000; + text-align: center; +} + +.jslider-close { + width:24px; + height:24px; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='white'%3E%3Cpath d='M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E"); + position:fixed; + top:15px; + right:15px; + display:none; + cursor:pointer; + z-index:3000; +} + +.jslider-preview { + position:fixed; + left:0; + top:0; + width: 100%; + min-height:100%; + z-index:2000; + margin:0px; + box-sizing:border-box; + + background-color:rgba(0,0,0,0.9); + -webkit-transition-duration: .4s; + transition-duration: .4s; + display: flex; + -ms-flex-align: center; + -webkit-align-items: center; + -webkit-box-align: center; + + align-items: center; +} + +.jslider-preview img { + min-width:70%; + max-width:100%; + height:auto; + box-sizing: border-box; + margin:0 auto; + vertical-align:middle; + display:none; +} + + +.jswitch { + display: inline-block; + cursor: pointer; + -webkit-tap-highlight-color: transparent; +} + +.jswitch i { + position: relative; + display: inline-block; + margin-right: .5rem; + width: 46px; + height: 26px; + background-color: #e6e6e6; + border-radius: 23px; + vertical-align: text-bottom; + transition: all 0.3s linear; +} + +.jswitch i::before { + content: ""; + position: absolute; + left: 0; + width: 42px; + height: 22px; + background-color: #fff; + border-radius: 11px; + transform: translate3d(2px, 2px, 0) scale3d(1, 1, 1); + transition: all 0.25s linear; +} + +.jswitch i::after { + content: ""; + position: absolute; + left: 0; + width: 22px; + height: 22px; + background-color: #fff; + border-radius: 11px; + box-shadow: 0 2px 2px rgba(0, 0, 0, 0.24); + transform: translate3d(2px, 2px, 0); + transition: all 0.2s ease-in-out; +} + +.jswitch:active i::after { + width: 28px; + transform: translate3d(2px, 2px, 0); +} + +.jswitch:active input:checked + i::after { transform: translate3d(16px, 2px, 0); } + +.jswitch input { display: none; } + +.jswitch input:checked + i { background-color: #4BD763; } + +.jswitch input:checked + i::before { transform: translate3d(18px, 2px, 0) scale3d(0, 0, 0); } + +.jswitch input:checked + i::after { transform: translate3d(22px, 2px, 0); } + +.jtabs { + max-width: calc(100vw - 24px); + position: relative; +} +.jtabs > .jtabs-headers { + display: flex; + overflow-x: auto; +} + +.jtabs > .jtabs-headers > div { + padding: 6px; + padding-left: 20px; + padding-right: 20px; + margin-left: 1px; + margin-right: 1px; + margin-bottom: 1px; + background-color: #f1f1f1; + cursor: pointer; +} + +.jtabs > .jtabs-headers > div.jtabs-selected { + background-color: #e8e8e8; + color: #000; +} + +.jtabs > .jtabs-content > div { + display: none; +} + +.jtabs > .jtabs-content > div.jtabs-selected { + display: block; +} + +.jtabs > .jtabs-border { + position: absolute; + height: 2px; + background-color: #888; + transform-origin: left; + transition: all .2s cubic-bezier(0.4,0,0.2,1); + transition-property: color,left,transform; +} + +.jtabs .jtabs-add { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24' viewBox='0 0 24 24' width='24'%3E%3Cpath d='M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-2 10h-4v4h-2v-4H7v-2h4V7h2v4h4v2z' fill='%23bbbbbb'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E"); + cursor: pointer; + background-position: center; + background-repeat: no-repeat; + width: 24px; + height: 24px; + line-height: 24px; + margin: 3px; + margin-left: 10px; +} + +.jtags { + display: flex; + flex-wrap: wrap; + -ms-flex-direction: row; + -webkit-flex-direction: row; + flex-direction: row; + -ms-flex-pack: flex-start; + -webkit-justify-content: space-between; + justify-content: flex-start; + padding: 2px; + border: 1px solid #ccc; +} + +.jtags > div { + padding: 3px; + padding-left: 10px; + padding-right: 22px; + position: relative; + border-radius: 1px; + margin: 2px; + display: block; + outline: none; +} + +.jtags > div::after { + content: 'x'; + position: absolute; + top: 4px; + right: 4px; + width: 12px; + height: 12px; + cursor: pointer; + font-size: 11px; + display: none; +} + +.jtags_label { + background-color: #eeeeee !important; +} + +.jtags_label::after { + display: inline-block !important; +} + +.jtags_error::after { + color: #fff !important; +} + +.jtags_error { + background-color: #d93025 !important; + color: #fff; +} + + +.jtags_search { + position: absolute; + display: none; + box-shadow: 0 1px 2px 0 rgba(60,64,67,0.302), 0 2px 6px 2px rgba(60,64,67,0.149); + border: none; + -webkit-border-radius: 4px; + border-radius: 4px; + width: 280px; + padding: 8px 0; + + -webkit-box-shadow: 0 2px 4px rgba(0,0,0,0.2); + box-shadow: 0 2px 4px rgba(0,0,0,0.2); + -webkit-transition: opacity .218s; + transition: opacity .218s; + background: #fff; + border: 1px solid rgba(0,0,0,.2); + cursor: pointer; + margin: 0; + min-width: 300px; + outline: none; + width: auto; + user-select: none; +} + +.jtags_search > div { + color: #333; + cursor: pointer; + display: -webkit-box; + display: -webkit-flex; + display: flex; + padding: 5px 10px; + user-select: none; + -webkit-align-items: center; + align-items: center; +} + +.jtags_search > div:hover { + background-color: #e8eaed; +} + +.jtags_search > div > img { + width: 32px; + height: 32px; + user-select: none; + border-radius: 16px; + margin-right: 2px; +} + +.jtags_search > div > div { + overflow: hidden; + text-overflow: ellipsis; + margin-left: 2px; + max-width: 300px; + white-space: nowrap; + user-select: none; +} + +.jtags_search .selected { + background-color: #e8eaed; +} + +/** + * (c) jTools Template renderer + * https://github.com/paulhodel/jtools + * + * @author: Paul Hodel <paul.hodel@gmail.com> + * @description: Template renderer + */ + +.jtemplate-pagination { + display:flex; + cursor:pointer; + margin-top:10px; + margin-bottom:10px; +} + +.jtemplate-pagination div { + text-align:center; + font-size:0.8em; + width:30px; + height:30px; + line-height:30px; + -webkit-touch-callout: none; /* iOS Safari */ + -webkit-user-select: none; /* Safari */ + khtml-user-select: none; /* Konqueror HTML */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* Internet Explorer/Edge */ + user-select: none; +} + +.jtemplate-pagination div:hover { + background-color:#eee; +} + +.jtemplate-results { + margin-top:15px; + margin-bottom:15px; +} + +.app .jtemplate-empty { + text-align:center; + padding-top:10px; + padding-bottom:10px; + margin-right:10px; +} + + + diff --git a/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jsuites/jsuites.css.xml b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jsuites/jsuites.css.xml new file mode 100644 index 0000000000000000000000000000000000000000..c2a7e41dab0e20f582da6b3373c2b7adc7971851 --- /dev/null +++ b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jsuites/jsuites.css.xml @@ -0,0 +1,28 @@ +<?xml version="1.0"?> +<ZopeData> + <record id="1" aka="AAAAAAAAAAE="> + <pickle> + <global name="File" module="OFS.Image"/> + </pickle> + <pickle> + <dictionary> + <item> + <key> <string>__name__</string> </key> + <value> <string>jsuites.css</string> </value> + </item> + <item> + <key> <string>content_type</string> </key> + <value> <string>text/css</string> </value> + </item> + <item> + <key> <string>precondition</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>title</string> </key> + <value> <string></string> </value> + </item> + </dictionary> + </pickle> + </record> +</ZopeData> diff --git a/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jsuites/jsuites.js.js b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jsuites/jsuites.js.js new file mode 100644 index 0000000000000000000000000000000000000000..b01ba28ba8bc929b621886eaa2f77ee73cf5fef1 --- /dev/null +++ b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jsuites/jsuites.js.js @@ -0,0 +1,8949 @@ +/** + * (c) jSuites Javascript Web Components (v2.7) + * + * Author: Paul Hodel <paul.hodel@gmail.com> + * Website: https://bossanova.uk/jsuites/ + * Description: Create amazing web based applications. + * + * MIT License + * + */ +;(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + global.jSuites = factory(); +}(this, (function () { + + 'use strict'; + +var jSuites = function(options) { + var obj = {} + + obj.init = function() { + // Find root element + var app = document.querySelector('.japp'); + + // Root element + if (app) { + obj.el = app; + } else { + obj.el = document.body; + } + + // Loading modules + var modules = document.querySelectorAll('[data-autoload]'); + for (var i = 0; i < modules.length; i++) { + var m = modules[i].getAttribute('data-autoload'); + if (typeof(window[m]) == 'function') { + window[m](modules[i]); + } + } + } + + obj.guid = function() { + var guid = ''; + for (var i = 0; i < 32; i++) { + guid += Math.floor(Math.random()*0xF).toString(0xF); + } + return guid; + } + + obj.getWindowWidth = function() { + var w = window, + d = document, + e = d.documentElement, + g = d.getElementsByTagName('body')[0], + x = w.innerWidth || e.clientWidth || g.clientWidth; + return x; + } + + obj.getWindowHeight = function() { + var w = window, + d = document, + e = d.documentElement, + g = d.getElementsByTagName('body')[0], + y = w.innerHeight|| e.clientHeight|| g.clientHeight; + return y; + } + + obj.getPosition = function(e) { + if (e.changedTouches && e.changedTouches[0]) { + var x = e.changedTouches[0].pageX; + var y = e.changedTouches[0].pageY; + } else { + var x = (window.Event) ? e.pageX : e.clientX + (document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft); + var y = (window.Event) ? e.pageY : e.clientY + (document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop); + } + + return [ x, y ]; + } + + obj.click = function(el) { + if (el.click) { + el.click(); + } else { + var evt = new MouseEvent('click', { + bubbles: true, + cancelable: true, + view: window + }); + el.dispatchEvent(evt); + } + } + + obj.getElement = function(element, className) { + var foundElement = false; + + function path (element) { + if (element.className) { + if (element.classList.contains(className)) { + foundElement = element; + } + } + + if (element.parentNode) { + path(element.parentNode); + } + } + + path(element); + + return foundElement; + } + + obj.getLinkElement = function(element) { + var targetElement = false; + + function path (element) { + if ((element.tagName == 'A' || element.tagName == 'DIV') && element.getAttribute('data-href')) { + targetElement = element; + } + + if (element.parentNode) { + path(element.parentNode); + } + } + + path(element); + + return targetElement; + } + + obj.getFormElements = function(formObject) { + var ret = {}; + + if (formObject) { + var elements = formObject.querySelectorAll("input, select, textarea"); + } else { + var elements = document.querySelectorAll("input, select, textarea"); + } + + for (var i = 0; i < elements.length; i++) { + var element = elements[i]; + var name = element.name; + var value = element.value; + + if (name) { + ret[name] = value; + } + } + + return ret; + } + + obj.exists = function(url, __callback) { + var http = new XMLHttpRequest(); + http.open('HEAD', url, false); + http.send(); + if (http.status) { + __callback(http.status); + } + } + + obj.getFiles = function(element) { + if (! element) { + console.error('No element defined in the arguments of your method'); + } + + // Get attachments + var files = element.querySelectorAll('.jfile'); + + if (files.length > 0) { + var data = []; + for (var i = 0; i < files.length; i++) { + var file = {}; + + var src = files[i].getAttribute('src'); + + if (files[i].classList.contains('jremove')) { + file.remove = 1; + } else { + if (src.substr(0,4) == 'data') { + file.content = src.substr(src.indexOf(',') + 1); + file.extension = files[i].getAttribute('data-extension'); + } else { + file.file = src; + file.extension = files[i].getAttribute('data-extension'); + if (! file.extension) { + file.extension = src.substr(src.lastIndexOf('.') + 1); + } + if (jSuites.files[file.file]) { + file.content = jSuites.files[file.file]; + } + } + + // Optional file information + if (files[i].getAttribute('data-name')) { + file.name = files[i].getAttribute('data-name'); + } + + if (files[i].getAttribute('data-file')) { + file.file = files[i].getAttribute('data-file'); + } + + if (files[i].getAttribute('data-size')) { + file.size = files[i].getAttribute('data-size'); + } + + if (files[i].getAttribute('data-date')) { + file.date = files[i].getAttribute('data-date'); + } + + if (files[i].getAttribute('data-cover')) { + file.cover = files[i].getAttribute('data-cover'); + } + } + + // TODO SMALL thumbs? + + data[i] = file; + } + + return data; + } + } + + obj.ajax = function(options) { + if (! options.data) { + options.data = {}; + } + + if (options.type) { + options.method = options.type; + } + + if (options.data) { + var data = []; + var keys = Object.keys(options.data); + + if (keys.length) { + for (var i = 0; i < keys.length; i++) { + if (typeof(options.data[keys[i]]) == 'object') { + var o = options.data[keys[i]]; + for (var j = 0; j < o.length; j++) { + if (typeof(o[j]) == 'string') { + data.push(keys[i] + '[' + j + ']=' + encodeURIComponent(o[j])); + } else { + var prop = Object.keys(o[j]); + for (var z = 0; z < prop.length; z++) { + data.push(keys[i] + '[' + j + '][' + prop[z] + ']=' + encodeURIComponent(o[j][prop[z]])); + } + } + } + } else { + data.push(keys[i] + '=' + encodeURIComponent(options.data[keys[i]])); + } + } + } + + if (options.method == 'GET' && data.length > 0) { + if (options.url.indexOf('?') < 0) { + options.url += '?'; + } + options.url += data.join('&'); + } + } + + var httpRequest = new XMLHttpRequest(); + httpRequest.open(options.method, options.url, true); + httpRequest.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); + + if (options.method == 'POST') { + httpRequest.setRequestHeader('Accept', 'application/json'); + httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + } else { + if (options.dataType == 'json') { + httpRequest.setRequestHeader('Content-Type', 'text/json'); + } + } + + // No cache + if (options.cache != true) { + httpRequest.setRequestHeader('pragma', 'no-cache'); + httpRequest.setRequestHeader('cache-control', 'no-cache'); + } + + // Authentication + if (options.withCredentials == true) { + httpRequest.withCredentials = true + } + + // Before send + if (typeof(options.beforeSend) == 'function') { + options.beforeSend(httpRequest); + } + + httpRequest.onload = function() { + if (httpRequest.status === 200) { + if (options.dataType == 'json') { + try { + var result = JSON.parse(httpRequest.responseText); + + if (options.success && typeof(options.success) == 'function') { + options.success(result); + } + } catch(err) { + if (options.error && typeof(options.error) == 'function') { + options.error(result); + } + } + } else { + var result = httpRequest.responseText; + + if (options.success && typeof(options.success) == 'function') { + options.success(result); + } + } + } else { + if (options.error && typeof(options.error) == 'function') { + options.error(httpRequest.responseText); + } + } + + // Global complete method + if (obj.ajax.requests && obj.ajax.requests.length) { + // Get index of this request in the container + var index = obj.ajax.requests.indexOf(httpRequest) + // Remove from the ajax requests container + obj.ajax.requests.splice(index, 1); + // Last one? + if (! obj.ajax.requests.length) { + if (options.complete && typeof(options.complete) == 'function') { + options.complete(result); + } + } + } + } + + if (data) { + httpRequest.send(data.join('&')); + } else { + httpRequest.send(); + } + + obj.ajax.requests.push(httpRequest); + + return httpRequest; + } + + obj.ajax.requests = []; + + obj.slideLeft = function(element, direction, done) { + if (direction == true) { + element.classList.add('slide-left-in'); + setTimeout(function() { + element.classList.remove('slide-left-in'); + if (typeof(done) == 'function') { + done(); + } + }, 400); + } else { + element.classList.add('slide-left-out'); + setTimeout(function() { + element.classList.remove('slide-left-out'); + if (typeof(done) == 'function') { + done(); + } + }, 400); + } + } + + obj.slideRight = function(element, direction, done) { + if (direction == true) { + element.classList.add('slide-right-in'); + setTimeout(function() { + element.classList.remove('slide-right-in'); + if (typeof(done) == 'function') { + done(); + } + }, 400); + } else { + element.classList.add('slide-right-out'); + setTimeout(function() { + element.classList.remove('slide-right-out'); + if (typeof(done) == 'function') { + done(); + } + }, 400); + } + } + + obj.slideTop = function(element, direction, done) { + if (direction == true) { + element.classList.add('slide-top-in'); + setTimeout(function() { + element.classList.remove('slide-top-in'); + if (typeof(done) == 'function') { + done(); + } + }, 400); + } else { + element.classList.add('slide-top-out'); + setTimeout(function() { + element.classList.remove('slide-top-out'); + if (typeof(done) == 'function') { + done(); + } + }, 400); + } + } + + obj.slideBottom = function(element, direction, done) { + if (direction == true) { + element.classList.add('slide-bottom-in'); + setTimeout(function() { + element.classList.remove('slide-bottom-in'); + if (typeof(done) == 'function') { + done(); + } + }, 400); + } else { + element.classList.add('slide-bottom-out'); + setTimeout(function() { + element.classList.remove('slide-bottom-out'); + if (typeof(done) == 'function') { + done(); + } + }, 100); + } + } + + obj.fadeIn = function(element, done) { + element.classList.add('fade-in'); + setTimeout(function() { + element.classList.remove('fade-in'); + if (typeof(done) == 'function') { + done(); + } + }, 2000); + } + + obj.fadeOut = function(element, done) { + element.classList.add('fade-out'); + setTimeout(function() { + element.classList.remove('fade-out'); + if (typeof(done) == 'function') { + done(); + } + }, 1000); + } + + obj.keyDownControls = function(e) { + if (e.which == 27) { + var nodes = document.querySelectorAll('.jslider'); + if (nodes.length > 0) { + for (var i = 0; i < nodes.length; i++) { + nodes[i].slider.close(); + } + } + + if (document.querySelector('.jdialog')) { + jSuites.dialog.close(); + } + } else if (e.which == 13) { + if (document.querySelector('.jdialog')) { + if (typeof(jSuites.dialog.options.onconfirm) == 'function') { + jSuites.dialog.options.onconfirm(); + } + jSuites.dialog.close(); + } + } + + // Verify mask + if (jSuites.mask) { + jSuites.mask.apply(e); + } + } + + var actionUpControl = function(e) { + var element = null; + if (element = jSuites.getLinkElement(e.target)) { + var link = element.getAttribute('data-href'); + if (link == '#back') { + window.history.back(); + } else if (link == '#panel') { + jSuites.panel(); + } else { + jSuites.pages(link); + } + } + } + + var controlSwipeLeft = function(e) { + var element = jSuites.getElement(e.target, 'option'); + + if (element && element.querySelector('.option-actions')) { + element.scrollTo({ + left: 100, + behavior: 'smooth' + }); + } else { + var element = jSuites.getElement(e.target, 'jcalendar'); + if (element && jSuites.calendar.current) { + jSuites.calendar.current.prev(); + } else { + if (jSuites.panel) { + var element = jSuites.panel.get(); + if (element) { + if (element.style.display != 'none') { + jSuites.panel.close(); + } + } + } + } + } + } + + var controlSwipeRight = function(e) { + var element = jSuites.getElement(e.target, 'option'); + if (element && element.querySelector('.option-actions')) { + element.scrollTo({ + left: 0, + behavior: 'smooth' + }); + } else { + var element = jSuites.getElement(e.target, 'jcalendar'); + if (element && jSuites.calendar.current) { + jSuites.calendar.current.next(); + } else { + if (jSuites.panel) { + var element = jSuites.panel.get(); + if (element) { + if (element.style.display == 'none') { + jSuites.panel(); + } + } + } + } + } + } + + var actionOverControl = function(e) { + // Tooltip + if (jSuites.tooltip) { + jSuites.tooltip(e); + } + } + + var actionOutControl = function(e) { + // Tooltip + if (jSuites.tooltip) { + jSuites.tooltip.hide(); + } + } + + // Create page container + document.addEventListener('swipeleft', controlSwipeLeft); + document.addEventListener('swiperight', controlSwipeRight); + document.addEventListener('keydown', obj.keyDownControls); + + if ('ontouchend' in document.documentElement === true) { + document.addEventListener('touchend', actionUpControl); + } else { + document.addEventListener('mouseup', actionUpControl); + } + + // Onmouseover + document.addEventListener('mouseover', actionOverControl); + document.addEventListener('mouseout', actionOutControl); + document.addEventListener('DOMContentLoaded', function() { + obj.init(); + }); + + // Pop state control + window.onpopstate = function(e) { + if (e.state && e.state.route) { + if (jSuites.pages.get(e.state.route)) { + jSuites.pages(e.state.route, { ignoreHistory:true }); + } + } + } + + return obj; +}(); + +jSuites.files = []; + +jSuites.calendar = (function(el, options) { + var obj = {}; + obj.options = {}; + + // Global container + if (! jSuites.calendar.current) { + jSuites.calendar.current = null; + } + + // Default configuration + var defaults = { + // Data + data: null, + // Inline or not + type: null, + // Restrictions + validRange: null, + // Starting weekday - 0 for sunday, 6 for saturday + startingDay: null, + // Date format + format: 'DD/MM/YYYY', + // Allow keyboard date entry + readonly: true, + // Today is default + today: false, + // Show timepicker + time: false, + // Show the reset button + resetButton: true, + // Placeholder + placeholder: '', + // Translations can be done here + months: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + weekdays: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'], + weekdays_short: ['S', 'M', 'T', 'W', 'T', 'F', 'S'], + // Value + value: null, + // Events + onclose: null, + onchange: null, + // Fullscreen (this is automatic set for screensize < 800) + fullscreen: false, + // Internal mode controller + mode: null, + position: null, + // Create the calendar closed as default + opened: false, + }; + + // Loop through our object + for (var property in defaults) { + if (options && options.hasOwnProperty(property)) { + obj.options[property] = options[property]; + } else { + obj.options[property] = defaults[property]; + } + } + + // Value + if (! obj.options.value) { + if (el.tagName == 'INPUT' && el.value) { + obj.options.value = el.value; + } + } + + // Make sure use upper case in the format + obj.options.format = obj.options.format.toUpperCase(); + + if (obj.options.value) { + var date = obj.options.value.split(' '); + var time = date[1]; + var date = date[0].split('-'); + var y = parseInt(date[0]); + var m = parseInt(date[1]); + var d = parseInt(date[2]); + + if (time) { + var time = time.split(':'); + var h = parseInt(time[0]); + var i = parseInt(time[1]); + } else { + var h = 0; + var i = 0; + } + } else { + var date = new Date(); + var y = date.getFullYear(); + var m = date.getMonth() + 1; + var d = date.getDate(); + var h = date.getHours(); + var i = date.getMinutes(); + } + + // Current value + obj.date = [ y, m, d, h, i, 0 ]; + + // Two digits + var two = function(value) { + value = '' + value; + if (value.length == 1) { + value = '0' + value; + } + return value; + } + + // Calendar elements + var calendarReset = document.createElement('div'); + calendarReset.className = 'jcalendar-reset'; + calendarReset.innerHTML = 'Reset'; + + var calendarConfirm = document.createElement('div'); + calendarConfirm.className = 'jcalendar-confirm'; + calendarConfirm.innerHTML = 'Done'; + + var calendarControls = document.createElement('div'); + calendarControls.className = 'jcalendar-controls' + if (obj.options.resetButton) { + calendarControls.appendChild(calendarReset); + } + calendarControls.appendChild(calendarConfirm); + + var calendarContainer = document.createElement('div'); + calendarContainer.className = 'jcalendar-container'; + + var calendarContent = document.createElement('div'); + calendarContent.className = 'jcalendar-content'; + calendarContent.appendChild(calendarControls); + calendarContainer.appendChild(calendarContent); + + // Table container + var calendarTableContainer = document.createElement('div'); + calendarTableContainer.className = 'jcalendar-table'; + calendarContent.appendChild(calendarTableContainer); + + // Main element + if (el.tagName == 'INPUT') { + var calendar = document.createElement('div'); + } else { + var calendar = el; + } + calendar.className = 'jcalendar'; + calendar.appendChild(calendarContainer); + + // Previous button + var calendarHeaderPrev = document.createElement('td'); + calendarHeaderPrev.setAttribute('colspan', '2'); + calendarHeaderPrev.className = 'jcalendar-prev'; + + // Header with year and month + var calendarLabelYear = document.createElement('span'); + calendarLabelYear.className = 'jcalendar-year'; + + var calendarLabelMonth = document.createElement('span'); + calendarLabelMonth.className = 'jcalendar-month'; + + var calendarHeaderTitle = document.createElement('td'); + calendarHeaderTitle.className = 'jcalendar-header'; + calendarHeaderTitle.setAttribute('colspan', '3'); + calendarHeaderTitle.appendChild(calendarLabelMonth); + calendarHeaderTitle.appendChild(calendarLabelYear); + + var calendarHeaderNext = document.createElement('td'); + calendarHeaderNext.setAttribute('colspan', '2'); + calendarHeaderNext.className = 'jcalendar-next'; + + var calendarHeaderRow = document.createElement('tr'); + calendarHeaderRow.appendChild(calendarHeaderPrev); + calendarHeaderRow.appendChild(calendarHeaderTitle); + calendarHeaderRow.appendChild(calendarHeaderNext); + + var calendarHeader = document.createElement('thead'); + calendarHeader.appendChild(calendarHeaderRow); + + var calendarBody = document.createElement('tbody'); + var calendarFooter = document.createElement('tfoot'); + + // Calendar table + var calendarTable = document.createElement('table'); + calendarTable.setAttribute('cellpadding', '0'); + calendarTable.setAttribute('cellspacing', '0'); + calendarTable.appendChild(calendarHeader); + calendarTable.appendChild(calendarBody); + calendarTable.appendChild(calendarFooter); + calendarTableContainer.appendChild(calendarTable); + + var calendarSelectHour = document.createElement('select'); + calendarSelectHour.className = 'jcalendar-select'; + calendarSelectHour.onchange = function() { + obj.date[3] = this.value; + } + + for (var i = 0; i < 24; i++) { + var element = document.createElement('option'); + element.value = i; + element.innerHTML = two(i); + calendarSelectHour.appendChild(element); + } + + var calendarSelectMin = document.createElement('select'); + calendarSelectMin.className = 'jcalendar-select'; + calendarSelectMin.onchange = function() { + obj.date[4] = this.value; + } + + for (var i = 0; i < 60; i++) { + var element = document.createElement('option'); + element.value = i; + element.innerHTML = two(i); + calendarSelectMin.appendChild(element); + } + + // Footer controls + var calendarControlsFooter = document.createElement('div'); + calendarControlsFooter.className = 'jcalendar-controls'; + + var calendarControlsTime = document.createElement('div'); + calendarControlsTime.className = 'jcalendar-time'; + calendarControlsTime.style.maxWidth = '140px'; + calendarControlsTime.appendChild(calendarSelectHour); + calendarControlsTime.appendChild(calendarSelectMin); + + var calendarControlsUpdateButton = document.createElement('input'); + calendarControlsUpdateButton.setAttribute('type', 'button'); + calendarControlsUpdateButton.className = 'jcalendar-update'; + calendarControlsUpdateButton.value = 'Update'; + + var calendarControlsUpdate = document.createElement('div'); + calendarControlsUpdate.style.flexGrow = '10'; + calendarControlsUpdate.appendChild(calendarControlsUpdateButton); + calendarControlsFooter.appendChild(calendarControlsTime); + calendarControlsFooter.appendChild(calendarControlsUpdate); + calendarContent.appendChild(calendarControlsFooter); + + var calendarBackdrop = document.createElement('div'); + calendarBackdrop.className = 'jcalendar-backdrop'; + calendar.appendChild(calendarBackdrop); + + // Update actions button + var updateActions = function() { + var currentDay = calendar.querySelector('.jcalendar-selected'); + + if (currentDay && currentDay.classList.contains('jcalendar-disabled')) { + calendarControlsUpdateButton.setAttribute('disabled', 'disabled'); + calendarSelectHour.setAttribute('disabled', 'disabled'); + calendarSelectMin.setAttribute('disabled', 'disabled'); + } else { + calendarControlsUpdateButton.removeAttribute('disabled'); + calendarSelectHour.removeAttribute('disabled'); + calendarSelectMin.removeAttribute('disabled'); + } + } + + // Methods + obj.open = function (value) { + if (! calendar.classList.contains('jcalendar-focus')) { + if (jSuites.calendar.current) { + jSuites.calendar.current.close(); + } + // Current + jSuites.calendar.current = obj; + // Show calendar + calendar.classList.add('jcalendar-focus'); + // Get days + obj.getDays(); + // Hour + if (obj.options.time) { + calendarSelectHour.value = obj.date[3]; + calendarSelectMin.value = obj.date[4]; + } + + // Get the position of the corner helper + if (jSuites.getWindowWidth() < 800 || obj.options.fullscreen) { + // Full + calendar.classList.add('jcalendar-fullsize'); + // Animation + jSuites.slideBottom(calendarContent, 1); + } else { + const rect = el.getBoundingClientRect(); + const rectContent = calendarContent.getBoundingClientRect(); + + if (obj.options.position) { + calendarContainer.style.position = 'fixed'; + if (window.innerHeight < rect.bottom + rectContent.height) { + calendarContainer.style.top = (rect.top - (rectContent.height + 2)) + 'px'; + } else { + calendarContainer.style.top = (rect.top + rect.height + 2) + 'px'; + } + calendarContainer.style.left = rect.left + 'px'; + } else { + if (window.innerHeight < rect.bottom + rectContent.height) { + calendarContainer.style.bottom = (1 * rect.height + rectContent.height + 2) + 'px'; + } else { + calendarContainer.style.top = 2 + 'px'; + } + } + } + } + } + + obj.close = function (ignoreEvents, update) { + if (jSuites.calendar.current) { + // Current + jSuites.calendar.current = null; + + if (update !== false) { + var element = calendar.querySelector('.jcalendar-selected'); + + if (typeof(update) == 'string') { + var value = update; + } else if (element && element.classList.contains('jcalendar-disabled')) { + var value = obj.options.value + } else { + var value = obj.getValue(); + } + + obj.setValue(value); + } + + // Events + if (! ignoreEvents && typeof(obj.options.onclose) == 'function') { + obj.options.onclose(el); + } + + // Hide + calendar.classList.remove('jcalendar-focus'); + } + + return obj.options.value; + } + + obj.prev = function() { + // Check if the visualization is the days picker or years picker + if (obj.options.mode == 'years') { + obj.date[0] = obj.date[0] - 12; + + // Update picker table of days + obj.getYears(); + } else { + // Go to the previous month + if (obj.date[1] < 2) { + obj.date[0] = obj.date[0] - 1; + obj.date[1] = 12; + } else { + obj.date[1] = obj.date[1] - 1; + } + + // Update picker table of days + obj.getDays(); + } + } + + obj.next = function() { + // Check if the visualization is the days picker or years picker + if (obj.options.mode == 'years') { + obj.date[0] = parseInt(obj.date[0]) + 12; + + // Update picker table of days + obj.getYears(); + } else { + // Go to the previous month + if (obj.date[1] > 11) { + obj.date[0] = parseInt(obj.date[0]) + 1; + obj.date[1] = 1; + } else { + obj.date[1] = parseInt(obj.date[1]) + 1; + } + + // Update picker table of days + obj.getDays(); + } + } + + obj.setValue = function(val) { + if (! val) { + val = '' + val; + } + // Values + var newValue = val; + var oldValue = obj.options.value; + // Set label + var value = obj.setLabel(newValue, obj.options.format); + var date = newValue.split(' '); + if (! date[1]) { + date[1] = '00:00:00'; + } + var time = date[1].split(':') + var date = date[0].split('-'); + var y = parseInt(date[0]); + var m = parseInt(date[1]); + var d = parseInt(date[2]); + var h = parseInt(time[0]); + var i = parseInt(time[1]); + obj.date = [ y, m, d, h, i, 0 ]; + var val = obj.setLabel(newValue, obj.options.format); + + if (oldValue != newValue) { + // Input value + if (el.tagName == 'INPUT') { + el.value = val; + } + // New value + obj.options.value = newValue; + // On change + if (typeof(obj.options.onchange) == 'function') { + obj.options.onchange(el, newValue, oldValue); + } + } + + obj.getDays(); + } + + obj.getValue = function() { + if (obj.date) { + if (obj.options.time) { + return two(obj.date[0]) + '-' + two(obj.date[1]) + '-' + two(obj.date[2]) + ' ' + two(obj.date[3]) + ':' + two(obj.date[4]) + ':' + two(0); + } else { + return two(obj.date[0]) + '-' + two(obj.date[1]) + '-' + two(obj.date[2]) + ' ' + two(0) + ':' + two(0) + ':' + two(0); + } + } else { + return ""; + } + } + + /** + * Calendar + */ + obj.update = function(element) { + if (element.classList.contains('jcalendar-disabled')) { + // Do nothing + } else { + obj.date[2] = element.innerText; + + if (! obj.options.time) { + obj.close(); + } else { + obj.date[3] = calendarSelectHour.value; + obj.date[4] = calendarSelectMin.value; + } + + var elements = calendar.querySelector('.jcalendar-selected'); + if (elements) { + elements.classList.remove('jcalendar-selected'); + } + element.classList.add('jcalendar-selected'); + } + + // Update + updateActions(); + } + + /** + * Set to blank + */ + obj.reset = function() { + // Close calendar + obj.close(false, ''); + } + + /** + * Get calendar days + */ + obj.getDays = function() { + // Mode + obj.options.mode = 'days'; + + // Setting current values in case of NULLs + var date = new Date(); + + // Current selection + var year = obj.date && obj.date[0] ? obj.date[0] : parseInt(date.getFullYear()); + var month = obj.date && obj.date[1] ? obj.date[1] : parseInt(date.getMonth()) + 1; + var day = obj.date && obj.date[2] ? obj.date[2] : parseInt(date.getDay()); + var hour = obj.date && obj.date[3] ? obj.date[3] : parseInt(date.getHours()); + var min = obj.date && obj.date[4] ? obj.date[4] : parseInt(date.getMinutes()); + + // Selection container + obj.date = [year, month, day, hour, min, 0 ]; + + // Update title + calendarLabelYear.innerHTML = year; + calendarLabelMonth.innerHTML = obj.options.months[month - 1]; + + // Current month and Year + var isCurrentMonthAndYear = (date.getMonth() == month - 1) && (date.getFullYear() == year) ? true : false; + var currentDay = date.getDate(); + + // Number of days in the month + var date = new Date(year, month, 0, 0, 0); + var numberOfDays = date.getDate(); + + // First day + var date = new Date(year, month-1, 0, 0, 0); + var firstDay = date.getDay() + 1; + + // Index value + var index = obj.options.startingDay || 0; + + // First of day relative to the starting calendar weekday + firstDay = firstDay - index; + + // Reset table + calendarBody.innerHTML = ''; + + // Weekdays Row + var row = document.createElement('tr'); + row.setAttribute('align', 'center'); + calendarBody.appendChild(row); + + // Create weekdays row + for (var i = 0; i < 7; i++) { + var cell = document.createElement('td'); + cell.classList.add('jcalendar-weekday') + cell.innerHTML = obj.options.weekdays_short[index]; + row.appendChild(cell); + // Next week day + index++; + // Restart index + if (index > 6) { + index = 0; + } + } + + // Index of days + var index = 0; + var d = 0; + + // Calendar table + for (var j = 0; j < 5; j++) { + // Reset cells container + var row = document.createElement('tr'); + row.setAttribute('align', 'center'); + // Data control + var emptyRow = true; + // Create cells + for (var i = 0; i < 7; i++) { + // Create cell + var cell = document.createElement('td'); + cell.classList.add('jcalendar-set-day'); + + if (index >= firstDay && index < (firstDay + numberOfDays)) { + // Day cell + d++; + cell.innerHTML = d; + + // Selected + if (d == day) { + cell.classList.add('jcalendar-selected'); + } + + // Current selection day is today + if (isCurrentMonthAndYear && currentDay == d) { + cell.style.fontWeight = 'bold'; + } + + // Current selection day + var current = jSuites.calendar.now(new Date(year, month-1, d), true); + + // Available ranges + if (obj.options.validRange) { + if (! obj.options.validRange[0] || current >= obj.options.validRange[0]) { + var test1 = true; + } else { + var test1 = false; + } + + if (! obj.options.validRange[1] || current <= obj.options.validRange[1]) { + var test2 = true; + } else { + var test2 = false; + } + + if (! (test1 && test2)) { + cell.classList.add('jcalendar-disabled'); + } + } + + // Control + emptyRow = false; + } + // Day cell + row.appendChild(cell); + // Index + index++; + } + + // Add cell to the calendar body + if (emptyRow == false) { + calendarBody.appendChild(row); + } + } + + // Show time controls + if (obj.options.time) { + calendarControlsTime.style.display = ''; + } else { + calendarControlsTime.style.display = 'none'; + } + + // Update + updateActions(); + } + + obj.getMonths = function() { + // Mode + obj.options.mode = 'months'; + + // Loading month labels + var months = obj.options.months; + + // Update title + calendarLabelYear.innerHTML = obj.date[0]; + calendarLabelMonth.innerHTML = ''; + + // Create months table + var html = '<td colspan="7"><table width="100%"><tr align="center">'; + + for (i = 0; i < 12; i++) { + if ((i > 0) && (!(i % 4))) { + html += '</tr><tr align="center">'; + } + + var month = parseInt(i) + 1; + html += '<td class="jcalendar-set-month" data-value="' + month + '">' + months[i] +'</td>'; + } + + html += '</tr></table></td>'; + + calendarBody.innerHTML = html; + } + + obj.getYears = function() { + // Mode + obj.options.mode = 'years'; + + // Array of years + var y = []; + for (i = 0; i < 25; i++) { + y[i] = parseInt(obj.date[0]) + (i - 12); + } + + // Assembling the year tables + var html = '<td colspan="7"><table width="100%"><tr align="center">'; + + for (i = 0; i < 25; i++) { + if ((i > 0) && (!(i % 5))) { + html += '</tr><tr align="center">'; + } + html += '<td class="jcalendar-set-year">'+ y[i] +'</td>'; + } + + html += '</tr></table></td>'; + + calendarBody.innerHTML = html; + } + + obj.setLabel = function(value, format) { + return jSuites.calendar.getDateString(value, format); + } + + obj.fromFormatted = function (value, format) { + return jSuites.calendar.extractDateFromString(value, format); + } + + var mouseUpControls = function(e) { + var action = e.target.className; + + // Object id + if (action == 'jcalendar-prev') { + obj.prev(); + e.stopPropagation(); + e.preventDefault(); + } else if (action == 'jcalendar-next') { + obj.next(); + e.stopPropagation(); + e.preventDefault(); + } else if (action == 'jcalendar-month') { + obj.getMonths(); + e.stopPropagation(); + e.preventDefault(); + } else if (action == 'jcalendar-year') { + obj.getYears(); + e.stopPropagation(); + e.preventDefault(); + } else if (action == 'jcalendar-set-year') { + obj.date[0] = e.target.innerText; + obj.getDays(); + e.stopPropagation(); + e.preventDefault(); + } else if (action == 'jcalendar-set-month') { + obj.date[1] = parseInt(e.target.getAttribute('data-value')); + obj.getDays(); + e.stopPropagation(); + e.preventDefault(); + } else if (action == 'jcalendar-confirm' || action == 'jcalendar-update') { + obj.close(); + e.stopPropagation(); + e.preventDefault(); + } else if (action == 'jcalendar-close') { + obj.close(); + e.stopPropagation(); + e.preventDefault(); + } else if (action == 'jcalendar-backdrop') { + obj.close(false, false); + e.stopPropagation(); + e.preventDefault(); + } else if (action == 'jcalendar-reset') { + obj.reset(); + e.stopPropagation(); + e.preventDefault(); + } else if (e.target.classList.contains('jcalendar-set-day')) { + if (e.target.innerText) { + obj.update(e.target); + e.stopPropagation(); + e.preventDefault(); + } + } + } + + var keyUpControls = function(e) { + if (e.target.value && e.target.value.length > 3) { + var test = jSuites.calendar.extractDateFromString(e.target.value, obj.options.format); + if (test) { + if (e.target.getAttribute('data-completed') == 'true') { + obj.setValue(test); + } + } + } + } + + // Handle events + el.addEventListener("keyup", keyUpControls); + + // Add global events + calendar.addEventListener("swipeleft", function(e) { + jSuites.slideLeft(calendarTable, 0, function() { + obj.next(); + jSuites.slideRight(calendarTable, 1); + }); + e.preventDefault(); + e.stopPropagation(); + }); + + calendar.addEventListener("swiperight", function(e) { + jSuites.slideRight(calendarTable, 0, function() { + obj.prev(); + jSuites.slideLeft(calendarTable, 1); + }); + e.preventDefault(); + e.stopPropagation(); + }); + + if ('ontouchend' in document.documentElement === true) { + calendar.addEventListener("touchend", mouseUpControls); + + el.addEventListener("touchend", function(e) { + obj.open(); + }); + } else { + calendar.addEventListener("mouseup", mouseUpControls); + + el.addEventListener("mouseup", function(e) { + obj.open(); + }); + } + + // Append element to the DOM + if (el.tagName == 'INPUT') { + el.parentNode.insertBefore(calendar, el.nextSibling); + // Add properties + el.setAttribute('autocomplete', 'off'); + el.setAttribute('data-mask', obj.options.format.toLowerCase()); + + if (obj.options.readonly) { + el.setAttribute('readonly', 'readonly'); + } + if (obj.options.placeholder) { + el.setAttribute('placeholder', obj.options.placeholder); + } + // Element + el.classList.add('jcalendar-input'); + // Value + el.value = obj.setLabel(obj.getValue(), obj.options.format); + } + + // Keep object available from the node + el.calendar = obj; + + if (obj.options.opened == true) { + obj.open(); + } + + return obj; +}); + +jSuites.calendar.prettify = function(d, texts) { + if (! texts) { + var texts = { + justNow: 'Just now', + xMinutesAgo: '{0}m ago', + xHoursAgo: '{0}h ago', + xDaysAgo: '{0}d ago', + xWeeksAgo: '{0}w ago', + xMonthsAgo: '{0} mon ago', + xYearsAgo: '{0}y ago', + } + } + + var d1 = new Date(); + var d2 = new Date(d); + var total = parseInt((d1 - d2) / 1000 / 60); + + String.prototype.format = function(o) { + return this.replace('{0}', o); + } + + if (total == 0) { + var text = texts.justNow; + } else if (total < 90) { + var text = texts.xMinutesAgo.format(total); + } else if (total < 1440) { // One day + var text = texts.xHoursAgo.format(Math.round(total/60)); + } else if (total < 20160) { // 14 days + var text = texts.xDaysAgo.format(Math.round(total / 1440)); + } else if (total < 43200) { // 30 days + var text = texts.xWeeksAgo.format(Math.round(total / 10080)); + } else if (total < 1036800) { // 24 months + var text = texts.xMonthsAgo.format(Math.round(total / 43200)); + } else { // 24 months+ + var text = texts.xYearsAgo.format(Math.round(total / 525600)); + } + + return text; +} + +jSuites.calendar.prettifyAll = function() { + var elements = document.querySelectorAll('.prettydate'); + for (var i = 0; i < elements.length; i++) { + if (elements[i].getAttribute('data-date')) { + elements[i].innerHTML = jSuites.calendar.prettify(elements[i].getAttribute('data-date')); + } else { + elements[i].setAttribute('data-date', elements[i].innerHTML); + elements[i].innerHTML = jSuites.calendar.prettify(elements[i].innerHTML); + } + } +} + +jSuites.calendar.now = function(date, dateOnly) { + if (! date) { + var date = new Date(); + } + var y = date.getFullYear(); + var m = date.getMonth() + 1; + var d = date.getDate(); + var h = date.getHours(); + var i = date.getMinutes(); + var s = date.getSeconds(); + + // Two digits + var two = function(value) { + value = '' + value; + if (value.length == 1) { + value = '0' + value; + } + return value; + } + + if (dateOnly == true) { + return two(y) + '-' + two(m) + '-' + two(d); + } else { + return two(y) + '-' + two(m) + '-' + two(d) + ' ' + two(h) + ':' + two(i) + ':' + two(s); + } +} + +// Helper to extract date from a string +jSuites.calendar.extractDateFromString = function(date, format) { + format = "DD/MM/YYYY"; + var v1 = '' + date; + var v2 = format.replace(/[0-9]/g,''); + + var test = 1; + + // Get year + var y = v2.search("YYYY"); + y = v1.substr(y,4); + if (parseInt(y) != y) { + test = 0; + } + + // Get month + var m = v2.search("MM"); + m = v1.substr(m,2); + if (parseInt(m) != m || d > 12) { + test = 0; + } + + // Get day + var d = v2.search("DD"); + d = v1.substr(d,2); + if (parseInt(d) != d || d > 31) { + test = 0; + } + + // Get hour + var h = v2.search("HH"); + if (h >= 0) { + h = v1.substr(h,2); + if (! parseInt(h) || h > 23) { + h = '00'; + } + } else { + h = '00'; + } + + // Get minutes + var i = v2.search("MI"); + if (i >= 0) { + i = v1.substr(i,2); + if (! parseInt(i) || i > 59) { + i = '00'; + } + } else { + i = '00'; + } + + // Get seconds + var s = v2.search("SS"); + if (s >= 0) { + s = v1.substr(s,2); + if (! parseInt(s) || s > 59) { + s = '00'; + } + } else { + s = '00'; + } + + if (test == 1 && date.length == v2.length) { + // Update source + var data = y + '-' + m + '-' + d + ' ' + h + ':' + i + ':' + s; + + return data; + } + + return ''; +} + +// Helper to convert date into string +jSuites.calendar.getDateString = function(value, format) { + // Default calendar + if (! format) { + var format = 'DD/MM/YYYY'; + } + + if (value) { + var d = ''+value; + d = d.split(' '); + + var h = ''; + var m = ''; + var s = ''; + + if (d[1]) { + h = d[1].split(':'); + m = h[1] ? h[1] : '00'; + s = h[2] ? h[2] : '00'; + h = h[0] ? h[0] : '00'; + } else { + h = '00'; + m = '00'; + s = '00'; + } + + d = d[0].split('-'); + + if (d[0] && d[1] && d[2] && d[0] > 0 && d[1] > 0 && d[1] < 13 && d[2] > 0 && d[2] < 32) { + var calendar = new Date(d[0], d[1]-1, d[2]); + var weekday = new Array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'); + var months = new Array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'); + + d[1] = (d[1].length < 2 ? '0' : '') + d[1]; + d[2] = (d[2].length < 2 ? '0' : '') + d[2]; + h = (h.length < 2 ? '0' : '') + h; + m = (m.length < 2 ? '0' : '') + m; + s = (s.length < 2 ? '0' : '') + s; + + value = format; + value = value.replace('WD', weekday[calendar.getDay()]); + value = value.replace('DD', d[2]); + value = value.replace('MM', d[1]); + value = value.replace('YYYY', d[0]); + value = value.replace('YY', d[0].substring(2,4)); + value = value.replace('MON', months[parseInt(d[1])-1].toUpperCase()); + + if (h) { + value = value.replace('HH24', h); + } + + if (h > 12) { + value = value.replace('HH12', h - 12); + value = value.replace('HH', h); + } else { + value = value.replace('HH12', h); + value = value.replace('HH', h); + } + + value = value.replace('MI', m); + value = value.replace('MM', m); + value = value.replace('SS', s); + } else { + value = ''; + } + } + + return value; +} + +jSuites.calendar.isOpen = function(e) { + if (jSuites.calendar.current) { + if (! e.target.className || e.target.className.indexOf('jcalendar') == -1) { + jSuites.calendar.current.close(false, false); + } + } +} + +if ('ontouchstart' in document.documentElement === true) { + document.addEventListener("touchstart", jSuites.calendar.isOpen); +} else { + document.addEventListener("mousedown", jSuites.calendar.isOpen); +} + +jSuites.color = (function(el, options) { + var obj = {}; + obj.options = {}; + obj.values = []; + + // Global container + if (! jSuites.color.current) { + jSuites.color.current = null; + } + + /** + * @typedef {Object} defaults + * @property {(string|Array)} value - Initial value of the compontent + * @property {string} placeholder - The default instruction text on the element + * @property {requestCallback} onchange - Method to be execute after any changes on the element + * @property {requestCallback} onclose - Method to be execute when the element is closed + */ + var defaults = { + placeholder: '', + value: null, + onclose: null, + onchange: null, + closeOnChange: true, + }; + + // Loop through our object + for (var property in defaults) { + if (options && options.hasOwnProperty(property)) { + obj.options[property] = options[property]; + } else { + obj.options[property] = defaults[property]; + } + } + + var palette = { + "red": { + "50": "#ffebee", + "100": "#ffcdd2", + "200": "#ef9a9a", + "300": "#e57373", + "400": "#ef5350", + "500": "#f44336", + "600": "#e53935", + "700": "#d32f2f", + "800": "#c62828", + "900": "#b71c1c", + }, + "pink": { + "50": "#fce4ec", + "100": "#f8bbd0", + "200": "#f48fb1", + "300": "#f06292", + "400": "#ec407a", + "500": "#e91e63", + "600": "#d81b60", + "700": "#c2185b", + "800": "#ad1457", + "900": "#880e4f", + }, + "purple": { + "50": "#f3e5f5", + "100": "#e1bee7", + "200": "#ce93d8", + "300": "#ba68c8", + "400": "#ab47bc", + "500": "#9c27b0", + "600": "#8e24aa", + "700": "#7b1fa2", + "800": "#6a1b9a", + "900": "#4a148c", + }, + "indigo": { + "50": "#e8eaf6", + "100": "#c5cae9", + "200": "#9fa8da", + "300": "#7986cb", + "400": "#5c6bc0", + "500": "#3f51b5", + "600": "#3949ab", + "700": "#303f9f", + "800": "#283593", + "900": "#1a237e", + }, + "blue": { + "50": "#e3f2fd", + "100": "#bbdefb", + "200": "#90caf9", + "300": "#64b5f6", + "400": "#42a5f5", + "500": "#2196f3", + "600": "#1e88e5", + "700": "#1976d2", + "800": "#1565c0", + "900": "#0d47a1", + }, + "cyan": { + "50": "#e0f7fa", + "100": "#b2ebf2", + "200": "#80deea", + "300": "#4dd0e1", + "400": "#26c6da", + "500": "#00bcd4", + "600": "#00acc1", + "700": "#0097a7", + "800": "#00838f", + "900": "#006064", + }, + "teal": { + "50": "#e0f2f1", + "100": "#b2dfdb", + "200": "#80cbc4", + "300": "#4db6ac", + "400": "#26a69a", + "500": "#009688", + "600": "#00897b", + "700": "#00796b", + "800": "#00695c", + "900": "#004d40", + }, + "green": { + "50": "#e8f5e9", + "100": "#c8e6c9", + "200": "#a5d6a7", + "300": "#81c784", + "400": "#66bb6a", + "500": "#4caf50", + "600": "#43a047", + "700": "#388e3c", + "800": "#2e7d32", + "900": "#1b5e20", + }, + "lightgreen": { + "50": "#f1f8e9", + "100": "#dcedc8", + "200": "#c5e1a5", + "300": "#aed581", + "400": "#9ccc65", + "500": "#8bc34a", + "600": "#7cb342", + "700": "#689f38", + "800": "#558b2f", + "900": "#33691e", + }, + "lime": { + "50": "#f9fbe7", + "100": "#f0f4c3", + "200": "#e6ee9c", + "300": "#dce775", + "400": "#d4e157", + "500": "#cddc39", + "600": "#c0ca33", + "700": "#afb42b", + "800": "#9e9d24", + "900": "#827717", + }, + "yellow": { + "50": "#fffde7", + "100": "#fff9c4", + "200": "#fff59d", + "300": "#fff176", + "400": "#ffee58", + "500": "#ffeb3b", + "600": "#fdd835", + "700": "#fbc02d", + "800": "#f9a825", + "900": "#f57f17", + }, + "amber": { + "50": "#fff8e1", + "100": "#ffecb3", + "200": "#ffe082", + "300": "#ffd54f", + "400": "#ffca28", + "500": "#ffc107", + "600": "#ffb300", + "700": "#ffa000", + "800": "#ff8f00", + "900": "#ff6f00", + }, + "orange": { + "50": "#fff3e0", + "100": "#ffe0b2", + "200": "#ffcc80", + "300": "#ffb74d", + "400": "#ffa726", + "500": "#ff9800", + "600": "#fb8c00", + "700": "#f57c00", + "800": "#ef6c00", + "900": "#e65100", + }, + "deeporange": { + "50": "#fbe9e7", + "100": "#ffccbc", + "200": "#ffab91", + "300": "#ff8a65", + "400": "#ff7043", + "500": "#ff5722", + "600": "#f4511e", + "700": "#e64a19", + "800": "#d84315", + "900": "#bf360c", + }, + "brown": { + "50": "#efebe9", + "100": "#d7ccc8", + "200": "#bcaaa4", + "300": "#a1887f", + "400": "#8d6e63", + "500": "#795548", + "600": "#6d4c41", + "700": "#5d4037", + "800": "#4e342e", + "900": "#3e2723" + }, + "grey": { + "50": "#fafafa", + "100": "#f5f5f5", + "200": "#eeeeee", + "300": "#e0e0e0", + "400": "#bdbdbd", + "500": "#9e9e9e", + "600": "#757575", + "700": "#616161", + "800": "#424242", + "900": "#212121" + }, + "bluegrey": { + "50": "#eceff1", + "100": "#cfd8dc", + "200": "#b0bec5", + "300": "#90a4ae", + "400": "#78909c", + "500": "#607d8b", + "600": "#546e7a", + "700": "#455a64", + "800": "#37474f", + "900": "#263238" + } + }; + + var x = 0; + var y = 0; + var colors = []; + + var col = Object.keys(palette); + var shade = Object.keys(palette[col[0]]); + + for (var i = 0; i < col.length; i++) { + for (var j = 0; j < shade.length; j++) { + if (! colors[j]) { + colors[j] = []; + } + colors[j][i] = palette[col[i]][shade[j]]; + } + }; + + // Value + if (obj.options.value) { + el.value = obj.options.value; + } + + // Table container + var container = document.createElement('div'); + container.className = 'jcolor'; + + // Table container + var backdrop = document.createElement('div'); + backdrop.className = 'jcolor-backdrop'; + container.appendChild(backdrop); + + // Content + var content = document.createElement('div'); + content.className = 'jcolor-content'; + + // Close button + var closeButton = document.createElement('div'); + closeButton.className = 'jcolor-close'; + closeButton.innerHTML = 'Done'; + closeButton.onclick = function() { + obj.close(); + } + content.appendChild(closeButton); + + // Table pallete + var table = document.createElement('table'); + table.setAttribute('cellpadding', '7'); + table.setAttribute('cellspacing', '0'); + + for (var i = 0; i < colors.length; i++) { + var tr = document.createElement('tr'); + for (var j = 0; j < colors[i].length; j++) { + var td = document.createElement('td'); + td.style.backgroundColor = colors[i][j]; + td.setAttribute('data-value', colors[i][j]); + td.innerHTML = ''; + tr.appendChild(td); + + // Selected color + if (obj.options.value == colors[i][j]) { + td.classList.add('jcolor-selected'); + } + + // Possible values + obj.values[colors[i][j]] = td; + } + table.appendChild(tr); + } + + /** + * Open color pallete + */ + obj.open = function() { + if (jSuites.color.current) { + if (jSuites.color.current != obj) { + jSuites.color.current.close(); + } + } + + if (! jSuites.color.current) { + // Persist element + jSuites.color.current = obj; + // Show colorpicker + container.classList.add('jcolor-focus'); + + const rectContent = content.getBoundingClientRect(); + + if (jSuites.getWindowWidth() < 800) { + content.style.top = ''; + content.classList.add('jcolor-fullscreen'); + jSuites.slideBottom(content, 1); + backdrop.style.display = 'block'; + } else { + if (content.classList.contains('jcolor-fullscreen')) { + content.classList.remove('jcolor-fullscreen'); + backdrop.style.display = ''; + } + + const rect = el.getBoundingClientRect(); + + if (window.innerHeight < rect.bottom + rectContent.height) { + content.style.top = -1 * (rectContent.height + rect.height + 2) + 'px'; + } else { + content.style.top = '2px'; + } + } + + container.focus(); + } + } + + /** + * Close color pallete + */ + obj.close = function(ignoreEvents) { + if (jSuites.color.current) { + jSuites.color.current = null; + if (! ignoreEvents && typeof(obj.options.onclose) == 'function') { + obj.options.onclose(el); + } + container.classList.remove('jcolor-focus'); + } + + // Make sure backdrop is hidden + backdrop.style.display = ''; + + return obj.options.value; + } + + /** + * Set value + */ + obj.setValue = function(color) { + if (color) { + el.value = color; + obj.options.value = color; + } + + // Remove current selecded mark + var selected = container.querySelector('.jcolor-selected'); + if (selected) { + selected.classList.remove('jcolor-selected'); + } + + // Mark cell as selected + obj.values[color].classList.add('jcolor-selected'); + + // Onchange + if (typeof(obj.options.onchange) == 'function') { + obj.options.onchange(el, color); + } + + if (obj.options.closeOnChange == true) { + obj.close(); + } + } + + /** + * Get value + */ + obj.getValue = function() { + return obj.options.value; + } + + /** + * If element is focus open the picker + */ + el.addEventListener("focus", function(e) { + obj.open(); + }); + + el.addEventListener("mousedown", function(e) { + if (! jSuites.color.current) { + setTimeout(function() { + obj.open(); + e.preventDefault(); + }, 200); + } + }); + + // Select color + container.addEventListener("mouseup", function(e) { + if (e.target.tagName == 'TD') { + jSuites.color.current.setValue(e.target.getAttribute('data-value')); + + if (jSuites.color.current) { + jSuites.color.current.close(); + } + } + }); + + // Close controller + document.addEventListener("mousedown", function(e) { + if (jSuites.color.current) { + var element = jSuites.getElement(e.target, 'jcolor'); + if (! element) { + jSuites.color.current.close(); + } + } + }); + + // Possible to focus the container + container.setAttribute('tabindex', '900'); + + // Placeholder + if (obj.options.placeholder) { + el.setAttribute('placeholder', obj.options.placeholder); + } + + // Append to the table + content.appendChild(table); + container.appendChild(content); + + // Insert picker after the element + if (el.tagName == 'INPUT') { + el.parentNode.insertBefore(container, el.nextSibling); + } else { + el.appendChild(container); + } + + // Keep object available from the node + el.color = obj; + + return obj; +}); + + +jSuites.contextmenu = (function(el, options) { + var obj = {}; + obj.options = {}; + + // Default configuration + var defaults = { + items: null, + onclick: null, + }; + + // Loop through our object + for (var property in defaults) { + if (options && options.hasOwnProperty(property)) { + obj.options[property] = options[property]; + } else { + obj.options[property] = defaults[property]; + } + } + + // Class definition + el.classList.add('jcontextmenu'); + // Focusable + el.setAttribute('tabindex', '900'); + + /** + * Open contextmenu + */ + obj.open = function(e, items) { + if (items) { + // Update content + obj.options.items = items; + // Create items + obj.create(items); + } + // Coordinates + if (e.target) { + var x = e.clientX; + var y = e.clientY; + } else { + var x = e.x; + var y = e.y; + } + + el.classList.add('jcontextmenu-focus'); + el.focus(); + + const rect = el.getBoundingClientRect(); + + if (window.innerHeight < y + rect.height) { + el.style.top = (y - rect.height) + 'px'; + } else { + el.style.top = y + 'px'; + } + + if (window.innerWidth < x + rect.width) { + if (x - rect.width > 0) { + el.style.left = (x - rect.width) + 'px'; + } else { + el.style.left = '10px'; + } + } else { + el.style.left = x + 'px'; + } + } + + /** + * Close menu + */ + obj.close = function() { + if (el.classList.contains('jcontextmenu-focus')) { + el.classList.remove('jcontextmenu-focus'); + } + } + + /** + * Create items based on the declared objectd + * @param {object} items - List of object + */ + obj.create = function(items) { + // Update content + el.innerHTML = ''; + + // Append items + for (var i = 0; i < items.length; i++) { + if (items[i].type && items[i].type == 'line') { + var itemContainer = document.createElement('hr'); + } else { + var itemContainer = document.createElement('div'); + var itemText = document.createElement('a'); + itemText.innerHTML = items[i].title; + + if (items[i].disabled) { + itemContainer.className = 'jcontextmenu-disabled'; + } else if (items[i].onclick) { + itemContainer.method = items[i].onclick; + itemContainer.addEventListener("mouseup", function() { + // Execute method + this.method(this); + }); + } + itemContainer.appendChild(itemText); + + if (items[i].shortcut) { + var itemShortCut = document.createElement('span'); + itemShortCut.innerHTML = items[i].shortcut; + itemContainer.appendChild(itemShortCut); + } + } + + el.appendChild(itemContainer); + } + } + + if (typeof(obj.options.onclick) == 'function') { + el.addEventListener('click', function(e) { + obj.options.onclick(obj); + }); + } + + el.addEventListener('blur', function(e) { + setTimeout(function() { + obj.close(); + }, 120); + }); + + window.addEventListener("mousewheel", function() { + obj.close(); + }); + + // Create items + if (obj.options.items) { + obj.create(obj.options.items); + } + + el.contextmenu = obj; + + return obj; +}); + +jSuites.contextmenu.getElement = function(element) { + var foundId = 0; + + function path (element) { + if (element.parentNode && element.getAttribute('aria-contextmenu-id')) { + foundId = element.getAttribute('aria-contextmenu-id') + } else { + if (element.parentNode) { + path(element.parentNode); + } + } + } + + path(element); + + return foundId; +} + +document.addEventListener("contextmenu", function(e) { + var id = jSuites.contextmenu.getElement(e.target); + if (id) { + var element = document.querySelector('#' + id); + if (! element) { + console.error('JSUITES: Contextmenu id not found'); + } else { + element.contextmenu.open(e); + e.preventDefault(); + } + } +}); + +/** + * Dialog v1.0.1 + * Author: paul.hodel@gmail.com + * https://github.com/paulhodel/jtools + */ + +jSuites.dialog = (function() { + var obj = {}; + obj.options = {}; + + var dialog = document.createElement('div'); + dialog.setAttribute('tabindex', '901'); + dialog.className = 'jdialog'; + dialog.id = 'dialog'; + + var dialogHeader = document.createElement('div'); + dialogHeader.className = 'jdialog-header'; + + var dialogTitle = document.createElement('div'); + dialogTitle.className = 'jdialog-title'; + dialogHeader.appendChild(dialogTitle); + + var dialogMessage = document.createElement('div'); + dialogMessage.className = 'jdialog-message'; + dialogHeader.appendChild(dialogMessage); + + var dialogFooter = document.createElement('div'); + dialogFooter.className = 'jdialog-footer'; + + var dialogContainer = document.createElement('div'); + dialogContainer.className = 'jdialog-container'; + dialogContainer.appendChild(dialogHeader); + dialogContainer.appendChild(dialogFooter); + + // Confirm + var dialogConfirm = document.createElement('div'); + var dialogConfirmButton = document.createElement('input'); + dialogConfirmButton.value = obj.options.confirmLabel; + dialogConfirmButton.type = 'button'; + dialogConfirmButton.onclick = function() { + if (typeof(obj.options.onconfirm) == 'function') { + obj.options.onconfirm(); + } + obj.close(); + }; + dialogConfirm.appendChild(dialogConfirmButton); + dialogFooter.appendChild(dialogConfirm); + + // Cancel + var dialogCancel = document.createElement('div'); + var dialogCancelButton = document.createElement('input'); + dialogCancelButton.value = obj.options.cancelLabel; + dialogCancelButton.type = 'button'; + dialogCancelButton.onclick = function() { + if (typeof(obj.options.oncancel) == 'function') { + obj.options.oncancel(); + } + obj.close(); + } + dialogCancel.appendChild(dialogCancelButton); + dialogFooter.appendChild(dialogCancel); + + // Dialog + dialog.appendChild(dialogContainer); + + obj.open = function(options) { + obj.options = options; + + if (obj.options.title) { + dialogTitle.innerHTML = obj.options.title; + } + + if (obj.options.message) { + dialogMessage.innerHTML = obj.options.message; + } + + if (! obj.options.confirmLabel) { + obj.options.confirmLabel = 'OK'; + } + dialogConfirmButton.value = obj.options.confirmLabel; + + if (! obj.options.cancelLabel) { + obj.options.cancelLabel = 'Cancel'; + } + dialogCancelButton.value = obj.options.cancelLabel; + + if (obj.options.type == 'confirm') { + dialogCancelButton.parentNode.style.display = ''; + } else { + dialogCancelButton.parentNode.style.display = 'none'; + } + + // Append element to the app + dialog.style.opacity = 100; + + // Append to the page + if (jSuites.el) { + jSuites.el.appendChild(dialog); + } else { + document.body.appendChild(dialog); + } + + // Focus + dialog.focus(); + + // Show + setTimeout(function() { + dialogContainer.style.opacity = 100; + }, 0); + }; + + obj.close = function() { + dialog.style.opacity = 0; + dialogContainer.style.opacity = 0; + setTimeout(function() { + dialog.remove(); + }, 100); + }; + + return obj; +})(); + +jSuites.confirm = (function(message, onconfirm) { + if (jSuites.getWindowWidth() < 800) { + jSuites.dialog.open({ + type: 'confirm', + message: message, + title: 'Confirmation', + onconfirm: onconfirm, + }); + } else { + if (confirm(message)) { + onconfirm(); + } + } +}); + +jSuites.alert = function(message) { + if (jSuites.getWindowWidth() < 800) { + jSuites.dialog.open({ + title:'Alert', + message:message, + }); + } else { + alert(message); + } +} + + +jSuites.dropdown = (function(el, options) { + var obj = {}; + obj.options = {}; + + // If the element is a SELECT tag, create a configuration object + if (el.tagName == 'SELECT') { + var ret = jSuites.dropdown.extractFromDom(el, options); + el = ret.el; + options = ret.options; + } + + // Default configuration + var defaults = { + url: null, + data: [], + multiple: false, + autocomplete: false, + type: null, + width: null, + opened: false, + value: null, + placeholder: '', + position: false, + onchange: null, + onload: null, + onopen: null, + onclose: null, + onblur: null, + }; + + // Loop through our object + for (var property in defaults) { + if (options && options.hasOwnProperty(property)) { + obj.options[property] = options[property]; + } else { + obj.options[property] = defaults[property]; + } + } + + // Global container + if (! jSuites.dropdown.current) { + jSuites.dropdown.current = null; + } + + // Containers + obj.items = []; + obj.groups = []; + obj.selected = []; + + // Create dropdown + el.classList.add('jdropdown'); + + if (obj.options.type == 'searchbar') { + el.classList.add('jdropdown-searchbar'); + } else if (obj.options.type == 'list') { + el.classList.add('jdropdown-list'); + } else if (obj.options.type == 'picker') { + el.classList.add('jdropdown-picker'); + } else { + if (jSuites.getWindowWidth() < 800) { + el.classList.add('jdropdown-picker'); + obj.options.type = 'picker'; + } else { + if (obj.options.width) { + el.style.width = obj.options.width; + el.style.minWidth = obj.options.width; + } + el.classList.add('jdropdown-default'); + obj.options.type = 'default'; + } + } + + // Header container + var containerHeader = document.createElement('div'); + containerHeader.className = 'jdropdown-container-header'; + + // Header + var header = document.createElement('input'); + header.className = 'jdropdown-header'; + if (typeof(obj.options.onblur) == 'function') { + header.onblur = function() { + obj.options.onblur(el); + } + } + + // Container + var container = document.createElement('div'); + container.className = 'jdropdown-container'; + + // Dropdown content + var content = document.createElement('div'); + content.className = 'jdropdown-content'; + + // Close button + var closeButton = document.createElement('div'); + closeButton.className = 'jdropdown-close'; + closeButton.innerHTML = 'Done'; + + // Create backdrop + var backdrop = document.createElement('div'); + backdrop.className = 'jdropdown-backdrop'; + + // Autocomplete + if (obj.options.autocomplete == true) { + el.setAttribute('data-autocomplete', true); + + // Handler + var keyTimer = null; + header.addEventListener('keyup', function(e) { + if (keyTimer) { + clearTimeout(keyTimer); + } + keyTimer = setTimeout(function() { + obj.find(header.value); + keyTimer = null; + }, 500); + + if (! el.classList.contains('jdropdown-focus')) { + if (e.which > 65) { + obj.open(); + } + } + }); + } else { + header.setAttribute('readonly', 'readonly'); + } + + // Place holder + if (! obj.options.placeholder && el.getAttribute('placeholder')) { + obj.options.placeholder = el.getAttribute('placeholder'); + } + + if (obj.options.placeholder) { + header.setAttribute('placeholder', obj.options.placeholder); + } + + // Append elements + containerHeader.appendChild(header); + if (obj.options.type == 'searchbar') { + containerHeader.appendChild(closeButton); + } else { + container.appendChild(closeButton); + } + container.appendChild(content); + el.appendChild(containerHeader); + el.appendChild(container); + el.appendChild(backdrop); + + /** + * Init dropdown + */ + obj.init = function() { + if (obj.options.url) { + jSuites.ajax({ + url: obj.options.url, + method: 'GET', + dataType: 'json', + success: function(data) { + if (data) { + // Set data + obj.setData(data); + // Set value + if (obj.options.value != null) { + obj.setValue(obj.options.value); + } + // Onload method + if (typeof(obj.options.onload) == 'function') { + obj.options.onload(el, obj, data); + } + } + } + }); + } else { + // Set data + obj.setData(); + // Set value + if (obj.options.value != null) { + obj.setValue(obj.options.value); + } + // Onload + if (typeof(obj.options.onload) == 'function') { + obj.options.onload(el, obj, data); + } + } + + // Open dropdown + if (obj.options.opened == true) { + obj.open(); + } + } + + obj.getUrl = function() { + return obj.options.url; + } + + obj.setUrl = function(url) { + obj.options.url = url; + + jSuites.ajax({ + url: obj.options.url, + method: 'GET', + dataType: 'json', + success: function(data) { + obj.setData(data); + } + }); + } + + /** + * Create a new item + */ + obj.createItem = function(data) { + // Create item + var item = {}; + item.element = document.createElement('div'); + item.element.className = 'jdropdown-item'; + item.value = data.id; + item.text = data.name; + item.textLowerCase = '' + data.name.toLowerCase(); + + // Image + if (data.image) { + var image = document.createElement('img'); + image.className = 'jdropdown-image'; + image.src = data.image; + if (! data.title) { + image.classList.add('jdropdown-image-small'); + } + item.element.appendChild(image); + } + + // Set content + var node = document.createElement('div'); + node.className = 'jdropdown-description'; + node.innerHTML = data.name; + + // Title + if (data.title) { + var title = document.createElement('div'); + title.className = 'jdropdown-title'; + title.innerHTML = data.title; + node.appendChild(title); + } + + // Add node to item + item.element.appendChild(node); + + return item; + } + + obj.setData = function(data) { + // Update data + if (data) { + obj.options.data = data; + } + + // Data + var data = obj.options.data; + + // Remove content from the DOM + container.removeChild(content); + + // Make sure the content container is blank + content.innerHTML = ''; + + // Reset + obj.reset(); + + // Reset items + obj.items = []; + + // Helpers + var items = []; + var groups = []; + + // Create elements + if (data.length) { + // Prepare data + for (var i = 0; i < data.length; i++) { + // Compatibility + if (typeof(data[i]) != 'object') { + // Correct format + obj.options.data[i] = data[i] = { id: data[i], name: data[i] }; + } + + // Process groups + if (data[i].group) { + if (! groups[data[i].group]) { + groups[data[i].group] = []; + } + groups[data[i].group].push(i); + } else { + items.push(i); + } + } + + // Groups + var groupNames = Object.keys(groups); + + // Append groups in case exists + if (groupNames.length > 0) { + for (var i = 0; i < groupNames.length; i++) { + // Group container + var group = document.createElement('div'); + group.className = 'jdropdown-group'; + // Group name + var groupName = document.createElement('div'); + groupName.className = 'jdropdown-group-name'; + groupName.innerHTML = groupNames[i]; + // Group arrow + var groupArrow = document.createElement('i'); + groupArrow.className = 'jdropdown-group-arrow jdropdown-group-arrow-down'; + groupName.appendChild(groupArrow); + // Group items + var groupContent = document.createElement('div'); + groupContent.className = 'jdropdown-group-items'; + for (var j = 0; j < groups[groupNames[i]].length; j++) { + var item = obj.createItem(data[groups[groupNames[i]][j]]); + groupContent.appendChild(item.element); + // Items + obj.items.push(item); + } + // Group itens + group.appendChild(groupName); + group.appendChild(groupArrow); + group.appendChild(groupContent); + content.appendChild(group); + } + } + + if (items.length) { + for (var i = 0; i < items.length; i++) { + var item = obj.createItem(data[items[i]]); + obj.items.push(item); + content.appendChild(item.element); + } + } + + // Create the Indexes + for (var i = 0; i < obj.items.length; i++) { + obj.items[i].element.setAttribute('data-index', i); + } + } + + // Re-insert the content to the container + container.appendChild(content); + } + + obj.getText = function(asArray) { + // Result + var result = []; + // Append options + for (var i = 0; i < obj.selected.length; i++) { + if (obj.items[obj.selected[i]]) { + result.push(obj.items[obj.selected[i]].text); + } + } + + if (asArray) { + return result; + } else { + return result.join('; '); + } + } + + obj.getValue = function(asArray) { + // Result + var result = []; + // Append options + for (var i = 0; i < obj.selected.length; i++) { + if (obj.items[obj.selected[i]]) { + result.push(obj.items[obj.selected[i]].value); + } + } + + if (asArray) { + return result; + } else { + return result.join(';'); + } + } + + obj.setValue = function(value) { + // Remove values + for (var i = 0; i < obj.selected.length; i++) { + obj.items[obj.selected[i]].element.classList.remove('jdropdown-selected') + } + + // Reset selected + obj.selected = []; + + // Set values + if (value != null) { + if (Array.isArray(value)) { + for (var i = 0; i < obj.items.length; i++) { + for (var j = 0; j < value.length; j++) { + if (obj.items[i].value == value[j]) { + // Keep index of the selected item + obj.selected.push(i); + // Visual selection + obj.items[i].element.classList.add('jdropdown-selected'); + } + } + } + } else { + for (var i = 0; i < obj.items.length; i++) { + if (obj.items[i].value == value) { + // Keep index of the selected item + obj.selected.push(i); + // Visual selection + obj.items[i].element.classList.add('jdropdown-selected'); + } + } + } + } + + // Update labels + obj.updateLabel(); + } + + obj.selectIndex = function(index) { + // Only select those existing elements + if (obj.items && obj.items[index]) { + var index = index = parseInt(index); + // Current selection + var oldValue = obj.getValue(); + var oldLabel = obj.getText(); + + // Remove cursor style + if (obj.currentIndex != null) { + obj.items[obj.currentIndex].element.classList.remove('jdropdown-cursor'); + } + // Set cursor style + obj.items[index].element.classList.add('jdropdown-cursor'); + + // Update cursor position + obj.currentIndex = index; + + // Focus behaviour + if (! obj.options.multiple) { + // Unselect option + if (obj.items[index].element.classList.contains('jdropdown-selected')) { + // Reset selected + obj.resetSelected(); + } else { + // Reset selected + obj.resetSelected(); + // Update selected item + obj.items[index].element.classList.add('jdropdown-selected'); + // Add to the selected list + obj.selected.push(index); + // Close + obj.close(); + } + } else { + // Toggle option + if (obj.items[index].element.classList.contains('jdropdown-selected')) { + obj.items[index].element.classList.remove('jdropdown-selected'); + // Remove from selected list + var indexToRemove = obj.selected.indexOf(index); + // Remove select + obj.selected.splice(indexToRemove, 1); + } else { + // Select element + obj.items[index].element.classList.add('jdropdown-selected'); + // Add to the selected list + obj.selected.push(index); + } + + // Update labels for multiple dropdown + if (! obj.options.autocomplete) { + obj.updateLabel(); + } + } + + // Current selection + var newValue = obj.getValue(); + var newLabel = obj.getText(); + + // Events + if (typeof(obj.options.onchange) == 'function') { + obj.options.onchange(el, index, oldValue, newValue, oldLabel, newLabel); + } + } + } + + obj.selectItem = function(item) { + if (jSuites.dropdown.current) { + var index = item.getAttribute('data-index'); + if (index != null) { + obj.selectIndex(index); + } + } + } + + obj.find = function(str) { + // Force lowercase + var str = str ? str.toLowerCase() : null; + + // Append options + for (var i = 0; i < obj.items.length; i++) { + if (str == null || obj.items[i].textLowerCase.indexOf(str) != -1) { + obj.items[i].element.style.display = ''; + } else { + if (obj.selected.indexOf(i) == -1) { + obj.items[i].element.style.display = 'none'; + } else { + obj.items[i].element.style.display = ''; + } + } + } + + var numVisibleItems = function(items) { + var visible = 0; + for (var j = 0; j < items.length; j++) { + if (items[j].style.display != 'none') { + visible++; + } + } + return visible; + } + + // Hide groups + /*for (var i = 0; i < obj.groups.length; i++) { + if (numVisibleItems(obj.groups[i].querySelectorAll('.jdropdown-item'))) { + obj.groups[i].children[0].style.display = ''; + } else { + obj.groups[i].children[0].style.display = 'none'; + } + }*/ + } + + obj.updateLabel = function() { + // Update label + header.value = obj.getText(); + } + + obj.open = function() { + if (jSuites.dropdown.current != el) { + if (jSuites.dropdown.current) { + jSuites.dropdown.current.dropdown.close(); + } + jSuites.dropdown.current = el; + } + + // Focus + if (! el.classList.contains('jdropdown-focus')) { + // Add focus + el.classList.add('jdropdown-focus'); + + // Animation + if (jSuites.getWindowWidth() < 800) { + if (obj.options.type == null || obj.options.type == 'picker') { + jSuites.slideBottom(container, 1); + } + } + + // Filter + if (obj.options.autocomplete == true) { + // Redo search + obj.find(); + // Clear search field + header.value = ''; + header.focus(); + } + + // Set cursor for the first or first selected element + var cursor = (obj.selected && obj.selected[0]) ? obj.selected[0] : 0; + obj.updateCursor(cursor); + + // Container Size + if (! obj.options.type || obj.options.type == 'default') { + const rect = el.getBoundingClientRect(); + const rectContainer = container.getBoundingClientRect(); + + if (obj.options.position) { + container.style.position = 'fixed'; + if (window.innerHeight < rect.bottom + rectContainer.height) { + container.style.top = ''; + container.style.bottom = (window.innerHeight - rect.top ) + 1 + 'px'; + } else { + container.style.top = rect.bottom + 'px'; + container.style.bottom = ''; + } + container.style.left = rect.left + 'px'; + } else { + if (window.innerHeight < rect.bottom + rectContainer.height) { + container.style.top = ''; + container.style.bottom = rect.height + 1 + 'px'; + } else { + container.style.top = ''; + container.style.bottom = ''; + } + } + + container.style.minWidth = rect.width + 'px'; + } + } + + // Events + if (typeof(obj.options.onopen) == 'function') { + obj.options.onopen(el); + } + } + + obj.close = function(ignoreEvents) { + if (jSuites.dropdown.current) { + // Remove controller + jSuites.dropdown.current = null + // Remove cursor + obj.resetCursor(); + // Update labels + obj.updateLabel(); + // Events + if (! ignoreEvents && typeof(obj.options.onclose) == 'function') { + obj.options.onclose(el); + } + // Blur + if (header.blur) { + header.blur(); + } + // Remove focus + el.classList.remove('jdropdown-focus'); + } + + return obj.getValue(); + } + + /** + * Update position cursor + */ + obj.updateCursor = function(index) { + // Set new cursor + if (obj.items && obj.items[index] && obj.items[index].element) { + // Reset cursor + obj.resetCursor(); + + // Set new cursor + obj.items[index].element.classList.add('jdropdown-cursor'); + + // Update position + obj.currentIndex = parseInt(index); + + // Update scroll to the cursor element + var container = content.scrollTop; + var element = obj.items[obj.currentIndex].element; + content.scrollTop = element.offsetTop - element.scrollTop + element.clientTop - 95; + } + } + + /** + * Reset cursor + */ + obj.resetCursor = function() { + // Remove current cursor + if (obj.currentIndex != null) { + // Remove visual cursor + if (obj.items && obj.items[obj.currentIndex]) { + obj.items[obj.currentIndex].element.classList.remove('jdropdown-cursor'); + } + // Reset cursor + obj.currentIndex = null; + } + } + + /** + * Reset cursor + */ + obj.resetSelected = function() { + // Unselected all + if (obj.selected) { + // Remove visual selection + for (var i = 0; i < obj.selected.length; i++) { + if (obj.items[obj.selected[i]]) { + obj.items[obj.selected[i]].element.classList.remove('jdropdown-selected'); + } + } + // Reset current selected items + obj.selected = []; + } + } + + /** + * Reset cursor and selected items + */ + obj.reset = function() { + // Reset cursor + obj.resetCursor(); + + // Reset selected + obj.resetSelected(); + + // Update labels + obj.updateLabel(); + } + + /** + * First visible item + */ + obj.firstVisible = function() { + var newIndex = null; + for (var i = 0; i < obj.items.length; i++) { + if (obj.items[i].element.style.display != 'none') { + newIndex = i; + break; + } + } + + if (newIndex == null) { + return false; + } + + obj.updateCursor(newIndex); + } + + /** + * Navigation + */ + obj.first = function() { + var newIndex = null; + for (var i = obj.currentIndex - 1; i >= 0; i--) { + if (obj.items[i].element.style.display != 'none') { + newIndex = i; + } + } + + if (newIndex == null) { + return false; + } + + obj.updateCursor(newIndex); + } + + obj.last = function() { + var newIndex = null; + for (var i = obj.currentIndex + 1; i < obj.items.length; i++) { + if (obj.items[i].element.style.display != 'none') { + newIndex = i; + } + } + + if (newIndex == null) { + return false; + } + + obj.updateCursor(newIndex); + } + + obj.next = function() { + var newIndex = null; + for (var i = obj.currentIndex + 1; i < obj.items.length; i++) { + if (obj.items[i].element.style.display != 'none') { + newIndex = i; + break; + } + } + + if (newIndex == null) { + return false; + } + + obj.updateCursor(newIndex); + } + + obj.prev = function() { + var newIndex = null; + for (var i = obj.currentIndex - 1; i >= 0; i--) { + if (obj.items[i].element.style.display != 'none') { + newIndex = i; + break; + } + } + + if (newIndex == null) { + return false; + } + + obj.updateCursor(newIndex); + } + + if (! jSuites.dropdown.hasEvents) { + if ('ontouchsend' in document.documentElement === true) { + document.addEventListener('touchsend', jSuites.dropdown.mouseup); + } else { + document.addEventListener('mouseup', jSuites.dropdown.mouseup); + } + document.addEventListener('keydown', jSuites.dropdown.onkeydown); + + jSuites.dropdown.hasEvents = true; + } + + // Start dropdown + obj.init(); + + // Keep object available from the node + el.dropdown = obj; + + return obj; +}); + +jSuites.dropdown.hasEvents = false; + +jSuites.dropdown.mouseup = function(e) { + var element = jSuites.getElement(e.target, 'jdropdown'); + if (element) { + var dropdown = element.dropdown; + if (e.target.classList.contains('jdropdown-header')) { + if (element.classList.contains('jdropdown-focus') && element.classList.contains('jdropdown-default')) { + dropdown.close(); + } else { + dropdown.open(); + } + } else if (e.target.classList.contains('jdropdown-group-name')) { + var items = e.target.nextSibling.children; + if (e.target.nextSibling.style.display != 'none') { + for (var i = 0; i < items.length; i++) { + if (items[i].style.display != 'none') { + dropdown.selectItem(items[i]); + } + } + } + } else if (e.target.classList.contains('jdropdown-group-arrow')) { + if (e.target.classList.contains('jdropdown-group-arrow-down')) { + e.target.classList.remove('jdropdown-group-arrow-down'); + e.target.classList.add('jdropdown-group-arrow-up'); + e.target.parentNode.nextSibling.style.display = 'none'; + } else { + e.target.classList.remove('jdropdown-group-arrow-up'); + e.target.classList.add('jdropdown-group-arrow-down'); + e.target.parentNode.nextSibling.style.display = ''; + } + } else if (e.target.classList.contains('jdropdown-item')) { + dropdown.selectItem(e.target); + } else if (e.target.classList.contains('jdropdown-image')) { + dropdown.selectIndex(e.target.parentNode.getAttribute('data-index')); + } else if (e.target.classList.contains('jdropdown-description')) { + dropdown.selectIndex(e.target.parentNode.getAttribute('data-index')); + } else if (e.target.classList.contains('jdropdown-title')) { + dropdown.selectIndex(e.target.parentNode.parentNode.getAttribute('data-index')); + } else if (e.target.classList.contains('jdropdown-close') || e.target.classList.contains('jdropdown-backdrop')) { + // Close + dropdown.close(); + } + + e.stopPropagation(); + e.preventDefault(); + } else { + if (jSuites.dropdown.current) { + jSuites.dropdown.current.dropdown.close(); + } + } +} + + +// Keydown controls +jSuites.dropdown.onkeydown = function(e) { + if (jSuites.dropdown.current) { + // Element + var element = jSuites.dropdown.current.dropdown; + // Index + var index = element.currentIndex; + + if (e.shiftKey) { + + } else { + if (e.which == 13 || e.which == 27 || e.which == 35 || e.which == 36 || e.which == 38 || e.which == 40) { + // Move cursor + if (e.which == 13) { + element.selectIndex(index) + } else if (e.which == 38) { + if (index == null) { + element.firstVisible(); + } else if (index > 0) { + element.prev(); + } + } else if (e.which == 40) { + if (index == null) { + element.firstVisible(); + } else if (index + 1 < element.options.data.length) { + element.next(); + } + } else if (e.which == 36) { + element.first(); + } else if (e.which == 35) { + element.last(); + } else if (e.which == 27) { + element.close(); + } + + e.stopPropagation(); + e.preventDefault(); + } + } + } +} + +jSuites.dropdown.extractFromDom = function(el, options) { + // Keep reference + var select = el; + if (! options) { + options = {}; + } + // Prepare configuration + if (el.getAttribute('multiple') && (! options || options.multiple == undefined)) { + options.multiple = true; + } + if (el.getAttribute('placeholder') && (! options || options.placeholder == undefined)) { + options.placeholder = el.getAttribute('placeholder'); + } + if (el.getAttribute('data-autocomplete') && (! options || options.autocomplete == undefined)) { + options.autocomplete = true; + } + if (! options || options.width == undefined) { + options.width = el.offsetWidth; + } + if (el.value && (! options || options.value == undefined)) { + options.value = el.value; + } + if (! options || options.data == undefined) { + options.data = []; + for (var j = 0; j < el.children.length; j++) { + if (el.children[j].tagName == 'OPTGROUP') { + for (var i = 0; i < el.children[j].children.length; i++) { + options.data.push({ + id: el.children[j].children[i].value, + name: el.children[j].children[i].innerHTML, + group: el.children[j].getAttribute('label'), + }); + } + } else { + options.data.push({ + id: el.children[j].value, + name: el.children[j].innerHTML, + }); + } + } + } + if (! options || options.onchange == undefined) { + options.onchange = function(a,b,c,d) { + if (options.multiple == true) { + if (obj.items[b].classList.contains('jdropdown-selected')) { + select.options[b].setAttribute('selected', 'selected'); + } else { + select.options[b].removeAttribute('selected'); + } + } else { + select.value = d; + } + } + } + // Create DIV + var div = document.createElement('div'); + el.parentNode.insertBefore(div, el); + el.style.display = 'none'; + el = div; + + return { el:el, options:options }; +} + +/** + * (c) jTools Text Editor + * https://github.com/paulhodel/jtools + * + * @author: Paul Hodel <paul.hodel@gmail.com> + * @description: Inline richtext editor + */ + +jSuites.editor = (function(el, options) { + var obj = {}; + obj.options = {}; + + // Default configuration + var defaults = { + // Initial HTML content + value: null, + // Initial snippet + snippet: null, + // Add toolbar + toolbar: null, + // Max height + maxHeight: null, + // Website parser is to read websites and images from cross domain + remoteParser: null, + // Key from youtube to read properties from URL + youtubeKey: null, + // User list + userSearch: null, + // Parse URL + parseURL: false, + // Accept drop files + dropZone: true, + dropAsAttachment: false, + acceptImages: true, + acceptFiles: false, + maxFileSize: 5000000, + // Border + border: true, + padding: true, + focus: false, + // Events + onclick: null, + onfocus: null, + onblur: null, + onload: null, + onenter: null, + onkeyup: null, + onkeydown: null, + }; + + // Loop through our object + for (var property in defaults) { + if (options && options.hasOwnProperty(property)) { + obj.options[property] = options[property]; + } else { + obj.options[property] = defaults[property]; + } + } + + // Private controllers + var imageResize = 0; + var editorTimer = null; + var editorAction = null; + + // Make sure element is empty + el.innerHTML = ''; + + // Prepare container + el.classList.add('jeditor-container'); + + // Padding + if (obj.options.padding == true) { + el.classList.add('jeditor-padding'); + } + + // Border + if (obj.options.border == false) { + el.style.border = '0px'; + } + + // Snippet + var snippet = document.createElement('div'); + snippet.className = 'snippet'; + snippet.setAttribute('contenteditable', false); + + // Toolbar + var toolbar = document.createElement('div'); + toolbar.className = 'jeditor-toolbar'; + + // Create editor + var editor = document.createElement('div'); + editor.setAttribute('contenteditable', true); + editor.setAttribute('spellcheck', false); + editor.className = 'jeditor'; + + // Max height + if (obj.options.maxHeight) { + editor.style.overflowY = 'auto'; + editor.style.maxHeight = obj.options.maxHeight; + } + + // Set editor initial value + if (obj.options.value) { + var value = obj.options.value; + } else { + var value = el.innerHTML ? el.innerHTML : ''; + } + + if (! value) { + var value = '<br>'; + } + + /** + * Extract images from a HTML string + */ + var extractImageFromHtml = function(html) { + // Create temp element + var div = document.createElement('div'); + div.innerHTML = html; + + // Extract images + var img = div.querySelectorAll('img'); + + if (img.length) { + for (var i = 0; i < img.length; i++) { + obj.addImage(img[i].src); + } + } + } + + /** + * Insert node at caret + */ + var insertNodeAtCaret = function(newNode) { + var sel, range; + + if (window.getSelection) { + sel = window.getSelection(); + if (sel.rangeCount) { + range = sel.getRangeAt(0); + var selectedText = range.toString(); + range.deleteContents(); + range.insertNode(newNode); + // move the cursor after element + range.setStartAfter(newNode); + range.setEndAfter(newNode); + sel.removeAllRanges(); + sel.addRange(range); + } + } + } + + /** + * Append snippet or thumbs in the editor + * @Param object data + */ + var appendElement = function(data) { + // Reset snippet + snippet.innerHTML = ''; + + if (data.image) { + var div = document.createElement('div'); + div.className = 'snippet-image'; + div.setAttribute('data-k', 'image'); + snippet.appendChild(div); + + var image = document.createElement('img'); + image.src = data.image; + div.appendChild(image); + } + + var div = document.createElement('div'); + div.className = 'snippet-title'; + div.setAttribute('data-k', 'title'); + div.innerHTML = data.title; + snippet.appendChild(div); + + var div = document.createElement('div'); + div.className = 'snippet-description'; + div.setAttribute('data-k', 'description'); + div.innerHTML = data.description; + snippet.appendChild(div); + + var div = document.createElement('div'); + div.className = 'snippet-host'; + div.setAttribute('data-k', 'host'); + div.innerHTML = data.host; + snippet.appendChild(div); + + var div = document.createElement('div'); + div.className = 'snippet-url'; + div.setAttribute('data-k', 'url'); + div.innerHTML = data.url; + snippet.appendChild(div); + + editor.appendChild(snippet); + } + + var verifyEditor = function() { + clearTimeout(editorTimer); + editorTimer = setTimeout(function() { + var snippet = editor.querySelector('.snippet'); + var thumbsContainer = el.querySelector('.jeditor-thumbs-container'); + + if (! snippet && ! thumbsContainer) { + var html = editor.innerHTML.replace(/\n/g, ' '); + var container = document.createElement('div'); + container.innerHTML = html; + var thumbsContainer = container.querySelector('.jeditor-thumbs-container'); + if (thumbsContainer) { + thumbsContainer.remove(); + } + var text = container.innerText; + var url = jSuites.editor.detectUrl(text); + + if (url) { + if (url[0].substr(-3) == 'jpg' || url[0].substr(-3) == 'png' || url[0].substr(-3) == 'gif') { + if (jSuites.editor.getDomain(url[0]) == window.location.hostname) { + obj.importImage(url[0], ''); + } else { + obj.importImage(obj.options.remoteParser + url[0], ''); + } + } else { + var id = jSuites.editor.youtubeParser(url[0]); + + if (id) { + obj.getYoutube(id); + } else { + obj.getWebsite(url[0]); + } + } + } + } + }, 1000); + } + + obj.parseContent = function() { + verifyEditor(); + } + + /** + * Get metadata from a youtube video + */ + obj.getYoutube = function(id) { + if (! obj.options.youtubeKey) { + console.error('The youtubeKey is not defined'); + } else { + jSuites.ajax({ + url: 'https://www.googleapis.com/youtube/v3/videos?part=snippet,statistics&key=' + obj.options.youtubeKey + '&id=' + id, + method: 'GET', + dataType: 'json', + success: function(result) { + // Only valid elements to be appended + if (result.items && result.items[0]) { + var p = { + title: '', + description: '', + image: '', + host: 'www.youtube.com', + url: 'https://www.youtube.com?watch=' + id, + } + if (result.items[0].snippet.title) { + p.title = result.items[0].snippet.title; + } + if (result.items[0].snippet.description) { + p.description = result.items[0].snippet.description; + + if (p.description.length > 150) { + p.description = p.description.substr(0, 150) + '...'; + } + } + if (result.items[0].snippet.thumbnails.medium.url) { + p.image = result.items[0].snippet.thumbnails.medium.url; + } + + appendElement(p); + } + } + }); + } + } + + /** + * Get meta information from a website + */ + obj.getWebsite = function(url) { + if (! obj.options.remoteParser) { + console.log('The remoteParser is not defined'); + } else { + jSuites.ajax({ + url: obj.options.remoteParser + encodeURI(url.trim()), + method: 'GET', + dataType: 'json', + success: function(result) { + var p = { + title: '', + description: '', + image: '', + host: url, + url: url, + } + + if (result.title) { + p.title = result.title; + } + if (result.description) { + p.description = result.description; + } + if (result.image) { + p.image = result.image; + } else if (result['og:image']) { + p.image = result['og:image']; + } + if (result.host) { + p.host = result.host; + } + if (result.url) { + p.url = result.url; + } + + appendElement(p); + } + }); + } + } + + /** + * Set editor value + */ + obj.setData = function(html) { + editor.innerHTML = html; + jSuites.editor.setCursor(editor, true); + } + + /** + * Get editor data + */ + obj.getData = function(json) { + if (! json) { + var data = editor.innerHTML; + } else { + var data = { + content : '', + } + + // Get tag users + var tagged = editor.querySelectorAll('.post-tag'); + if (tagged.length) { + data.users = []; + for (var i = 0; i < tagged.length; i++) { + var userId = tagged[i].getAttribute('data-user'); + if (userId) { + data.users.push(userId); + } + } + data.users = data.users.join(','); + } + + if (snippet.innerHTML) { + var index = 0; + data.snippet = {}; + for (var i = 0; i < snippet.children.length; i++) { + // Get key from element + var key = snippet.children[i].getAttribute('data-k'); + if (key) { + if (key == 'image') { + data.snippet.image = snippet.children[i].children[0].getAttribute('src'); + } else { + data.snippet[key] = snippet.children[i].innerHTML; + } + } + } + + snippet.innerHTML = ''; + snippet.remove(); + } + + var text = editor.innerHTML; + text = text.replace(/<br>/g, "\n"); + text = text.replace(/<\/div>/g, "<\/div>\n"); + text = text.replace(/<(?:.|\n)*?>/gm, ""); + data.content = text.trim(); + data = JSON.stringify(data); + } + + return data; + } + + // Reset + obj.reset = function() { + editor.innerHTML = ''; + } + + obj.addPdf = function(data) { + if (data.result.substr(0,4) != 'data') { + console.error('Invalid source'); + } else { + var canvas = document.createElement('canvas'); + canvas.width = 60; + canvas.height = 60; + + var img = new Image(); + var ctx = canvas.getContext('2d'); + ctx.drawImage(img, 0, 0, canvas.width, canvas.height); + + canvas.toBlob(function(blob) { + var newImage = document.createElement('img'); + newImage.src = window.URL.createObjectURL(blob); + newImage.setAttribute('data-extension', 'pdf'); + if (data.name) { + newImage.setAttribute('data-name', data.name); + } + if (data.size) { + newImage.setAttribute('data-size', data.size); + } + if (data.date) { + newImage.setAttribute('data-date', data.date); + } + newImage.className = 'jfile pdf'; + + insertNodeAtCaret(newImage); + jSuites.files[newImage.src] = data.result.substr(data.result.indexOf(',') + 1); + }); + } + } + + obj.addImage = function(src, name, size, date) { + if (src.substr(0,4) != 'data' && ! obj.options.remoteParser) { + console.error('remoteParser not defined in your initialization'); + } else { + // This is to process cross domain images + if (src.substr(0,4) == 'data') { + var extension = src.split(';') + extension = extension[0].split('/'); + extension = extension[1]; + } else { + var extension = src.substr(src.lastIndexOf('.') + 1); + // Work for cross browsers + src = obj.options.remoteParser + src; + } + + var img = new Image(); + + img.onload = function onload() { + var canvas = document.createElement('canvas'); + canvas.width = img.width; + canvas.height = img.height; + + var ctx = canvas.getContext('2d'); + ctx.drawImage(img, 0, 0, canvas.width, canvas.height); + + canvas.toBlob(function(blob) { + var newImage = document.createElement('img'); + newImage.src = window.URL.createObjectURL(blob); + newImage.setAttribute('tabindex', '900'); + newImage.setAttribute('data-extension', extension); + if (name) { + newImage.setAttribute('data-name', name); + } + if (size) { + newImage.setAttribute('data-size', size); + } + if (date) { + newImage.setAttribute('data-date', date); + } + newImage.className = 'jfile'; + var content = canvas.toDataURL(); + insertNodeAtCaret(newImage); + + jSuites.files[newImage.src] = content.substr(content.indexOf(',') + 1); + }); + }; + + img.src = src; + } + } + + obj.addFile = function(files) { + var reader = []; + + for (var i = 0; i < files.length; i++) { + if (files[i].size > obj.options.maxFileSize) { + alert('The file is too big'); + } else { + // Only PDF or Images + var type = files[i].type.split('/'); + + if (type[0] == 'image') { + type = 1; + } else if (type[1] == 'pdf') { + type = 2; + } else { + type = 0; + } + + if (type) { + // Create file + reader[i] = new FileReader(); + reader[i].index = i; + reader[i].type = type; + reader[i].name = files[i].name; + reader[i].date = files[i].lastModified; + reader[i].size = files[i].size; + reader[i].addEventListener("load", function (data) { + // Get result + if (data.target.type == 2) { + if (obj.options.acceptFiles == true) { + obj.addPdf(data.target); + } + } else { + obj.addImage(data.target.result, data.target.name, data.total, data.target.lastModified); + } + }, false); + + reader[i].readAsDataURL(files[i]) + } else { + alert('The extension is not allowed'); + } + } + } + } + + // Destroy + obj.destroy = function() { + editor.removeEventListener('mouseup', editorMouseUp); + editor.removeEventListener('mousedown', editorMouseDown); + editor.removeEventListener('mousemove', editorMouseMove); + editor.removeEventListener('keyup', editorKeyUp); + editor.removeEventListener('keydown', editorKeyDown); + editor.removeEventListener('dragstart', editorDragStart); + editor.removeEventListener('dragenter', editorDragEnter); + editor.removeEventListener('dragover', editorDragOver); + editor.removeEventListener('drop', editorDrop); + editor.removeEventListener('paste', editorPaste); + + if (typeof(obj.options.onblur) == 'function') { + editor.removeEventListener('blur', editorBlur); + } + if (typeof(obj.options.onfocus) == 'function') { + editor.removeEventListener('focus', editorFocus); + } + + el.editor = null; + el.classList.remove('jeditor-container'); + + toolbar.remove(); + snippet.remove(); + editor.remove(); + } + + var isLetter = function (str) { + 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]+)/g; + return str.match(regex) ? 1 : 0; + } + + // Event handlers + var editorMouseUp = function(e) { + editorAction = false; + } + + var editorMouseDown = function(e) { + var close = function(snippet) { + var rect = snippet.getBoundingClientRect(); + if (rect.width - (e.clientX - rect.left) < 40 && e.clientY - rect.top < 40) { + snippet.innerHTML = ''; + snippet.remove(); + } + } + + if (e.target.tagName == 'IMG') { + if (e.target.style.cursor) { + var rect = e.target.getBoundingClientRect(); + editorAction = { + e: e.target, + x: e.clientX, + y: e.clientY, + w: rect.width, + h: rect.height, + d: e.target.style.cursor, + } + + if (! e.target.style.width) { + e.target.style.width = rect.width + 'px'; + } + + if (! e.target.style.height) { + e.target.style.height = rect.height + 'px'; + } + + var s = window.getSelection(); + if (s.rangeCount) { + for (var i = 0; i < s.rangeCount; i++) { + s.removeRange(s.getRangeAt(i)); + } + } + } else { + editorAction = true; + } + } else { + if (e.target.classList.contains('snippet')) { + close(e.target); + } else if (e.target.parentNode.classList.contains('snippet')) { + close(e.target.parentNode); + } + + editorAction = true; + } + } + + var editorMouseMove = function(e) { + if (e.target.tagName == 'IMG') { + if (e.target.getAttribute('tabindex')) { + var rect = e.target.getBoundingClientRect(); + if (e.clientY - rect.top < 5) { + if (rect.width - (e.clientX - rect.left) < 5) { + e.target.style.cursor = 'ne-resize'; + } else if (e.clientX - rect.left < 5) { + e.target.style.cursor = 'nw-resize'; + } else { + e.target.style.cursor = 'n-resize'; + } + } else if (rect.height - (e.clientY - rect.top) < 5) { + if (rect.width - (e.clientX - rect.left) < 5) { + e.target.style.cursor = 'se-resize'; + } else if (e.clientX - rect.left < 5) { + e.target.style.cursor = 'sw-resize'; + } else { + e.target.style.cursor = 's-resize'; + } + } else if (rect.width - (e.clientX - rect.left) < 5) { + e.target.style.cursor = 'e-resize'; + } else if (e.clientX - rect.left < 5) { + e.target.style.cursor = 'w-resize'; + } else { + e.target.style.cursor = ''; + } + } + } + + // Move + if (e.which == 1 && editorAction && editorAction.d) { + if (editorAction.d == 'e-resize' || editorAction.d == 'ne-resize' || editorAction.d == 'se-resize') { + editorAction.e.style.width = (editorAction.w + (e.clientX - editorAction.x)) + 'px'; + + if (e.shiftKey) { + var newHeight = (e.clientX - editorAction.x) * (editorAction.h / editorAction.w); + editorAction.e.style.height = editorAction.h + newHeight + 'px'; + } else { + var newHeight = null; + } + } + + if (! newHeight) { + if (editorAction.d == 's-resize' || editorAction.d == 'se-resize' || editorAction.d == 'sw-resize') { + if (! e.shiftKey) { + editorAction.e.style.height = editorAction.h + (e.clientY - editorAction.y); + } + } + } + } + } + + var editorKeyUp = function(e) { + if (! editor.innerHTML) { + editor.innerHTML = '<div><br></div>'; + } + + if (typeof(obj.options.onkeyup) == 'function') { + obj.options.onkeyup(e, el); + } + } + + + var editorKeyDown = function(e) { + // Check for URL + if (obj.options.parseURL == true) { + verifyEditor(); + } + + // Closable + if (typeof(obj.options.onenter) == 'function' && e.which == 13) { + var data = obj.getData(); + obj.options.onenter(obj, el, data, e); + } + + if (typeof(obj.options.onkeydown) == 'function') { + obj.options.onkeydown(e, el); + } + } + + var editorPaste = function(e) { + if (e.clipboardData || e.originalEvent.clipboardData) { + var html = (e.originalEvent || e).clipboardData.getData('text/html'); + var text = (e.originalEvent || e).clipboardData.getData('text/plain'); + var file = (e.originalEvent || e).clipboardData.files + } else if (window.clipboardData) { + var html = window.clipboardData.getData('Html'); + var text = window.clipboardData.getData('Text'); + var file = window.clipboardData.files + } + + if (file.length) { + // Paste a image from the clipboard + obj.addFile(file); + } else { + // Paste text + text = text.split('\r\n'); + var str = ''; + if (e.target.nodeName == 'DIV' && e.target.classList.contains('jeditor')) { + for (var i = 0; i < text.length; i++) { + var tmp = document.createElement('div'); + if (text[i]) { + tmp.innerHTML = text[i]; + } else { + tmp.innerHTML = '<br/>'; + } + e.target.appendChild(tmp); + } + } else { + for (var i = 0; i < text.length; i++) { + if (text[i]) { + str += '<div>' + text[i] + "</div>\r\n"; + } + } + // Insert text + document.execCommand('insertHtml', false, str); + } + + // Extra images from the paste + if (obj.options.acceptImages == true) { + extractImageFromHtml(html); + } + } + + e.preventDefault(); + } + + var editorDragStart = function(e) { + if (editorAction && editorAction.e) { + e.preventDefault(); + } + } + + var editorDragEnter = function(e) { + if (editorAction || obj.options.dropZone == false) { + // Do nothing + } else { + el.classList.add('jeditor-dragging'); + } + } + + var editorDragOver = function(e) { + if (editorAction || obj.options.dropZone == false) { + // Do nothing + } else { + if (editorTimer) { + clearTimeout(editorTimer); + } + + editorTimer = setTimeout(function() { + el.classList.remove('jeditor-dragging'); + }, 100); + } + } + + var editorDrop = function(e) { + if (editorAction || obj.options.dropZone == false) { + // Do nothing + } else { + // Position caret on the drop + var range = null; + if (document.caretRangeFromPoint) { + range=document.caretRangeFromPoint(e.clientX, e.clientY); + } else if (e.rangeParent) { + range=document.createRange(); + range.setStart(e.rangeParent,e.rangeOffset); + } + var sel = window.getSelection(); + sel.removeAllRanges(); + sel.addRange(range); + sel.anchorNode.parentNode.focus(); + + var html = (e.originalEvent || e).dataTransfer.getData('text/html'); + var text = (e.originalEvent || e).dataTransfer.getData('text/plain'); + var file = (e.originalEvent || e).dataTransfer.files; + + if (file.length) { + obj.addFile(file); + } else if (text) { + extractImageFromHtml(html); + } + + el.classList.remove('jeditor-dragging'); + e.preventDefault(); + } + } + + var editorBlur = function() { + obj.options.onblur(obj, el, obj.getData()); + } + + var editorFocus = function() { + obj.options.onfocus(obj, el, obj.getData()); + } + + editor.addEventListener('mouseup', editorMouseUp); + editor.addEventListener('mousedown', editorMouseDown); + editor.addEventListener('mousemove', editorMouseMove); + editor.addEventListener('keyup', editorKeyUp); + editor.addEventListener('keydown', editorKeyDown); + editor.addEventListener('dragstart', editorDragStart); + editor.addEventListener('dragenter', editorDragEnter); + editor.addEventListener('dragover', editorDragOver); + editor.addEventListener('drop', editorDrop); + editor.addEventListener('paste', editorPaste); + + // Blur + if (typeof(obj.options.onblur) == 'function') { + editor.addEventListener('blur', editorBlur); + } + + // Focus + if (typeof(obj.options.onfocus) == 'function') { + editor.addEventListener('focus', editorFocus); + } + + // Onload + if (typeof(obj.options.onload) == 'function') { + obj.options.onload(el, editor); + } + + // Set value to the editor + editor.innerHTML = value; + + // Append editor to the containre + el.appendChild(editor); + + // Snippet + if (obj.options.snippet) { + appendElement(obj.options.snippet); + } + + // Default toolbar + if (obj.options.toolbar == null) { + obj.options.toolbar = jSuites.editor.getDefaultToolbar(); + } + + // Add toolbar + if (obj.options.toolbar) { + for (var i = 0; i < obj.options.toolbar.length; i++) { + if (obj.options.toolbar[i].icon) { + var item = document.createElement('div'); + item.style.userSelect = 'none'; + var itemIcon = document.createElement('i'); + itemIcon.className = 'material-icons'; + itemIcon.innerHTML = obj.options.toolbar[i].icon; + itemIcon.onclick = (function (a) { + var b = a; + return function () { + obj.options.toolbar[b].onclick(el, obj, this) + }; + })(i); + item.appendChild(itemIcon); + toolbar.appendChild(item); + } else { + if (obj.options.toolbar[i].type == 'divisor') { + var item = document.createElement('div'); + item.className = 'jeditor-toolbar-divisor'; + toolbar.appendChild(item); + } else if (obj.options.toolbar[i].type == 'button') { + var item = document.createElement('div'); + item.classList.add('jeditor-toolbar-button'); + item.innerHTML = obj.options.toolbar[i].value; + toolbar.appendChild(item); + } + } + } + + el.appendChild(toolbar); + } + + // Focus to the editor + if (obj.options.focus) { + jSuites.editor.setCursor(editor, obj.options.focus == 'initial' ? true : false); + } + + el.editor = obj; + + return obj; +}); + +jSuites.editor.setCursor = function(element, first) { + element.focus(); + document.execCommand('selectAll'); + var sel = window.getSelection(); + var range = sel.getRangeAt(0); + if (first == true) { + var node = range.startContainer; + var size = 0; + } else { + var node = range.endContainer; + var size = node.length; + } + range.setStart(node, size); + range.setEnd(node, size); + sel.removeAllRanges(); + sel.addRange(range); +} + +jSuites.editor.getDomain = function(url) { + return url.replace('http://','').replace('https://','').replace('www.','').split(/[/?#]/)[0].split(/:/g)[0]; +} + +jSuites.editor.detectUrl = function(text) { + var expression = /(((https?:\/\/)|(www\.))[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|]+)/ig; + var links = text.match(expression); + + if (links) { + if (links[0].substr(0,3) == 'www') { + links[0] = 'http://' + links[0]; + } + } + + return links; +} + +jSuites.editor.youtubeParser = function(url) { + var regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#\&\?]*).*/; + var match = url.match(regExp); + + return (match && match[7].length == 11) ? match[7] : false; +} + +jSuites.editor.getDefaultToolbar = function() { + return [ + { + icon:'undo', + onclick: function() { + document.execCommand('undo'); + } + }, + { + icon:'redo', + onclick: function() { + document.execCommand('redo'); + } + }, + { + type:'divisor' + }, + { + icon:'format_bold', + onclick: function(a,b,c) { + document.execCommand('bold'); + + if (document.queryCommandState("bold")) { + c.classList.add('selected'); + } else { + c.classList.remove('selected'); + } + } + }, + { + icon:'format_italic', + onclick: function(a,b,c) { + document.execCommand('italic'); + + if (document.queryCommandState("italic")) { + c.classList.add('selected'); + } else { + c.classList.remove('selected'); + } + } + }, + { + icon:'format_underline', + onclick: function(a,b,c) { + document.execCommand('underline'); + + if (document.queryCommandState("underline")) { + c.classList.add('selected'); + } else { + c.classList.remove('selected'); + } + } + }, + { + type:'divisor' + }, + { + icon:'format_list_bulleted', + onclick: function(a,b,c) { + document.execCommand('insertUnorderedList'); + + if (document.queryCommandState("insertUnorderedList")) { + c.classList.add('selected'); + } else { + c.classList.remove('selected'); + } + } + }, + { + icon:'format_list_numbered', + onclick: function(a,b,c) { + document.execCommand('insertOrderedList'); + + if (document.queryCommandState("insertOrderedList")) { + c.classList.add('selected'); + } else { + c.classList.remove('selected'); + } + } + }, + { + icon:'format_indent_increase', + onclick: function(a,b,c) { + document.execCommand('indent', true, null); + + if (document.queryCommandState("indent")) { + c.classList.add('selected'); + } else { + c.classList.remove('selected'); + } + } + }, + { + icon:'format_indent_decrease', + onclick: function(a,b,c) { + document.execCommand('outdent'); + + if (document.queryCommandState("outdent")) { + c.classList.add('selected'); + } else { + c.classList.remove('selected'); + } + } + } + /*{ + type:'select', + items: ['Verdana','Arial','Courier New'], + onchange: function() { + } + }, + { + type:'select', + items: ['10px','12px','14px','16px','18px','20px','22px'], + onchange: function() { + } + }, + { + icon:'format_align_left', + onclick: function() { + document.execCommand('JustifyLeft'); + + if (document.queryCommandState("JustifyLeft")) { + this.classList.add('selected'); + } else { + this.classList.remove('selected'); + } + } + }, + { + icon:'format_align_center', + onclick: function() { + document.execCommand('justifyCenter'); + + if (document.queryCommandState("justifyCenter")) { + this.classList.add('selected'); + } else { + this.classList.remove('selected'); + } + } + }, + { + icon:'format_align_right', + onclick: function() { + document.execCommand('justifyRight'); + + if (document.queryCommandState("justifyRight")) { + this.classList.add('selected'); + } else { + this.classList.remove('selected'); + } + } + }, + { + icon:'format_align_justify', + onclick: function() { + document.execCommand('justifyFull'); + + if (document.queryCommandState("justifyFull")) { + this.classList.add('selected'); + } else { + this.classList.remove('selected'); + } + } + }, + { + icon:'format_list_bulleted', + onclick: function() { + document.execCommand('insertUnorderedList'); + + if (document.queryCommandState("insertUnorderedList")) { + this.classList.add('selected'); + } else { + this.classList.remove('selected'); + } + } + }*/ + ]; +} + + +jSuites.image = (function(el, options) { + var obj = {}; + obj.options = {}; + + // Default configuration + var defaults = { + minWidth: false, + onchange: null, + singleFile: true, + remoteParser: null, + text:{ + extensionNotAllowed:'The extension is not allowed', + imageTooSmall:'The resolution is too low, try a image with a better resolution. width > 800px', + } + }; + + // Loop through our object + for (var property in defaults) { + if (options && options.hasOwnProperty(property)) { + obj.options[property] = options[property]; + } else { + obj.options[property] = defaults[property]; + } + } + + // Upload icon + el.classList.add('jupload'); + + // Add image + obj.addImage = function(file) { + if (! file.date) { + file.date = ''; + } + var img = document.createElement('img'); + img.setAttribute('data-date', file.lastmodified ? file.lastmodified : file.date); + img.setAttribute('data-name', file.name); + img.setAttribute('data-size', file.size); + img.setAttribute('data-small', file.small ? file.small : ''); + img.setAttribute('data-cover', file.cover ? 1 : 0); + img.setAttribute('data-extension', file.extension); + img.setAttribute('src', file.file); + img.className = 'jfile'; + img.style.width = '100%'; + + return img; + } + + // Add image + obj.addImages = function(files) { + if (obj.options.singleFile == true) { + el.innerHTML = ''; + } + + for (var i = 0; i < files.length; i++) { + el.appendChild(obj.addImage(files[i])); + } + } + + obj.addFromFile = function(file) { + var type = file.type.split('/'); + if (type[0] == 'image') { + if (obj.options.singleFile == true) { + el.innerHTML = ''; + } + + var imageFile = new FileReader(); + imageFile.addEventListener("load", function (v) { + + var img = new Image(); + + img.onload = function onload() { + var canvas = document.createElement('canvas'); + canvas.width = img.width; + canvas.height = img.height; + + var ctx = canvas.getContext('2d'); + ctx.drawImage(img, 0, 0, canvas.width, canvas.height); + + var data = { + file: canvas.toDataURL(), + extension: file.name.substr(file.name.lastIndexOf('.') + 1), + name: file.name, + size: file.size, + lastmodified: file.lastModified, + } + var newImage = obj.addImage(data); + el.appendChild(newImage); + + // Onchange + if (typeof(obj.options.onchange) == 'function') { + obj.options.onchange(newImage); + } + }; + + img.src = v.srcElement.result; + }); + + imageFile.readAsDataURL(file); + } else { + alert(text.extentionNotAllowed); + } + } + + obj.addFromUrl = function(src) { + if (src.substr(0,4) != 'data' && ! obj.options.remoteParser) { + console.error('remoteParser not defined in your initialization'); + } else { + // This is to process cross domain images + if (src.substr(0,4) == 'data') { + var extension = src.split(';') + extension = extension[0].split('/'); + extension = extension[1]; + } else { + var extension = src.substr(src.lastIndexOf('.') + 1); + // Work for cross browsers + src = obj.options.remoteParser + src; + } + + var img = new Image(); + + img.onload = function onload() { + var canvas = document.createElement('canvas'); + canvas.width = img.width; + canvas.height = img.height; + + var ctx = canvas.getContext('2d'); + ctx.drawImage(img, 0, 0, canvas.width, canvas.height); + + canvas.toBlob(function(blob) { + var data = { + file: window.URL.createObjectURL(blob), + extension: extension + } + var newImage = obj.addImage(data); + el.appendChild(newImage); + + // Keep base64 ready to go + var content = canvas.toDataURL(); + jSuites.files[data.file] = content.substr(content.indexOf(',') + 1); + + // Onchange + if (typeof(obj.options.onchange) == 'function') { + obj.options.onchange(newImage); + } + }); + }; + + img.src = src; + } + } + + var attachmentInput = document.createElement('input'); + attachmentInput.type = 'file'; + attachmentInput.setAttribute('accept', 'image/*'); + attachmentInput.onchange = function() { + for (var i = 0; i < this.files.length; i++) { + obj.addFromFile(this.files[i]); + } + } + + el.addEventListener("dblclick", function(e) { + jSuites.click(attachmentInput); + }); + + el.addEventListener('dragenter', function(e) { + el.style.border = '1px dashed #000'; + }); + + el.addEventListener('dragleave', function(e) { + el.style.border = '1px solid #eee'; + }); + + el.addEventListener('dragstop', function(e) { + el.style.border = '1px solid #eee'; + }); + + el.addEventListener('dragover', function(e) { + e.preventDefault(); + }); + + el.addEventListener('drop', function(e) { + e.preventDefault(); + e.stopPropagation(); + + + var html = (e.originalEvent || e).dataTransfer.getData('text/html'); + var file = (e.originalEvent || e).dataTransfer.files; + + if (file.length) { + for (var i = 0; i < e.dataTransfer.files.length; i++) { + obj.addFromFile(e.dataTransfer.files[i]); + } + } else if (html) { + if (obj.options.singleFile == true) { + el.innerHTML = ''; + } + + // Create temp element + var div = document.createElement('div'); + div.innerHTML = html; + + // Extract images + var img = div.querySelectorAll('img'); + + if (img.length) { + for (var i = 0; i < img.length; i++) { + obj.addFromUrl(img[i].src); + } + } + } + + el.style.border = '1px solid #eee'; + + return false; + }); + + el.image = obj; + + return obj; +}); + +/** + * (c) jLoading + * https://github.com/paulhodel/jtools + * + * @author: Paul Hodel <paul.hodel@gmail.com> + * @description: Page loading spin + */ + +jSuites.loading = (function() { + var obj = {}; + + var loading = document.createElement('div'); + loading.className = 'jloading'; + + obj.show = function() { + document.body.appendChild(loading); + }; + + obj.hide = function() { + document.body.removeChild(loading); + }; + + return obj; +})(); + +/** + * (c) jLogin + * https://github.com/paulhodel/jtools + * + * @author: Paul Hodel <paul.hodel@gmail.com> + * @description: Login helper + */ + +jSuites.login = (function(el, options) { + var obj = {}; + obj.options = {}; + + // Default configuration + var defaults = { + url: window.location.href, + prepareRequest: null, + accessToken: null, + deviceToken: null, + facebookUrl: null, + facebookAuthentication: null, + maxHeight: null, + onload: null, + onsuccess: null, + onerror: null, + message: null, + logo: null, + newProfile: false, + newProfileUrl: false, + newProfileLogin: false, + fullscreen: false, + newPasswordValidation: null, + }; + + // Loop through our object + for (var property in defaults) { + if (options && options.hasOwnProperty(property)) { + obj.options[property] = options[property]; + } else { + obj.options[property] = defaults[property]; + } + } + + // Message console container + if (! obj.options.message) { + var messageElement = document.querySelector('.message'); + if (messageElement) { + obj.options.message = messageElement; + } + } + + // Action + var action = null; + + // Container + var container = document.createElement('form'); + el.appendChild(container); + + // Logo + var divLogo = document.createElement('div'); + divLogo.className = 'jlogin-logo' + container.appendChild(divLogo); + + if (obj.options.logo) { + var logo = document.createElement('img'); + logo.src = obj.options.logo; + divLogo.appendChild(logo); + } + + // Code + var labelCode = document.createElement('label'); + labelCode.innerHTML = 'Please enter here the code received'; + var inputCode = document.createElement('input'); + inputCode.type = 'number'; + inputCode.id = 'code'; + inputCode.setAttribute('maxlength', 6); + var divCode = document.createElement('div'); + divCode.appendChild(labelCode); + divCode.appendChild(inputCode); + + // Hash + var inputHash = document.createElement('input'); + inputHash.type = 'hidden'; + inputHash.name = 'h'; + var divHash = document.createElement('div'); + divHash.appendChild(inputHash); + + // Recovery + var inputRecovery = document.createElement('input'); + inputRecovery.type = 'hidden'; + inputRecovery.name = 'recovery'; + inputRecovery.value = '1'; + var divRecovery = document.createElement('div'); + divRecovery.appendChild(inputRecovery); + + // Login + var labelLogin = document.createElement('label'); + labelLogin.innerHTML = 'Login'; + var inputLogin = document.createElement('input'); + inputLogin.type = 'text'; + inputLogin.name = 'login'; + inputLogin.setAttribute('autocomplete', 'off'); + inputLogin.onkeyup = function() { + this.value = this.value.toLowerCase().replace(/[^a-zA-Z0-9_+]+/gi, ''); + } + var divLogin = document.createElement('div'); + divLogin.appendChild(labelLogin); + divLogin.appendChild(inputLogin); + + // Name + var labelName = document.createElement('label'); + labelName.innerHTML = 'Name'; + var inputName = document.createElement('input'); + inputName.type = 'text'; + inputName.name = 'name'; + var divName = document.createElement('div'); + divName.appendChild(labelName); + divName.appendChild(inputName); + + // Email + var labelUsername = document.createElement('label'); + labelUsername.innerHTML = 'E-mail'; + var inputUsername = document.createElement('input'); + inputUsername.type = 'text'; + inputUsername.name = 'username'; + inputUsername.setAttribute('autocomplete', 'new-username'); + var divUsername = document.createElement('div'); + divUsername.appendChild(labelUsername); + divUsername.appendChild(inputUsername); + + // Password + var labelPassword = document.createElement('label'); + labelPassword.innerHTML = 'New password'; + var inputPassword = document.createElement('input'); + inputPassword.type = 'password'; + inputPassword.name = 'password'; + inputPassword.setAttribute('autocomplete', 'new-password'); + var divPassword = document.createElement('div'); + divPassword.appendChild(labelPassword); + divPassword.appendChild(inputPassword); + divPassword.onkeydown = function(e) { + if (e.keyCode == 13) { + obj.execute(); + } + } + + // Repeat password + var labelRepeatPassword = document.createElement('label'); + labelRepeatPassword.innerHTML = 'Repeat the new password'; + var inputRepeatPassword = document.createElement('input'); + inputRepeatPassword.type = 'password'; + inputRepeatPassword.name = 'password'; + var divRepeatPassword = document.createElement('div'); + divRepeatPassword.appendChild(labelRepeatPassword); + divRepeatPassword.appendChild(inputRepeatPassword); + + // Remember checkbox + var labelRemember = document.createElement('label'); + labelRemember.innerHTML = 'Remember me on this device'; + var inputRemember = document.createElement('input'); + inputRemember.type = 'checkbox'; + inputRemember.name = 'remember'; + inputRemember.value = '1'; + labelRemember.appendChild(inputRemember); + var divRememberButton = document.createElement('div'); + divRememberButton.className = 'rememberButton'; + divRememberButton.appendChild(labelRemember); + + // Login button + var actionButton = document.createElement('input'); + actionButton.type = 'button'; + actionButton.value = 'Log In'; + actionButton.onclick = function() { + obj.execute(); + } + var divActionButton = document.createElement('div'); + divActionButton.appendChild(actionButton); + + // Cancel button + var cancelButton = document.createElement('div'); + cancelButton.innerHTML = 'Cancel'; + cancelButton.className = 'cancelButton'; + cancelButton.onclick = function() { + obj.requestAccess(); + } + var divCancelButton = document.createElement('div'); + divCancelButton.appendChild(cancelButton); + + // Captcha + var labelCaptcha = document.createElement('label'); + labelCaptcha.innerHTML = 'Please type here the code below'; + var inputCaptcha = document.createElement('input'); + inputCaptcha.type = 'text'; + inputCaptcha.name = 'captcha'; + var imageCaptcha = document.createElement('img'); + var divCaptcha = document.createElement('div'); + divCaptcha.className = 'jlogin-captcha'; + divCaptcha.appendChild(labelCaptcha); + divCaptcha.appendChild(inputCaptcha); + divCaptcha.appendChild(imageCaptcha); + + // Facebook + var facebookButton = document.createElement('div'); + facebookButton.innerHTML = 'Login with Facebook'; + facebookButton.className = 'facebookButton'; + var divFacebookButton = document.createElement('div'); + divFacebookButton.appendChild(facebookButton); + divFacebookButton.onclick = function() { + obj.requestLoginViaFacebook(); + } + // Forgot password + var inputRequest = document.createElement('span'); + inputRequest.innerHTML = 'Request a new password'; + var divRequestButton = document.createElement('div'); + divRequestButton.className = 'requestButton'; + divRequestButton.appendChild(inputRequest); + divRequestButton.onclick = function() { + obj.requestNewPassword(); + } + // Create a new Profile + var inputNewProfile = document.createElement('span'); + inputNewProfile.innerHTML = 'Create a new profile'; + var divNewProfileButton = document.createElement('div'); + divNewProfileButton.className = 'newProfileButton'; + divNewProfileButton.appendChild(inputNewProfile); + divNewProfileButton.onclick = function() { + obj.newProfile(); + } + + el.className = 'jlogin'; + + if (obj.options.fullscreen == true) { + el.classList.add('jlogin-fullscreen'); + } + + /** + * Show message + */ + obj.showMessage = function(data) { + var message = (typeof(data) == 'object') ? data.message : data; + + if (typeof(obj.options.showMessage) == 'function') { + obj.options.showMessage(data); + } else { + jSuites.alert(data); + } + } + + /** + * New profile + */ + obj.newProfile = function() { + container.innerHTML = ''; + container.appendChild(divLogo); + if (obj.options.newProfileLogin) { + container.appendChild(divLogin); + } + container.appendChild(divName); + container.appendChild(divUsername); + container.appendChild(divActionButton); + if (obj.options.facebookAuthentication == true) { + container.appendChild(divFacebookButton); + } + container.appendChild(divCancelButton); + + // Reset inputs + inputLogin.value = ''; + inputUsername.value = ''; + inputPassword.value = ''; + + // Button + actionButton.value = 'Create new profile'; + + // Action + action = 'newProfile'; + } + + /** + * Request the email with the recovery instructions + */ + obj.requestNewPassword = function() { + if (Array.prototype.indexOf.call(container.children, divCaptcha) >= 0) { + var captcha = true; + } + + container.innerHTML = ''; + container.appendChild(divLogo); + container.appendChild(divRecovery); + container.appendChild(divUsername); + if (captcha) { + container.appendChild(divCaptcha); + } + container.appendChild(divActionButton); + container.appendChild(divCancelButton); + actionButton.value = 'Request a new password'; + inputRecovery.value = 1; + + // Action + action = 'requestNewPassword'; + } + + /** + * Confirm recovery code + */ + obj.codeConfirmation = function() { + container.innerHTML = ''; + container.appendChild(divLogo); + container.appendChild(divHash); + container.appendChild(divCode); + container.appendChild(divActionButton); + container.appendChild(divCancelButton); + actionButton.value = 'Confirm code'; + inputRecovery.value = 2; + + // Action + action = 'codeConfirmation'; + } + + /** + * Update my password + */ + obj.changeMyPassword = function(hash) { + container.innerHTML = ''; + container.appendChild(divLogo); + container.appendChild(divHash); + container.appendChild(divPassword); + container.appendChild(divRepeatPassword); + container.appendChild(divActionButton); + container.appendChild(divCancelButton); + actionButton.value = 'Change my password'; + inputHash.value = hash; + + // Action + action = 'changeMyPassword'; + } + + /** + * Request access default method + */ + obj.requestAccess = function() { + container.innerHTML = ''; + container.appendChild(divLogo); + container.appendChild(divUsername); + container.appendChild(divPassword); + container.appendChild(divActionButton); + if (obj.options.facebookAuthentication == true) { + container.appendChild(divFacebookButton); + } + container.appendChild(divRequestButton); + container.appendChild(divRememberButton); + container.appendChild(divRequestButton); + if (obj.options.newProfile == true) { + container.appendChild(divNewProfileButton); + } + + // Button + actionButton.value = 'Login'; + + // Password + inputPassword.value = ''; + + // Email persistence + if (window.localStorage.getItem('username')) { + inputUsername.value = window.localStorage.getItem('username'); + inputPassword.focus(); + } else { + inputUsername.focus(); + } + + // Action + action = 'requestAccess'; + } + + /** + * Request login via facebook + */ + obj.requestLoginViaFacebook = function() { + if (typeof(deviceNotificationToken) == 'undefined') { + FB.getLoginStatus(function(response) { + if (! response.status || response.status != 'connected') { + FB.login(function(response) { + if (response.authResponse) { + obj.execute({ f:response.authResponse.accessToken }); + } else { + obj.showMessage('Not authorized by facebook'); + } + }, {scope: 'public_profile,email'}); + } else { + obj.execute({ f:response.authResponse.accessToken }); + } + }, true); + } else { + jDestroy = function() { + fbLogin.removeEventListener('loadstart', jStart); + fbLogin.removeEventListener('loaderror', jError); + fbLogin.removeEventListener('exit', jExit); + fbLogin.close(); + fbLogin = null; + } + + jStart = function(event) { + var url = event.url; + if (url.indexOf("access_token") >= 0) { + setTimeout(function(){ + var u = url.match(/=(.*?)&/); + if (u[1].length > 32) { + obj.execute({ f:u[1] }); + } + jDestroy(); + },500); + } + + if (url.indexOf("error=access_denied") >= 0) { + setTimeout(jDestroy ,500); + // Not authorized by facebook + obj.showMessage('Not authorized by facebook'); + } + } + + jError = function(event) { + jDestroy(); + } + + jExit = function(event) { + jDestroy(); + } + + fbLogin = window.open(obj.options.facebookUrl, "_blank", "location=no,closebuttoncaption=Exit,disallowoverscroll=yes,toolbar=no"); + fbLogin.addEventListener('loadstart', jStart); + fbLogin.addEventListener('loaderror', jError); + fbLogin.addEventListener('exit', jExit); + } + + // Action + action = 'requestLoginViaFacebook'; + } + + // Perform request + obj.execute = function(data) { + // New profile + if (action == 'newProfile') { + var pattern = new RegExp(/^([\w-\.]+@([\w-]+\.)+[\w-]{2,4})?$/); + if (! inputUsername.value || ! pattern.test(inputUsername.value)) { + var message = 'Invalid e-mail address'; + } + + var pattern = new RegExp(/^[a-zA-Z0-9\_\-\.\s+]+$/); + if (! inputLogin.value || ! pattern.test(inputLogin.value)) { + var message = 'Invalid username, please use only characters and numbers'; + } + + if (message) { + obj.showMessage(message); + return false; + } + } else if (action == 'changeMyPassword') { + if (inputPassword.value.length < 3) { + var message = 'Password is too short'; + } else if (inputPassword.value != inputRepeatPassword.value) { + var message = 'Password should match'; + } else { + if (typeof(obj.options.newPasswordValidation) == 'function') { + var val = obj.options.newPasswordValidation(obj, inputPassword.value, inputPassword.value); + if (val != undefined) { + message = val; + } + } + } + + if (message) { + obj.showMessage(message); + return false; + } + } + + // Keep email + if (inputUsername.value != '') { + window.localStorage.setItem('username', inputUsername.value); + } + + // Captcha + if (Array.prototype.indexOf.call(container.children, divCaptcha) >= 0) { + if (inputCaptcha.value == '') { + obj.showMessage('Please enter the captch code below'); + return false; + } + } + + // Url + var url = obj.options.url; + + // Device token + if (obj.options.deviceToken) { + url += '?token=' + obj.options.deviceToken; + } + + // Callback + var onsuccess = function(result) { + if (result) { + // Successfully response + if (result.success == 1) { + // Recovery process + if (action == 'requestNewPassword') { + obj.codeConfirmation(); + } else if (action == 'codeConfirmation') { + obj.requestAccess(); + } else if (action == 'newProfile') { + obj.requestAccess(); + // New profile + result.newProfile = true; + } + + // Token + if (result.token) { + // Set token + obj.options.accessToken = result.token; + // Save token + window.localStorage.setItem('Access-Token', result.token); + } + } + + // Show message + if (result.message) { + // Show message + obj.showMessage(result.message) + } + + // Request captcha code + if (! result.data) { + if (Array.prototype.indexOf.call(container.children, divCaptcha) >= 0) { + divCaptcha.remove(); + } + } else { + container.insertBefore(divCaptcha, divActionButton); + imageCaptcha.setAttribute('src', 'data:image/png;base64,' + result.data); + } + + // Give time to user see the message + if (result.hash) { + // Change password + obj.changeMyPassword(result.hash); + } else if (result.url) { + // App initialization + if (result.success == 1) { + if (typeof(obj.options.onsuccess) == 'function') { + obj.options.onsuccess(result); + } else { + if (result.message) { + setTimeout(function() { window.location.href = result.url; }, 2000); + } else { + window.location.href = result.url; + } + } + } else { + if (typeof(obj.options.onerror) == 'function') { + obj.options.onerror(result); + } + } + } + } + } + + // Password + if (! data) { + var data = jSuites.getFormElements(el); + // Encode passworfd + if (data.password) { + data.password = jSuites.login.sha512(data.password); + } + // Recovery code + if (Array.prototype.indexOf.call(container.children, divCode) >= 0 && inputCode.value) { + data.h = jSuites.login.sha512(inputCode.value); + } + } + + // Loading + el.classList.add('jlogin-loading'); + + // Url + var url = (action == 'newProfile' && obj.options.newProfileUrl) ? obj.options.newProfileUrl : obj.options.url; + + // Remote call + jSuites.ajax({ + url: url, + method: 'POST', + dataType: 'json', + data: data, + success: function(result) { + // Remove loading + el.classList.remove('jlogin-loading'); + // Callback + onsuccess(result); + }, + error: function(result) { + // Error + el.classList.remove('jlogin-loading'); + + if (typeof(obj.options.onerror) == 'function') { + obj.options.onerror(result); + } + } + }); + } + + var queryString = window.location.href.split('?'); + if (queryString[1] && queryString[1].length == 130 && queryString[1].substr(0,2) == 'h=') { + obj.changeMyPassword(queryString[1].substr(2)); + } else { + obj.requestAccess(); + } + + return obj; +}); + +jSuites.login.sha512 = (function(str) { + function int64(msint_32, lsint_32) { + this.highOrder = msint_32; + this.lowOrder = lsint_32; + } + + var H = [new int64(0x6a09e667, 0xf3bcc908), new int64(0xbb67ae85, 0x84caa73b), + new int64(0x3c6ef372, 0xfe94f82b), new int64(0xa54ff53a, 0x5f1d36f1), + new int64(0x510e527f, 0xade682d1), new int64(0x9b05688c, 0x2b3e6c1f), + new int64(0x1f83d9ab, 0xfb41bd6b), new int64(0x5be0cd19, 0x137e2179)]; + + var K = [new int64(0x428a2f98, 0xd728ae22), new int64(0x71374491, 0x23ef65cd), + new int64(0xb5c0fbcf, 0xec4d3b2f), new int64(0xe9b5dba5, 0x8189dbbc), + new int64(0x3956c25b, 0xf348b538), new int64(0x59f111f1, 0xb605d019), + new int64(0x923f82a4, 0xaf194f9b), new int64(0xab1c5ed5, 0xda6d8118), + new int64(0xd807aa98, 0xa3030242), new int64(0x12835b01, 0x45706fbe), + new int64(0x243185be, 0x4ee4b28c), new int64(0x550c7dc3, 0xd5ffb4e2), + new int64(0x72be5d74, 0xf27b896f), new int64(0x80deb1fe, 0x3b1696b1), + new int64(0x9bdc06a7, 0x25c71235), new int64(0xc19bf174, 0xcf692694), + new int64(0xe49b69c1, 0x9ef14ad2), new int64(0xefbe4786, 0x384f25e3), + new int64(0x0fc19dc6, 0x8b8cd5b5), new int64(0x240ca1cc, 0x77ac9c65), + new int64(0x2de92c6f, 0x592b0275), new int64(0x4a7484aa, 0x6ea6e483), + new int64(0x5cb0a9dc, 0xbd41fbd4), new int64(0x76f988da, 0x831153b5), + new int64(0x983e5152, 0xee66dfab), new int64(0xa831c66d, 0x2db43210), + new int64(0xb00327c8, 0x98fb213f), new int64(0xbf597fc7, 0xbeef0ee4), + new int64(0xc6e00bf3, 0x3da88fc2), new int64(0xd5a79147, 0x930aa725), + new int64(0x06ca6351, 0xe003826f), new int64(0x14292967, 0x0a0e6e70), + new int64(0x27b70a85, 0x46d22ffc), new int64(0x2e1b2138, 0x5c26c926), + new int64(0x4d2c6dfc, 0x5ac42aed), new int64(0x53380d13, 0x9d95b3df), + new int64(0x650a7354, 0x8baf63de), new int64(0x766a0abb, 0x3c77b2a8), + new int64(0x81c2c92e, 0x47edaee6), new int64(0x92722c85, 0x1482353b), + new int64(0xa2bfe8a1, 0x4cf10364), new int64(0xa81a664b, 0xbc423001), + new int64(0xc24b8b70, 0xd0f89791), new int64(0xc76c51a3, 0x0654be30), + new int64(0xd192e819, 0xd6ef5218), new int64(0xd6990624, 0x5565a910), + new int64(0xf40e3585, 0x5771202a), new int64(0x106aa070, 0x32bbd1b8), + new int64(0x19a4c116, 0xb8d2d0c8), new int64(0x1e376c08, 0x5141ab53), + new int64(0x2748774c, 0xdf8eeb99), new int64(0x34b0bcb5, 0xe19b48a8), + new int64(0x391c0cb3, 0xc5c95a63), new int64(0x4ed8aa4a, 0xe3418acb), + new int64(0x5b9cca4f, 0x7763e373), new int64(0x682e6ff3, 0xd6b2b8a3), + new int64(0x748f82ee, 0x5defb2fc), new int64(0x78a5636f, 0x43172f60), + new int64(0x84c87814, 0xa1f0ab72), new int64(0x8cc70208, 0x1a6439ec), + new int64(0x90befffa, 0x23631e28), new int64(0xa4506ceb, 0xde82bde9), + new int64(0xbef9a3f7, 0xb2c67915), new int64(0xc67178f2, 0xe372532b), + new int64(0xca273ece, 0xea26619c), new int64(0xd186b8c7, 0x21c0c207), + new int64(0xeada7dd6, 0xcde0eb1e), new int64(0xf57d4f7f, 0xee6ed178), + new int64(0x06f067aa, 0x72176fba), new int64(0x0a637dc5, 0xa2c898a6), + new int64(0x113f9804, 0xbef90dae), new int64(0x1b710b35, 0x131c471b), + new int64(0x28db77f5, 0x23047d84), new int64(0x32caab7b, 0x40c72493), + new int64(0x3c9ebe0a, 0x15c9bebc), new int64(0x431d67c4, 0x9c100d4c), + new int64(0x4cc5d4be, 0xcb3e42b6), new int64(0x597f299c, 0xfc657e2a), + new int64(0x5fcb6fab, 0x3ad6faec), new int64(0x6c44198c, 0x4a475817)]; + + var W = new Array(64); + var a, b, c, d, e, f, g, h, i, j; + var T1, T2; + var charsize = 8; + + function utf8_encode(str) { + return unescape(encodeURIComponent(str)); + } + + function str2binb(str) { + var bin = []; + var mask = (1 << charsize) - 1; + var len = str.length * charsize; + + for (var i = 0; i < len; i += charsize) { + bin[i >> 5] |= (str.charCodeAt(i / charsize) & mask) << (32 - charsize - (i % 32)); + } + + return bin; + } + + function binb2hex(binarray) { + var hex_tab = "0123456789abcdef"; + var str = ""; + var length = binarray.length * 4; + var srcByte; + + for (var i = 0; i < length; i += 1) { + srcByte = binarray[i >> 2] >> ((3 - (i % 4)) * 8); + str += hex_tab.charAt((srcByte >> 4) & 0xF) + hex_tab.charAt(srcByte & 0xF); + } + + return str; + } + + function safe_add_2(x, y) { + var lsw, msw, lowOrder, highOrder; + + lsw = (x.lowOrder & 0xFFFF) + (y.lowOrder & 0xFFFF); + msw = (x.lowOrder >>> 16) + (y.lowOrder >>> 16) + (lsw >>> 16); + lowOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); + + lsw = (x.highOrder & 0xFFFF) + (y.highOrder & 0xFFFF) + (msw >>> 16); + msw = (x.highOrder >>> 16) + (y.highOrder >>> 16) + (lsw >>> 16); + highOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); + + return new int64(highOrder, lowOrder); + } + + function safe_add_4(a, b, c, d) { + var lsw, msw, lowOrder, highOrder; + + lsw = (a.lowOrder & 0xFFFF) + (b.lowOrder & 0xFFFF) + (c.lowOrder & 0xFFFF) + (d.lowOrder & 0xFFFF); + msw = (a.lowOrder >>> 16) + (b.lowOrder >>> 16) + (c.lowOrder >>> 16) + (d.lowOrder >>> 16) + (lsw >>> 16); + lowOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); + + lsw = (a.highOrder & 0xFFFF) + (b.highOrder & 0xFFFF) + (c.highOrder & 0xFFFF) + (d.highOrder & 0xFFFF) + (msw >>> 16); + msw = (a.highOrder >>> 16) + (b.highOrder >>> 16) + (c.highOrder >>> 16) + (d.highOrder >>> 16) + (lsw >>> 16); + highOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); + + return new int64(highOrder, lowOrder); + } + + function safe_add_5(a, b, c, d, e) { + var lsw, msw, lowOrder, highOrder; + + lsw = (a.lowOrder & 0xFFFF) + (b.lowOrder & 0xFFFF) + (c.lowOrder & 0xFFFF) + (d.lowOrder & 0xFFFF) + (e.lowOrder & 0xFFFF); + msw = (a.lowOrder >>> 16) + (b.lowOrder >>> 16) + (c.lowOrder >>> 16) + (d.lowOrder >>> 16) + (e.lowOrder >>> 16) + (lsw >>> 16); + lowOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); + + lsw = (a.highOrder & 0xFFFF) + (b.highOrder & 0xFFFF) + (c.highOrder & 0xFFFF) + (d.highOrder & 0xFFFF) + (e.highOrder & 0xFFFF) + (msw >>> 16); + msw = (a.highOrder >>> 16) + (b.highOrder >>> 16) + (c.highOrder >>> 16) + (d.highOrder >>> 16) + (e.highOrder >>> 16) + (lsw >>> 16); + highOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); + + return new int64(highOrder, lowOrder); + } + + function maj(x, y, z) { + return new int64( + (x.highOrder & y.highOrder) ^ (x.highOrder & z.highOrder) ^ (y.highOrder & z.highOrder), + (x.lowOrder & y.lowOrder) ^ (x.lowOrder & z.lowOrder) ^ (y.lowOrder & z.lowOrder) + ); + } + + function ch(x, y, z) { + return new int64( + (x.highOrder & y.highOrder) ^ (~x.highOrder & z.highOrder), + (x.lowOrder & y.lowOrder) ^ (~x.lowOrder & z.lowOrder) + ); + } + + function rotr(x, n) { + if (n <= 32) { + return new int64( + (x.highOrder >>> n) | (x.lowOrder << (32 - n)), + (x.lowOrder >>> n) | (x.highOrder << (32 - n)) + ); + } else { + return new int64( + (x.lowOrder >>> n) | (x.highOrder << (32 - n)), + (x.highOrder >>> n) | (x.lowOrder << (32 - n)) + ); + } + } + + function sigma0(x) { + var rotr28 = rotr(x, 28); + var rotr34 = rotr(x, 34); + var rotr39 = rotr(x, 39); + + return new int64( + rotr28.highOrder ^ rotr34.highOrder ^ rotr39.highOrder, + rotr28.lowOrder ^ rotr34.lowOrder ^ rotr39.lowOrder + ); + } + + function sigma1(x) { + var rotr14 = rotr(x, 14); + var rotr18 = rotr(x, 18); + var rotr41 = rotr(x, 41); + + return new int64( + rotr14.highOrder ^ rotr18.highOrder ^ rotr41.highOrder, + rotr14.lowOrder ^ rotr18.lowOrder ^ rotr41.lowOrder + ); + } + + function gamma0(x) { + var rotr1 = rotr(x, 1), rotr8 = rotr(x, 8), shr7 = shr(x, 7); + + return new int64( + rotr1.highOrder ^ rotr8.highOrder ^ shr7.highOrder, + rotr1.lowOrder ^ rotr8.lowOrder ^ shr7.lowOrder + ); + } + + function gamma1(x) { + var rotr19 = rotr(x, 19); + var rotr61 = rotr(x, 61); + var shr6 = shr(x, 6); + + return new int64( + rotr19.highOrder ^ rotr61.highOrder ^ shr6.highOrder, + rotr19.lowOrder ^ rotr61.lowOrder ^ shr6.lowOrder + ); + } + + function shr(x, n) { + if (n <= 32) { + return new int64( + x.highOrder >>> n, + x.lowOrder >>> n | (x.highOrder << (32 - n)) + ); + } else { + return new int64( + 0, + x.highOrder << (32 - n) + ); + } + } + + var str = utf8_encode(str); + var strlen = str.length*charsize; + str = str2binb(str); + + str[strlen >> 5] |= 0x80 << (24 - strlen % 32); + str[(((strlen + 128) >> 10) << 5) + 31] = strlen; + + for (var i = 0; i < str.length; i += 32) { + a = H[0]; + b = H[1]; + c = H[2]; + d = H[3]; + e = H[4]; + f = H[5]; + g = H[6]; + h = H[7]; + + for (var j = 0; j < 80; j++) { + if (j < 16) { + W[j] = new int64(str[j*2 + i], str[j*2 + i + 1]); + } else { + W[j] = safe_add_4(gamma1(W[j - 2]), W[j - 7], gamma0(W[j - 15]), W[j - 16]); + } + + T1 = safe_add_5(h, sigma1(e), ch(e, f, g), K[j], W[j]); + T2 = safe_add_2(sigma0(a), maj(a, b, c)); + h = g; + g = f; + f = e; + e = safe_add_2(d, T1); + d = c; + c = b; + b = a; + a = safe_add_2(T1, T2); + } + + H[0] = safe_add_2(a, H[0]); + H[1] = safe_add_2(b, H[1]); + H[2] = safe_add_2(c, H[2]); + H[3] = safe_add_2(d, H[3]); + H[4] = safe_add_2(e, H[4]); + H[5] = safe_add_2(f, H[5]); + H[6] = safe_add_2(g, H[6]); + H[7] = safe_add_2(h, H[7]); + } + + var binarray = []; + for (var i = 0; i < H.length; i++) { + binarray.push(H[i].highOrder); + binarray.push(H[i].lowOrder); + } + + return binb2hex(binarray); +}); + +/** + * (c) jTools Input Mask + * https://github.com/paulhodel/jtools + * + * @author: Paul Hodel <paul.hodel@gmail.com> + * @description: Input mask + */ + +jSuites.mask = (function() { + var obj = {}; + var index = 0; + var values = [] + var pieces = []; + + obj.run = function(value, mask, decimal) { + if (value && mask) { + if (! decimal) { + decimal = '.'; + } + if (value == Number(value)) { + var number = (''+value).split('.'); + var value = number[0]; + var valueDecimal = number[1]; + } else { + value = '' + value; + } + index = 0; + values = []; + // Create mask token + obj.prepare(mask); + // Current value + var currentValue = value; + if (currentValue) { + // Checking current value + for (var i = 0; i < currentValue.length; i++) { + if (currentValue[i] != null) { + obj.process(currentValue[i]); + } + } + } + if (valueDecimal) { + obj.process(decimal); + var currentValue = valueDecimal; + if (currentValue) { + // Checking current value + for (var i = 0; i < currentValue.length; i++) { + if (currentValue[i] != null) { + obj.process(currentValue[i]); + } + } + } + } + // Formatted value + return values.join(''); + } else { + return ''; + } + } + + obj.apply = function(e) { + if (e.target && ! e.target.getAttribute('readonly')) { + var mask = e.target.getAttribute('data-mask'); + if (mask && e.keyCode > 46) { + index = 0; + values = []; + // Create mask token + obj.prepare(mask); + // Current value + if (e.target.selectionStart < e.target.selectionEnd) { + var currentValue = e.target.value.substring(0, e.target.selectionStart); + } else { + var currentValue = e.target.value; + } + if (currentValue) { + // Checking current value + for (var i = 0; i < currentValue.length; i++) { + if (currentValue[i] != null) { + obj.process(currentValue[i]); + } + } + } + // New input + obj.process(obj.fromKeyCode(e)); + // Update value to the element + e.target.value = values.join(''); + if (pieces.length == values.length && pieces[pieces.length-1].length == values[values.length-1].length) { + e.target.setAttribute('data-completed', 'true'); + } else { + e.target.setAttribute('data-completed', 'false'); + } + // Prevent default + e.preventDefault(); + } + } + } + + /** + * Process inputs and save to values + */ + obj.process = function(input) { + do { + if (pieces[index] == 'mm') { + if (values[index] == null || values[index] == '') { + if (parseInt(input) > 1 && parseInt(input) < 10) { + values[index] = '0' + input; + index++; + return true; + } else if (parseInt(input) < 10) { + values[index] = input; + return true; + } else { + return false; + } + } else { + if (values[index] == 1 && values[index] < 2 && parseInt(input) < 3) { + values[index] += input; + index++; + return true; + } else if (values[index] == 0 && values[index] < 10) { + values[index] += input; + index++; + return true; + } else { + return false + } + } + } else if (pieces[index] == 'dd') { + if (values[index] == null || values[index] == '') { + if (parseInt(input) > 3 && parseInt(input) < 10) { + values[index] = '0' + input; + index++; + return true; + } else if (parseInt(input) < 10) { + values[index] = input; + return true; + } else { + return false; + } + } else { + if (values[index] == 3 && parseInt(input) < 2) { + values[index] += input; + index++; + return true; + } else if (values[index] < 3 && parseInt(input) < 10) { + values[index] += input; + index++; + return true; + } else { + return false + } + } + } else if (pieces[index] == 'hh24') { + if (values[index] == null || values[index] == '') { + if (parseInt(input) > 2 && parseInt(input) < 10) { + values[index] = '0' + input; + index++; + return true; + } else if (parseInt(input) < 10) { + values[index] = input; + return true; + } else { + return false; + } + } else { + if (values[index] == 2 && parseInt(input) < 4) { + values[index] += input; + index++; + return true; + } else if (values[index] < 2 && parseInt(input) < 10) { + values[index] += input; + index++; + return true; + } else { + return false + } + } + } else if (pieces[index] == 'hh') { + if (values[index] == null || values[index] == '') { + if (parseInt(input) > 1 && parseInt(input) < 10) { + values[index] = '0' + input; + index++; + return true; + } else if (parseInt(input) < 10) { + values[index] = input; + return true; + } else { + return false; + } + } else { + if (values[index] == 1 && parseInt(input) < 3) { + values[index] += input; + index++; + return true; + } else if (values[index] < 1 && parseInt(input) < 10) { + values[index] += input; + index++; + return true; + } else { + return false + } + } + } else if (pieces[index] == 'mi' || pieces[index] == 'ss') { + if (values[index] == null || values[index] == '') { + if (parseInt(input) > 5 && parseInt(input) < 10) { + values[index] = '0' + input; + index++; + return true; + } else if (parseInt(input) < 10) { + values[index] = input; + return true; + } else { + return false; + } + } else { + if (parseInt(input) < 10) { + values[index] += input; + index++; + return true; + } else { + return false + } + } + } else if (pieces[index] == 'yy' || pieces[index] == 'yyyy') { + if (parseInt(input) < 10) { + if (values[index] == null || values[index] == '') { + values[index] = input; + } else { + values[index] += input; + } + + if (values[index].length == pieces[index].length) { + index++; + } + return true; + } else { + return false; + } + } else if (pieces[index] == '#' || pieces[index] == '#.##' || pieces[index] == '#,##' || pieces[index] == '# ##') { + if (input.match(/[0-9]/g)) { + if (pieces[index] == '#.##') { + var separator = '.'; + } else if (pieces[index] == '#,##') { + var separator = ','; + } else if (pieces[index] == '# ##') { + var separator = ' '; + } else { + var separator = ''; + } + if (values[index] == null || values[index] == '') { + values[index] = input; + } else { + values[index] += input; + if (separator) { + values[index] = values[index].match(/[0-9]/g).join(''); + var t = []; + var s = 0; + for (var j = values[index].length - 1; j >= 0 ; j--) { + t.push(values[index][j]); + s++; + if (! (s % 3)) { + t.push(separator); + } + } + t = t.reverse(); + values[index] = t.join(''); + if (values[index].substr(0,1) == separator) { + values[index] = values[index].substr(1); + } + } + } + return true; + } else { + if (pieces[index] == '#.##' && input == '.') { + // Do nothing + } else if (pieces[index] == '#,##' && input == ',') { + // Do nothing + } else if (pieces[index] == '# ##' && input == ' ') { + // Do nothing + } else { + if (values[index]) { + index++; + if (pieces[index]) { + if (pieces[index] == input) { + values[index] = input; + return true; + } else { + if (pieces[index] == '0' && pieces[index+1] == input) { + index++; + values[index] = input; + return true; + } + } + } + } + } + + return false; + } + } else if (pieces[index] == '0') { + if (input.match(/[0-9]/g)) { + values[index] = input; + index++; + return true; + } else { + return false; + } + } else if (pieces[index] == 'a') { + if (input.match(/[a-zA-Z]/g)) { + values[index] = input; + index++; + return true; + } else { + return false; + } + } else { + if (pieces[index] != null) { + if (pieces[index] == '\\a') { + var v = 'a'; + } else if (pieces[index] == '\\0') { + var v = '0'; + } else if (pieces[index] == '[-]') { + if (input == '-' || input == '+') { + var v = input; + } else { + var v = ' '; + } + } else { + var v = pieces[index]; + } + values[index] = v; + if (input == v) { + index++; + return true; + } + } + } + + index++; + } while (pieces[index]); + } + + /** + * Create tokens for the mask + */ + obj.prepare = function(mask) { + pieces = []; + for (var i = 0; i < mask.length; i++) { + if (mask[i].match(/[0-9]|[a-z]|\\/g)) { + if (mask[i] == 'y' && mask[i+1] == 'y' && mask[i+2] == 'y' && mask[i+3] == 'y') { + pieces.push('yyyy'); + i += 3; + } else if (mask[i] == 'y' && mask[i+1] == 'y') { + pieces.push('yy'); + i++; + } else if (mask[i] == 'm' && mask[i+1] == 'm' && mask[i+2] == 'm' && mask[i+3] == 'm') { + pieces.push('mmmm'); + i += 3; + } else if (mask[i] == 'm' && mask[i+1] == 'm' && mask[i+2] == 'm') { + pieces.push('mmm'); + i += 2; + } else if (mask[i] == 'm' && mask[i+1] == 'm') { + pieces.push('mm'); + i++; + } else if (mask[i] == 'd' && mask[i+1] == 'd') { + pieces.push('dd'); + i++; + } else if (mask[i] == 'h' && mask[i+1] == 'h' && mask[i+2] == '2' && mask[i+3] == '4') { + pieces.push('hh24'); + i += 3; + } else if (mask[i] == 'h' && mask[i+1] == 'h') { + pieces.push('hh'); + i++; + } else if (mask[i] == 'm' && mask[i+1] == 'i') { + pieces.push('mi'); + i++; + } else if (mask[i] == 's' && mask[i+1] == 's') { + pieces.push('ss'); + i++; + } else if (mask[i] == 'a' && mask[i+1] == 'm') { + pieces.push('am'); + i++; + } else if (mask[i] == 'p' && mask[i+1] == 'm') { + pieces.push('pm'); + i++; + } else if (mask[i] == '\\' && mask[i+1] == '0') { + pieces.push('\\0'); + i++; + } else if (mask[i] == '\\' && mask[i+1] == 'a') { + pieces.push('\\a'); + i++; + } else { + pieces.push(mask[i]); + } + } else { + if (mask[i] == '#' && mask[i+1] == '.' && mask[i+2] == '#' && mask[i+3] == '#') { + pieces.push('#.##'); + i += 3; + } else if (mask[i] == '#' && mask[i+1] == ',' && mask[i+2] == '#' && mask[i+3] == '#') { + pieces.push('#,##'); + i += 3; + } else if (mask[i] == '#' && mask[i+1] == ' ' && mask[i+2] == '#' && mask[i+3] == '#') { + pieces.push('# ##'); + i += 3; + } else if (mask[i] == '[' && mask[i+1] == '-' && mask[i+2] == ']') { + pieces.push('[-]'); + i += 2; + } else { + pieces.push(mask[i]); + } + } + } + } + + /** + * Thanks for the collaboration + */ + obj.fromKeyCode = function(e) { + var _to_ascii = { + '188': '44', + '109': '45', + '190': '46', + '191': '47', + '192': '96', + '220': '92', + '222': '39', + '221': '93', + '219': '91', + '173': '45', + '187': '61', //IE Key codes + '186': '59', //IE Key codes + '189': '45' //IE Key codes + } + + var shiftUps = { + "96": "~", + "49": "!", + "50": "@", + "51": "#", + "52": "$", + "53": "%", + "54": "^", + "55": "&", + "56": "*", + "57": "(", + "48": ")", + "45": "_", + "61": "+", + "91": "{", + "93": "}", + "92": "|", + "59": ":", + "39": "\"", + "44": "<", + "46": ">", + "47": "?" + }; + + var c = e.which; + + if (_to_ascii.hasOwnProperty(c)) { + c = _to_ascii[c]; + } + + if (!e.shiftKey && (c >= 65 && c <= 90)) { + c = String.fromCharCode(c + 32); + } else if (e.shiftKey && shiftUps.hasOwnProperty(c)) { + c = shiftUps[c]; + } else if (96 <= c && c <= 105) { + c = String.fromCharCode(c - 48); + } else { + c = String.fromCharCode(c); + } + + return c; + } + + return obj; +})(); + +jSuites.mobile = (function(el, options) { + var obj = {}; + obj.options = {}; + + if (jSuites.el) { + jSuites.el.addEventListener('mousedown', function(e) { + if (e.target.classList.contains('option-title')) { + if (e.target.classList.contains('selected')) { + e.target.classList.remove('selected'); + } else { + e.target.classList.add('selected'); + } + } + }); + } + + return obj; +})(); + +jSuites.pages = (function() { + var container = null; + var current = null; + + // Create a page + var createPage = function(options, callback) { + // Create page + var page = document.createElement('div'); + page.classList.add('page'); + + // Always hidden + page.style.display = 'none'; + + // Keep options + page.options = options ? options : {}; + + if (! current) { + container.appendChild(page); + } else { + container.insertBefore(page, current.nextSibling); + } + + jSuites.ajax({ + url: page.options.url, + method: 'GET', + success: function(result) { + // Push to refresh controls + jSuites.refresh(page, page.options.onpush); + + // Open page + page.innerHTML = result; + // Get javascript + var script = page.getElementsByTagName('script'); + // Run possible inline scripts + for (var i = 0; i < script.length; i++) { + // Get type + var type = script[i].getAttribute('type'); + if (! type || type == 'text/javascript') { + eval(script[i].innerHTML); + } + } + // Set title + page.setTitle = function(text) { + this.children[0].children[0].children[1].innerHTML = text; + } + // Show page + if (! page.options.closed) { + showPage(page); + } + // Onload callback + if (typeof(page.options.onload) == 'function') { + page.options.onload(page); + } + // Force callback + if (typeof(callback) == 'function') { + callback(page); + } + } + }); + + return page; + } + + var showPage = function(page, ignoreHistory, callback) { + if (current) { + if (current == page) { + current = page; + } else { + // Keep scroll in the top + window.scrollTo({ top: 0 }); + + // Show page + page.style.display = ''; + + var a = Array.prototype.indexOf.call(container.children, current); + var b = Array.prototype.indexOf.call(container.children, page); + + // Leave + if (typeof(current.options.onleave) == 'function') { + current.options.onleave(current, page, ignoreHistory); + } + + jSuites.slideLeft(container, (a < b ? 0 : 1), function() { + current.style.display = 'none'; + current = page; + }); + + // Enter + if (typeof(page.options.onenter) == 'function') { + page.options.onenter(page, current, ignoreHistory); + } + } + } else { + // Show + page.style.display = ''; + + // Keep current + current = page; + + // Enter + if (typeof(page.options.onenter) == 'function') { + page.options.onenter(page); + } + } + + // Add history + if (! ignoreHistory) { + // Add history + window.history.pushState({ route: page.options.route }, page.options.title, page.options.route); + } + + // Callback + if (typeof(callback) == 'function') { + callback(page); + } + } + + // Init method + var obj = function(route, mixed) { + + // Create page container + if (! container) { + container = document.querySelector('.pages'); + if (! container) { + container = document.createElement('div'); + container.className = 'pages'; + } + + // Append container to the application + if (jSuites.el) { + jSuites.el.appendChild(container); + } else { + document.body.appendChild(container); + } + } + + if (! obj.pages[route]) { + if (! route) { + alert('Error, no route provided'); + } else { + if (typeof(mixed) == 'function') { + var options = {}; + var callback = mixed; + } else { + // Page options + var options = mixed ? mixed : {}; + } + + // Closed + options.closed = mixed && mixed.closed ? 1 : 0; + // Keep Route + options.route = route; + + // New page url + if (! options.url) { + var routePath = route.split('#'); + options.url = jSuites.pages.path + routePath[0] + '.html'; + } + // Title + if (! options.title) { + options.title = 'Untitled'; + } + + // Create new page + obj.pages[route] = createPage(options, callback ? callback : null); + } + } else { + // Update config + if (mixed) { + // History + var ignoreHistory = 0; + + if (typeof(mixed) == 'function') { + var callback = mixed; + } else { + if (typeof(mixed.onenter) == 'function') { + obj.pages[route].options.onenter = mixed.onenter; + } + if (typeof(mixed.onleave) == 'function') { + obj.pages[route].options.onleave = mixed.onleave; + } + + // Ignore history + ignoreHistory = mixed.ignoreHistory ? 1 : 0; + } + } + + showPage(obj.pages[route], ignoreHistory, callback ? callback : null); + } + } + + obj.pages = {}; + + // Get page + obj.get = function(route) { + if (obj.pages[route]) { + return obj.pages[route]; + } + } + + obj.getContainer = function() { + return container; + } + + obj.destroy = function() { + // Current is null + current = null; + // Destroy containers + obj.pages = {}; + // Reset container + if (container) { + container.innerHTML = ''; + } + } + + return obj; +})(); + +// Path +jSuites.pages.path = 'pages'; + +// Panel +jSuites.panel = (function() { + // No initial panel declared + var panel = null; + + var obj = function(route) { + if (! panel) { + obj.create(jSuites.pages.path + route + '.html'); + } + + // Show panel + panel.style.display = ''; + + // Add animation + if (panel.classList.contains('panel-left')) { + jSuites.slideLeft(panel, 1); + } else { + jSuites.slideRight(panel, 1); + } + } + + obj.create = function(route) { + if (! panel) { + // Create element + panel = document.createElement('div'); + panel.classList.add('panel'); + panel.classList.add('panel-left'); + panel.style.display = 'none'; + + // Bind to the app + if (jSuites.el) { + jSuites.el.appendChild(panel); + } else { + document.body.appendChild(panel); + } + } + + // Remote content + if (route) { + var url = jSuites.pages.path + route + '.html'; + + jSuites.ajax({ + url: url, + method: 'GET', + success: function(result) { + // Set content + panel.innerHTML = result; + // Get javascript + var script = panel.getElementsByTagName('script'); + // Run possible inline scripts + for (var i = 0; i < script.length; i++) { + // Get type + var type = script[i].getAttribute('type'); + if (! type || type == 'text/javascript') { + eval(script[i].innerHTML); + } + } + } + }); + } + } + + obj.close = function() { + if (panel) { + // Animation + if (panel.classList.contains('panel-left')) { + jSuites.slideLeft(panel, 0, function() { + panel.style.display = 'none'; + }); + } else { + jSuites.slideRight(panel, 0, function() { + panel.style.display = 'none'; + }); + } + } + } + + obj.get = function() { + return panel; + } + + obj.destroy = function() { + panel.remove(); + panel = null; + } + + return obj; +})(); + +jSuites.toolbar = (function(el, options) { + var obj = {}; + obj.options = options; + + obj.selectItem = function(element) { + var elements = toolbarContent.children; + for (var i = 0; i < elements.length; i++) { + elements[i].classList.remove('selected'); + } + element.classList.add('selected'); + } + + obj.hide = function() { + jSuites.slideBottom(toolbar, 0, function() { + toolbar.style.display = 'none'; + }); + } + + obj.show = function() { + toolbar.style.display = ''; + jSuites.slideBottom(toolbar, 1); + } + + obj.get = function() { + return toolbar; + } + + obj.setBadge = function(index, value) { + toolbarContent.children[index].children[1].firstChild.innerHTML = value; + } + + obj.destroy = function() { + toolbar.remove(); + toolbar = null; + } + + var toolbar = document.createElement('div'); + toolbar.classList.add('jtoolbar'); + toolbar.onclick = function(e) { + var element = jSuites.getElement(e.target, 'jtoolbar-item'); + if (element) { + obj.selectItem(element); + } + } + + var toolbarContent = document.createElement('div'); + toolbar.appendChild(toolbarContent); + + for (var i = 0; i < options.items.length; i++) { + var toolbarItem = document.createElement('div'); + toolbarItem.classList.add('jtoolbar-item'); + if (options.items[i].route) { + toolbarItem.setAttribute('data-href', options.items[i].route); + jSuites.pages(options.items[i].route, { + closed: true, + onenter: function() { + obj.selectItem(toolbarItem); + } + }); + } + + if (options.items[i].icon) { + var toolbarIcon = document.createElement('i'); + toolbarIcon.classList.add('material-icons'); + toolbarIcon.innerHTML = options.items[i].icon; + toolbarItem.appendChild(toolbarIcon); + } + + var toolbarBadge = document.createElement('div'); + toolbarBadge.classList.add('jbadge'); + var toolbarBadgeContent = document.createElement('div'); + toolbarBadgeContent.innerHTML = options.items[i].badge ? options.items[i].badge : ''; + toolbarBadge.appendChild(toolbarBadgeContent); + toolbarItem.appendChild(toolbarBadge); + + if (options.items[i].title) { + var toolbarTitle = document.createElement('span'); + toolbarTitle.innerHTML = options.items[i].title; + toolbarItem.appendChild(toolbarTitle); + } + + toolbarContent.appendChild(toolbarItem); + } + + el.toolbar = obj; + + el.appendChild(toolbar); + + return obj; +}); + +jSuites.actionsheet = (function() { + var actionsheet = document.createElement('div'); + actionsheet.className = 'jactionsheet'; + actionsheet.style.display = 'none'; + + var actionContent = document.createElement('div'); + actionContent.className = 'jactionsheet-content'; + actionsheet.appendChild(actionContent); + + var obj = function(options) { + if (options) { + obj.options = options; + } + + // Reset container + actionContent.innerHTML = ''; + + // Create new elements + for (var i = 0; i < obj.options.length; i++) { + var actionGroup = document.createElement('div'); + actionGroup.className = 'jactionsheet-group'; + + for (var j = 0; j < obj.options[i].length; j++) { + var v = obj.options[i][j]; + var actionItem = document.createElement('div'); + var actionInput = document.createElement('input'); + actionInput.type = 'button'; + actionInput.value = v.title; + if (v.className) { + actionInput.className = v.className; + } + if (v.onclick) { + actionInput.onclick = v.onclick; + } + if (v.action == 'cancel') { + actionInput.style.color = 'red'; + } + actionItem.appendChild(actionInput); + actionGroup.appendChild(actionItem); + } + + actionContent.appendChild(actionGroup); + } + + // Show + actionsheet.style.display = ''; + + // Append + jSuites.el.appendChild(actionsheet); + + // Animation + jSuites.slideBottom(actionContent, true); + } + + obj.close = function() { + if (actionsheet.style.display != 'none') { + // Remove any existing actionsheet + jSuites.slideBottom(actionContent, false, function() { + actionsheet.remove(); + actionsheet.style.display = 'none'; + }); + } + } + + var mouseUp = function(e) { + obj.close(); + } + + actionsheet.addEventListener('mouseup', mouseUp); + + obj.options = {}; + + return obj; +})(); + +/** + * (c) jSuites modal + * https://github.com/paulhodel/jsuites + * + * @author: Paul Hodel <paul.hodel@gmail.com> + * @description: Modal + */ + +jSuites.modal = (function(el, options) { + var obj = {}; + obj.options = {}; + + // Default configuration + var defaults = { + url: null, + onopen: null, + onclose: null, + closed: false, + width: null, + height: null, + title: null, + }; + + // Loop through our object + for (var property in defaults) { + if (options && options.hasOwnProperty(property)) { + obj.options[property] = options[property]; + } else { + obj.options[property] = defaults[property]; + } + } + + // Title + if (! obj.options.title && el.getAttribute('title')) { + obj.options.title = el.getAttribute('title'); + } + + var temp = document.createElement('div'); + for (var i = 0; i < el.children.length; i++) { + temp.appendChild(el.children[i]); + } + + obj.content = document.createElement('div'); + obj.content.className = 'jmodal_content'; + obj.content.innerHTML = el.innerHTML; + + for (var i = 0; i < temp.children.length; i++) { + obj.content.appendChild(temp.children[i]); + } + + obj.container = document.createElement('div'); + obj.container.className = 'jmodal'; + obj.container.appendChild(obj.content); + + if (obj.options.width) { + obj.container.style.width = obj.options.width; + } + if (obj.options.height) { + obj.container.style.height = obj.options.height; + } + if (obj.options.title) { + obj.container.setAttribute('title', obj.options.title); + } else { + obj.container.classList.add('no-title'); + } + el.innerHTML = ''; + el.style.display = 'none'; + el.appendChild(obj.container); + + // Backdrop + var backdrop = document.createElement('div'); + backdrop.className = 'jmodal_backdrop'; + el.appendChild(backdrop); + + obj.open = function() { + el.style.display = 'block'; + // Fullscreen + const rect = obj.container.getBoundingClientRect(); + if (jSuites.getWindowWidth() < rect.width) { + obj.container.style.top = ''; + obj.container.style.left = ''; + obj.container.classList.add('jmodal_fullscreen'); + jSuites.slideBottom(obj.container, 1); + } else { + backdrop.style.display = 'block'; + } + // Current + jSuites.modal.current = obj; + // Event + if (typeof(obj.options.onopen) == 'function') { + obj.options.onopen(el, obj); + } + } + + obj.resetPosition = function() { + obj.container.style.top = ''; + obj.container.style.left = ''; + } + + obj.isOpen = function() { + return el.style.display != 'none' ? true : false; + } + + obj.close = function() { + el.style.display = 'none'; + // Backdrop + backdrop.style.display = ''; + // Current + jSuites.modal.current = null; + // Remove fullscreen class + obj.container.classList.remove('jmodal_fullscreen'); + // Event + if (typeof(obj.options.onclose) == 'function') { + obj.options.onclose(el, obj); + } + } + + if (! jSuites.modal.hasEvents) { + jSuites.modal.current = obj; + + if ('ontouchstart' in document.documentElement === true) { + document.addEventListener("touchstart", jSuites.modal.mouseDownControls); + } else { + document.addEventListener('mousedown', jSuites.modal.mouseDownControls); + document.addEventListener('mousemove', jSuites.modal.mouseMoveControls); + document.addEventListener('mouseup', jSuites.modal.mouseUpControls); + } + + document.addEventListener('keydown', jSuites.modal.keyDownControls); + + jSuites.modal.hasEvents = true; + } + + if (obj.options.url) { + jSuites.ajax({ + url: obj.options.url, + method: 'GET', + success: function(data) { + obj.content.innerHTML = data; + + if (! obj.options.closed) { + obj.open(); + } + } + }); + } else { + if (! obj.options.closed) { + obj.open(); + } + } + + // Keep object available from the node + el.modal = obj; + + return obj; +}); + +jSuites.modal.current = null; +jSuites.modal.position = null; + +jSuites.modal.keyDownControls = function(e) { + if (e.which == 27) { + if (jSuites.modal.current) { + jSuites.modal.current.close(); + } + } +} + +jSuites.modal.mouseUpControls = function(e) { + if (jSuites.modal.current) { + jSuites.modal.current.container.style.cursor = 'auto'; + } + jSuites.modal.position = null; +} + +jSuites.modal.mouseMoveControls = function(e) { + if (jSuites.modal.current && jSuites.modal.position) { + if (e.which == 1 || e.which == 3) { + var position = jSuites.modal.position; + jSuites.modal.current.container.style.top = (position[1] + (e.clientY - position[3]) + (position[5] / 2)) + 'px'; + jSuites.modal.current.container.style.left = (position[0] + (e.clientX - position[2]) + (position[4] / 2)) + 'px'; + jSuites.modal.current.container.style.cursor = 'move'; + } else { + jSuites.modal.current.container.style.cursor = 'auto'; + } + } +} + +jSuites.modal.mouseDownControls = function(e) { + jSuites.modal.position = []; + + if (e.target.classList.contains('jmodal')) { + setTimeout(function() { + // Get target info + var rect = e.target.getBoundingClientRect(); + + if (e.changedTouches && e.changedTouches[0]) { + var x = e.changedTouches[0].clientX; + var y = e.changedTouches[0].clientY; + } else { + var x = e.clientX; + var y = e.clientY; + } + + if (rect.width - (x - rect.left) < 50 && (y - rect.top) < 50) { + setTimeout(function() { + jSuites.modal.current.close(); + }, 100); + } else { + if (e.target.getAttribute('title') && (y - rect.top) < 50) { + if (document.selection) { + document.selection.empty(); + } else if ( window.getSelection ) { + window.getSelection().removeAllRanges(); + } + + jSuites.modal.position = [ + rect.left, + rect.top, + e.clientX, + e.clientY, + rect.width, + rect.height, + ]; + } + } + }, 100); + } +} + + +jSuites.notification = (function(options) { + var obj = {}; + obj.options = {}; + + // Default configuration + var defaults = { + icon: null, + name: 'Notification', + date: null, + title: null, + message: null, + timeout: 4000, + autoHide: true, + closeable: true, + }; + + // Loop through our object + for (var property in defaults) { + if (options && options.hasOwnProperty(property)) { + obj.options[property] = options[property]; + } else { + obj.options[property] = defaults[property]; + } + } + + var notification = document.createElement('div'); + notification.className = 'jnotification'; + + var notificationContainer = document.createElement('div'); + notificationContainer.className = 'jnotification-container'; + notification.appendChild(notificationContainer); + + var notificationHeader = document.createElement('div'); + notificationHeader.className = 'jnotification-header'; + notificationContainer.appendChild(notificationHeader); + + var notificationImage = document.createElement('div'); + notificationImage.className = 'jnotification-image'; + notificationHeader.appendChild(notificationImage); + + if (obj.options.icon) { + var notificationIcon = document.createElement('img'); + notificationIcon.src = obj.options.icon; + notificationImage.appendChild(notificationIcon); + } + + var notificationName = document.createElement('div'); + notificationName.className = 'jnotification-name'; + notificationName.innerHTML = obj.options.name; + notificationHeader.appendChild(notificationName); + + if (obj.options.closeable == true) { + var notificationClose = document.createElement('div'); + notificationClose.className = 'jnotification-close'; + notificationClose.onclick = function() { + obj.hide(); + } + notificationHeader.appendChild(notificationClose); + } + + var notificationDate = document.createElement('div'); + notificationDate.className = 'jnotification-date'; + notificationHeader.appendChild(notificationDate); + + var notificationContent = document.createElement('div'); + notificationContent.className = 'jnotification-content'; + notificationContainer.appendChild(notificationContent); + + if (obj.options.title) { + var notificationTitle = document.createElement('div'); + notificationTitle.className = 'jnotification-title'; + notificationTitle.innerHTML = obj.options.title; + notificationContent.appendChild(notificationTitle); + } + + var notificationMessage = document.createElement('div'); + notificationMessage.className = 'jnotification-message'; + notificationMessage.innerHTML = obj.options.message; + notificationContent.appendChild(notificationMessage); + + obj.show = function() { + document.body.appendChild(notification); + if (jSuites.getWindowWidth() > 800) { + jSuites.fadeIn(notification); + } else { + jSuites.slideTop(notification, 1); + } + } + + obj.hide = function() { + if (jSuites.getWindowWidth() > 800) { + jSuites.fadeOut(notification, function() { + if (notification.parentNode) { + notification.parentNode.removeChild(notification); + if (notificationTimeout) { + clearTimeout(notificationTimeout); + } + } + }); + } else { + jSuites.slideTop(notification, 0, function() { + if (notification.parentNode) { + notification.parentNode.removeChild(notification); + if (notificationTimeout) { + clearTimeout(notificationTimeout); + } + } + }); + } + }; + + obj.show(); + + if (obj.options.autoHide == true) { + var notificationTimeout = setTimeout(function() { + obj.hide(); + }, obj.options.timeout); + } + + if (jSuites.getWindowWidth() < 800) { + notification.addEventListener("swipeup", function(e) { + obj.hide(); + e.preventDefault(); + e.stopPropagation(); + }); + } + + return obj; +}); + +jSuites.rating = (function(el, options) { + var obj = {}; + obj.options = {}; + + // Default configuration + var defaults = { + number: 5, + value: 0, + tooltip: [ 'Very bad', 'Bad', 'Average', 'Good', 'Very good' ], + onchange: null, + }; + + // Loop through the initial configuration + for (var property in defaults) { + if (options && options.hasOwnProperty(property)) { + obj.options[property] = options[property]; + } else { + obj.options[property] = defaults[property]; + } + } + + // Class + el.classList.add('jrating'); + + // Add elements + for (var i = 0; i < obj.options.number; i++) { + var div = document.createElement('div'); + div.setAttribute('data-index', (i + 1)) + div.setAttribute('title', obj.options.tooltip[i]) + el.appendChild(div); + } + + // Set value + obj.setValue = function(index) { + for (var i = 0; i < obj.options.number; i++) { + if (i < index) { + el.children[i].classList.add('jrating-selected'); + } else { + el.children[i].classList.remove('jrating-over'); + el.children[i].classList.remove('jrating-selected'); + } + } + + obj.options.value = index; + + if (typeof(obj.options.onchange) == 'function') { + obj.options.onchange(el, index); + } + } + + obj.getValue = function() { + return obj.options.value; + } + + if (obj.options.value) { + for (var i = 0; i < obj.options.number; i++) { + if (i < obj.options.value) { + el.children[i].classList.add('jrating-selected'); + } + } + } + + // Events + el.addEventListener("click", function(e) { + var index = e.target.getAttribute('data-index'); + if (index != undefined) { + if (index == obj.options.value) { + obj.setValue(0); + } else { + obj.setValue(index); + } + } + }); + + el.addEventListener("mouseover", function(e) { + var index = e.target.getAttribute('data-index'); + for (var i = 0; i < obj.options.number; i++) { + if (i < index) { + el.children[i].classList.add('jrating-over'); + } else { + el.children[i].classList.remove('jrating-over'); + } + } + }); + + el.addEventListener("mouseout", function(e) { + for (var i = 0; i < obj.options.number; i++) { + el.children[i].classList.remove('jrating-over'); + } + }); + + el.rating = obj; + + return obj; +}); + + +/** + * (c) Image slider + * https://github.com/paulhodel/jtools + * + * @author: Paul Hodel <paul.hodel@gmail.com> + * @description: Image Slider + */ + +jSuites.slider = (function(el, options) { + var obj = {}; + obj.options = {}; + obj.currentImage = null; + + if (options) { + obj.options = options; + } + + // Items + obj.options.items = []; + + if (! el.classList.contains('jslider')) { + el.classList.add('jslider'); + + // Create container + var container = document.createElement('div'); + container.className = 'jslider-container'; + + // Move children inside + if (el.children.length > 0) { + // Keep children items + for (var i = 0; i < el.children.length; i++) { + obj.options.items.push(el.children[i]); + } + } + if (obj.options.items.length > 0) { + for (var i = 0; i < obj.options.items.length; i++) { + obj.options.items[i].classList.add('jfile'); + var index = obj.options.items[i].src.lastIndexOf('/'); + if (index < 0) { + obj.options.items[i].setAttribute('data-name', obj.options.items[i].src); + } else { + obj.options.items[i].setAttribute('data-name', obj.options.items[i].src.substr(index + 1)); + } + var index = obj.options.items[i].src.lastIndexOf('/'); + + container.appendChild(obj.options.items[i]); + } + } + el.appendChild(container); + // Add close buttom + var close = document.createElement('div'); + close.className = 'jslider-close'; + close.innerHTML = ''; + close.onclick = function() { + obj.close(); + } + el.appendChild(close); + } else { + var container = el.querySelector('slider-container'); + } + + obj.show = function(target) { + if (! target) { + var target = container.children[0]; + } + + if (! container.classList.contains('jslider-preview')) { + container.classList.add('jslider-preview'); + close.style.display = 'block'; + } + + // Hide all images + for (var i = 0; i < container.children.length; i++) { + container.children[i].style.display = 'none'; + } + + // Show clicked only + target.style.display = 'block'; + + // Is there any previous + if (target.previousSibling) { + container.classList.add('jslider-left'); + } else { + container.classList.remove('jslider-left'); + } + + // Is there any next + if (target.nextSibling) { + container.classList.add('jslider-right'); + } else { + container.classList.remove('jslider-right'); + } + + obj.currentImage = target; + } + + obj.open = function() { + obj.show(); + + // Event + if (typeof(obj.options.onopen) == 'function') { + obj.options.onopen(el); + } + } + + obj.close = function() { + container.classList.remove('jslider-preview'); + container.classList.remove('jslider-left'); + container.classList.remove('jslider-right'); + + for (var i = 0; i < container.children.length; i++) { + container.children[i].style.display = ''; + } + + close.style.display = ''; + + obj.currentImage = null; + + // Event + if (typeof(obj.options.onclose) == 'function') { + obj.options.onclose(el); + } + } + + obj.reset = function() { + container.innerHTML = ''; + } + + obj.addFile = function(v, ignoreEvents) { + var img = document.createElement('img'); + img.setAttribute('data-lastmodified', v.lastmodified); + img.setAttribute('data-name', v.name); + img.setAttribute('data-size', v.size); + img.setAttribute('data-extension', v.extension); + img.setAttribute('data-cover', v.cover); + img.setAttribute('src', v.file); + img.className = 'jfile'; + container.appendChild(img); + obj.options.items.push(img); + + // Onchange + if (! ignoreEvents) { + if (typeof(obj.options.onchange) == 'function') { + obj.options.onchange(el, v); + } + } + } + + obj.addFiles = function(files) { + for (var i = 0; i < files.length; i++) { + obj.addFile(files[i]); + } + } + + obj.next = function() { + if (obj.currentImage.nextSibling) { + obj.show(obj.currentImage.nextSibling); + } + } + + obj.prev = function() { + if (obj.currentImage.previousSibling) { + obj.show(obj.currentImage.previousSibling); + } + } + + obj.getData = function() { + return jSuites.getFiles(container); + } + + // Append data + if (obj.options.data && obj.options.data.length) { + for (var i = 0; i < obj.options.data.length; i++) { + if (obj.options.data[i]) { + obj.addFile(obj.options.data[i]); + } + } + } + + // Allow insert + if (obj.options.allowAttachment) { + var attachmentInput = document.createElement('input'); + attachmentInput.type = 'file'; + attachmentInput.className = 'slider-attachment'; + attachmentInput.setAttribute('accept', 'image/*'); + attachmentInput.style.display = 'none'; + attachmentInput.onchange = function() { + var reader = []; + + for (var i = 0; i < this.files.length; i++) { + var type = this.files[i].type.split('/'); + + if (type[0] == 'image') { + var extension = this.files[i].name; + extension = extension.split('.'); + extension = extension[extension.length-1]; + + var file = { + size: this.files[i].size, + name: this.files[i].name, + extension: extension, + cover: 0, + lastmodified: this.files[i].lastModified, + } + + reader[i] = new FileReader(); + reader[i].addEventListener("load", function (e) { + file.file = e.target.result; + obj.addFile(file); + }, false); + + reader[i].readAsDataURL(this.files[i]); + } else { + alert('The extension is not allowed'); + } + }; + } + + var attachmentIcon = document.createElement('i'); + attachmentIcon.innerHTML = 'attachment'; + attachmentIcon.className = 'jslider-attach material-icons'; + attachmentIcon.onclick = function() { + jSuites.click(attachmentInput); + } + + el.appendChild(attachmentInput); + el.appendChild(attachmentIcon); + } + + // Push to refresh + var longTouchTimer = null; + + var mouseDown = function(e) { + if (e.target.tagName == 'IMG') { + // Remove + var targetImage = e.target; + longTouchTimer = setTimeout(function() { + if (e.target.src.substr(0,4) == 'data') { + e.target.remove(); + } else { + if (e.target.classList.contains('jremove')) { + e.target.classList.remove('jremove'); + } else { + e.target.classList.add('jremove'); + } + } + + // Onchange + if (typeof(obj.options.onchange) == 'function') { + obj.options.onchange(el, e.target); + } + }, 1000); + } + } + + var mouseUp = function(e) { + if (longTouchTimer) { + clearTimeout(longTouchTimer); + } + + // Open slider + if (e.target.tagName == 'IMG') { + if (! e.target.classList.contains('jremove')) { + obj.show(e.target); + } + } else { + // Arrow controls + if (e.target.clientWidth - e.offsetX < 40) { + // Show next image + obj.next(); + } else if (e.offsetX < 40) { + // Show previous image + obj.prev(); + } + } + } + + container.addEventListener('mousedown', mouseDown); + container.addEventListener('touchstart', mouseDown); + container.addEventListener('mouseup', mouseUp); + container.addEventListener('touchend', mouseUp); + + // Add global events + el.addEventListener("swipeleft", function(e) { + obj.next(); + e.preventDefault(); + e.stopPropagation(); + }); + + el.addEventListener("swiperight", function(e) { + obj.prev(); + e.preventDefault(); + e.stopPropagation(); + }); + + + el.slider = obj; + + return obj; +}); + +/** + * (c) jTools v1.0.1 - Element sorting + * https://github.com/paulhodel/jtools + * + * @author: Paul Hodel <paul.hodel@gmail.com> + * @description: Element drag and drop sorting + */ + +jSuites.sorting = (function(el, options) { + el.classList.add('jsorting'); + + el.addEventListener('dragstart', function(e) { + e.target.classList.add('jsorting_dragging'); + }); + + el.addEventListener('dragover', function(e) { + e.preventDefault(); + + if (getElement(e.target) && ! e.target.classList.contains('jsorting')) { + if (e.target.clientHeight / 2 > e.offsetY) { + e.path[0].style.borderTop = '1px dotted red'; + e.path[0].style.borderBottom = ''; + } else { + e.path[0].style.borderTop = ''; + e.path[0].style.borderBottom = '1px dotted red'; + } + } + }); + + el.addEventListener('dragleave', function(e) { + e.path[0].style.borderTop = ''; + e.path[0].style.borderBottom = ''; + }); + + el.addEventListener('dragend', function(e) { + e.path[1].querySelector('.jsorting_dragging').classList.remove('jsorting_dragging'); + }); + + el.addEventListener('drop', function(e) { + e.preventDefault(); + + if (getElement(e.target) && ! e.target.classList.contains('jsorting')) { + var element = e.path[1].querySelector('.jsorting_dragging'); + + if (e.target.clientHeight / 2 > e.offsetY) { + e.path[1].insertBefore(element, e.path[0]); + } else { + e.path[1].insertBefore(element, e.path[0].nextSibling); + } + } + + e.path[0].style.borderTop = ''; + e.path[0].style.borderBottom = ''; + }); + + var getElement = function(element) { + var sorting = false; + + function path (element) { + if (element.className) { + if (element.classList.contains('jsorting')) { + sorting = true; + } + } + + if (! sorting) { + path(element.parentNode); + } + } + + path(element); + + return sorting; + } + + for (var i = 0; i < el.children.length; i++) { + el.children[i].setAttribute('draggable', 'true'); + }; + + return el; +}); + +jSuites.tabs = (function(el, options) { + var obj = {}; + obj.options = {}; + + // Default configuration + var defaults = { + data: null, + allowCreate: false, + onload: null, + onchange: null, + oncreate: null, + animation: false, + create: null, + autoName: false, + prefixName: '', + hideHeaders: false, + }; + + // Loop through the initial configuration + for (var property in defaults) { + if (options && options.hasOwnProperty(property)) { + obj.options[property] = options[property]; + } else { + obj.options[property] = defaults[property]; + } + } + + // Class + el.classList.add('jtabs'); + + if (obj.options.animation == true) { + // Border + var border = document.createElement('div'); + border.className = 'jtabs-border'; + el.appendChild(border); + + var setBorder = function(index) { + var rect = headers.children[index].getBoundingClientRect(); + var rectContent = content.children[index].getBoundingClientRect(); + border.style.width = rect.width + 'px'; + border.style.left = (rect.left - rectContent.left) + 'px'; + border.style.top = rect.height + 'px'; + } + } + + // Set value + obj.open = function(index) { + for (var i = 0; i < headers.children.length; i++) { + headers.children[i].classList.remove('jtabs-selected'); + if (content.children[i]) { + content.children[i].classList.remove('jtabs-selected'); + } + } + + headers.children[index].classList.add('jtabs-selected'); + if (content.children[index]) { + content.children[index].classList.add('jtabs-selected'); + } + + // Hide + if (obj.options.hideHeaders == true && (headers.children.length < 2 && obj.options.allowCreate == false)) { + headers.style.display = 'none'; + } else { + headers.style.display = ''; + // Set border + if (obj.options.animation == true) { + setTimeout(function() { + setBorder(index); + }, 100); + } + } + } + + obj.selectIndex = function(a) { + var index = Array.prototype.indexOf.call(headers.children, a); + if (index >= 0) { + obj.open(index); + } + } + + obj.create = function(title, div) { + if (typeof(obj.options.create) == 'function') { + obj.options.create(); + } else { + obj.appendElement(title, div); + + if (typeof(obj.options.oncreate) == 'function') { + obj.options.oncreate(el, div) + } + } + } + + obj.appendElement = function(title, div) { + if (! title) { + if (obj.options.autoName == true) { + var title = obj.options.prefixName; + title += ' ' + (parseInt(content.children.length) + 1); + } else { + var title = prompt('Title?', ''); + } + } + + if (title) { + // Add headers + var header = document.createElement('div'); + header.innerHTML = title; + if (obj.options.allowCreate) { + headers.insertBefore(header, headers.lastChild); + } else { + headers.appendChild(header); + } + + // Add content + if (! div) { + var div = document.createElement('div'); + } + content.appendChild(div); + + // Open new tab + obj.selectIndex(header); + } + } + + // Create from data + if (obj.options.data) { + // Make sure the component is blank + el.innerHTML = ''; + var headers = document.createElement('div'); + var content = document.createElement('div'); + headers.classList.add('jtabs-headers'); + content.classList.add('jtabs-content'); + el.appendChild(headers); + el.appendChild(content); + + for (var i = 0; i < obj.options.data.length; i++) { + var headersItem = document.createElement('div'); + headers.appendChild(headersItem); + var contentItem = document.createElement('div'); + content.appendChild(contentItem); + + headersItem.innerHTML = obj.options.data[i].title; + if (obj.options.data[i].content) { + contentItem.innerHTML = obj.options.data[i].content; + } else if (obj.options.data[i].url) { + jSuites.ajax({ + url: obj.options.data[i].url, + type: 'GET', + success: function(result) { + contentItem.innerHTML = result; + }, + complete: function() { + if (typeof(obj.options.onload) == 'function') { + obj.options.onload(el); + + obj.open(0); + } + } + }); + } + } + } else if (el.children[0] && el.children[1]) { + // Create from existing elements + var headers = el.children[0]; + var content = el.children[1]; + headers.classList.add('jtabs-headers'); + content.classList.add('jtabs-content'); + } else { + el.innerHTML = ''; + var headers = document.createElement('div'); + var content = document.createElement('div'); + headers.classList.add('jtabs-headers'); + content.classList.add('jtabs-content'); + el.appendChild(headers); + el.appendChild(content); + } + + // New + if (obj.options.allowCreate == true) { + var add = document.createElement('i'); + add.className = 'jtabs-add'; + headers.appendChild(add); + } + + // Events + headers.addEventListener("click", function(e) { + if (e.target.tagName == 'DIV') { + obj.selectIndex(e.target); + } else { + obj.create(); + } + }); + + if (headers.children.length) { + obj.open(0); + } + + el.tabs = obj; + + return obj; +}); + +jSuites.tags = (function(el, options) { + var obj = {}; + obj.options = {}; + + /** + * @typedef {Object} defaults + * @property {(string|Array)} value - Initial value of the compontent + * @property {number} limit - Max number of tags inside the element + * @property {string} search - The URL for suggestions + * @property {string} placeholder - The default instruction text on the element + * @property {validation} validation - Method to validate the tags + * @property {requestCallback} onbeforechange - Method to be execute before any changes on the element + * @property {requestCallback} onchange - Method to be execute after any changes on the element + * @property {requestCallback} onfocus - Method to be execute when on focus + * @property {requestCallback} onblur - Method to be execute when on blur + * @property {requestCallback} onload - Method to be execute when the element is loaded + */ + var defaults = { + value: null, + limit: null, + limitMessage: 'The limit of entries is: ', + search: null, + placeholder: null, + validation: null, + onbeforechange: null, + onchange: null, + onfocus: null, + onblur: null, + onload: null, + colors: null, + }; + + // Loop through though the default configuration + for (var property in defaults) { + if (options && options.hasOwnProperty(property)) { + obj.options[property] = options[property]; + } else { + obj.options[property] = defaults[property]; + } + } + + // Search helpers + var searchContainer = null; + var searchTerms = null; + var searchIndex = 0; + var searchTimer = 0; + + /** + * Add a new tag to the element + * @param {(?string|Array)} value - The value of the new element + */ + obj.add = function(value, focus) { + if (typeof(obj.options.onbeforechange) == 'function') { + var v = obj.options.onbeforechange(el, obj, value); + if (v != null) { + value = v; + } + } + + // Close search + if (searchContainer) { + searchContainer.style.display = ''; + } + + if (obj.options.limit > 0 && el.children.length >= obj.options.limit) { + alert(obj.options.limitMessage + ' ' + obj.options.limit); + } else { + // Get node + var node = getSelectionStart(); + + // Mix argument string or array + if (! value || typeof(value) == 'string') { + var div = document.createElement('div'); + div.innerHTML = value ? value : '<br>'; + if (node && node.parentNode.classList.contains('jtags')) { + el.insertBefore(div, node.nextSibling); + } else { + el.appendChild(div); + } + } else { + if (node && node.parentNode.classList.contains('jtags')) { + if (! node.innerText.replace("\n", "")) { + el.removeChild(node); + } + } + + for (var i = 0; i <= value.length; i++) { + if (! obj.options.limit || el.children.length < obj.options.limit) { + var div = document.createElement('div'); + div.innerHTML = value[i] ? value[i] : '<br>'; + el.appendChild(div); + } + } + } + + // Place caret + if (focus) { + setTimeout(function() { + caret(div); + }, 0); + } + + // Filter + filter(); + + if (typeof(obj.options.onchange) == 'function') { + obj.options.onchange(el, obj, value ? value : ''); + } + } + } + + obj.remove = function(node) { + // Remove node + node.parentNode.removeChild(node); + if (! el.children.length) { + obj.add(''); + } + } + + /** + * Get all tags in the element + * @return {Array} data - All tags as an array + */ + obj.getData = function() { + var data = []; + for (var i = 0; i < el.children.length; i++) { + var value = obj.getValue(i); + if (value) { + var id = el.children[i].getAttribute('data-id'); + if (! id) { + id = value; + } + data.push({ id: id, value: value }); + } + } + return data; + } + + /** + * Get the value of one tag. Null for all tags + * @param {?number} index - Tag index number. Null for all tags. + * @return {string} value - All tags separated by comma + */ + obj.getValue = function(index) { + var value = null; + + if (index != null) { + // Get one individual value + value = el.children[index].innerText.replace("\n", ""); + } else { + // Get all + var data = []; + for (var i = 0; i < el.children.length; i++) { + value = el.children[i].innerText.replace("\n", ""); + if (value) { + data.push(obj.getValue(i)); + } + } + value = data.join(','); + } + + return value; + } + + /** + * Set the value of the element based on a string separeted by (,|;|\r\n) + * @param {string} value - A string with the tags + */ + obj.setValue = function(text) { + // Remove whitespaces + text = text.trim(); + + if (text) { + // Tags + var data = extractTags(text); + // Add tags to the element + obj.add(data); + } + } + + obj.reset = function() { + el.innerHTML = '<div><br></div>'; + } + + /** + * Verify if all tags in the element are valid + * @return {boolean} + */ + obj.isValid = function() { + var test = 0; + for (var i = 0; i < el.children.length; i++) { + if (el.children[i].classList.contains('jtags_error')) { + test++; + } + } + return test == 0 ? true : false; + } + + /** + * Add one element from the suggestions to the element + * @param {object} item - Node element in the suggestions container + */ + obj.selectIndex = function(item) { + // Reset terms + searchTerms = ''; + var node = getSelectionStart(); + // Append text to the caret + node.innerText = item.children[1].innerText; + // Set node id + if (item.children[1].getAttribute('data-id')) { + node.setAttribute('data-id', item.children[1].getAttribute('data-id')); + } + // Close container + if (searchContainer) { + searchContainer.style.display = ''; + searchContainer.innerHTML = ''; + } + // Remove any error + node.classList.remove('jtags_error'); + // Add new item + obj.add(); + } + + /** + * Search for suggestions + * @param {object} node - Target node for any suggestions + */ + obj.search = function(node) { + // Create and append search container to the DOM + if (! searchContainer) { + var div = document.createElement('div'); + div.style.position = 'relative'; + el.parentNode.insertBefore(div, el.nextSibling); + + // Create container + searchContainer = document.createElement('div'); + searchContainer.classList.add('jtags_search'); + div.appendChild(searchContainer); + } + + // Search for + var terms = node.anchorNode.nodeValue; + + // Search + if (node.anchorNode.nodeValue && terms != searchTerms) { + // Terms + searchTerms = node.anchorNode.nodeValue; + // Reset index + searchIndex = 0; + // Get remove results + jSuites.ajax({ + url: obj.options.search + searchTerms, + method: 'GET', + dataType: 'json', + success: function(data) { + // Reset container + searchContainer.innerHTML = ''; + + // Print results + if (! data.length) { + // Show container + searchContainer.style.display = ''; + } else { + // Show container + searchContainer.style.display = 'block'; + + // Show items + var len = data.length < 11 ? data.length : 10; + for (var i = 0; i < len; i++) { + var div = document.createElement('div'); + if (i == 0) { + div.classList.add('selected'); + } + var img = document.createElement('img'); + if (data[i].image) { + img.src = data[i].image; + } else { + img.style.display = 'none'; + } + div.appendChild(img); + + var item = document.createElement('div'); + item.setAttribute('data-id', data[i].id); + item.innerHTML = data[i].name; + div.onclick = function() { + // Add item + obj.selectIndex(this); + } + div.appendChild(item); + // Append item to the container + searchContainer.appendChild(div); + } + } + } + }); + } + } + + // Destroy tags element + obj.destroy = function() { + // Bind events + el.removeEventListener('mouseup', tagsMouseUp); + el.removeEventListener('keydown', tagsKeyDown); + el.removeEventListener('keyup', tagsKeyUp); + el.removeEventListener('paste', tagsPaste); + el.removeEventListener('focus', tagsFocus); + el.removeEventListener('blur', tagsBlur); + // Remove element + el.parentNode.removeChild(el); + } + + var getRandomColor = function(index) { + var rand = function(min, max) { + return min + Math.random() * (max - min); + } + return 'hsl(' + rand(1, 360) + ',' + rand(40, 70) + '%,' + rand(65, 72) + '%)'; + } + + /** + * Filter tags + */ + var filter = function() { + for (var i = 0; i < el.children.length; i++) { + // Create label design + if (! obj.getValue(i)) { + el.children[i].classList.remove('jtags_label'); + } else { + el.children[i].classList.add('jtags_label'); + + // Validation in place + if (typeof(obj.options.validation) == 'function') { + if (obj.getValue(i)) { + if (! obj.options.validation(el.children[i], el.children[i].innerText, el.children[i].getAttribute('data-id'))) { + el.children[i].classList.add('jtags_error'); + } else { + el.children[i].classList.remove('jtags_error'); + } + } else { + el.children[i].classList.remove('jtags_error'); + } + } + } + } + } + + /** + * Place caret in the element node + */ + var caret = function(e) { + var range = document.createRange(); + var sel = window.getSelection(); + range.setStart(e, e.innerText.length); + range.collapse(true); + sel.removeAllRanges(); + sel.addRange(range); + } + + /** + * Selection + */ + var getSelectionStart = function() { + var node = document.getSelection().anchorNode; + if (node) { + return (node.nodeType == 3 ? node.parentNode : node); + } else { + return null; + } + } + + /** + * Extract tags from a string + * @param {string} text - Raw string + * @return {Array} data - Array with extracted tags + */ + var extractTags = function(text) { + /** @type {Array} */ + var data = []; + + /** @type {string} */ + var word = ''; + + // Remove whitespaces + text = text.trim(); + + if (text) { + for (var i = 0; i < text.length; i++) { + if (text[i] == ',' || text[i] == ';' || text[i] == '\r\n') { + if (word) { + data.push(word); + word = ''; + } + } else { + word += text[i]; + } + } + + if (word) { + data.push(word); + } + } + + return data; + } + + /** @type {number} */ + var anchorOffset = 0; + + /** + * Processing event keydown on the element + * @param e {object} + */ + var tagsKeyDown = function(e) { + // Anchoroffset + anchorOffset = window.getSelection().anchorOffset; + + // Verify content + if (! el.children.length) { + var div = document.createElement('div'); + div.innerHTML = '<br>'; + el.appendChild(div); + } + // Comma + if (e.which == 9 || e.which == 186 || e.which == 188) { + var n = window.getSelection().anchorOffset; + if (n > 1) { + if (! obj.options.limit || el.children.length < obj.options.limit) { + obj.add('', true); + } + } + e.preventDefault(); + } else if (e.which == 13) { + // Enter + if (searchContainer && searchContainer.style.display != '') { + obj.selectIndex(searchContainer.children[searchIndex]); + } else { + var n = window.getSelection().anchorOffset; + if (n > 1) { + if (! obj.options.limit || el.children.length < obj.options.limit) { + obj.add('', true); + } + } + } + e.preventDefault(); + } else if (e.which == 38) { + // Up + if (searchContainer && searchContainer.style.display != '') { + searchContainer.children[searchIndex].classList.remove('selected'); + if (searchIndex > 0) { + searchIndex--; + } + searchContainer.children[searchIndex].classList.add('selected'); + e.preventDefault(); + } + } else if (e.which == 40) { + // Down + if (searchContainer && searchContainer.style.display != '') { + searchContainer.children[searchIndex].classList.remove('selected'); + if (searchIndex < 9) { + searchIndex++; + } + searchContainer.children[searchIndex].classList.add('selected'); + e.preventDefault(); + } + } + } + + /** + * Processing event keyup on the element + * @param e {object} + */ + var tagsKeyUp = function(e) { + if (e.which == 39) { + var n = window.getSelection().anchorOffset; + if (n > 1 && n == anchorOffset) { + obj.add(); + } + } else if (e.which == 13 || e.which == 38 || e.which == 40) { + e.preventDefault(); + } else { + if (searchTimer) { + clearTimeout(searchTimer); + } + + searchTimer = setTimeout(function() { + // Current node + var node = window.getSelection(); + // Search + if (obj.options.search) { + obj.search(node); + } + searchTimer = null; + }, 300); + } + + filter(); + } + + /** + * Processing event paste on the element + * @param e {object} + */ + var tagsPaste = function(e) { + if (e.clipboardData || e.originalEvent.clipboardData) { + var html = (e.originalEvent || e).clipboardData.getData('text/html'); + var text = (e.originalEvent || e).clipboardData.getData('text/plain'); + } else if (window.clipboardData) { + var html = window.clipboardData.getData('Html'); + var text = window.clipboardData.getData('Text'); + } + + obj.setValue(text); + e.preventDefault(); + } + + /** + * Processing event mouseup on the element + * @param e {object} + */ + var tagsMouseUp = function(e) { + if (e.target.parentNode && e.target.parentNode.classList.contains('jtags')) { + if (e.target.classList.contains('jtags_label') || e.target.classList.contains('jtags_error')) { + const rect = e.target.getBoundingClientRect(); + if (rect.width - (e.clientX - rect.left) < 16) { + obj.remove(e.target); + } + } + } + + if (searchContainer) { + searchContainer.style.display = ''; + } + } + + /** + * Processing event focus on the element + * @param e {object} + */ + var tagsFocus = function(e) { + if (! el.children.length || obj.getValue(el.children.length - 1)) { + if (! obj.options.limit || el.children.length < obj.options.limit) { + var div = document.createElement('div'); + div.innerHTML = '<br>'; + el.appendChild(div); + } + } + + if (typeof(obj.options.onfocus) == 'function') { + obj.options.onfocus(el, obj, obj.getValue()); + } + } + + /** + * Processing event blur on the element + * @param e {object} + */ + var tagsBlur = function(e) { + if (searchContainer) { + setTimeout(function() { + searchContainer.style.display = ''; + }, 200); + } + + for (var i = 0; i < el.children.length - 1; i++) { + // Create label design + if (! obj.getValue(i)) { + el.removeChild(el.children[i]); + } + } + + if (typeof(obj.options.onblur) == 'function') { + obj.options.onblur(el, obj, obj.getValue()); + } + } + + // Bind events + el.addEventListener('mouseup', tagsMouseUp); + el.addEventListener('keydown', tagsKeyDown); + el.addEventListener('keyup', tagsKeyUp); + el.addEventListener('paste', tagsPaste); + el.addEventListener('focus', tagsFocus); + el.addEventListener('blur', tagsBlur); + + // Prepare container + el.classList.add('jtags'); + el.setAttribute('contenteditable', true); + el.setAttribute('spellcheck', false); + + if (obj.options.placeholder) { + el.placeholder = obj.options.placeholder; + } + + // Make sure element is empty + if (obj.options.value) { + obj.setValue(obj.options.value); + } else { + el.innerHTML = '<div><br></div>'; + } + + if (typeof(obj.options.onload) == 'function') { + obj.options.onload(el, obj); + } + + el.tags = obj; + + return obj; +}); + +/** + * (c) jSuites template renderer + * https://github.com/paulhodel/jsuites + * + * @author: Paul Hodel <paul.hodel@gmail.com> + * @description: Template renderer + */ + +jSuites.template = (function(el, options) { + var obj = {}; + obj.options = {}; + + // Default configuration + var defaults = { + url: null, + data: null, + filter: null, + pageNumber: 0, + numberOfPages: 0, + template: null, + render: null, + onload: null, + onchange: null, + noRecordsFound: 'No records found', + // Searchable + search: null, + searchInput: true, + // Search happens in the back end + searchValue: '', + remoteData: null, + // Pagination page number of items + pagination: null, + } + + // Loop through our object + for (var property in defaults) { + if (options && options.hasOwnProperty(property)) { + obj.options[property] = options[property]; + } else { + obj.options[property] = defaults[property]; + } + } + + // Reset content + el.innerHTML = ''; + + // Input search + if (obj.options.search && obj.options.searchInput == true) { + // Timer + var searchTimer = null; + + // Search container + var searchContainer = document.createElement('div'); + searchContainer.className = 'jtemplate-results'; + var searchInput = document.createElement('input'); + searchInput.value = obj.options.searchValue; + searchInput.onkeyup = function(e) { + // Clear current trigger + if (searchTimer) { + clearTimeout(searchTimer); + } + // Prepare search + searchTimer = setTimeout(function() { + obj.search(searchInput.value.toLowerCase()); + searchTimer = null; + }, 300) + } + searchContainer.appendChild(searchInput); + el.appendChild(searchContainer); + } + + // Pagination container + if (obj.options.pagination) { + var pagination = document.createElement('div'); + pagination.className = 'jtemplate-pagination'; + el.appendChild(pagination); + } + + // Content + var container = document.createElement('div'); + container.className = 'jtemplate-content'; + el.appendChild(container); + + // Data container + var searchResults = null; + + obj.updatePagination = function() { + // Reset pagination container + if (pagination) { + pagination.innerHTML = ''; + } + + // Create pagination + if (obj.options.pagination > 0 && obj.options.numberOfPages > 1) { + // Number of pages + var numberOfPages = obj.options.numberOfPages; + + // Controllers + if (obj.options.pageNumber < 6) { + var startNumber = 1; + var finalNumber = numberOfPages < 10 ? numberOfPages : 10; + } else if (numberOfPages - obj.options.pageNumber < 5) { + var startNumber = numberOfPages - 9; + var finalNumber = numberOfPages; + if (startNumber < 1) { + startNumber = 1; + } + } else { + var startNumber = obj.options.pageNumber - 4; + var finalNumber = obj.options.pageNumber + 5; + } + + // First + if (startNumber > 1) { + var paginationItem = document.createElement('div'); + paginationItem.innerHTML = '<'; + paginationItem.title = 1; + pagination.appendChild(paginationItem); + } + + // Get page links + for (var i = startNumber; i <= finalNumber; i++) { + var paginationItem = document.createElement('div'); + paginationItem.innerHTML = i; + pagination.appendChild(paginationItem); + + if (obj.options.pageNumber == i - 1) { + paginationItem.style.fontWeight = 'bold'; + } + } + + // Last + if (finalNumber < numberOfPages) { + var paginationItem = document.createElement('div'); + paginationItem.innerHTML = '>'; + paginationItem.title = numberOfPages - 1; + pagination.appendChild(paginationItem); + } + } + } + + obj.addItem = function(data, beginOfDataSet) { + // Append itens + var content = document.createElement('div'); + // Append data + if (beginOfDataSet) { + obj.options.data.unshift(data); + } else { + obj.options.data.push(data); + } + // Get content + content.innerHTML = obj.options.template[Object.keys(options.template)[0]](data); + // Add animation + content.children[0].classList.add('fade-in'); + // Add and do the animation + if (beginOfDataSet) { + container.prepend(content.children[0]); + } else { + container.append(content.children[0]); + } + // Onchange method + if (typeof(obj.options.onchange) == 'function') { + obj.options.onchange(el, obj.options.data); + } + } + + obj.removeItem = function(element) { + if (Array.prototype.indexOf.call(container.children, element) > -1) { + // Remove data from array + var index = obj.options.data.indexOf(element.dataReference); + if (index > -1) { + obj.options.data.splice(index, 1); + } + // Remove element from DOM + jSuites.fadeOut(element, function() { + element.parentNode.removeChild(element); + }); + } else { + console.error('Element not found'); + } + } + + obj.setData = function(data) { + if (data) { + obj.options.pageNumber = 1; + obj.options.searchValue = ''; + // Set data + obj.options.data = data; + // Reset any search results + searchResults = null; + + // Render new data + obj.render(); + + // Onchange method + if (typeof(obj.options.onchange) == 'function') { + obj.options.onchange(el, obj.options.data); + } + } + } + + obj.appendData = function(data, pageNumber) { + if (pageNumber) { + obj.options.pageNumber = pageNumber; + } + + var execute = function(data) { + // Concat data + obj.options.data.concat(data); + // Number of pages + if (obj.options.pagination > 0) { + obj.options.numberOfPages = Math.ceil(obj.options.data.length / obj.options.pagination); + } + var startNumber = 0; + var finalNumber = data.length; + // Append itens + var content = document.createElement('div'); + for (var i = startNumber; i < finalNumber; i++) { + content.innerHTML = obj.options.template[Object.keys(obj.options.template)[0]](data[i]); + content.children[0].dataReference = data[i]; + container.appendChild(content.children[0]); + } + } + + if (obj.options.url && obj.options.remoteData == true) { + // URL + var url = obj.options.url; + // Data + var ajaxData = {}; + // Options for backend search + if (obj.options.remoteData) { + // Search value + if (obj.options.searchValue) { + ajaxData.q = obj.options.searchValue; + } + // Page number + if (obj.options.pageNumber) { + ajaxData.p = obj.options.pageNumber; + } + // Number items per page + if (obj.options.pagination) { + ajaxData.t = obj.options.pagination; + } + } + // Remote loading + jSuites.ajax({ + url: url, + method: 'GET', + data: ajaxData, + dataType: 'json', + success: function(data) { + execute(data); + } + }); + } else { + if (! obj.options.data) { + console.log('TEMPLATE: no data or external url defined'); + } else { + execute(data); + } + } + } + + obj.renderTemplate = function() { + // Data container + var data = searchResults ? searchResults : obj.options.data; + + // Reset pagination + obj.updatePagination(); + + if (! data.length) { + container.innerHTML = obj.options.noRecordsFound; + container.classList.add('jtemplate-empty'); + } else { + // Reset content + container.classList.remove('jtemplate-empty'); + + // Create pagination + if (obj.options.pagination && data.length > obj.options.pagination) { + var startNumber = (obj.options.pagination * obj.options.pageNumber); + var finalNumber = (obj.options.pagination * obj.options.pageNumber) + obj.options.pagination; + + if (data.length < finalNumber) { + var finalNumber = data.length; + } + } else { + var startNumber = 0; + var finalNumber = data.length; + } + + // Append itens + var content = document.createElement('div'); + for (var i = startNumber; i < finalNumber; i++) { + content.innerHTML = obj.options.template[Object.keys(obj.options.template)[0]](data[i]); + content.children[0].dataReference = data[i]; + container.appendChild(content.children[0]); + } + } + } + + obj.render = function(pageNumber, forceLoad) { + // Update page number + if (pageNumber != undefined) { + obj.options.pageNumber = pageNumber; + } else { + if (! obj.options.pageNumber && obj.options.pagination > 0) { + obj.options.pageNumber = 0; + } + } + + // Render data into template + var execute = function() { + // Render new content + if (typeof(obj.options.render) == 'function') { + container.innerHTML = obj.options.render(obj); + } else { + container.innerHTML = ''; + } + + // Load data + obj.renderTemplate(); + + // Onload + if (forceLoad && typeof(obj.options.onload) == 'function') { + obj.options.onload(el, obj); + } + } + + if (obj.options.url && (obj.options.remoteData == true || forceLoad)) { + // URL + var url = obj.options.url; + // Data + var ajaxData = {}; + // Options for backend search + if (obj.options.remoteData) { + // Search value + if (obj.options.searchValue) { + ajaxData.q = obj.options.searchValue; + } + // Page number + if (obj.options.pageNumber) { + ajaxData.p = obj.options.pageNumber; + } + // Number items per page + if (obj.options.pagination) { + ajaxData.t = obj.options.pagination; + } + } + // Remote loading + jSuites.ajax({ + url: url, + method: 'GET', + dataType: 'json', + data: ajaxData, + success: function(data) { + // Search and keep data in the client side + if (data.hasOwnProperty("total")) { + obj.options.numberOfPages = Math.ceil(data.total / obj.options.pagination); + obj.options.data = data.data; + } else { + // Number of pages + if (obj.options.pagination > 0) { + obj.options.numberOfPages = Math.ceil(data.length / obj.options.pagination); + } + obj.options.data = data; + } + + // Load data for the user + execute(); + } + }); + } else { + if (! obj.options.data) { + console.log('TEMPLATE: no data or external url defined'); + } else { + // Number of pages + if (obj.options.pagination > 0) { + if (searchResults) { + obj.options.numberOfPages = Math.ceil(searchResults.length / obj.options.pagination); + } else { + obj.options.numberOfPages = Math.ceil(obj.options.data.length / obj.options.pagination); + } + } + // Load data for the user + execute(); + } + } + } + + obj.search = function(query) { + obj.options.pageNumber = 0; + obj.options.searchValue = query ? query : ''; + + // Filter data + if (obj.options.remoteData == true || ! query) { + searchResults = null; + } else { + var test = function(o, query) { + for (var key in o) { + var value = o[key]; + + if ((''+value).toLowerCase().search(query) >= 0) { + return true; + } + } + return false; + } + + if (typeof(obj.options.filter) == 'function') { + searchResults = obj.options.filter(obj.options.data, query); + } else { + searchResults = obj.options.data.filter(function(item) { + return test(item, query); + }); + } + } + + obj.render(); + } + + obj.refresh = function() { + obj.render(); + } + + obj.reload = function() { + obj.render(0, true); + } + + el.addEventListener('mousedown', function(e) { + if (e.target.parentNode.classList.contains('jtemplate-pagination')) { + var index = e.target.innerText; + if (index == '<') { + obj.render(0); + } else if (index == '>') { + obj.render(e.target.getAttribute('title')); + } else { + obj.render(parseInt(index)-1); + } + e.preventDefault(); + } + }); + + el.template = obj; + + // Render data + obj.render(0, true); + + return obj; +}); + +jSuites.tracker = (function(el, options) { + var obj = {}; + obj.options = {}; + + // Default configuration + var defaults = { + url: null, + message: 'Are you sure? There are unsaved information in your form', + ignore: false, + currentHash: null, + submitButton:null, + onload: null, + onbeforesave: null, + onsave: null, + }; + + // Loop through our object + for (var property in defaults) { + if (options && options.hasOwnProperty(property)) { + obj.options[property] = options[property]; + } else { + obj.options[property] = defaults[property]; + } + } + + obj.setUrl = function(url) { + obj.options.url = url; + } + + obj.load = function() { + jSuites.ajax({ + url: obj.options.url, + method: 'GET', + dataType: 'json', + success: function(data) { + var elements = el.querySelectorAll("input, select, textarea"); + + for (var i = 0; i < elements.length; i++) { + var name = elements[i].getAttribute('name'); + if (data[name]) { + elements[i].value = data[name]; + } + } + + if (typeof(obj.options.onload) == 'function') { + obj.options.onload(el, data); + } + } + }); + } + + obj.save = function() { + var test = obj.validate(); + + if (test) { + jSuites.alert(test); + } else { + var data = obj.getElements(true); + + if (typeof(obj.options.onbeforesave) == 'function') { + var data = obj.options.onbeforesave(el, data); + + if (data === false) { + console.log('Onbeforesave returned false'); + return; + } + } + + jSuites.ajax({ + url: obj.options.url, + method: 'POST', + dataType: 'json', + data: data, + success: function(result) { + jSuites.alert(result.message); + + if (typeof(obj.options.onsave) == 'function') { + var data = obj.options.onsave(el, result); + } + + obj.reset(); + } + }); + } + } + + obj.validateElement = function(element) { + var emailChecker = function(data) { + var pattern = new RegExp(/^([\w-\.]+@([\w-]+\.)+[\w-]{2,4})?$/); + return pattern.test(data) ? true : false; + } + + var passwordChecker = function(data) { + return (data.length > 5) ? true : false; + } + + var addError = function(element) { + // Add error in the element + element.classList.add('error'); + // Submit button + if (obj.options.submitButton) { + obj.options.submitButton.setAttribute('disabled', true); + } + // Return error message + return element.getAttribute('data-error') || 'There is an error in the form'; + } + + var delError = function(element) { + var error = false; + // Remove class from this element + element.classList.remove('error'); + // Get elements in the form + var elements = el.querySelectorAll("input, select, textarea"); + // Run all elements + for (var i = 0; i < elements.length; i++) { + if (elements[i].getAttribute('data-validation')) { + if (elements[i].classList.contains('error')) { + error = true; + } + } + } + + if (obj.options.submitButton) { + if (error) { + obj.options.submitButton.setAttribute('disabled', true); + } else { + obj.options.submitButton.removeAttribute('disabled'); + } + } + } + + // Blank + var test = ''; + if (! element.value) { + test = addError(element); + } else if (element.getAttribute('data-email') && ! emailChecker(element.value)) { + test = addError(element); + } else if (element.getAttribute('data-password') && ! emailChecker(element.value)) { + test = addError(element); + } else { + if (element.classList.contains('error')) { + delError(element); + } + } + + return test; + } + + // Run form validation + obj.validate = function() { + var test = ''; + // Get elements in the form + var elements = el.querySelectorAll("input, select, textarea"); + // Run all elements + for (var i = 0; i < elements.length; i++) { + if (elements[i].getAttribute('data-validation')) { + if (test) { + test += "<br>\r\n"; + } + test += obj.validateElement(elements[i]); + } + } + return test; + } + + // Check the form + obj.getError = function() { + // Validation + return obj.validation() ? true : false; + } + + // Return the form hash + obj.setHash = function() { + return obj.getHash(obj.getElements()); + } + + // Get the form hash + obj.getHash = function(str) { + var hash = 0, i, chr; + + if (str.length === 0) { + return hash; + } else { + for (i = 0; i < str.length; i++) { + chr = str.charCodeAt(i); + hash = ((hash << 5) - hash) + chr; + hash |= 0; + } + } + + return hash; + } + + // Is there any change in the form since start tracking? + obj.isChanged = function() { + var hash = obj.setHash(); + return (obj.options.currentHash != hash); + } + + // Restart tracking + obj.resetTracker = function() { + obj.options.currentHash = obj.setHash(); + obj.options.ignore = false; + } + + obj.reset = function() { + obj.options.currentHash = obj.setHash(); + obj.options.ignore = false; + } + + // Ignore flag + obj.setIgnore = function(ignoreFlag) { + obj.options.ignore = ignoreFlag ? true : false; + } + + // Get form elements + obj.getElements = function(asArray) { + var data = {}; + var elements = el.querySelectorAll("input, select, textarea"); + + for (var i = 0; i < elements.length; i++) { + var element = elements[i]; + var name = element.name; + var value = element.value; + + if (name) { + data[name] = value; + } + } + + return asArray == true ? data : JSON.stringify(data); + } + + // Start tracking in one second + setTimeout(function() { + obj.options.currentHash = obj.setHash(); + }, 1000); + + // Alert + window.addEventListener("beforeunload", function (e) { + if (obj.isChanged() && obj.options.ignore == false) { + var confirmationMessage = obj.options.message? obj.options.message : "\o/"; + + if (confirmationMessage) { + if (typeof e == 'undefined') { + e = window.event; + } + + if (e) { + e.returnValue = confirmationMessage; + } + + return confirmationMessage; + } else { + return void(0); + } + } + }); + + // Validations + el.addEventListener("keyup", function(e) { + if (e.target.getAttribute('data-validation')) { + obj.validateElement(e.target); + } + }); + + el.tracker = obj; + + return obj; +}); + + + + return jSuites; + +}))); \ No newline at end of file diff --git a/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jsuites/jsuites.js.xml b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jsuites/jsuites.js.xml new file mode 100644 index 0000000000000000000000000000000000000000..3726e4bcff0dbdfde0966f059795d3c115c7157f --- /dev/null +++ b/bt5/erp5_jexcel_editor/SkinTemplateItem/portal_skins/erp5_jexcel_editor/jsuites/jsuites.js.xml @@ -0,0 +1,28 @@ +<?xml version="1.0"?> +<ZopeData> + <record id="1" aka="AAAAAAAAAAE="> + <pickle> + <global name="File" module="OFS.Image"/> + </pickle> + <pickle> + <dictionary> + <item> + <key> <string>__name__</string> </key> + <value> <string>jsuites.js</string> </value> + </item> + <item> + <key> <string>content_type</string> </key> + <value> <string>application/x-javascript</string> </value> + </item> + <item> + <key> <string>precondition</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>title</string> </key> + <value> <string></string> </value> + </item> + </dictionary> + </pickle> + </record> +</ZopeData> diff --git a/bt5/erp5_jexcel_editor/bt/description b/bt5/erp5_jexcel_editor/bt/description new file mode 100644 index 0000000000000000000000000000000000000000..43fcf17e8e882d7686973d222685e49b595b906b --- /dev/null +++ b/bt5/erp5_jexcel_editor/bt/description @@ -0,0 +1 @@ +Spreadsheet editor with Jexcel library. \ No newline at end of file diff --git a/bt5/erp5_jexcel_editor/bt/template_format_version b/bt5/erp5_jexcel_editor/bt/template_format_version new file mode 100644 index 0000000000000000000000000000000000000000..56a6051ca2b02b04ef92d5150c9ef600403cb1de --- /dev/null +++ b/bt5/erp5_jexcel_editor/bt/template_format_version @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/bt5/erp5_jexcel_editor/bt/template_skin_id_list b/bt5/erp5_jexcel_editor/bt/template_skin_id_list new file mode 100644 index 0000000000000000000000000000000000000000..4d91c4f6c7ef494d7e263e9bb60d488a54f23f1a --- /dev/null +++ b/bt5/erp5_jexcel_editor/bt/template_skin_id_list @@ -0,0 +1 @@ +erp5_jexcel_editor \ No newline at end of file diff --git a/bt5/erp5_jexcel_editor/bt/title b/bt5/erp5_jexcel_editor/bt/title new file mode 100644 index 0000000000000000000000000000000000000000..4d91c4f6c7ef494d7e263e9bb60d488a54f23f1a --- /dev/null +++ b/bt5/erp5_jexcel_editor/bt/title @@ -0,0 +1 @@ +erp5_jexcel_editor \ No newline at end of file diff --git a/bt5/erp5_jexcel_editor/bt/version b/bt5/erp5_jexcel_editor/bt/version new file mode 100644 index 0000000000000000000000000000000000000000..0f3016682b38e6ad179e8bafb3008b4cde4251e7 --- /dev/null +++ b/bt5/erp5_jexcel_editor/bt/version @@ -0,0 +1 @@ +001 \ No newline at end of file diff --git a/bt5/erp5_officejs_jquery_app/PathTemplateItem/portal_types/Web%20Table/web_table_clone.xml b/bt5/erp5_officejs_jquery_app/PathTemplateItem/portal_types/Web%20Table/web_table_clone.xml new file mode 100644 index 0000000000000000000000000000000000000000..707b39d604aa295b3c4daacdab5f78e37015d500 --- /dev/null +++ b/bt5/erp5_officejs_jquery_app/PathTemplateItem/portal_types/Web%20Table/web_table_clone.xml @@ -0,0 +1,75 @@ +<?xml version="1.0"?> +<ZopeData> + <record id="1" aka="AAAAAAAAAAE="> + <pickle> + <global name="Action Information" module="erp5.portal_type"/> + </pickle> + <pickle> + <dictionary> + <item> + <key> <string>action</string> </key> + <value> + <persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent> + </value> + </item> + <item> + <key> <string>action_permission</string> </key> + <value> + <tuple> + <string>Add portal content</string> + </tuple> + </value> + </item> + <item> + <key> <string>categories</string> </key> + <value> + <tuple> + <string>action_type/object_jio_js_script</string> + </tuple> + </value> + </item> + <item> + <key> <string>description</string> </key> + <value> + <none/> + </value> + </item> + <item> + <key> <string>id</string> </key> + <value> <string>web_table_clone</string> </value> + </item> + <item> + <key> <string>language</string> </key> + <value> + <none/> + </value> + </item> + <item> + <key> <string>portal_type</string> </key> + <value> <string>Action Information</string> </value> + </item> + <item> + <key> <string>reference</string> </key> + <value> <string>web_table_clone</string> </value> + </item> + <item> + <key> <string>title</string> </key> + <value> <string>Clone</string> </value> + </item> + </dictionary> + </pickle> + </record> + <record id="2" aka="AAAAAAAAAAI="> + <pickle> + <global name="Expression" module="Products.CMFCore.Expression"/> + </pickle> + <pickle> + <dictionary> + <item> + <key> <string>text</string> </key> + <value> <string>string:${object_url}/Base_cloneDocumentForWebTable</string> </value> + </item> + </dictionary> + </pickle> + </record> +</ZopeData> diff --git a/bt5/erp5_officejs_jquery_app/PathTemplateItem/web_page_module/gadget_officejs_web_table_configuration.xml b/bt5/erp5_officejs_jquery_app/PathTemplateItem/web_page_module/gadget_officejs_web_table_configuration.xml index 3905b41a22444f5ad2a36179e9316215bdb2252b..99c1b3faca8f4be1056b31be675144ded11bc53e 100644 --- a/bt5/erp5_officejs_jquery_app/PathTemplateItem/web_page_module/gadget_officejs_web_table_configuration.xml +++ b/bt5/erp5_officejs_jquery_app/PathTemplateItem/web_page_module/gadget_officejs_web_table_configuration.xml @@ -110,7 +110,7 @@ <item> <key> <string>text_content</string> </key> <value> <string>CONFIGURATION MANIFEST\n -# generated on Fri Dec 13 20:31:13 2019\n +# generated on Tue Jul 28 08:56:19 2020\n CACHE:\n \n hateoas_appcache/definition_view/cG9ydGFsX3R5cGVzL1dlYiBQYWdlIE1vZHVsZQ==\n @@ -119,6 +119,8 @@ hateoas_appcache/definition_view/cG9ydGFsX3NraW5zL2VycDVfd2ViX3RhYmxlX2VkaXRvci9 hateoas_appcache/definition_view/cG9ydGFsX3R5cGVzL1dlYiBUYWJsZQ==\n hateoas_appcache/definition_view/cG9ydGFsX3R5cGVzL1dlYiBUYWJsZS93ZWJfdGFibGVfdmlldw==\n hateoas_appcache/definition_view/cG9ydGFsX3NraW5zL2VycDVfd2ViX3RhYmxlX2VkaXRvci9XZWJUYWJsZV92aWV3QXNKaW9Gb3JXZWJUYWJsZUVkaXRvcg==\n +hateoas_appcache/definition_view/cG9ydGFsX3R5cGVzL1dlYiBUYWJsZS93ZWJfdGFibGVfY2xvbmU=\n +hateoas_appcache/definition_view/cG9ydGFsX3NraW5zL2VycDVfd2ViX3RhYmxlX2VkaXRvci9CYXNlX2Nsb25lRG9jdW1lbnRGb3JXZWJUYWJsZQ==\n \n NETWORK:\n *</string> </value> diff --git a/bt5/erp5_officejs_jquery_app/PathTemplateItem/web_page_module/gadget_web_table_router.html.html b/bt5/erp5_officejs_jquery_app/PathTemplateItem/web_page_module/gadget_web_table_router.html.html index 05cec78f5ddfe80e26f4ce6fad482cc6c060f071..2984d4aa4b5d0dc41441fe45dcfa3b80b341f275 100644 --- a/bt5/erp5_officejs_jquery_app/PathTemplateItem/web_page_module/gadget_web_table_router.html.html +++ b/bt5/erp5_officejs_jquery_app/PathTemplateItem/web_page_module/gadget_web_table_router.html.html @@ -19,7 +19,7 @@ <script data-renderjs-configuration="parent_relative_url" type="text/x-renderjs-configuration">web_page_module</script> <script data-renderjs-configuration="parent_portal_type" type="text/x-renderjs-configuration">Web Page Module</script> <script data-renderjs-configuration="portal_skin_folder" type="text/x-renderjs-configuration">erp5_web_table_editor</script> - <script data-renderjs-configuration="app_actions" type="text/x-renderjs-configuration">('Web Table | web_table_view', 'Web Page Module | web_table_view')</script> + <script data-renderjs-configuration="app_actions" type="text/x-renderjs-configuration">('Web Table | web_table_view', 'Web Page Module | web_table_view', 'Web Table | web_table_clone')</script> <script data-renderjs-configuration="app_allowed_sub_types" type="text/x-renderjs-configuration">('Web Page Module | Web Table',)</script> <script data-renderjs-configuration="app_view_reference" type="text/x-renderjs-configuration">web_table_view</script> <script data-renderjs-configuration="web_page_module_dict" type="text/x-renderjs-configuration">{"front_page": 1, "editable": 0, "hide_add_button": 0, "jump_button": 0, "fast_input_button": 0, "export_button": 0, "filter_action": 1, "panel_action": 1, "previous_next_button": 0, "history_previous_link": 0, "title": "Jquery Sheet Documents", "hide_listbox_buttons": 1, "blob_type": "", "blob_create_object_url": 0}</script> diff --git a/bt5/erp5_officejs_jquery_app/PathTemplateItem/web_page_module/gadget_web_table_router.html.xml b/bt5/erp5_officejs_jquery_app/PathTemplateItem/web_page_module/gadget_web_table_router.html.xml index 914738b554cbaee5cc822859e5ec892363c47456..96d1fde0f3a6ea9a2ee31c616d924b95c29f10bf 100644 --- a/bt5/erp5_officejs_jquery_app/PathTemplateItem/web_page_module/gadget_web_table_router.html.xml +++ b/bt5/erp5_officejs_jquery_app/PathTemplateItem/web_page_module/gadget_web_table_router.html.xml @@ -235,7 +235,7 @@ </item> <item> <key> <string>serial</string> </key> - <value> <string>977.62260.6779.17339</string> </value> + <value> <string>984.59943.25702.48622</string> </value> </item> <item> <key> <string>state</string> </key> @@ -253,7 +253,7 @@ </tuple> <state> <tuple> - <float>1568973158.24</float> + <float>1595923550.24</float> <string>UTC</string> </tuple> </state> diff --git a/bt5/erp5_officejs_jquery_app/PathTemplateItem/web_site_module/officejs_web_table_editor/app.xml b/bt5/erp5_officejs_jquery_app/PathTemplateItem/web_site_module/officejs_web_table_editor/app.xml index f1e272a070da2d0fc1337c52454e65e605536e7b..73193b0cfb18aac461ae8bdf73617adeb25a8741 100644 --- a/bt5/erp5_officejs_jquery_app/PathTemplateItem/web_site_module/officejs_web_table_editor/app.xml +++ b/bt5/erp5_officejs_jquery_app/PathTemplateItem/web_site_module/officejs_web_table_editor/app.xml @@ -412,7 +412,8 @@ <item> <key> <string>configuration_precache_manifest_script_list</string> </key> <value> <string>WebSection_getOfficeJsPrecacheManifestList\n -WebSection_getWebTableEditorPrecacheManifestList</string> </value> +WebSection_getWebTableEditorPrecacheManifestList\n +WebSection_getJexcelEditorPrecacheManifestList</string> </value> </item> <item> <key> <string>configuration_router_gadget_url</string> </key> @@ -677,7 +678,7 @@ WebSection_getWebTableEditorPrecacheManifestList</string> </value> </item> <item> <key> <string>serial</string> </key> - <value> <string>983.23018.43853.17715</string> </value> + <value> <string>984.59943.25702.48622</string> </value> </item> <item> <key> <string>state</string> </key> @@ -695,7 +696,7 @@ WebSection_getWebTableEditorPrecacheManifestList</string> </value> </tuple> <state> <tuple> - <float>1587729708.72</float> + <float>1594816416.45</float> <string>UTC</string> </tuple> </state> diff --git a/bt5/erp5_officejs_jquery_app/SkinTemplateItem/portal_skins/erp5_web_table_editor/Base_cloneDocumentForWebTable.xml b/bt5/erp5_officejs_jquery_app/SkinTemplateItem/portal_skins/erp5_web_table_editor/Base_cloneDocumentForWebTable.xml new file mode 100644 index 0000000000000000000000000000000000000000..e360dfdbb86758ce07fc16544a60fa28a4408f2c --- /dev/null +++ b/bt5/erp5_officejs_jquery_app/SkinTemplateItem/portal_skins/erp5_web_table_editor/Base_cloneDocumentForWebTable.xml @@ -0,0 +1,116 @@ +<?xml version="1.0"?> +<ZopeData> + <record id="1" aka="AAAAAAAAAAE="> + <pickle> + <global name="ERP5 Form" module="erp5.portal_type"/> + </pickle> + <pickle> + <dictionary> + <item> + <key> <string>_objects</string> </key> + <value> + <tuple/> + </value> + </item> + <item> + <key> <string>action</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>encoding</string> </key> + <value> <string>UTF-8</string> </value> + </item> + <item> + <key> <string>enctype</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>group_list</string> </key> + <value> + <list> + <string>left</string> + <string>right</string> + <string>center</string> + <string>bottom</string> + <string>hidden</string> + </list> + </value> + </item> + <item> + <key> <string>groups</string> </key> + <value> + <dictionary> + <item> + <key> <string>bottom</string> </key> + <value> + <list/> + </value> + </item> + <item> + <key> <string>center</string> </key> + <value> + <list/> + </value> + </item> + <item> + <key> <string>hidden</string> </key> + <value> + <list/> + </value> + </item> + <item> + <key> <string>left</string> </key> + <value> + <list> + <string>action_description</string> + <string>gadget_field_action_js_script</string> + </list> + </value> + </item> + <item> + <key> <string>right</string> </key> + <value> + <list/> + </value> + </item> + </dictionary> + </value> + </item> + <item> + <key> <string>id</string> </key> + <value> <string>Base_cloneDocumentForWebTable</string> </value> + </item> + <item> + <key> <string>method</string> </key> + <value> <string>POST</string> </value> + </item> + <item> + <key> <string>name</string> </key> + <value> <string>Base_cloneDocumentForJexcelEditor</string> </value> + </item> + <item> + <key> <string>portal_type</string> </key> + <value> <string>ERP5 Form</string> </value> + </item> + <item> + <key> <string>row_length</string> </key> + <value> <int>4</int> </value> + </item> + <item> + <key> <string>stored_encoding</string> </key> + <value> <string>UTF-8</string> </value> + </item> + <item> + <key> <string>title</string> </key> + <value> + <none/> + </value> + </item> + <item> + <key> <string>unicode_mode</string> </key> + <value> <int>0</int> </value> + </item> + </dictionary> + </pickle> + </record> +</ZopeData> diff --git a/bt5/erp5_officejs_jquery_app/SkinTemplateItem/portal_skins/erp5_web_table_editor/Base_cloneDocumentForWebTable/action_description.xml b/bt5/erp5_officejs_jquery_app/SkinTemplateItem/portal_skins/erp5_web_table_editor/Base_cloneDocumentForWebTable/action_description.xml new file mode 100644 index 0000000000000000000000000000000000000000..475edfbcad226a1ada84e08d6208c468cf00da35 --- /dev/null +++ b/bt5/erp5_officejs_jquery_app/SkinTemplateItem/portal_skins/erp5_web_table_editor/Base_cloneDocumentForWebTable/action_description.xml @@ -0,0 +1,139 @@ +<?xml version="1.0"?> +<ZopeData> + <record id="1" aka="AAAAAAAAAAE="> + <pickle> + <global name="LabelField" module="Products.Formulator.StandardFields"/> + </pickle> + <pickle> + <dictionary> + <item> + <key> <string>id</string> </key> + <value> <string>action_description</string> </value> + </item> + <item> + <key> <string>message_values</string> </key> + <value> + <dictionary/> + </value> + </item> + <item> + <key> <string>overrides</string> </key> + <value> + <dictionary> + <item> + <key> <string>css_class</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>default</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>description</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>editable</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>enabled</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>extra</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>hidden</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>title</string> </key> + <value> <string></string> </value> + </item> + </dictionary> + </value> + </item> + <item> + <key> <string>tales</string> </key> + <value> + <dictionary> + <item> + <key> <string>css_class</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>default</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>description</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>editable</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>enabled</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>extra</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>hidden</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>title</string> </key> + <value> <string></string> </value> + </item> + </dictionary> + </value> + </item> + <item> + <key> <string>values</string> </key> + <value> + <dictionary> + <item> + <key> <string>css_class</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>default</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>description</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>editable</string> </key> + <value> <int>1</int> </value> + </item> + <item> + <key> <string>enabled</string> </key> + <value> <int>1</int> </value> + </item> + <item> + <key> <string>extra</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>hidden</string> </key> + <value> <int>0</int> </value> + </item> + <item> + <key> <string>title</string> </key> + <value> <string>Confirm clone</string> </value> + </item> + </dictionary> + </value> + </item> + </dictionary> + </pickle> + </record> +</ZopeData> diff --git a/bt5/erp5_officejs_jquery_app/SkinTemplateItem/portal_skins/erp5_web_table_editor/Base_cloneDocumentForWebTable/gadget_field_action_js_script.xml b/bt5/erp5_officejs_jquery_app/SkinTemplateItem/portal_skins/erp5_web_table_editor/Base_cloneDocumentForWebTable/gadget_field_action_js_script.xml new file mode 100644 index 0000000000000000000000000000000000000000..55111cd50cb7f2690e455a9b694588436a1cc26f --- /dev/null +++ b/bt5/erp5_officejs_jquery_app/SkinTemplateItem/portal_skins/erp5_web_table_editor/Base_cloneDocumentForWebTable/gadget_field_action_js_script.xml @@ -0,0 +1,246 @@ +<?xml version="1.0"?> +<ZopeData> + <record id="1" aka="AAAAAAAAAAE="> + <pickle> + <global name="GadgetField" module="Products.ERP5Form.GadgetField"/> + </pickle> + <pickle> + <dictionary> + <item> + <key> <string>id</string> </key> + <value> <string>gadget_field_action_js_script</string> </value> + </item> + <item> + <key> <string>message_values</string> </key> + <value> + <dictionary> + <item> + <key> <string>external_validator_failed</string> </key> + <value> <string>The input failed the external validator.</string> </value> + </item> + <item> + <key> <string>no_validator</string> </key> + <value> <string>Does not support this operation.</string> </value> + </item> + </dictionary> + </value> + </item> + <item> + <key> <string>overrides</string> </key> + <value> + <dictionary> + <item> + <key> <string>alternate_name</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>css_class</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>data_url</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>default</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>description</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>editable</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>enabled</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>external_validator</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>extra</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>gadget_url</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>hidden</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>js_sandbox</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>renderjs_extra</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>title</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>validator_field_id</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>validator_form_id</string> </key> + <value> <string></string> </value> + </item> + </dictionary> + </value> + </item> + <item> + <key> <string>tales</string> </key> + <value> + <dictionary> + <item> + <key> <string>alternate_name</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>css_class</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>data_url</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>default</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>description</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>editable</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>enabled</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>external_validator</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>extra</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>gadget_url</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>hidden</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>js_sandbox</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>renderjs_extra</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>title</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>validator_field_id</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>validator_form_id</string> </key> + <value> <string></string> </value> + </item> + </dictionary> + </value> + </item> + <item> + <key> <string>values</string> </key> + <value> + <dictionary> + <item> + <key> <string>alternate_name</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>css_class</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>data_url</string> </key> + <value> <int>0</int> </value> + </item> + <item> + <key> <string>default</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>description</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>editable</string> </key> + <value> <int>1</int> </value> + </item> + <item> + <key> <string>enabled</string> </key> + <value> <int>1</int> </value> + </item> + <item> + <key> <string>external_validator</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>extra</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>gadget_url</string> </key> + <value> <string>action_web_table_clone.html</string> </value> + </item> + <item> + <key> <string>hidden</string> </key> + <value> <int>0</int> </value> + </item> + <item> + <key> <string>js_sandbox</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>renderjs_extra</string> </key> + <value> + <list/> + </value> + </item> + <item> + <key> <string>title</string> </key> + <value> <string>gadget_field_action_js_script</string> </value> + </item> + <item> + <key> <string>validator_field_id</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>validator_form_id</string> </key> + <value> <string></string> </value> + </item> + </dictionary> + </value> + </item> + </dictionary> + </pickle> + </record> +</ZopeData> diff --git a/bt5/erp5_officejs_jquery_app/SkinTemplateItem/portal_skins/erp5_web_table_editor/WebSection_getWebTableEditorPrecacheManifestList.py b/bt5/erp5_officejs_jquery_app/SkinTemplateItem/portal_skins/erp5_web_table_editor/WebSection_getWebTableEditorPrecacheManifestList.py index 69ec684d269ecd0f8a7adb5b3c9ef1787a56ccfe..4c99f1920760df6fb8dfcd9bdf470f94f0f4cb34 100644 --- a/bt5/erp5_officejs_jquery_app/SkinTemplateItem/portal_skins/erp5_web_table_editor/WebSection_getWebTableEditorPrecacheManifestList.py +++ b/bt5/erp5_officejs_jquery_app/SkinTemplateItem/portal_skins/erp5_web_table_editor/WebSection_getWebTableEditorPrecacheManifestList.py @@ -4,43 +4,10 @@ url_list = [ "gadget_officejs_web_table_router.html", "gadget_officejs_jio_web_table_view.html", "gadget_officejs_jio_web_table_view.js", + "action_web_table_clone.html", + "action_web_table_clone.js", "gadget_officejs_web_table.json", "web_table_app_logo.svg", - - #jquery-sheets - "jquery.js", - "jquery-sheets.gadget.html", - "jquery-sheets.gadget.js", - "jquery-sheets/wickedgrid.css", - "jquery-sheets/menu.html", - "jquery-sheets/font-awesome.css", - "jquery-sheets/js/thaw.js", - "jquery-sheets/js/raphael.js", - "jquery-sheets/js/copy3_of_undomanager.js", - "jquery-sheets/js/jquery-ui.js", - "jquery-sheets/js/globalize.js", - "jquery-sheets/js/jquery.nearest.js", - "jquery-sheets/js/megatable.js", - "jquery-sheets/js/wickedgrid.js", - "jquery-sheets/js/graphael/g.bar.js", - "jquery-sheets/js/graphael/g.line.js", - "jquery-sheets/js/graphael/g.raphael.js", - "jquery-sheets/js/graphael/g.pie.js", - "jquery-sheets/js/graphael/g.dot.js", - "jquery-sheets/js/ZeroClipboard.js", - "jquery-sheets/js/jquery.elastic.source.js", - "jquery-sheets/js/infiniscroll.js", - "jquery-sheets/js/undomanager.js", - "jquery-sheets/js/MouseWheel.js", - "jquery-sheets/bootstrap-theme.css", - "jquery-sheets/images/underline1.svg", - "jquery-sheets/images/strikethrough.svg", - "jquery-sheets/images/right183.svg", - "jquery-sheets/images/left181.svg", - "jquery-sheets/images/centered5.svg", - "jquery-sheets/images/italic3.svg", - "jquery-sheets/images/bold13.svg", - "jquery-sheets/bootstrap.css", ] return url_list diff --git a/bt5/erp5_officejs_jquery_app/SkinTemplateItem/portal_skins/erp5_web_table_editor/WebTable_viewAsJioForWebTableEditor/my_text_content.xml b/bt5/erp5_officejs_jquery_app/SkinTemplateItem/portal_skins/erp5_web_table_editor/WebTable_viewAsJioForWebTableEditor/my_text_content.xml index 3e28e523fca09b0fac687014efdf9a5474b6affe..ea911048bac80125393165492575fd44ab9ed21f 100644 --- a/bt5/erp5_officejs_jquery_app/SkinTemplateItem/portal_skins/erp5_web_table_editor/WebTable_viewAsJioForWebTableEditor/my_text_content.xml +++ b/bt5/erp5_officejs_jquery_app/SkinTemplateItem/portal_skins/erp5_web_table_editor/WebTable_viewAsJioForWebTableEditor/my_text_content.xml @@ -224,8 +224,8 @@ <value> <list> <tuple> - <string>{"editor": "jquery-sheets", "maximize": true}</string> - <string>{"editor": "jquery-sheets", "maximize": true}</string> + <string>{"editor": "jexcel", "maximize": true}</string> + <string>{"editor": "jexcel", "maximize": true}</string> </tuple> </list> </value> diff --git a/bt5/erp5_officejs_jquery_app/SkinTemplateItem/portal_skins/erp5_web_table_editor/action_web_table_clone.html.html b/bt5/erp5_officejs_jquery_app/SkinTemplateItem/portal_skins/erp5_web_table_editor/action_web_table_clone.html.html new file mode 100644 index 0000000000000000000000000000000000000000..479d48572950fd8a841edf959f93f2ca2e9c4f78 --- /dev/null +++ b/bt5/erp5_officejs_jquery_app/SkinTemplateItem/portal_skins/erp5_web_table_editor/action_web_table_clone.html.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>Web Table Clone</title> + + <script src="rsvp.js"></script> + <script src="renderjs.js"></script> + + <script src="action_web_table_clone.js"></script> + </head> + <body> + </body> +</html> \ No newline at end of file diff --git a/bt5/erp5_officejs_jquery_app/SkinTemplateItem/portal_skins/erp5_web_table_editor/action_web_table_clone.html.xml b/bt5/erp5_officejs_jquery_app/SkinTemplateItem/portal_skins/erp5_web_table_editor/action_web_table_clone.html.xml new file mode 100644 index 0000000000000000000000000000000000000000..9731023dde16ecdc7936802f3db82a5358aae890 --- /dev/null +++ b/bt5/erp5_officejs_jquery_app/SkinTemplateItem/portal_skins/erp5_web_table_editor/action_web_table_clone.html.xml @@ -0,0 +1,28 @@ +<?xml version="1.0"?> +<ZopeData> + <record id="1" aka="AAAAAAAAAAE="> + <pickle> + <global name="File" module="OFS.Image"/> + </pickle> + <pickle> + <dictionary> + <item> + <key> <string>__name__</string> </key> + <value> <string>action_web_table_clone.html</string> </value> + </item> + <item> + <key> <string>content_type</string> </key> + <value> <string>text/html</string> </value> + </item> + <item> + <key> <string>precondition</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>title</string> </key> + <value> <string></string> </value> + </item> + </dictionary> + </pickle> + </record> +</ZopeData> diff --git a/bt5/erp5_officejs_jquery_app/SkinTemplateItem/portal_skins/erp5_web_table_editor/action_web_table_clone.js.js b/bt5/erp5_officejs_jquery_app/SkinTemplateItem/portal_skins/erp5_web_table_editor/action_web_table_clone.js.js new file mode 100644 index 0000000000000000000000000000000000000000..c52506100fa4cb68ac533c1cb80d5de8faafd3f1 --- /dev/null +++ b/bt5/erp5_officejs_jquery_app/SkinTemplateItem/portal_skins/erp5_web_table_editor/action_web_table_clone.js.js @@ -0,0 +1,57 @@ +/*jslint nomen: true, indent: 2 */ +/*global window, rJS, RSVP*/ +(function (window, rJS) { + "use strict"; + rJS(window) + .declareAcquiredMethod("jio_get", "jio_get") + .declareAcquiredMethod("jio_post", "jio_post") + .declareMethod("preRenderDocument", function (parent_options) { + var gadget = this; + return gadget.jio_get(parent_options.jio_key) + .push(function (parent_document) { + return parent_document; + }); + }) + .declareMethod("handleSubmit", function (content_dict, parent_options) { + //must return a dict with: + //notify: options_dict for notifySubmitted + //redirect: options_dict for redirect + var return_submit_dict = { + notify: { + message: "", + status: "" + }, + redirect: { + command: "display", + options: {} + } + }, + gadget = this, + document = parent_options.doc, + property; + delete content_dict.dialog_method; + for (property in content_dict) { + if (content_dict.hasOwnProperty(property)) { + document[property] = content_dict[property]; + } + } + return gadget.jio_post(document) + .push(function (jio_key) { + return_submit_dict.notify.message = "Document Cloned"; + return_submit_dict.notify.status = "success"; + return_submit_dict.redirect.options = { + jio_key: jio_key, + editable: true + }; + return return_submit_dict; + }, function (error) { + if (error instanceof jIO.util.jIOError) { + return_submit_dict.notify.message = "Failure cloning document"; + return_submit_dict.notify.status = "error"; + return return_submit_dict; + } + throw error; + }); + }); + +}(window, rJS)); \ No newline at end of file diff --git a/bt5/erp5_officejs_jquery_app/SkinTemplateItem/portal_skins/erp5_web_table_editor/action_web_table_clone.js.xml b/bt5/erp5_officejs_jquery_app/SkinTemplateItem/portal_skins/erp5_web_table_editor/action_web_table_clone.js.xml new file mode 100644 index 0000000000000000000000000000000000000000..d4345103f0720a404339ba04276f2dfb5a138efa --- /dev/null +++ b/bt5/erp5_officejs_jquery_app/SkinTemplateItem/portal_skins/erp5_web_table_editor/action_web_table_clone.js.xml @@ -0,0 +1,28 @@ +<?xml version="1.0"?> +<ZopeData> + <record id="1" aka="AAAAAAAAAAE="> + <pickle> + <global name="File" module="OFS.Image"/> + </pickle> + <pickle> + <dictionary> + <item> + <key> <string>__name__</string> </key> + <value> <string>action_web_table_clone.js</string> </value> + </item> + <item> + <key> <string>content_type</string> </key> + <value> <string>application/javascript</string> </value> + </item> + <item> + <key> <string>precondition</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>title</string> </key> + <value> <string></string> </value> + </item> + </dictionary> + </pickle> + </record> +</ZopeData> diff --git a/bt5/erp5_officejs_jquery_app/bt/dependency_list b/bt5/erp5_officejs_jquery_app/bt/dependency_list index 3306c17352a13dd90bf69d2d63933ca39939b768..4b8bc687f7f9411a2a740ec0bb2b3f531d6acc0b 100644 --- a/bt5/erp5_officejs_jquery_app/bt/dependency_list +++ b/bt5/erp5_officejs_jquery_app/bt/dependency_list @@ -1,3 +1,4 @@ erp5_officejs erp5_jquery_sheet_js_editor -erp5_minipaint \ No newline at end of file +erp5_minipaint +erp5_jexcel_editor \ No newline at end of file diff --git a/bt5/erp5_officejs_jquery_app/bt/template_path_list b/bt5/erp5_officejs_jquery_app/bt/template_path_list index e060ebe3fc2be74a47f1d4cd75b50fc65ee4845d..9717521187341df53456fec122dcb3436914e601 100644 --- a/bt5/erp5_officejs_jquery_app/bt/template_path_list +++ b/bt5/erp5_officejs_jquery_app/bt/template_path_list @@ -3,6 +3,7 @@ image_module/web_table_app_logo_svg portal_types/Image Module/image_editor_view portal_types/Image/image_editor_view portal_types/Web Page Module/web_table_view +portal_types/Web Table/web_table_clone portal_types/Web Table/web_table_view web_page_module/gadget_image_editor* web_page_module/gadget_officejs_image_editor* @@ -11,4 +12,4 @@ web_page_module/gadget_web_table* web_site_module/officejs_image_editor web_site_module/officejs_image_editor/** web_site_module/officejs_web_table_editor -web_site_module/officejs_web_table_editor/** +web_site_module/officejs_web_table_editor/** \ No newline at end of file diff --git a/bt5/erp5_officejs_ui_test/PathTemplateItem/portal_tests/officejs_ui_webtable_zuite/testJexcelEditor.xml b/bt5/erp5_officejs_ui_test/PathTemplateItem/portal_tests/officejs_ui_webtable_zuite/testJexcelEditor.xml new file mode 100644 index 0000000000000000000000000000000000000000..593fb50b1624384479aa10a933fafeac4aaa2193 --- /dev/null +++ b/bt5/erp5_officejs_ui_test/PathTemplateItem/portal_tests/officejs_ui_webtable_zuite/testJexcelEditor.xml @@ -0,0 +1,58 @@ +<?xml version="1.0"?> +<ZopeData> + <record id="1" aka="AAAAAAAAAAE="> + <pickle> + <global name="ZopePageTemplate" module="Products.PageTemplates.ZopePageTemplate"/> + </pickle> + <pickle> + <dictionary> + <item> + <key> <string>_bind_names</string> </key> + <value> + <object> + <klass> + <global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/> + </klass> + <tuple/> + <state> + <dictionary> + <item> + <key> <string>_asgns</string> </key> + <value> + <dictionary> + <item> + <key> <string>name_subpath</string> </key> + <value> <string>traverse_subpath</string> </value> + </item> + </dictionary> + </value> + </item> + </dictionary> + </state> + </object> + </value> + </item> + <item> + <key> <string>content_type</string> </key> + <value> <string>text/html</string> </value> + </item> + <item> + <key> <string>expand</string> </key> + <value> <int>0</int> </value> + </item> + <item> + <key> <string>id</string> </key> + <value> <string>testJexcelEditor</string> </value> + </item> + <item> + <key> <string>output_encoding</string> </key> + <value> <string>utf-8</string> </value> + </item> + <item> + <key> <string>title</string> </key> + <value> <unicode></unicode> </value> + </item> + </dictionary> + </pickle> + </record> +</ZopeData> diff --git a/bt5/erp5_officejs_ui_test/PathTemplateItem/portal_tests/officejs_ui_webtable_zuite/testJexcelEditor.zpt b/bt5/erp5_officejs_ui_test/PathTemplateItem/portal_tests/officejs_ui_webtable_zuite/testJexcelEditor.zpt new file mode 100644 index 0000000000000000000000000000000000000000..0b7be1b6995a52e5d02a484fc27384205623617b --- /dev/null +++ b/bt5/erp5_officejs_ui_test/PathTemplateItem/portal_tests/officejs_ui_webtable_zuite/testJexcelEditor.zpt @@ -0,0 +1,177 @@ +<html xmlns:tal="http://xml.zope.org/namespaces/tal" + xmlns:metal="http://xml.zope.org/namespaces/metal"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> +<title>Test OfficeJS UI</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">Test OfficeJS Jexcel UI</td></tr> +</thead><tbody> +<tal:block metal:use-macro="here/Zuite_CommonTemplate/macros/init" /> +<!-- Clean Up --> +<tr> + <td>open</td> + <td>${base_url}/bar_module/ListBoxZuite_reset</td> + <td></td> +</tr> +<tr> + <td>assertTextPresent</td> + <td>Reset Successfully.</td> + <td></td> +</tr> +<!-- Initialize and skip app installation --> +<tr> + <td>open</td> + <td>${base_url}/web_site_module/officejs_web_table_editor/</td> + <td></td> +</tr> + +<tr> + <td>waitForElementPresent</td> + <td>link=Skip</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>link=Skip</td> + <td></td> +</tr> + +<tr> + <td>waitForElementPresent</td> + <td>//a[@data-i18n='Storages']</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>//a[@data-i18n='Storages']</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>link=Local is Enough</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>link=Local is Enough</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>link=Add</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>link=Add</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>//button[@data-i18n='Save']</td> + <td></td> +</tr> +<tr> +<!-- Wait for spreadsheet to be rendered by the gadget --> +<tr> + <td>waitForElementPresent</td> + <td>//div[@class='spreadsheet']</td> + <td></td> +</tr> +<!-- Verify maximize button is present --> +<tr> + <td>waitForElementPresent</td> + <td>//button[contains(@class, 'ui-icon-expand ui-btn-icon-notext')]</td> + <td></td> +</tr> +<!-- Verify editor has been maximized --> +<tr> + <td>click</td> + <td>//button[contains(@class, 'ui-icon-expand ui-btn-icon-notext')]</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>//div[contains(@class, 'editor-maximize')]</td> + <td></td> +</tr> +<!-- Verify searchbar is present --> +<tr> + <td>waitForElementPresent</td> + <td>//div[contains(@class, 'jexcel_filter')]</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>//div[contains(@class, 'jexcel_filter')]</td> + <td></td> +</tr> +<!-- Verify toolbar is present --> +<tr> + <td>waitForElementPresent</td> + <td>//div[contains(@class, 'jexcel_toolbar')]</td> + <td></td> +</tr> +<!-- Verify body is present, draggable and resizable --> +<tr> + <td>waitForElementPresent</td> + <td>//tbody[contains(@class, 'draggable resizable')]</td> + <td></td> +</tr> +<!-- Add table --> +<tr> + <td>waitForElementPresent</td> + <td>//i[@title='Add table']</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>//i[@title='Add table']</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>//div[@data-spreadsheet='1']</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>//div[@data-spreadsheet='1']</td> + <td>Table 2</td> +</tr> +<!-- Delete Sheet --> +<tr> + <td>click</td> + <td>//div[@data-spreadsheet='0']</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>//i[@title='Delete table']</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>//i[@title='Delete table']</td> + <td></td> +</tr> +<tr> + <td>assertConfirmation</td> + <td>Delete this table ?</td> + <td></td> +</tr> +<tr> + <td>assertElementNotPresent</td> + <td>//div[@data-spreadsheet='1']</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>//div[@data-spreadsheet='0']</td> + <td>Table 1</td> +</tr> +</tbody> +</table> \ No newline at end of file diff --git a/bt5/erp5_officejs_ui_test/PathTemplateItem/portal_tests/officejs_ui_webtable_zuite/testOfficeJSWebTableEditor.zpt b/bt5/erp5_officejs_ui_test/PathTemplateItem/portal_tests/officejs_ui_webtable_zuite/testOfficeJSWebTableEditor.zpt index 1901f8a68d612bdd17aa445766dbfd51f3064cd3..0caf76de0125bcf3e1d9ee65df2506428d1bc178 100644 --- a/bt5/erp5_officejs_ui_test/PathTemplateItem/portal_tests/officejs_ui_webtable_zuite/testOfficeJSWebTableEditor.zpt +++ b/bt5/erp5_officejs_ui_test/PathTemplateItem/portal_tests/officejs_ui_webtable_zuite/testOfficeJSWebTableEditor.zpt @@ -116,7 +116,7 @@ </tr> <tr> <td>waitForElementPresent</td> - <td>identifier=jquery_sheet_gadget</td> + <td>//div[@class='spreadsheet']</td> <td></td> </tr> <!-- Wait for editor completly loaded, this is workaround, wait for lock in renderjs --> diff --git a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/gadget_editor.js.js b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/gadget_editor.js.js index f63db3a8234f53852d751d39784a39d45b109b08..fcef0cc6ae306caf44b4b2e02c230ecf6d33be3e 100644 --- a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/gadget_editor.js.js +++ b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/gadget_editor.js.js @@ -13,7 +13,8 @@ "jquery-sheets": {"url": "jquery-sheets.gadget.html"}, "pdf": {"url": "pdf_js/pdfjs.gadget.html"}, "notebook_editor": {"url": "gadget_notebook.html"}, - "jsmd_editor": {"url": "gadget_jsmd_viewer.html"} + "jsmd_editor": {"url": "gadget_jsmd_viewer.html"}, + "jexcel" : {"url": "jexcel.gadget.html"} };