diff --git a/app/assets/javascripts/discourse/app/components/modal/spreadsheet-editor.gjs b/app/assets/javascripts/discourse/app/components/modal/spreadsheet-editor.gjs
new file mode 100644
index 00000000000..eaa1043e7a7
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/components/modal/spreadsheet-editor.gjs
@@ -0,0 +1,389 @@
+import Component from "@glimmer/component";
+import { tracked } from "@glimmer/tracking";
+import { action } from "@ember/object";
+import didInsert from "@ember/render-modifiers/modifiers/did-insert";
+import { schedule } from "@ember/runloop";
+import { inject as service } from "@ember/service";
+import ConditionalLoadingSpinner from "discourse/components/conditional-loading-spinner";
+import DButton from "discourse/components/d-button";
+import DModal from "discourse/components/d-modal";
+import DModalCancel from "discourse/components/d-modal-cancel";
+import TextField from "discourse/components/text-field";
+import { ajax } from "discourse/lib/ajax";
+import { popupAjaxError } from "discourse/lib/ajax-error";
+import loadScript from "discourse/lib/load-script";
+import {
+ arrayToTable,
+ findTableRegex,
+ tokenRange,
+} from "discourse/lib/utilities";
+import i18n from "discourse-common/helpers/i18n";
+import I18n from "discourse-i18n";
+import DTooltip from "float-kit/components/d-tooltip";
+
+export default class SpreadsheetEditor extends Component {
+ @service dialog;
+ @tracked showEditReason = false;
+ @tracked loading = null;
+ spreadsheet = null;
+ defaultColWidth = 150;
+ isEditingTable = !!this.args.model.tableTokens;
+
+ get modalAttributes() {
+ if (this.isEditingTable) {
+ return {
+ title: "table_builder.edit.modal.title",
+ insertTable: {
+ title: "table_builder.edit.modal.create",
+ icon: "pencil-alt",
+ },
+ };
+ } else {
+ return {
+ title: "table_builder.modal.title",
+ insertTable: {
+ title: "table_builder.modal.create",
+ icon: "plus",
+ },
+ };
+ }
+ }
+
+ @action
+ createSpreadsheet(spreadsheet) {
+ this.spreadsheet = spreadsheet;
+
+ schedule("afterRender", () => {
+ this.loadLibraries().then(() => {
+ if (this.isEditingTable) {
+ this.buildPopulatedTable(this.args.model.tableTokens);
+ } else {
+ this.buildNewTable();
+ }
+ });
+ });
+ }
+
+ @action
+ showEditReasonField() {
+ this.showEditReason = !this.showEditReason;
+ }
+
+ @action
+ interceptCloseModal() {
+ if (this._hasChanges()) {
+ this.dialog.yesNoConfirm({
+ message: I18n.t("table_builder.modal.confirm_close"),
+ didConfirm: () => this.args.closeModal(),
+ });
+ } else {
+ this.args.closeModal();
+ }
+ }
+
+ @action
+ insertTable() {
+ const updatedHeaders = this.spreadsheet.getHeaders().split(","); // keys
+ const updatedData = this.spreadsheet.getData(); // values
+ const markdownTable = this.buildTableMarkdown(updatedHeaders, updatedData);
+
+ if (!this.isEditingTable) {
+ this.args.model.toolbarEvent.addText(markdownTable);
+ return this.args.closeModal();
+ } else {
+ return this.updateTable(markdownTable);
+ }
+ }
+
+ _hasChanges() {
+ if (this.isEditingTable) {
+ const originalSpreadsheetData = this.extractTableContent(
+ tokenRange(this.args.model.tableTokens, "tr_open", "tr_close")
+ );
+ const currentHeaders = this.spreadsheet.getHeaders().split(",");
+ const currentRows = this.spreadsheet.getData();
+ const currentSpreadsheetData = currentHeaders.concat(currentRows.flat());
+
+ return (
+ JSON.stringify(currentSpreadsheetData) !==
+ JSON.stringify(originalSpreadsheetData)
+ );
+ } else {
+ return this.spreadsheet
+ .getData()
+ .flat()
+ .some((element) => element !== "");
+ }
+ }
+
+ loadLibraries() {
+ this.loading = true;
+ return loadScript("/javascripts/jsuites/jsuites.js")
+ .then(() => {
+ return loadScript("/javascripts/jspreadsheet/jspreadsheet.js");
+ })
+ .finally(() => (this.loading = false));
+ }
+
+ buildNewTable() {
+ const data = [
+ ["", "", ""],
+ ["", "", ""],
+ ["", "", ""],
+ ["", "", ""],
+ ["", "", ""],
+ ["", "", ""],
+ ];
+
+ const columns = [
+ {
+ title: I18n.t("table_builder.default_header.col_1"),
+ width: this.defaultColWidth,
+ },
+ {
+ title: I18n.t("table_builder.default_header.col_2"),
+ width: this.defaultColWidth,
+ },
+ {
+ title: I18n.t("table_builder.default_header.col_3"),
+
+ width: this.defaultColWidth,
+ },
+ {
+ title: I18n.t("table_builder.default_header.col_4"),
+
+ width: this.defaultColWidth,
+ },
+ ];
+
+ return this.buildSpreadsheet(data, columns);
+ }
+
+ extractTableContent(data) {
+ return data
+ .flat()
+ .filter((t) => t.type === "inline")
+ .map((t) => t.content);
+ }
+
+ buildPopulatedTable(tableTokens) {
+ const contentRows = tokenRange(tableTokens, "tr_open", "tr_close");
+ const rows = [];
+ let headings;
+ const rowWidthFactor = 8;
+
+ contentRows.forEach((row, index) => {
+ if (index === 0) {
+ // headings
+ headings = this.extractTableContent(row).map((heading) => {
+ return {
+ title: heading,
+ width: Math.max(
+ heading.length * rowWidthFactor,
+ this.defaultColWidth
+ ),
+ align: "left",
+ };
+ });
+ } else {
+ // rows:
+ rows.push(this.extractTableContent(row));
+ }
+ });
+
+ return this.buildSpreadsheet(rows, headings);
+ }
+
+ buildSpreadsheet(data, columns, opts = {}) {
+ const postNumber = this.args.model?.post_number;
+ const exportFileName = postNumber
+ ? `post-${postNumber}-table-export`
+ : `post-table-export`;
+
+ // eslint-disable-next-line no-undef
+ this.spreadsheet = jspreadsheet(this.spreadsheet, {
+ data,
+ columns,
+ defaultColAlign: "left",
+ wordWrap: true,
+ csvFileName: exportFileName,
+ text: this.localeMapping(),
+ ...opts,
+ });
+ }
+
+ buildUpdatedPost(tableIndex, raw, newRaw) {
+ const tableToEdit = raw.match(findTableRegex());
+ let editedTable;
+
+ if (tableToEdit.length) {
+ editedTable = raw.replace(tableToEdit[tableIndex], newRaw);
+ } else {
+ return raw;
+ }
+
+ // replace null characters
+ editedTable = editedTable.replace(/\0/g, "\ufffd");
+ return editedTable;
+ }
+
+ updateTable(markdownTable) {
+ const tableIndex = this.args.model.tableIndex;
+ const postId = this.args.model.post.id;
+ const newRaw = markdownTable;
+
+ const editReason =
+ this.editReason || I18n.t("table_builder.edit.default_edit_reason");
+ const raw = this.args.model.post.raw;
+ const newPostRaw = this.buildUpdatedPost(tableIndex, raw, newRaw);
+
+ return this.sendTableUpdate(postId, newPostRaw, editReason);
+ }
+
+ sendTableUpdate(postId, raw, edit_reason) {
+ return ajax(`/posts/${postId}.json`, {
+ type: "PUT",
+ data: {
+ post: {
+ raw,
+ edit_reason,
+ },
+ },
+ })
+ .catch(popupAjaxError)
+ .finally(() => {
+ this.args.closeModal();
+ });
+ }
+
+ buildTableMarkdown(headers, data) {
+ const table = [];
+ data.forEach((row) => {
+ const result = {};
+
+ headers.forEach((_key, index) => {
+ const columnKey = `col${index}`;
+ return (result[columnKey] = row[index]);
+ });
+ table.push(result);
+ });
+
+ return arrayToTable(table, headers);
+ }
+
+ localeMapping() {
+ return {
+ noRecordsFound: prefixedLocale("no_records_found"),
+ show: prefixedLocale("show"),
+ entries: prefixedLocale("entries"),
+ insertANewColumnBefore: prefixedLocale("context_menu.col.before"),
+ insertANewColumnAfter: prefixedLocale("context_menu.col.after"),
+ deleteSelectedColumns: prefixedLocale("context_menu.col.delete"),
+ renameThisColumn: prefixedLocale("context_menu.col.rename"),
+ orderAscending: prefixedLocale("context_menu.order.ascending"),
+ orderDescending: prefixedLocale("context_menu.order.descending"),
+ insertANewRowBefore: prefixedLocale("context_menu.row.before"),
+ insertANewRowAfter: prefixedLocale("context_menu.row.after"),
+ deleteSelectedRows: prefixedLocale("context_menu.row.delete"),
+ copy: prefixedLocale("context_menu.copy"),
+ paste: prefixedLocale("context_menu.paste"),
+ saveAs: prefixedLocale("context_menu.save"),
+ about: prefixedLocale("about"),
+ areYouSureToDeleteTheSelectedRows: prefixedLocale(
+ "prompts.delete_selected_rows"
+ ),
+ areYouSureToDeleteTheSelectedColumns: prefixedLocale(
+ "prompts.delete_selected_cols"
+ ),
+ thisActionWillDestroyAnyExistingMergedCellsAreYouSure: prefixedLocale(
+ "prompts.will_destroy_merged_cells"
+ ),
+ thisActionWillClearYourSearchResultsAreYouSure: prefixedLocale(
+ "prompts.will_clear_search_results"
+ ),
+ thereIsAConflictWithAnotherMergedCell: prefixedLocale(
+ "prompts.conflict_with_merged_cells"
+ ),
+ invalidMergeProperties: prefixedLocale("invalid_merge_props"),
+ cellAlreadyMerged: prefixedLocale("cells_already_merged"),
+ noCellsSelected: prefixedLocale("no_cells_selected"),
+ };
+ }
+
+
+
+ <:body>
+
+
+
+
+
+ <:footer>
+
+
+
+
+
+
+
+ {{#if this.isEditingTable}}
+
+
+ {{#if this.showEditReason}}
+
+ {{/if}}
+
+ {{/if}}
+
+
+ {{i18n "table_builder.modal.help.title"}}
+ -
+
+ {{i18n "table_builder.modal.help.enter_key"}}
+
+ {{i18n "table_builder.modal.help.new_row"}}
+
+ -
+
+ {{i18n "table_builder.modal.help.tab_key"}}
+
+ {{i18n "table_builder.modal.help.new_col"}}
+
+ - {{i18n "table_builder.modal.help.options"}}
+
+
+
+
+
+
+}
+
+function prefixedLocale(localeString) {
+ return I18n.t(`table_builder.spreadsheet.${localeString}`);
+}
diff --git a/app/assets/javascripts/discourse/app/instance-initializers/post-decorations.js b/app/assets/javascripts/discourse/app/instance-initializers/post-decorations.js
index 0ecf7f88544..9c04cdad135 100644
--- a/app/assets/javascripts/discourse/app/instance-initializers/post-decorations.js
+++ b/app/assets/javascripts/discourse/app/instance-initializers/post-decorations.js
@@ -1,13 +1,18 @@
import { schedule } from "@ember/runloop";
import { create } from "virtual-dom";
import FullscreenTableModal from "discourse/components/modal/fullscreen-table";
+import SpreadsheetEditor from "discourse/components/modal/spreadsheet-editor";
+import { ajax } from "discourse/lib/ajax";
+import { popupAjaxError } from "discourse/lib/ajax-error";
import Columns from "discourse/lib/columns";
import highlightSyntax from "discourse/lib/highlight-syntax";
import { nativeLazyLoading } from "discourse/lib/lazy-load-images";
import lightbox from "discourse/lib/lightbox";
import { SELECTORS } from "discourse/lib/lightbox/constants";
import { withPluginApi } from "discourse/lib/plugin-api";
+import { parseAsync } from "discourse/lib/text";
import { setTextDirections } from "discourse/lib/text-direction";
+import { tokenRange } from "discourse/lib/utilities";
import { iconHTML, iconNode } from "discourse-common/lib/icon-library";
import I18n from "discourse-i18n";
@@ -106,21 +111,33 @@ export default {
});
});
- function _createButton() {
+ function _createButton(props) {
const openPopupBtn = document.createElement("button");
- openPopupBtn.classList.add(
+ const defaultClasses = [
"open-popup-link",
"btn-default",
"btn",
"btn-icon",
- "btn-expand-table",
- "no-text"
- );
- const expandIcon = create(
- iconNode("discourse-expand", { class: "expand-table-icon" })
- );
- openPopupBtn.title = I18n.t("fullscreen_table.expand_btn");
- openPopupBtn.append(expandIcon);
+ "no-text",
+ ];
+
+ openPopupBtn.classList.add(...defaultClasses);
+
+ if (props.classes) {
+ openPopupBtn.classList.add(...props.classes);
+ }
+
+ if (props.title) {
+ openPopupBtn.title = I18n.t(props.title);
+ }
+
+ if (props.icon) {
+ const icon = create(
+ iconNode(props.icon.name, { class: props.icon?.class })
+ );
+ openPopupBtn.append(icon);
+ }
+
return openPopupBtn;
}
@@ -128,14 +145,64 @@ export default {
return scrollWidth > clientWidth;
}
- function generateModal(event) {
+ function generateFullScreenTableModal(event) {
const table = event.currentTarget.parentElement.nextElementSibling;
const tempTable = table.cloneNode(true);
modal.show(FullscreenTableModal, { model: { tableHtml: tempTable } });
}
- function generatePopups(tables) {
- tables.forEach((table) => {
+ function generateSpreadsheetModal() {
+ const tableIndex = this.tableIndex;
+
+ return ajax(`/posts/${this.id}`, { type: "GET" })
+ .then((post) => {
+ parseAsync(post.raw).then((tokens) => {
+ const allTables = tokenRange(tokens, "table_open", "table_close");
+ const tableTokens = allTables[tableIndex];
+
+ modal.show(SpreadsheetEditor, {
+ model: {
+ post,
+ tableIndex,
+ tableTokens,
+ },
+ });
+ });
+ })
+ .catch(popupAjaxError);
+ }
+
+ function generatePopups(tables, attrs) {
+ tables.forEach((table, index) => {
+ const buttonWrapper = document.createElement("div");
+ buttonWrapper.classList.add("fullscreen-table-wrapper__buttons");
+
+ const tableEditorBtn = _createButton({
+ classes: ["btn-edit-table"],
+ title: "table_builder.edit.btn_edit",
+ icon: {
+ name: "pencil-alt",
+ class: "edit-table-icon",
+ },
+ });
+
+ table.parentNode.setAttribute("data-table-index", index);
+ table.parentNode.classList.add("fullscreen-table-wrapper");
+
+ if (attrs.canEdit) {
+ buttonWrapper.append(tableEditorBtn);
+ tableEditorBtn.addEventListener(
+ "click",
+ generateSpreadsheetModal.bind({
+ tableIndex: index,
+ ...attrs,
+ }),
+ false
+ );
+ }
+
+ table.parentNode.insertBefore(buttonWrapper, table);
+
if (!isOverflown(table.parentNode)) {
return;
}
@@ -144,28 +211,50 @@ export default {
return;
}
- const popupBtn = _createButton();
- table.parentNode.classList.add("fullscreen-table-wrapper");
- // Create a button wrapper for case of multiple buttons (i.e. table builder extension)
- const buttonWrapper = document.createElement("div");
- buttonWrapper.classList.add("fullscreen-table-wrapper--buttons");
- buttonWrapper.append(popupBtn);
- popupBtn.addEventListener("click", generateModal, false);
+ const expandTableBtn = _createButton({
+ classes: ["btn-expand-table"],
+ title: "fullscreen_table.expand_btn",
+ icon: { name: "discourse-expand", class: "expand-table-icon" },
+ });
+ buttonWrapper.append(expandTableBtn);
+ expandTableBtn.addEventListener(
+ "click",
+ generateFullScreenTableModal,
+ false
+ );
table.parentNode.insertBefore(buttonWrapper, table);
});
}
+ function cleanupPopupBtns() {
+ const editTableBtn = document.querySelector(
+ ".open-popup-link.btn-edit-table"
+ );
+ const expandTableBtn = document.querySelector(
+ ".open-popup-link.btn-expand-table"
+ );
+
+ expandTableBtn?.removeEventListener(
+ "click",
+ generateFullScreenTableModal
+ );
+ editTableBtn?.removeEventListener("click", generateSpreadsheetModal);
+ }
+
api.decorateCookedElement(
- (post) => {
+ (post, helper) => {
schedule("afterRender", () => {
- const tables = post.querySelectorAll("table");
- generatePopups(tables);
+ const tables = post.querySelectorAll(".md-table table");
+ generatePopups(tables, helper.widget.attrs);
});
},
{
onlyStream: true,
+ id: "table-wrapper",
}
);
+
+ api.cleanupStream(cleanupPopupBtns);
});
},
};
diff --git a/app/assets/javascripts/discourse/app/lib/utilities.js b/app/assets/javascripts/discourse/app/lib/utilities.js
index 9b5fe6235b9..9d6e9d7d5a9 100644
--- a/app/assets/javascripts/discourse/app/lib/utilities.js
+++ b/app/assets/javascripts/discourse/app/lib/utilities.js
@@ -613,3 +613,79 @@ export function getCaretPosition(element, options) {
return adjustedPosition;
}
+
+/**
+ * Generate markdown table from an array of objects
+ * Inspired by https://github.com/Ygilany/array-to-table
+ *
+ * @param {Array} array Array of objects
+ * @param {Array} columns Column headings
+ * @param {String} colPrefix Table column prefix
+ *
+ * @return {String} Markdown table
+ */
+export function arrayToTable(array, cols, colPrefix = "col") {
+ let table = "";
+
+ // Generate table headers
+ table += "|";
+ table += cols.join(" | ");
+ table += "|\r\n|";
+
+ // Generate table header separator
+ table += cols
+ .map(function () {
+ return "---";
+ })
+ .join(" | ");
+ table += "|\r\n";
+
+ // Generate table body
+ array.forEach(function (item) {
+ table += "|";
+
+ table +=
+ cols
+ .map(function (_key, index) {
+ return String(item[`${colPrefix}${index}`] || "").replace(
+ /\r?\n|\r/g,
+ " "
+ );
+ })
+ .join(" | ") + "|\r\n";
+ });
+
+ return table;
+}
+
+/**
+ *
+ * @returns a regular expression finding all markdown tables
+ */
+export function findTableRegex() {
+ return /((\r?){2}|^)(^\|[^\r\n]*(\r?\n)?)+(?=(\r?\n){2}|$)/gm;
+}
+
+export function tokenRange(tokens, start, end) {
+ const contents = [];
+ let startPushing = false;
+ let items = [];
+
+ tokens.forEach((token) => {
+ if (token.type === start) {
+ startPushing = true;
+ }
+
+ if (token.type === end) {
+ contents.push(items);
+ items = [];
+ startPushing = false;
+ }
+
+ if (startPushing) {
+ items.push(token);
+ }
+ });
+
+ return contents;
+}
diff --git a/app/assets/javascripts/discourse/app/services/composer.js b/app/assets/javascripts/discourse/app/services/composer.js
index b52d5f4de94..534a587dd54 100644
--- a/app/assets/javascripts/discourse/app/services/composer.js
+++ b/app/assets/javascripts/discourse/app/services/composer.js
@@ -9,6 +9,7 @@ import $ from "jquery";
import { Promise } from "rsvp";
import DiscardDraftModal from "discourse/components/modal/discard-draft";
import PostEnqueuedModal from "discourse/components/modal/post-enqueued";
+import SpreadsheetEditor from "discourse/components/modal/spreadsheet-editor";
import { categoryBadgeHTML } from "discourse/helpers/category-link";
import {
cannotPostAgain,
@@ -424,6 +425,14 @@ export default class ComposerService extends Service {
})
);
+ options.push(
+ this._setupPopupMenuOption({
+ action: "toggleSpreadsheet",
+ icon: "table",
+ label: "composer.insert_table",
+ })
+ );
+
return options.concat(
customPopupMenuOptions
.map((option) => this._setupPopupMenuOption(option))
@@ -723,6 +732,16 @@ export default class ComposerService extends Service {
this.toggleProperty("model.whisper");
}
+ @action
+ toggleSpreadsheet() {
+ this.modal.show(SpreadsheetEditor, {
+ model: {
+ toolbarEvent: this.toolbarEvent,
+ tableTokens: null,
+ },
+ });
+ }
+
@action
toggleInvisible() {
this.toggleProperty("model.unlistTopic");
diff --git a/app/assets/javascripts/discourse/tests/acceptance/post-table-wrapper-test.js b/app/assets/javascripts/discourse/tests/acceptance/post-table-wrapper-test.js
index 74d4b1719f5..38415c4c666 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/post-table-wrapper-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/post-table-wrapper-test.js
@@ -17,13 +17,13 @@ acceptance("Post Table Wrapper Test", function () {
assert.ok(
exists(
- `${postWithLargeTable} .fullscreen-table-wrapper .fullscreen-table-wrapper--buttons .open-popup-link`
+ `${postWithLargeTable} .fullscreen-table-wrapper .fullscreen-table-wrapper__buttons .open-popup-link`
),
"buttons for the table wrapper appear inside a separate div"
);
const fullscreenButtonWrapper = query(
- `${postWithLargeTable} .fullscreen-table-wrapper .fullscreen-table-wrapper--buttons`
+ `${postWithLargeTable} .fullscreen-table-wrapper .fullscreen-table-wrapper__buttons`
);
assert.strictEqual(
diff --git a/app/assets/javascripts/discourse/tests/acceptance/table-builder-test.js b/app/assets/javascripts/discourse/tests/acceptance/table-builder-test.js
new file mode 100644
index 00000000000..c509104c25c
--- /dev/null
+++ b/app/assets/javascripts/discourse/tests/acceptance/table-builder-test.js
@@ -0,0 +1,33 @@
+import { click, visit } from "@ember/test-helpers";
+import { test } from "qunit";
+import { acceptance, exists } from "discourse/tests/helpers/qunit-helpers";
+import selectKit from "discourse/tests/helpers/select-kit-helper";
+import I18n from "discourse-i18n";
+
+acceptance("Table Builder", function (needs) {
+ needs.user();
+
+ test("Can see table builder button when creating a topic", async function (assert) {
+ await visit("/");
+ await click("#create-topic");
+ await click(".d-editor-button-bar .options");
+ await selectKit(".toolbar-popup-menu-options").expand();
+
+ assert
+ .dom(`.select-kit-row[data-name='${I18n.t("composer.insert_table")}']`)
+ .exists("it shows the builder button");
+ });
+
+ test("Can see table builder button when editing post", async function (assert) {
+ await visit("/t/internationalization-localization/280");
+ await click("#post_1 .show-more-actions");
+ await click("#post_1 .edit");
+ assert.ok(exists("#reply-control"));
+ await click(".d-editor-button-bar .options");
+ await selectKit(".toolbar-popup-menu-options").expand();
+
+ assert
+ .dom(`.select-kit-row[data-name='${I18n.t("composer.insert_table")}']`)
+ .exists("it shows the builder button");
+ });
+});
diff --git a/app/assets/javascripts/discourse/tests/fixtures/md-table.js b/app/assets/javascripts/discourse/tests/fixtures/md-table.js
new file mode 100644
index 00000000000..cf410f5c6a4
--- /dev/null
+++ b/app/assets/javascripts/discourse/tests/fixtures/md-table.js
@@ -0,0 +1,3 @@
+export const mdTable = `|Make | Model | Year|\r\n|--- | --- | ---|\r\n|Toyota | Supra | 1998|\r\n|Nissan | Skyline | 1999|\r\n|Honda | S2000 | 2001|\r\n`;
+export const mdTableSpecialChars = `|Make | Model | Price|\r\n|--- | --- | ---|\r\n|Toyota | Supra | $50,000|\r\n| | Celica | $20,000|\r\n|Nissan | GTR | $80,000|\r\n`;
+export const mdTableNonUniqueHeadings = `|col1 | col2 | col1|\r\n|--- | --- | ---|\r\n|Col A | Col B | Col C|\r\n`;
diff --git a/app/assets/javascripts/discourse/tests/unit/lib/utilities-test.js b/app/assets/javascripts/discourse/tests/unit/lib/utilities-test.js
index b1c379fe57c..4019f393d80 100644
--- a/app/assets/javascripts/discourse/tests/unit/lib/utilities-test.js
+++ b/app/assets/javascripts/discourse/tests/unit/lib/utilities-test.js
@@ -6,6 +6,7 @@ import Handlebars from "handlebars";
import { module, test } from "qunit";
import sinon from "sinon";
import {
+ arrayToTable,
caretRowCol,
clipboardCopyAsync,
defaultHomepage,
@@ -13,6 +14,7 @@ import {
escapeExpression,
extractDomainFromUrl,
fillMissingDates,
+ findTableRegex,
inCodeBlock,
initializeDefaultHomepage,
mergeSortedLists,
@@ -22,6 +24,11 @@ import {
slugify,
toAsciiPrintable,
} from "discourse/lib/utilities";
+import {
+ mdTable,
+ mdTableNonUniqueHeadings,
+ mdTableSpecialChars,
+} from "discourse/tests/fixtures/md-table";
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import { chromeTest } from "discourse/tests/helpers/qunit-helpers";
@@ -373,3 +380,158 @@ module("Unit | Utilities | clipboard", function (hooks) {
}
);
});
+
+module("Unit | Utilities | table-builder", function (hooks) {
+ setupTest(hooks);
+
+ test("arrayToTable", function (assert) {
+ const tableData = [
+ {
+ col0: "Toyota",
+ col1: "Supra",
+ col2: "1998",
+ },
+ {
+ col0: "Nissan",
+ col1: "Skyline",
+ col2: "1999",
+ },
+ {
+ col0: "Honda",
+ col1: "S2000",
+ col2: "2001",
+ },
+ ];
+
+ assert.strictEqual(
+ arrayToTable(tableData, ["Make", "Model", "Year"]),
+ mdTable,
+ "it creates a markdown table from an array of objects (with headers as keys)"
+ );
+
+ const specialCharsTableData = [
+ {
+ col0: "Toyota",
+ col1: "Supra",
+ col2: "$50,000",
+ },
+ {
+ col0: "",
+ col1: "Celica",
+ col2: "$20,000",
+ },
+ {
+ col0: "Nissan",
+ col1: "GTR",
+ col2: "$80,000",
+ },
+ ];
+
+ assert.strictEqual(
+ arrayToTable(specialCharsTableData, ["Make", "Model", "Price"]),
+ mdTableSpecialChars,
+ "it creates a markdown table with special characters in correct alignment"
+ );
+
+ const nonUniqueColumns = ["col1", "col2", "col1"];
+
+ assert.strictEqual(
+ arrayToTable(
+ [{ col0: "Col A", col1: "Col B", col2: "Col C" }],
+ nonUniqueColumns
+ ),
+ mdTableNonUniqueHeadings,
+ "it does not suppress a column if heading is the same as another column"
+ );
+ });
+ test("arrayToTable with custom column prefix", function (assert) {
+ const tableData = [
+ {
+ A0: "hey",
+ A1: "you",
+ },
+ {
+ A0: "over",
+ A1: "there",
+ },
+ ];
+
+ assert.strictEqual(
+ arrayToTable(tableData, ["Col 1", "Col 2"], "A"),
+ `|Col 1 | Col 2|\r\n|--- | ---|\r\n|hey | you|\r\n|over | there|\r\n`,
+ "it works"
+ );
+ });
+
+ test("arrayToTable returns valid table with multiline cell data", function (assert) {
+ const tableData = [
+ {
+ col0: "Jane\nDoe",
+ col1: "Teri",
+ },
+ {
+ col0: "Finch",
+ col1: "Sami",
+ },
+ ];
+
+ assert.strictEqual(
+ arrayToTable(tableData, ["Col 1", "Col 2"]),
+ `|Col 1 | Col 2|\r\n|--- | ---|\r\n|Jane Doe | Teri|\r\n|Finch | Sami|\r\n`,
+ "it creates a valid table"
+ );
+ });
+
+ test("findTableRegex", function (assert) {
+ const oneTable = `|Make|Model|Year|\r\n|--- | --- | ---|\r\n|Toyota|Supra|1998|`;
+
+ assert.strictEqual(
+ oneTable.match(findTableRegex()).length,
+ 1,
+ "finds one table in markdown"
+ );
+
+ const threeTables = `## Heading
+|Table1 | PP Port | Device | DP | Medium|
+|--- | --- | --- | --- | ---|
+| Something | (1+2) | Dude | Mate | Bro |
+
+|Table2 | PP Port | Device | DP | Medium|
+|--- | --- | --- | --- | ---|
+| Something | (1+2) | Dude | Mate | Bro |
+| ✅ | (1+2) | Dude | Mate | Bro |
+| ✅ | (1+2) | Dude | Mate | Bro |
+
+|Table3 | PP Port | Device | DP |
+|--- | --- | --- | --- |
+| Something | (1+2) | Dude | Sound |
+| | (1+2) | Dude | OW |
+| | (1+2) | Dude | OI |
+
+Random extras
+ `;
+
+ assert.strictEqual(
+ threeTables.match(findTableRegex()).length,
+ 3,
+ "finds three tables in markdown"
+ );
+
+ const ignoreUploads = `
+:information_source: Something
+
+[details=Example of a cross-connect in Equinix]
+
+[/details]
+
+|Table1 | PP Port | Device | DP | Medium|
+|--- | --- | --- | --- | ---|
+| Something | (1+2) | Dude | Mate | Bro |
+`;
+ assert.strictEqual(
+ ignoreUploads.match(findTableRegex()).length,
+ 1,
+ "finds on table, ignoring upload markup"
+ );
+ });
+});
diff --git a/app/assets/stylesheets/common.scss b/app/assets/stylesheets/common.scss
index 18c72861311..dc113567074 100644
--- a/app/assets/stylesheets/common.scss
+++ b/app/assets/stylesheets/common.scss
@@ -19,3 +19,4 @@
@import "common/loading-slider";
@import "common/float-kit/_index";
@import "common/login/_index";
+@import "common/table-builder/_index";
diff --git a/app/assets/stylesheets/common/base/topic-post.scss b/app/assets/stylesheets/common/base/topic-post.scss
index 73131be273c..3ee5ca6638d 100644
--- a/app/assets/stylesheets/common/base/topic-post.scss
+++ b/app/assets/stylesheets/common/base/topic-post.scss
@@ -1648,10 +1648,13 @@ iframe {
}
.open-popup-link {
+ display: inline;
+ margin-inline: 0.25em;
position: sticky;
left: 1rem;
opacity: 0%;
white-space: nowrap;
+ transition: 0.25s ease-in-out opacity;
}
.fullscreen-table-wrapper {
@@ -1659,7 +1662,7 @@ iframe {
display: block;
position: relative;
- &--buttons {
+ &__buttons {
position: absolute;
top: 0.5rem;
right: 0.5rem;
diff --git a/app/assets/stylesheets/common/table-builder/_index.scss b/app/assets/stylesheets/common/table-builder/_index.scss
new file mode 100644
index 00000000000..34264181789
--- /dev/null
+++ b/app/assets/stylesheets/common/table-builder/_index.scss
@@ -0,0 +1,5 @@
+@import "vendor/jspreadsheet";
+@import "vendor/jsuites";
+@import "jspreadsheet-theme";
+@import "table-edit-decorator";
+@import "insert-table-modal";
diff --git a/app/assets/stylesheets/common/table-builder/insert-table-modal.scss b/app/assets/stylesheets/common/table-builder/insert-table-modal.scss
new file mode 100644
index 00000000000..60fe5f7dd73
--- /dev/null
+++ b/app/assets/stylesheets/common/table-builder/insert-table-modal.scss
@@ -0,0 +1,78 @@
+.btn-insert-table {
+ background: var(--tertiary);
+ color: var(--secondary);
+
+ .d-icon {
+ color: var(--secondary);
+ }
+
+ .discourse-no-touch & {
+ &:hover {
+ background-color: var(--tertiary-hover);
+ color: var(--secondary);
+
+ .d-icon {
+ color: var(--secondary);
+ }
+ }
+ }
+}
+
+.insert-table-modal {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+
+ .d-modal__container,
+ .modal-inner-container {
+ --modal-max-width: $reply-area-max-width;
+ width: 100%;
+ height: 100%;
+ max-height: unset;
+
+ display: grid;
+ grid-template-rows: auto 1fr auto;
+ }
+
+ .modal-body {
+ padding: 0;
+ margin: 0;
+ }
+
+ .d-modal__footer,
+ .modal-footer {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+
+ .secondary-actions {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+
+ .edit-reason {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ }
+
+ .btn {
+ margin-right: 0;
+ }
+
+ input {
+ margin: 0;
+ }
+ }
+
+ .secondary-actions .tippy-content {
+ h4 {
+ color: var(--primary);
+ }
+ li {
+ margin-block: 0.25rem;
+ color: var(--primary-high);
+ }
+ }
+ }
+}
diff --git a/app/assets/stylesheets/common/table-builder/jspreadsheet-theme.scss b/app/assets/stylesheets/common/table-builder/jspreadsheet-theme.scss
new file mode 100644
index 00000000000..a05f4552798
--- /dev/null
+++ b/app/assets/stylesheets/common/table-builder/jspreadsheet-theme.scss
@@ -0,0 +1,272 @@
+:root {
+ --jexcel_header_color: var(--primary-high);
+ --jexcel_header_color_highlighted: var(--primary-high);
+ --jexcel_header_background: var(--primary-very-low);
+ --jexcel_header_background_highlighted: var(--primary-low);
+
+ --jexcel_content_color: var(--primary);
+ --jexcel_content_color_highlighted: var(--primary-high);
+ --jexcel_content_background: var(--secondary);
+ --jexcel_content_background_highlighted: var(--tertiary-very-low);
+
+ --jexcel_menu_background: var(--secondary);
+ --jexcel_menu_background_highlighted: var(--secondary-very-high);
+ --jexcel_menu_color: var(--primary-medium);
+ --jexcel_menu_color_highlighted: var(--primary);
+
+ --jexcel_border_color: var(--primary-low-mid);
+ --jexcel_border_color_highlighted: var(--tertiary-high);
+
+ --active_color: var(--primary-very-low);
+ --active-color: var(--active_color);
+}
+
+.jexcel {
+ border-bottom: 1px solid var(--jexcel_border_color);
+ border-right: transparent;
+ background-color: transparent;
+}
+
+table.jexcel > thead > tr > td {
+ border-top: 1px solid transparent;
+ border-left: 1px solid transparent;
+ border-right: 1px solid transparent;
+ border-bottom: 1px solid #000;
+ background-color: #fff;
+ padding: 10px;
+ font-weight: bold;
+}
+table.jexcel > tbody > tr > td {
+ padding: 8px;
+ border-right: 1px solid transparent;
+ border-left: 1px solid transparent;
+}
+
+table.jexcel {
+ border-bottom: 1px solid var(--jexcel_border_color);
+}
+
+.jcontextmenu.jexcel_contextmenu hr {
+ border-color: var(--jexcel_border_color);
+}
+
+.jexcel_container .jcontextmenu > div a {
+ color: var(--jexcel_menu_color);
+}
+
+.jexcel_corner {
+ background-color: var(--tertiary);
+}
+
+.jexcel > tbody > tr > td,
+.jexcel > thead > tr > td {
+ border-top: 1px solid var(--jexcel_border_color);
+ border-left: 1px solid var(--jexcel_border_color);
+ background-color: var(--jexcel_content_background);
+ color: var(--jexcel_content_color);
+}
+
+.jexcel > tbody > tr > td:first-child,
+.jexcel > thead > tr > td {
+ background-color: var(--jexcel_header_background);
+ color: var(--jexcel_header_color);
+}
+
+.jexcel > thead > tr > td.selected,
+.jexcel > tbody > tr.selected > td:first-child {
+ background-color: var(--jexcel_header_background_highlighted);
+ color: var(--jexcel_header_color_highlighted);
+}
+
+table.jexcel > tbody > tr > td:first-child {
+ background-color: var(--jexcel_header_background);
+}
+
+table.jexcel > tbody > tr.selected > td:first-child {
+ background-color: var(--jexcel_header_background_highlighted);
+}
+
+.jexcel > tbody > tr > td.jexcel_cursor a {
+ color: var(--active-color);
+}
+
+.jexcel_pagination > div > div {
+ color: var(--jexcel_header_color);
+ background: var(--jexcel_header_background);
+ border: 1px solid var(--jexcel_border_color);
+}
+
+.jexcel_page,
+.jexcel_container input,
+.jexcel_container select {
+ color: var(--jexcel_header_color);
+ background: var(--jexcel_header_background);
+ border: 1px solid var(--jexcel_border_color);
+}
+
+.jexcel_contextmenu.jcontextmenu {
+ border: 1px solid var(--jexcel_border_color);
+ background: var(--jexcel_menu_background);
+ color: var(--jexcel_menu_color);
+ box-shadow: 0 12px 12px rgba(0, 0, 0, 0.15);
+}
+
+.jcontextmenu > div a {
+ color: var(--jexcel_menu_color);
+}
+
+.jcontextmenu > div:not(.contextmenu-line):hover a {
+ color: var(--jexcel_menu_color_highlighted);
+}
+
+.jcontextmenu > div:not(.contextmenu-line):hover {
+ background: var(--jexcel_menu_background_highlighted);
+}
+
+.jexcel_dropdown .jdropdown-container,
+.jexcel_dropdown .jdropdown-content {
+ background-color: var(--jexcel_content_background);
+ color: var(--jexcel_content_color);
+}
+
+.jexcel_dropdown .jdropdown-item {
+ color: var(--jexcel_content_color);
+}
+
+.jexcel_dropdown .jdropdown-item:hover,
+.jexcel_dropdown .jdropdown-selected,
+.jexcel_dropdown .jdropdown-cursor {
+ background-color: var(--jexcel_content_background_highlighted);
+ color: var(--jexcel_content_color_highlighted);
+}
+
+.jexcel .jcalendar-content {
+ background-color: var(--jexcel_header_background);
+ color: var(--jexcel_header_color);
+}
+
+.jexcel .jcalendar-content > table {
+ background-color: var(--jexcel_content_background);
+ color: var(--jexcel_content_color);
+}
+
+.jexcel .jcalendar-weekday {
+ background-color: var(--jexcel_content_background_highlighted);
+ color: var(--jexcel_content_color_highlighted);
+}
+
+.jexcel .jcalendar-sunday {
+ color: var(--jexcel_header_color);
+}
+
+.jexcel .jcalendar-selected {
+ background-color: var(--jexcel_content_background_highlighted);
+ color: var(--jexcel_content_color_highlighted);
+}
+
+.jexcel_toolbar i.jexcel_toolbar_item {
+ color: var(--jexcel_content_color);
+}
+
+.jexcel_toolbar i.jexcel_toolbar_item:hover {
+ background: var(--jexcel_content_background_highlighted);
+ color: var(--jexcel_content_color_highlighted);
+}
+
+.jexcel_toolbar {
+ background: var(--jexcel_header_background);
+}
+
+.jexcel_content::-webkit-scrollbar-track {
+ background: var(--jexcel_background_head);
+}
+
+.jexcel_content::-webkit-scrollbar-thumb {
+ background: var(--jexcel_background_head_highlighted);
+}
+
+.jexcel_border_main {
+ border: 1px solid #000;
+ border-color: var(--jexcel_border_color_highlighted);
+}
+
+.jexcel .highlight {
+ background-color: var(--jexcel_content_background_highlighted);
+}
+
+.jexcel .highlight-bottom {
+ border-bottom: 1.5px solid var(--jexcel_border_color_highlighted);
+}
+.jexcel .highlight-right {
+ border-right: 1.5px solid var(--jexcel_border_color_highlighted);
+}
+.jexcel .highlight-left {
+ border-left: 1.5px solid var(--jexcel_border_color_highlighted);
+}
+.jexcel .highlight-top {
+ border-top: 1.5px solid var(--jexcel_border_color_highlighted);
+}
+.jexcel .copying-top {
+ border-top-color: var(--jexcel_border_color_highlighted);
+}
+.jexcel .copying-right {
+ border-right-color: var(--jexcel_border_color_highlighted);
+}
+.jexcel .copying-left {
+ border-left-color: var(--jexcel_border_color_highlighted);
+}
+.jexcel .copying-bottom {
+ border-bottom-color: var(--jexcel_border_color_highlighted);
+}
+.jexcel_border_main,
+.jexcel .highlight-top.highlight-left,
+.jexcel .highlight-top,
+.jexcel .highlight-left {
+ -webkit-box-shadow: unset;
+ box-shadow: unset;
+}
+
+table.jexcel > thead > tr > td {
+ border-top: 1px solid var(--jexcel_border_color);
+ border-right: 1px solid var(--jexcel_border_color);
+ border-bottom: 1px solid var(--jexcel_border_color);
+ background-color: var(--jexcel_header_background);
+
+ &:first-child {
+ border-left: 1px solid var(--jexcel_border_color);
+ }
+}
+
+table.jexcel > thead > tr > td.selected {
+ background-color: var(--jexcel_header_background_highlighted);
+ color: var(--jexcel_header_color_highlighted);
+}
+
+table.jexcel > tbody > tr > td {
+ border-right: 1px solid var(--jexcel_border_color);
+
+ &:first-child {
+ border-left: 1px solid var(--jexcel_border_color);
+ }
+}
+
+// Hides about item in context menu
+.jcontextmenu > div:not(.contextmenu-line):last-child {
+ display: none;
+}
+
+.jexcel_container {
+ padding: 0.5em;
+ min-width: 100%;
+ .jexcel_content {
+ min-width: 100%;
+ padding: 0;
+ table.jexcel {
+ min-width: 100%;
+ }
+ }
+}
+
+.jexcel_container {
+ padding: 0;
+}
diff --git a/app/assets/stylesheets/common/table-builder/table-edit-decorator.scss b/app/assets/stylesheets/common/table-builder/table-edit-decorator.scss
new file mode 100644
index 00000000000..3ee65d9e2b2
--- /dev/null
+++ b/app/assets/stylesheets/common/table-builder/table-edit-decorator.scss
@@ -0,0 +1,36 @@
+.btn-edit-table {
+ -webkit-user-select: none;
+ -webkit-touch-callout: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.fullscreen-table-wrapper:hover .fullscreen-table-wrapper__buttons button {
+ opacity: 100%;
+}
+
+.mobile-view {
+ .btn-edit-table {
+ display: none;
+ z-index: 2;
+ position: absolute;
+ top: 1rem;
+ left: 1rem;
+ }
+
+ .fullscreen-table-wrapper {
+ position: relative;
+
+ &:hover {
+ table {
+ opacity: 0.5;
+ }
+
+ .btn-edit-table {
+ display: block;
+ z-index: 2;
+ }
+ }
+ }
+}
diff --git a/app/assets/stylesheets/common/table-builder/vendor/jspreadsheet.scss b/app/assets/stylesheets/common/table-builder/vendor/jspreadsheet.scss
new file mode 100644
index 00000000000..92572f0831f
--- /dev/null
+++ b/app/assets/stylesheets/common/table-builder/vendor/jspreadsheet.scss
@@ -0,0 +1,699 @@
+:root {
+ --jexcel-border-color: #000;
+}
+
+.jexcel_container {
+ display: inline-block;
+ padding-right: 2px;
+ box-sizing: border-box;
+ overscroll-behavior: contain;
+ outline: none;
+}
+
+.jexcel_container.fullscreen {
+ position: fixed;
+ top: 0px;
+ left: 0px;
+ width: 100%;
+ height: 100%;
+ z-index: 21;
+}
+
+.jexcel_container.fullscreen .jexcel_content {
+ overflow: auto;
+ width: 100%;
+ height: 100%;
+ background-color: #ffffff;
+}
+
+.jexcel_container.with-toolbar .jexcel > thead > tr > td {
+ top: 0;
+}
+
+.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: 8px;
+ height: 8px;
+}
+
+.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;
+}
+
+.jexcel_container.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: relative;
+ 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_overflow > tbody > tr > td {
+ overflow: hidden;
+}
+
+.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;
+ padding-right: 2px;
+ 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);
+}
+.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: 20;
+ 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: 0.875rem;
+ letter-spacing: 0.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: 0.875rem;
+ letter-spacing: 0.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: 8px;
+ 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 4px 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: 10px;
+ padding-left: 20px;
+ padding-right: 20px;
+ margin-right: 5px;
+ margin-bottom: 5px;
+ 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 > tfoot > 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,
+.jexcel tfoot .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;
+}
+
+.red {
+ color: red;
+}
+
+.jexcel > tbody > tr > td.readonly > input[type="checkbox"],
+.jexcel > tbody > tr > td.readonly > input[type="radio"] {
+ pointer-events: none;
+ opacity: 0.5;
+}
diff --git a/app/assets/stylesheets/common/table-builder/vendor/jsuites.scss b/app/assets/stylesheets/common/table-builder/vendor/jsuites.scss
new file mode 100644
index 00000000000..dcbd6c05b85
--- /dev/null
+++ b/app/assets/stylesheets/common/table-builder/vendor/jsuites.scss
@@ -0,0 +1,3038 @@
+.jexcel_container {
+ :root {
+ --button-color: #298ba8;
+ --active-color: #007aff;
+ --safe-area-top: env(safe-area-inset-top);
+ --safe-area-bottom: env(safe-area-inset-bottom);
+ }
+
+ [data-visible="false"] {
+ display: none;
+ }
+
+ div[data-before]:before {
+ content: attr(data-before);
+ }
+
+ .unselectable {
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ }
+
+ .jreadonly {
+ pointer-events: none;
+ }
+
+ .jdragging {
+ opacity: 0.2;
+ filter: alpha(opacity=20);
+ }
+
+ .jupload.input {
+ position: relative;
+ box-sizing: border-box;
+ background-size: initial;
+ height: 33px;
+ min-height: initial;
+ padding: 6px;
+ padding-right: 30px;
+ }
+
+ .jupload.input:before {
+ content: "save";
+ font-size: 18px;
+ font-family: "Material Icons";
+ color: #000;
+ position: absolute;
+ right: 5px;
+ }
+
+ .jupload:empty:before {
+ z-index: 0;
+ }
+
+ .jupload img {
+ width: 100%;
+ }
+
+ .jupload.input img {
+ width: initial;
+ max-width: 100%;
+ height: 100%;
+ }
+
+ .jupload[data-multiple] {
+ padding: 10px;
+ }
+
+ .jupload[data-multiple] img {
+ height: 70px;
+ width: 100px;
+ object-fit: cover;
+ margin-right: 5px;
+ margin-bottom: 5px;
+ }
+
+ .jupload {
+ position: relative;
+ border: 1px dotted #eee;
+ cursor: pointer;
+ box-sizing: border-box;
+ width: 100%;
+ max-width: 100%;
+ max-height: 100%;
+ min-height: 180px;
+ }
+
+ .jupload:not(.input):before {
+ content: "\e2c3";
+ font-family: "Material Icons";
+ font-size: 90px;
+ color: #eee;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: absolute;
+ z-index: -1;
+ }
+
+ .jupload-item {
+ padding-right: 22px;
+ border-radius: 1px;
+ display: inline-block;
+ position: relative;
+ }
+
+ .jphoto {
+ position: relative;
+ border: 1px dotted #eee;
+ cursor: pointer;
+ box-sizing: border-box;
+ width: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .jphoto:empty:before {
+ content: "\e2c3";
+ font-family: "Material Icons";
+ font-size: 90px;
+ color: #eee;
+ width: 100%;
+ height: 100%;
+ text-align: center;
+ }
+
+ .jremove {
+ opacity: 0.2;
+ filter: alpha(opacity=20);
+ }
+
+ .round img {
+ border-radius: 1000px;
+ }
+
+ .jtooltip {
+ position: fixed;
+ top: 10px;
+ left: 10px;
+ z-index: 5;
+
+ font-family: initial;
+ font-size: 12px;
+ color: #000;
+ background-color: #fff;
+ border: 1px solid black;
+ padding: 8px;
+ margin: 10px;
+
+ display: block;
+ animation: fadeIn 0.5s;
+ pointer-events: none;
+ }
+
+ .jtooltip:empty {
+ display: none;
+ }
+
+ @keyframes fadeIn {
+ 0% {
+ opacity: 0;
+ }
+ 100% {
+ opacity: 1;
+ }
+ }
+ /** Loading */
+ .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 0.8s linear infinite;
+ animation: spin 0.8s linear infinite;
+ }
+
+ .jloading.spin {
+ background-color: transparent;
+ }
+
+ .jloading.spin::after {
+ margin: 0 auto;
+ margin-top: 80px;
+ border-color: #aaa;
+ border-top-color: transparent;
+ }
+
+ /** Animations **/
+ .fade-in {
+ animation: fade-in 2s forwards;
+ }
+
+ .fade-out {
+ animation: fade-out 1s forwards;
+ }
+
+ .slide-left-in {
+ position: relative;
+ animation: slide-left-in 0.4s forwards;
+ }
+
+ .slide-left-out {
+ position: relative;
+ animation: slide-left-out 0.4s forwards;
+ }
+
+ .slide-right-in {
+ position: relative;
+ animation: slide-right-in 0.4s forwards;
+ }
+
+ .slide-right-out {
+ position: relative;
+ animation: slide-right-out 0.4s forwards;
+ }
+
+ .slide-top-in {
+ position: relative;
+ animation: slide-top-in 0.4s forwards;
+ }
+
+ .slide-top-out {
+ position: relative;
+ animation: slide-top-out 0.2s forwards;
+ }
+
+ .slide-bottom-in {
+ position: relative;
+ animation: slide-bottom-in 0.4s forwards;
+ }
+
+ .slide-bottom-out {
+ position: relative;
+ animation: slide-bottom-out 0.1s forwards;
+ }
+
+ .slide-left-in > div {
+ -webkit-transform: translateZ(0px);
+ -webkit-transform: translate3d(0, 0, 0);
+ }
+
+ .slide-left-out > div {
+ -webkit-transform: translateZ(0px);
+ -webkit-transform: translate3d(0, 0, 0);
+ }
+
+ .slide-right-in > div {
+ -webkit-transform: translateZ(0px);
+ -webkit-transform: translate3d(0, 0, 0);
+ }
+
+ .slide-right-out > div {
+ -webkit-transform: translateZ(0px);
+ -webkit-transform: translate3d(0, 0, 0);
+ }
+
+ .spin {
+ animation: spin 2s infinite linear;
+ }
+
+ /** 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% {
+ left: -100%;
+ }
+ 100% {
+ left: 0%;
+ }
+ }
+
+ @-webkit-keyframes slide-left-in {
+ 0% {
+ left: -100%;
+ }
+ 100% {
+ left: 0%;
+ }
+ }
+
+ @keyframes slide-left-out {
+ 0% {
+ left: 0%;
+ }
+ 100% {
+ left: -100%;
+ }
+ }
+
+ @-webkit-keyframes slide-left-out {
+ 0% {
+ left: 0%;
+ }
+ 100% {
+ left: -100%;
+ }
+ }
+
+ /** Keyframes Right to Left **/
+ @keyframes slide-right-in {
+ 0% {
+ left: 100%;
+ }
+ 100% {
+ left: 0%;
+ }
+ }
+
+ @-webkit-keyframes slide-right-in {
+ 0% {
+ left: 100%;
+ }
+ 100% {
+ left: 0%;
+ }
+ }
+
+ @keyframes slide-right-out {
+ 0% {
+ left: 0%;
+ }
+ 100% {
+ left: 100%;
+ }
+ }
+
+ @-webkit-keyframes slide-right-out {
+ 0% {
+ left: 0%;
+ }
+ 100% {
+ left: 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%);
+ }
+ }
+
+ @-webkit-keyframes spin {
+ from {
+ -webkit-transform: rotate(0deg);
+ }
+ to {
+ -webkit-transform: rotate(359deg);
+ }
+ }
+
+ @keyframes spin {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(359deg);
+ }
+ }
+ .jcalendar {
+ position: absolute;
+ 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.jcalendar-focus {
+ display: block;
+ }
+
+ .jcalendar .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 .jcalendar-container {
+ position: relative;
+ box-sizing: border-box;
+ }
+
+ .jcalendar .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-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=%27http://www.w3.org/2000/svg%27 width=%2724%27 height=%2724%27 viewBox=%270 0 24 24%27%3E%3Cpath d=%27M15.41 16.59L10.83 12l4.58-4.59L14 6l-6 6 6 6 1.41-1.41z%27 fill=%27%23000%27 /%3E%3Cpath fill=%27none%27 d=%27M0 0h24v24H0V0z%27/%3E%3C/svg%3E");
+ background-position: center;
+ background-repeat: no-repeat;
+ }
+
+ .jcalendar-next {
+ cursor: pointer;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2724%27 height=%2724%27 viewBox=%270 0 24 24%27%3E%3Cpath d=%27M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z%27 fill=%27%23000%27 /%3E%3Cpath fill=%27none%27 d=%27M0 0h24v24H0V0z%27/%3E%3C/svg%3E");
+ background-position: center;
+ background-repeat: no-repeat;
+ }
+
+ .jcalendar-weekday {
+ font-weight: 600;
+ background-color: #fcfcfc;
+ padding: 14px;
+ }
+
+ .jcalendar-table {
+ padding: 10px;
+ }
+
+ .jcalendar-table > table {
+ width: 100%;
+ background-color: #fff;
+ }
+
+ .jcalendar-table > table > thead {
+ cursor: pointer;
+ }
+
+ .jcalendar-table thead td {
+ padding: 10px;
+ height: 40px;
+ }
+
+ .jcalendar-table > table > tbody > tr {
+ height: 34px;
+ }
+
+ .jcalendar-table > table > tbody td {
+ box-sizing: border-box;
+ cursor: pointer;
+ padding: 9px;
+ font-size: 0.9em;
+ }
+
+ .jcalendar-table 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=%27http://www.w3.org/2000/svg%27 width=%2716%27 height=%2716%27 viewBox=%270 0 24 24%27 fill=%27gray%27%3E%3Cpath d=%27M20 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%27/%3E%3Cpath fill=%27none%27 d=%27M0 0h24v24H0z%27/%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-container select {
+ width: 55px;
+ display: inline-block;
+ border: 0px;
+ padding: 4px;
+ text-align: center;
+ font-size: 1.1em;
+ user-select: none;
+ margin-right: 10px;
+ }
+
+ .jcalendar-container 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;
+ }
+
+ .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;
+ }
+
+ .jcalendar-time {
+ display: flex;
+ }
+
+ .jcalendar_warning {
+ color: red;
+ }
+
+ .jcalendar-hide-controls .jcalendar-controls {
+ display: none;
+ }
+
+ .jcolor {
+ display: none;
+ outline: none;
+ position: absolute;
+ }
+
+ .jcolor-input {
+ padding-right: 24px !important;
+ background: url("data:image/svg+xml,%0A%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 24 24%27 fill=%27black%27 width=%2718px%27 height=%2718px%27%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3Cpath d=%27M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9c.83 0 1.5-.67 1.5-1.5 0-.39-.15-.74-.39-1.01-.23-.26-.38-.61-.38-.99 0-.83.67-1.5 1.5-1.5H16c2.76 0 5-2.24 5-5 0-4.42-4.03-8-9-8zm-5.5 9c-.83 0-1.5-.67-1.5-1.5S5.67 9 6.5 9 8 9.67 8 10.5 7.33 12 6.5 12zm3-4C8.67 8 8 7.33 8 6.5S8.67 5 9.5 5s1.5.67 1.5 1.5S10.33 8 9.5 8zm5 0c-.83 0-1.5-.67-1.5-1.5S13.67 5 14.5 5s1.5.67 1.5 1.5S15.33 8 14.5 8zm3 4c-.83 0-1.5-.67-1.5-1.5S16.67 9 17.5 9s1.5.67 1.5 1.5-.67 1.5-1.5 1.5z%27/%3E%3C/svg%3E")
+ top 50% right 4px no-repeat,
+ content-box;
+ box-sizing: border-box;
+ }
+
+ .jcolor-content {
+ position: absolute;
+ z-index: 9000;
+ user-select: none;
+ -webkit-font-smoothing: antialiased;
+ font-size: 0.875rem;
+ letter-spacing: 0.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;
+ min-width: 260px;
+ }
+
+ .jmodal .jcolor-content {
+ position: fixed;
+ }
+
+ .jcolor-controls {
+ display: flex;
+ padding: 10px;
+ border-bottom: 1px solid #eee;
+ margin-bottom: 5px;
+ }
+
+ .jcolor-controls div {
+ flex: 1;
+ font-size: 1em;
+ color: var(--active-color);
+ text-transform: uppercase;
+ font-weight: bold;
+ box-sizing: border-box;
+ }
+
+ .jcolor-content table {
+ border-collapse: collapse;
+ box-sizing: border-box;
+ }
+
+ .jcolor-focus {
+ display: block;
+ }
+
+ .jcolor table {
+ width: 100%;
+ height: 100%;
+ min-height: 160px;
+ }
+
+ .jcolor td {
+ padding: 7px;
+ }
+
+ .jcolor-selected {
+ background-repeat: no-repeat;
+ background-size: 16px;
+ background-position: center;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2724%27 height=%2724%27 viewBox=%270 0 24 24%27%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3Cpath d=%27M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z%27 fill=%27white%27/%3E%3C/svg%3E");
+ }
+
+ .jcolor-fullscreen {
+ position: fixed;
+ left: 0px;
+ bottom: 0px;
+ width: 100%;
+ max-height: 290px;
+ border-radius: 0px;
+ box-sizing: border-box;
+ }
+
+ .jcolor-fullscreen .jcolor-controls {
+ padding: 15px;
+ -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);
+ }
+
+ .jcolor-reset {
+ text-align: left;
+ }
+
+ .jcolor-close {
+ text-align: right;
+ }
+
+ .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 */
+ }
+
+ .jcolor-content .jtabs-content {
+ padding: 7px;
+ }
+
+ .jcolor-grid tr:first-child > td:first-child {
+ border-top-left-radius: 3px;
+ }
+
+ .jcolor-grid tr:first-child > td:last-child {
+ border-top-right-radius: 3px;
+ }
+
+ .jcolor-grid tr:last-child > td:first-child {
+ border-bottom-left-radius: 3px;
+ }
+
+ .jcolor-grid tr:last-child > td:last-child {
+ border-bottom-right-radius: 3px;
+ }
+
+ .jcolor-hsl {
+ box-sizing: border-box;
+ }
+
+ .jcolor-hsl > div {
+ height: 100%;
+ position: relative;
+ }
+
+ .jcolor-hsl canvas {
+ display: block;
+ border-radius: 4px;
+ -webkit-user-drag: none;
+ }
+
+ .jcolor-point {
+ height: 5px;
+ width: 5px;
+ background-color: #000;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ border-radius: 50%;
+ }
+
+ .jcolor-sliders {
+ padding: 10px 20px 10px 10px;
+ }
+
+ .jcolor-sliders input {
+ -webkit-appearance: none;
+
+ height: 12px;
+ width: 80%;
+
+ background: #d3d3d3;
+ opacity: 1;
+
+ border-radius: 30px;
+ outline: none;
+ }
+
+ .jcolor-sliders-input-subcontainer {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ .jcolor-sliders-input-container {
+ margin-top: 4px;
+ line-height: 0.8em;
+ text-align: left;
+ }
+
+ .jcolor-sliders-input-container > label {
+ font-size: 10px;
+ text-transform: uppercase;
+ color: #bbbbbd;
+ }
+
+ .jcolor-sliders-input-subcontainer > input {
+ border: 0px;
+ padding: 1px;
+ }
+
+ .jcolor-sliders-input-container input::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ height: 12px;
+ width: 12px;
+ border-radius: 50%;
+ background: #000;
+ border: 2px solid #fff;
+ cursor: pointer;
+ }
+
+ .jcolor-sliders-input-container input::-moz-range-thumb {
+ -webkit-appearance: none;
+ height: 12px;
+ width: 12px;
+ border-radius: 50%;
+ background: #000;
+ border: 2px solid #fff;
+ cursor: pointer;
+ }
+
+ .jcolor-sliders-final-color {
+ padding: 6px;
+ user-select: all;
+ margin-top: 10px;
+ text-align: center;
+ }
+
+ .jcolor-sliders-final-color > div:nth-child(2) {
+ width: 71px;
+ text-transform: uppercase;
+ }
+
+ .jcolor .jtabs .jtabs-headers-container .jtabs-controls {
+ display: none !important;
+ }
+
+ .jcolor .jtabs .jtabs-headers-container {
+ display: flex !important;
+ justify-content: center;
+ padding: 4px;
+ }
+
+ .jcolor .jtabs-headers > div:not(.jtabs-border) {
+ padding: 2px !important;
+ padding-left: 15px !important;
+ padding-right: 15px !important;
+ font-size: 0.8em;
+ }
+ .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: flex;
+ padding: 8px 8px 8px 32px;
+ width: 250px;
+ position: relative;
+ cursor: default;
+ font-size: 11px;
+ font-family: sans-serif;
+ text-align: left;
+ -webkit-box-align: center;
+ align-items: center;
+ }
+
+ .jcontextmenu > div::before {
+ content: attr(data-icon);
+ font-family: "Material Icons" !important;
+ font-size: 15px;
+ position: absolute;
+ left: 9px;
+ line-height: 24px;
+ }
+
+ .jcontextmenu.symbols > div::before {
+ font-family: "Material Symbols Outlined" !important;
+ }
+
+ .jcontextmenu > div.header {
+ display: none;
+ }
+
+ .jcontextmenu > div a {
+ color: #555;
+ text-decoration: none;
+ flex: 1;
+ cursor: pointer;
+ }
+
+ .jcontextmenu > div span {
+ margin-right: 10px;
+ }
+
+ .jcontextmenu .jcontextmenu-disabled a {
+ color: #ccc;
+ }
+
+ .jcontextmenu .jcontextmenu-disabled::before {
+ color: #ccc;
+ }
+
+ .jcontextmenu > div:hover {
+ background: #ebebeb;
+ }
+
+ .jcontextmenu hr {
+ border: 1px solid #e9e9e9;
+ border-bottom: 0;
+ margin-top: 5px;
+ margin-bottom: 5px;
+ }
+
+ .jcontextmenu > hr:hover {
+ background: transparent;
+ }
+
+ .jcontextmenu .jcontextmenu {
+ top: 4px;
+ left: 99%;
+ opacity: 0;
+ position: absolute;
+ }
+
+ .jcontextmenu > div:hover > .jcontextmenu {
+ display: block;
+ opacity: 1;
+ -webkit-transform: translate(0, 0) scale(1);
+ transform: translate(0, 0) scale(1);
+ pointer-events: auto;
+ }
+
+ @media only screen and (max-width: 420px) {
+ .jcontextmenu {
+ top: initial !important;
+ left: 0px !important;
+ bottom: 0px !important;
+ width: 100vw;
+ height: 260px;
+ overflow: scroll;
+ animation: slide-bottom-in 0.4s forwards;
+ padding-top: 0px;
+ }
+ .jcontextmenu div {
+ width: 100%;
+ text-align: center;
+ border-bottom: 1px solid #ccc;
+ padding: 15px;
+ }
+ .jcontextmenu > div.header {
+ background-color: lightgray;
+ padding: 5px;
+ top: 0px;
+ position: sticky;
+ z-index: 2;
+ }
+ .jcontextmenu > div.header > a.title {
+ text-align: left;
+ }
+
+ .jcontextmenu > div.header > a.close {
+ text-align: right;
+ }
+ .jcontextmenu a {
+ font-size: 1.4em;
+ text-transform: uppercase;
+ }
+ .jcontextmenu span {
+ display: none;
+ }
+ .jcontextmenu span {
+ display: none;
+ }
+ .jcontextmenu hr {
+ display: none;
+ }
+ }
+
+ .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-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[disabled] {
+ opacity: 0.5;
+ pointer-events: none;
+ }
+
+ .jdropdown-focus {
+ position: relative;
+ }
+
+ .jdropdown-focus .jdropdown-container {
+ transform: translate3d(0, 0, 0);
+ }
+
+ .jdropdown-default.jdropdown-focus .jdropdown-header {
+ outline: auto 5px -webkit-focus-ring-color;
+ }
+
+ .jdropdown-default.jdropdown-focus .jdropdown-header.jdropdown-add {
+ background-image: url("data:image/svg+xml,%0A%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 24 24%27 fill=%27gray%27 width=%2724px%27 height=%2724px%27%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3Cpath d=%27M19 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%27/%3E%3C/svg%3E");
+ }
+
+ .jdropdown-container-header {
+ padding: 0px;
+ margin: 0px;
+ position: relative;
+ box-sizing: border-box;
+ }
+
+ .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=%27http://www.w3.org/2000/svg%27 width=%2724%27 height=%2724%27 viewBox=%270 0 24 24%27%3E%3Cpath fill=%27none%27 d=%27M0 0h24v24H0V0z%27/%3E%3Cpath d=%27M7 10l5 5 5-5H7z%27 fill=%27gray%27/%3E%3C/svg%3E");
+ text-overflow: ellipsis;
+ cursor: pointer;
+ box-sizing: border-box;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ padding-right: 30px !important;
+ }
+
+ .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: 12px;
+ 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-description {
+ text-overflow: ellipsis;
+ overflow: hidden;
+ line-height: 1.5em;
+ }
+
+ .jdropdown-image {
+ margin-right: 10px;
+ width: 32px;
+ height: 32px;
+ border-radius: 20px;
+ }
+
+ .jdropdown-image-small {
+ width: 24px;
+ height: 24px;
+ }
+
+ .jdropdown-icon {
+ margin-right: 10px;
+ font-size: 30px;
+ margin-left: -5px;
+ }
+
+ .jdropdown-icon-small {
+ font-size: 24px;
+ margin-left: 0px;
+ }
+
+ .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: #eee;
+ }
+
+ .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;
+ text-align: left;
+ }
+
+ .jdropdown-default .jdropdown-reset_ {
+ content: "x";
+ position: absolute;
+ top: 0;
+ right: 0;
+ margin: 5px;
+ margin-right: 10px;
+ font-size: 12px;
+ width: 12px;
+ cursor: pointer;
+ text-shadow: 0px 0px 5px #fff;
+ display: none;
+ line-height: 1.8em;
+ }
+
+ .jdropdown-default.jdropdown-focus .jdropdown-reset_ {
+ display: block;
+ }
+
+ /** Default render for mobile **/
+
+ .jdropdown-picker.jdropdown-focus .jdropdown-backdrop {
+ display: block;
+ }
+
+ .jdropdown-picker .jdropdown-header {
+ outline: none;
+ }
+
+ .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 !important;
+ left: 0px !important;
+ width: 100% !important;
+ height: 100% !important;
+ background-color: #fafafa;
+ padding: 0px;
+ z-index: 9001;
+ overflow-y: scroll;
+ will-change: scroll-position;
+ -webkit-overflow-scrolling: touch;
+ }
+
+ .jdropdown-searchbar.jdropdown-focus .jdropdown-container-header {
+ position: fixed;
+ top: 0px;
+ left: 0px;
+ z-index: 9002;
+ padding: 6px;
+ background-color: #fff;
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
+ width: 100%;
+ }
+
+ .jdropdown-searchbar.jdropdown-focus .jdropdown-header {
+ border: 0px !important;
+ background-position-x: 0% !important;
+ background-position-y: 40% !important;
+ background-repeat: no-repeat;
+ background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMTUuNSAxNGgtLjc5bC0uMjgtLjI3QzE1LjQxIDEyLjU5IDE2IDExLjExIDE2IDkuNSAxNiA1LjkxIDEzLjA5IDMgOS41IDNTMyA1LjkxIDMgOS41IDUuOTEgMTYgOS41IDE2YzEuNjEgMCAzLjA5LS41OSA0LjIzLTEuNTdsLjI3LjI4di43OWw1IDQuOTlMMjAuNDkgMTlsLTQuOTktNXptLTYgMEM3LjAxIDE0IDUgMTEuOTkgNSA5LjVTNy4wMSA1IDkuNSA1IDE0IDcuMDEgMTQgOS41IDExLjk5IDE0IDkuNSAxNHoiIGZpbGw9IiNlNmU2ZTgiLz48cGF0aCBkPSJNMCAwaDI0djI0SDB6IiBmaWxsPSJub25lIi8+PC9zdmc+);
+ padding-left: 30px !important;
+ padding-right: 60px !important;
+ }
+
+ .jdropdown-searchbar.jdropdown-focus .jdropdown-close {
+ display: block;
+ }
+
+ .jdropdown-searchbar .jdropdown-header {
+ outline: none;
+ }
+
+ .jdropdown-searchbar .jdropdown-container {
+ margin-top: 40px;
+ 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-width: 800px) {
+ .jdropdown-list {
+ width: 100% !important;
+ border: 0px;
+ padding: 0px;
+ }
+
+ .jdropdown-list .jdropdown-container {
+ min-width: 100%;
+ }
+
+ .jdropdown-searchbar.jdropdown-focus .jdropdown-description {
+ text-transform: uppercase;
+ }
+ }
+
+ .app .jdropdown-item {
+ text-transform: uppercase;
+ }
+
+ .jdropdown-create-container {
+ margin: 10px;
+ border: 1px solid #ccc;
+ border-radius: 2px;
+ padding: 6px;
+ }
+
+ .jdropdown-color {
+ background-color: #fff;
+ border: 1px solid transparent;
+ border-radius: 12px;
+ width: 12px;
+ height: 12px;
+ margin-right: 6px;
+ }
+
+ .jdropdown-item[data-disabled] {
+ opacity: 0.5;
+ pointer-events: none;
+ }
+ .jeditor-container {
+ border: 1px solid #ccc;
+ box-sizing: border-box;
+ }
+
+ .jeditor-container.with-margin {
+ background-color: #f2f2f2;
+ max-width: 1200px;
+ }
+
+ .jeditor-dragging {
+ border: 1px dashed #000;
+ }
+
+ .jeditor {
+ outline: none;
+ word-break: break-word;
+ }
+
+ .jeditor-container.with-margin .jeditor {
+ background-color: #fff;
+ margin: 80px;
+ min-height: 800px;
+ padding: 80px;
+ max-width: 800px;
+ }
+
+ .jeditor[data-placeholder]:empty:before {
+ content: attr(data-placeholder);
+ color: lightgray;
+ }
+
+ /** Snippet **/
+
+ .jsnippet {
+ margin-top: 15px;
+ cursor: pointer;
+ border: 1px solid #ccc;
+ position: relative;
+ }
+
+ .jsnippet:focus {
+ outline: none;
+ }
+
+ .jsnippet img {
+ width: 100%;
+ }
+
+ .jsnippet .jsnippet-title {
+ padding: 15px;
+ font-size: 1.4em;
+ }
+
+ .jsnippet .jsnippet-description {
+ padding-left: 15px;
+ padding-right: 15px;
+ font-size: 1em;
+ }
+
+ .jsnippet .jsnippet-host {
+ padding: 15px;
+ text-transform: uppercase;
+ font-size: 0.8em;
+ color: #777;
+ text-align: right;
+ }
+
+ .jsnippet .jsnippet-url {
+ display: none;
+ }
+
+ .jeditor .jsnippet:after {
+ content: "close";
+ font-family: "Material icons";
+ font-size: 24px;
+ width: 24px;
+ height: 24px;
+ line-height: 24px;
+ cursor: pointer;
+ text-shadow: 0px 0px 2px #fff;
+ position: absolute;
+ top: 12px;
+ right: 12px;
+ }
+
+ .jsnippet * {
+ -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.resizing {
+ -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:focus {
+ border: 2px solid #0096fd;
+ outline: #0096fd;
+ }
+
+ .jeditor .pdf {
+ background-image: url("data:image/svg+xml,%3Csvg version=%271.1%27 id=%27Layer_1%27 xmlns=%27http://www.w3.org/2000/svg%27 xmlns:xlink=%27http://www.w3.org/1999/xlink%27 x=%270px%27 y=%270px%27 viewBox=%270 0 512 512%27 style=%27enable-background:new 0 0 512 512;%27 xml:space=%27preserve%27%3E%3Cpath style=%27fill:%23C30B15;%27 d=%27M511.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%27/%3E%3Cpath style=%27fill:%2385080E;%27 d=%27M511.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%27/%3E%3Cpolygon style=%27fill:%23FFFFFF;%27 points=%27278.328,333.913 255.711,77.913 119.096,77.913 119.096,311.652 %27/%3E%3Cpolygon style=%27fill:%23E8E6E6;%27 points=%27392.904,311.652 392.904,155.826 337.252,133.565 314.991,77.913 255.711,77.913 256.067,333.913 %27/%3E%3Cpolygon style=%27fill:%23FFFFFF;%27 points=%27314.991,155.826 314.991,77.913 392.904,155.826 %27/%3E%3Crect x=%27119.096%27 y=%27311.652%27 style=%27fill:%23FC0F1A;%27 width=%27273.809%27 height=%27122.435%27/%3E%3Cg%3E%3Cpath style=%27fill:%23FFFFFF;%27 d=%27M204.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%27/%3E%3Cpath style=%27fill:%23FFFFFF;%27 d=%27M257.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%27/%3E%3Cpath style=%27fill:%23FFFFFF;%27 d=%27M308.563,356.982v12.26h23.763v10.596h-23.763v19.525h-13.471v-52.975h39.277v10.595h-25.806 V356.982z%27/%3E%3C/g%3E%3C/svg%3E%0A");
+ background-repeat: no-repeat;
+ background-size: cover;
+ width: 60px;
+ height: 60px;
+ }
+
+ .jeditor-toolbar {
+ width: fit-content;
+ max-width: 100%;
+ box-sizing: border-box;
+ margin: 10px;
+ }
+
+ .toolbar-on-top .jeditor-toolbar {
+ width: initial;
+ margin: 0px;
+ box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
+ display: block;
+ }
+
+ .toolbar-on-top .jeditor {
+ padding: 15px;
+ }
+
+ .toolbar-on-top .jtoolbar .material-icons {
+ font-size: 24px;
+ transform: initial;
+ margin: 4px;
+ }
+
+ .toolbar-on-top .jtoolbar .jpicker-header {
+ font-size: 1em;
+ margin-top: 4px;
+ margin-bottom: 4px;
+ }
+
+ .jeditor table {
+ border-collapse: collapse;
+ }
+
+ .jeditor table td {
+ border: 1px solid #bbb;
+ height: 2em;
+ }
+
+ .jeditor table td:focus {
+ border: 1px solid blue;
+ }
+
+ .jeditor .line-break {
+ border-top: 1px dashed #ccc;
+ display: flex;
+ justify-content: center;
+ pointer-events: none;
+ }
+
+ .jeditor .line-break:before {
+ content: "New page";
+ background-color: #fff;
+ color: #ccc;
+ margin: -1em;
+ padding: 6px;
+ position: absolute;
+ }
+ .jfloating {
+ position: fixed;
+ bottom: 0px;
+ right: 0px;
+ margin-right: 5px;
+
+ -webkit-box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
+ -moz-box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
+ border: 1px solid #ccc;
+ background-color: #fff;
+ box-sizing: border-box;
+ padding-top: 50px !important;
+ z-index: 9002;
+ border-radius: 8px;
+ }
+
+ .jfloating.jfloating-big {
+ width: 510px !important;
+ height: 472px !important;
+ }
+
+ .jfloating.jfloating-small {
+ width: 300px !important;
+ height: 320px !important;
+ }
+
+ .jfloating.jfloating-large {
+ width: 600px !important;
+ height: 600px !important;
+ }
+
+ .jfloating:before {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ content: attr(title);
+ padding: 15px;
+ box-sizing: border-box;
+ font-size: 1.2em;
+ box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.2);
+ background-color: #fff;
+ border-radius: 8px 8px 0px 0px;
+ background-color: #404040;
+ font-size: 0.93rem;
+ font-weight: 600;
+ color: white;
+ letter-spacing: 0.5px;
+ }
+
+ .jfloating:after {
+ content: "";
+ background-image: url("data:image/svg+xml,%0A%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2724%27 height=%2724%27 viewBox=%270 0 24 24%27%3E%3Cpath fill=%27%23FFF%27 d=%27M19 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%27/%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%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;
+ }
+
+ .jfloating_content {
+ padding: 20px;
+ overflow-y: auto;
+ max-height: 100%;
+ box-sizing: border-box;
+ height: -webkit-fill-available;
+ }
+
+ .jfloating.jfloating-minimized {
+ height: 50px !important;
+ }
+
+ .jfloating.jfloating-minimized .jfloating_content {
+ display: none;
+ }
+
+ .jmodal {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ width: 60%;
+ height: 60%;
+ -webkit-box-shadow: 0 2px 12px rgba(0, 0, 0, 0.2);
+ -moz-box-shadow: 0 2px 12px rgba(0, 0, 0, 0.2);
+ border: 1px solid #ccc;
+ background-color: #fff;
+ transform: translate(-50%, -50%);
+ box-sizing: border-box;
+ z-index: 9002;
+ border-radius: 4px;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .jmodal_title {
+ padding: 20px;
+ height: 70px;
+ box-sizing: border-box;
+ font-size: 1.4em;
+ background-color: #fff;
+ border-radius: 8px 8px 0px 0px;
+ pointer-events: none;
+ display: flex;
+ -webkit-align-items: center;
+ -webkit-box-align: center;
+ align-items: center;
+ border-bottom: 1px solid #eee;
+ }
+
+ .jmodal_title > div {
+ font-size: 1.4em;
+ }
+
+ .jmodal_title[data-icon]:before {
+ content: attr(data-icon);
+ font-family: "Material Icons" !important;
+ width: 24px;
+ height: 24px;
+ font-size: 24px;
+ margin-right: 10px;
+ line-height: 24px;
+ }
+
+ .jmodal_content {
+ padding: 20px;
+ overflow-y: auto;
+ height: 100%;
+ box-sizing: border-box;
+ scrollbar-width: thin;
+ scrollbar-color: #333 transparent;
+ }
+
+ .jmodal_title:empty {
+ display: none;
+ }
+
+ .jmodal_title:empty + .jmodal_content {
+ height: 100%;
+ }
+
+ .jmodal_content::-webkit-scrollbar {
+ height: 12px;
+ }
+
+ .jmodal_content::-webkit-scrollbar {
+ width: 12px;
+ }
+
+ .jmodal_content::-webkit-scrollbar-track {
+ border: 1px solid #fff;
+ background: #eee;
+ }
+
+ .jmodal_content::-webkit-scrollbar-thumb {
+ border: 1px solid #fff;
+ background: #888;
+ }
+
+ .jmodal:after {
+ content: "";
+ background-image: url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2724%27 height=%2724%27 viewBox=%270 0 24 24%27%3E%3Cpath d=%27M19 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%27/%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3C/svg%3E");
+ position: absolute;
+ top: 0;
+ right: 0;
+ margin: 25px;
+ 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: 0px;
+ border-radius: 0px;
+ }
+
+ .jmodal_backdrop {
+ position: fixed;
+ top: 0px;
+ left: 0px;
+ min-width: 100%;
+ min-height: 100%;
+ background-color: rgba(0, 0, 0, 0.2);
+ 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 */
+ }
+
+ .jmodal_content .jcalendar .jcalendar-content,
+ .jmodal_content .jdropdown-container {
+ position: fixed;
+ }
+
+ .jnotification {
+ position: fixed;
+ z-index: 10000;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ padding: 10px;
+ bottom: 0px;
+ }
+
+ .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;
+
+ 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-close {
+ content: "";
+ background-image: url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2720%27 height=%2720%27 viewBox=%270 0 24 24%27 fill=%27white%27%3E%3Cpath d=%27M19 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%27/%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%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;
+ }
+
+ .jnotification-error .jnotification-container {
+ background: rgb(182, 38, 6);
+ background: linear-gradient(
+ 0deg,
+ rgba(170, 41, 13, 1) 0%,
+ rgba(149, 11, 11, 1) 100%
+ );
+ }
+
+ @media (max-width: 800px) {
+ .jnotification {
+ top: calc(0px + var(--safe-area-top));
+ width: 100%;
+ }
+ .jnotification-container {
+ background: rgba(255, 255, 255, 0.95);
+ border: 1px solid #eee;
+ color: #444;
+ margin: 0px;
+ width: initial;
+ }
+ .jnotification-error .jnotification-container {
+ background: rgba(255, 255, 255, 0.95);
+ color: #790909;
+ }
+ .jnotification-close {
+ background-image: url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2720%27 height=%2720%27 viewBox=%270 0 24 24%27 fill=%27black%27%3E%3Cpath d=%27M19 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%27/%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3C/svg%3E");
+ }
+ }
+
+ .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;
+ }
+ .jpicker {
+ cursor: pointer;
+ white-space: nowrap;
+ display: inline-flex;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ outline: none;
+ position: relative;
+ }
+
+ .jpicker-header {
+ background-repeat: no-repeat;
+ background-position: top 50% right 5px;
+ background-image: url("data:image/svg+xml,%0A%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2724%27 height=%2724%27 viewBox=%270 0 24 24%27%3E%3Cpath fill=%27none%27 d=%27M0 0h24v24H0V0z%27/%3E%3Cpath d=%27M7 10l5 5 5-5H7z%27 fill=%27gray%27/%3E%3C/svg%3E");
+ text-overflow: ellipsis;
+ cursor: pointer;
+ box-sizing: border-box;
+ text-align: left;
+ outline: none;
+
+ line-height: 24px;
+ padding: 2px;
+ padding-left: 12px;
+ padding-right: 35px;
+ outline: none;
+ border-radius: 4px;
+ }
+
+ .jpicker-header:hover {
+ background-color: #f2f2f2;
+ }
+
+ .jpicker-content {
+ position: absolute;
+ top: 0;
+ display: none;
+ 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);
+ border-radius: 4px;
+ background-color: #fff;
+ padding: 4px;
+ z-index: 50;
+ text-align: left;
+ max-height: 250px;
+ scrollbar-width: thin;
+ scrollbar-color: #333 transparent;
+ }
+
+ .jpicker-content::-webkit-scrollbar {
+ width: 8px;
+ }
+
+ .jpicker-content::-webkit-scrollbar-track {
+ background: #eee;
+ }
+
+ .jpicker-content::-webkit-scrollbar-thumb {
+ background: #888;
+ }
+
+ .jpicker-content > div {
+ padding: 6px;
+ padding-left: 15px;
+ padding-right: 15px;
+ }
+
+ .jpicker-focus > .jpicker-content {
+ display: block;
+ }
+
+ .jpicker-content > div:hover {
+ background-color: #efefef;
+ }
+
+ .jpicker-content > div:empty {
+ opacity: 0;
+ }
+
+ .jpicker-header > i,
+ .jpicker-header > div {
+ display: block;
+ }
+
+ .jpicker-focus > .jpicker-content.jpicker-columns {
+ display: flex !important ;
+ justify-content: center;
+ flex-wrap: wrap;
+ }
+
+ .jpicker-focus .jpicker-content.jpicker-grid {
+ display: inline-grid;
+ }
+
+ .jprogressbar {
+ 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;
+ box-sizing: border-box;
+ cursor: pointer;
+ border: 1px solid #ccc;
+ position: relative;
+ }
+
+ .jprogressbar::before {
+ content: attr(data-value);
+ position: absolute;
+ margin: 5px;
+ margin-left: 10px;
+ }
+
+ .jprogressbar-header::placeholder {
+ color: #000;
+ }
+
+ .jprogressbar::focus {
+ outline: auto 5px -webkit-focus-ring-color;
+ }
+
+ .jprogressbar > div {
+ background-color: #eee;
+ background-color: red;
+ box-sizing: border-box;
+ height: 31px;
+ }
+ .jrating {
+ display: flex;
+ }
+ .jrating > div {
+ width: 24px;
+ height: 24px;
+ line-height: 24px;
+ background-image: url("data:image/svg+xml,%0A%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2724%27 height=%2724%27 viewBox=%270 0 24 24%27%3E%3Cpath d=%27M22 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%27 fill=%27gray%27/%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3C/svg%3E");
+ }
+
+ .jrating .jrating-over {
+ background-image: url("data:image/svg+xml,%0A%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2724%27 height=%2724%27 viewBox=%270 0 24 24%27 fill=%27black%27%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3Cpath d=%27M12 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%27/%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3C/svg%3E");
+ opacity: 0.7;
+ }
+
+ .jrating .jrating-selected {
+ background-image: url("data:image/svg+xml,%0A%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2724%27 height=%2724%27 viewBox=%270 0 24 24%27 fill=%27red%27%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3Cpath d=%27M12 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%27/%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3C/svg%3E");
+ }
+
+ .jsearch {
+ position: relative;
+ display: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ }
+
+ .jsearch_container {
+ position: absolute;
+ 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;
+ z-index: 1;
+
+ -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 0.218s;
+ transition: opacity 0.218s;
+ background: #fff;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ cursor: pointer;
+ margin: 0;
+ min-width: 300px;
+ outline: none;
+ width: auto;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ }
+
+ .jsearch_container:empty:after {
+ content: attr(data-placeholder);
+ }
+
+ .jsearch_container > 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;
+
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ }
+
+ .jsearch_container > div:hover {
+ background-color: #e8eaed;
+ }
+
+ .jsearch_container > div > img {
+ width: 32px;
+ height: 32px;
+ user-select: none;
+ border-radius: 16px;
+ margin-right: 2px;
+ }
+
+ .jsearch_container > div > div {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ margin-left: 2px;
+ max-width: 300px;
+ white-space: nowrap;
+ user-select: none;
+ }
+
+ .jsearch_container .selected {
+ background-color: #e8eaed;
+ }
+ .jslider {
+ outline: none;
+ }
+
+ .jslider-focus {
+ width: 100% !important;
+ height: 100% !important;
+ }
+
+ .jslider-focus img {
+ display: none;
+ }
+
+ .jslider img {
+ width: 100px;
+ }
+
+ .jslider-left::before {
+ position: fixed;
+ left: 15px;
+ top: 50%;
+ content: "arrow_back_ios";
+ color: #fff;
+ width: 30px;
+ height: 30px;
+ font-family: "Material Icons";
+ font-size: 30px;
+ /* before it was 0px 0px 0px #000 */
+ text-shadow: 0px 0px 6px rgb(56, 56, 56);
+ text-align: center;
+ cursor: pointer;
+ }
+
+ .jslider-right::after {
+ position: fixed;
+ right: 15px;
+ top: 50%;
+ content: "arrow_forward_ios";
+ color: #fff;
+ width: 30px;
+ height: 30px;
+ font-family: "Material Icons";
+ font-size: 30px;
+ /* before it was 0px 0px 0px #000 */
+ text-shadow: 0px 0px 6px rgb(56, 56, 56);
+ text-align: center;
+ cursor: pointer;
+ }
+
+ .jslider-close {
+ width: 24px;
+ height: 24px;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2724%27 height=%2724%27 viewBox=%270 0 24 24%27 fill=%27white%27%3E%3Cpath d=%27M19 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%27/%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3C/svg%3E");
+ position: fixed;
+ top: 15px;
+ right: 15px;
+ cursor: pointer;
+ z-index: 3000;
+
+ display: block !important;
+ }
+
+ .jslider-counter {
+ height: 24px;
+ background-color: transparent;
+ position: fixed;
+ left: 50%;
+ transform: translateX(-50%);
+ bottom: 15px;
+ cursor: pointer;
+ z-index: 3000;
+
+ display: flex;
+ display: -webkit-flex;
+ -webkit-justify-content: center;
+ -webkit-align-items: center;
+ -webkit-flex-direction: row;
+ justify-content: center;
+ align-items: center;
+ flex-direction: row;
+ }
+
+ .jslider-caption {
+ position: fixed;
+ max-width: 90vw;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+ top: 15px;
+ left: 15px;
+ z-index: 3000;
+ color: #fff;
+ font-size: 1rem;
+
+ display: block !important;
+ }
+
+ .jslider-counter div {
+ width: 10px;
+ height: 10px;
+ background: #fff;
+ border-radius: 50%;
+ margin: 0px 5px;
+
+ display: block !important;
+ }
+
+ .jslider-counter .jslider-counter-focus {
+ background-color: cornflowerblue;
+ pointer-events: none;
+ }
+
+ .jslider-focus {
+ position: fixed;
+ left: 0;
+ top: 0;
+ width: 100%;
+ min-height: 100%;
+ max-height: 100%;
+ z-index: 2000;
+ margin: 0px;
+ box-sizing: border-box;
+
+ background-color: rgba(0, 0, 0, 0.8);
+ -webkit-transition-duration: 0.05s;
+ transition-duration: 0.05s;
+ display: flex;
+ -ms-flex-align: center;
+ -webkit-align-items: center;
+ -webkit-box-align: center;
+
+ align-items: center;
+ }
+
+ .jslider-focus img {
+ width: 50vw;
+ height: auto;
+ box-sizing: border-box;
+ margin: 0 auto;
+ vertical-align: middle;
+ display: none;
+ }
+
+ .jslider-focus img.jslider-vertical {
+ width: auto;
+ /* before it was 50vh */
+ height: 80vh;
+ }
+
+ @media only screen and (max-width: 576px) {
+ .jslider-focus img.jslider-vertical {
+ width: 99vw !important;
+ height: auto !important;
+ }
+
+ .jslider-focus img {
+ width: 100vw !important;
+ height: auto !important;
+ }
+ }
+
+ .jslider-grid {
+ display: -ms-grid;
+ display: grid;
+ grid-gap: 1px;
+ position: relative;
+ }
+
+ .jslider-grid[data-number="2"] {
+ -ms-grid-columns: 1fr 50%;
+ grid-template-columns: 1fr 50%;
+ }
+
+ .jslider-grid[data-number="3"] {
+ -ms-grid-columns: 1fr 33%;
+ grid-template-columns: 1fr 33%;
+ }
+
+ .jslider-grid[data-number="4"] {
+ -ms-grid-columns: 1fr 25%;
+ grid-template-columns: 1fr 25%;
+ }
+
+ .jslider-grid img {
+ display: none;
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ }
+
+ .jslider-grid[data-total]:after {
+ content: attr(data-total) "+";
+ font-size: 1.5em;
+ position: absolute;
+ color: #fff;
+ right: 15px;
+ bottom: 6px;
+ }
+
+ .jslider-grid img:first-child {
+ -ms-grid-column: 1;
+ -ms-grid-row: 1;
+ grid-column: 1;
+ grid-row: 1;
+ display: block;
+ }
+
+ .jslider-grid[data-number="2"] img:nth-child(2) {
+ -ms-grid-column: 2;
+ -ms-grid-row: 1;
+ grid-column: 2;
+ grid-row: 1;
+ display: block;
+ }
+
+ .jslider-grid[data-number="3"] img:first-child {
+ -ms-grid-column: 1 / 2;
+ -ms-grid-row: 1 / 4;
+ grid-column: 1 / 2;
+ grid-row: 1 / 4;
+ }
+
+ .jslider-grid[data-number="3"] img:nth-child(2) {
+ -ms-grid-column: 2;
+ -ms-grid-row: 1;
+ grid-column: 2;
+ grid-row: 1;
+ display: block;
+ }
+
+ .jslider-grid[data-number="3"] img:nth-child(3) {
+ -ms-grid-column: 2;
+ -ms-grid-row: 2;
+ grid-column: 2;
+ grid-row: 2;
+ display: block;
+ }
+
+ .jslider-grid[data-number="4"] img:first-child {
+ -ms-grid-column: 1 / 2;
+ -ms-grid-row: 1 / 4;
+ grid-column: 1 / 2;
+ grid-row: 1 / 4;
+ }
+
+ .jslider-grid[data-number="4"] img:nth-child(2) {
+ -ms-grid-column: 2;
+ -ms-grid-row: 1;
+ grid-column: 2;
+ grid-row: 1;
+ display: block;
+ }
+
+ .jslider-grid[data-number="4"] img:nth-child(3) {
+ -ms-grid-column: 2;
+ -ms-grid-row: 2;
+ grid-column: 2;
+ grid-row: 2;
+ display: block;
+ }
+
+ .jslider-grid[data-number="4"] img:nth-child(4) {
+ -ms-grid-column: 2;
+ -ms-grid-row: 3;
+ grid-column: 2;
+ grid-row: 3;
+ display: block;
+ }
+
+ .jtabs {
+ max-width: 100vw;
+ position: relative;
+ }
+
+ .jtabs .jtabs-headers-container {
+ display: flex;
+ align-items: center;
+ }
+
+ .jtabs .jtabs-headers {
+ display: flex;
+ align-items: center;
+ overflow: hidden;
+ position: relative;
+ }
+
+ .jtabs .jtabs-headers > div:not(.jtabs-border) {
+ padding: 8px;
+ padding-left: 20px;
+ padding-right: 20px;
+ margin-left: 1px;
+ margin-right: 1px;
+ background-color: #f1f1f1;
+ cursor: pointer;
+ white-space: nowrap;
+ text-align: center;
+ }
+
+ .jtabs .jtabs-headers > div.jtabs-selected {
+ background-color: #e8e8e8;
+ color: #000;
+ }
+
+ .jtabs .jtabs-headers > div > div {
+ color: #555;
+ width: 100%;
+ overflow: hidden;
+ }
+
+ .jtabs .jtabs-headers i {
+ display: block;
+ margin: auto;
+ }
+
+ .jtabs .jtabs-content {
+ box-sizing: border-box;
+ }
+
+ .jtabs .jtabs-content > div {
+ display: none;
+ box-sizing: border-box;
+ }
+
+ .jtabs .jtabs-content > div.jtabs-selected {
+ display: block;
+ }
+
+ .jtabs .jtabs-border {
+ position: absolute;
+ height: 2px;
+ background-color: #888;
+ transform-origin: left;
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
+ transition-property: color, left, transform;
+ display: none;
+ }
+
+ .jtabs-animation .jtabs-border {
+ display: initial;
+ }
+
+ .jtabs .jtabs-controls {
+ margin: 3px;
+ margin-left: 10px;
+ display: flex;
+ min-width: 82px;
+ }
+
+ .jtabs .jtabs-controls > div {
+ cursor: pointer;
+ background-position: center;
+ background-repeat: no-repeat;
+ width: 24px;
+ height: 24px;
+ line-height: 24px;
+ }
+
+ .jtabs .jtabs-prev {
+ margin-left: 10px;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 24 24%27 fill=%27gray%27 width=%2718px%27 height=%2718px%27%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3Cpath d=%27M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z%27/%3E%3C/svg%3E");
+ }
+
+ .jtabs .jtabs-prev.disabled {
+ margin-left: 10px;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 24 24%27 fill=%27lightgray%27 width=%2718px%27 height=%2718px%27%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3Cpath d=%27M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z%27/%3E%3C/svg%3E");
+ }
+
+ .jtabs .jtabs-next {
+ background-image: url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 24 24%27 fill=%27gray%27 width=%2718px%27 height=%2718px%27%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3Cpath d=%27M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z%27/%3E%3C/svg%3E");
+ }
+
+ .jtabs .jtabs-next.disabled {
+ background-image: url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 24 24%27 fill=%27lightgray%27 width=%2718px%27 height=%2718px%27%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3Cpath d=%27M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z%27/%3E%3C/svg%3E");
+ }
+
+ .jtabs .jtabs-add {
+ background-image: url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 height=%2724%27 viewBox=%270 0 24 24%27 width=%2724%27%3E%3Cpath d=%27M19 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%27 fill=%27%23bbbbbb%27/%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3C/svg%3E");
+ }
+
+ /** Modern skin **/
+
+ .jtabs.jtabs-modern .jtabs-headers > div:not(.jtabs-border) {
+ padding: 4px;
+ padding-left: 10px;
+ padding-right: 10px;
+ background-color: #fff;
+ }
+
+ .jtabs.jtabs-modern .jtabs-headers > .jtabs-selected {
+ color: #000;
+ }
+
+ .jtabs.jtabs-modern .jtabs-headers > .jtabs-selected .material-icons {
+ color: #000;
+ }
+
+ .jtabs.jtabs-modern .jtabs-headers {
+ background: #eeeeef !important;
+ padding: 2px;
+ border-radius: 4px;
+ }
+
+ .jtabs.jtabs-modern .jtabs-headers .jtabs-border {
+ border-color: #eeeeef !important;
+ }
+
+ .jtabs.jtabs-modern .jtabs-border {
+ background-color: rgba(194, 197, 188, 0.884);
+ }
+ .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: 1px;
+ border: 1px solid #ccc;
+ position: relative;
+ }
+
+ .jtags.jtags-empty:not(.jtags-focus)::before {
+ position: absolute;
+ margin: 3px;
+ color: #ccc;
+ content: attr(data-placeholder);
+ top: 0px;
+ margin-left: 6px;
+ }
+
+ .jtags > div {
+ padding: 5px;
+ padding-left: 10px;
+ padding-right: 22px;
+ position: relative;
+ border-radius: 1px;
+ margin: 2px;
+ display: block;
+ outline: none;
+ }
+
+ .jtags > div:empty:before {
+ content: " ";
+ white-space: pre;
+ }
+
+ .jtags > div::after {
+ content: "x";
+ position: absolute;
+ top: 6px;
+ 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;
+ }
+
+ .jtoolbar-container {
+ border-radius: 2px;
+ 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);
+ display: inline-flex !important;
+ }
+
+ .jtoolbar {
+ cursor: pointer;
+ white-space: nowrap;
+ display: flex;
+ padding: 4px;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ font-size: 13px;
+ }
+
+ .jtoolbar-disabled {
+ pointer-events: none;
+ opacity: 0.4;
+ }
+
+ .jtoolbar-mobile {
+ display: flex;
+ position: fixed;
+ bottom: 0;
+ margin: 0;
+ left: 0;
+ width: 100%;
+ background: #f7f7f8;
+ z-index: 1;
+ box-sizing: border-box;
+ box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.1);
+ border-radius: 0px;
+ }
+
+ .jtoolbar > div {
+ display: inline-flex;
+ align-items: center;
+ box-sizing: border-box;
+ vertical-align: middle;
+ justify-content: space-evenly;
+ }
+
+ .jtoolbar-mobile > div {
+ display: flex;
+ width: 100%;
+ }
+
+ .jtoolbar .jtoolbar-item {
+ text-align: center;
+ margin: auto;
+ padding: 2px;
+ padding-left: 4px;
+ padding-right: 4px;
+ }
+
+ .jtoolbar-mobile .jtoolbar-item {
+ position: relative;
+ flex: 1;
+ }
+
+ .jtoolbar .jtoolbar-divisor {
+ width: 2px;
+ height: 18px;
+ padding: 0px;
+ margin-left: 4px;
+ margin-right: 4px;
+ background-color: #ddd;
+ }
+
+ .jtoolbar .jtoolbar-label {
+ padding-left: 8px;
+ padding-right: 8px;
+ }
+
+ .jtoolbar-mobile a {
+ text-decoration: none;
+ display: inline-block;
+ }
+
+ .jtoolbar-mobile i {
+ display: inline-flex !important;
+ color: #929292;
+ }
+
+ .jtoolbar-mobile span {
+ font-size: 0.7em;
+ display: block;
+ color: #929292;
+ }
+
+ .jtoolbar-mobile .jtoolbar-selected a,
+ .jtoolbar-mobile .jtoolbar-selected i,
+ .jtoolbar-mobile .jtoolbar-selected span {
+ color: var(--active-color) !important;
+ background-color: transparent;
+ }
+
+ .jtoolbar-item {
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ }
+
+ .jtoolbar-item i {
+ display: block;
+ color: #333;
+ }
+
+ .jtoolbar-item:hover {
+ background-color: #f2f2f2;
+ }
+
+ .jtoolbar .jpicker {
+ padding-left: 0px;
+ padding-right: 0px;
+ }
+
+ .jtoolbar .jpicker-header {
+ height: 24px;
+ line-height: 24px;
+ padding: 0px;
+ padding-right: 20px;
+ padding-left: 8px;
+ background-position: top 50% right 0px;
+ display: flex;
+ align-items: center;
+ font-size: 0.9em;
+ }
+
+ .jtoolbar .jpicker-content > div {
+ padding: 6px;
+ }
+
+ .jtoolbar-active {
+ background-color: #eee;
+ }
+
+ .jtoolbar .fa {
+ width: 18px;
+ height: 18px;
+ display: block;
+ line-height: 18px;
+ font-size: 14px;
+ }
+
+ .jtoolbar .material-icons {
+ font-size: 18px;
+ width: 24px;
+ height: 24px;
+ display: block;
+ line-height: 24px;
+ transform: rotate(0.03deg);
+ text-align: center;
+ }
+
+ .jtoolbar .jtoolbar-arrow {
+ background-repeat: no-repeat;
+ background-position: center;
+ background-image: url("data:image/svg+xml,%0A%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 24 24%27 fill=%27black%27 width=%2718px%27 height=%2718px%27%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3Cpath d=%27M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z%27/%3E%3C/svg%3E");
+ width: 24px;
+ height: 16px;
+ margin-left: 4px;
+ border-left: 1px solid #f2f2f2;
+ }
+
+ .jtoolbar-floating {
+ position: absolute;
+ display: none;
+ 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);
+ border-radius: 4px;
+ background-color: #fff;
+ padding: 4px;
+ z-index: 50;
+ text-align: left;
+ margin-right: 20px;
+ }
+
+ .jtoolbar-floating .jtoolbar-divisor {
+ display: none;
+ }
+
+ .jtoolbar-arrow-selected .jtoolbar-floating {
+ display: flex;
+ flex-wrap: wrap;
+ }
+}
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index b98d4fad470..b27705c956b 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -2382,6 +2382,7 @@ en:
add_warning: "This is an official warning."
toggle_whisper: "Toggle Whisper"
toggle_unlisted: "Toggle Unlisted"
+ insert_table: "Insert Table"
posting_not_on_topic: "Which topic do you want to reply to?"
saved_local_draft_tip: "saved locally"
similar_topics: "Your topic is similar to…"
@@ -4645,6 +4646,64 @@ en:
patternMismatch: "Please match the requested format."
badInput: "Please enter a valid input."
+ table_builder:
+ title: "Table Builder"
+ modal:
+ title: "Table Builder"
+ create: "Build Table"
+ help:
+ title: "Using the Spreadsheet Editor"
+ enter_key: "Enter"
+ tab_key: "Tab"
+ new_row: "at the end of a row to insert a new row."
+ new_col: "at the end of a column to insert a new column."
+ options: "Right-click on cells to access more options in a dropdown menu."
+ confirm_close: "Are you sure you want to close the table bulder? Any unsaved changes will be lost."
+ edit:
+ btn_edit: "Edit Table"
+ modal:
+ title: "Edit Table"
+ cancel: "cancel"
+ create: "Save"
+ reason: "why are you editing?"
+ trigger_reason: "Add reason for edit"
+ default_edit_reason: "Update Table with Table Editor"
+ default_header:
+ col_1: "Column 1"
+ col_2: "Column 2"
+ col_3: "Column 3"
+ col_4: "Column 4"
+ spreadsheet:
+ no_records_found: "No records found"
+ show: "Show"
+ entries: "entries"
+ about: "About"
+ prompts:
+ delete_selected_rows: "Are you sure you want to delete the selected rows?"
+ delete_selected_cols: "Are you sure you want to delete the selected columns?"
+ will_destroy_merged_cells: "This action will destroy any existing merged cells. Are you sure?"
+ will_clear_search_results: "This action will destroy any existing merged cells. Are you sure?"
+ conflicts_with_merged_cells: "There is a conflict with another merged cell"
+ invalid_merge_props: "Invalid merged properties"
+ cells_already_merged: "Cell already merged"
+ no_cells_selected: "No cells selected"
+ context_menu:
+ row:
+ before: "Insert a new row before"
+ after: "Insert a new row after"
+ delete: "Delete selected rows"
+ col:
+ before: "Insert a new column before"
+ after: "Insert a new column after"
+ delete: "Delete selected columns"
+ rename: "Rename this column"
+ order:
+ ascending: "Order ascending"
+ descending: "Order descending"
+ copy: "Copy..."
+ paste: "Paste..."
+ save: "Save as..."
+
# This section is exported to the javascript for i18n in the admin section
admin_js:
# This is a text input placeholder, keep the translation short
diff --git a/public/javascripts/jspreadsheet/jspreadsheet.js b/public/javascripts/jspreadsheet/jspreadsheet.js
new file mode 100644
index 00000000000..33ed309cd3b
--- /dev/null
+++ b/public/javascripts/jspreadsheet/jspreadsheet.js
@@ -0,0 +1,16905 @@
+/**
+ * Jspreadsheet v4.11.1
+ *
+ * Website: https://bossanova.uk/jspreadsheet/
+ * Description: Create amazing web based spreadsheets.
+ *
+ * This software is distribute under MIT License
+ */
+
+var formula = (function () {
+ // 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;
+ })();
+
+ var met = {};
+
+ met.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;
+ })();
+
+ met.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;
+ })();
+
+ met.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.95054210748189e31,
+ ],
+ ["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", 1e24, "Y"],
+ Z: ["zetta", 1e21, "Z"],
+ E: ["exa", 1e18, "E"],
+ P: ["peta", 1e15, "P"],
+ T: ["tera", 1e12, "T"],
+ G: ["giga", 1e9, "G"],
+ M: ["mega", 1e6, "M"],
+ k: ["kilo", 1e3, "k"],
+ h: ["hecto", 1e2, "h"],
+ e: ["dekao", 1e1, "e"],
+ d: ["deci", 1e-1, "d"],
+ c: ["centi", 1e-2, "c"],
+ m: ["milli", 1e-3, "m"],
+ u: ["micro", 1e-6, "u"],
+ n: ["nano", 1e-9, "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", 1e1, "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", 1e1, "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;
+ })();
+
+ met.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;
+ })();
+
+ met.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;
+ })();
+
+ met.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;
+ })();
+
+ met.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 () {
+ var args = utils.argsToArray(arguments);
+ var criteria = args.pop();
+ var range = utils.parseNumberArray(utils.flatten(args));
+ 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 += 2) {
+ if (isNaN(criteria[c][i])) {
+ condition += '"' + criteria[c][i] + '"' + criteria[c + 1];
+ } else {
+ condition += criteria[c][i] + criteria[c + 1];
+ }
+ if (c !== n_criterias - 1) {
+ condition += " && ";
+ }
+ }
+ condition = condition.slice(0, -4);
+ 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;
+ })();
+
+ met.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;
+ })();
+
+ met.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;
+ }
+
+ if (text == null) {
+ return 0;
+ }
+
+ 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;
+ })();
+
+ met.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 () {
+ var args = utils.argsToArray(arguments);
+ var criteria = args.pop();
+ var range = utils.flatten(args);
+ 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;
+ })();
+
+ met.utils = (function () {
+ var exports = {};
+
+ exports.PROGRESS = function (p, c) {
+ var color = c ? c : "red";
+ var value = p ? p : "0";
+
+ return (
+ '
'
+ );
+ };
+
+ exports.RATING = function (v) {
+ var html = '';
+ for (var i = 0; i < 5; i++) {
+ if (i < v) {
+ html += '
';
+ } else {
+ html += "
";
+ }
+ }
+ html += "
";
+ return html;
+ };
+
+ return exports;
+ })();
+
+ for (var i = 0; i < Object.keys(met).length; i++) {
+ var methods = met[Object.keys(met)[i]];
+ var keys = Object.keys(methods);
+ for (var j = 0; j < keys.length; j++) {
+ if (!methods[keys[j]]) {
+ window[keys[j]] = function () {
+ return keys[j] + "Not implemented";
+ };
+ } else if (
+ typeof methods[keys[j]] == "function" ||
+ typeof methods[keys[j]] == "object"
+ ) {
+ window[keys[j]] = methods[keys[j]];
+ window[keys[j]].toString = function () {
+ return "#ERROR";
+ };
+
+ if (typeof methods[keys[j]] == "object") {
+ var tmp = Object.keys(methods[keys[j]]);
+ for (var z = 0; z < tmp.length; z++) {
+ window[keys[j]][tmp[z]].toString = function () {
+ return "#ERROR";
+ };
+ }
+ }
+ } else {
+ window[keys[j]] = function () {
+ return keys[j] + "Not implemented";
+ };
+ }
+ }
+ }
+
+ /**
+ * Instance execution helpers
+ */
+ var x = null;
+ var y = null;
+ var instance = null;
+
+ window["TABLE"] = function () {
+ return instance;
+ };
+ window["COLUMN"] = window["COL"] = function () {
+ return parseInt(x) + 1;
+ };
+ window["ROW"] = function () {
+ return parseInt(y) + 1;
+ };
+ window["CELL"] = function () {
+ return F.getColumnNameFromCoords(x, y);
+ };
+ window["VALUE"] = function (col, row, processed) {
+ return instance.getValueFromCoords(
+ parseInt(col) - 1,
+ parseInt(row) - 1,
+ processed
+ );
+ };
+ window["THISROWCELL"] = function (col) {
+ return instance.getValueFromCoords(parseInt(col) - 1, parseInt(y));
+ };
+
+ // Secure formula
+ var secureFormula = function (oldValue, runtime) {
+ var newValue = "";
+ var inside = 0;
+
+ var special = ["=", "!", ">", "<"];
+
+ 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();
+
+ if (runtime == true) {
+ if (
+ i > 0 &&
+ oldValue[i] == "=" &&
+ special.indexOf(oldValue[i - 1]) == -1 &&
+ special.indexOf(oldValue[i + 1]) == -1
+ ) {
+ newValue += "=";
+ }
+ }
+ }
+ }
+
+ // Adapt to JS
+ newValue = newValue.replace(/\^/g, "**");
+ newValue = newValue.replace(/\<\>/g, "!=");
+ newValue = newValue.replace(/\&/g, "+");
+ newValue = newValue.replace(/\$/g, "");
+
+ return newValue;
+ };
+
+ // Convert range tokens
+ var tokensUpdate = function (tokens, e) {
+ for (var index = 0; index < tokens.length; index++) {
+ var f = F.getTokensFromRange(tokens[index]);
+ e = e.replace(tokens[index], "[" + f.join(",") + "]");
+ }
+ return e;
+ };
+
+ var F = function (expression, variables, i, j, obj) {
+ // Global helpers
+ instance = obj;
+ x = i;
+ y = j;
+ // String
+ var s = "";
+ var keys = Object.keys(variables);
+ if (keys.length) {
+ for (var i = 0; i < keys.length; i++) {
+ if (keys[i].indexOf(".") == -1 && keys[i].indexOf("!") == -1) {
+ s += "var " + keys[i] + " = " + variables[keys[i]] + ";\n";
+ } else {
+ s += keys[i] + " = " + variables[keys[i]] + ";\n";
+ }
+ }
+ }
+ // Remove $
+ expression = expression.replace(/\$/g, "");
+ // Replace ! per dot
+ expression = expression.replace(/\!/g, ".");
+ // Adapt to JS
+ expression = secureFormula(expression, true);
+ // Update range
+ var tokens = expression.match(
+ /([A-Z]+[0-9]*\.)?(\$?[A-Z]+\$?[0-9]+):(\$?[A-Z]+\$?[0-9]+)?/g
+ );
+ if (tokens && tokens.length) {
+ expression = tokensUpdate(tokens, expression);
+ }
+
+ // Calculate
+ return new Function(s + "; return " + expression)();
+ };
+
+ /**
+ * Get letter based on a number
+ * @param {number} i
+ * @return {string}
+ */
+ var 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;
+ };
+
+ /**
+ * Get column name from coords
+ */
+ F.getColumnNameFromCoords = function (x, y) {
+ return getColumnName(parseInt(x)) + (parseInt(y) + 1);
+ };
+
+ F.getCoordsFromColumnName = function (columnName) {
+ // Get the letters
+ var t = /^[a-zA-Z]+/.exec(columnName);
+
+ 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 jspreadsheet starts on zero
+ if (code < 0) {
+ code = 0;
+ }
+
+ // Number
+ var number = parseInt(/[0-9]+$/.exec(columnName)) || null;
+ if (number > 0) {
+ number--;
+ }
+
+ return [code, number];
+ }
+ };
+
+ F.getRangeFromTokens = function (tokens) {
+ tokens = tokens.filter(function (v) {
+ return v != "#REF!";
+ });
+
+ var d = "";
+ var t = "";
+ for (var i = 0; i < tokens.length; i++) {
+ if (tokens[i].indexOf(".") >= 0) {
+ d = ".";
+ } else if (tokens[i].indexOf("!") >= 0) {
+ d = "!";
+ }
+ if (d) {
+ t = tokens[i].split(d);
+ tokens[i] = t[1];
+ t = t[0] + d;
+ }
+ }
+
+ tokens.sort(function (a, b) {
+ var t1 = Helpers.getCoordsFromColumnName(a);
+ var t2 = Helpers.getCoordsFromColumnName(b);
+ if (t1[1] > t2[1]) {
+ return 1;
+ } else if (t1[1] < t2[1]) {
+ return -1;
+ } else {
+ if (t1[0] > t2[0]) {
+ return 1;
+ } else if (t1[0] < t2[0]) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+ });
+
+ if (!tokens.length) {
+ return "#REF!";
+ } else {
+ return t + (tokens[0] + ":" + tokens[tokens.length - 1]);
+ }
+ };
+
+ F.getTokensFromRange = function (range) {
+ if (range.indexOf(".") > 0) {
+ var t = range.split(".");
+ range = t[1];
+ t = t[0] + ".";
+ } else if (range.indexOf("!") > 0) {
+ var t = range.split("!");
+ range = t[1];
+ t = t[0] + "!";
+ } else {
+ var t = "";
+ }
+
+ var range = range.split(":");
+ var e1 = F.getCoordsFromColumnName(range[0]);
+ var e2 = F.getCoordsFromColumnName(range[1]);
+
+ 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] === null && e2[1] == null) {
+ var y1 = null;
+ var y2 = null;
+
+ var k = Object.keys(vars);
+ for (var i = 0; i < k.length; i++) {
+ var tmp = F.getCoordsFromColumnName(k[i]);
+ if (tmp[0] === e1[0]) {
+ if (y1 === null || tmp[1] < y1) {
+ y1 = tmp[1];
+ }
+ }
+ if (tmp[0] === e2[0]) {
+ if (y2 === null || tmp[1] > y2) {
+ y2 = tmp[1];
+ }
+ }
+ }
+ } else {
+ if (e1[1] <= e2[1]) {
+ var y1 = e1[1];
+ var y2 = e2[1];
+ } else {
+ var y1 = e2[1];
+ var y2 = e1[1];
+ }
+ }
+
+ var f = [];
+ for (var j = y1; j <= y2; j++) {
+ var line = [];
+ for (var i = x1; i <= x2; i++) {
+ line.push(t + F.getColumnNameFromCoords(i, j));
+ }
+ f.push(line);
+ }
+
+ return f;
+ };
+
+ F.setFormula = function (o) {
+ var k = Object.keys(o);
+ for (var i = 0; i < k.length; i++) {
+ if (typeof o[k[i]] == "function") {
+ window[k[i]] = o[k[i]];
+ }
+ }
+ };
+
+ return F;
+})();
+
+if (!jSuites && typeof require === "function") {
+ var jSuites = require("jsuites");
+}
+
+(function (global, factory) {
+ typeof exports === "object" && typeof module !== "undefined"
+ ? (module.exports = factory())
+ : typeof define === "function" && define.amd
+ ? define(factory)
+ : (global.jspreadsheet = global.jexcel = factory());
+})(this, function () {
+ "use strict";
+
+ // Basic version information
+ var Version = (function () {
+ // Information
+ var info = {
+ title: "Jspreadsheet",
+ version: "4.11.1",
+ type: "CE",
+ host: "https://bossanova.uk/jspreadsheet",
+ license: "MIT",
+ print: function () {
+ return [
+ this.title + " " + this.type + " " + this.version,
+ this.host,
+ this.license,
+ ].join("\r\n");
+ },
+ };
+
+ return function () {
+ return info;
+ };
+ })();
+
+ /**
+ * The value is a formula
+ */
+ var isFormula = function (value) {
+ var v = ("" + value)[0];
+ return v == "=" || v == "#" ? true : false;
+ };
+
+ /**
+ * Get the mask in the jSuites.mask format
+ */
+ var getMask = function (o) {
+ if (o.format || o.mask || o.locale) {
+ var opt = {};
+ if (o.mask) {
+ opt.mask = o.mask;
+ } else if (o.format) {
+ opt.mask = o.format;
+ } else {
+ opt.locale = o.locale;
+ opt.options = o.options;
+ }
+
+ if (o.decimal) {
+ if (!opt.options) {
+ opt.options = {};
+ }
+ opt.options = { decimal: o.decimal };
+ }
+ return opt;
+ }
+
+ return null;
+ };
+
+ // Jspreadsheet core object
+ var jexcel = function (el, options) {
+ // Create jspreadsheet object
+ var obj = {};
+ obj.options = {};
+
+ if (!(el instanceof Element || el instanceof HTMLDocument)) {
+ console.error("Jspreadsheet: 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("Jspreadsheet: el is not a valid DOM element");
+ return false;
+ }
+ }
+
+ // Loading default configuration
+ var defaults = {
+ // External data
+ url: null,
+ // Ajax options
+ method: "GET",
+ requestVariables: null,
+ // Data
+ data: null,
+ // Custom sorting handler
+ sorting: 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",
+ // Rows height default
+ defaultRowHeight: null,
+ // 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: "jspreadsheet",
+ // 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,
+ textOverflow: false,
+ // Meta
+ meta: null,
+ // Style
+ style: null,
+ classes: null,
+ // Execute formulas
+ parseFormulas: true,
+ autoIncrement: true,
+ autoCasting: true,
+ // Security
+ secureFormulas: true,
+ stripHTML: true,
+ stripHTMLOnCopy: false,
+ // Filters
+ filters: false,
+ footers: null,
+ // Event handles
+ onundo: null,
+ onredo: null,
+ onload: null,
+ onchange: null,
+ oncomments: null,
+ onbeforechange: null,
+ onafterchanges: null,
+ onbeforeinsertrow: null,
+ oninsertrow: null,
+ onbeforeinsertcolumn: null,
+ oninsertcolumn: null,
+ onbeforedeleterow: null,
+ ondeleterow: null,
+ onbeforedeletecolumn: null,
+ ondeletecolumn: null,
+ onmoverow: null,
+ onmovecolumn: null,
+ onresizerow: null,
+ onresizecolumn: null,
+ onsort: null,
+ onselection: null,
+ oncopy: null,
+ onpaste: null,
+ onbeforepaste: null,
+ onmerge: null,
+ onfocus: null,
+ onblur: null,
+ onchangeheader: null,
+ oncreateeditor: null,
+ oneditionstart: null,
+ oneditionend: null,
+ onchangestyle: null,
+ onchangemeta: null,
+ onchangepage: null,
+ onbeforesave: null,
+ onsave: null,
+ // Global event dispatcher
+ onevent: null,
+ // Persistance
+ persistance: false,
+ // Customize any cell behavior
+ updateTable: null,
+ // Detach the HTML table when calling updateTable
+ detachForUpdates: false,
+ freezeColumns: null,
+ // Texts
+ text: {
+ noRecordsFound: "No records found",
+ showingPage: "Showing page {0} of {1} entries",
+ show: "Show ",
+ search: "Search",
+ entries: " entries",
+ columnName: "Column name",
+ insertANewColumnBefore: "Insert a new column before",
+ insertANewColumnAfter: "Insert a new column after",
+ deleteSelectedColumns: "Delete selected columns",
+ renameThisColumn: "Rename this column",
+ orderAscending: "Order ascending",
+ orderDescending: "Order descending",
+ insertANewRowBefore: "Insert a new row before",
+ insertANewRowAfter: "Insert a new row after",
+ deleteSelectedRows: "Delete selected rows",
+ editComments: "Edit comments",
+ addComments: "Add comments",
+ comments: "Comments",
+ clearComments: "Clear comments",
+ copy: "Copy...",
+ paste: "Paste...",
+ saveAs: "Save as...",
+ about: "About",
+ areYouSureToDeleteTheSelectedRows:
+ "Are you sure to delete the selected rows?",
+ areYouSureToDeleteTheSelectedColumns:
+ "Are you sure to delete the selected columns?",
+ thisActionWillDestroyAnyExistingMergedCellsAreYouSure:
+ "This action will destroy any existing merged cells. Are you sure?",
+ thisActionWillClearYourSearchResultsAreYouSure:
+ "This action will clear your search results. Are you sure?",
+ thereIsAConflictWithAnotherMergedCell:
+ "There is a conflict with another merged cell",
+ invalidMergeProperties: "Invalid merged properties",
+ cellAlreadyMerged: "Cell already merged",
+ noCellsSelected: "No cells selected",
+ },
+ // About message
+ about: true,
+ };
+
+ // 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(
+ "Jspreadsheet: 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 jspreadsheet table
+ *
+ * @Param config
+ */
+ obj.prepareTable = function () {
+ // Loading initial data from remote sources
+ var results = [];
+
+ // Number of columns
+ var size = obj.options.columns.length;
+
+ if (obj.options.data && typeof obj.options.data[0] !== "undefined") {
+ // Data keys
+ var keys = Object.keys(obj.options.data[0]);
+
+ if (keys.length > size) {
+ size = keys.length;
+ }
+ }
+
+ // Minimal dimensions
+ if (obj.options.minDimensions[0] > size) {
+ size = obj.options.minDimensions[0];
+ }
+
+ // Requests
+ var multiple = [];
+
+ // Preparations
+ for (var i = 0; i < size; i++) {
+ // Deprected options. You should use only columns
+ if (!obj.options.colHeaders[i]) {
+ obj.options.colHeaders[i] = "";
+ }
+ if (!obj.options.colWidths[i]) {
+ obj.options.colWidths[i] = obj.options.defaultColWidth;
+ }
+ if (!obj.options.colAlignments[i]) {
+ obj.options.colAlignments[i] = obj.options.defaultColAlign;
+ }
+
+ // Default column description
+ if (!obj.options.columns[i]) {
+ obj.options.columns[i] = { type: "text" };
+ } else if (!obj.options.columns[i].type) {
+ obj.options.columns[i].type = "text";
+ }
+ if (!obj.options.columns[i].name) {
+ obj.options.columns[i].name = keys && keys[i] ? keys[i] : i;
+ }
+ if (!obj.options.columns[i].source) {
+ obj.options.columns[i].source = [];
+ }
+ if (!obj.options.columns[i].options) {
+ obj.options.columns[i].options = [];
+ }
+ if (!obj.options.columns[i].editor) {
+ obj.options.columns[i].editor = null;
+ }
+ if (!obj.options.columns[i].allowEmpty) {
+ obj.options.columns[i].allowEmpty = false;
+ }
+ if (!obj.options.columns[i].title) {
+ obj.options.columns[i].title = obj.options.colHeaders[i]
+ ? obj.options.colHeaders[i]
+ : "";
+ }
+ if (!obj.options.columns[i].width) {
+ obj.options.columns[i].width = obj.options.colWidths[i]
+ ? obj.options.colWidths[i]
+ : obj.options.defaultColWidth;
+ }
+ if (!obj.options.columns[i].align) {
+ obj.options.columns[i].align = obj.options.colAlignments[i]
+ ? obj.options.colAlignments[i]
+ : "center";
+ }
+
+ // Pre-load initial source for json autocomplete
+ if (
+ obj.options.columns[i].type == "autocomplete" ||
+ obj.options.columns[i].type == "dropdown"
+ ) {
+ // if remote content
+ if (obj.options.columns[i].url) {
+ multiple.push({
+ url: obj.options.columns[i].url,
+ index: i,
+ method: "GET",
+ dataType: "json",
+ success: function (data) {
+ var source = [];
+ for (var i = 0; i < data.length; i++) {
+ obj.options.columns[this.index].source.push(data[i]);
+ }
+ },
+ });
+ }
+ } else if (obj.options.columns[i].type == "calendar") {
+ // Default format for date columns
+ if (!obj.options.columns[i].options.format) {
+ obj.options.columns[i].options.format = "DD/MM/YYYY";
+ }
+ }
+ }
+ // Create the table when is ready
+ if (!multiple.length) {
+ obj.createTable();
+ } else {
+ jSuites.ajax(multiple, function () {
+ obj.createTable();
+ });
+ }
+ };
+
+ obj.createTable = function () {
+ // Elements
+ obj.table = document.createElement("table");
+ obj.thead = document.createElement("thead");
+ obj.tbody = document.createElement("tbody");
+
+ // Create headers controllers
+ obj.headers = [];
+ obj.colgroup = [];
+
+ // Create table container
+ obj.content = document.createElement("div");
+ obj.content.classList.add("jexcel_content");
+ obj.content.onscroll = function (e) {
+ obj.scrollControls(e);
+ };
+ obj.content.onwheel = function (e) {
+ obj.wheelControls(e);
+ };
+
+ // Create toolbar object
+ obj.toolbar = document.createElement("div");
+ obj.toolbar.classList.add("jexcel_toolbar");
+
+ // Search
+ var searchContainer = document.createElement("div");
+ var searchText = document.createTextNode(obj.options.text.search + ": ");
+ obj.searchInput = document.createElement("input");
+ obj.searchInput.classList.add("jexcel_search");
+ searchContainer.appendChild(searchText);
+ searchContainer.appendChild(obj.searchInput);
+ obj.searchInput.onfocus = function () {
+ obj.resetSelection();
+ };
+
+ // Pagination select option
+ var paginationUpdateContainer = document.createElement("div");
+
+ if (
+ obj.options.pagination > 0 &&
+ obj.options.paginationOptions &&
+ obj.options.paginationOptions.length > 0
+ ) {
+ obj.paginationDropdown = document.createElement("select");
+ obj.paginationDropdown.classList.add("jexcel_pagination_dropdown");
+ obj.paginationDropdown.onchange = function () {
+ obj.options.pagination = parseInt(this.value);
+ obj.page(0);
+ };
+
+ for (var i = 0; i < obj.options.paginationOptions.length; i++) {
+ var temp = document.createElement("option");
+ temp.value = obj.options.paginationOptions[i];
+ temp.innerHTML = obj.options.paginationOptions[i];
+ obj.paginationDropdown.appendChild(temp);
+ }
+
+ // Set initial pagination value
+ obj.paginationDropdown.value = obj.options.pagination;
+
+ paginationUpdateContainer.appendChild(
+ document.createTextNode(obj.options.text.show)
+ );
+ paginationUpdateContainer.appendChild(obj.paginationDropdown);
+ paginationUpdateContainer.appendChild(
+ document.createTextNode(obj.options.text.entries)
+ );
+ }
+
+ // Filter and pagination container
+ var filter = document.createElement("div");
+ filter.classList.add("jexcel_filter");
+ filter.appendChild(paginationUpdateContainer);
+ filter.appendChild(searchContainer);
+
+ // Colsgroup
+ obj.colgroupContainer = document.createElement("colgroup");
+ var tempCol = document.createElement("col");
+ tempCol.setAttribute("width", "50");
+ obj.colgroupContainer.appendChild(tempCol);
+
+ // Nested
+ if (obj.options.nestedHeaders && obj.options.nestedHeaders.length > 0) {
+ // Flexible way to handle nestedheaders
+ if (obj.options.nestedHeaders[0] && obj.options.nestedHeaders[0][0]) {
+ for (var j = 0; j < obj.options.nestedHeaders.length; j++) {
+ obj.thead.appendChild(
+ obj.createNestedHeader(obj.options.nestedHeaders[j])
+ );
+ }
+ } else {
+ obj.thead.appendChild(
+ obj.createNestedHeader(obj.options.nestedHeaders)
+ );
+ }
+ }
+
+ // Row
+ obj.headerContainer = document.createElement("tr");
+ var tempCol = document.createElement("td");
+ tempCol.classList.add("jexcel_selectall");
+ obj.headerContainer.appendChild(tempCol);
+
+ for (var i = 0; i < obj.options.columns.length; i++) {
+ // Create header
+ obj.createCellHeader(i);
+ // Append cell to the container
+ obj.headerContainer.appendChild(obj.headers[i]);
+ obj.colgroupContainer.appendChild(obj.colgroup[i]);
+ }
+
+ obj.thead.appendChild(obj.headerContainer);
+
+ // Filters
+ if (obj.options.filters == true) {
+ obj.filter = document.createElement("tr");
+ var td = document.createElement("td");
+ obj.filter.appendChild(td);
+
+ for (var i = 0; i < obj.options.columns.length; i++) {
+ var td = document.createElement("td");
+ td.innerHTML = " ";
+ td.setAttribute("data-x", i);
+ td.className = "jexcel_column_filter";
+ if (obj.options.columns[i].type == "hidden") {
+ td.style.display = "none";
+ }
+ obj.filter.appendChild(td);
+ }
+
+ obj.thead.appendChild(obj.filter);
+ }
+
+ // Content table
+ obj.table = document.createElement("table");
+ obj.table.classList.add("jexcel");
+ obj.table.setAttribute("cellpadding", "0");
+ obj.table.setAttribute("cellspacing", "0");
+ obj.table.setAttribute("unselectable", "yes");
+ //obj.table.setAttribute('onselectstart', 'return false');
+ obj.table.appendChild(obj.colgroupContainer);
+ obj.table.appendChild(obj.thead);
+ obj.table.appendChild(obj.tbody);
+
+ if (!obj.options.textOverflow) {
+ obj.table.classList.add("jexcel_overflow");
+ }
+
+ // 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 Jspreadsheet
+ var ads = document.createElement("a");
+ ads.setAttribute("href", "https://bossanova.uk/jspreadsheet/");
+ 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/jspreadsheet/logo.png";
+ ads.appendChild(img);
+ }
+ } catch (exception) {}
+ var span = document.createElement("span");
+ span.innerHTML = "Jspreadsheet CE";
+ 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["box-shadow"] =
+ "rgb(221 221 221) 2px 2px 5px 0.1px";
+ 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);
+ }
+
+ // Classes
+ if (obj.options.classes) {
+ var k = Object.keys(obj.options.classes);
+ for (var i = 0; i < k.length; i++) {
+ var cell = jexcel.getIdFromColumnName(k[i], true);
+ obj.records[cell[1]][cell[0]].classList.add(
+ obj.options.classes[k[i]]
+ );
+ }
+ }
+ };
+
+ /**
+ * 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: obj.options.method,
+ data: obj.options.requestVariables,
+ 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(
+ "Jspreadsheet: 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 json data by row number
+ *
+ * @param integer row number
+ * @return object
+ */
+ obj.getJsonRow = function (rowNumber) {
+ var rowData = obj.options.data[rowNumber];
+ var x = obj.options.columns.length;
+
+ var row = {};
+ for (var i = 0; i < x; i++) {
+ if (!obj.options.columns[i].name) {
+ obj.options.columns[i].name = i;
+ }
+ row[obj.options.columns[i].name] = rowData[i];
+ }
+
+ return row;
+ };
+
+ /**
+ * Get the whole table data
+ *
+ * @param bool highlighted cells only
+ * @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);
+ 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);
+ // Index
+ var index = null;
+
+ // Set default row height
+ if (obj.options.defaultRowHeight) {
+ obj.rows[j].style.height = obj.options.defaultRowHeight + "px";
+ }
+
+ // Definitions
+ if (obj.options.rows[j]) {
+ if (obj.options.rows[j].height) {
+ obj.rows[j].style.height = obj.options.rows[j].height;
+ }
+ if (obj.options.rows[j].title) {
+ index = obj.options.rows[j].title;
+ }
+ }
+ if (!index) {
+ index = parseInt(j + 1);
+ }
+ // Row number label
+ var td = document.createElement("td");
+ td.innerHTML = index;
+ td.setAttribute("data-y", j);
+ td.className = "jexcel_row";
+ obj.rows[j].appendChild(td);
+
+ // Data columns
+ for (var i = 0; i < obj.options.columns.length; i++) {
+ // New column of data to be append in the line
+ obj.records[j][i] = obj.createCell(i, j, data[i]);
+ // Add column to the row
+ obj.rows[j].appendChild(obj.records[j][i]);
+ }
+
+ // Add row to the table body
+ return obj.rows[j];
+ };
+
+ obj.parseValue = function (i, j, value, cell) {
+ if (
+ ("" + value).substr(0, 1) == "=" &&
+ obj.options.parseFormulas == true
+ ) {
+ value = obj.executeFormula(value, i, j);
+ }
+
+ // Column options
+ var options = obj.options.columns[i];
+ if (options && !isFormula(value)) {
+ // Mask options
+ var opt = null;
+ if ((opt = getMask(options))) {
+ if (value && value == Number(value)) {
+ value = Number(value);
+ }
+ // Process the decimals to match the mask
+ var masked = jSuites.mask.render(value, opt, true);
+ // Negative indication
+ if (cell) {
+ if (opt.mask) {
+ var t = opt.mask.split(";");
+ if (t[1]) {
+ var t1 = t[1].match(new RegExp("\\[Red\\]", "gi"));
+ if (t1) {
+ if (value < 0) {
+ cell.classList.add("red");
+ } else {
+ cell.classList.remove("red");
+ }
+ }
+ var t2 = t[1].match(new RegExp("\\(", "gi"));
+ if (t2) {
+ if (value < 0) {
+ masked = "(" + masked + ")";
+ }
+ }
+ }
+ }
+ }
+
+ if (masked) {
+ value = masked;
+ }
+ }
+ }
+
+ return value;
+ };
+
+ var validDate = function (date) {
+ date = "" + date;
+ if (date.substr(4, 1) == "-" && date.substr(7, 1) == "-") {
+ return true;
+ } else {
+ date = date.split("-");
+ if (
+ date[0].length == 4 &&
+ date[0] == Number(date[0]) &&
+ date[1].length == 2 &&
+ date[1] == Number(date[1])
+ ) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+ /**
+ * 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.textContent = 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.textContent = 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 = null;
+ if (!validDate(value)) {
+ var tmp = jSuites.calendar.extractDateFromString(
+ value,
+ obj.options.columns[i].options.format
+ );
+ if (tmp) {
+ formatted = tmp;
+ }
+ }
+ // Create calendar cell
+ td.textContent = 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.textContent = 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.textContent = 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, td));
+ } else {
+ if (
+ obj.options.stripHTML === false ||
+ obj.options.columns[i].stripHTML === false
+ ) {
+ td.innerHTML = stripScript(obj.parseValue(i, j, value, td));
+ } else {
+ td.textContent = obj.parseValue(i, j, value, td);
+ }
+ }
+ }
+ }
+
+ // 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 (this.options.textOverflow == true) {
+ if (value || td.innerHTML) {
+ obj.records[j][i - 1].style.overflow = "hidden";
+ } else {
+ if (i == obj.options.columns.length - 1) {
+ td.style.overflow = "hidden";
+ }
+ }
+ }
+ }
+ return td;
+ };
+
+ obj.createCellHeader = function (colNumber) {
+ // Create col global control
+ var colWidth = obj.options.columns[colNumber].width
+ ? obj.options.columns[colNumber].width
+ : obj.options.defaultColWidth;
+ var colAlign = obj.options.columns[colNumber].align
+ ? obj.options.columns[colNumber].align
+ : obj.options.defaultColAlign;
+
+ // Create header cell
+ obj.headers[colNumber] = document.createElement("td");
+ if (obj.options.stripHTML) {
+ obj.headers[colNumber].textContent = obj.options.columns[colNumber]
+ .title
+ ? obj.options.columns[colNumber].title
+ : jexcel.getColumnName(colNumber);
+ } else {
+ obj.headers[colNumber].innerHTML = obj.options.columns[colNumber].title
+ ? obj.options.columns[colNumber].title
+ : jexcel.getColumnName(colNumber);
+ }
+ obj.headers[colNumber].setAttribute("data-x", colNumber);
+ obj.headers[colNumber].style.textAlign = colAlign;
+ if (obj.options.columns[colNumber].title) {
+ obj.headers[colNumber].setAttribute(
+ "title",
+ obj.options.columns[colNumber].title
+ );
+ }
+ if (obj.options.columns[colNumber].id) {
+ obj.headers[colNumber].setAttribute(
+ "id",
+ obj.options.columns[colNumber].id
+ );
+ }
+
+ // 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].textContent =
+ 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.textContent = 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);
+ toolbarItem.setAttribute("id", toolbar[i].id);
+
+ // 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.textContent = 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.textContent = 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.textContent = 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("Jspreadsheet: filters not enabled.");
+ } else {
+ // Make sure is integer
+ columnId = parseInt(columnId);
+ // Reset selection
+ obj.resetSelection();
+ // Load options
+ var optionsFiltered = [];
+ if (obj.options.columns[columnId].type == "checkbox") {
+ optionsFiltered.push({ id: "true", name: "True" });
+ optionsFiltered.push({ id: "false", name: "False" });
+ } else {
+ var options = [];
+ var hasBlanks = false;
+ 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;
+ } else {
+ var hasBlanks = true;
+ }
+ }
+ var keys = Object.keys(options);
+ var optionsFiltered = [];
+ for (var j = 0; j < keys.length; j++) {
+ optionsFiltered.push({ id: keys[j], name: options[keys[j]] });
+ }
+ // Has blank options
+ if (hasBlanks) {
+ optionsFiltered.push({ value: "", id: "", name: "(Blanks)" });
+ }
+ }
+
+ // Create dropdown
+ var div = document.createElement("div");
+ obj.filter.children[columnId + 1].innerHTML = "";
+ obj.filter.children[columnId + 1].appendChild(div);
+ obj.filter.children[columnId + 1].style.paddingLeft = "0px";
+ obj.filter.children[columnId + 1].style.paddingRight = "0px";
+ obj.filter.children[columnId + 1].style.overflow = "initial";
+
+ var opt = {
+ data: optionsFiltered,
+ multiple: true,
+ autocomplete: true,
+ opened: true,
+ value:
+ obj.filters[columnId] !== undefined ? obj.filters[columnId] : null,
+ width: "100%",
+ position:
+ obj.options.tableOverflow == true || obj.options.fullscreen == true
+ ? true
+ : false,
+ onclose: function (o) {
+ obj.resetFilters();
+ obj.filters[columnId] = o.dropdown.getValue(true);
+ obj.filter.children[columnId + 1].innerHTML = o.dropdown.getText();
+ obj.filter.children[columnId + 1].style.paddingLeft = "";
+ obj.filter.children[columnId + 1].style.paddingRight = "";
+ obj.filter.children[columnId + 1].style.overflow = "";
+ obj.closeFilter(columnId);
+ obj.refreshSelection();
+ },
+ };
+
+ // Dynamic dropdown
+ jSuites.dropdown(div, opt);
+ }
+ };
+
+ obj.resetFilters = function () {
+ if (obj.options.filters) {
+ for (var i = 0; i < obj.filter.children.length; i++) {
+ obj.filter.children[i].innerHTML = " ";
+ obj.filters[i] = null;
+ }
+ }
+
+ obj.results = null;
+ obj.updateResult();
+ };
+
+ 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++) {
+ var value = "" + obj.options.data[y][x];
+ var label = "" + obj.records[y][x].innerHTML;
+ if (query[i] == value || query[i] == label) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+ var query = obj.filters[columnId];
+ obj.results = [];
+ for (var j = 0; j < obj.options.data.length; j++) {
+ if (search(query, columnId, j)) {
+ obj.results.push(j);
+ }
+ }
+ if (!obj.results.length) {
+ obj.results = null;
+ }
+
+ obj.updateResult();
+ };
+
+ /**
+ * Open the editor
+ *
+ * @param object cell
+ * @return void
+ */
+ obj.openEditor = function (cell, empty, e) {
+ // Get cell position
+ var y = cell.getAttribute("data-y");
+ var x = cell.getAttribute("data-x");
+
+ // On edition start
+ obj.dispatch("oneditionstart", el, cell, x, y);
+
+ // Overflow
+ if (x > 0) {
+ obj.records[y][x - 1].style.overflow = "hidden";
+ }
+
+ // Create editor
+ var createEditor = function (type) {
+ // Cell information
+ var info = cell.getBoundingClientRect();
+
+ // Create dropdown
+ var editor = document.createElement(type);
+ editor.style.width = info.width + "px";
+ editor.style.height = info.height - 2 + "px";
+ editor.style.minHeight = info.height - 2 + "px";
+
+ // Edit cell
+ cell.classList.add("editor");
+ cell.innerHTML = "";
+ cell.appendChild(editor);
+
+ // On edition start
+ obj.dispatch("oncreateeditor", el, cell, x, y, editor);
+
+ return editor;
+ };
+
+ // Readonly
+ if (cell.classList.contains("readonly") == true) {
+ // Do nothing
+ } else {
+ // Holder
+ obj.edition = [obj.records[y][x], obj.records[y][x].innerHTML, x, y];
+
+ // If there is a custom editor for it
+ if (obj.options.columns[x].editor) {
+ // Custom editors
+ obj.options.columns[x].editor.openEditor(cell, el, empty, e);
+ } else {
+ // Native functions
+ if (obj.options.columns[x].type == "hidden") {
+ // Do nothing
+ } else if (
+ obj.options.columns[x].type == "checkbox" ||
+ obj.options.columns[x].type == "radio"
+ ) {
+ // Get value
+ var value = cell.children[0].checked ? false : true;
+ // Toogle value
+ obj.setValue(cell, value);
+ // Do not keep edition open
+ obj.edition = null;
+ } else if (
+ obj.options.columns[x].type == "dropdown" ||
+ obj.options.columns[x].type == "autocomplete"
+ ) {
+ // Get current value
+ var value = obj.options.data[y][x];
+ if (obj.options.columns[x].multiple && !Array.isArray(value)) {
+ value = value.split(";");
+ }
+
+ // Create dropdown
+ if (typeof obj.options.columns[x].filter == "function") {
+ var source = obj.options.columns[x].filter(
+ el,
+ cell,
+ x,
+ y,
+ obj.options.columns[x].source
+ );
+ } else {
+ var source = obj.options.columns[x].source;
+ }
+
+ // Do not change the original source
+ var data = [];
+ for (var j = 0; j < source.length; j++) {
+ data.push(source[j]);
+ }
+
+ // Create editor
+ var editor = createEditor("div");
+ var options = {
+ data: data,
+ multiple: obj.options.columns[x].multiple ? true : false,
+ autocomplete:
+ obj.options.columns[x].autocomplete ||
+ obj.options.columns[x].type == "autocomplete"
+ ? true
+ : false,
+ opened: true,
+ value: value,
+ width: "100%",
+ height: editor.style.minHeight,
+ position:
+ obj.options.tableOverflow == true ||
+ obj.options.fullscreen == true
+ ? true
+ : false,
+ onclose: function () {
+ obj.closeEditor(cell, true);
+ },
+ };
+ if (
+ obj.options.columns[x].options &&
+ obj.options.columns[x].options.type
+ ) {
+ options.type = obj.options.columns[x].options.type;
+ }
+ jSuites.dropdown(editor, options);
+ } else if (
+ obj.options.columns[x].type == "calendar" ||
+ obj.options.columns[x].type == "color"
+ ) {
+ // Value
+ var value = obj.options.data[y][x];
+ // Create editor
+ var editor = createEditor("input");
+ editor.value = value;
+
+ if (
+ obj.options.tableOverflow == true ||
+ obj.options.fullscreen == true
+ ) {
+ obj.options.columns[x].options.position = true;
+ }
+ obj.options.columns[x].options.value = obj.options.data[y][x];
+ obj.options.columns[x].options.opened = true;
+ obj.options.columns[x].options.onclose = function (el, value) {
+ obj.closeEditor(cell, true);
+ };
+ // Current value
+ if (obj.options.columns[x].type == "color") {
+ jSuites.color(editor, obj.options.columns[x].options);
+ } else {
+ jSuites.calendar(editor, obj.options.columns[x].options);
+ }
+ // Focus on editor
+ editor.focus();
+ } else if (obj.options.columns[x].type == "html") {
+ var value = obj.options.data[y][x];
+ // Create editor
+ var editor = createEditor("div");
+ editor.style.position = "relative";
+ var div = document.createElement("div");
+ div.classList.add("jexcel_richtext");
+ editor.appendChild(div);
+ jSuites.editor(div, {
+ focus: true,
+ value: value,
+ });
+ var rect = cell.getBoundingClientRect();
+ var 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);
+ var rect = cell.getBoundingClientRect();
+ var 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");
+ }
+
+ editor.focus();
+ editor.value = value;
+
+ // Column options
+ var options = obj.options.columns[x];
+ // Format
+ var opt = null;
+
+ // Apply format when is not a formula
+ if (!isFormula(value)) {
+ // Format
+ if ((opt = getMask(options))) {
+ // Masking
+ if (!options.disabledMaskOnEdition) {
+ if (options.mask) {
+ var m = options.mask.split(";");
+ editor.setAttribute("data-mask", m[0]);
+ } else if (options.locale) {
+ editor.setAttribute("data-locale", options.locale);
+ }
+ }
+ // Input
+ opt.input = editor;
+ // Configuration
+ editor.mask = opt;
+ // Do not treat the decimals
+ jSuites.mask.render(value, opt, false);
+ }
+ }
+
+ editor.onblur = function () {
+ obj.closeEditor(cell, true);
+ };
+ editor.scrollLeft = editor.scrollWidth;
+ }
+ }
+ }
+ };
+
+ /**
+ * 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;
+
+ // Column options
+ var options = obj.options.columns[x];
+ // Format
+ var opt = null;
+ if ((opt = getMask(options))) {
+ // Keep numeric in the raw data
+ if (
+ value !== "" &&
+ !isFormula(value) &&
+ typeof value !== "number"
+ ) {
+ var t = jSuites.mask.extract(value, opt, true);
+ if (t && t.value !== "") {
+ value = t.value;
+ }
+ }
+ }
+ }
+ }
+
+ // 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 column options
+ * @param x
+ * @param y
+ * @returns {{type: string}}
+ */
+ obj.getColumnOptions = function (x, y) {
+ // Type
+ var options = obj.options.columns[x];
+
+ // Cell type
+ if (!options) {
+ options = { type: "text" };
+ }
+
+ return options;
+ };
+
+ /**
+ * 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].textContent = obj.getDropDownValue(x, value);
+ } else if (obj.options.columns[x].type == "calendar") {
+ // Try formatted date
+ var formatted = null;
+ if (!validDate(value)) {
+ var tmp = jSuites.calendar.extractDateFromString(
+ value,
+ obj.options.columns[x].options.format
+ );
+ if (tmp) {
+ formatted = tmp;
+ }
+ }
+ // Update data and cell
+ obj.options.data[y][x] = value;
+ obj.records[y][x].textContent = 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].textContent = "";
+ obj.records[y][x].appendChild(color);
+ } else {
+ obj.records[y][x].style.color = value;
+ obj.records[y][x].textContent = 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, obj.records[y][x])
+ );
+ } else {
+ obj.records[y][x].textContent = obj.parseValue(
+ x,
+ y,
+ value,
+ obj.records[y][x]
+ );
+ }
+ }
+ // 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 overlaps 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 highlighted 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();
+
+ // select column
+ if (y1 == null) {
+ y1 = 0;
+ y2 = obj.rows.length - 1;
+ }
+
+ // 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] && obj.records[borderTop][i]) {
+ obj.records[borderTop][i].classList.add("highlight-top");
+ }
+ // Bottom border
+ if (obj.records[borderBottom] && 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] && 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];
+ var lastX = last.getAttribute("data-x");
+
+ var contentRect = obj.content.getBoundingClientRect();
+ var x1 = contentRect.left;
+ var y1 = contentRect.top;
+
+ var 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();
+ // Only check if the last column is not part of the merged cells
+ if (lastX > obj.options.freezeColumns - 1 && x2 - x1 + w2 < width) {
+ obj.corner.style.display = "none";
+ } else {
+ if (obj.options.selectionCopy == true) {
+ obj.corner.style.display = "";
+ }
+ }
+ } else {
+ if (obj.options.selectionCopy == true) {
+ obj.corner.style.display = "";
+ }
+ }
+ }
+ };
+
+ /**
+ * Update scroll position based on the selection
+ */
+ obj.updateScroll = function (direction) {
+ // Jspreadsheet Container information
+ var 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
+ var 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 (typeof column === "undefined") {
+ // Get all headers
+ var data = [];
+ for (var i = 0; i < obj.headers.length; i++) {
+ data.push(obj.options.columns[i].width);
+ }
+ } else {
+ // In case the column is an object
+ if (typeof column == "object") {
+ column = $(column).getAttribute("data-x");
+ }
+
+ data = obj.colgroup[column].getAttribute("width");
+ }
+
+ return data;
+ };
+
+ /**
+ * Set the column width
+ *
+ * @param int column number (first column is: 0)
+ * @param int new column width
+ * @param int old column width
+ */
+ obj.setWidth = function (column, width, oldWidth) {
+ if (width) {
+ if (Array.isArray(column)) {
+ // Oldwidth
+ if (!oldWidth) {
+ var oldWidth = [];
+ }
+ // Set width
+ for (var i = 0; i < column.length; i++) {
+ if (!oldWidth[i]) {
+ oldWidth[i] = obj.colgroup[column[i]].getAttribute("width");
+ }
+ var w = Array.isArray(width) && width[i] ? width[i] : width;
+ obj.colgroup[column[i]].setAttribute("width", w);
+ obj.options.columns[column[i]].width = w;
+ }
+ } else {
+ // Oldwidth
+ if (!oldWidth) {
+ oldWidth = obj.colgroup[column].getAttribute("width");
+ }
+ // Set width
+ obj.colgroup[column].setAttribute("width", width);
+ obj.options.columns[column].width = width;
+ }
+
+ // Keeping history of changes
+ obj.setHistory({
+ action: "setWidth",
+ column: column,
+ oldValue: oldWidth,
+ newValue: width,
+ });
+
+ // On resize column
+ obj.dispatch("onresizecolumn", el, column, width, oldWidth);
+
+ // Update corner position
+ obj.updateCornerPosition();
+ }
+ };
+
+ /**
+ * Set the row height
+ *
+ * @param row - row number (first row is: 0)
+ * @param height - new row height
+ * @param oldHeight - old row height
+ */
+ obj.setHeight = function (row, height, oldHeight) {
+ if (height > 0) {
+ // In case the column is an object
+ if (typeof row == "object") {
+ row = row.getAttribute("data-y");
+ }
+
+ // Oldwidth
+ if (!oldHeight) {
+ oldHeight = obj.rows[row].getAttribute("height");
+
+ if (!oldHeight) {
+ var rect = obj.rows[row].getBoundingClientRect();
+ oldHeight = rect.height;
+ }
+ }
+
+ // Integer
+ height = parseInt(height);
+
+ // Set width
+ obj.rows[row].style.height = height + "px";
+
+ // Keep options updated
+ if (!obj.options.rows[row]) {
+ obj.options.rows[row] = {};
+ }
+ obj.options.rows[row].height = height;
+
+ // Keeping history of changes
+ obj.setHistory({
+ action: "setHeight",
+ row: row,
+ oldValue: oldHeight,
+ newValue: height,
+ });
+
+ // On resize column
+ obj.dispatch("onresizerow", el, row, height, oldHeight);
+
+ // Update corner position
+ obj.updateCornerPosition();
+ }
+ };
+
+ /**
+ * Get the row height
+ *
+ * @param row - row number (first row is: 0)
+ * @return height - current row height
+ */
+ obj.getHeight = function (row) {
+ if (typeof row === "undefined") {
+ // 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.textContent = obj.parseValue(
+ +obj.records.length + i,
+ j,
+ obj.options.footers[j][i]
+ );
+
+ // Hide/Show with hideColumn()/showColumn()
+ td.style.display = obj.colgroup[i].style.display;
+ }
+ }
+ }
+ };
+
+ /**
+ * 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].textContent;
+ };
+
+ /**
+ * 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].textContent;
+
+ if (!newValue) {
+ newValue = prompt(obj.options.text.columnName, oldValue);
+ }
+
+ if (newValue) {
+ obj.headers[column].textContent = 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]] &&
+ (obj.records[cell[1]][cell[0]].classList.contains("readonly") ==
+ false ||
+ force)
+ ) {
+ // 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,
+ });
+ // Set comments
+ obj.dispatch("oncomments", el, comments, title, cell, cell[0], cell[1]);
+ };
+
+ /**
+ * 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;
+ }
+
+ // Test order
+ var temp = [];
+ if (
+ obj.options.columns[column].type == "number" ||
+ obj.options.columns[column].type == "numeric" ||
+ 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].textContent.toLowerCase()];
+ }
+ }
+
+ // Default sorting method
+ if (typeof obj.options.sorting !== "function") {
+ obj.options.sorting = function (direction) {
+ return function (a, b) {
+ var valueA = a[1];
+ var valueB = b[1];
+
+ if (!direction) {
+ 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;
+ }
+ };
+ };
+ }
+
+ temp = temp.sort(obj.options.sorting(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
+ ) {
+ 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 records
+ if (rowNumber + numOfRows >= obj.options.data.length) {
+ numOfRows = obj.options.data.length - rowNumber;
+ }
+
+ // Onbeforedeleterow
+ if (
+ obj.dispatch("onbeforedeleterow", el, rowNumber, numOfRows) ===
+ 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;
+ }
+
+ // If delete all rows, and set allowDeletingAllRows false, will stay one row
+ if (
+ obj.options.allowDeletingAllRows == false &&
+ lastRow + 1 === numOfRows
+ ) {
+ numOfRows--;
+ console.error(
+ "Jspreadsheet: It is not possible to delete the last row"
+ );
+ }
+
+ // 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(
+ "Jspreadsheet: 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
+ ) {
+ 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);
+ var o =
+ obj.thead.children[j].children[
+ obj.thead.children[j].children.length - 1
+ ].getAttribute("data-column");
+ o = o.split(",");
+ for (
+ var col = columnIndex;
+ col < numOfColumns + columnIndex;
+ col++
+ ) {
+ o.push(col);
+ }
+ obj.thead.children[j].children[
+ obj.thead.children[j].children.length - 1
+ ].setAttribute("data-column", o);
+ }
+ } else {
+ var colspan =
+ parseInt(obj.options.nestedHeaders[0].colspan) + numOfColumns;
+ obj.options.nestedHeaders[0].colspan = colspan;
+ obj.thead.children[0].children[
+ obj.thead.children[0].children.length - 1
+ ].setAttribute("colspan", colspan);
+ }
+ }
+
+ // Keep history
+ obj.setHistory({
+ action: "insertColumn",
+ columnNumber: columnNumber,
+ numOfColumns: numOfColumns,
+ insertBefore: insertBefore,
+ columns: properties,
+ headers: historyHeaders,
+ colgroup: historyColgroup,
+ records: historyRecords,
+ footers: historyFooters,
+ data: historyData,
+ });
+
+ // Remove table references
+ obj.updateTableReferences();
+
+ // Events
+ obj.dispatch(
+ "oninsertcolumn",
+ el,
+ columnNumber,
+ numOfColumns,
+ historyRecords,
+ insertBefore
+ );
+ }
+ };
+
+ /**
+ * Delete a column by number
+ *
+ * @param integer columnNumber - reference column to be excluded
+ * @param integer numOfColumns - number of columns to be excluded from the reference column
+ * @return void
+ */
+ obj.deleteColumn = function (columnNumber, numOfColumns) {
+ // Global Configuration
+ if (obj.options.allowDeleteColumn == true) {
+ if (obj.headers.length > 1) {
+ // Delete column definitions
+ if (columnNumber == undefined) {
+ var number = obj.getSelectedColumns(true);
+
+ if (!number.length) {
+ // Remove last column
+ columnNumber = obj.headers.length - 1;
+ numOfColumns = 1;
+ } else {
+ // Remove selected
+ columnNumber = parseInt(number[0]);
+ numOfColumns = parseInt(number.length);
+ }
+ }
+
+ // Lasat column
+ var lastColumn = obj.options.data[0].length - 1;
+
+ if (
+ columnNumber == undefined ||
+ columnNumber > lastColumn ||
+ columnNumber < 0
+ ) {
+ columnNumber = lastColumn;
+ }
+
+ // Minimum of columns to be delete is 1
+ if (!numOfColumns) {
+ numOfColumns = 1;
+ }
+
+ // Can't delete more than the limit of the table
+ if (numOfColumns > obj.options.data[0].length - columnNumber) {
+ numOfColumns = obj.options.data[0].length - columnNumber;
+ }
+
+ // onbeforedeletecolumn
+ if (
+ obj.dispatch(
+ "onbeforedeletecolumn",
+ el,
+ columnNumber,
+ numOfColumns
+ ) === false
+ ) {
+ 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(
+ "Jspreadsheet: It is not possible to delete the last column"
+ );
+ }
+ }
+ };
+
+ /**
+ * Get selected 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 selected 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].textContent,
+ jexcel.getColumnNameFromId([i, j])
+ );
+ }
+ }
+
+ if (obj.options.detachForUpdates) {
+ el.insertBefore(obj.content, obj.pagination);
+ }
+ }
+
+ // Update footers
+ if (obj.options.footers) {
+ obj.setFooter();
+ }
+
+ // Update corner position
+ setTimeout(function () {
+ obj.updateCornerPosition();
+ }, 0);
+ };
+
+ /**
+ * Readonly
+ */
+ obj.isReadOnly = function (cell) {
+ if ((cell = obj.getCell(cell))) {
+ return cell.classList.contains("readonly") ? true : false;
+ }
+ };
+
+ /**
+ * Readonly
+ */
+ obj.setReadOnly = function (cell, state) {
+ if ((cell = obj.getCell(cell))) {
+ if (state) {
+ cell.classList.add("readonly");
+ } else {
+ cell.classList.remove("readonly");
+ }
+ }
+ };
+
+ /**
+ * Show row
+ */
+ obj.showRow = function (rowNumber) {
+ obj.rows[rowNumber].style.display = "";
+ };
+
+ /**
+ * Hide row
+ */
+ obj.hideRow = function (rowNumber) {
+ obj.rows[rowNumber].style.display = "none";
+ };
+
+ /**
+ * Show column
+ */
+ obj.showColumn = function (colNumber) {
+ obj.headers[colNumber].style.display = "";
+ obj.colgroup[colNumber].style.display = "";
+ if (obj.filter && obj.filter.children.length > colNumber + 1) {
+ obj.filter.children[colNumber + 1].style.display = "";
+ }
+ for (var j = 0; j < obj.options.data.length; j++) {
+ obj.records[j][colNumber].style.display = "";
+ }
+
+ // Update footers
+ if (obj.options.footers) {
+ obj.setFooter();
+ }
+
+ obj.resetSelection();
+ };
+
+ /**
+ * Hide column
+ */
+ obj.hideColumn = function (colNumber) {
+ obj.headers[colNumber].style.display = "none";
+ obj.colgroup[colNumber].style.display = "none";
+ if (obj.filter && obj.filter.children.length > colNumber + 1) {
+ obj.filter.children[colNumber + 1].style.display = "none";
+ }
+ for (var j = 0; j < obj.options.data.length; j++) {
+ obj.records[j][colNumber].style.display = "none";
+ }
+
+ // Update footers
+ if (obj.options.footers) {
+ obj.setFooter();
+ }
+
+ obj.resetSelection();
+ };
+
+ /**
+ * 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(","));
+ }
+ };
+
+ // Range with $ remove $
+ expression = expression.replace(/\$?([A-Z]+)\$?([0-9]+)/g, "$1$2");
+
+ var tokens = expression.match(/([A-Z]+[0-9]+)\:([A-Z]+[0-9]+)/g);
+ if (tokens && tokens.length) {
+ tokensUpdate(tokens);
+ }
+
+ // 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 {
+ // Expressions to be used in the parsing
+ var formulaExpressions = {};
+
+ 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
+ formulaExpressions[tokens[i]] = null;
+ } else {
+ if (
+ value == Number(value) &&
+ obj.options.autoCasting == true
+ ) {
+ // Number
+ formulaExpressions[tokens[i]] = Number(value);
+ } else {
+ // Trying any formatted number
+ var number = obj.parseNumber(value, position[0]);
+ if (obj.options.autoCasting == true && number) {
+ formulaExpressions[tokens[i]] = number;
+ } else {
+ formulaExpressions[tokens[i]] = '"' + value + '"';
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Convert formula to javascript
+ try {
+ var res = jexcel.formula(
+ expression.substr(1),
+ formulaExpressions,
+ x,
+ y,
+ obj
+ );
+ } catch (e) {
+ var res = "#ERROR";
+ console.log(e);
+ }
+
+ 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.isInteger(Number(number[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.options.filters == true) &&
+ obj.results
+ ) {
+ var results = obj.results;
+ } else {
+ var results = obj.rows;
+ }
+
+ // Per page
+ var quantityPerPage = 100;
+
+ // pageNumber
+ if (pageNumber == null || pageNumber == -1) {
+ // Last page
+ pageNumber = Math.ceil(results.length / quantityPerPage) - 1;
+ }
+
+ var startRow = pageNumber * quantityPerPage;
+ var finalRow = pageNumber * quantityPerPage + quantityPerPage;
+ if (finalRow > results.length) {
+ finalRow = results.length;
+ }
+ startRow = finalRow - 100;
+ if (startRow < 0) {
+ startRow = 0;
+ }
+
+ // Appeding items
+ for (var j = startRow; j < finalRow; j++) {
+ if (
+ (obj.options.search == true || obj.options.filters == 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.options.filters == 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.options.filters == 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.options.filters == 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.options.filters == 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.options.filters == 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.options.filters == 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.options.filters == 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.options.filters == true) &&
+ obj.results
+ ) {
+ var results = obj.results;
+ } else {
+ var results = obj.rows;
+ }
+
+ // Per page
+ var quantityPerPage = parseInt(obj.options.pagination);
+
+ // pageNumber
+ if (pageNumber == null || pageNumber == -1) {
+ // Last page
+ pageNumber = Math.ceil(results.length / quantityPerPage) - 1;
+ }
+
+ // Page number
+ obj.pageNumber = pageNumber;
+
+ var startRow = pageNumber * quantityPerPage;
+ var finalRow = pageNumber * quantityPerPage + quantityPerPage;
+ if (finalRow > results.length) {
+ finalRow = results.length;
+ }
+ if (startRow < 0) {
+ startRow = 0;
+ }
+
+ // Reset container
+ while (obj.tbody.firstChild) {
+ obj.tbody.removeChild(obj.tbody.firstChild);
+ }
+
+ // Appeding items
+ for (var j = startRow; j < finalRow; j++) {
+ if (
+ (obj.options.search == true || obj.options.filters == 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.options.filters == 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 = "";
+
+ // Get data
+ data += obj.copy(
+ false,
+ obj.options.csvDelimiter,
+ true,
+ includeHeaders,
+ true
+ );
+
+ // Download element
+ var blob = new Blob(["\uFEFF" + data], {
+ type: "text/csv;charset=utf-8;",
+ });
+
+ // IE Compatibility
+ if (window.navigator && window.navigator.msSaveOrOpenBlob) {
+ window.navigator.msSaveOrOpenBlob(
+ blob,
+ obj.options.csvFileName + ".csv"
+ );
+ } else {
+ // Download element
+ var pom = document.createElement("a");
+ var url = URL.createObjectURL(blob);
+ pom.href = url;
+ pom.setAttribute("download", obj.options.csvFileName + ".csv");
+ document.body.appendChild(pom);
+ pom.click();
+ pom.parentNode.removeChild(pom);
+ }
+ }
+ };
+
+ /**
+ * Initializes a new history record for undo/redo
+ *
+ * @return null
+ */
+ obj.setHistory = function (changes) {
+ if (obj.ignoreHistory != true) {
+ // Increment and get the current history index
+ var index = ++obj.historyIndex;
+
+ // Slice the array to discard undone changes
+ obj.history = obj.history = obj.history.slice(0, index + 1);
+
+ // Keep history
+ obj.history[index] = changes;
+ }
+ };
+
+ /**
+ * Copy method
+ *
+ * @param bool highlighted - Get only highlighted cells
+ * @param delimiter - \t default to keep compatibility with excel
+ * @return string value
+ */
+ obj.copy = function (
+ highlighted,
+ delimiter,
+ returnData,
+ includeHeaders,
+ download
+ ) {
+ if (!delimiter) {
+ delimiter = "\t";
+ }
+
+ var div = new RegExp(delimiter, "ig");
+
+ // Controls
+ var header = [];
+ var col = [];
+ var colLabel = [];
+ var row = [];
+ var rowLabel = [];
+ var x = obj.options.data[0].length;
+ var y = obj.options.data.length;
+ var tmp = "";
+ var copyHeader = false;
+ var headers = "";
+ var nestedHeaders = "";
+ var numOfCols = 0;
+ var numOfRows = 0;
+
+ // Partial copy
+ var copyX = 0;
+ var copyY = 0;
+ var isPartialCopy = true;
+ // Go through the columns to get the data
+ for (var j = 0; j < y; j++) {
+ for (var i = 0; i < x; i++) {
+ // If cell is highlighted
+ if (
+ !highlighted ||
+ obj.records[j][i].classList.contains("highlight")
+ ) {
+ if (copyX <= i) {
+ copyX = i;
+ }
+ if (copyY <= j) {
+ copyY = j;
+ }
+ }
+ }
+ }
+ if (x === copyX + 1 && y === copyY + 1) {
+ isPartialCopy = false;
+ }
+
+ if (
+ (download && obj.options.includeHeadersOnDownload == true) ||
+ (!download &&
+ obj.options.includeHeadersOnCopy == true &&
+ !isPartialCopy) ||
+ includeHeaders
+ ) {
+ // 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])
+ ) {
+ tmp = [obj.options.nestedHeaders];
+ } else {
+ tmp = obj.options.nestedHeaders;
+ }
+
+ for (var j = 0; j < tmp.length; j++) {
+ var nested = [];
+ for (var i = 0; i < tmp[j].length; i++) {
+ var colspan = parseInt(tmp[j][i].colspan);
+ nested.push(tmp[j][i].title);
+ for (var c = 0; c < colspan - 1; c++) {
+ nested.push("");
+ }
+ }
+ nestedHeaders += nested.join(delimiter) + "\r\n";
+ }
+ }
+
+ copyHeader = true;
+ }
+
+ // Reset container
+ obj.style = [];
+
+ // Go through the columns to get the data
+ for (var j = 0; j < y; j++) {
+ col = [];
+ colLabel = [];
+
+ for (var i = 0; i < x; i++) {
+ // If cell is highlighted
+ if (
+ !highlighted ||
+ obj.records[j][i].classList.contains("highlight")
+ ) {
+ if (copyHeader == true) {
+ header.push(obj.headers[i].textContent);
+ }
+ // Values
+ var value = obj.options.data[j][i];
+ if (
+ value.match &&
+ (value.match(div) ||
+ value.match(/,/g) ||
+ value.match(/\n/) ||
+ value.match(/\"/))
+ ) {
+ value = value.replace(new RegExp('"', "g"), '""');
+ value = '"' + value + '"';
+ }
+ col.push(value);
+
+ // Labels
+ if (
+ obj.options.columns[i].type == "checkbox" ||
+ obj.options.columns[i].type == "radio"
+ ) {
+ var label = value;
+ } else {
+ if (obj.options.stripHTMLOnCopy == true) {
+ var label = obj.records[j][i].textContent;
+ } else {
+ var label = obj.records[j][i].innerHTML;
+ }
+ if (
+ label.match &&
+ (label.match(div) ||
+ label.match(/,/g) ||
+ label.match(/\n/) ||
+ label.match(/\"/))
+ ) {
+ // Scape double quotes
+ label = label.replace(new RegExp('"', "g"), '""');
+ label = '"' + label + '"';
+ }
+ }
+ colLabel.push(label);
+
+ // Get style
+ tmp = obj.records[j][i].getAttribute("style");
+ tmp = tmp.replace("display: none;", "");
+ obj.style.push(tmp ? tmp : "");
+ }
+ }
+
+ if (col.length) {
+ if (copyHeader) {
+ numOfCols = col.length;
+ row.push(header.join(delimiter));
+ }
+ row.push(col.join(delimiter));
+ }
+ if (colLabel.length) {
+ numOfRows++;
+ if (copyHeader) {
+ rowLabel.push(header.join(delimiter));
+ copyHeader = false;
+ }
+ rowLabel.push(colLabel.join(delimiter));
+ }
+ }
+
+ if (x == numOfCols && y == numOfRows) {
+ headers = nestedHeaders;
+ }
+
+ // Final string
+ var str = headers + row.join("\r\n");
+ var strLabel = headers + 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
+ if (!returnData) {
+ 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;
+ };
+
+ /**
+ * Jspreadsheet 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) {
+ // If the pasted column is out of range, create it if possible
+ if (obj.options.allowInsertColumn == true) {
+ obj.insertColumn();
+ // Otherwise skip the pasted data that overflows
+ } else {
+ break;
+ }
+ }
+ colIndex = obj.right.get(colIndex, rowIndex);
+ }
+ }
+
+ j++;
+ if (data[j]) {
+ if (rowIndex >= obj.rows.length - 1) {
+ // If the pasted row is out of range, create it if possible
+ if (obj.options.allowInsertRow == true) {
+ obj.insertRow();
+ // Otherwise skip the pasted data that overflows
+ } else {
+ break;
+ }
+ }
+ rowIndex = obj.down.get(x, rowIndex);
+ }
+ }
+
+ // Select the new cells
+ obj.updateSelectionFromCoords(x, y, colIndex, rowIndex);
+
+ // Update history
+ obj.setHistory({
+ action: "setValue",
+ records: records,
+ selection: obj.selectedCell,
+ newStyle: newStyle,
+ oldStyle: oldStyle,
+ });
+
+ // Update table
+ obj.updateTable();
+
+ // Paste event
+ obj.dispatch("onpaste", el, data);
+
+ // On after changes
+ obj.onafterchanges(el, records);
+ }
+
+ obj.removeCopyingSelection();
+ };
+
+ /**
+ * Remove copying border
+ */
+ obj.removeCopyingSelection = function () {
+ var copying = document.querySelectorAll(".jexcel .copying");
+ for (var i = 0; i < copying.length; i++) {
+ copying[i].classList.remove("copying");
+ copying[i].classList.remove("copying-left");
+ copying[i].classList.remove("copying-right");
+ copying[i].classList.remove("copying-top");
+ copying[i].classList.remove("copying-bottom");
+ }
+ };
+
+ /**
+ * Process row
+ */
+ obj.historyProcessRow = function (type, historyRecord) {
+ var rowIndex = !historyRecord.insertBefore
+ ? historyRecord.rowNumber + 1
+ : +historyRecord.rowNumber;
+
+ if (obj.options.search == true) {
+ if (obj.results && obj.results.length != obj.rows.length) {
+ obj.resetSearch();
+ }
+ }
+
+ // Remove row
+ if (type == 1) {
+ var numOfRows = historyRecord.numOfRows;
+ // Remove nodes
+ for (var j = rowIndex; j < numOfRows + rowIndex; j++) {
+ obj.rows[j].parentNode.removeChild(obj.rows[j]);
+ }
+ // Remove references
+ obj.records.splice(rowIndex, numOfRows);
+ obj.options.data.splice(rowIndex, numOfRows);
+ obj.rows.splice(rowIndex, numOfRows);
+
+ obj.conditionalSelectionUpdate(1, rowIndex, numOfRows + rowIndex - 1);
+ } else {
+ // Insert data
+ obj.records = jexcel.injectArray(
+ obj.records,
+ rowIndex,
+ historyRecord.rowRecords
+ );
+ obj.options.data = jexcel.injectArray(
+ obj.options.data,
+ rowIndex,
+ historyRecord.rowData
+ );
+ obj.rows = jexcel.injectArray(
+ obj.rows,
+ rowIndex,
+ historyRecord.rowNode
+ );
+ // Insert nodes
+ var index = 0;
+ for (var j = rowIndex; j < historyRecord.numOfRows + rowIndex; j++) {
+ obj.tbody.insertBefore(
+ historyRecord.rowNode[index],
+ obj.tbody.children[j]
+ );
+ index++;
+ }
+ }
+
+ // Respect pagination
+ if (obj.options.pagination > 0) {
+ obj.page(obj.pageNumber);
+ }
+
+ obj.updateTableReferences();
+ };
+
+ /**
+ * Process column
+ */
+ obj.historyProcessColumn = function (type, historyRecord) {
+ var columnIndex = !historyRecord.insertBefore
+ ? historyRecord.columnNumber + 1
+ : historyRecord.columnNumber;
+
+ // Remove column
+ if (type == 1) {
+ var numOfColumns = historyRecord.numOfColumns;
+
+ obj.options.columns.splice(columnIndex, numOfColumns);
+ for (var i = columnIndex; i < numOfColumns + columnIndex; i++) {
+ obj.headers[i].parentNode.removeChild(obj.headers[i]);
+ obj.colgroup[i].parentNode.removeChild(obj.colgroup[i]);
+ }
+ obj.headers.splice(columnIndex, numOfColumns);
+ obj.colgroup.splice(columnIndex, numOfColumns);
+ for (var j = 0; j < historyRecord.data.length; j++) {
+ for (var i = columnIndex; i < numOfColumns + columnIndex; i++) {
+ obj.records[j][i].parentNode.removeChild(obj.records[j][i]);
+ }
+ obj.records[j].splice(columnIndex, numOfColumns);
+ obj.options.data[j].splice(columnIndex, numOfColumns);
+ }
+ // Process footers
+ if (obj.options.footers) {
+ for (var j = 0; j < obj.options.footers.length; j++) {
+ obj.options.footers[j].splice(columnIndex, numOfColumns);
+ }
+ }
+ } else {
+ // Insert data
+ obj.options.columns = jexcel.injectArray(
+ obj.options.columns,
+ columnIndex,
+ historyRecord.columns
+ );
+ obj.headers = jexcel.injectArray(
+ obj.headers,
+ columnIndex,
+ historyRecord.headers
+ );
+ obj.colgroup = jexcel.injectArray(
+ obj.colgroup,
+ columnIndex,
+ historyRecord.colgroup
+ );
+
+ var index = 0;
+ for (
+ var i = columnIndex;
+ i < historyRecord.numOfColumns + columnIndex;
+ i++
+ ) {
+ obj.headerContainer.insertBefore(
+ historyRecord.headers[index],
+ obj.headerContainer.children[i + 1]
+ );
+ obj.colgroupContainer.insertBefore(
+ historyRecord.colgroup[index],
+ obj.colgroupContainer.children[i + 1]
+ );
+ index++;
+ }
+
+ for (var j = 0; j < historyRecord.data.length; j++) {
+ obj.options.data[j] = jexcel.injectArray(
+ obj.options.data[j],
+ columnIndex,
+ historyRecord.data[j]
+ );
+ obj.records[j] = jexcel.injectArray(
+ obj.records[j],
+ columnIndex,
+ historyRecord.records[j]
+ );
+ var index = 0;
+ for (
+ var i = columnIndex;
+ i < historyRecord.numOfColumns + columnIndex;
+ i++
+ ) {
+ obj.rows[j].insertBefore(
+ historyRecord.records[j][index],
+ obj.rows[j].children[i + 1]
+ );
+ index++;
+ }
+ }
+ // Process footers
+ if (obj.options.footers) {
+ for (var j = 0; j < obj.options.footers.length; j++) {
+ obj.options.footers[j] = jexcel.injectArray(
+ obj.options.footers[j],
+ columnIndex,
+ historyRecord.footers[j]
+ );
+ }
+ }
+ }
+
+ // Adjust nested headers
+ if (obj.options.nestedHeaders && obj.options.nestedHeaders.length > 0) {
+ // Flexible way to handle nestedheaders
+ if (obj.options.nestedHeaders[0] && obj.options.nestedHeaders[0][0]) {
+ for (var j = 0; j < obj.options.nestedHeaders.length; j++) {
+ if (type == 1) {
+ var colspan =
+ parseInt(
+ obj.options.nestedHeaders[j][
+ obj.options.nestedHeaders[j].length - 1
+ ].colspan
+ ) - historyRecord.numOfColumns;
+ } else {
+ var colspan =
+ parseInt(
+ obj.options.nestedHeaders[j][
+ obj.options.nestedHeaders[j].length - 1
+ ].colspan
+ ) + historyRecord.numOfColumns;
+ }
+ obj.options.nestedHeaders[j][
+ obj.options.nestedHeaders[j].length - 1
+ ].colspan = colspan;
+ obj.thead.children[j].children[
+ obj.thead.children[j].children.length - 1
+ ].setAttribute("colspan", colspan);
+ }
+ } else {
+ if (type == 1) {
+ var colspan =
+ parseInt(obj.options.nestedHeaders[0].colspan) -
+ historyRecord.numOfColumns;
+ } else {
+ var colspan =
+ parseInt(obj.options.nestedHeaders[0].colspan) +
+ historyRecord.numOfColumns;
+ }
+ obj.options.nestedHeaders[0].colspan = colspan;
+ obj.thead.children[0].children[
+ obj.thead.children[0].children.length - 1
+ ].setAttribute("colspan", colspan);
+ }
+ }
+
+ obj.updateTableReferences();
+ };
+
+ /**
+ * Undo last action
+ */
+ obj.undo = function () {
+ // Ignore events and history
+ var ignoreEvents = obj.ignoreEvents ? true : false;
+ var ignoreHistory = obj.ignoreHistory ? true : false;
+
+ obj.ignoreEvents = true;
+ obj.ignoreHistory = true;
+
+ // Records
+ var records = [];
+
+ // Update cells
+ if (obj.historyIndex >= 0) {
+ // History
+ var historyRecord = obj.history[obj.historyIndex--];
+
+ if (historyRecord.action == "insertRow") {
+ obj.historyProcessRow(1, historyRecord);
+ } else if (historyRecord.action == "deleteRow") {
+ obj.historyProcessRow(0, historyRecord);
+ } else if (historyRecord.action == "insertColumn") {
+ obj.historyProcessColumn(1, historyRecord);
+ } else if (historyRecord.action == "deleteColumn") {
+ obj.historyProcessColumn(0, historyRecord);
+ } else if (historyRecord.action == "moveRow") {
+ obj.moveRow(historyRecord.newValue, historyRecord.oldValue);
+ } else if (historyRecord.action == "moveColumn") {
+ obj.moveColumn(historyRecord.newValue, historyRecord.oldValue);
+ } else if (historyRecord.action == "setMerge") {
+ obj.removeMerge(historyRecord.column, historyRecord.data);
+ } else if (historyRecord.action == "setStyle") {
+ obj.setStyle(historyRecord.oldValue, null, null, 1);
+ } else if (historyRecord.action == "setWidth") {
+ obj.setWidth(historyRecord.column, historyRecord.oldValue);
+ } else if (historyRecord.action == "setHeight") {
+ obj.setHeight(historyRecord.row, historyRecord.oldValue);
+ } else if (historyRecord.action == "setHeader") {
+ obj.setHeader(historyRecord.column, historyRecord.oldValue);
+ } else if (historyRecord.action == "setComments") {
+ obj.setComments(
+ historyRecord.column,
+ historyRecord.oldValue[0],
+ historyRecord.oldValue[1]
+ );
+ } else if (historyRecord.action == "orderBy") {
+ var rows = [];
+ for (var j = 0; j < historyRecord.rows.length; j++) {
+ rows[historyRecord.rows[j]] = j;
+ }
+ obj.updateOrderArrow(
+ historyRecord.column,
+ historyRecord.order ? 0 : 1
+ );
+ obj.updateOrder(rows);
+ } else if (historyRecord.action == "setValue") {
+ // Redo for changes in cells
+ for (var i = 0; i < historyRecord.records.length; i++) {
+ records.push({
+ x: historyRecord.records[i].x,
+ y: historyRecord.records[i].y,
+ newValue: historyRecord.records[i].oldValue,
+ });
+
+ if (historyRecord.oldStyle) {
+ obj.resetStyle(historyRecord.oldStyle);
+ }
+ }
+ // Update records
+ obj.setValue(records);
+
+ // Update selection
+ if (historyRecord.selection) {
+ obj.updateSelectionFromCoords(
+ historyRecord.selection[0],
+ historyRecord.selection[1],
+ historyRecord.selection[2],
+ historyRecord.selection[3]
+ );
+ }
+ }
+ }
+ obj.ignoreEvents = ignoreEvents;
+ obj.ignoreHistory = ignoreHistory;
+
+ // Events
+ obj.dispatch("onundo", el, historyRecord);
+ };
+
+ /**
+ * Redo previously undone action
+ */
+ obj.redo = function () {
+ // Ignore events and history
+ var ignoreEvents = obj.ignoreEvents ? true : false;
+ var ignoreHistory = obj.ignoreHistory ? true : false;
+
+ obj.ignoreEvents = true;
+ obj.ignoreHistory = true;
+
+ // Records
+ var records = [];
+
+ // Update cells
+ if (obj.historyIndex < obj.history.length - 1) {
+ // History
+ var historyRecord = obj.history[++obj.historyIndex];
+
+ if (historyRecord.action == "insertRow") {
+ obj.historyProcessRow(0, historyRecord);
+ } else if (historyRecord.action == "deleteRow") {
+ obj.historyProcessRow(1, historyRecord);
+ } else if (historyRecord.action == "insertColumn") {
+ obj.historyProcessColumn(0, historyRecord);
+ } else if (historyRecord.action == "deleteColumn") {
+ obj.historyProcessColumn(1, historyRecord);
+ } else if (historyRecord.action == "moveRow") {
+ obj.moveRow(historyRecord.oldValue, historyRecord.newValue);
+ } else if (historyRecord.action == "moveColumn") {
+ obj.moveColumn(historyRecord.oldValue, historyRecord.newValue);
+ } else if (historyRecord.action == "setMerge") {
+ obj.setMerge(
+ historyRecord.column,
+ historyRecord.colspan,
+ historyRecord.rowspan,
+ 1
+ );
+ } else if (historyRecord.action == "setStyle") {
+ obj.setStyle(historyRecord.newValue, null, null, 1);
+ } else if (historyRecord.action == "setWidth") {
+ obj.setWidth(historyRecord.column, historyRecord.newValue);
+ } else if (historyRecord.action == "setHeight") {
+ obj.setHeight(historyRecord.row, historyRecord.newValue);
+ } else if (historyRecord.action == "setHeader") {
+ obj.setHeader(historyRecord.column, historyRecord.newValue);
+ } else if (historyRecord.action == "setComments") {
+ obj.setComments(
+ historyRecord.column,
+ historyRecord.newValue[0],
+ historyRecord.newValue[1]
+ );
+ } else if (historyRecord.action == "orderBy") {
+ obj.updateOrderArrow(historyRecord.column, historyRecord.order);
+ obj.updateOrder(historyRecord.rows);
+ } else if (historyRecord.action == "setValue") {
+ obj.setValue(historyRecord.records);
+ // Redo for changes in cells
+ for (var i = 0; i < historyRecord.records.length; i++) {
+ if (historyRecord.oldStyle) {
+ obj.resetStyle(historyRecord.newStyle);
+ }
+ }
+ // Update selection
+ if (historyRecord.selection) {
+ obj.updateSelectionFromCoords(
+ historyRecord.selection[0],
+ historyRecord.selection[1],
+ historyRecord.selection[2],
+ historyRecord.selection[3]
+ );
+ }
+ }
+ }
+ obj.ignoreEvents = ignoreEvents;
+ obj.ignoreHistory = ignoreHistory;
+
+ // Events
+ obj.dispatch("onredo", el, historyRecord);
+ };
+
+ /**
+ * Get dropdown value from key
+ */
+ obj.getDropDownValue = function (column, key) {
+ var value = [];
+
+ if (obj.options.columns[column] && obj.options.columns[column].source) {
+ // Create array from source
+ var combo = [];
+ var source = obj.options.columns[column].source;
+
+ for (var i = 0; i < source.length; i++) {
+ if (typeof source[i] == "object") {
+ combo[source[i].id] = source[i].name;
+ } else {
+ combo[source[i]] = source[i];
+ }
+ }
+
+ // Guarantee single multiple compatibility
+ var keys = Array.isArray(key) ? key : ("" + key).split(";");
+
+ for (var i = 0; i < keys.length; i++) {
+ if (typeof keys[i] === "object") {
+ value.push(combo[keys[i].id]);
+ } else {
+ if (combo[keys[i]]) {
+ value.push(combo[keys[i]]);
+ }
+ }
+ }
+ } else {
+ console.error("Invalid column");
+ }
+
+ return value.length > 0 ? value.join("; ") : "";
+ };
+
+ /**
+ * From stack overflow 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: obj.options.method,
+ data: obj.options.requestVariables,
+ 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: obj.options.method,
+ data: obj.options.requestVariables,
+ 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 () {
+ if (obj.options.about === true) {
+ alert(Version().print());
+ } else {
+ 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 - 10
+ ) {
+ 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) {
+ // Must check if the previous column is hidden or not to determin whether the width shoule be added or not!
+ if (obj.options.columns[i - 1].type !== "hidden") {
+ 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;
+ el.jspreadsheet = obj;
+
+ obj.init();
+
+ return obj;
+ };
+
+ // Define dictionary
+ jexcel.setDictionary = function (o) {
+ jSuites.setDictionary(o);
+ };
+
+ // Define extensions
+ jexcel.setExtensions = function (o) {
+ var k = Object.keys(o);
+ for (var i = 0; i < k.length; i++) {
+ if (typeof o[k[i]] === "function") {
+ jexcel[k[i]] = o[k[i]];
+ if (jexcel.license && typeof o[k[i]].license == "function") {
+ o[k[i]].license(jexcel.license);
+ }
+ }
+ }
+ };
+
+ /**
+ * Formulas
+ */
+ if (typeof formula !== "undefined") {
+ jexcel.formula = formula;
+ }
+ jexcel.version = Version;
+
+ jexcel.current = null;
+ jexcel.timeControl = null;
+ jexcel.timeControlLoading = null;
+
+ const destroyEvents = function (root) {
+ 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.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) {
+ destroyEvents(root);
+ jexcel = null;
+ }
+ }
+ };
+
+ jexcel.build = function (root) {
+ destroyEvents(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 (
+ ["calendar", "html"].includes(
+ jexcel.current.options.columns[jexcel.current.edition[2]].type
+ )
+ ) {
+ 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 == 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 == 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
+ e.preventDefault();
+ if (
+ jspreadsheet.current.options.columns[columnId].type ==
+ "checkbox" ||
+ jspreadsheet.current.options.columns[columnId].type ==
+ "radio"
+ ) {
+ jspreadsheet.current.setCheckRadioValue();
+ } else {
+ // Start edition
+ jspreadsheet.current.openEditor(
+ jspreadsheet.current.records[rowId][columnId],
+ true
+ );
+ }
+ } else if (e.keyCode == 113) {
+ // Start edition with current content F2
+ jexcel.current.openEditor(
+ jexcel.current.records[rowId][columnId],
+ false
+ );
+ } else if (
+ e.keyCode == 8 ||
+ (e.keyCode >= 48 && e.keyCode <= 57) ||
+ (e.keyCode >= 96 && e.keyCode <= 111) ||
+ (e.keyCode >= 187 && e.keyCode <= 190) ||
+ ((String.fromCharCode(e.keyCode) == e.key ||
+ String.fromCharCode(e.keyCode).toLowerCase() ==
+ e.key.toLowerCase()) &&
+ jexcel.validLetter(String.fromCharCode(e.keyCode)))
+ ) {
+ // Start edition
+ jexcel.current.openEditor(
+ jexcel.current.records[rowId][columnId],
+ true
+ );
+ // Prevent entries in the calendar
+ if (
+ jexcel.current.options.columns[columnId].type ==
+ "calendar"
+ ) {
+ e.preventDefault();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ } else {
+ if (e.target.classList.contains("jexcel_search")) {
+ if (jexcel.timeControl) {
+ clearTimeout(jexcel.timeControl);
+ }
+
+ jexcel.timeControl = setTimeout(function () {
+ jexcel.current.search(e.target.value);
+ }, 200);
+ }
+ }
+ }
+ };
+
+ jexcel.isMouseAction = false;
+
+ jexcel.mouseDownControls = function (e) {
+ e = e || window.event;
+ if (e.buttons) {
+ var mouseButton = e.buttons;
+ } else if (e.button) {
+ var mouseButton = e.button;
+ } else {
+ var mouseButton = e.which;
+ }
+
+ // Get elements
+ var jexcelTable = jexcel.getElement(e.target);
+
+ if (jexcelTable[0]) {
+ if (jexcel.current != jexcelTable[0].jexcel) {
+ if (jexcel.current) {
+ if (jexcel.current.edition) {
+ jexcel.current.closeEditor(jexcel.current.edition[0], true);
+ }
+ jexcel.current.resetSelection();
+ }
+ jexcel.current = jexcelTable[0].jexcel;
+ }
+ } else {
+ if (jexcel.current) {
+ if (jexcel.current.edition) {
+ jexcel.current.closeEditor(jexcel.current.edition[0], true);
+ }
+
+ jexcel.current.resetSelection(true);
+ jexcel.current = null;
+ }
+ }
+
+ if (jexcel.current && mouseButton == 1) {
+ if (e.target.classList.contains("jexcel_selectall")) {
+ if (jexcel.current) {
+ jexcel.current.selectAll();
+ }
+ } else if (e.target.classList.contains("jexcel_corner")) {
+ if (jexcel.current.options.editable == true) {
+ jexcel.current.selectedCorner = true;
+ }
+ } else {
+ // Header found
+ if (jexcelTable[1] == 1) {
+ var columnId = e.target.getAttribute("data-x");
+ if (columnId) {
+ // Update cursor
+ var info = e.target.getBoundingClientRect();
+ if (
+ jexcel.current.options.columnResize == true &&
+ info.width - e.offsetX < 6
+ ) {
+ // Resize helper
+ jexcel.current.resizing = {
+ mousePosition: e.pageX,
+ column: columnId,
+ width: info.width,
+ };
+
+ // Border indication
+ jexcel.current.headers[columnId].classList.add("resizing");
+ for (var j = 0; j < jexcel.current.records.length; j++) {
+ if (jexcel.current.records[j][columnId]) {
+ jexcel.current.records[j][columnId].classList.add("resizing");
+ }
+ }
+ } else if (
+ jexcel.current.options.columnDrag == true &&
+ info.height - e.offsetY < 6
+ ) {
+ if (jexcel.current.isColMerged(columnId).length) {
+ console.error(
+ "Jspreadsheet: 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(
+ "Jspreadsheet: This row is part of a merged cell"
+ );
+ } else if (
+ jexcel.current.options.search == true &&
+ jexcel.current.results
+ ) {
+ console.error(
+ "Jspreadsheet: 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.textContent == "<") {
+ jexcel.current.page(0);
+ } else if (e.target.textContent == ">") {
+ jexcel.current.page(e.target.getAttribute("title") - 1);
+ } else {
+ jexcel.current.page(e.target.textContent - 1);
+ }
+ }
+ }
+
+ if (jexcel.current.edition) {
+ jexcel.isMouseAction = false;
+ } else {
+ jexcel.isMouseAction = true;
+ }
+ } else {
+ jexcel.isMouseAction = false;
+ }
+ };
+
+ jexcel.mouseUpControls = function (e) {
+ if (jexcel.current) {
+ // Update cell size
+ if (jexcel.current.resizing) {
+ // Columns to be updated
+ if (jexcel.current.resizing.column) {
+ // New width
+ var newWidth =
+ jexcel.current.colgroup[
+ jexcel.current.resizing.column
+ ].getAttribute("width");
+ // Columns
+ var columns = jexcel.current.getSelectedColumns();
+ if (columns.length > 1) {
+ var currentWidth = [];
+ for (var i = 0; i < columns.length; i++) {
+ currentWidth.push(
+ parseInt(
+ jexcel.current.colgroup[columns[i]].getAttribute("width")
+ )
+ );
+ }
+ // Previous width
+ var index = columns.indexOf(
+ parseInt(jexcel.current.resizing.column)
+ );
+ currentWidth[index] = jexcel.current.resizing.width;
+ jexcel.current.setWidth(columns, newWidth, currentWidth);
+ } else {
+ jexcel.current.setWidth(
+ jexcel.current.resizing.column,
+ newWidth,
+ jexcel.current.resizing.width
+ );
+ }
+ // Remove border
+ jexcel.current.headers[
+ jexcel.current.resizing.column
+ ].classList.remove("resizing");
+ for (var j = 0; j < jexcel.current.records.length; j++) {
+ if (jexcel.current.records[j][jexcel.current.resizing.column]) {
+ jexcel.current.records[j][
+ jexcel.current.resizing.column
+ ].classList.remove("resizing");
+ }
+ }
+ } else {
+ // Remove Class
+ jexcel.current.rows[
+ jexcel.current.resizing.row
+ ].children[0].classList.remove("resizing");
+ var newHeight =
+ jexcel.current.rows[jexcel.current.resizing.row].getAttribute(
+ "height"
+ );
+ jexcel.current.setHeight(
+ jexcel.current.resizing.row,
+ newHeight,
+ jexcel.current.resizing.height
+ );
+ // Remove border
+ jexcel.current.resizing.element.classList.remove("resizing");
+ }
+ // Reset resizing helper
+ jexcel.current.resizing = null;
+ } else if (jexcel.current.dragging) {
+ // Reset dragging helper
+ if (jexcel.current.dragging) {
+ if (jexcel.current.dragging.column) {
+ // Target
+ var columnId = e.target.getAttribute("data-x");
+ // Remove move style
+ jexcel.current.headers[
+ jexcel.current.dragging.column
+ ].classList.remove("dragging");
+ for (var j = 0; j < jexcel.current.rows.length; j++) {
+ if (jexcel.current.records[j][jexcel.current.dragging.column]) {
+ jexcel.current.records[j][
+ jexcel.current.dragging.column
+ ].classList.remove("dragging");
+ }
+ }
+ for (var i = 0; i < jexcel.current.headers.length; i++) {
+ jexcel.current.headers[i].classList.remove("dragging-left");
+ jexcel.current.headers[i].classList.remove("dragging-right");
+ }
+ // Update position
+ if (columnId) {
+ if (
+ jexcel.current.dragging.column !=
+ jexcel.current.dragging.destination
+ ) {
+ jexcel.current.moveColumn(
+ jexcel.current.dragging.column,
+ jexcel.current.dragging.destination
+ );
+ }
+ }
+ } else {
+ if (jexcel.current.dragging.element.nextSibling) {
+ var position = parseInt(
+ jexcel.current.dragging.element.nextSibling.getAttribute(
+ "data-y"
+ )
+ );
+ if (jexcel.current.dragging.row < position) {
+ position -= 1;
+ }
+ } else {
+ var position = parseInt(
+ jexcel.current.dragging.element.previousSibling.getAttribute(
+ "data-y"
+ )
+ );
+ }
+ if (
+ jexcel.current.dragging.row != jexcel.current.dragging.destination
+ ) {
+ jexcel.current.moveRow(
+ jexcel.current.dragging.row,
+ position,
+ true
+ );
+ }
+ jexcel.current.dragging.element.classList.remove("dragging");
+ }
+ jexcel.current.dragging = null;
+ }
+ } else {
+ // Close any corner selection
+ if (jexcel.current.selectedCorner) {
+ jexcel.current.selectedCorner = false;
+
+ // Data to be copied
+ if (jexcel.current.selection.length > 0) {
+ // Copy data
+ jexcel.current.copyData(
+ jexcel.current.selection[0],
+ jexcel.current.selection[jexcel.current.selection.length - 1]
+ );
+
+ // Remove selection
+ jexcel.current.removeCopySelection();
+ }
+ }
+ }
+ }
+
+ // Clear any time control
+ if (jexcel.timeControl) {
+ clearTimeout(jexcel.timeControl);
+ jexcel.timeControl = null;
+ }
+
+ // Mouse up
+ jexcel.isMouseAction = false;
+ };
+
+ // Mouse move controls
+ jexcel.mouseMoveControls = function (e) {
+ e = e || window.event;
+ if (e.buttons) {
+ var mouseButton = e.buttons;
+ } else if (e.button) {
+ var mouseButton = e.button;
+ } else {
+ var mouseButton = e.which;
+ }
+
+ if (!mouseButton) {
+ jexcel.isMouseAction = false;
+ }
+
+ if (jexcel.current) {
+ if (jexcel.isMouseAction == true) {
+ // Resizing is ongoing
+ if (jexcel.current.resizing) {
+ if (jexcel.current.resizing.column) {
+ var width = e.pageX - jexcel.current.resizing.mousePosition;
+
+ if (jexcel.current.resizing.width + width > 0) {
+ var tempWidth = jexcel.current.resizing.width + width;
+ jexcel.current.colgroup[
+ jexcel.current.resizing.column
+ ].setAttribute("width", tempWidth);
+
+ jexcel.current.updateCornerPosition();
+ }
+ } else {
+ var height = e.pageY - jexcel.current.resizing.mousePosition;
+
+ if (jexcel.current.resizing.height + height > 0) {
+ var tempHeight = jexcel.current.resizing.height + height;
+ jexcel.current.rows[jexcel.current.resizing.row].setAttribute(
+ "height",
+ tempHeight
+ );
+
+ jexcel.current.updateCornerPosition();
+ }
+ }
+ } else if (jexcel.current.dragging) {
+ if (jexcel.current.dragging.column) {
+ var columnId = e.target.getAttribute("data-x");
+ if (columnId) {
+ if (jexcel.current.isColMerged(columnId).length) {
+ console.error(
+ "Jspreadsheet: 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 {
+ var rowId = e.target.getAttribute("data-y");
+ if (rowId) {
+ if (jexcel.current.isRowMerged(rowId).length) {
+ console.error(
+ "Jspreadsheet: This row is part of a merged cell."
+ );
+ } else {
+ var target =
+ e.target.clientHeight / 2 > e.offsetY
+ ? e.target.parentNode.nextSibling
+ : e.target.parentNode;
+ if (jexcel.current.dragging.element != target) {
+ e.target.parentNode.parentNode.insertBefore(
+ jexcel.current.dragging.element,
+ target
+ );
+ jexcel.current.dragging.destination =
+ Array.prototype.indexOf.call(
+ jexcel.current.dragging.element.parentNode.children,
+ jexcel.current.dragging.element
+ );
+ }
+ }
+ }
+ }
+ }
+ } else {
+ 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.resizing || jexcel.current.dragging) {
+ } 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) {
+ if (
+ x < parseInt(jexcel.current.selectedCell[0]) ||
+ x > parseInt(jexcel.current.selectedCell[2]) ||
+ y < parseInt(jexcel.current.selectedCell[1]) ||
+ y > parseInt(jexcel.current.selectedCell[3])
+ ) {
+ jexcel.current.updateSelectionFromCoords(x, y, x, y);
+ }
+
+ // Table found
+ var items = jexcel.current.options.contextMenu(
+ jexcel.current,
+ x,
+ y,
+ e
+ );
+ // The id is depending on header and body
+ jexcel.current.contextMenu.contextmenu.open(e, items);
+ // Avoid the real one
+ e.preventDefault();
+ }
+ }
+ }
+ }
+ };
+
+ jexcel.touchStartControls = function (e) {
+ var jexcelTable = jexcel.getElement(e.target);
+
+ if (jexcelTable[0]) {
+ if (jexcel.current != jexcelTable[0].jexcel) {
+ if (jexcel.current) {
+ jexcel.current.resetSelection();
+ }
+ jexcel.current = jexcelTable[0].jexcel;
+ }
+ } else {
+ if (jexcel.current) {
+ jexcel.current.resetSelection();
+ jexcel.current = null;
+ }
+ }
+
+ if (jexcel.current) {
+ if (!jexcel.current.edition) {
+ var columnId = e.target.getAttribute("data-x");
+ var rowId = e.target.getAttribute("data-y");
+
+ if (columnId && rowId) {
+ jexcel.current.updateSelectionFromCoords(columnId, rowId);
+
+ jexcel.timeControl = setTimeout(function () {
+ // Keep temporary reference to the element
+ if (jexcel.current.options.columns[columnId].type == "color") {
+ jexcel.tmpElement = null;
+ } else {
+ jexcel.tmpElement = e.target;
+ }
+ jexcel.current.openEditor(e.target, false, e);
+ }, 500);
+ }
+ }
+ }
+ };
+
+ jexcel.touchEndControls = function (e) {
+ // Clear any time control
+ if (jexcel.timeControl) {
+ clearTimeout(jexcel.timeControl);
+ jexcel.timeControl = null;
+ // Element
+ if (
+ jexcel.tmpElement &&
+ jexcel.tmpElement.children[0].tagName == "INPUT"
+ ) {
+ jexcel.tmpElement.children[0].focus();
+ }
+ jexcel.tmpElement = null;
+ }
+ };
+
+ /**
+ * Jexcel extensions
+ */
+
+ jexcel.tabs = function (tabs, result) {
+ var instances = [];
+ // Create tab container
+ if (!tabs.classList.contains("jexcel_tabs")) {
+ tabs.innerHTML = "";
+ tabs.classList.add("jexcel_tabs");
+ tabs.jexcel = [];
+
+ var div = document.createElement("div");
+ var headers = tabs.appendChild(div);
+ var div = document.createElement("div");
+ var content = tabs.appendChild(div);
+ } else {
+ var headers = tabs.children[0];
+ var content = tabs.children[1];
+ }
+
+ var spreadsheet = [];
+ var link = [];
+ for (var i = 0; i < result.length; i++) {
+ // Spreadsheet container
+ spreadsheet[i] = document.createElement("div");
+ spreadsheet[i].classList.add("jexcel_tab");
+ var worksheet = jexcel(spreadsheet[i], result[i]);
+ content.appendChild(spreadsheet[i]);
+ instances[i] = tabs.jexcel.push(worksheet);
+
+ // Tab link
+ link[i] = document.createElement("div");
+ link[i].classList.add("jexcel_tab_link");
+ link[i].setAttribute("data-spreadsheet", tabs.jexcel.length - 1);
+ link[i].innerHTML = result[i].sheetName;
+ link[i].onclick = function () {
+ for (var j = 0; j < headers.children.length; j++) {
+ headers.children[j].classList.remove("selected");
+ content.children[j].style.display = "none";
+ }
+ var i = this.getAttribute("data-spreadsheet");
+ content.children[i].style.display = "block";
+ headers.children[i].classList.add("selected");
+ };
+ headers.appendChild(link[i]);
+ }
+
+ // First tab
+ for (var j = 0; j < headers.children.length; j++) {
+ headers.children[j].classList.remove("selected");
+ content.children[j].style.display = "none";
+ }
+ headers.children[headers.children.length - 1].classList.add("selected");
+ content.children[headers.children.length - 1].style.display = "block";
+
+ return instances;
+ };
+
+ // Compability to older versions
+ jexcel.createTabs = jexcel.tabs;
+
+ jexcel.fromSpreadsheet = function (file, __callback) {
+ var convert = function (workbook) {
+ var spreadsheets = [];
+ workbook.SheetNames.forEach(function (sheetName) {
+ var spreadsheet = {};
+ spreadsheet.rows = [];
+ spreadsheet.columns = [];
+ spreadsheet.data = [];
+ spreadsheet.style = {};
+ spreadsheet.sheetName = sheetName;
+
+ // Column widths
+ var temp = workbook.Sheets[sheetName]["!cols"];
+ if (temp && temp.length) {
+ for (var i = 0; i < temp.length; i++) {
+ spreadsheet.columns[i] = {};
+ if (temp[i] && temp[i].wpx) {
+ spreadsheet.columns[i].width = temp[i].wpx + "px";
+ }
+ }
+ }
+ // Rows heights
+ var temp = workbook.Sheets[sheetName]["!rows"];
+ if (temp && temp.length) {
+ for (var i = 0; i < temp.length; i++) {
+ if (temp[i] && temp[i].hpx) {
+ spreadsheet.rows[i] = {};
+ spreadsheet.rows[i].height = temp[i].hpx + "px";
+ }
+ }
+ }
+ // Merge cells
+ var temp = workbook.Sheets[sheetName]["!merges"];
+ if (temp && temp.length > 0) {
+ spreadsheet.mergeCells = [];
+ for (var i = 0; i < temp.length; i++) {
+ var x1 = temp[i].s.c;
+ var y1 = temp[i].s.r;
+ var x2 = temp[i].e.c;
+ var y2 = temp[i].e.r;
+ var key = jexcel.getColumnNameFromId([x1, y1]);
+ spreadsheet.mergeCells[key] = [x2 - x1 + 1, y2 - y1 + 1];
+ }
+ }
+ // Data container
+ var max_x = 0;
+ var max_y = 0;
+ var temp = Object.keys(workbook.Sheets[sheetName]);
+ for (var i = 0; i < temp.length; i++) {
+ if (temp[i].substr(0, 1) != "!") {
+ var cell = workbook.Sheets[sheetName][temp[i]];
+ var info = jexcel.getIdFromColumnName(temp[i], true);
+ if (!spreadsheet.data[info[1]]) {
+ spreadsheet.data[info[1]] = [];
+ }
+ spreadsheet.data[info[1]][info[0]] = cell.f ? "=" + cell.f : cell.w;
+ if (max_x < info[0]) {
+ max_x = info[0];
+ }
+ if (max_y < info[1]) {
+ max_y = info[1];
+ }
+ // Style
+ if (cell.style && Object.keys(cell.style).length > 0) {
+ spreadsheet.style[temp[i]] = cell.style;
+ }
+ if (cell.s && cell.s.fgColor) {
+ if (spreadsheet.style[temp[i]]) {
+ spreadsheet.style[temp[i]] += ";";
+ }
+ spreadsheet.style[temp[i]] +=
+ "background-color:#" + cell.s.fgColor.rgb;
+ }
+ }
+ }
+ var numColumns = spreadsheet.columns;
+ for (var j = 0; j <= max_y; j++) {
+ for (var i = 0; i <= max_x; i++) {
+ if (!spreadsheet.data[j]) {
+ spreadsheet.data[j] = [];
+ }
+ if (!spreadsheet.data[j][i]) {
+ if (numColumns < i) {
+ spreadsheet.data[j][i] = "";
+ }
+ }
+ }
+ }
+ spreadsheets.push(spreadsheet);
+ });
+
+ return spreadsheets;
+ };
+
+ var oReq;
+ oReq = new XMLHttpRequest();
+ oReq.open("GET", file, true);
+
+ if (typeof Uint8Array !== "undefined") {
+ oReq.responseType = "arraybuffer";
+ oReq.onload = function (e) {
+ var arraybuffer = oReq.response;
+ var data = new Uint8Array(arraybuffer);
+ var wb = XLSX.read(data, {
+ type: "array",
+ cellFormula: true,
+ cellStyles: true,
+ });
+ __callback(convert(wb));
+ };
+ } else {
+ oReq.setRequestHeader("Accept-Charset", "x-user-defined");
+ oReq.onreadystatechange = function () {
+ if (oReq.readyState == 4 && oReq.status == 200) {
+ var ff = convertResponseBodyToText(oReq.responseBody);
+ var wb = XLSX.read(ff, {
+ type: "binary",
+ cellFormula: true,
+ cellStyles: true,
+ });
+ __callback(convert(wb));
+ }
+ };
+ }
+
+ oReq.send();
+ };
+
+ /**
+ * Valid international letter
+ */
+
+ jexcel.validLetter = function (text) {
+ var regex =
+ /([\u0041-\u005A\u0061-\u007A\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC-\u0400-\u04FF']+)/g;
+ return text.match(regex) ? 1 : 0;
+ };
+
+ /**
+ * Helper injectArray
+ */
+ jexcel.injectArray = function (o, idx, arr) {
+ return o.slice(0, idx).concat(arr).concat(o.slice(idx));
+ };
+
+ /**
+ * Get letter based on a number
+ *
+ * @param integer i
+ * @return string letter
+ */
+ jexcel.getColumnName = function (i) {
+ var letter = "";
+ if (i > 701) {
+ letter += String.fromCharCode(64 + parseInt(i / 676));
+ letter += String.fromCharCode(64 + parseInt((i % 676) / 26));
+ } else if (i > 25) {
+ letter += String.fromCharCode(64 + parseInt(i / 26));
+ }
+ letter += String.fromCharCode(65 + (i % 26));
+
+ return letter;
+ };
+
+ /**
+ * Convert excel like column to jexcel id
+ *
+ * @param string id
+ * @return string id
+ */
+ jexcel.getIdFromColumnName = function (id, arr) {
+ // Get the letters
+ var t = /^[a-zA-Z]+/.exec(id);
+
+ if (t) {
+ // Base 26 calculation
+ var code = 0;
+ for (var i = 0; i < t[0].length; i++) {
+ code +=
+ parseInt(t[0].charCodeAt(i) - 64) * Math.pow(26, t[0].length - 1 - i);
+ }
+ code--;
+ // Make sure jexcel starts on zero
+ if (code < 0) {
+ code = 0;
+ }
+
+ // Number
+ var number = parseInt(/[0-9]+$/.exec(id));
+ if (number > 0) {
+ number--;
+ }
+
+ if (arr == true) {
+ id = [code, number];
+ } else {
+ id = code + "-" + number;
+ }
+ }
+
+ return id;
+ };
+
+ /**
+ * Convert jexcel id to excel like column name
+ *
+ * @param string id
+ * @return string id
+ */
+ jexcel.getColumnNameFromId = function (cellId) {
+ if (!Array.isArray(cellId)) {
+ cellId = cellId.split("-");
+ }
+
+ return (
+ jexcel.getColumnName(parseInt(cellId[0])) + (parseInt(cellId[1]) + 1)
+ );
+ };
+
+ /**
+ * Verify element inside jexcel table
+ *
+ * @param string id
+ * @return string id
+ */
+ jexcel.getElement = function (element) {
+ var jexcelSection = 0;
+ var jexcelElement = 0;
+
+ function path(element) {
+ if (element.className) {
+ if (element.classList.contains("jexcel_container")) {
+ jexcelElement = element;
+ }
+ }
+
+ if (element.tagName == "THEAD") {
+ jexcelSection = 1;
+ } else if (element.tagName == "TBODY") {
+ jexcelSection = 2;
+ }
+
+ if (element.parentNode) {
+ if (!jexcelElement) {
+ path(element.parentNode);
+ }
+ }
+ }
+
+ path(element);
+
+ return [jexcelElement, jexcelSection];
+ };
+
+ jexcel.doubleDigitFormat = function (v) {
+ v = "" + v;
+ if (v.length == 1) {
+ v = "0" + v;
+ }
+ return v;
+ };
+
+ jexcel.createFromTable = function (el, options) {
+ if (el.tagName != "TABLE") {
+ console.log("Element is not a table");
+ } else {
+ // Configuration
+ if (!options) {
+ options = {};
+ }
+ options.columns = [];
+ options.data = [];
+
+ // Colgroup
+ var colgroup = el.querySelectorAll("colgroup > col");
+ if (colgroup.length) {
+ // Get column width
+ for (var i = 0; i < colgroup.length; i++) {
+ var width = colgroup[i].style.width;
+ if (!width) {
+ var width = colgroup[i].getAttribute("width");
+ }
+ // Set column width
+ if (width) {
+ if (!options.columns[i]) {
+ options.columns[i] = {};
+ }
+ options.columns[i].width = width;
+ }
+ }
+ }
+
+ // Parse header
+ var parseHeader = function (header) {
+ // Get width information
+ var info = header.getBoundingClientRect();
+ var width = info.width > 50 ? info.width : 50;
+
+ // Create column option
+ if (!options.columns[i]) {
+ options.columns[i] = {};
+ }
+ if (header.getAttribute("data-celltype")) {
+ options.columns[i].type = header.getAttribute("data-celltype");
+ } else {
+ options.columns[i].type = "text";
+ }
+ options.columns[i].width = width + "px";
+ options.columns[i].title = header.innerHTML;
+ options.columns[i].align = header.style.textAlign || "center";
+
+ if ((info = header.getAttribute("name"))) {
+ options.columns[i].name = info;
+ }
+ if ((info = header.getAttribute("id"))) {
+ options.columns[i].id = info;
+ }
+ };
+
+ // Headers
+ var nested = [];
+ var headers = el.querySelectorAll(":scope > thead > tr");
+ if (headers.length) {
+ for (var j = 0; j < headers.length - 1; j++) {
+ var cells = [];
+ for (var i = 0; i < headers[j].children.length; i++) {
+ var row = {
+ title: headers[j].children[i].textContent,
+ colspan: headers[j].children[i].getAttribute("colspan") || 1,
+ };
+ cells.push(row);
+ }
+ nested.push(cells);
+ }
+ // 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 classes = {};
+
+ var content = el.querySelectorAll(":scope > tr, :scope > tbody > tr");
+ for (var j = 0; j < content.length; j++) {
+ options.data[rowNumber] = [];
+ if (
+ options.parseTableFirstRowAsHeader == true &&
+ !headers.length &&
+ j == 0
+ ) {
+ for (var i = 0; i < content[j].children.length; i++) {
+ parseHeader(content[j].children[i]);
+ }
+ } else {
+ for (var i = 0; i < content[j].children.length; i++) {
+ // WickedGrid formula compatibility
+ var value = content[j].children[i].getAttribute("data-formula");
+ if (value) {
+ if (value.substr(0, 1) != "=") {
+ value = "=" + value;
+ }
+ } else {
+ var value = content[j].children[i].innerHTML;
+ }
+ options.data[rowNumber].push(value);
+
+ // Key
+ var cellName = jexcel.getColumnNameFromId([i, j]);
+
+ // Classes
+ var tmp = content[j].children[i].getAttribute("class");
+ if (tmp) {
+ classes[cellName] = tmp;
+ }
+
+ // Merged cells
+ var mergedColspan =
+ parseInt(content[j].children[i].getAttribute("colspan")) || 0;
+ var mergedRowspan =
+ parseInt(content[j].children[i].getAttribute("rowspan")) || 0;
+ if (mergedColspan || mergedRowspan) {
+ mergeCells[cellName] = [mergedColspan || 1, mergedRowspan || 1];
+ }
+
+ // Avoid problems with hidden cells
+ if (
+ (s =
+ content[j].children[i].style &&
+ content[j].children[i].style.display == "none")
+ ) {
+ content[j].children[i].style.display = "";
+ }
+ // Get style
+ var s = content[j].children[i].getAttribute("style");
+ if (s) {
+ style[cellName] = s;
+ }
+ // Bold
+ if (content[j].children[i].classList.contains("styleBold")) {
+ if (style[cellName]) {
+ style[cellName] += "; font-weight:bold;";
+ } else {
+ style[cellName] = "font-weight:bold;";
+ }
+ }
+ }
+
+ // Row Height
+ if (content[j].style && content[j].style.height) {
+ rows[j] = { height: content[j].style.height };
+ }
+
+ // Index
+ rowNumber++;
+ }
+ }
+
+ // Nested
+ if (Object.keys(nested).length > 0) {
+ options.nestedHeaders = nested;
+ }
+ // 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;
+ }
+ // Classes
+ if (Object.keys(classes).length > 0) {
+ options.classes = classes;
+ }
+
+ var content = el.querySelectorAll("tfoot tr");
+ if (content.length) {
+ var footers = [];
+ for (var j = 0; j < content.length; j++) {
+ var footer = [];
+ for (var i = 0; i < content[j].children.length; i++) {
+ footer.push(content[j].children[i].textContent);
+ }
+ footers.push(footer);
+ }
+ if (Object.keys(footers).length > 0) {
+ options.footers = footers;
+ }
+ }
+ // 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;
+ }
+ };
+
+ // Helpers
+ jexcel.helpers = (function () {
+ var component = {};
+
+ /**
+ * Get carret position for one element
+ */
+ component.getCaretIndex = function (e) {
+ if (this.config.root) {
+ var d = this.config.root;
+ } else {
+ var d = window;
+ }
+ var pos = 0;
+ var s = d.getSelection();
+ if (s) {
+ if (s.rangeCount !== 0) {
+ var r = s.getRangeAt(0);
+ var p = r.cloneRange();
+ p.selectNodeContents(e);
+ p.setEnd(r.endContainer, r.endOffset);
+ pos = p.toString().length;
+ }
+ }
+ return pos;
+ };
+
+ /**
+ * Invert keys and values
+ */
+ component.invert = function (o) {
+ var d = [];
+ var k = Object.keys(o);
+ for (var i = 0; i < k.length; i++) {
+ d[o[k[i]]] = k[i];
+ }
+ return d;
+ };
+
+ /**
+ * Get letter based on a number
+ *
+ * @param integer i
+ * @return string letter
+ */
+ component.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;
+ };
+
+ /**
+ * Get column name from coords
+ */
+ component.getColumnNameFromCoords = function (x, y) {
+ return component.getColumnName(parseInt(x)) + (parseInt(y) + 1);
+ };
+
+ component.getCoordsFromColumnName = function (columnName) {
+ // Get the letters
+ var t = /^[a-zA-Z]+/.exec(columnName);
+
+ 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 jspreadsheet starts on zero
+ if (code < 0) {
+ code = 0;
+ }
+
+ // Number
+ var number = parseInt(/[0-9]+$/.exec(columnName)) || null;
+ if (number > 0) {
+ number--;
+ }
+
+ return [code, number];
+ }
+ };
+
+ /**
+ * Extract json configuration from a TABLE DOM tag
+ */
+ component.createFromTable = function () {};
+
+ /**
+ * Helper injectArray
+ */
+ component.injectArray = function (o, idx, arr) {
+ return o.slice(0, idx).concat(arr).concat(o.slice(idx));
+ };
+
+ /**
+ * Parse CSV string to JS array
+ */
+ component.parseCSV = function (str, delimiter) {
+ // user-supplied delimeter or default comma
+ delimiter = delimiter || ",";
+
+ // Final data
+ var col = 0;
+ var row = 0;
+ var num = 0;
+ var data = [[]];
+ var limit = 0;
+ var flag = null;
+ var inside = false;
+ var closed = false;
+
+ // Go over all chars
+ for (var i = 0; i < str.length; i++) {
+ // Create new row
+ if (!data[row]) {
+ data[row] = [];
+ }
+ // Create new column
+ if (!data[row][col]) {
+ data[row][col] = "";
+ }
+
+ // Ignore
+ if (str[i] == "\r") {
+ continue;
+ }
+
+ // New row
+ if (
+ (str[i] == "\n" || str[i] == delimiter) &&
+ (inside == false || closed == true || !flag)
+ ) {
+ // Restart flags
+ flag = null;
+ inside = false;
+ closed = false;
+
+ if (data[row][col][0] == '"') {
+ var val = data[row][col].trim();
+ if (val[val.length - 1] == '"') {
+ data[row][col] = val.substr(1, val.length - 2);
+ }
+ }
+
+ // Go to the next cell
+ if (str[i] == "\n") {
+ // New line
+ col = 0;
+ row++;
+ } else {
+ // New column
+ col++;
+ if (col > limit) {
+ // Keep the reference of max column
+ limit = col;
+ }
+ }
+ } else {
+ // Inside quotes
+ if (str[i] == '"') {
+ inside = !inside;
+ }
+
+ if (flag === null) {
+ flag = inside;
+ if (flag == true) {
+ continue;
+ }
+ } else if (flag === true && !closed) {
+ if (str[i] == '"') {
+ if (str[i + 1] == '"') {
+ inside = true;
+ data[row][col] += str[i];
+ i++;
+ } else {
+ closed = true;
+ }
+ continue;
+ }
+ }
+
+ data[row][col] += str[i];
+ }
+ }
+
+ // Make sure a square matrix is generated
+ for (var j = 0; j < data.length; j++) {
+ for (var i = 0; i <= limit; i++) {
+ if (data[j][i] === undefined) {
+ data[j][i] = "";
+ }
+ }
+ }
+
+ return data;
+ };
+
+ return component;
+ })();
+
+ /**
+ * Jquery Support
+ */
+ if (typeof jQuery != "undefined") {
+ (function ($) {
+ $.fn.jspreadsheet = $.fn.jexcel = function (mixed) {
+ var spreadsheetContainer = $(this).get(0);
+ if (!spreadsheetContainer.jexcel) {
+ return jexcel($(this).get(0), arguments[0]);
+ } else {
+ if (Array.isArray(spreadsheetContainer.jexcel)) {
+ return spreadsheetContainer.jexcel[mixed][arguments[1]].apply(
+ this,
+ Array.prototype.slice.call(arguments, 2)
+ );
+ } else {
+ return spreadsheetContainer.jexcel[mixed].apply(
+ this,
+ Array.prototype.slice.call(arguments, 1)
+ );
+ }
+ }
+ };
+ })(jQuery);
+ }
+
+ return jexcel;
+});
diff --git a/public/javascripts/jsuites/jsuites.js b/public/javascripts/jsuites/jsuites.js
new file mode 100644
index 00000000000..0088464e5aa
--- /dev/null
+++ b/public/javascripts/jsuites/jsuites.js
@@ -0,0 +1,12414 @@
+/**
+ * (c) jSuites Javascript Web Components
+ *
+ * Website: https://jsuites.net
+ * 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 = {};
+
+var Version = '4.17.5';
+
+var Events = function() {
+
+ document.jsuitesComponents = [];
+
+ var find = function(DOMElement, component) {
+ if (DOMElement[component.type] && DOMElement[component.type] == component) {
+ return true;
+ }
+ if (DOMElement.component && DOMElement.component == component) {
+ return true;
+ }
+ if (DOMElement.parentNode) {
+ return find(DOMElement.parentNode, component);
+ }
+ return false;
+ }
+
+ var isOpened = function(e) {
+ if (document.jsuitesComponents && document.jsuitesComponents.length > 0) {
+ for (var i = 0; i < document.jsuitesComponents.length; i++) {
+ if (document.jsuitesComponents[i] && ! find(e, document.jsuitesComponents[i])) {
+ document.jsuitesComponents[i].close();
+ }
+ }
+ }
+ }
+
+ // Width of the border
+ var cornerSize = 15;
+
+ // Current element
+ var element = null;
+
+ // Controllers
+ var editorAction = false;
+
+ // Event state
+ var state = {
+ x: null,
+ y: null,
+ }
+
+ // Tooltip element
+ var tooltip = document.createElement('div')
+ tooltip.classList.add('jtooltip');
+
+ // Events
+ var mouseDown = function(e) {
+ // Check if this is the floating
+ var item = jSuites.findElement(e.target, 'jpanel');
+ // Jfloating found
+ if (item && ! item.classList.contains("readonly")) {
+ // Add focus to the chart container
+ item.focus();
+ // Keep the tracking information
+ var rect = e.target.getBoundingClientRect();
+ editorAction = {
+ e: item,
+ x: e.clientX,
+ y: e.clientY,
+ w: rect.width,
+ h: rect.height,
+ d: item.style.cursor,
+ resizing: item.style.cursor ? true : false,
+ actioned: false,
+ }
+
+ // Make sure width and height styling is OK
+ if (! item.style.width) {
+ item.style.width = rect.width + 'px';
+ }
+
+ if (! item.style.height) {
+ item.style.height = rect.height + 'px';
+ }
+
+ // Remove any selection from the page
+ var s = window.getSelection();
+ if (s.rangeCount) {
+ for (var i = 0; i < s.rangeCount; i++) {
+ s.removeRange(s.getRangeAt(i));
+ }
+ }
+
+ e.preventDefault();
+ e.stopPropagation();
+ } else {
+ // No floating action found
+ editorAction = false;
+ }
+
+ // Verify current components tracking
+ 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;
+ }
+
+ // Which component I am clicking
+ var path = e.path || (e.composedPath && e.composedPath());
+
+ // If path available get the first element in the chain
+ if (path) {
+ element = path[0];
+ } else {
+ // Try to guess using the coordinates
+ if (e.target && e.target.shadowRoot) {
+ var d = e.target.shadowRoot;
+ } else {
+ var d = document;
+ }
+ // Get the first target element
+ element = d.elementFromPoint(x, y);
+ }
+
+ isOpened(element);
+ }
+
+ var mouseUp = function(e) {
+ if (editorAction && editorAction.e) {
+ if (typeof(editorAction.e.refresh) == 'function' && state.actioned) {
+ editorAction.e.refresh();
+ }
+ editorAction.e.style.cursor = '';
+ }
+
+ // Reset
+ state = {
+ x: null,
+ y: null,
+ }
+
+ editorAction = false;
+ }
+
+ var mouseMove = function(e) {
+ if (editorAction) {
+ var x = e.clientX || e.pageX;
+ var y = e.clientY || e.pageY;
+
+ // Action on going
+ if (! editorAction.resizing) {
+ if (state.x == null && state.y == null) {
+ state.x = x;
+ state.y = y;
+ }
+
+ var dx = x - state.x;
+ var dy = y - state.y;
+ var top = editorAction.e.offsetTop + dy;
+ var left = editorAction.e.offsetLeft + dx;
+
+ // Update position
+ editorAction.e.style.top = top + 'px';
+ editorAction.e.style.left = left + 'px';
+ editorAction.e.style.cursor = "move";
+
+ state.x = x;
+ state.y = y;
+
+
+ // Update element
+ if (typeof(editorAction.e.refresh) == 'function') {
+ state.actioned = true;
+ editorAction.e.refresh('position', top, left);
+ }
+ } else {
+ var width = null;
+ var height = null;
+
+ if (editorAction.d == 'e-resize' || editorAction.d == 'ne-resize' || editorAction.d == 'se-resize') {
+ // Update width
+ width = editorAction.w + (x - editorAction.x);
+ editorAction.e.style.width = width + 'px';
+
+ // Update Height
+ if (e.shiftKey) {
+ var newHeight = (x - editorAction.x) * (editorAction.h / editorAction.w);
+ height = editorAction.h + newHeight;
+ editorAction.e.style.height = height + 'px';
+ } else {
+ var newHeight = false;
+ }
+ }
+
+ if (! newHeight) {
+ if (editorAction.d == 's-resize' || editorAction.d == 'se-resize' || editorAction.d == 'sw-resize') {
+ height = editorAction.h + (y - editorAction.y);
+ editorAction.e.style.height = height + 'px';
+ }
+ }
+
+ // Update element
+ if (typeof(editorAction.e.refresh) == 'function') {
+ state.actioned = true;
+ editorAction.e.refresh('dimensions', width, height);
+ }
+ }
+ } else {
+ // Resizing action
+ var item = jSuites.findElement(e.target, 'jpanel');
+ // Found eligible component
+ if (item) {
+ if (item.getAttribute('tabindex')) {
+ var rect = item.getBoundingClientRect();
+ if (e.clientY - rect.top < cornerSize) {
+ if (rect.width - (e.clientX - rect.left) < cornerSize) {
+ item.style.cursor = 'ne-resize';
+ } else if (e.clientX - rect.left < cornerSize) {
+ item.style.cursor = 'nw-resize';
+ } else {
+ item.style.cursor = 'n-resize';
+ }
+ } else if (rect.height - (e.clientY - rect.top) < cornerSize) {
+ if (rect.width - (e.clientX - rect.left) < cornerSize) {
+ item.style.cursor = 'se-resize';
+ } else if (e.clientX - rect.left < cornerSize) {
+ item.style.cursor = 'sw-resize';
+ } else {
+ item.style.cursor = 's-resize';
+ }
+ } else if (rect.width - (e.clientX - rect.left) < cornerSize) {
+ item.style.cursor = 'e-resize';
+ } else if (e.clientX - rect.left < cornerSize) {
+ item.style.cursor = 'w-resize';
+ } else {
+ item.style.cursor = '';
+ }
+ }
+ }
+ }
+ }
+
+ var mouseOver = function(e) {
+ var message = e.target.getAttribute('data-tooltip');
+ if (message) {
+ // Instructions
+ tooltip.innerText = message;
+
+ // Position
+ 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;
+ }
+
+ tooltip.style.top = y + 'px';
+ tooltip.style.left = x + 'px';
+ document.body.appendChild(tooltip);
+ } else if (tooltip.innerText) {
+ tooltip.innerText = '';
+ document.body.removeChild(tooltip);
+ }
+ }
+
+ var dblClick = function(e) {
+ var item = jSuites.findElement(e.target, 'jpanel');
+ if (item && typeof(item.dblclick) == 'function') {
+ // Create edition
+ item.dblclick(e);
+ }
+ }
+
+ var contextMenu = function(e) {
+ var item = document.activeElement;
+ if (item && typeof(item.contextmenu) == 'function') {
+ // Create edition
+ item.contextmenu(e);
+
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ } else {
+ // Search for possible context menus
+ item = jSuites.findElement(e.target, function(o) {
+ return o.tagName && o.getAttribute('aria-contextmenu-id');
+ });
+
+ if (item) {
+ var o = document.querySelector('#' + item);
+ if (! o) {
+ console.error('JSUITES: contextmenu id not found: ' + item);
+ } else {
+ o.contextmenu.open(e);
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ }
+ }
+ }
+ }
+
+ var keyDown = function(e) {
+ var item = document.activeElement;
+ if (item) {
+ if (e.key == "Delete" && typeof(item.delete) == 'function') {
+ item.delete();
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ }
+ }
+
+ if (document.jsuitesComponents && document.jsuitesComponents.length) {
+ if (item = document.jsuitesComponents[document.jsuitesComponents.length - 1]) {
+ if (e.key == "Escape" && typeof(item.isOpened) == 'function' && typeof(item.close) == 'function') {
+ if (item.isOpened()) {
+ item.close();
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ }
+ }
+ }
+ }
+ }
+
+ document.addEventListener('mouseup', mouseUp);
+ document.addEventListener("mousedown", mouseDown);
+ document.addEventListener('mousemove', mouseMove);
+ document.addEventListener('mouseover', mouseOver);
+ document.addEventListener('dblclick', dblClick);
+ document.addEventListener('keydown', keyDown);
+ document.addEventListener('contextmenu', contextMenu);
+}
+
+/**
+ * Global jsuites event
+ */
+if (typeof(document) !== "undefined" && ! document.jsuitesComponents) {
+ Events();
+}
+
+jSuites.version = Version;
+
+jSuites.setExtensions = function(o) {
+ if (typeof(o) == 'object') {
+ var k = Object.keys(o);
+ for (var i = 0; i < k.length; i++) {
+ jSuites[k[i]] = o[k[i]];
+ }
+ }
+}
+
+jSuites.tracking = function(component, state) {
+ if (state == true) {
+ document.jsuitesComponents = document.jsuitesComponents.filter(function(v) {
+ return v !== null;
+ });
+
+ // Start after all events
+ setTimeout(function() {
+ document.jsuitesComponents.push(component);
+ }, 0);
+
+ } else {
+ var index = document.jsuitesComponents.indexOf(component);
+ if (index >= 0) {
+ document.jsuitesComponents.splice(index, 1);
+ }
+ }
+}
+
+/**
+ * Get or set a property from a JSON from a string.
+ */
+jSuites.path = function(str, val) {
+ str = str.split('.');
+ if (str.length) {
+ var o = this;
+ var p = null;
+ while (str.length > 1) {
+ // Get the property
+ p = str.shift();
+ // Check if the property exists
+ if (o.hasOwnProperty(p)) {
+ o = o[p];
+ } else {
+ // Property does not exists
+ if (val === undefined) {
+ return undefined;
+ } else {
+ // Create the property
+ o[p] = {};
+ // Next property
+ o = o[p];
+ }
+ }
+ }
+ // Get the property
+ p = str.shift();
+ // Set or get the value
+ if (val !== undefined) {
+ o[p] = val;
+ // Success
+ return true;
+ } else {
+ // Return the value
+ if (o) {
+ return o[p];
+ }
+ }
+ }
+ // Something went wrong
+ return false;
+}
+
+// Update dictionary
+jSuites.setDictionary = function(d) {
+ if (! document.dictionary) {
+ document.dictionary = {}
+ }
+ // Replace the key into the dictionary and append the new ones
+ var k = Object.keys(d);
+ for (var i = 0; i < k.length; i++) {
+ document.dictionary[k[i]] = d[k[i]];
+ }
+
+ // Translations
+ var t = null;
+ for (var i = 0; i < jSuites.calendar.weekdays.length; i++) {
+ t = jSuites.translate(jSuites.calendar.weekdays[i]);
+ if (jSuites.calendar.weekdays[i]) {
+ jSuites.calendar.weekdays[i] = t;
+ jSuites.calendar.weekdaysShort[i] = t.substr(0,3);
+ }
+ }
+ for (var i = 0; i < jSuites.calendar.months.length; i++) {
+ t = jSuites.translate(jSuites.calendar.months[i]);
+ if (t) {
+ jSuites.calendar.months[i] = t;
+ jSuites.calendar.monthsShort[i] = t.substr(0,3);
+ }
+ }
+}
+
+// Translate
+jSuites.translate = function(t) {
+ if (typeof(document) !== "undefined" && document.dictionary) {
+ return document.dictionary[t] || t;
+ } else {
+ return t;
+ }
+}
+
+jSuites.ajax = (function(options, complete) {
+ if (Array.isArray(options)) {
+ // Create multiple request controller
+ var multiple = {
+ instance: [],
+ complete: complete,
+ }
+
+ if (options.length > 0) {
+ for (var i = 0; i < options.length; i++) {
+ options[i].multiple = multiple;
+ multiple.instance.push(jSuites.ajax(options[i]));
+ }
+ }
+
+ return multiple;
+ }
+
+ if (! options.data) {
+ options.data = {};
+ }
+
+ if (options.type) {
+ options.method = options.type;
+ }
+
+ // Default method
+ if (! options.method) {
+ options.method = 'GET';
+ }
+
+ // Default type
+ if (! options.dataType) {
+ options.dataType = 'json';
+ }
+
+ if (options.data) {
+ // Parse object to variables format
+ var parseData = function (value, key) {
+ var vars = [];
+ if (value) {
+ var keys = Object.keys(value);
+ if (keys.length) {
+ for (var i = 0; i < keys.length; i++) {
+ if (key) {
+ var k = key + '[' + keys[i] + ']';
+ } else {
+ var k = keys[i];
+ }
+
+ if (value[k] instanceof FileList) {
+ vars[k] = value[keys[i]];
+ } else if (value[keys[i]] === null || value[keys[i]] === undefined) {
+ vars[k] = '';
+ } else if (typeof(value[keys[i]]) == 'object') {
+ var r = parseData(value[keys[i]], k);
+ var o = Object.keys(r);
+ for (var j = 0; j < o.length; j++) {
+ vars[o[j]] = r[o[j]];
+ }
+ } else {
+ vars[k] = value[keys[i]];
+ }
+ }
+ }
+ }
+
+ return vars;
+ }
+
+ var d = parseData(options.data);
+ var k = Object.keys(d);
+
+ // Data form
+ if (options.method == 'GET') {
+ if (k.length) {
+ var data = [];
+ for (var i = 0; i < k.length; i++) {
+ data.push(k[i] + '=' + encodeURIComponent(d[k[i]]));
+ }
+
+ if (options.url.indexOf('?') < 0) {
+ options.url += '?';
+ }
+ options.url += data.join('&');
+ }
+ } else {
+ var data = new FormData();
+ for (var i = 0; i < k.length; i++) {
+ if (d[k[i]] instanceof FileList) {
+ if (d[k[i]].length) {
+ for (var j = 0; j < d[k[i]].length; j++) {
+ data.append(k[i], d[k[i]][j], d[k[i]][j].name);
+ }
+ }
+ } else {
+ data.append(k[i], d[k[i]]);
+ }
+ }
+ }
+ }
+
+ var httpRequest = new XMLHttpRequest();
+ httpRequest.open(options.method, options.url, true);
+ httpRequest.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
+
+ // Content type
+ if (options.contentType) {
+ httpRequest.setRequestHeader('Content-Type', options.contentType);
+ }
+
+ // Headers
+ if (options.method == 'POST') {
+ httpRequest.setRequestHeader('Accept', 'application/json');
+ } else {
+ if (options.dataType == 'blob') {
+ httpRequest.responseType = "blob";
+ } else {
+ if (! options.contentType) {
+ if (options.dataType == 'json') {
+ httpRequest.setRequestHeader('Content-Type', 'text/json');
+ } else if (options.dataType == 'html') {
+ httpRequest.setRequestHeader('Content-Type', 'text/html');
+ }
+ }
+ }
+ }
+
+ // 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);
+ }
+
+ // Before send
+ if (typeof(jSuites.ajax.beforeSend) == 'function') {
+ jSuites.ajax.beforeSend(httpRequest);
+ }
+
+ if (document.ajax && typeof(document.ajax.beforeSend) == 'function') {
+ document.ajax.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(err, result);
+ }
+ }
+ } else {
+ if (options.dataType == 'blob') {
+ var result = httpRequest.response;
+ } 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, httpRequest.status);
+ }
+ }
+
+ // Global queue
+ if (jSuites.ajax.queue && jSuites.ajax.queue.length > 0) {
+ jSuites.ajax.send(jSuites.ajax.queue.shift());
+ }
+
+ // Global complete method
+ if (jSuites.ajax.requests && jSuites.ajax.requests.length) {
+ // Get index of this request in the container
+ var index = jSuites.ajax.requests.indexOf(httpRequest);
+ // Remove from the ajax requests container
+ jSuites.ajax.requests.splice(index, 1);
+ // Deprected: Last one?
+ if (! jSuites.ajax.requests.length) {
+ // Object event
+ if (options.complete && typeof(options.complete) == 'function') {
+ options.complete(result);
+ }
+ }
+ // Group requests
+ if (options.group) {
+ if (jSuites.ajax.oncomplete && typeof(jSuites.ajax.oncomplete[options.group]) == 'function') {
+ if (! jSuites.ajax.pending(options.group)) {
+ jSuites.ajax.oncomplete[options.group]();
+ jSuites.ajax.oncomplete[options.group] = null;
+ }
+ }
+ }
+ // Multiple requests controller
+ if (options.multiple && options.multiple.instance) {
+ // Get index of this request in the container
+ var index = options.multiple.instance.indexOf(httpRequest);
+ // Remove from the ajax requests container
+ options.multiple.instance.splice(index, 1);
+ // If this is the last one call method complete
+ if (! options.multiple.instance.length) {
+ if (options.multiple.complete && typeof(options.multiple.complete) == 'function') {
+ options.multiple.complete(result);
+ }
+ }
+ }
+ }
+ }
+
+ // Keep the options
+ httpRequest.options = options;
+ // Data
+ httpRequest.data = data;
+
+ // Queue
+ if (options.queue == true && jSuites.ajax.requests.length > 0) {
+ jSuites.ajax.queue.push(httpRequest);
+ } else {
+ jSuites.ajax.send(httpRequest)
+ }
+
+ return httpRequest;
+});
+
+jSuites.ajax.send = function(httpRequest) {
+ if (httpRequest.data) {
+ if (Array.isArray(httpRequest.data)) {
+ httpRequest.send(httpRequest.data.join('&'));
+ } else {
+ httpRequest.send(httpRequest.data);
+ }
+ } else {
+ httpRequest.send();
+ }
+
+ jSuites.ajax.requests.push(httpRequest);
+}
+
+jSuites.ajax.exists = function(url, __callback) {
+ var http = new XMLHttpRequest();
+ http.open('HEAD', url, false);
+ http.send();
+ if (http.status) {
+ __callback(http.status);
+ }
+}
+
+jSuites.ajax.pending = function(group) {
+ var n = 0;
+ var o = jSuites.ajax.requests;
+ if (o && o.length) {
+ for (var i = 0; i < o.length; i++) {
+ if (! group || group == o[i].options.group) {
+ n++
+ }
+ }
+ }
+ return n;
+}
+
+jSuites.ajax.oncomplete = {};
+jSuites.ajax.requests = [];
+jSuites.ajax.queue = [];
+
+jSuites.alert = function(message) {
+ if (jSuites.getWindowWidth() < 800 && jSuites.dialog) {
+ jSuites.dialog.open({
+ title:'Alert',
+ message:message,
+ });
+ } else {
+ alert(message);
+ }
+}
+
+jSuites.animation = {};
+
+jSuites.animation.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);
+ }
+}
+
+jSuites.animation.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);
+ }
+}
+
+jSuites.animation.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);
+ }
+}
+
+jSuites.animation.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);
+ }
+}
+
+jSuites.animation.fadeIn = function(element, done) {
+ element.style.display = '';
+ element.classList.add('fade-in');
+ setTimeout(function() {
+ element.classList.remove('fade-in');
+ if (typeof(done) == 'function') {
+ done();
+ }
+ }, 2000);
+}
+
+jSuites.animation.fadeOut = function(element, done) {
+ element.classList.add('fade-out');
+ setTimeout(function() {
+ element.style.display = 'none';
+ element.classList.remove('fade-out');
+ if (typeof(done) == 'function') {
+ done();
+ }
+ }, 1000);
+}
+
+jSuites.calendar = (function(el, options) {
+ // Already created, update options
+ if (el.calendar) {
+ return el.calendar.setOptions(options, true);
+ }
+
+ // New instance
+ var obj = { type:'calendar' };
+ obj.options = {};
+
+ // Date
+ obj.date = null;
+
+ /**
+ * Update options
+ */
+ obj.setOptions = function(options, reset) {
+ // Default configuration
+ var defaults = {
+ // Render type: [ default | year-month-picker ]
+ type: 'default',
+ // 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: jSuites.calendar.monthsShort,
+ monthsFull: jSuites.calendar.months,
+ weekdays: jSuites.calendar.weekdays,
+ textDone: jSuites.translate('Done'),
+ textReset: jSuites.translate('Reset'),
+ textUpdate: jSuites.translate('Update'),
+ // Value
+ value: null,
+ // Fullscreen (this is automatic set for screensize < 800)
+ fullscreen: false,
+ // Create the calendar closed as default
+ opened: false,
+ // Events
+ onopen: null,
+ onclose: null,
+ onchange: null,
+ onupdate: null,
+ // Internal mode controller
+ mode: null,
+ position: null,
+ // Data type
+ dataType: null,
+ // Controls
+ controls: true,
+ }
+
+ // Loop through our object
+ for (var property in defaults) {
+ if (options && options.hasOwnProperty(property)) {
+ obj.options[property] = options[property];
+ } else {
+ if (typeof(obj.options[property]) == 'undefined' || reset === true) {
+ obj.options[property] = defaults[property];
+ }
+ }
+ }
+
+ // Reset button
+ if (obj.options.resetButton == false) {
+ calendarReset.style.display = 'none';
+ } else {
+ calendarReset.style.display = '';
+ }
+
+ // Readonly
+ if (obj.options.readonly) {
+ el.setAttribute('readonly', 'readonly');
+ } else {
+ el.removeAttribute('readonly');
+ }
+
+ // Placeholder
+ if (obj.options.placeholder) {
+ el.setAttribute('placeholder', obj.options.placeholder);
+ } else {
+ el.removeAttribute('placeholder');
+ }
+
+ if (jSuites.isNumeric(obj.options.value) && obj.options.value > 0) {
+ obj.options.value = jSuites.calendar.numToDate(obj.options.value);
+ // Data type numeric
+ obj.options.dataType = 'numeric';
+ }
+
+ // Texts
+ calendarReset.innerHTML = obj.options.textReset;
+ calendarConfirm.innerHTML = obj.options.textDone;
+ calendarControlsUpdateButton.innerHTML = obj.options.textUpdate;
+
+ // Define mask
+ el.setAttribute('data-mask', obj.options.format.toLowerCase());
+
+ // Value
+ if (! obj.options.value && obj.options.today) {
+ var value = jSuites.calendar.now();
+ } else {
+ var value = obj.options.value;
+ }
+
+ // Set internal date
+ if (value) {
+ // Force the update
+ obj.options.value = null;
+ // New value
+ obj.setValue(value);
+ }
+
+ return obj;
+ }
+
+ /**
+ * Open the calendar
+ */
+ obj.open = function (value) {
+ if (! calendar.classList.contains('jcalendar-focus')) {
+ if (! calendar.classList.contains('jcalendar-inline')) {
+ // Current
+ jSuites.calendar.current = obj;
+ // Start tracking
+ jSuites.tracking(obj, true);
+ // Create the days
+ obj.getDays();
+ // Render months
+ if (obj.options.type == 'year-month-picker') {
+ obj.getMonths();
+ }
+ // Get time
+ if (obj.options.time) {
+ calendarSelectHour.value = obj.date[3];
+ calendarSelectMin.value = obj.date[4];
+ }
+
+ // Show calendar
+ calendar.classList.add('jcalendar-focus');
+
+ // Get the position of the corner helper
+ if (jSuites.getWindowWidth() < 800 || obj.options.fullscreen) {
+ calendar.classList.add('jcalendar-fullsize');
+ // Animation
+ jSuites.animation.slideBottom(calendarContent, 1);
+ } else {
+ calendar.classList.remove('jcalendar-fullsize');
+
+ var rect = el.getBoundingClientRect();
+ var 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) {
+ var d = -1 * (rect.height + rectContent.height + 2);
+ if (d + rect.top < 0) {
+ d = -1 * (rect.top + rect.height);
+ }
+ calendarContainer.style.top = d + 'px';
+ } else {
+ calendarContainer.style.top = 2 + 'px';
+ }
+
+ if (window.innerWidth < rect.left + rectContent.width) {
+ var d = window.innerWidth - (rect.left + rectContent.width + 20);
+ calendarContainer.style.left = d + 'px';
+ } else {
+ calendarContainer.style.left = '0px';
+ }
+ }
+ }
+
+ // Events
+ if (typeof(obj.options.onopen) == 'function') {
+ obj.options.onopen(el);
+ }
+ }
+ }
+ }
+
+ obj.close = function (ignoreEvents, update) {
+ if (calendar.classList.contains('jcalendar-focus')) {
+ 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');
+ // Stop tracking
+ jSuites.tracking(obj, false);
+ // Current
+ jSuites.calendar.current = null;
+ }
+
+ 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 if (obj.options.mode == 'months') {
+ obj.date[0] = parseInt(obj.date[0]) - 1;
+ // Update picker table of months
+ obj.getMonths();
+ } 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 if (obj.options.mode == 'months') {
+ obj.date[0] = parseInt(obj.date[0]) + 1;
+ // Update picker table of months
+ obj.getMonths();
+ } 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();
+ }
+ }
+
+ /**
+ * Set today
+ */
+ obj.setToday = function() {
+ // Today
+ var value = new Date().toISOString().substr(0, 10);
+ // Change value
+ obj.setValue(value);
+ // Value
+ return value;
+ }
+
+ obj.setValue = function(val) {
+ if (! val) {
+ val = '' + val;
+ }
+ // Values
+ var newValue = val;
+ var oldValue = obj.options.value;
+
+ if (oldValue != newValue) {
+ // Set label
+ if (! newValue) {
+ obj.date = null;
+ var val = '';
+ el.classList.remove('jcalendar_warning');
+ el.title = '';
+ } else {
+ var value = obj.setLabel(newValue, obj.options);
+ 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);
+
+ // Current selection day
+ var current = jSuites.calendar.now(new Date(y, m-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)) {
+ el.classList.add('jcalendar_warning');
+ el.title = jSuites.translate('Date outside the valid range');
+ } else {
+ el.classList.remove('jcalendar_warning');
+ el.title = '';
+ }
+ } else {
+ el.classList.remove('jcalendar_warning');
+ el.title = '';
+ }
+ }
+
+ // New value
+ obj.options.value = newValue;
+
+ if (typeof(obj.options.onchange) == 'function') {
+ obj.options.onchange(el, newValue, oldValue);
+ }
+
+ // Lemonade JS
+ if (el.value != val) {
+ el.value = val;
+ if (typeof(el.oninput) == 'function') {
+ el.oninput({
+ type: 'input',
+ target: el,
+ value: el.value
+ });
+ }
+ }
+ }
+
+ obj.getDays();
+ // Render months
+ if (obj.options.type == 'year-month-picker') {
+ obj.getMonths();
+ }
+ }
+
+ obj.getValue = function() {
+ if (obj.date) {
+ if (obj.options.time) {
+ return jSuites.two(obj.date[0]) + '-' + jSuites.two(obj.date[1]) + '-' + jSuites.two(obj.date[2]) + ' ' + jSuites.two(obj.date[3]) + ':' + jSuites.two(obj.date[4]) + ':' + jSuites.two(0);
+ } else {
+ return jSuites.two(obj.date[0]) + '-' + jSuites.two(obj.date[1]) + '-' + jSuites.two(obj.date[2]) + ' ' + jSuites.two(0) + ':' + jSuites.two(0) + ':' + jSuites.two(0);
+ }
+ } else {
+ return "";
+ }
+ }
+
+ /**
+ * Calendar
+ */
+ obj.update = function(element, v) {
+ if (element.classList.contains('jcalendar-disabled')) {
+ // Do nothing
+ } else {
+ var elements = calendar.querySelector('.jcalendar-selected');
+ if (elements) {
+ elements.classList.remove('jcalendar-selected');
+ }
+ element.classList.add('jcalendar-selected');
+
+ if (element.classList.contains('jcalendar-set-month')) {
+ obj.date[1] = v;
+ obj.date[2] = 1; // first day of the month
+ } else {
+ obj.date[2] = element.innerText;
+ }
+
+ if (! obj.options.time) {
+ obj.close();
+ } else {
+ obj.date[3] = calendarSelectHour.value;
+ obj.date[4] = calendarSelectMin.value;
+ }
+ }
+
+ // Update
+ updateActions();
+ }
+
+ /**
+ * Set to blank
+ */
+ obj.reset = function() {
+ // Close calendar
+ obj.setValue('');
+ obj.date = null;
+ obj.close(false, 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 && jSuites.isNumeric(obj.date[0]) ? obj.date[0] : parseInt(date.getFullYear());
+ var month = obj.date && jSuites.isNumeric(obj.date[1]) ? obj.date[1] : parseInt(date.getMonth()) + 1;
+ var day = obj.date && jSuites.isNumeric(obj.date[2]) ? obj.date[2] : parseInt(date.getDate());
+ var hour = obj.date && jSuites.isNumeric(obj.date[3]) ? obj.date[3] : parseInt(date.getHours());
+ var min = obj.date && jSuites.isNumeric(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[index].substr(0,1);
+ 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 < 6; j++) {
+ // Reset cells container
+ var row = document.createElement('tr');
+ row.setAttribute('align', 'center');
+ row.style.height = '34px';
+
+ // 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');
+ }
+ }
+ }
+ // Day cell
+ row.appendChild(cell);
+ // Index
+ index++;
+ }
+
+ // Add cell to the calendar body
+ 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;
+
+ // Value
+ var value = obj.options.value;
+
+ // Current date
+ var date = new Date();
+ var currentYear = parseInt(date.getFullYear());
+ var currentMonth = parseInt(date.getMonth()) + 1;
+ var selectedYear = obj.date && jSuites.isNumeric(obj.date[0]) ? obj.date[0] : currentYear;
+ var selectedMonth = obj.date && jSuites.isNumeric(obj.date[1]) ? obj.date[1] : currentMonth;
+
+ // Update title
+ calendarLabelYear.innerHTML = obj.date[0];
+ calendarLabelMonth.innerHTML = months[selectedMonth-1];
+
+ // Table
+ var table = document.createElement('table');
+ table.setAttribute('width', '100%');
+
+ // Row
+ var row = null;
+
+ // Calendar table
+ for (var i = 0; i < 12; i++) {
+ if (! (i % 4)) {
+ // Reset cells container
+ var row = document.createElement('tr');
+ row.setAttribute('align', 'center');
+ table.appendChild(row);
+ }
+
+ // Create cell
+ var cell = document.createElement('td');
+ cell.classList.add('jcalendar-set-month');
+ cell.setAttribute('data-value', i+1);
+ cell.innerText = months[i];
+
+ if (obj.options.validRange) {
+ var current = selectedYear + '-' + jSuites.two(i+1);
+ if (! obj.options.validRange[0] || current >= obj.options.validRange[0].substr(0,7)) {
+ var test1 = true;
+ } else {
+ var test1 = false;
+ }
+
+ if (! obj.options.validRange[1] || current <= obj.options.validRange[1].substr(0,7)) {
+ var test2 = true;
+ } else {
+ var test2 = false;
+ }
+
+ if (! (test1 && test2)) {
+ cell.classList.add('jcalendar-disabled');
+ }
+ }
+
+ if (i+1 == selectedMonth) {
+ cell.classList.add('jcalendar-selected');
+ }
+
+ if (currentYear == selectedYear && i+1 == currentMonth) {
+ cell.style.fontWeight = 'bold';
+ }
+
+ row.appendChild(cell);
+ }
+
+ calendarBody.innerHTML = ' |
';
+ calendarBody.children[0].children[0].appendChild(table);
+
+ // Update
+ updateActions();
+ }
+
+ obj.getYears = function() {
+ // Mode
+ obj.options.mode = 'years';
+
+ // Current date
+ var date = new Date();
+ var currentYear = date.getFullYear();
+ var selectedYear = obj.date && jSuites.isNumeric(obj.date[0]) ? obj.date[0] : parseInt(date.getFullYear());
+
+ // Array of years
+ var y = [];
+ for (var i = 0; i < 25; i++) {
+ y[i] = parseInt(obj.date[0]) + (i - 12);
+ }
+
+ // Assembling the year tables
+ var table = document.createElement('table');
+ table.setAttribute('width', '100%');
+
+ for (var i = 0; i < 25; i++) {
+ if (! (i % 5)) {
+ // Reset cells container
+ var row = document.createElement('tr');
+ row.setAttribute('align', 'center');
+ table.appendChild(row);
+ }
+
+ // Create cell
+ var cell = document.createElement('td');
+ cell.classList.add('jcalendar-set-year');
+ cell.innerText = y[i];
+
+ if (selectedYear == y[i]) {
+ cell.classList.add('jcalendar-selected');
+ }
+
+ if (currentYear == y[i]) {
+ cell.style.fontWeight = 'bold';
+ }
+
+ row.appendChild(cell);
+ }
+
+ calendarBody.innerHTML = ' |
';
+ calendarBody.firstChild.firstChild.appendChild(table);
+
+ // Update
+ updateActions();
+ }
+
+ obj.setLabel = function(value, mixed) {
+ return jSuites.calendar.getDateString(value, mixed);
+ }
+
+ obj.fromFormatted = function (value, format) {
+ return jSuites.calendar.extractDateFromString(value, format);
+ }
+
+ var mouseUpControls = function(e) {
+ var element = jSuites.findElement(e.target, 'jcalendar-container');
+ if (element) {
+ var action = e.target.className;
+
+ // Object id
+ if (action == 'jcalendar-prev') {
+ obj.prev();
+ } else if (action == 'jcalendar-next') {
+ obj.next();
+ } else if (action == 'jcalendar-month') {
+ obj.getMonths();
+ } else if (action == 'jcalendar-year') {
+ obj.getYears();
+ } else if (action == 'jcalendar-set-year') {
+ obj.date[0] = e.target.innerText;
+ if (obj.options.type == 'year-month-picker') {
+ obj.getMonths();
+ } else {
+ obj.getDays();
+ }
+ } else if (e.target.classList.contains('jcalendar-set-month')) {
+ var month = parseInt(e.target.getAttribute('data-value'));
+ if (obj.options.type == 'year-month-picker') {
+ obj.update(e.target, month);
+ } else {
+ obj.date[1] = month;
+ obj.getDays();
+ }
+ } else if (action == 'jcalendar-confirm' || action == 'jcalendar-update' || action == 'jcalendar-close') {
+ obj.close();
+ } else if (action == 'jcalendar-backdrop') {
+ obj.close(false, false);
+ } else if (action == 'jcalendar-reset') {
+ obj.reset();
+ } else if (e.target.classList.contains('jcalendar-set-day') && e.target.innerText) {
+ obj.update(e.target);
+ }
+ } else {
+ obj.close();
+ }
+ }
+
+ 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) {
+ obj.setValue(test);
+ }
+ }
+ }
+
+ // 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');
+ }
+
+ // Event
+ if (typeof(obj.options.onupdate) == 'function') {
+ obj.options.onupdate(el, obj.getValue());
+ }
+ }
+
+ var calendar = null;
+ var calendarReset = null;
+ var calendarConfirm = null;
+ var calendarContainer = null;
+ var calendarContent = null;
+ var calendarLabelYear = null;
+ var calendarLabelMonth = null;
+ var calendarTable = null;
+ var calendarBody = null;
+
+ var calendarControls = null;
+ var calendarControlsTime = null;
+ var calendarControlsUpdate = null;
+ var calendarControlsUpdateButton = null;
+ var calendarSelectHour = null;
+ var calendarSelectMin = null;
+
+ var init = function() {
+ // Get value from initial element if that is an input
+ if (el.tagName == 'INPUT' && el.value) {
+ options.value = el.value;
+ }
+
+ // Calendar DOM elements
+ calendarReset = document.createElement('div');
+ calendarReset.className = 'jcalendar-reset';
+
+ calendarConfirm = document.createElement('div');
+ calendarConfirm.className = 'jcalendar-confirm';
+
+ calendarControls = document.createElement('div');
+ calendarControls.className = 'jcalendar-controls'
+ calendarControls.style.borderBottom = '1px solid #ddd';
+ calendarControls.appendChild(calendarReset);
+ calendarControls.appendChild(calendarConfirm);
+
+ calendarContainer = document.createElement('div');
+ calendarContainer.className = 'jcalendar-container';
+ calendarContent = document.createElement('div');
+ calendarContent.className = 'jcalendar-content';
+ calendarContainer.appendChild(calendarContent);
+
+ // Main element
+ if (el.tagName == 'DIV') {
+ calendar = el;
+ calendar.classList.add('jcalendar-inline');
+ } else {
+ // Add controls to the screen
+ calendarContent.appendChild(calendarControls);
+
+ calendar = document.createElement('div');
+ calendar.className = 'jcalendar';
+ }
+ calendar.classList.add('jcalendar-container');
+ calendar.appendChild(calendarContainer);
+
+ // Table container
+ var calendarTableContainer = document.createElement('div');
+ calendarTableContainer.className = 'jcalendar-table';
+ calendarContent.appendChild(calendarTableContainer);
+
+ // Previous button
+ var calendarHeaderPrev = document.createElement('td');
+ calendarHeaderPrev.setAttribute('colspan', '2');
+ calendarHeaderPrev.className = 'jcalendar-prev';
+
+ // Header with year and month
+ calendarLabelYear = document.createElement('span');
+ calendarLabelYear.className = 'jcalendar-year';
+ 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 calendarHeader = document.createElement('thead');
+ var calendarHeaderRow = document.createElement('tr');
+ calendarHeaderRow.appendChild(calendarHeaderPrev);
+ calendarHeaderRow.appendChild(calendarHeaderTitle);
+ calendarHeaderRow.appendChild(calendarHeaderNext);
+ calendarHeader.appendChild(calendarHeaderRow);
+
+ calendarTable = document.createElement('table');
+ calendarBody = document.createElement('tbody');
+ calendarTable.setAttribute('cellpadding', '0');
+ calendarTable.setAttribute('cellspacing', '0');
+ calendarTable.appendChild(calendarHeader);
+ calendarTable.appendChild(calendarBody);
+ calendarTableContainer.appendChild(calendarTable);
+
+ calendarSelectHour = document.createElement('select');
+ calendarSelectHour.className = 'jcalendar-select';
+ calendarSelectHour.onchange = function() {
+ obj.date[3] = this.value;
+
+ // Event
+ if (typeof(obj.options.onupdate) == 'function') {
+ obj.options.onupdate(el, obj.getValue());
+ }
+ }
+
+ for (var i = 0; i < 24; i++) {
+ var element = document.createElement('option');
+ element.value = i;
+ element.innerHTML = jSuites.two(i);
+ calendarSelectHour.appendChild(element);
+ }
+
+ calendarSelectMin = document.createElement('select');
+ calendarSelectMin.className = 'jcalendar-select';
+ calendarSelectMin.onchange = function() {
+ obj.date[4] = this.value;
+
+ // Event
+ if (typeof(obj.options.onupdate) == 'function') {
+ obj.options.onupdate(el, obj.getValue());
+ }
+ }
+
+ for (var i = 0; i < 60; i++) {
+ var element = document.createElement('option');
+ element.value = i;
+ element.innerHTML = jSuites.two(i);
+ calendarSelectMin.appendChild(element);
+ }
+
+ // Footer controls
+ var calendarControlsFooter = document.createElement('div');
+ calendarControlsFooter.className = 'jcalendar-controls';
+
+ calendarControlsTime = document.createElement('div');
+ calendarControlsTime.className = 'jcalendar-time';
+ calendarControlsTime.style.maxWidth = '140px';
+ calendarControlsTime.appendChild(calendarSelectHour);
+ calendarControlsTime.appendChild(calendarSelectMin);
+
+ calendarControlsUpdateButton = document.createElement('button');
+ calendarControlsUpdateButton.setAttribute('type', 'button');
+ calendarControlsUpdateButton.className = 'jcalendar-update';
+
+ calendarControlsUpdate = document.createElement('div');
+ calendarControlsUpdate.style.flexGrow = '10';
+ calendarControlsUpdate.appendChild(calendarControlsUpdateButton);
+ calendarControlsFooter.appendChild(calendarControlsTime);
+
+ // Only show the update button for input elements
+ if (el.tagName == 'INPUT') {
+ calendarControlsFooter.appendChild(calendarControlsUpdate);
+ }
+
+ calendarContent.appendChild(calendarControlsFooter);
+
+ var calendarBackdrop = document.createElement('div');
+ calendarBackdrop.className = 'jcalendar-backdrop';
+ calendar.appendChild(calendarBackdrop);
+
+ // Handle events
+ el.addEventListener("keyup", keyUpControls);
+
+ // Add global events
+ calendar.addEventListener("swipeleft", function(e) {
+ jSuites.animation.slideLeft(calendarTable, 0, function() {
+ obj.next();
+ jSuites.animation.slideRight(calendarTable, 1);
+ });
+ e.preventDefault();
+ e.stopPropagation();
+ });
+
+ calendar.addEventListener("swiperight", function(e) {
+ jSuites.animation.slideRight(calendarTable, 0, function() {
+ obj.prev();
+ jSuites.animation.slideLeft(calendarTable, 1);
+ });
+ e.preventDefault();
+ e.stopPropagation();
+ });
+
+ if ('ontouchend' in document.documentElement === true) {
+ calendar.addEventListener("touchend", mouseUpControls);
+ el.addEventListener("touchend", obj.open);
+ } else {
+ calendar.addEventListener("mouseup", mouseUpControls);
+ el.addEventListener("mouseup", obj.open);
+ }
+
+ // Global controls
+ if (! jSuites.calendar.hasEvents) {
+ // Execute only one time
+ jSuites.calendar.hasEvents = true;
+ // Enter and Esc
+ document.addEventListener("keydown", jSuites.calendar.keydown);
+ }
+
+ // Set configuration
+ obj.setOptions(options);
+
+ // Append element to the DOM
+ if (el.tagName == 'INPUT') {
+ el.parentNode.insertBefore(calendar, el.nextSibling);
+ // Add properties
+ el.setAttribute('autocomplete', 'off');
+ // Element
+ el.classList.add('jcalendar-input');
+ // Value
+ el.value = obj.setLabel(obj.getValue(), obj.options);
+ } else {
+ // Get days
+ obj.getDays();
+ // Hour
+ if (obj.options.time) {
+ calendarSelectHour.value = obj.date[3];
+ calendarSelectMin.value = obj.date[4];
+ }
+ }
+
+ // Default opened
+ if (obj.options.opened == true) {
+ obj.open();
+ }
+
+ // Controls
+ if (obj.options.controls == false) {
+ calendarContainer.classList.add('jcalendar-hide-controls');
+ }
+
+ // Change method
+ el.change = obj.setValue;
+
+ // Global generic value handler
+ el.val = function(val) {
+ if (val === undefined) {
+ return obj.getValue();
+ } else {
+ obj.setValue(val);
+ }
+ }
+
+ // Keep object available from the node
+ el.calendar = calendar.calendar = obj;
+ }
+
+ init();
+
+ return obj;
+});
+
+jSuites.calendar.keydown = function(e) {
+ var calendar = null;
+ if (calendar = jSuites.calendar.current) {
+ if (e.which == 13) {
+ // ENTER
+ calendar.close(false, true);
+ } else if (e.which == 27) {
+ // ESC
+ calendar.close(false, false);
+ }
+ }
+}
+
+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 {
+ if (elements[i].innerHTML) {
+ elements[i].setAttribute('title', elements[i].innerHTML);
+ elements[i].setAttribute('data-date', elements[i].innerHTML);
+ elements[i].innerHTML = jSuites.calendar.prettify(elements[i].innerHTML);
+ }
+ }
+ }
+}
+
+jSuites.calendar.now = function(date, dateOnly) {
+ if (Array.isArray(date)) {
+ var y = date[0];
+ var m = date[1];
+ var d = date[2];
+ var h = date[3];
+ var i = date[4];
+ var s = date[5];
+ } else {
+ 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();
+ }
+
+ if (dateOnly == true) {
+ return jSuites.two(y) + '-' + jSuites.two(m) + '-' + jSuites.two(d);
+ } else {
+ return jSuites.two(y) + '-' + jSuites.two(m) + '-' + jSuites.two(d) + ' ' + jSuites.two(h) + ':' + jSuites.two(i) + ':' + jSuites.two(s);
+ }
+}
+
+jSuites.calendar.toArray = function(value) {
+ var date = value.split(((value.indexOf('T') !== -1) ? 'T' : ' '));
+ 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;
+ }
+ return [ y, m, d, h, i, 0 ];
+}
+
+// Helper to extract date from a string
+jSuites.calendar.extractDateFromString = function(date, format) {
+ var o = jSuites.mask(date, { mask: format }, true);
+
+ // Check if in format Excel (Need difference with format date or type detected is numeric)
+ if (date > 0 && Number(date) == date && (o.values.join("") !== o.value || o.type == "numeric")) {
+ var d = new Date(Math.round((date - 25569)*86400*1000));
+ return d.getFullYear() + "-" + jSuites.two(d.getMonth()) + "-" + jSuites.two(d.getDate()) + ' 00:00:00';
+ }
+
+ var complete = false;
+
+ if (o.values.length === o.tokens.length && o.values[o.values.length-1].length >= o.tokens[o.tokens.length-1].length) {
+ complete = true;
+ }
+
+ if (o.date[0] && o.date[1] && (o.date[2] || complete)) {
+ if (! o.date[2]) {
+ o.date[2] = 1;
+ }
+
+ return o.date[0] + '-' + jSuites.two(o.date[1]) + '-' + jSuites.two(o.date[2]) + ' ' + jSuites.two(o.date[3]) + ':' + jSuites.two(o.date[4])+ ':' + jSuites.two(o.date[5]);
+ }
+
+ return '';
+}
+
+var excelInitialTime = Date.UTC(1900, 0, 0);
+var excelLeapYearBug = Date.UTC(1900, 1, 29);
+var millisecondsPerDay = 86400000;
+
+/**
+ * Date to number
+ */
+jSuites.calendar.dateToNum = function(jsDate) {
+ if (typeof(jsDate) === 'string') {
+ jsDate = new Date(jsDate + ' GMT+0');
+ }
+ var jsDateInMilliseconds = jsDate.getTime();
+
+ if (jsDateInMilliseconds >= excelLeapYearBug) {
+ jsDateInMilliseconds += millisecondsPerDay;
+ }
+
+ jsDateInMilliseconds -= excelInitialTime;
+
+ return jsDateInMilliseconds / millisecondsPerDay;
+}
+
+/**
+ * Number to date
+ */
+// !IMPORTANT!
+// Excel incorrectly considers 1900 to be a leap year
+jSuites.calendar.numToDate = function(excelSerialNumber) {
+ var jsDateInMilliseconds = excelInitialTime + excelSerialNumber * millisecondsPerDay;
+
+ if (jsDateInMilliseconds >= excelLeapYearBug) {
+ jsDateInMilliseconds -= millisecondsPerDay;
+ }
+
+ const d = new Date(jsDateInMilliseconds);
+
+ var date = [
+ d.getUTCFullYear(),
+ d.getUTCMonth()+1,
+ d.getUTCDate(),
+ d.getUTCHours(),
+ d.getUTCMinutes(),
+ d.getUTCSeconds(),
+ ];
+
+ return jSuites.calendar.now(date);
+}
+
+// Helper to convert date into string
+jSuites.calendar.getDateString = function(value, options) {
+ if (! options) {
+ var options = {};
+ }
+
+ // Labels
+ if (options && typeof(options) == 'object') {
+ var format = options.format;
+ } else {
+ var format = options;
+ }
+
+ if (! format) {
+ format = 'YYYY-MM-DD';
+ }
+
+ // Convert to number of hours
+ if (format.indexOf('[h]') >= 0) {
+ var result = 0;
+ if (value && jSuites.isNumeric(value)) {
+ result = parseFloat(24 * Number(value));
+ if (format.indexOf('mm') >= 0) {
+ var h = (''+result).split('.');
+ if (h[1]) {
+ var d = 60 * parseFloat('0.' + h[1])
+ d = parseFloat(d.toFixed(2));
+ } else {
+ var d = 0;
+ }
+ result = parseInt(h[0]) + ':' + jSuites.two(d);
+ }
+ }
+ return result;
+ }
+
+ // Date instance
+ if (value instanceof Date) {
+ value = jSuites.calendar.now(value);
+ } else if (value && jSuites.isNumeric(value)) {
+ value = jSuites.calendar.numToDate(value);
+ }
+
+ // Tokens
+ var tokens = [ 'DAY', 'WD', 'DDDD', 'DDD', 'DD', 'D', 'Q', 'HH24', 'HH12', 'HH', 'H', 'AM/PM', 'MI', 'SS', 'MS', 'YYYY', 'YYY', 'YY', 'Y', 'MONTH', 'MON', 'MMMMM', 'MMMM', 'MMM', 'MM', 'M', '.' ];
+
+ // Expression to extract all tokens from the string
+ var e = new RegExp(tokens.join('|'), 'gi');
+ // Extract
+ var t = format.match(e);
+
+ // Compatibility with excel
+ for (var i = 0; i < t.length; i++) {
+ if (t[i].toUpperCase() == 'MM') {
+ // Not a month, correct to minutes
+ if (t[i-1] && t[i-1].toUpperCase().indexOf('H') >= 0) {
+ t[i] = 'mi';
+ } else if (t[i-2] && t[i-2].toUpperCase().indexOf('H') >= 0) {
+ t[i] = 'mi';
+ } else if (t[i+1] && t[i+1].toUpperCase().indexOf('S') >= 0) {
+ t[i] = 'mi';
+ } else if (t[i+2] && t[i+2].toUpperCase().indexOf('S') >= 0) {
+ t[i] = 'mi';
+ }
+ }
+ }
+
+ // Object
+ var o = {
+ tokens: t
+ }
+
+ // Value
+ if (value) {
+ var d = ''+value;
+ var splitStr = (d.indexOf('T') !== -1) ? 'T' : ' ';
+ d = d.split(splitStr);
+
+ var h = 0;
+ var m = 0;
+ var s = 0;
+
+ if (d[1]) {
+ h = d[1].split(':');
+ m = h[1] ? h[1] : 0;
+ s = h[2] ? h[2] : 0;
+ h = h[0] ? h[0] : 0;
+ }
+
+ 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) {
+
+ // Data
+ o.data = [ d[0], d[1], d[2], h, m, s ];
+
+ // Value
+ o.value = [];
+
+ // Calendar instance
+ var calendar = new Date(o.data[0], o.data[1]-1, o.data[2], o.data[3], o.data[4], o.data[5]);
+
+ // Get method
+ var get = function(i) {
+ // Token
+ var t = this.tokens[i];
+ // Case token
+ var s = t.toUpperCase();
+ var v = null;
+
+ if (s === 'YYYY') {
+ v = this.data[0];
+ } else if (s === 'YYY') {
+ v = this.data[0].substring(1,4);
+ } else if (s === 'YY') {
+ v = this.data[0].substring(2,4);
+ } else if (s === 'Y') {
+ v = this.data[0].substring(3,4);
+ } else if (t === 'MON') {
+ v = jSuites.calendar.months[calendar.getMonth()].substr(0,3).toUpperCase();
+ } else if (t === 'mon') {
+ v = jSuites.calendar.months[calendar.getMonth()].substr(0,3).toLowerCase();
+ } else if (t === 'MONTH') {
+ v = jSuites.calendar.months[calendar.getMonth()].toUpperCase();
+ } else if (t === 'month') {
+ v = jSuites.calendar.months[calendar.getMonth()].toLowerCase();
+ } else if (s === 'MMMMM') {
+ v = jSuites.calendar.months[calendar.getMonth()].substr(0, 1);
+ } else if (s === 'MMMM' || t === 'Month') {
+ v = jSuites.calendar.months[calendar.getMonth()];
+ } else if (s === 'MMM' || t == 'Mon') {
+ v = jSuites.calendar.months[calendar.getMonth()].substr(0,3);
+ } else if (s === 'MM') {
+ v = jSuites.two(this.data[1]);
+ } else if (s === 'M') {
+ v = calendar.getMonth()+1;
+ } else if (t === 'DAY') {
+ v = jSuites.calendar.weekdays[calendar.getDay()].toUpperCase();
+ } else if (t === 'day') {
+ v = jSuites.calendar.weekdays[calendar.getDay()].toLowerCase();
+ } else if (s === 'DDDD' || t == 'Day') {
+ v = jSuites.calendar.weekdays[calendar.getDay()];
+ } else if (s === 'DDD') {
+ v = jSuites.calendar.weekdays[calendar.getDay()].substr(0,3);
+ } else if (s === 'DD') {
+ v = jSuites.two(this.data[2]);
+ } else if (s === 'D') {
+ v = this.data[2];
+ } else if (s === 'Q') {
+ v = Math.floor((calendar.getMonth() + 3) / 3);
+ } else if (s === 'HH24' || s === 'HH') {
+ v = jSuites.two(this.data[3]);
+ } else if (s === 'HH12') {
+ if (this.data[3] > 12) {
+ v = jSuites.two(this.data[3] - 12);
+ } else {
+ v = jSuites.two(this.data[3]);
+ }
+ } else if (s === 'H') {
+ v = this.data[3];
+ } else if (s === 'MI') {
+ v = jSuites.two(this.data[4]);
+ } else if (s === 'SS') {
+ v = jSuites.two(this.data[5]);
+ } else if (s === 'MS') {
+ v = calendar.getMilliseconds();
+ } else if (s === 'AM/PM') {
+ if (this.data[3] >= 12) {
+ v = 'PM';
+ } else {
+ v = 'AM';
+ }
+ } else if (s === 'WD') {
+ v = jSuites.calendar.weekdays[calendar.getDay()];
+ }
+
+ if (v === null) {
+ this.value[i] = this.tokens[i];
+ } else {
+ this.value[i] = v;
+ }
+ }
+
+ for (var i = 0; i < o.tokens.length; i++) {
+ get.call(o, i);
+ }
+ // Put pieces together
+ value = o.value.join('');
+ } else {
+ value = '';
+ }
+ }
+
+ return value;
+}
+
+// Jsuites calendar labels
+jSuites.calendar.weekdays = [ 'Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday' ];
+jSuites.calendar.months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
+jSuites.calendar.weekdaysShort = [ 'Sun','Mon','Tue','Wed','Thu','Fri','Sat' ];
+jSuites.calendar.monthsShort = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
+
+
+jSuites.color = (function(el, options) {
+ // Already created, update options
+ if (el.color) {
+ return el.color.setOptions(options, true);
+ }
+
+ // New instance
+ var obj = { type: 'color' };
+ obj.options = {};
+
+ var container = null;
+ var backdrop = null;
+ var content = null;
+ var resetButton = null;
+ var closeButton = null;
+ var tabs = null;
+ var jsuitesTabs = null;
+
+ /**
+ * Update options
+ */
+ obj.setOptions = function(options, reset) {
+ /**
+ * @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
+ * @property {string} doneLabel - Label for button done
+ * @property {string} resetLabel - Label for button reset
+ * @property {string} resetValue - Value for button reset
+ * @property {Bool} showResetButton - Active or note for button reset - default false
+ */
+ var defaults = {
+ placeholder: '',
+ value: null,
+ onopen: null,
+ onclose: null,
+ onchange: null,
+ closeOnChange: true,
+ palette: null,
+ position: null,
+ doneLabel: 'Done',
+ resetLabel: 'Reset',
+ fullscreen: false,
+ opened: false,
+ }
+
+ if (! options) {
+ options = {};
+ }
+
+ if (options && ! options.palette) {
+ // Default pallete
+ options.palette = jSuites.palette();
+ }
+
+ // Loop through our object
+ for (var property in defaults) {
+ if (options && options.hasOwnProperty(property)) {
+ obj.options[property] = options[property];
+ } else {
+ if (typeof(obj.options[property]) == 'undefined' || reset === true) {
+ obj.options[property] = defaults[property];
+ }
+ }
+ }
+
+ // Update the text of the controls, if they have already been created
+ if (resetButton) {
+ resetButton.innerHTML = obj.options.resetLabel;
+ }
+ if (closeButton) {
+ closeButton.innerHTML = obj.options.doneLabel;
+ }
+
+ // Update the pallete
+ if (obj.options.palette && jsuitesTabs) {
+ jsuitesTabs.updateContent(0, table());
+ }
+
+ // Value
+ if (typeof obj.options.value === 'string') {
+ el.value = obj.options.value;
+ if (el.tagName === 'INPUT') {
+ el.style.color = el.value;
+ el.style.backgroundColor = el.value;
+ }
+ }
+
+ // Placeholder
+ if (obj.options.placeholder) {
+ el.setAttribute('placeholder', obj.options.placeholder);
+ } else {
+ if (el.getAttribute('placeholder')) {
+ el.removeAttribute('placeholder');
+ }
+ }
+
+ return obj;
+ }
+
+ obj.select = function(color) {
+ // Remove current selected mark
+ var selected = container.querySelector('.jcolor-selected');
+ if (selected) {
+ selected.classList.remove('jcolor-selected');
+ }
+
+ // Mark cell as selected
+ if (obj.values[color]) {
+ obj.values[color].classList.add('jcolor-selected');
+ }
+
+ obj.options.value = color;
+ }
+
+ /**
+ * Open color pallete
+ */
+ obj.open = function() {
+ if (! container.classList.contains('jcolor-focus')) {
+ // Start tracking
+ jSuites.tracking(obj, true);
+
+ // Show color picker
+ container.classList.add('jcolor-focus');
+
+ // Select current color
+ if (obj.options.value) {
+ obj.select(obj.options.value);
+ }
+
+ // Reset margin
+ content.style.marginTop = '';
+ content.style.marginLeft = '';
+
+ var rectContent = content.getBoundingClientRect();
+ var availableWidth = jSuites.getWindowWidth();
+ var availableHeight = jSuites.getWindowHeight();
+
+ if (availableWidth < 800 || obj.options.fullscreen == true) {
+ content.classList.add('jcolor-fullscreen');
+ jSuites.animation.slideBottom(content, 1);
+ backdrop.style.display = 'block';
+ } else {
+ if (content.classList.contains('jcolor-fullscreen')) {
+ content.classList.remove('jcolor-fullscreen');
+ backdrop.style.display = '';
+ }
+
+ if (obj.options.position) {
+ content.style.position = 'fixed';
+ } else {
+ content.style.position = '';
+ }
+
+ if (rectContent.left + rectContent.width > availableWidth) {
+ content.style.marginLeft = -1 * (rectContent.left + rectContent.width - (availableWidth - 20)) + 'px';
+ }
+ if (rectContent.top + rectContent.height > availableHeight) {
+ content.style.marginTop = -1 * (rectContent.top + rectContent.height - (availableHeight - 20)) + 'px';
+ }
+ }
+
+
+ if (typeof(obj.options.onopen) == 'function') {
+ obj.options.onopen(el);
+ }
+
+ jsuitesTabs.setBorder(jsuitesTabs.getActive());
+
+ // Update sliders
+ if (obj.options.value) {
+ var rgb = HexToRgb(obj.options.value);
+
+ rgbInputs.forEach(function(rgbInput, index) {
+ rgbInput.value = rgb[index];
+ rgbInput.dispatchEvent(new Event('input'));
+ });
+ }
+ }
+ }
+
+ /**
+ * Close color pallete
+ */
+ obj.close = function(ignoreEvents) {
+ if (container.classList.contains('jcolor-focus')) {
+ // Remove focus
+ container.classList.remove('jcolor-focus');
+ // Make sure backdrop is hidden
+ backdrop.style.display = '';
+ // Call related events
+ if (! ignoreEvents && typeof(obj.options.onclose) == 'function') {
+ obj.options.onclose(el);
+ }
+ // Stop the object
+ jSuites.tracking(obj, false);
+ }
+
+ return obj.options.value;
+ }
+
+ /**
+ * Set value
+ */
+ obj.setValue = function(color) {
+ if (! color) {
+ color = '';
+ }
+
+ if (color != obj.options.value) {
+ obj.options.value = color;
+ slidersResult = color;
+
+ // Remove current selecded mark
+ obj.select(color);
+
+ // Onchange
+ if (typeof(obj.options.onchange) == 'function') {
+ obj.options.onchange(el, color);
+ }
+
+ // Changes
+ if (el.value != obj.options.value) {
+ // Set input value
+ el.value = obj.options.value;
+ if (el.tagName === 'INPUT') {
+ el.style.color = el.value;
+ el.style.backgroundColor = el.value;
+ }
+
+ // Element onchange native
+ if (typeof(el.oninput) == 'function') {
+ el.oninput({
+ type: 'input',
+ target: el,
+ value: el.value
+ });
+ }
+ }
+
+ if (obj.options.closeOnChange == true) {
+ obj.close();
+ }
+ }
+ }
+
+ /**
+ * Get value
+ */
+ obj.getValue = function() {
+ return obj.options.value;
+ }
+
+ var backdropClickControl = false;
+
+ // Converts a number in decimal to hexadecimal
+ var decToHex = function(num) {
+ var hex = num.toString(16);
+ return hex.length === 1 ? "0" + hex : hex;
+ }
+
+ // Converts a color in rgb to hexadecimal
+ var rgbToHex = function(r, g, b) {
+ return "#" + decToHex(r) + decToHex(g) + decToHex(b);
+ }
+
+ // Converts a number in hexadecimal to decimal
+ var hexToDec = function(hex) {
+ return parseInt('0x' + hex);
+ }
+
+ // Converts a color in hexadecimal to rgb
+ var HexToRgb = function(hex) {
+ return [hexToDec(hex.substr(1, 2)), hexToDec(hex.substr(3, 2)), hexToDec(hex.substr(5, 2))]
+ }
+
+ var table = function() {
+ // Content of the first tab
+ var tableContainer = document.createElement('div');
+ tableContainer.className = 'jcolor-grid';
+
+ // Cells
+ obj.values = [];
+
+ // Table pallete
+ var t = document.createElement('table');
+ t.setAttribute('cellpadding', '7');
+ t.setAttribute('cellspacing', '0');
+
+ for (var j = 0; j < obj.options.palette.length; j++) {
+ var tr = document.createElement('tr');
+ for (var i = 0; i < obj.options.palette[j].length; i++) {
+ var td = document.createElement('td');
+ var color = obj.options.palette[j][i];
+ if (color.length < 7 && color.substr(0,1) !== '#') {
+ color = '#' + color;
+ }
+ td.style.backgroundColor = color;
+ td.setAttribute('data-value', color);
+ td.innerHTML = '';
+ tr.appendChild(td);
+
+ // Selected color
+ if (obj.options.value == color) {
+ td.classList.add('jcolor-selected');
+ }
+
+ // Possible values
+ obj.values[color] = td;
+ }
+ t.appendChild(tr);
+ }
+
+ // Append to the table
+ tableContainer.appendChild(t);
+
+ return tableContainer;
+ }
+
+ // Canvas where the image will be rendered
+ var canvas = document.createElement('canvas');
+ canvas.width = 200;
+ canvas.height = 160;
+ var context = canvas.getContext("2d");
+
+ var resizeCanvas = function() {
+ // Specifications necessary to correctly obtain colors later in certain positions
+ var m = tabs.firstChild.getBoundingClientRect();
+ canvas.width = m.width - 14;
+ gradient()
+ }
+
+ var gradient = function() {
+ var g = context.createLinearGradient(0, 0, canvas.width, 0);
+ // Create color gradient
+ g.addColorStop(0, "rgb(255,0,0)");
+ g.addColorStop(0.15, "rgb(255,0,255)");
+ g.addColorStop(0.33, "rgb(0,0,255)");
+ g.addColorStop(0.49, "rgb(0,255,255)");
+ g.addColorStop(0.67, "rgb(0,255,0)");
+ g.addColorStop(0.84, "rgb(255,255,0)");
+ g.addColorStop(1, "rgb(255,0,0)");
+ context.fillStyle = g;
+ context.fillRect(0, 0, canvas.width, canvas.height);
+ g = context.createLinearGradient(0, 0, 0, canvas.height);
+ g.addColorStop(0, "rgba(255,255,255,1)");
+ g.addColorStop(0.5, "rgba(255,255,255,0)");
+ g.addColorStop(0.5, "rgba(0,0,0,0)");
+ g.addColorStop(1, "rgba(0,0,0,1)");
+ context.fillStyle = g;
+ context.fillRect(0, 0, canvas.width, canvas.height);
+ }
+
+ var hsl = function() {
+ var element = document.createElement('div');
+ element.className = "jcolor-hsl";
+
+ var point = document.createElement('div');
+ point.className = 'jcolor-point';
+
+ var div = document.createElement('div');
+ div.appendChild(canvas);
+ div.appendChild(point);
+ element.appendChild(div);
+
+ // Moves the marquee point to the specified position
+ var update = function(buttons, x, y) {
+ if (buttons === 1) {
+ var rect = element.getBoundingClientRect();
+ var left = x - rect.left;
+ var top = y - rect.top;
+ if (left < 0) {
+ left = 0;
+ }
+ if (top < 0) {
+ top = 0;
+ }
+ if (left > rect.width) {
+ left = rect.width;
+ }
+ if (top > rect.height) {
+ top = rect.height;
+ }
+ point.style.left = left + 'px';
+ point.style.top = top + 'px';
+ var pixel = context.getImageData(left, top, 1, 1).data;
+ slidersResult = rgbToHex(pixel[0], pixel[1], pixel[2]);
+ }
+ }
+
+ // Applies the point's motion function to the div that contains it
+ element.addEventListener('mousedown', function(e) {
+ update(e.buttons, e.clientX, e.clientY);
+ });
+
+ element.addEventListener('mousemove', function(e) {
+ update(e.buttons, e.clientX, e.clientY);
+ });
+
+ element.addEventListener('touchmove', function(e) {
+ update(1, e.changedTouches[0].clientX, e.changedTouches[0].clientY);
+ });
+
+ return element;
+ }
+
+ var slidersResult = '';
+
+ var rgbInputs = [];
+
+ var changeInputColors = function() {
+ if (slidersResult !== '') {
+ for (var j = 0; j < rgbInputs.length; j++) {
+ var currentColor = HexToRgb(slidersResult);
+
+ currentColor[j] = 0;
+
+ var newGradient = 'linear-gradient(90deg, rgb(';
+ newGradient += currentColor.join(', ');
+ newGradient += '), rgb(';
+
+ currentColor[j] = 255;
+
+ newGradient += currentColor.join(', ');
+ newGradient += '))';
+
+ rgbInputs[j].style.backgroundImage = newGradient;
+ }
+ }
+ }
+
+ var sliders = function() {
+ // Content of the third tab
+ var slidersElement = document.createElement('div');
+ slidersElement.className = 'jcolor-sliders';
+
+ var slidersBody = document.createElement('div');
+
+ // Creates a range-type input with the specified name
+ var createSliderInput = function(name) {
+ var inputContainer = document.createElement('div');
+ inputContainer.className = 'jcolor-sliders-input-container';
+
+ var label = document.createElement('label');
+ label.innerText = name;
+
+ var subContainer = document.createElement('div');
+ subContainer.className = 'jcolor-sliders-input-subcontainer';
+
+ var input = document.createElement('input');
+ input.type = 'range';
+ input.min = 0;
+ input.max = 255;
+ input.value = 0;
+
+ inputContainer.appendChild(label);
+ subContainer.appendChild(input);
+
+ var value = document.createElement('div');
+ value.innerText = input.value;
+
+ input.addEventListener('input', function() {
+ value.innerText = input.value;
+ });
+
+ subContainer.appendChild(value);
+ inputContainer.appendChild(subContainer);
+
+ slidersBody.appendChild(inputContainer);
+
+ return input;
+ }
+
+ // Creates red, green and blue inputs
+ rgbInputs = [
+ createSliderInput('Red'),
+ createSliderInput('Green'),
+ createSliderInput('Blue'),
+ ];
+
+ slidersElement.appendChild(slidersBody);
+
+ // Element that prints the current color
+ var slidersResultColor = document.createElement('div');
+ slidersResultColor.className = 'jcolor-sliders-final-color';
+
+ var resultElement = document.createElement('div');
+ resultElement.style.visibility = 'hidden';
+ resultElement.innerText = 'a';
+ slidersResultColor.appendChild(resultElement)
+
+ // Update the element that prints the current color
+ var updateResult = function() {
+ var resultColor = rgbToHex(parseInt(rgbInputs[0].value), parseInt(rgbInputs[1].value), parseInt(rgbInputs[2].value));
+
+ resultElement.innerText = resultColor;
+ resultElement.style.color = resultColor;
+ resultElement.style.removeProperty('visibility');
+
+ slidersResult = resultColor;
+ }
+
+ // Apply the update function to color inputs
+ rgbInputs.forEach(function(rgbInput) {
+ rgbInput.addEventListener('input', function() {
+ updateResult();
+ changeInputColors();
+ });
+ });
+
+ slidersElement.appendChild(slidersResultColor);
+
+ return slidersElement;
+ }
+
+ var init = function() {
+ // Initial options
+ obj.setOptions(options);
+
+ // Add a proper input tag when the element is an input
+ if (el.tagName == 'INPUT') {
+ el.classList.add('jcolor-input');
+ el.readOnly = true;
+ }
+
+ // Table container
+ container = document.createElement('div');
+ container.className = 'jcolor';
+
+ // Table container
+ backdrop = document.createElement('div');
+ backdrop.className = 'jcolor-backdrop';
+ container.appendChild(backdrop);
+
+ // Content
+ content = document.createElement('div');
+ content.className = 'jcolor-content';
+
+ // Controls
+ var controls = document.createElement('div');
+ controls.className = 'jcolor-controls';
+ content.appendChild(controls);
+
+ // Reset button
+ resetButton = document.createElement('div');
+ resetButton.className = 'jcolor-reset';
+ resetButton.innerHTML = obj.options.resetLabel;
+ controls.appendChild(resetButton);
+
+ // Close button
+ closeButton = document.createElement('div');
+ closeButton.className = 'jcolor-close';
+ closeButton.innerHTML = obj.options.doneLabel;
+ controls.appendChild(closeButton);
+
+ // Element that will be used to create the tabs
+ tabs = document.createElement('div');
+ content.appendChild(tabs);
+
+ // Starts the jSuites tabs component
+ jsuitesTabs = jSuites.tabs(tabs, {
+ animation: true,
+ data: [
+ {
+ title: 'Grid',
+ contentElement: table(),
+ },
+ {
+ title: 'Spectrum',
+ contentElement: hsl(),
+ },
+ {
+ title: 'Sliders',
+ contentElement: sliders(),
+ }
+ ],
+ onchange: function(element, instance, index) {
+ if (index === 1) {
+ resizeCanvas();
+ } else {
+ var color = slidersResult !== '' ? slidersResult : obj.getValue();
+
+ if (index === 2 && color) {
+ var rgb = HexToRgb(color);
+
+ rgbInputs.forEach(function(rgbInput, index) {
+ rgbInput.value = rgb[index];
+ rgbInput.dispatchEvent(new Event('input'));
+ });
+ }
+ }
+ },
+ palette: 'modern',
+ });
+
+ container.appendChild(content);
+
+ // Insert picker after the element
+ if (el.tagName == 'INPUT') {
+ el.parentNode.insertBefore(container, el.nextSibling);
+ } else {
+ el.appendChild(container);
+ }
+
+ container.addEventListener("click", function(e) {
+ if (e.target.tagName == 'TD') {
+ var value = e.target.getAttribute('data-value');
+ if (value) {
+ obj.setValue(value);
+ }
+ } else if (e.target.classList.contains('jcolor-reset')) {
+ obj.setValue('');
+ obj.close();
+ } else if (e.target.classList.contains('jcolor-close')) {
+ if (jsuitesTabs.getActive() > 0) {
+ obj.setValue(slidersResult);
+ }
+ obj.close();
+ } else if (e.target.classList.contains('jcolor-backdrop')) {
+ obj.close();
+ } else {
+ obj.open();
+ }
+ });
+
+ /**
+ * If element is focus open the picker
+ */
+ el.addEventListener("mouseup", function(e) {
+ obj.open();
+ });
+
+ // If the picker is open on the spectrum tab, it changes the canvas size when the window size is changed
+ window.addEventListener('resize', function() {
+ if (container.classList.contains('jcolor-focus') && jsuitesTabs.getActive() == 1) {
+ resizeCanvas();
+ }
+ });
+
+ // Default opened
+ if (obj.options.opened == true) {
+ obj.open();
+ }
+
+ // Change
+ el.change = obj.setValue;
+
+ // Global generic value handler
+ el.val = function(val) {
+ if (val === undefined) {
+ return obj.getValue();
+ } else {
+ obj.setValue(val);
+ }
+ }
+
+ // Keep object available from the node
+ el.color = obj;
+
+ // Container shortcut
+ container.color = obj;
+ }
+
+ obj.toHex = function(rgb) {
+ var hex = function(x) {
+ return ("0" + parseInt(x).toString(16)).slice(-2);
+ }
+ if (rgb) {
+ if (/^#[0-9A-F]{6}$/i.test(rgb)) {
+ return rgb;
+ } else {
+ rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
+ if (rgb && rgb.length) {
+ return "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]);
+ } else {
+ return "";
+ }
+ }
+ }
+ }
+
+ init();
+
+ return obj;
+});
+
+
+
+jSuites.contextmenu = (function(el, options) {
+ // New instance
+ var obj = { type:'contextmenu'};
+ 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');
+
+ /**
+ * Open contextmenu
+ */
+ obj.open = function(e, items) {
+ if (items) {
+ // Update content
+ obj.options.items = items;
+ // Create items
+ obj.create(items);
+ }
+
+ // Close current contextmenu
+ if (jSuites.contextmenu.current) {
+ jSuites.contextmenu.current.close();
+ }
+
+ // Add to the opened components monitor
+ jSuites.tracking(obj, true);
+
+ // Show context menu
+ el.classList.add('jcontextmenu-focus');
+
+ // Current
+ jSuites.contextmenu.current = obj;
+
+ // Coordinates
+ if ((obj.options.items && obj.options.items.length > 0) || el.children.length) {
+ if (e.target) {
+ if (e.changedTouches && e.changedTouches[0]) {
+ x = e.changedTouches[0].clientX;
+ y = e.changedTouches[0].clientY;
+ } else {
+ var x = e.clientX;
+ var y = e.clientY;
+ }
+ } else {
+ var x = e.x;
+ var y = e.y;
+ }
+
+ var rect = el.getBoundingClientRect();
+
+ if (window.innerHeight < y + rect.height) {
+ var h = y - rect.height;
+ if (h < 0) {
+ h = 0;
+ }
+ el.style.top = h + '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';
+ }
+ }
+ }
+
+ obj.isOpened = function() {
+ return el.classList.contains('jcontextmenu-focus') ? true : false;
+ }
+
+ /**
+ * Close menu
+ */
+ obj.close = function() {
+ if (el.classList.contains('jcontextmenu-focus')) {
+ el.classList.remove('jcontextmenu-focus');
+ }
+ jSuites.tracking(obj, false);
+ }
+
+ /**
+ * Create items based on the declared objectd
+ * @param {object} items - List of object
+ */
+ obj.create = function(items) {
+ // Update content
+ el.innerHTML = '';
+
+ // Add header contextmenu
+ var itemHeader = createHeader();
+ el.appendChild(itemHeader);
+
+ // Append items
+ for (var i = 0; i < items.length; i++) {
+ var itemContainer = createItemElement(items[i]);
+ el.appendChild(itemContainer);
+ }
+ }
+
+ /**
+ * createHeader for context menu
+ * @private
+ * @returns {HTMLElement}
+ */
+ function createHeader() {
+ var header = document.createElement('div');
+ header.classList.add("header");
+ header.addEventListener("click", function(e) {
+ e.preventDefault();
+ e.stopPropagation();
+ });
+ var title = document.createElement('a');
+ title.classList.add("title");
+ title.innerHTML = jSuites.translate("Menu");
+
+ header.appendChild(title);
+
+ var closeButton = document.createElement('a');
+ closeButton.classList.add("close");
+ closeButton.innerHTML = jSuites.translate("close");
+ closeButton.addEventListener("click", function(e) {
+ obj.close();
+ });
+
+ header.appendChild(closeButton);
+
+ return header;
+ }
+
+ /**
+ * Private function for create a new Item element
+ * @param {type} item
+ * @returns {jsuitesL#15.jSuites.contextmenu.createItemElement.itemContainer}
+ */
+ function createItemElement(item) {
+ if (item.type && (item.type == 'line' || item.type == 'divisor')) {
+ var itemContainer = document.createElement('hr');
+ } else {
+ var itemContainer = document.createElement('div');
+ var itemText = document.createElement('a');
+ itemText.innerHTML = item.title;
+
+ if (item.tooltip) {
+ itemContainer.setAttribute('title', item.tooltip);
+ }
+
+ if (item.icon) {
+ itemContainer.setAttribute('data-icon', item.icon);
+ }
+
+ if (item.id) {
+ itemContainer.id = item.id;
+ }
+
+ if (item.disabled) {
+ itemContainer.className = 'jcontextmenu-disabled';
+ } else if (item.onclick) {
+ itemContainer.method = item.onclick;
+ itemContainer.addEventListener("mousedown", function(e) {
+ e.preventDefault();
+ });
+ itemContainer.addEventListener("mouseup", function(e) {
+ // Execute method
+ this.method(this, e);
+ });
+ }
+ itemContainer.appendChild(itemText);
+
+ if (item.submenu) {
+ var itemIconSubmenu = document.createElement('span');
+ itemIconSubmenu.innerHTML = "►";
+ itemContainer.appendChild(itemIconSubmenu);
+ itemContainer.classList.add('jcontexthassubmenu');
+ var el_submenu = document.createElement('div');
+ // Class definition
+ el_submenu.classList.add('jcontextmenu');
+ // Focusable
+ el_submenu.setAttribute('tabindex', '900');
+
+ // Append items
+ var submenu = item.submenu;
+ for (var i = 0; i < submenu.length; i++) {
+ var itemContainerSubMenu = createItemElement(submenu[i]);
+ el_submenu.appendChild(itemContainerSubMenu);
+ }
+
+ itemContainer.appendChild(el_submenu);
+ } else if (item.shortcut) {
+ var itemShortCut = document.createElement('span');
+ itemShortCut.innerHTML = item.shortcut;
+ itemContainer.appendChild(itemShortCut);
+ }
+ }
+ return itemContainer;
+ }
+
+ if (typeof(obj.options.onclick) == 'function') {
+ el.addEventListener('click', function(e) {
+ obj.options.onclick(obj, e);
+ });
+ }
+
+ // Create items
+ if (obj.options.items) {
+ obj.create(obj.options.items);
+ }
+
+ window.addEventListener("mousewheel", function() {
+ obj.close();
+ });
+
+ el.contextmenu = obj;
+
+ return obj;
+});
+
+
+jSuites.dropdown = (function(el, options) {
+ // Already created, update options
+ if (el.dropdown) {
+ return el.dropdown.setOptions(options, true);
+ }
+
+ // New instance
+ var obj = { type: 'dropdown' };
+ obj.options = {};
+
+ // Success
+ var success = function(data, val) {
+ // Set data
+ if (data && data.length) {
+ // Sort
+ if (obj.options.sortResults !== false) {
+ if(typeof obj.options.sortResults == "function") {
+ data.sort(obj.options.sortResults);
+ } else {
+ data.sort(sortData);
+ }
+ }
+
+ obj.setData(data);
+ }
+
+ // Onload method
+ if (typeof(obj.options.onload) == 'function') {
+ obj.options.onload(el, obj, data, val);
+ }
+
+ // Set value
+ if (val) {
+ applyValue(val);
+ }
+
+ // Component value
+ if (val === undefined || val === null) {
+ obj.options.value = '';
+ }
+ el.value = obj.options.value;
+
+ // Open dropdown
+ if (obj.options.opened == true) {
+ obj.open();
+ }
+ }
+
+
+ // Default sort
+ var sortData = function(itemA, itemB) {
+ var testA, testB;
+ if(typeof itemA == "string") {
+ testA = itemA;
+ } else {
+ if(itemA.text) {
+ testA = itemA.text;
+ } else if(itemA.name) {
+ testA = itemA.name;
+ }
+ }
+
+ if(typeof itemB == "string") {
+ testB = itemB;
+ } else {
+ if(itemB.text) {
+ testB = itemB.text;
+ } else if(itemB.name) {
+ testB = itemB.name;
+ }
+ }
+
+ if (typeof testA == "string" || typeof testB == "string") {
+ if (typeof testA != "string") { testA = ""+testA; }
+ if (typeof testB != "string") { testB = ""+testB; }
+ return testA.localeCompare(testB);
+ } else {
+ return testA - testB;
+ }
+ }
+
+ /**
+ * Reset the options for the dropdown
+ */
+ var resetValue = function() {
+ // Reset value container
+ obj.value = {};
+ // Remove selected
+ for (var i = 0; i < obj.items.length; i++) {
+ if (obj.items[i].selected == true) {
+ if (obj.items[i].element) {
+ obj.items[i].element.classList.remove('jdropdown-selected')
+ }
+ obj.items[i].selected = null;
+ }
+ }
+ // Reset options
+ obj.options.value = '';
+ // Reset value
+ el.value = '';
+ }
+
+ /**
+ * Apply values to the dropdown
+ */
+ var applyValue = function(values) {
+ // Reset the current values
+ resetValue();
+
+ // Read values
+ if (values !== null) {
+ if (! values) {
+ if (typeof(obj.value['']) !== 'undefined') {
+ obj.value[''] = '';
+ }
+ } else {
+ if (! Array.isArray(values)) {
+ values = ('' + values).split(';');
+ }
+ for (var i = 0; i < values.length; i++) {
+ obj.value[values[i]] = '';
+ }
+ }
+ }
+
+ // Update the DOM
+ for (var i = 0; i < obj.items.length; i++) {
+ if (typeof(obj.value[Value(i)]) !== 'undefined') {
+ if (obj.items[i].element) {
+ obj.items[i].element.classList.add('jdropdown-selected')
+ }
+ obj.items[i].selected = true;
+
+ // Keep label
+ obj.value[Value(i)] = Text(i);
+ }
+ }
+
+ // Global value
+ obj.options.value = Object.keys(obj.value).join(';');
+
+ // Update labels
+ obj.header.value = obj.getText();
+ }
+
+ // Get the value of one item
+ var Value = function(k, v) {
+ // Legacy purposes
+ if (! obj.options.format) {
+ var property = 'value';
+ } else {
+ var property = 'id';
+ }
+
+ if (obj.items[k]) {
+ if (v !== undefined) {
+ return obj.items[k].data[property] = v;
+ } else {
+ return obj.items[k].data[property];
+ }
+ }
+
+ return '';
+ }
+
+ // Get the label of one item
+ var Text = function(k, v) {
+ // Legacy purposes
+ if (! obj.options.format) {
+ var property = 'text';
+ } else {
+ var property = 'name';
+ }
+
+ if (obj.items[k]) {
+ if (v !== undefined) {
+ return obj.items[k].data[property] = v;
+ } else {
+ return obj.items[k].data[property];
+ }
+ }
+
+ return '';
+ }
+
+ var getValue = function() {
+ return Object.keys(obj.value);
+ }
+
+ var getText = function() {
+ var data = [];
+ var k = Object.keys(obj.value);
+ for (var i = 0; i < k.length; i++) {
+ data.push(obj.value[k[i]]);
+ }
+ return data;
+ }
+
+ obj.setOptions = function(options, reset) {
+ if (! options) {
+ options = {};
+ }
+
+ // Default configuration
+ var defaults = {
+ url: null,
+ data: [],
+ format: 0,
+ multiple: false,
+ autocomplete: false,
+ remoteSearch: false,
+ lazyLoading: false,
+ type: null,
+ width: null,
+ maxWidth: null,
+ opened: false,
+ value: null,
+ placeholder: '',
+ newOptions: false,
+ position: false,
+ onchange: null,
+ onload: null,
+ onopen: null,
+ onclose: null,
+ onfocus: null,
+ onblur: null,
+ oninsert: null,
+ onbeforeinsert: null,
+ sortResults: false,
+ autofocus: false,
+ }
+
+ // Loop through our object
+ for (var property in defaults) {
+ if (options && options.hasOwnProperty(property)) {
+ obj.options[property] = options[property];
+ } else {
+ if (typeof(obj.options[property]) == 'undefined' || reset === true) {
+ obj.options[property] = defaults[property];
+ }
+ }
+ }
+
+ // Force autocomplete search
+ if (obj.options.remoteSearch == true || obj.options.type === 'searchbar') {
+ obj.options.autocomplete = true;
+ }
+
+ // New options
+ if (obj.options.newOptions == true) {
+ obj.header.classList.add('jdropdown-add');
+ } else {
+ obj.header.classList.remove('jdropdown-add');
+ }
+
+ // Autocomplete
+ if (obj.options.autocomplete == true) {
+ obj.header.removeAttribute('readonly');
+ } else {
+ obj.header.setAttribute('readonly', 'readonly');
+ }
+
+ // Place holder
+ if (obj.options.placeholder) {
+ obj.header.setAttribute('placeholder', obj.options.placeholder);
+ } else {
+ obj.header.removeAttribute('placeholder');
+ }
+
+ // Remove specific dropdown typing to add again
+ el.classList.remove('jdropdown-searchbar');
+ el.classList.remove('jdropdown-picker');
+ el.classList.remove('jdropdown-list');
+
+ 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) {
+ if (obj.options.autocomplete) {
+ el.classList.add('jdropdown-searchbar');
+ obj.options.type = 'searchbar';
+ } else {
+ 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;
+ } else {
+ el.style.removeProperty('width');
+ el.style.removeProperty('min-width');
+ }
+
+ el.classList.add('jdropdown-default');
+ obj.options.type = 'default';
+ }
+ }
+
+ // Close button
+ if (obj.options.type == 'searchbar') {
+ containerHeader.appendChild(closeButton);
+ } else {
+ container.insertBefore(closeButton, container.firstChild);
+ }
+
+ // Load the content
+ if (obj.options.url && ! options.data) {
+ jSuites.ajax({
+ url: obj.options.url,
+ method: 'GET',
+ dataType: 'json',
+ success: function(data) {
+ if (data) {
+ success(data, obj.options.value);
+ }
+ }
+ });
+ } else {
+ success(obj.options.data, obj.options.value);
+ }
+
+ // Return the instance
+ return obj;
+ }
+
+ // Helpers
+ var containerHeader = null;
+ var container = null;
+ var content = null;
+ var closeButton = null;
+ var resetButton = null;
+ var backdrop = null;
+
+ var keyTimer = null;
+
+ /**
+ * Init dropdown
+ */
+ var init = function() {
+ // Do not accept null
+ if (! options) {
+ 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;
+ }
+
+ // Place holder
+ if (! options.placeholder && el.getAttribute('placeholder')) {
+ options.placeholder = el.getAttribute('placeholder');
+ }
+
+ // Value container
+ obj.value = {};
+ // Containers
+ obj.items = [];
+ obj.groups = [];
+ // Search options
+ obj.search = '';
+ obj.results = null;
+
+ // Create dropdown
+ el.classList.add('jdropdown');
+
+ // Header container
+ containerHeader = document.createElement('div');
+ containerHeader.className = 'jdropdown-container-header';
+
+ // Header
+ obj.header = document.createElement('input');
+ obj.header.className = 'jdropdown-header jss_object';
+ obj.header.type = 'text';
+ obj.header.setAttribute('autocomplete', 'off');
+ obj.header.onfocus = function() {
+ if (typeof(obj.options.onfocus) == 'function') {
+ obj.options.onfocus(el);
+ }
+ }
+
+ obj.header.onblur = function() {
+ if (typeof(obj.options.onblur) == 'function') {
+ obj.options.onblur(el);
+ }
+ }
+
+ obj.header.onkeyup = function(e) {
+ if (obj.options.autocomplete == true && ! keyTimer) {
+ if (obj.search != obj.header.value.trim()) {
+ keyTimer = setTimeout(function() {
+ obj.find(obj.header.value.trim());
+ keyTimer = null;
+ }, 400);
+ }
+
+ if (! el.classList.contains('jdropdown-focus')) {
+ obj.open();
+ }
+ } else {
+ if (! obj.options.autocomplete) {
+ obj.next(e.key);
+ }
+ }
+ }
+
+ // Global controls
+ if (! jSuites.dropdown.hasEvents) {
+ // Execute only one time
+ jSuites.dropdown.hasEvents = true;
+ // Enter and Esc
+ document.addEventListener("keydown", jSuites.dropdown.keydown);
+ }
+
+ // Container
+ container = document.createElement('div');
+ container.className = 'jdropdown-container';
+
+ // Dropdown content
+ content = document.createElement('div');
+ content.className = 'jdropdown-content';
+
+ // Close button
+ closeButton = document.createElement('div');
+ closeButton.className = 'jdropdown-close';
+ closeButton.innerHTML = 'Done';
+
+ // Reset button
+ resetButton = document.createElement('div');
+ resetButton.className = 'jdropdown-reset';
+ resetButton.innerHTML = 'x';
+ resetButton.onclick = function() {
+ obj.reset();
+ obj.close();
+ }
+
+ // Create backdrop
+ backdrop = document.createElement('div');
+ backdrop.className = 'jdropdown-backdrop';
+
+ // Append elements
+ containerHeader.appendChild(obj.header);
+
+ container.appendChild(content);
+ el.appendChild(containerHeader);
+ el.appendChild(container);
+ el.appendChild(backdrop);
+
+ // Set the otiptions
+ obj.setOptions(options);
+
+ if ('ontouchsend' in document.documentElement === true) {
+ el.addEventListener('touchsend', jSuites.dropdown.mouseup);
+ } else {
+ el.addEventListener('mouseup', jSuites.dropdown.mouseup);
+ }
+
+ // Lazyloading
+ if (obj.options.lazyLoading == true) {
+ jSuites.lazyLoading(content, {
+ loadUp: obj.loadUp,
+ loadDown: obj.loadDown,
+ });
+ }
+
+ content.onwheel = function(e) {
+ e.stopPropagation();
+ }
+
+ // Change method
+ el.change = obj.setValue;
+
+ // Global generic value handler
+ el.val = function(val) {
+ if (val === undefined) {
+ return obj.getValue(obj.options.multiple ? true : false);
+ } else {
+ obj.setValue(val);
+ }
+ }
+
+ // Keep object available from the node
+ el.dropdown = obj;
+ }
+
+ /**
+ * Get the current remote source of data URL
+ */
+ obj.getUrl = function() {
+ return obj.options.url;
+ }
+
+ /**
+ * Set the new data from a remote source
+ * @param {string} url - url from the remote source
+ * @param {function} callback - callback when the data is loaded
+ */
+ obj.setUrl = function(url, callback) {
+ obj.options.url = url;
+
+ jSuites.ajax({
+ url: obj.options.url,
+ method: 'GET',
+ dataType: 'json',
+ success: function(data) {
+ obj.setData(data);
+ // Callback
+ if (typeof(callback) == 'function') {
+ callback(obj);
+ }
+ }
+ });
+ }
+
+ /**
+ * Set ID for one item
+ */
+ obj.setId = function(item, v) {
+ // Legacy purposes
+ if (! obj.options.format) {
+ var property = 'value';
+ } else {
+ var property = 'id';
+ }
+
+ if (typeof(item) == 'object') {
+ item[property] = v;
+ } else {
+ obj.items[item].data[property] = v;
+ }
+ }
+
+ /**
+ * Add a new item
+ * @param {string} title - title of the new item
+ * @param {string} id - value/id of the new item
+ */
+ obj.add = function(title, id) {
+ if (! title) {
+ var current = obj.options.autocomplete == true ? obj.header.value : '';
+ var title = prompt(jSuites.translate('Add A New Option'), current);
+ if (! title) {
+ return false;
+ }
+ }
+
+ // Id
+ if (! id) {
+ id = jSuites.guid();
+ }
+
+ // Create new item
+ if (! obj.options.format) {
+ var item = {
+ value: id,
+ text: title,
+ }
+ } else {
+ var item = {
+ id: id,
+ name: title,
+ }
+ }
+
+ // Callback
+ if (typeof(obj.options.onbeforeinsert) == 'function') {
+ var ret = obj.options.onbeforeinsert(obj, item);
+ if (ret === false) {
+ return false;
+ } else if (ret) {
+ item = ret;
+ }
+ }
+
+ // Add item to the main list
+ obj.options.data.push(item);
+
+ // Create DOM
+ var newItem = obj.createItem(item);
+
+ // Append DOM to the list
+ content.appendChild(newItem.element);
+
+ // Callback
+ if (typeof(obj.options.oninsert) == 'function') {
+ obj.options.oninsert(obj, item, newItem);
+ }
+
+ // Show content
+ if (content.style.display == 'none') {
+ content.style.display = '';
+ }
+
+ // Search?
+ if (obj.results) {
+ obj.results.push(newItem);
+ }
+
+ return item;
+ }
+
+ /**
+ * Create a new item
+ */
+ obj.createItem = function(data, group, groupName) {
+ // Keep the correct source of data
+ if (! obj.options.format) {
+ if (! data.value && data.id !== undefined) {
+ data.value = data.id;
+ //delete data.id;
+ }
+ if (! data.text && data.name !== undefined) {
+ data.text = data.name;
+ //delete data.name;
+ }
+ } else {
+ if (! data.id && data.value !== undefined) {
+ data.id = data.value;
+ //delete data.value;
+ }
+ if (! data.name && data.text !== undefined) {
+ data.name = data.text
+ //delete data.text;
+ }
+ }
+
+ // Create item
+ var item = {};
+ item.element = document.createElement('div');
+ item.element.className = 'jdropdown-item';
+ item.element.indexValue = obj.items.length;
+ item.data = data;
+
+ // Groupd DOM
+ if (group) {
+ item.group = group;
+ }
+
+ // Id
+ if (data.id) {
+ item.element.setAttribute('id', data.id);
+ }
+
+ // Disabled
+ if (data.disabled == true) {
+ item.element.setAttribute('data-disabled', true);
+ }
+
+ // Tooltip
+ if (data.tooltip) {
+ item.element.setAttribute('title', data.tooltip);
+ }
+
+ // 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);
+ } else if (data.icon) {
+ var icon = document.createElement('span');
+ icon.className = "jdropdown-icon material-icons";
+ icon.innerText = data.icon;
+ if (! data.title) {
+ icon.classList.add('jdropdown-icon-small');
+ }
+ if (data.color) {
+ icon.style.color = data.color;
+ }
+ item.element.appendChild(icon);
+ } else if (data.color) {
+ var color = document.createElement('div');
+ color.className = 'jdropdown-color';
+ color.style.backgroundColor = data.color;
+ item.element.appendChild(color);
+ }
+
+ // Set content
+ if (! obj.options.format) {
+ var text = data.text;
+ } else {
+ var text = data.name;
+ }
+
+ var node = document.createElement('div');
+ node.className = 'jdropdown-description';
+ node.innerHTML = text || ' ';
+
+ // Title
+ if (data.title) {
+ var title = document.createElement('div');
+ title.className = 'jdropdown-title';
+ title.innerText = data.title;
+ node.appendChild(title);
+ }
+
+ // Set content
+ if (! obj.options.format) {
+ var val = data.value;
+ } else {
+ var val = data.id;
+ }
+
+ // Value
+ if (obj.value[val]) {
+ item.element.classList.add('jdropdown-selected');
+ item.selected = true;
+ }
+
+ // Keep DOM accessible
+ obj.items.push(item);
+
+ // Add node to item
+ item.element.appendChild(node);
+
+ return item;
+ }
+
+ obj.appendData = function(data) {
+ // Create elements
+ if (data.length) {
+ // Helpers
+ var items = [];
+ var groups = [];
+
+ // Prepare data
+ for (var i = 0; i < data.length; 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);
+ }
+ }
+
+ // Number of items counter
+ var counter = 0;
+
+ // 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]], group, groupNames[i]);
+
+ if (obj.options.lazyLoading == false || counter < 200) {
+ groupContent.appendChild(item.element);
+ counter++;
+ }
+ }
+ // Group itens
+ group.appendChild(groupName);
+ group.appendChild(groupContent);
+ // Keep group DOM
+ obj.groups.push(group);
+ // Only add to the screen if children on the group
+ if (groupContent.children.length > 0) {
+ // Add DOM to the content
+ content.appendChild(group);
+ }
+ }
+ }
+
+ if (items.length) {
+ for (var i = 0; i < items.length; i++) {
+ var item = obj.createItem(data[items[i]]);
+ if (obj.options.lazyLoading == false || counter < 200) {
+ content.appendChild(item.element);
+ counter++;
+ }
+ }
+ }
+ }
+ }
+
+ obj.setData = function(data) {
+ // Reset current value
+ resetValue();
+
+ // Make sure the content container is blank
+ content.innerHTML = '';
+
+ // Reset
+ obj.header.value = '';
+
+ // Reset items and values
+ obj.items = [];
+
+ // Prepare data
+ if (data && data.length) {
+ for (var i = 0; i < data.length; i++) {
+ // Compatibility
+ if (typeof(data[i]) != 'object') {
+ // Correct format
+ if (! obj.options.format) {
+ data[i] = {
+ value: data[i],
+ text: data[i]
+ }
+ } else {
+ data[i] = {
+ id: data[i],
+ name: data[i]
+ }
+ }
+ }
+ }
+
+ // Append data
+ obj.appendData(data);
+
+ // Update data
+ obj.options.data = data;
+ } else {
+ // Update data
+ obj.options.data = [];
+ }
+
+ obj.close();
+ }
+
+ obj.getData = function() {
+ return obj.options.data;
+ }
+
+ /**
+ * Get position of the item
+ */
+ obj.getPosition = function(val) {
+ for (var i = 0; i < obj.items.length; i++) {
+ if (Value(i) == val) {
+ return i;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get dropdown current text
+ */
+ obj.getText = function(asArray) {
+ // Get value
+ var v = getText();
+ // Return value
+ if (asArray) {
+ return v;
+ } else {
+ return v.join('; ');
+ }
+ }
+
+ /**
+ * Get dropdown current value
+ */
+ obj.getValue = function(asArray) {
+ // Get value
+ var v = getValue();
+ // Return value
+ if (asArray) {
+ return v;
+ } else {
+ return v.join(';');
+ }
+ }
+
+ /**
+ * Change event
+ */
+ var change = function(oldValue) {
+ // Lemonade JS
+ if (el.value != obj.options.value) {
+ el.value = obj.options.value;
+ if (typeof(el.oninput) == 'function') {
+ el.oninput({
+ type: 'input',
+ target: el,
+ value: el.value
+ });
+ }
+ }
+
+ // Events
+ if (typeof(obj.options.onchange) == 'function') {
+ obj.options.onchange(el, obj, oldValue, obj.options.value);
+ }
+ }
+
+ /**
+ * Set value
+ */
+ obj.setValue = function(newValue) {
+ // Current value
+ var oldValue = obj.getValue();
+ // New value
+ if (Array.isArray(newValue)) {
+ newValue = newValue.join(';')
+ }
+
+ if (oldValue !== newValue) {
+ // Set value
+ applyValue(newValue);
+
+ // Change
+ change(oldValue);
+ }
+ }
+
+ obj.resetSelected = function() {
+ obj.setValue(null);
+ }
+
+ obj.selectIndex = function(index, force) {
+ // Make sure is a number
+ var index = parseInt(index);
+
+ // Only select those existing elements
+ if (obj.items && obj.items[index] && (force === true || obj.items[index].data.disabled !== true)) {
+ // Reset cursor to a new position
+ obj.setCursor(index, false);
+
+ // Behaviour
+ if (! obj.options.multiple) {
+ // Update value
+ if (obj.items[index].selected) {
+ obj.setValue(null);
+ } else {
+ obj.setValue(Value(index));
+ }
+
+ // Close component
+ obj.close();
+ } else {
+ // Old value
+ var oldValue = obj.options.value;
+
+ // Toggle option
+ if (obj.items[index].selected) {
+ obj.items[index].element.classList.remove('jdropdown-selected');
+ obj.items[index].selected = false;
+
+ delete obj.value[Value(index)];
+ } else {
+ // Select element
+ obj.items[index].element.classList.add('jdropdown-selected');
+ obj.items[index].selected = true;
+
+ // Set value
+ obj.value[Value(index)] = Text(index);
+ }
+
+ // Global value
+ obj.options.value = Object.keys(obj.value).join(';');
+
+ // Update labels for multiple dropdown
+ if (obj.options.autocomplete == false) {
+ obj.header.value = getText().join('; ');
+ }
+
+ // Events
+ change(oldValue);
+ }
+ }
+ }
+
+ obj.selectItem = function(item) {
+ obj.selectIndex(item.indexValue);
+ }
+
+ var exists = function(k, result) {
+ for (var j = 0; j < result.length; j++) {
+ if (! obj.options.format) {
+ if (result[j].value == k) {
+ return true;
+ }
+ } else {
+ if (result[j].id == k) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ obj.find = function(str) {
+ if (obj.search == str.trim()) {
+ return false;
+ }
+
+ // Search term
+ obj.search = str;
+
+ // Reset index
+ obj.setCursor();
+
+ // Remove nodes from all groups
+ if (obj.groups.length) {
+ for (var i = 0; i < obj.groups.length; i++) {
+ obj.groups[i].lastChild.innerHTML = '';
+ }
+ }
+
+ // Remove all nodes
+ content.innerHTML = '';
+
+ // Remove current items in the remote search
+ if (obj.options.remoteSearch == true) {
+ // Reset results
+ obj.results = null;
+ // URL
+ var url = obj.options.url + (obj.options.url.indexOf('?') > 0 ? '&' : '?') + 'q=' + str;
+ // Remote search
+ jSuites.ajax({
+ url: url,
+ method: 'GET',
+ dataType: 'json',
+ success: function(result) {
+ // Reset items
+ obj.items = [];
+
+ // Add the current selected items to the results in case they are not there
+ var current = Object.keys(obj.value);
+ if (current.length) {
+ for (var i = 0; i < current.length; i++) {
+ if (! exists(current[i], result)) {
+ if (! obj.options.format) {
+ result.unshift({ value: current[i], text: obj.value[current[i]] });
+ } else {
+ result.unshift({ id: current[i], name: obj.value[current[i]] });
+ }
+ }
+ }
+ }
+ // Append data
+ obj.appendData(result);
+ // Show or hide results
+ if (! result.length) {
+ content.style.display = 'none';
+ } else {
+ content.style.display = '';
+ }
+ }
+ });
+ } else {
+ // Search terms
+ str = new RegExp(str, 'gi');
+
+ // Reset search
+ var results = [];
+
+ // Append options
+ for (var i = 0; i < obj.items.length; i++) {
+ // Item label
+ var label = Text(i);
+ // Item title
+ var title = obj.items[i].data.title || '';
+ // Group name
+ var groupName = obj.items[i].data.group || '';
+ // Synonym
+ var synonym = obj.items[i].data.synonym || '';
+ if (synonym) {
+ synonym = synonym.join(' ');
+ }
+
+ if (str == null || obj.items[i].selected == true || label.match(str) || title.match(str) || groupName.match(str) || synonym.match(str)) {
+ results.push(obj.items[i]);
+ }
+ }
+
+ if (! results.length) {
+ content.style.display = 'none';
+
+ // Results
+ obj.results = null;
+ } else {
+ content.style.display = '';
+
+ // Results
+ obj.results = results;
+
+ // Show 200 items at once
+ var number = results.length || 0;
+
+ // Lazyloading
+ if (obj.options.lazyLoading == true && number > 200) {
+ number = 200;
+ }
+
+ for (var i = 0; i < number; i++) {
+ if (obj.results[i].group) {
+ if (! obj.results[i].group.parentNode) {
+ content.appendChild(obj.results[i].group);
+ }
+ obj.results[i].group.lastChild.appendChild(obj.results[i].element);
+ } else {
+ content.appendChild(obj.results[i].element);
+ }
+ }
+ }
+ }
+
+ // Auto focus
+ if (obj.options.autofocus == true) {
+ obj.first();
+ }
+ }
+
+ obj.open = function() {
+ // Focus
+ if (! el.classList.contains('jdropdown-focus')) {
+ // Current dropdown
+ jSuites.dropdown.current = obj;
+
+ // Start tracking
+ jSuites.tracking(obj, true);
+
+ // Add focus
+ el.classList.add('jdropdown-focus');
+
+ // Animation
+ if (jSuites.getWindowWidth() < 800) {
+ if (obj.options.type == null || obj.options.type == 'picker') {
+ jSuites.animation.slideBottom(container, 1);
+ }
+ }
+
+ // Filter
+ if (obj.options.autocomplete == true) {
+ obj.header.value = obj.search;
+ obj.header.focus();
+ }
+
+ // Set cursor for the first or first selected element
+ var k = getValue();
+ if (k[0]) {
+ var cursor = obj.getPosition(k[0]);
+ if (cursor !== false) {
+ obj.setCursor(cursor);
+ }
+ }
+
+ // Container Size
+ if (! obj.options.type || obj.options.type == 'default') {
+ var rect = el.getBoundingClientRect();
+ var 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';
+
+ if (obj.options.maxWidth) {
+ container.style.maxWidth = obj.options.maxWidth;
+ }
+
+ if (! obj.items.length && obj.options.autocomplete == true) {
+ content.style.display = 'none';
+ } else {
+ content.style.display = '';
+ }
+ }
+ }
+
+ // Events
+ if (typeof(obj.options.onopen) == 'function') {
+ obj.options.onopen(el);
+ }
+ }
+
+ obj.close = function(ignoreEvents) {
+ if (el.classList.contains('jdropdown-focus')) {
+ // Update labels
+ obj.header.value = obj.getText();
+ // Remove cursor
+ obj.setCursor();
+ // Events
+ if (! ignoreEvents && typeof(obj.options.onclose) == 'function') {
+ obj.options.onclose(el);
+ }
+ // Blur
+ if (obj.header.blur) {
+ obj.header.blur();
+ }
+ // Remove focus
+ el.classList.remove('jdropdown-focus');
+ // Start tracking
+ jSuites.tracking(obj, false);
+ // Current dropdown
+ jSuites.dropdown.current = null;
+ }
+
+ return obj.getValue();
+ }
+
+ /**
+ * Set cursor
+ */
+ obj.setCursor = function(index, setPosition) {
+ // 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');
+ }
+ }
+
+ if (index == undefined) {
+ obj.currentIndex = null;
+ } else {
+ index = parseInt(index);
+
+ // Cursor only for visible items
+ if (obj.items[index].element.parentNode) {
+ obj.items[index].element.classList.add('jdropdown-cursor');
+ obj.currentIndex = index;
+
+ // Update scroll to the cursor element
+ if (setPosition !== false && obj.items[obj.currentIndex].element) {
+ var container = content.scrollTop;
+ var element = obj.items[obj.currentIndex].element;
+ content.scrollTop = element.offsetTop - element.scrollTop + element.clientTop - 95;
+ }
+ }
+ }
+ }
+
+ // Compatibility
+ obj.resetCursor = obj.setCursor;
+ obj.updateCursor = obj.setCursor;
+
+ /**
+ * Reset cursor and selected items
+ */
+ obj.reset = function() {
+ // Reset cursor
+ obj.setCursor();
+
+ // Reset selected
+ obj.setValue(null);
+ }
+
+ /**
+ * First available item
+ */
+ obj.first = function() {
+ if (obj.options.lazyLoading === true) {
+ obj.loadFirst();
+ }
+
+ var items = content.querySelectorAll('.jdropdown-item');
+ if (items.length) {
+ var newIndex = items[0].indexValue;
+ obj.setCursor(newIndex);
+ }
+ }
+
+ /**
+ * Last available item
+ */
+ obj.last = function() {
+ if (obj.options.lazyLoading === true) {
+ obj.loadLast();
+ }
+
+ var items = content.querySelectorAll('.jdropdown-item');
+ if (items.length) {
+ var newIndex = items[items.length-1].indexValue;
+ obj.setCursor(newIndex);
+ }
+ }
+
+ obj.next = function(letter) {
+ var newIndex = null;
+
+ if (letter) {
+ if (letter.length == 1) {
+ // Current index
+ var current = obj.currentIndex || -1;
+ // Letter
+ letter = letter.toLowerCase();
+
+ var e = null;
+ var l = null;
+ var items = content.querySelectorAll('.jdropdown-item');
+ if (items.length) {
+ for (var i = 0; i < items.length; i++) {
+ if (items[i].indexValue > current) {
+ if (e = obj.items[items[i].indexValue]) {
+ if (l = e.element.innerText[0]) {
+ l = l.toLowerCase();
+ if (letter == l) {
+ newIndex = items[i].indexValue;
+ break;
+ }
+ }
+ }
+ }
+ }
+ obj.setCursor(newIndex);
+ }
+ }
+ } else {
+ if (obj.currentIndex == undefined || obj.currentIndex == null) {
+ obj.first();
+ } else {
+ var element = obj.items[obj.currentIndex].element;
+
+ var next = element.nextElementSibling;
+ if (next) {
+ if (next.classList.contains('jdropdown-group')) {
+ next = next.lastChild.firstChild;
+ }
+ newIndex = next.indexValue;
+ } else {
+ if (element.parentNode.classList.contains('jdropdown-group-items')) {
+ if (next = element.parentNode.parentNode.nextElementSibling) {
+ if (next.classList.contains('jdropdown-group')) {
+ next = next.lastChild.firstChild;
+ } else if (next.classList.contains('jdropdown-item')) {
+ newIndex = next.indexValue;
+ } else {
+ next = null;
+ }
+ }
+
+ if (next) {
+ newIndex = next.indexValue;
+ }
+ }
+ }
+
+ if (newIndex !== null) {
+ obj.setCursor(newIndex);
+ }
+ }
+ }
+ }
+
+ obj.prev = function() {
+ var newIndex = null;
+
+ if (obj.currentIndex === null) {
+ obj.first();
+ } else {
+ var element = obj.items[obj.currentIndex].element;
+
+ var prev = element.previousElementSibling;
+ if (prev) {
+ if (prev.classList.contains('jdropdown-group')) {
+ prev = prev.lastChild.lastChild;
+ }
+ newIndex = prev.indexValue;
+ } else {
+ if (element.parentNode.classList.contains('jdropdown-group-items')) {
+ if (prev = element.parentNode.parentNode.previousElementSibling) {
+ if (prev.classList.contains('jdropdown-group')) {
+ prev = prev.lastChild.lastChild;
+ } else if (prev.classList.contains('jdropdown-item')) {
+ newIndex = prev.indexValue;
+ } else {
+ prev = null
+ }
+ }
+
+ if (prev) {
+ newIndex = prev.indexValue;
+ }
+ }
+ }
+ }
+
+ if (newIndex !== null) {
+ obj.setCursor(newIndex);
+ }
+ }
+
+ obj.loadFirst = function() {
+ // Search
+ if (obj.results) {
+ var results = obj.results;
+ } else {
+ var results = obj.items;
+ }
+
+ // Show 200 items at once
+ var number = results.length || 0;
+
+ // Lazyloading
+ if (obj.options.lazyLoading == true && number > 200) {
+ number = 200;
+ }
+
+ // Reset container
+ content.innerHTML = '';
+
+ // First 200 items
+ for (var i = 0; i < number; i++) {
+ if (results[i].group) {
+ if (! results[i].group.parentNode) {
+ content.appendChild(results[i].group);
+ }
+ results[i].group.lastChild.appendChild(results[i].element);
+ } else {
+ content.appendChild(results[i].element);
+ }
+ }
+
+ // Scroll go to the begin
+ content.scrollTop = 0;
+ }
+
+ obj.loadLast = function() {
+ // Search
+ if (obj.results) {
+ var results = obj.results;
+ } else {
+ var results = obj.items;
+ }
+
+ // Show first page
+ var number = results.length;
+
+ // Max 200 items
+ if (number > 200) {
+ number = number - 200;
+
+ // Reset container
+ content.innerHTML = '';
+
+ // First 200 items
+ for (var i = number; i < results.length; i++) {
+ if (results[i].group) {
+ if (! results[i].group.parentNode) {
+ content.appendChild(results[i].group);
+ }
+ results[i].group.lastChild.appendChild(results[i].element);
+ } else {
+ content.appendChild(results[i].element);
+ }
+ }
+
+ // Scroll go to the begin
+ content.scrollTop = content.scrollHeight;
+ }
+ }
+
+ obj.loadUp = function() {
+ var test = false;
+
+ // Search
+ if (obj.results) {
+ var results = obj.results;
+ } else {
+ var results = obj.items;
+ }
+
+ var items = content.querySelectorAll('.jdropdown-item');
+ var fistItem = items[0].indexValue;
+ fistItem = obj.items[fistItem];
+ var index = results.indexOf(fistItem) - 1;
+
+ if (index > 0) {
+ var number = 0;
+
+ while (index > 0 && results[index] && number < 200) {
+ if (results[index].group) {
+ if (! results[index].group.parentNode) {
+ content.insertBefore(results[index].group, content.firstChild);
+ }
+ results[index].group.lastChild.insertBefore(results[index].element, results[index].group.lastChild.firstChild);
+ } else {
+ content.insertBefore(results[index].element, content.firstChild);
+ }
+
+ index--;
+ number++;
+ }
+
+ // New item added
+ test = true;
+ }
+
+ return test;
+ }
+
+ obj.loadDown = function() {
+ var test = false;
+
+ // Search
+ if (obj.results) {
+ var results = obj.results;
+ } else {
+ var results = obj.items;
+ }
+
+ var items = content.querySelectorAll('.jdropdown-item');
+ var lastItem = items[items.length-1].indexValue;
+ lastItem = obj.items[lastItem];
+ var index = results.indexOf(lastItem) + 1;
+
+ if (index < results.length) {
+ var number = 0;
+ while (index < results.length && results[index] && number < 200) {
+ if (results[index].group) {
+ if (! results[index].group.parentNode) {
+ content.appendChild(results[index].group);
+ }
+ results[index].group.lastChild.appendChild(results[index].element);
+ } else {
+ content.appendChild(results[index].element);
+ }
+
+ index++;
+ number++;
+ }
+
+ // New item added
+ test = true;
+ }
+
+ return test;
+ }
+
+ init();
+
+ return obj;
+});
+
+jSuites.dropdown.keydown = function(e) {
+ var dropdown = null;
+ if (dropdown = jSuites.dropdown.current) {
+ if (e.which == 13 || e.which == 9) { // enter or tab
+ if (dropdown.header.value && dropdown.currentIndex == null && dropdown.options.newOptions) {
+ // if they typed something in, but it matched nothing, and newOptions are allowed, start that flow
+ dropdown.add();
+ } else {
+ // Quick Select/Filter
+ if (dropdown.currentIndex == null && dropdown.options.autocomplete == true && dropdown.header.value != "") {
+ dropdown.find(dropdown.header.value);
+ }
+ dropdown.selectIndex(dropdown.currentIndex);
+ }
+ } else if (e.which == 38) { // up arrow
+ if (dropdown.currentIndex == null) {
+ dropdown.first();
+ } else if (dropdown.currentIndex > 0) {
+ dropdown.prev();
+ }
+ e.preventDefault();
+ } else if (e.which == 40) { // down arrow
+ if (dropdown.currentIndex == null) {
+ dropdown.first();
+ } else if (dropdown.currentIndex + 1 < dropdown.items.length) {
+ dropdown.next();
+ }
+ e.preventDefault();
+ } else if (e.which == 36) {
+ dropdown.first();
+ if (! e.target.classList.contains('jdropdown-header')) {
+ e.preventDefault();
+ }
+ } else if (e.which == 35) {
+ dropdown.last();
+ if (! e.target.classList.contains('jdropdown-header')) {
+ e.preventDefault();
+ }
+ } else if (e.which == 27) {
+ dropdown.close();
+ } else if (e.which == 33) { // page up
+ if (dropdown.currentIndex == null) {
+ dropdown.first();
+ } else if (dropdown.currentIndex > 0) {
+ for (var i = 0; i < 7; i++) {
+ dropdown.prev()
+ }
+ }
+ e.preventDefault();
+ } else if (e.which == 34) { // page down
+ if (dropdown.currentIndex == null) {
+ dropdown.first();
+ } else if (dropdown.currentIndex + 1 < dropdown.items.length) {
+ for (var i = 0; i < 7; i++) {
+ dropdown.next()
+ }
+ }
+ e.preventDefault();
+ }
+ }
+}
+
+jSuites.dropdown.mouseup = function(e) {
+ var element = jSuites.findElement(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')) {
+ var rect = element.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) < 30) {
+ if (e.target.classList.contains('jdropdown-add')) {
+ dropdown.add();
+ } else {
+ dropdown.close();
+ }
+ } else {
+ if (dropdown.options.autocomplete == false) {
+ 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.selectItem(e.target.parentNode);
+ } else if (e.target.classList.contains('jdropdown-description')) {
+ dropdown.selectItem(e.target.parentNode);
+ } else if (e.target.classList.contains('jdropdown-title')) {
+ dropdown.selectItem(e.target.parentNode.parentNode);
+ } else if (e.target.classList.contains('jdropdown-close') || e.target.classList.contains('jdropdown-backdrop')) {
+ dropdown.close();
+ }
+ }
+}
+
+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({
+ value: el.children[j].children[i].value,
+ text: el.children[j].children[i].innerHTML,
+ group: el.children[j].getAttribute('label'),
+ });
+ }
+ } else {
+ options.data.push({
+ value: el.children[j].value,
+ text: 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 };
+}
+
+jSuites.editor = (function(el, options) {
+ // New instance
+ var obj = { type:'editor' };
+ obj.options = {};
+
+ // Default configuration
+ var defaults = {
+ // Load data from a remove location
+ url: null,
+ // Initial HTML content
+ value: '',
+ // Initial snippet
+ snippet: null,
+ // Add toolbar
+ toolbar: true,
+ toolbarOnTop: false,
+ // Website parser is to read websites and images from cross domain
+ remoteParser: null,
+ // Placeholder
+ placeholder: null,
+ // Parse URL
+ filterPaste: true,
+ // Accept drop files
+ dropZone: true,
+ dropAsSnippet: false,
+ acceptImages: true,
+ acceptFiles: false,
+ maxFileSize: 5000000,
+ allowImageResize: true,
+ // Style
+ maxHeight: null,
+ height: null,
+ focus: false,
+ // Events
+ onclick: null,
+ onfocus: null,
+ onblur: null,
+ onload: null,
+ onkeyup: null,
+ onkeydown: null,
+ onchange: null,
+ extensions: null,
+ type: 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 editorTimer = null;
+ var editorAction = null;
+ var files = [];
+
+ // Keep the reference for the container
+ obj.el = el;
+
+ if (typeof(obj.options.onclick) == 'function') {
+ el.onclick = function(e) {
+ obj.options.onclick(el, obj, e);
+ }
+ }
+
+ // Prepare container
+ el.classList.add('jeditor-container');
+
+ // Snippet
+ var snippet = document.createElement('div');
+ snippet.className = 'jsnippet';
+ snippet.setAttribute('contenteditable', false);
+
+ // Toolbar
+ var toolbar = document.createElement('div');
+ toolbar.className = 'jeditor-toolbar';
+
+ obj.editor = document.createElement('div');
+ obj.editor.setAttribute('contenteditable', true);
+ obj.editor.setAttribute('spellcheck', false);
+ obj.editor.classList.add('jeditor');
+
+ // Placeholder
+ if (obj.options.placeholder) {
+ obj.editor.setAttribute('data-placeholder', obj.options.placeholder);
+ }
+
+ // Max height
+ if (obj.options.maxHeight || obj.options.height) {
+ obj.editor.style.overflowY = 'auto';
+
+ if (obj.options.maxHeight) {
+ obj.editor.style.maxHeight = obj.options.maxHeight;
+ }
+ if (obj.options.height) {
+ obj.editor.style.height = obj.options.height;
+ }
+ }
+
+ // Set editor initial value
+ if (obj.options.url) {
+ jSuites.ajax({
+ url: obj.options.url,
+ dataType: 'html',
+ success: function(result) {
+ obj.editor.innerHTML = result;
+
+ jSuites.editor.setCursor(obj.editor, obj.options.focus == 'initial' ? true : false);
+ }
+ })
+ } else {
+ if (obj.options.value) {
+ obj.editor.innerHTML = obj.options.value;
+ } else {
+ // Create from existing elements
+ for (var i = 0; i < el.children.length; i++) {
+ obj.editor.appendChild(el.children[i]);
+ }
+ }
+ }
+
+ // Make sure element is empty
+ el.innerHTML = '';
+
+ /**
+ * Onchange event controllers
+ */
+ var change = function(e) {
+ if (typeof(obj.options.onchange) == 'function') {
+ obj.options.onchange(el, obj, e);
+ }
+
+ // Update value
+ obj.options.value = obj.getData();
+
+ // Lemonade JS
+ if (el.value != obj.options.value) {
+ el.value = obj.options.value;
+ if (typeof(el.oninput) == 'function') {
+ el.oninput({
+ type: 'input',
+ target: el,
+ value: el.value
+ });
+ }
+ }
+ }
+
+ /**
+ * 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);
+ }
+ }
+ }
+
+ var updateTotalImages = function() {
+ var o = null;
+ if (o = snippet.children[0]) {
+ // Make sure is a grid
+ if (! o.classList.contains('jslider-grid')) {
+ o.classList.add('jslider-grid');
+ }
+ // Quantify of images
+ var number = o.children.length;
+ // Set the configuration of the grid
+ o.setAttribute('data-number', number > 4 ? 4 : number);
+ // Total of images inside the grid
+ if (number > 4) {
+ o.setAttribute('data-total', number - 4);
+ } else {
+ o.removeAttribute('data-total');
+ }
+ }
+ }
+
+ /**
+ * Append image to the snippet
+ */
+ var appendImage = function(image) {
+ if (! snippet.innerHTML) {
+ obj.appendSnippet({});
+ }
+ snippet.children[0].appendChild(image);
+ updateTotalImages();
+ }
+
+ /**
+ * Append snippet
+ * @Param object data
+ */
+ obj.appendSnippet = function(data) {
+ // Reset snippet
+ snippet.innerHTML = '';
+
+ // Attributes
+ var a = [ 'image', 'title', 'description', 'host', 'url' ];
+
+ for (var i = 0; i < a.length; i++) {
+ var div = document.createElement('div');
+ div.className = 'jsnippet-' + a[i];
+ div.setAttribute('data-k', a[i]);
+ snippet.appendChild(div);
+ if (data[a[i]]) {
+ if (a[i] == 'image') {
+ if (! Array.isArray(data.image)) {
+ data.image = [ data.image ];
+ }
+ for (var j = 0; j < data.image.length; j++) {
+ var img = document.createElement('img');
+ img.src = data.image[j];
+ div.appendChild(img);
+ }
+ } else {
+ div.innerHTML = data[a[i]];
+ }
+ }
+ }
+
+ obj.editor.appendChild(document.createElement('br'));
+ obj.editor.appendChild(snippet);
+ }
+
+ /**
+ * Set editor value
+ */
+ obj.setData = function(o) {
+ if (typeof(o) == 'object') {
+ obj.editor.innerHTML = o.content;
+ } else {
+ obj.editor.innerHTML = o;
+ }
+
+ if (obj.options.focus) {
+ jSuites.editor.setCursor(obj.editor, true);
+ }
+
+ // Reset files container
+ files = [];
+ }
+
+ obj.getFiles = function() {
+ var f = obj.editor.querySelectorAll('.jfile');
+ var d = [];
+ for (var i = 0; i < f.length; i++) {
+ if (files[f[i].src]) {
+ d.push(files[f[i].src]);
+ }
+ }
+ return d;
+ }
+
+ obj.getText = function() {
+ return obj.editor.innerText;
+ }
+
+ /**
+ * Get editor data
+ */
+ obj.getData = function(json) {
+ if (! json) {
+ var data = obj.editor.innerHTML;
+ } else {
+ var data = {
+ content : '',
+ }
+
+ // Get snippet
+ 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') {
+ if (! data.snippet.image) {
+ data.snippet.image = [];
+ }
+ // Get all images
+ for (var j = 0; j < snippet.children[i].children.length; j++) {
+ data.snippet.image.push(snippet.children[i].children[j].getAttribute('src'))
+ }
+ } else {
+ data.snippet[key] = snippet.children[i].innerHTML;
+ }
+ }
+ }
+ }
+
+ // Get files
+ var f = Object.keys(files);
+ if (f.length) {
+ data.files = [];
+ for (var i = 0; i < f.length; i++) {
+ data.files.push(files[f[i]]);
+ }
+ }
+
+ // Get content
+ var d = document.createElement('div');
+ d.innerHTML = obj.editor.innerHTML;
+ var s = d.querySelector('.jsnippet');
+ if (s) {
+ s.remove();
+ }
+
+ var text = d.innerHTML;
+ text = text.replace(/
/g, "\n");
+ text = text.replace(/<\/div>/g, "<\/div>\n");
+ text = text.replace(/<(?:.|\n)*?>/gm, "");
+ data.content = text.trim();
+
+ // Process extensions
+ processExtensions('getData', data);
+ }
+
+ return data;
+ }
+
+ // Reset
+ obj.reset = function() {
+ obj.editor.innerHTML = '';
+ snippet.innerHTML = '';
+ files = [];
+ }
+
+ 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.title = data.name;
+ newImage.className = 'jfile pdf';
+
+ files[newImage.src] = {
+ file: newImage.src,
+ extension: 'pdf',
+ content: data.result,
+ }
+
+ //insertNodeAtCaret(newImage);
+ document.execCommand('insertHtml', false, newImage.outerHTML);
+ });
+ }
+ }
+
+ obj.addImage = function(src, asSnippet) {
+ if (! src) {
+ 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 newImage = document.createElement('img');
+ newImage.src = window.URL.createObjectURL(blob);
+ newImage.classList.add('jfile');
+ newImage.setAttribute('tabindex', '900');
+ newImage.setAttribute('width', img.width);
+ newImage.setAttribute('height', img.height);
+ files[newImage.src] = {
+ file: newImage.src,
+ extension: extension,
+ content: canvas.toDataURL(),
+ }
+
+ if (obj.options.dropAsSnippet || asSnippet) {
+ appendImage(newImage);
+ // Just to understand the attachment is part of a snippet
+ files[newImage.src].snippet = true;
+ } else {
+ //insertNodeAtCaret(newImage);
+ document.execCommand('insertHtml', false, newImage.outerHTML);
+ }
+
+ change();
+ });
+ };
+
+ 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);
+ }
+ }, false);
+
+ reader[i].readAsDataURL(files[i])
+ } else {
+ alert('The extension is not allowed');
+ }
+ }
+ }
+ }
+
+ // Destroy
+ obj.destroy = function() {
+ obj.editor.removeEventListener('mouseup', editorMouseUp);
+ obj.editor.removeEventListener('mousedown', editorMouseDown);
+ obj.editor.removeEventListener('mousemove', editorMouseMove);
+ obj.editor.removeEventListener('keyup', editorKeyUp);
+ obj.editor.removeEventListener('keydown', editorKeyDown);
+ obj.editor.removeEventListener('dragstart', editorDragStart);
+ obj.editor.removeEventListener('dragenter', editorDragEnter);
+ obj.editor.removeEventListener('dragover', editorDragOver);
+ obj.editor.removeEventListener('drop', editorDrop);
+ obj.editor.removeEventListener('paste', editorPaste);
+ obj.editor.removeEventListener('blur', editorBlur);
+ obj.editor.removeEventListener('focus', editorFocus);
+
+ el.editor = null;
+ el.classList.remove('jeditor-container');
+
+ toolbar.remove();
+ snippet.remove();
+ obj.editor.remove();
+ }
+
+ obj.upload = function() {
+ jSuites.click(obj.file);
+ }
+
+ // Elements to be removed
+ var remove = [
+ HTMLUnknownElement,
+ HTMLAudioElement,
+ HTMLEmbedElement,
+ HTMLIFrameElement,
+ HTMLTextAreaElement,
+ HTMLInputElement,
+ HTMLScriptElement
+ ];
+
+ // Valid properties
+ var validProperty = ['width', 'height', 'align', 'border', 'src', 'tabindex'];
+
+ // Valid CSS attributes
+ var validStyle = ['color', 'font-weight', 'font-size', 'background', 'background-color', 'margin'];
+
+ var parse = function(element) {
+ // Remove attributes
+ if (element.attributes && element.attributes.length) {
+ var image = null;
+ var style = null;
+ // Process style attribute
+ var elementStyle = element.getAttribute('style');
+ if (elementStyle) {
+ style = [];
+ var t = elementStyle.split(';');
+ for (var j = 0; j < t.length; j++) {
+ var v = t[j].trim().split(':');
+ if (validStyle.indexOf(v[0].trim()) >= 0) {
+ var k = v.shift();
+ var v = v.join(':');
+ style.push(k + ':' + v);
+ }
+ }
+ }
+ // Process image
+ if (element.tagName.toUpperCase() == 'IMG') {
+ if (! obj.options.acceptImages || ! element.src) {
+ element.parentNode.removeChild(element);
+ } else {
+ // Check if is data
+ element.setAttribute('tabindex', '900');
+ // Check attributes for persistance
+ obj.addImage(element.src);
+ }
+ }
+ // Remove attributes
+ var attr = [];
+ var numAttributes = element.attributes.length - 1;
+ if (numAttributes > 0) {
+ for (var i = numAttributes; i >= 0 ; i--) {
+ attr.push(element.attributes[i].name);
+ }
+ attr.forEach(function(v) {
+ if (validProperty.indexOf(v) == -1) {
+ element.removeAttribute(v);
+ }
+ });
+ }
+ element.style = '';
+ // Add valid style
+ if (style && style.length) {
+ element.setAttribute('style', style.join(';'));
+ }
+ }
+ // Parse children
+ if (element.children.length) {
+ for (var i = 0; i < element.children.length; i++) {
+ parse(element.children[i]);
+ }
+ }
+
+ if (remove.indexOf(element.constructor) >= 0) {
+ element.remove();
+ }
+ }
+
+ var select = function(e) {
+ var s = window.getSelection()
+ var r = document.createRange();
+ r.selectNode(e);
+ s.addRange(r)
+ }
+
+ var filter = function(data) {
+ if (data) {
+ data = data.replace(new RegExp('', 'gsi'), '');
+ }
+ var parser = new DOMParser();
+ var d = parser.parseFromString(data, "text/html");
+ parse(d);
+ var span = document.createElement('span');
+ span.innerHTML = d.firstChild.innerHTML;
+ return span;
+ }
+
+ var editorPaste = function(e) {
+ if (obj.options.filterPaste == true) {
+ 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 {
+ if (! html) {
+ html = text.split('\r\n');
+ if (! e.target.innerText) {
+ html.map(function(v) {
+ var d = document.createElement('div');
+ d.innerText = v;
+ obj.editor.appendChild(d);
+ });
+ } else {
+ html = html.map(function(v) {
+ return '' + v + '
';
+ });
+ document.execCommand('insertHtml', false, html.join(''));
+ }
+ } else {
+ var d = filter(html);
+ // Paste to the editor
+ //insertNodeAtCaret(d);
+ document.execCommand('insertHtml', false, d.innerHTML);
+ }
+ }
+
+ 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');
+ e.preventDefault();
+ }
+ }
+
+ 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);
+ e.preventDefault();
+ }
+ }
+
+ 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(e) {
+ // Process extensions
+ processExtensions('onevent', e);
+ // Apply changes
+ change(e);
+ // Blur
+ if (typeof(obj.options.onblur) == 'function') {
+ obj.options.onblur(el, obj, e);
+ }
+ }
+
+ var editorFocus = function(e) {
+ // Focus
+ if (typeof(obj.options.onfocus) == 'function') {
+ obj.options.onfocus(el, obj, e);
+ }
+ }
+
+ var editorKeyUp = function(e) {
+ if (! obj.editor.innerHTML) {
+ obj.editor.innerHTML = '
';
+ }
+ if (typeof(obj.options.onkeyup) == 'function') {
+ obj.options.onkeyup(el, obj, e);
+ }
+ }
+
+ var editorKeyDown = function(e) {
+ // Process extensions
+ processExtensions('onevent', e);
+
+ if (e.key == 'Delete') {
+ if (e.target.tagName == 'IMG') {
+ var parent = e.target.parentNode;
+ select(e.target);
+ if (parent.classList.contains('jsnippet-image')) {
+ updateTotalImages();
+ }
+ }
+ }
+
+ if (typeof(obj.options.onkeydown) == 'function') {
+ obj.options.onkeydown(el, obj, e);
+ }
+ }
+
+ var editorMouseUp = function(e) {
+ if (editorAction && editorAction.e) {
+ editorAction.e.classList.remove('resizing');
+
+ if (editorAction.e.changed == true) {
+ var image = editorAction.e.cloneNode()
+ image.width = parseInt(editorAction.e.style.width) || editorAction.e.getAttribute('width');
+ image.height = parseInt(editorAction.e.style.height) || editorAction.e.getAttribute('height');
+ editorAction.e.style.width = '';
+ editorAction.e.style.height = '';
+ select(editorAction.e);
+ document.execCommand('insertHtml', false, image.outerHTML);
+ }
+ }
+
+ 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.getAttribute('width')) {
+ e.target.setAttribute('width', rect.width)
+ }
+
+ if (! e.target.getAttribute('height')) {
+ e.target.setAttribute('height', rect.height)
+ }
+
+ var s = window.getSelection();
+ if (s.rangeCount) {
+ for (var i = 0; i < s.rangeCount; i++) {
+ s.removeRange(s.getRangeAt(i));
+ }
+ }
+
+ e.target.classList.add('resizing');
+ } else {
+ editorAction = true;
+ }
+ } else {
+ if (e.target.classList.contains('jsnippet')) {
+ close(e.target);
+ } else if (e.target.parentNode.classList.contains('jsnippet')) {
+ close(e.target.parentNode);
+ }
+
+ editorAction = true;
+ }
+ }
+
+ var editorMouseMove = function(e) {
+ if (e.target.tagName == 'IMG' && ! e.target.parentNode.classList.contains('jsnippet-image') && obj.options.allowImageResize == true) {
+ 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));
+
+ if (e.shiftKey) {
+ var newHeight = (e.clientX - editorAction.x) * (editorAction.h / editorAction.w);
+ editorAction.e.style.height = editorAction.h + newHeight;
+ } 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);
+ }
+ }
+ }
+
+ editorAction.e.changed = true;
+ }
+ }
+
+ var processExtensions = function(method, data) {
+ if (obj.options.extensions) {
+ var ext = Object.keys(obj.options.extensions);
+ if (ext.length) {
+ for (var i = 0; i < ext.length; i++)
+ if (obj.options.extensions[ext[i]] && typeof(obj.options.extensions[ext[i]][method]) == 'function') {
+ obj.options.extensions[ext[i]][method].call(obj, data);
+ }
+ }
+ }
+ }
+
+ var loadExtensions = function() {
+ if (obj.options.extensions) {
+ var ext = Object.keys(obj.options.extensions);
+ if (ext.length) {
+ for (var i = 0; i < ext.length; i++) {
+ if (obj.options.extensions[ext[i]] && typeof (obj.options.extensions[ext[i]]) == 'function') {
+ obj.options.extensions[ext[i]] = obj.options.extensions[ext[i]](el, obj);
+ }
+ }
+ }
+ }
+ }
+
+ document.addEventListener('mouseup', editorMouseUp);
+ document.addEventListener('mousemove', editorMouseMove);
+ obj.editor.addEventListener('mousedown', editorMouseDown);
+ obj.editor.addEventListener('keyup', editorKeyUp);
+ obj.editor.addEventListener('keydown', editorKeyDown);
+ obj.editor.addEventListener('dragstart', editorDragStart);
+ obj.editor.addEventListener('dragenter', editorDragEnter);
+ obj.editor.addEventListener('dragover', editorDragOver);
+ obj.editor.addEventListener('drop', editorDrop);
+ obj.editor.addEventListener('paste', editorPaste);
+ obj.editor.addEventListener('focus', editorFocus);
+ obj.editor.addEventListener('blur', editorBlur);
+
+ // Append editor to the container
+ el.appendChild(obj.editor);
+ // Snippet
+ if (obj.options.snippet) {
+ obj.appendSnippet(obj.options.snippet);
+ }
+
+ // Add toolbar
+ if (obj.options.toolbar) {
+ // Default toolbar configuration
+ if (Array.isArray(obj.options.toolbar)) {
+ var toolbarOptions = {
+ container: true,
+ responsive: true,
+ items: obj.options.toolbar
+ }
+ } else if (obj.options.toolbar === true) {
+ var toolbarOptions = {
+ container: true,
+ responsive: true,
+ items: [],
+ }
+ } else {
+ var toolbarOptions = obj.options.toolbar;
+ }
+
+ // Default items
+ if (! (toolbarOptions.items && toolbarOptions.items.length)) {
+ toolbarOptions.items = jSuites.editor.getDefaultToolbar(obj);
+ }
+
+ if (obj.options.toolbarOnTop) {
+ // Add class
+ el.classList.add('toolbar-on-top');
+ // Append to the DOM
+ el.insertBefore(toolbar, el.firstChild);
+ } else {
+ // Add padding to the editor
+ obj.editor.style.padding = '15px';
+ // Append to the DOM
+ el.appendChild(toolbar);
+ }
+
+ // Create toolbar
+ jSuites.toolbar(toolbar, toolbarOptions);
+
+ toolbar.addEventListener('click', function() {
+ obj.editor.focus();
+ })
+ }
+
+ // Upload file
+ obj.file = document.createElement('input');
+ obj.file.style.display = 'none';
+ obj.file.type = 'file';
+ obj.file.setAttribute('accept', 'image/*');
+ obj.file.onchange = function() {
+ obj.addFile(this.files);
+ }
+ el.appendChild(obj.file);
+
+ // Focus to the editor
+ if (obj.editor.innerHTML && obj.options.focus) {
+ jSuites.editor.setCursor(obj.editor, obj.options.focus == 'initial' ? true : false);
+ }
+
+ // Change method
+ el.change = obj.setData;
+
+ // Global generic value handler
+ el.val = function(val) {
+ if (val === undefined) {
+ // Data type
+ var o = el.getAttribute('data-html') === 'true' ? false : true;
+ return obj.getData(o);
+ } else {
+ obj.setData(val);
+ }
+ }
+
+ loadExtensions();
+
+ el.editor = obj;
+
+ // Onload
+ if (typeof(obj.options.onload) == 'function') {
+ obj.options.onload(el, obj, obj.editor);
+ }
+
+ 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.getDefaultToolbar = function(obj) {
+
+ var color = function(a,b,c) {
+ if (! c.color) {
+ var t = null;
+ var colorPicker = jSuites.color(c, {
+ onchange: function(o, v) {
+ if (c.k === 'color') {
+ document.execCommand('foreColor', false, v);
+ } else {
+ document.execCommand('backColor', false, v);
+ }
+ }
+ });
+ c.color.open();
+ }
+ }
+
+ var items = [];
+
+ items.push({
+ content: 'undo',
+ onclick: function() {
+ document.execCommand('undo');
+ }
+ });
+
+ items.push({
+ content: 'redo',
+ onclick: function() {
+ document.execCommand('redo');
+ }
+ });
+
+ items.push({
+ type: 'divisor'
+ });
+
+ if (obj.options.toolbarOnTop) {
+ items.push({
+ type: 'select',
+ width: '140px',
+ options: ['Default', 'Verdana', 'Arial', 'Courier New'],
+ render: function (e) {
+ return '' + e + '';
+ },
+ onchange: function (a,b,c,d,e) {
+ document.execCommand("fontName", false, d);
+ }
+ });
+
+ items.push({
+ type: 'select',
+ content: 'format_size',
+ options: ['x-small', 'small', 'medium', 'large', 'x-large'],
+ render: function (e) {
+ return '' + e + '';
+ },
+ onchange: function (a,b,c,d,e) {
+ //var html = `${text}`;
+ //document.execCommand('insertHtml', false, html);
+ document.execCommand("fontSize", false, parseInt(e)+1);
+ //var f = window.getSelection().anchorNode.parentNode
+ //f.removeAttribute("size");
+ //f.style.fontSize = d;
+ }
+ });
+
+ items.push({
+ type: 'select',
+ options: ['format_align_left', 'format_align_center', 'format_align_right', 'format_align_justify'],
+ render: function (e) {
+ return '' + e + '';
+ },
+ onchange: function (a,b,c,d,e) {
+ var options = ['JustifyLeft','justifyCenter','justifyRight','justifyFull'];
+ document.execCommand(options[e]);
+ }
+ });
+
+ items.push({
+ type: 'divisor'
+ });
+
+ items.push({
+ content: 'format_color_text',
+ k: 'color',
+ onclick: color,
+ });
+
+ items.push({
+ content: 'format_color_fill',
+ k: 'background-color',
+ onclick: color,
+ });
+ }
+
+ items.push({
+ content: 'format_bold',
+ onclick: function(a,b,c) {
+ document.execCommand('bold');
+
+ if (document.queryCommandState("bold")) {
+ c.classList.add('selected');
+ } else {
+ c.classList.remove('selected');
+ }
+ }
+ });
+
+ items.push({
+ content: 'format_italic',
+ onclick: function(a,b,c) {
+ document.execCommand('italic');
+
+ if (document.queryCommandState("italic")) {
+ c.classList.add('selected');
+ } else {
+ c.classList.remove('selected');
+ }
+ }
+ });
+
+ items.push({
+ content: 'format_underline',
+ onclick: function(a,b,c) {
+ document.execCommand('underline');
+
+ if (document.queryCommandState("underline")) {
+ c.classList.add('selected');
+ } else {
+ c.classList.remove('selected');
+ }
+ }
+ });
+
+ items.push({
+ type:'divisor'
+ });
+
+ items.push({
+ content: 'format_list_bulleted',
+ onclick: function(a,b,c) {
+ document.execCommand('insertUnorderedList');
+
+ if (document.queryCommandState("insertUnorderedList")) {
+ c.classList.add('selected');
+ } else {
+ c.classList.remove('selected');
+ }
+ }
+ });
+
+ items.push({
+ content: 'format_list_numbered',
+ onclick: function(a,b,c) {
+ document.execCommand('insertOrderedList');
+
+ if (document.queryCommandState("insertOrderedList")) {
+ c.classList.add('selected');
+ } else {
+ c.classList.remove('selected');
+ }
+ }
+ });
+
+ items.push({
+ content: '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');
+ }
+ }
+ });
+
+ items.push({
+ content: 'format_indent_decrease',
+ onclick: function() {
+ document.execCommand('outdent');
+
+ if (document.queryCommandState("outdent")) {
+ this.classList.add('selected');
+ } else {
+ this.classList.remove('selected');
+ }
+ }
+ });
+
+ if (obj.options.toolbarOnTop) {
+ items.push({
+ type: 'divisor'
+ });
+
+ items.push({
+ content: 'photo',
+ onclick: function () {
+ obj.upload();
+ }
+ });
+
+ items.push({
+ type: 'select',
+ content: 'table_view',
+ columns: 10,
+ right: true,
+ options: [
+ '0x0', '1x0', '2x0', '3x0', '4x0', '5x0', '6x0', '7x0', '8x0', '9x0',
+ '0x1', '1x1', '2x1', '3x1', '4x1', '5x1', '6x1', '7x1', '8x1', '9x1',
+ '0x2', '1x2', '2x2', '3x2', '4x2', '5x2', '6x2', '7x2', '8x2', '9x2',
+ '0x3', '1x3', '2x3', '3x3', '4x3', '5x3', '6x3', '7x3', '8x3', '9x3',
+ '0x4', '1x4', '2x4', '3x4', '4x4', '5x4', '6x4', '7x4', '8x4', '9x4',
+ '0x5', '1x5', '2x5', '3x5', '4x5', '5x5', '6x5', '7x5', '8x5', '9x5',
+ '0x6', '1x6', '2x6', '3x6', '4x6', '5x6', '6x6', '7x6', '8x6', '9x6',
+ '0x7', '1x7', '2x7', '3x7', '4x7', '5x7', '6x7', '7x7', '8x7', '9x7',
+ '0x8', '1x8', '2x8', '3x8', '4x8', '5x8', '6x8', '7x8', '8x8', '9x8',
+ '0x9', '1x9', '2x9', '3x9', '4x9', '5x9', '6x9', '7x9', '8x9', '9x9',
+ ],
+ render: function (e, item) {
+ if (item) {
+ item.onmouseover = this.onmouseover;
+ e = e.split('x');
+ item.setAttribute('data-x', e[0]);
+ item.setAttribute('data-y', e[1]);
+ }
+ var element = document.createElement('div');
+ item.style.margin = '1px';
+ item.style.border = '1px solid #ddd';
+ return element;
+ },
+ onmouseover: function (e) {
+ var x = parseInt(e.target.getAttribute('data-x'));
+ var y = parseInt(e.target.getAttribute('data-y'));
+ for (var i = 0; i < e.target.parentNode.children.length; i++) {
+ var element = e.target.parentNode.children[i];
+ var ex = parseInt(element.getAttribute('data-x'));
+ var ey = parseInt(element.getAttribute('data-y'));
+ if (ex <= x && ey <= y) {
+ element.style.backgroundColor = '#cae1fc';
+ element.style.borderColor = '#2977ff';
+ } else {
+ element.style.backgroundColor = '';
+ element.style.borderColor = '#ddd';
+ }
+ }
+ },
+ onchange: function (a, b, c) {
+ c = c.split('x');
+ var table = document.createElement('table');
+ var tbody = document.createElement('tbody');
+ for (var y = 0; y <= c[1]; y++) {
+ var tr = document.createElement('tr');
+ for (var x = 0; x <= c[0]; x++) {
+ var td = document.createElement('td');
+ td.innerHTML = '';
+ tr.appendChild(td);
+ }
+ tbody.appendChild(tr);
+ }
+ table.appendChild(tbody);
+ table.setAttribute('width', '100%');
+ table.setAttribute('cellpadding', '6');
+ table.setAttribute('cellspacing', '0');
+ document.execCommand('insertHTML', false, table.outerHTML);
+ }
+ });
+ }
+
+ return items;
+}
+
+
+jSuites.form = (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,
+ validations: null,
+ onbeforeload: null,
+ onload: null,
+ onbeforesave: null,
+ onsave: null,
+ onbeforeremove: null,
+ onremove: null,
+ onerror: function(el, message) {
+ jSuites.alert(message);
+ }
+ };
+
+ // 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];
+ }
+ }
+
+ // Validations
+ if (! obj.options.validations) {
+ obj.options.validations = {};
+ }
+
+ // Submit Button
+ if (! obj.options.submitButton) {
+ obj.options.submitButton = el.querySelector('input[type=submit]');
+ }
+
+ if (obj.options.submitButton && obj.options.url) {
+ obj.options.submitButton.onclick = function() {
+ obj.save();
+ }
+ }
+
+ if (! obj.options.validations.email) {
+ obj.options.validations.email = jSuites.validations.email;
+ }
+
+ if (! obj.options.validations.length) {
+ obj.options.validations.length = jSuites.validations.length;
+ }
+
+ if (! obj.options.validations.required) {
+ obj.options.validations.required = jSuites.validations.required;
+ }
+
+ obj.setUrl = function(url) {
+ obj.options.url = url;
+ }
+
+ obj.load = function() {
+ jSuites.ajax({
+ url: obj.options.url,
+ method: 'GET',
+ dataType: 'json',
+ queue: true,
+ success: function(data) {
+ // Overwrite values from the backend
+ if (typeof(obj.options.onbeforeload) == 'function') {
+ var ret = obj.options.onbeforeload(el, data);
+ if (ret) {
+ data = ret;
+ }
+ }
+ // Apply values to the form
+ jSuites.form.setElements(el, data);
+ // Onload methods
+ if (typeof(obj.options.onload) == 'function') {
+ obj.options.onload(el, data);
+ }
+ }
+ });
+ }
+
+ obj.save = function() {
+ var test = obj.validate();
+
+ if (test) {
+ obj.options.onerror(el, test);
+ } else {
+ var data = jSuites.form.getElements(el, true);
+
+ if (typeof(obj.options.onbeforesave) == 'function') {
+ var data = obj.options.onbeforesave(el, data);
+
+ if (data === false) {
+ return;
+ }
+ }
+
+ jSuites.ajax({
+ url: obj.options.url,
+ method: 'POST',
+ dataType: 'json',
+ data: data,
+ success: function(result) {
+ if (typeof(obj.options.onsave) == 'function') {
+ obj.options.onsave(el, data, result);
+ }
+ }
+ });
+ }
+ }
+
+ obj.remove = function() {
+ if (typeof(obj.options.onbeforeremove) == 'function') {
+ var ret = obj.options.onbeforeremove(el, obj);
+ if (ret === false) {
+ return false;
+ }
+ }
+
+ jSuites.ajax({
+ url: obj.options.url,
+ method: 'DELETE',
+ dataType: 'json',
+ success: function(result) {
+ if (typeof(obj.options.onremove) == 'function') {
+ obj.options.onremove(el, obj, result);
+ }
+
+ obj.reset();
+ }
+ });
+ }
+
+ 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
+ var error = element.getAttribute('data-error') || 'There is an error in the form';
+ element.setAttribute('title', error);
+ return error;
+ }
+
+ var delError = function(element) {
+ var error = false;
+ // Remove class from this element
+ element.classList.remove('error');
+ element.removeAttribute('title');
+ // Get elements in the form
+ var elements = el.querySelectorAll("input, select, textarea, div[name]");
+ // 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');
+ }
+ }
+ }
+
+ obj.validateElement = function(element) {
+ // Test results
+ var test = false;
+ // Value
+ var value = jSuites.form.getValue(element);
+ // Validation
+ var validation = element.getAttribute('data-validation');
+ // Parse
+ if (typeof(obj.options.validations[validation]) == 'function' && ! obj.options.validations[validation](value, element)) {
+ // Not passed in the test
+ test = addError(element);
+ } else {
+ if (element.classList.contains('error')) {
+ delError(element);
+ }
+ }
+
+ return test;
+ }
+
+ obj.reset = function() {
+ // Get elements in the form
+ var name = null;
+ var elements = el.querySelectorAll("input, select, textarea, div[name]");
+ // Run all elements
+ for (var i = 0; i < elements.length; i++) {
+ if (name = elements[i].getAttribute('name')) {
+ if (elements[i].type == 'checkbox' || elements[i].type == 'radio') {
+ elements[i].checked = false;
+ } else {
+ if (typeof(elements[i].val) == 'function') {
+ elements[i].val('');
+ } else {
+ elements[i].value = '';
+ }
+ }
+ }
+ }
+ }
+
+ // Run form validation
+ obj.validate = function() {
+ var test = [];
+ // Get elements in the form
+ var elements = el.querySelectorAll("input, select, textarea, div[name]");
+ // Run all elements
+ for (var i = 0; i < elements.length; i++) {
+ // Required
+ if (elements[i].getAttribute('data-validation')) {
+ var res = obj.validateElement(elements[i]);
+ if (res) {
+ test.push(res);
+ }
+ }
+ }
+ if (test.length > 0) {
+ return test.join('
');
+ } else {
+ return false;
+ }
+ }
+
+ // Check the form
+ obj.getError = function() {
+ // Validation
+ return obj.validation() ? true : false;
+ }
+
+ // Return the form hash
+ obj.setHash = function() {
+ return obj.getHash(jSuites.form.getElements(el));
+ }
+
+ // 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;
+ }
+
+ // Ignore flag
+ obj.setIgnore = function(ignoreFlag) {
+ obj.options.ignore = ignoreFlag ? true : false;
+ }
+
+ // Start tracking in one second
+ setTimeout(function() {
+ obj.options.currentHash = obj.setHash();
+ }, 1000);
+
+ // Validations
+ el.addEventListener("keyup", function(e) {
+ if (e.target.getAttribute('data-validation')) {
+ obj.validateElement(e.target);
+ }
+ });
+
+ // Alert
+ if (! jSuites.form.hasEvents) {
+ 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);
+ }
+ }
+ });
+
+ jSuites.form.hasEvents = true;
+ }
+
+ el.form = obj;
+
+ return obj;
+});
+
+// Get value from one element
+jSuites.form.getValue = function(element) {
+ var value = null;
+ if (element.type == 'checkbox') {
+ if (element.checked == true) {
+ value = element.value || true;
+ }
+ } else if (element.type == 'radio') {
+ if (element.checked == true) {
+ value = element.value;
+ }
+ } else if (element.type == 'file') {
+ value = element.files;
+ } else if (element.tagName == 'select' && element.multiple == true) {
+ value = [];
+ var options = element.querySelectorAll("options[selected]");
+ for (var j = 0; j < options.length; j++) {
+ value.push(options[j].value);
+ }
+ } else if (typeof(element.val) == 'function') {
+ value = element.val();
+ } else {
+ value = element.value || '';
+ }
+
+ return value;
+}
+
+// Get form elements
+jSuites.form.getElements = function(el, asArray) {
+ var data = {};
+ var name = null;
+ var elements = el.querySelectorAll("input, select, textarea, div[name]");
+
+ for (var i = 0; i < elements.length; i++) {
+ if (name = elements[i].getAttribute('name')) {
+ data[name] = jSuites.form.getValue(elements[i]) || '';
+ }
+ }
+
+ return asArray == true ? data : JSON.stringify(data);
+}
+
+//Get form elements
+jSuites.form.setElements = function(el, data) {
+ var name = null;
+ var value = null;
+ var elements = el.querySelectorAll("input, select, textarea, div[name]");
+ for (var i = 0; i < elements.length; i++) {
+ // Attributes
+ var type = elements[i].getAttribute('type');
+ if (name = elements[i].getAttribute('name')) {
+ // Transform variable names in pathname
+ name = name.replace(new RegExp(/\[(.*?)\]/ig), '.$1');
+ value = null;
+ // Seach for the data in the path
+ if (name.match(/\./)) {
+ var tmp = jSuites.path.call(data, name) || '';
+ if (typeof(tmp) !== 'undefined') {
+ value = tmp;
+ }
+ } else {
+ if (typeof(data[name]) !== 'undefined') {
+ value = data[name];
+ }
+ }
+ // Set the values
+ if (value !== null) {
+ if (type == 'checkbox' || type == 'radio') {
+ elements[i].checked = value ? true : false;
+ } else if (type == 'file') {
+ // Do nothing
+ } else {
+ if (typeof (elements[i].val) == 'function') {
+ elements[i].val(value);
+ } else {
+ elements[i].value = value;
+ }
+ }
+ }
+ }
+ }
+}
+
+// Legacy
+jSuites.tracker = jSuites.form;
+
+jSuites.focus = function(el) {
+ if (el.innerText.length) {
+ var range = document.createRange();
+ var sel = window.getSelection();
+ var node = el.childNodes[el.childNodes.length-1];
+ range.setStart(node, node.length)
+ range.collapse(true)
+ sel.removeAllRanges()
+ sel.addRange(range)
+ el.scrollLeft = el.scrollWidth;
+ }
+}
+
+jSuites.isNumeric = (function (num) {
+ if (typeof(num) === 'string') {
+ num = num.trim();
+ }
+ return !isNaN(num) && num !== null && num !== '';
+});
+
+jSuites.guid = function() {
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
+ var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
+ return v.toString(16);
+ });
+}
+
+jSuites.getNode = function() {
+ var node = document.getSelection().anchorNode;
+ if (node) {
+ return (node.nodeType == 3 ? node.parentNode : node);
+ } else {
+ return null;
+ }
+}
+/**
+ * Generate hash from a string
+ */
+jSuites.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);
+ if (chr > 32) {
+ hash = ((hash << 5) - hash) + chr;
+ hash |= 0;
+ }
+ }
+ }
+ return hash;
+}
+
+/**
+ * Generate a random color
+ */
+jSuites.randomColor = function(h) {
+ var lum = -0.25;
+ var hex = String('#' + Math.random().toString(16).slice(2, 8).toUpperCase()).replace(/[^0-9a-f]/gi, '');
+ if (hex.length < 6) {
+ hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
+ }
+ var rgb = [], c, i;
+ for (i = 0; i < 3; i++) {
+ c = parseInt(hex.substr(i * 2, 2), 16);
+ c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
+ rgb.push(("00" + c).substr(c.length));
+ }
+
+ // Return hex
+ if (h == true) {
+ return '#' + jSuites.two(rgb[0].toString(16)) + jSuites.two(rgb[1].toString(16)) + jSuites.two(rgb[2].toString(16));
+ }
+
+ return rgb;
+}
+
+jSuites.getWindowWidth = function() {
+ var w = window,
+ d = document,
+ e = d.documentElement,
+ g = d.getElementsByTagName('body')[0],
+ x = w.innerWidth || e.clientWidth || g.clientWidth;
+ return x;
+}
+
+jSuites.getWindowHeight = function() {
+ var w = window,
+ d = document,
+ e = d.documentElement,
+ g = d.getElementsByTagName('body')[0],
+ y = w.innerHeight|| e.clientHeight|| g.clientHeight;
+ return y;
+}
+
+jSuites.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 ];
+}
+
+jSuites.click = function(el) {
+ if (el.click) {
+ el.click();
+ } else {
+ var evt = new MouseEvent('click', {
+ bubbles: true,
+ cancelable: true,
+ view: window
+ });
+ el.dispatchEvent(evt);
+ }
+}
+
+jSuites.findElement = function(element, condition) {
+ var foundElement = false;
+
+ function path (element) {
+ if (element && ! foundElement) {
+ if (typeof(condition) == 'function') {
+ foundElement = condition(element)
+ } else if (typeof(condition) == 'string') {
+ if (element.classList && element.classList.contains(condition)) {
+ foundElement = element;
+ }
+ }
+ }
+
+ if (element.parentNode && ! foundElement) {
+ path(element.parentNode);
+ }
+ }
+
+ path(element);
+
+ return foundElement;
+}
+
+// Two digits
+jSuites.two = function(value) {
+ value = '' + value;
+ if (value.length == 1) {
+ value = '0' + value;
+ }
+ return value;
+}
+
+jSuites.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);
+});
+
+
+jSuites.image = jSuites.upload = (function(el, options) {
+ var obj = {};
+ obj.options = {};
+
+ // Default configuration
+ var defaults = {
+ type: 'image',
+ extension: '*',
+ input: false,
+ minWidth: false,
+ maxWidth: null,
+ maxHeight: null,
+ maxJpegSizeBytes: null, // For example, 350Kb would be 350000
+ onchange: null,
+ multiple: false,
+ remoteParser: null,
+ text:{
+ extensionNotAllowed:'The extension is not allowed',
+ }
+ };
+
+ // 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];
+ }
+ }
+
+ // Multiple
+ if (obj.options.multiple == true) {
+ el.setAttribute('data-multiple', true);
+ }
+
+ // Container
+ el.content = [];
+
+ // Upload icon
+ el.classList.add('jupload');
+
+ if (obj.options.input == true) {
+ el.classList.add('input');
+ }
+
+ obj.add = function(data) {
+ // Reset container for single files
+ if (obj.options.multiple == false) {
+ el.content = [];
+ el.innerText = '';
+ }
+
+ // Append to the element
+ if (obj.options.type == 'image') {
+ var img = document.createElement('img');
+ img.setAttribute('src', data.file);
+ img.setAttribute('tabindex', -1);
+ if (! el.getAttribute('name')) {
+ img.className = 'jfile';
+ img.content = data;
+ }
+ el.appendChild(img);
+ } else {
+ if (data.name) {
+ var name = data.name;
+ } else {
+ var name = data.file;
+ }
+ var div = document.createElement('div');
+ div.innerText = name || obj.options.type;
+ div.classList.add('jupload-item');
+ div.setAttribute('tabindex', -1);
+ el.appendChild(div);
+ }
+
+ if (data.content) {
+ data.file = jSuites.guid();
+ }
+
+ // Push content
+ el.content.push(data);
+
+ // Onchange
+ if (typeof(obj.options.onchange) == 'function') {
+ obj.options.onchange(el, data);
+ }
+ }
+
+ obj.addFromFile = function(file) {
+ var type = file.type.split('/');
+ if (type[0] == obj.options.type) {
+ var readFile = new FileReader();
+ readFile.addEventListener("load", function (v) {
+ var data = {
+ file: v.srcElement.result,
+ extension: file.name.substr(file.name.lastIndexOf('.') + 1),
+ name: file.name,
+ size: file.size,
+ lastmodified: file.lastModified,
+ content: v.srcElement.result,
+ }
+
+ obj.add(data);
+ });
+
+ readFile.readAsDataURL(file);
+ } else {
+ alert(obj.options.text.extensionNotAllowed);
+ }
+ }
+
+ 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('/');
+ var type = extension[0].replace('data:','');
+ if (type == obj.options.type) {
+ var data = {
+ file: src,
+ name: '',
+ extension: extension[1],
+ content: src,
+ }
+ obj.add(data);
+ } else {
+ alert(obj.options.text.extensionNotAllowed);
+ }
+ } else {
+ var extension = src.substr(src.lastIndexOf('.') + 1);
+ // Work for cross browsers
+ src = obj.options.remoteParser + src;
+ // Get remove content
+ jSuites.ajax({
+ url: src,
+ type: 'GET',
+ dataType: 'blob',
+ success: function(data) {
+ //add(extension[0].replace('data:',''), data);
+ }
+ })
+ }
+ }
+ }
+
+ var getDataURL = function(canvas, type) {
+ var compression = 0.92;
+ var lastContentLength = null;
+ var content = canvas.toDataURL(type, compression);
+ while (obj.options.maxJpegSizeBytes && type === 'image/jpeg' &&
+ content.length > obj.options.maxJpegSizeBytes && content.length !== lastContentLength) {
+ // Apply the compression
+ compression *= 0.9;
+ lastContentLength = content.length;
+ content = canvas.toDataURL(type, compression);
+ }
+ return content;
+ }
+
+ var mime = obj.options.type + '/' + obj.options.extension;
+ var input = document.createElement('input');
+ input.type = 'file';
+ input.setAttribute('accept', mime);
+ input.onchange = function() {
+ for (var i = 0; i < this.files.length; i++) {
+ obj.addFromFile(this.files[i]);
+ }
+ }
+
+ // Allow multiple files
+ if (obj.options.multiple == true) {
+ input.setAttribute('multiple', true);
+ }
+
+ var current = null;
+
+ el.addEventListener("click", function(e) {
+ current = null;
+ if (! el.children.length || e.target === el) {
+ jSuites.click(input);
+ } else {
+ if (e.target.parentNode == el) {
+ current = e.target;
+ }
+ }
+ });
+
+ el.addEventListener("dblclick", function(e) {
+ jSuites.click(input);
+ });
+
+ 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('keydown', function(e) {
+ if (current && e.which == 46) {
+ var index = Array.prototype.indexOf.call(el.children, current);
+ if (index >= 0) {
+ el.content.splice(index, 1);
+ current.remove();
+ current = null;
+ }
+ }
+ });
+
+ 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.multiple == false) {
+ el.innerText = '';
+ }
+
+ // 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.val = function(val) {
+ if (val === undefined) {
+ return el.content && el.content.length ? el.content : null;
+ } else {
+ // Reset
+ el.innerText = '';
+ el.content = [];
+
+ if (val) {
+ if (Array.isArray(val)) {
+ for (var i = 0; i < val.length; i++) {
+ if (typeof(val[i]) == 'string') {
+ obj.add({ file: val[i] });
+ } else {
+ obj.add(val[i]);
+ }
+ }
+ } else if (typeof(val) == 'string') {
+ obj.add({ file: val });
+ }
+ }
+ }
+ }
+
+ el.upload = el.image = obj;
+
+ return obj;
+});
+
+jSuites.image.create = function(data) {
+ var img = document.createElement('img');
+ img.setAttribute('src', data.file);
+ img.className = 'jfile';
+ img.setAttribute('tabindex', -1);
+ img.content = data;
+
+ return img;
+}
+
+
+jSuites.lazyLoading = (function(el, options) {
+ var obj = {}
+
+ // Mandatory options
+ if (! options.loadUp || typeof(options.loadUp) != 'function') {
+ options.loadUp = function() {
+ return false;
+ }
+ }
+ if (! options.loadDown || typeof(options.loadDown) != 'function') {
+ options.loadDown = function() {
+ return false;
+ }
+ }
+ // Timer ms
+ if (! options.timer) {
+ options.timer = 100;
+ }
+
+ // Timer
+ var timeControlLoading = null;
+
+ // Controls
+ var scrollControls = function(e) {
+ if (timeControlLoading == null) {
+ var event = false;
+ var scrollTop = el.scrollTop;
+ if (el.scrollTop + (el.clientHeight * 2) >= el.scrollHeight) {
+ if (options.loadDown()) {
+ if (scrollTop == el.scrollTop) {
+ el.scrollTop = el.scrollTop - (el.clientHeight);
+ }
+ event = true;
+ }
+ } else if (el.scrollTop <= el.clientHeight) {
+ if (options.loadUp()) {
+ if (scrollTop == el.scrollTop) {
+ el.scrollTop = el.scrollTop + (el.clientHeight);
+ }
+ event = true;
+ }
+ }
+
+ timeControlLoading = setTimeout(function() {
+ timeControlLoading = null;
+ }, options.timer);
+
+ if (event) {
+ if (typeof(options.onupdate) == 'function') {
+ options.onupdate();
+ }
+ }
+ }
+ }
+
+ // Onscroll
+ el.onscroll = function(e) {
+ scrollControls(e);
+ }
+
+ el.onwheel = function(e) {
+ scrollControls(e);
+ }
+
+ return obj;
+});
+
+jSuites.loading = (function() {
+ var obj = {};
+
+ var loading = null;
+
+ obj.show = function(timeout) {
+ if (! loading) {
+ loading = document.createElement('div');
+ loading.className = 'jloading';
+ }
+ document.body.appendChild(loading);
+
+ // Max timeout in seconds
+ if (timeout > 0) {
+ setTimeout(function() {
+ obj.hide();
+ }, timeout * 1000)
+ }
+ }
+
+ obj.hide = function() {
+ if (loading && loading.parentNode) {
+ document.body.removeChild(loading);
+ }
+ }
+
+ return obj;
+})();
+
+jSuites.mask = (function() {
+ // Currency
+ var tokens = {
+ // Text
+ text: [ '@' ],
+ // Currency tokens
+ currency: [ '#(.{1})##0?(.{1}0+)?( ?;(.*)?)?', '#' ],
+ // Percentage
+ percentage: [ '0{1}(.{1}0+)?%' ],
+ // Number
+ numeric: [ '0{1}(.{1}0+)?' ],
+ // Data tokens
+ datetime: [ 'YYYY', 'YYY', 'YY', 'MMMMM', 'MMMM', 'MMM', 'MM', 'DDDDD', 'DDDD', 'DDD', 'DD', 'DY', 'DAY', 'WD', 'D', 'Q', 'MONTH', 'MON', 'HH24', 'HH12', 'HH', '\\[H\\]', 'H', 'AM/PM', 'PM', 'AM', 'MI', 'SS', 'MS', 'Y', 'M' ],
+ // Other
+ general: [ 'A', '0', '[0-9a-zA-Z\$]+', '.']
+ }
+
+ var getDate = function() {
+ if (this.mask.toLowerCase().indexOf('[h]') !== -1) {
+ var m = 0;
+ if (this.date[4]) {
+ m = parseFloat(this.date[4] / 60);
+ }
+ var v = parseInt(this.date[3]) + m;
+ v /= 24;
+ } else if (! (this.date[0] && this.date[1] && this.date[2]) && (this.date[3] || this.date[4])) {
+ v = jSuites.two(this.date[3]) + ':' + jSuites.two(this.date[4]) + ':' + jSuites.two(this.date[5])
+ } else {
+ if (this.date[0] && this.date[1] && ! this.date[2]) {
+ this.date[2] = 1;
+ }
+ v = jSuites.two(this.date[0]) + '-' + jSuites.two(this.date[1]) + '-' + jSuites.two(this.date[2]);
+
+ if (this.date[3] || this.date[4] || this.date[5]) {
+ v += ' ' + jSuites.two(this.date[3]) + ':' + jSuites.two(this.date[4]) + ':' + jSuites.two(this.date[5]);
+ }
+ }
+
+ return v;
+ }
+
+ var extractDate = function() {
+ var v = '';
+ if (! (this.date[0] && this.date[1] && this.date[2]) && (this.date[3] || this.date[4])) {
+ if (this.mask.toLowerCase().indexOf('[h]') !== -1) {
+ v = parseInt(this.date[3]);
+ } else {
+ v = parseInt(this.date[3]) % 24;
+ }
+ if (this.date[4]) {
+ v += parseFloat(this.date[4] / 60);
+ }
+ v /= 24;
+ } else if (this.date[0] || this.date[1] || this.date[2] || this.date[3] || this.date[4] || this.date[5]) {
+ if (this.date[0] && this.date[1] && ! this.date[2]) {
+ this.date[2] = 1;
+ }
+ var t = jSuites.calendar.now(this.date);
+ v = jSuites.calendar.dateToNum(t);
+ if (this.date[4]) {
+ v += parseFloat(this.date[4] / 60);
+ }
+ }
+ return v;
+ }
+
+ var isBlank = function(v) {
+ return v === null || v === '' || v === undefined ? true : false;
+ }
+
+ var isFormula = function(value) {
+ return (''+value).chartAt(0) == '=';
+ }
+
+ var isNumeric = function(t) {
+ return t === 'currency' || t === 'percentage' || t === 'numeric' ? true : false;
+ }
+ /**
+ * Get the decimal defined in the mask configuration
+ */
+ var getDecimal = function(v) {
+ if (v && Number(v) == v) {
+ return '.';
+ } else {
+ if (this.options.decimal) {
+ return this.options.decimal;
+ } else {
+ if (this.locale) {
+ var t = Intl.NumberFormat(this.locale).format(1.1);
+ return this.options.decimal = t[1];
+ } else {
+ if (! v) {
+ v = this.mask;
+ }
+ var e = new RegExp('0{1}(.{1})0+', 'ig');
+ var t = e.exec(v);
+ if (t && t[1] && t[1].length == 1) {
+ // Save decimal
+ this.options.decimal = t[1];
+ // Return decimal
+ return t[1];
+ } else {
+ // Did not find any decimal last resort the default
+ var e = new RegExp('#{1}(.{1})#+', 'ig');
+ var t = e.exec(v);
+ if (t && t[1] && t[1].length == 1) {
+ if (t[1] === ',') {
+ this.options.decimal = '.';
+ } else {
+ this.options.decimal = ',';
+ }
+ } else {
+ this.options.decimal = '1.1'.toLocaleString().substring(1,2);
+ }
+ }
+ }
+ }
+ }
+
+ if (this.options.decimal) {
+ return this.options.decimal;
+ } else {
+ return null;
+ }
+ }
+
+ var ParseValue = function(v, decimal) {
+ if (v == '') {
+ return '';
+ }
+
+ // Get decimal
+ if (! decimal) {
+ decimal = getDecimal.call(this);
+ }
+
+ // New value
+ v = (''+v).split(decimal);
+
+ // Signal
+ var signal = v[0].match(/[-]+/g);
+ if (signal && signal.length) {
+ signal = true;
+ } else {
+ signal = false;
+ }
+
+ v[0] = v[0].match(/[0-9]+/g);
+
+ if (v[0]) {
+ if (signal) {
+ v[0].unshift('-');
+ }
+ v[0] = v[0].join('');
+ } else {
+ if (signal) {
+ v[0] = '-';
+ }
+ }
+
+ if (v[0] || v[1]) {
+ if (v[1] !== undefined) {
+ v[1] = v[1].match(/[0-9]+/g);
+ if (v[1]) {
+ v[1] = v[1].join('');
+ } else {
+ v[1] = '';
+ }
+ }
+ } else {
+ return '';
+ }
+ return v;
+ }
+
+ var FormatValue = function(v, event) {
+ if (v == '') {
+ return '';
+ }
+ // Get decimal
+ var d = getDecimal.call(this);
+ // Convert value
+ var o = this.options;
+ // Parse value
+ v = ParseValue.call(this, v);
+ if (v == '') {
+ return '';
+ }
+ // Temporary value
+ if (v[0]) {
+ var t = parseFloat(v[0] + '.1');
+ if (o.style == 'percent') {
+ t /= 100;
+ }
+ } else {
+ var t = null;
+ }
+
+ if ((v[0] == '-' || v[0] == '-00') && ! v[1] && (event && event.inputType == "deleteContentBackward")) {
+ return '';
+ }
+
+ var n = new Intl.NumberFormat(this.locale, o).format(t);
+ n = n.split(d);
+ if (typeof(n[1]) !== 'undefined') {
+ var s = n[1].replace(/[0-9]*/g, '');
+ if (s) {
+ n[2] = s;
+ }
+ }
+
+ if (v[1] !== undefined) {
+ n[1] = d + v[1];
+ } else {
+ n[1] = '';
+ }
+
+ return n.join('');
+ }
+
+ var Format = function(e, event) {
+ var v = Value.call(e);
+ if (! v) {
+ return;
+ }
+
+ // Get decimal
+ var d = getDecimal.call(this);
+ var n = FormatValue.call(this, v, event);
+ var t = (n.length) - v.length;
+ var index = Caret.call(e) + t;
+ // Set value and update caret
+ Value.call(e, n, index, true);
+ }
+
+ var Extract = function(v) {
+ // Keep the raw value
+ var current = ParseValue.call(this, v);
+ if (current) {
+ // Negative values
+ if (current[0] === '-') {
+ current[0] = '-0';
+ }
+ return parseFloat(current.join('.'));
+ }
+ return null;
+ }
+
+ /**
+ * Caret getter and setter methods
+ */
+ var Caret = function(index, adjustNumeric) {
+ if (index === undefined) {
+ if (this.tagName == 'DIV') {
+ var pos = 0;
+ var s = window.getSelection();
+ if (s) {
+ if (s.rangeCount !== 0) {
+ var r = s.getRangeAt(0);
+ var p = r.cloneRange();
+ p.selectNodeContents(this);
+ p.setEnd(r.endContainer, r.endOffset);
+ pos = p.toString().length;
+ }
+ }
+ return pos;
+ } else {
+ return this.selectionStart;
+ }
+ } else {
+ // Get the current value
+ var n = Value.call(this);
+
+ // Review the position
+ if (adjustNumeric) {
+ var p = null;
+ for (var i = 0; i < n.length; i++) {
+ if (n[i].match(/[\-0-9]/g) || n[i] == '.' || n[i] == ',') {
+ p = i;
+ }
+ }
+
+ // If the string has no numbers
+ if (p === null) {
+ p = n.indexOf(' ');
+ }
+
+ if (index >= p) {
+ index = p + 1;
+ }
+ }
+
+ // Do not update caret
+ if (index > n.length) {
+ index = n.length;
+ }
+
+ if (index) {
+ // Set caret
+ if (this.tagName == 'DIV') {
+ var s = window.getSelection();
+ var r = document.createRange();
+
+ if (this.childNodes[0]) {
+ r.setStart(this.childNodes[0], index);
+ s.removeAllRanges();
+ s.addRange(r);
+ }
+ } else {
+ this.selectionStart = index;
+ this.selectionEnd = index;
+ }
+ }
+ }
+ }
+
+ /**
+ * Value getter and setter method
+ */
+ var Value = function(v, updateCaret, adjustNumeric) {
+ if (this.tagName == 'DIV') {
+ if (v === undefined) {
+ var v = this.innerText;
+ if (this.value && this.value.length > v.length) {
+ v = this.value;
+ }
+ return v;
+ } else {
+ if (this.innerText !== v) {
+ this.innerText = v;
+
+ if (updateCaret) {
+ Caret.call(this, updateCaret, adjustNumeric);
+ }
+ }
+ }
+ } else {
+ if (v === undefined) {
+ return this.value;
+ } else {
+ if (this.value !== v) {
+ this.value = v;
+ if (updateCaret) {
+ Caret.call(this, updateCaret, adjustNumeric);
+ }
+ }
+ }
+ }
+ }
+
+ // Labels
+ var weekDaysFull = jSuites.calendar.weekdays;
+ var weekDays = jSuites.calendar.weekdaysShort;
+ var monthsFull = jSuites.calendar.months;
+ var months = jSuites.calendar.monthsShort;
+
+ var parser = {
+ 'YEAR': function(v, s) {
+ var y = ''+new Date().getFullYear();
+
+ if (typeof(this.values[this.index]) === 'undefined') {
+ this.values[this.index] = '';
+ }
+ if (parseInt(v) >= 0 && parseInt(v) <= 10) {
+ if (this.values[this.index].length < s) {
+ this.values[this.index] += v;
+ }
+ }
+ if (this.values[this.index].length == s) {
+ if (s == 2) {
+ var y = y.substr(0,2) + this.values[this.index];
+ } else if (s == 3) {
+ var y = y.substr(0,1) + this.values[this.index];
+ } else if (s == 4) {
+ var y = this.values[this.index];
+ }
+ this.date[0] = y;
+ this.index++;
+ }
+ },
+ 'YYYY': function(v) {
+ parser.YEAR.call(this, v, 4);
+ },
+ 'YYY': function(v) {
+ parser.YEAR.call(this, v, 3);
+ },
+ 'YY': function(v) {
+ parser.YEAR.call(this, v, 2);
+ },
+ 'FIND': function(v, a) {
+ if (isBlank(this.values[this.index])) {
+ this.values[this.index] = '';
+ }
+ if (this.event && this.event.inputType && this.event.inputType.indexOf('delete') > -1) {
+ this.values[this.index] += v;
+ return;
+ }
+ var pos = 0;
+ var count = 0;
+ var value = (this.values[this.index] + v).toLowerCase();
+ for (var i = 0; i < a.length; i++) {
+ if (a[i].toLowerCase().indexOf(value) == 0) {
+ pos = i;
+ count++;
+ }
+ }
+ if (count > 1) {
+ this.values[this.index] += v;
+ } else if (count == 1) {
+ // Jump number of chars
+ var t = (a[pos].length - this.values[this.index].length) - 1;
+ this.position += t;
+
+ this.values[this.index] = a[pos];
+ this.index++;
+ return pos;
+ }
+ },
+ 'MMM': function(v) {
+ var ret = parser.FIND.call(this, v, months);
+ if (ret !== undefined) {
+ this.date[1] = ret + 1;
+ }
+ },
+ 'MON': function(v) {
+ parser['MMM'].call(this, v);
+ },
+ 'MMMM': function(v) {
+ var ret = parser.FIND.call(this, v, monthsFull);
+ if (ret !== undefined) {
+ this.date[1] = ret + 1;
+ }
+ },
+ 'MONTH': function(v) {
+ parser['MMMM'].call(this, v);
+ },
+ 'MMMMM': function(v) {
+ if (isBlank(this.values[this.index])) {
+ this.values[this.index] = '';
+ }
+ var pos = 0;
+ var count = 0;
+ var value = (this.values[this.index] + v).toLowerCase();
+ for (var i = 0; i < monthsFull.length; i++) {
+ if (monthsFull[i][0].toLowerCase().indexOf(value) == 0) {
+ this.values[this.index] = monthsFull[i][0];
+ this.date[1] = i + 1;
+ this.index++;
+ break;
+ }
+ }
+ },
+ 'MM': function(v) {
+ if (isBlank(this.values[this.index])) {
+ if (parseInt(v) > 1 && parseInt(v) < 10) {
+ this.date[1] = this.values[this.index] = '0' + v;
+ this.index++;
+ } else if (parseInt(v) < 2) {
+ this.values[this.index] = v;
+ }
+ } else {
+ if (this.values[this.index] == 1 && parseInt(v) < 3) {
+ this.date[1] = this.values[this.index] += v;
+ this.index++;
+ } else if (this.values[this.index] == 0 && parseInt(v) > 0 && parseInt(v) < 10) {
+ this.date[1] = this.values[this.index] += v;
+ this.index++;
+ }
+ }
+ },
+ 'M': function(v) {
+ var test = false;
+ if (parseInt(v) >= 0 && parseInt(v) < 10) {
+ if (isBlank(this.values[this.index])) {
+ this.values[this.index] = v;
+ if (v > 1) {
+ this.date[1] = this.values[this.index];
+ this.index++;
+ }
+ } else {
+ if (this.values[this.index] == 1 && parseInt(v) < 3) {
+ this.date[1] = this.values[this.index] += v;
+ this.index++;
+ } else if (this.values[this.index] == 0 && parseInt(v) > 0) {
+ this.date[1] = this.values[this.index] += v;
+ this.index++;
+ } else {
+ var test = true;
+ }
+ }
+ } else {
+ var test = true;
+ }
+
+ // Re-test
+ if (test == true) {
+ var t = parseInt(this.values[this.index]);
+ if (t > 0 && t < 12) {
+ this.date[2] = this.values[this.index];
+ this.index++;
+ // Repeat the character
+ this.position--;
+ }
+ }
+ },
+ 'D': function(v) {
+ var test = false;
+ if (parseInt(v) >= 0 && parseInt(v) < 10) {
+ if (isBlank(this.values[this.index])) {
+ this.values[this.index] = v;
+ if (parseInt(v) > 3) {
+ this.date[2] = this.values[this.index];
+ this.index++;
+ }
+ } else {
+ if (this.values[this.index] == 3 && parseInt(v) < 2) {
+ this.date[2] = this.values[this.index] += v;
+ this.index++;
+ } else if (this.values[this.index] == 1 || this.values[this.index] == 2) {
+ this.date[2] = this.values[this.index] += v;
+ this.index++;
+ } else if (this.values[this.index] == 0 && parseInt(v) > 0) {
+ this.date[2] = this.values[this.index] += v;
+ this.index++;
+ } else {
+ var test = true;
+ }
+ }
+ } else {
+ var test = true;
+ }
+
+ // Re-test
+ if (test == true) {
+ var t = parseInt(this.values[this.index]);
+ if (t > 0 && t < 32) {
+ this.date[2] = this.values[this.index];
+ this.index++;
+ // Repeat the character
+ this.position--;
+ }
+ }
+ },
+ 'DD': function(v) {
+ if (isBlank(this.values[this.index])) {
+ if (parseInt(v) > 3 && parseInt(v) < 10) {
+ this.date[2] = this.values[this.index] = '0' + v;
+ this.index++;
+ } else if (parseInt(v) < 10) {
+ this.values[this.index] = v;
+ }
+ } else {
+ if (this.values[this.index] == 3 && parseInt(v) < 2) {
+ this.date[2] = this.values[this.index] += v;
+ this.index++;
+ } else if ((this.values[this.index] == 1 || this.values[this.index] == 2) && parseInt(v) < 10) {
+ this.date[2] = this.values[this.index] += v;
+ this.index++;
+ } else if (this.values[this.index] == 0 && parseInt(v) > 0 && parseInt(v) < 10) {
+ this.date[2] = this.values[this.index] += v;
+ this.index++;
+ }
+ }
+ },
+ 'DDD': function(v) {
+ parser.FIND.call(this, v, weekDays);
+ },
+ 'DY': function(v) {
+ parser['DDD'].call(this, v);
+ },
+ 'DDDD': function(v) {
+ parser.FIND.call(this, v, weekDaysFull);
+ },
+ 'DAY': function(v) {
+ parser['DDDD'].call(this, v);
+ },
+ 'HH12': function(v, two) {
+ if (isBlank(this.values[this.index])) {
+ if (parseInt(v) > 1 && parseInt(v) < 10) {
+ if (two) {
+ v = 0 + v;
+ }
+ this.date[3] = this.values[this.index] = v;
+ this.index++;
+ } else if (parseInt(v) < 10) {
+ this.values[this.index] = v;
+ }
+ } else {
+ if (this.values[this.index] == 1 && parseInt(v) < 3) {
+ this.date[3] = this.values[this.index] += v;
+ this.index++;
+ } else if (this.values[this.index] < 1 && parseInt(v) < 10) {
+ this.date[3] = this.values[this.index] += v;
+ this.index++;
+ }
+ }
+ },
+ 'HH24': function(v, two) {
+ var test = false;
+ if (parseInt(v) >= 0 && parseInt(v) < 10) {
+ if (this.values[this.index] == null || this.values[this.index] == '') {
+ if (parseInt(v) > 2 && parseInt(v) < 10) {
+ if (two) {
+ v = 0 + v;
+ }
+ this.date[3] = this.values[this.index] = v;
+ this.index++;
+ } else if (parseInt(v) < 10) {
+ this.values[this.index] = v;
+ }
+ } else {
+ if (this.values[this.index] == 2 && parseInt(v) < 4) {
+ this.date[3] = this.values[this.index] += v;
+ this.index++;
+ } else if (this.values[this.index] < 2 && parseInt(v) < 10) {
+ this.date[3] = this.values[this.index] += v;
+ this.index++;
+ }
+ }
+ }
+ },
+ 'HH': function(v) {
+ parser['HH24'].call(this, v, 1);
+ },
+ 'H': function(v) {
+ parser['HH24'].call(this, v, 0);
+ },
+ '\\[H\\]': function(v) {
+ if (this.values[this.index] == undefined) {
+ this.values[this.index] = '';
+ }
+ if (v.match(/[0-9]/g)) {
+ this.date[3] = this.values[this.index] += v;
+ } else {
+ if (this.values[this.index].match(/[0-9]/g)) {
+ this.date[3] = this.values[this.index];
+ this.index++;
+ // Repeat the character
+ this.position--;
+ }
+ }
+ },
+ 'N60': function(v, i) {
+ if (this.values[this.index] == null || this.values[this.index] == '') {
+ if (parseInt(v) > 5 && parseInt(v) < 10) {
+ this.date[i] = this.values[this.index] = '0' + v;
+ this.index++;
+ } else if (parseInt(v) < 10) {
+ this.values[this.index] = v;
+ }
+ } else {
+ if (parseInt(v) < 10) {
+ this.date[i] = this.values[this.index] += v;
+ this.index++;
+ }
+ }
+ },
+ 'MI': function(v) {
+ parser.N60.call(this, v, 4);
+ },
+ 'SS': function(v) {
+ parser.N60.call(this, v, 5);
+ },
+ 'AM/PM': function(v) {
+ this.values[this.index] = '';
+ if (v) {
+ if (this.date[3] > 12) {
+ this.values[this.index] = 'PM';
+ } else {
+ this.values[this.index] = 'AM';
+ }
+ }
+ this.index++;
+ },
+ 'WD': function(v) {
+ if (typeof(this.values[this.index]) === 'undefined') {
+ this.values[this.index] = '';
+ }
+ if (parseInt(v) >= 0 && parseInt(v) < 7) {
+ this.values[this.index] = v;
+ }
+ if (this.value[this.index].length == 1) {
+ this.index++;
+ }
+ },
+ '0{1}(.{1}0+)?': function(v) {
+ // Get decimal
+ var decimal = getDecimal.call(this);
+ // Negative number
+ var neg = false;
+ // Create if is blank
+ if (isBlank(this.values[this.index])) {
+ this.values[this.index] = '';
+ } else {
+ if (this.values[this.index] == '-') {
+ neg = true;
+ }
+ }
+ var current = ParseValue.call(this, this.values[this.index], decimal);
+ if (current) {
+ this.values[this.index] = current.join(decimal);
+ }
+ // New entry
+ if (parseInt(v) >= 0 && parseInt(v) < 10) {
+ // Replace the zero for a number
+ if (this.values[this.index] == '0' && v > 0) {
+ this.values[this.index] = '';
+ } else if (this.values[this.index] == '-0' && v > 0) {
+ this.values[this.index] = '-';
+ }
+ // Don't add up zeros because does not mean anything here
+ if ((this.values[this.index] != '0' && this.values[this.index] != '-0') || v == decimal) {
+ this.values[this.index] += v;
+ }
+ } else if (decimal && v == decimal) {
+ if (this.values[this.index].indexOf(decimal) == -1) {
+ if (! this.values[this.index]) {
+ this.values[this.index] = '0';
+ }
+ this.values[this.index] += v;
+ }
+ } else if (v == '-') {
+ // Negative signed
+ neg = true;
+ }
+
+ if (neg === true && this.values[this.index][0] !== '-') {
+ this.values[this.index] = '-' + this.values[this.index];
+ }
+ },
+ '0{1}(.{1}0+)?%': function(v) {
+ parser['0{1}(.{1}0+)?'].call(this, v);
+
+ if (this.values[this.index].match(/[\-0-9]/g)) {
+ if (this.values[this.index] && this.values[this.index].indexOf('%') == -1) {
+ this.values[this.index] += '%';
+ }
+ } else {
+ this.values[this.index] = '';
+ }
+ },
+ '#(.{1})##0?(.{1}0+)?( ?;(.*)?)?': function(v) {
+ // Parse number
+ parser['0{1}(.{1}0+)?'].call(this, v);
+ // Get decimal
+ var decimal = getDecimal.call(this);
+ // Get separator
+ var separator = this.tokens[this.index].substr(1,1);
+ // Negative
+ var negative = this.values[this.index][0] === '-' ? true : false;
+ // Current value
+ var current = ParseValue.call(this, this.values[this.index], decimal);
+
+ // Get main and decimal parts
+ if (current !== '') {
+ // Format number
+ var n = current[0].match(/[0-9]/g);
+ if (n) {
+ // Format
+ n = n.join('');
+ var t = [];
+ var s = 0;
+ for (var j = n.length - 1; j >= 0 ; j--) {
+ t.push(n[j]);
+ s++;
+ if (! (s % 3)) {
+ t.push(separator);
+ }
+ }
+ t = t.reverse();
+ current[0] = t.join('');
+ if (current[0].substr(0,1) == separator) {
+ current[0] = current[0].substr(1);
+ }
+ } else {
+ current[0] = '';
+ }
+
+ // Value
+ this.values[this.index] = current.join(decimal);
+
+ // Negative
+ if (negative) {
+ this.values[this.index] = '-' + this.values[this.index];
+ }
+ }
+ },
+ '0': function(v) {
+ if (v.match(/[0-9]/g)) {
+ this.values[this.index] = v;
+ this.index++;
+ }
+ },
+ '[0-9a-zA-Z$]+': function(v) {
+ if (isBlank(this.values[this.index])) {
+ this.values[this.index] = '';
+ }
+ var t = this.tokens[this.index];
+ var s = this.values[this.index];
+ var i = s.length;
+
+ if (t[i] == v) {
+ this.values[this.index] += v;
+
+ if (this.values[this.index] == t) {
+ this.index++;
+ }
+ } else {
+ this.values[this.index] = t;
+ this.index++;
+
+ if (v.match(/[\-0-9]/g)) {
+ // Repeat the character
+ this.position--;
+ }
+ }
+ },
+ 'A': function(v) {
+ if (v.match(/[a-zA-Z]/gi)) {
+ this.values[this.index] = v;
+ this.index++;
+ }
+ },
+ '.': function(v) {
+ parser['[0-9a-zA-Z$]+'].call(this, v);
+ },
+ '@': function(v) {
+ if (isBlank(this.values[this.index])) {
+ this.values[this.index] = '';
+ }
+ this.values[this.index] += v;
+ }
+ }
+
+ /**
+ * Get the tokens in the mask string
+ */
+ var getTokens = function(str) {
+ if (this.type == 'general') {
+ var t = [].concat(tokens.general);
+ } else {
+ var t = [].concat(tokens.currency, tokens.datetime, tokens.percentage, tokens.numeric, tokens.text, tokens.general);
+ }
+ // Expression to extract all tokens from the string
+ var e = new RegExp(t.join('|'), 'gi');
+ // Extract
+ return str.match(e);
+ }
+
+ /**
+ * Get the method of one given token
+ */
+ var getMethod = function(str) {
+ if (! this.type) {
+ var types = Object.keys(tokens);
+ } else if (this.type == 'text') {
+ var types = [ 'text' ];
+ } else if (this.type == 'general') {
+ var types = [ 'general' ];
+ } else if (this.type == 'datetime') {
+ var types = [ 'numeric', 'datetime', 'general' ];
+ } else {
+ var types = [ 'currency', 'percentage', 'numeric', 'general' ];
+ }
+
+ // Found
+ for (var i = 0; i < types.length; i++) {
+ var type = types[i];
+ for (var j = 0; j < tokens[type].length; j++) {
+ var e = new RegExp(tokens[type][j], 'gi');
+ var r = str.match(e);
+ if (r) {
+ return { type: type, method: tokens[type][j] }
+ }
+ }
+ }
+ }
+
+ /**
+ * Identify each method for each token
+ */
+ var getMethods = function(t) {
+ var result = [];
+ for (var i = 0; i < t.length; i++) {
+ var m = getMethod.call(this, t[i]);
+ if (m) {
+ result.push(m.method);
+ } else {
+ result.push(null);
+ }
+ }
+
+ // Compatibility with excel
+ for (var i = 0; i < result.length; i++) {
+ if (result[i] == 'MM') {
+ // Not a month, correct to minutes
+ if (result[i-1] && result[i-1].indexOf('H') >= 0) {
+ result[i] = 'MI';
+ } else if (result[i-2] && result[i-2].indexOf('H') >= 0) {
+ result[i] = 'MI';
+ } else if (result[i+1] && result[i+1].indexOf('S') >= 0) {
+ result[i] = 'MI';
+ } else if (result[i+2] && result[i+2].indexOf('S') >= 0) {
+ result[i] = 'MI';
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Get the type for one given token
+ */
+ var getType = function(str) {
+ var m = getMethod.call(this, str);
+ if (m) {
+ var type = m.type;
+ }
+
+ if (type) {
+ var numeric = 0;
+ // Make sure the correct type
+ var t = getTokens.call(this, str);
+ for (var i = 0; i < t.length; i++) {
+ m = getMethod.call(this, t[i]);
+ if (m && isNumeric(m.type)) {
+ numeric++;
+ }
+ }
+ if (numeric > 1) {
+ type = 'general';
+ }
+ }
+
+ return type;
+ }
+
+ /**
+ * Parse character per character using the detected tokens in the mask
+ */
+ var parse = function() {
+ // Parser method for this position
+ if (typeof(parser[this.methods[this.index]]) == 'function') {
+ parser[this.methods[this.index]].call(this, this.value[this.position]);
+ this.position++;
+ } else {
+ this.values[this.index] = this.tokens[this.index];
+ this.index++;
+ }
+ }
+
+ var isFormula = function(value) {
+ var v = (''+value)[0];
+ return v == '=' ? true : false;
+ }
+
+ var toPlainString = function(num) {
+ return (''+ +num).replace(/(-?)(\d*)\.?(\d*)e([+-]\d+)/,
+ function(a,b,c,d,e) {
+ return e < 0
+ ? b + '0.' + Array(1-e-c.length).join(0) + c + d
+ : b + c + d + Array(e-d.length+1).join(0);
+ });
+ }
+
+ /**
+ * Mask function
+ * @param {mixed|string} JS input or a string to be parsed
+ * @param {object|string} When the first param is a string, the second is the mask or object with the mask options
+ */
+ var obj = function(e, config, returnObject) {
+ // Options
+ var r = null;
+ var t = null;
+ var o = {
+ // Element
+ input: null,
+ // Current value
+ value: null,
+ // Mask options
+ options: {},
+ // New values for each token found
+ values: [],
+ // Token position
+ index: 0,
+ // Character position
+ position: 0,
+ // Date raw values
+ date: [0,0,0,0,0,0],
+ // Raw number for the numeric values
+ number: 0,
+ }
+
+ // This is a JavaScript Event
+ if (typeof(e) == 'object') {
+ // Element
+ o.input = e.target;
+ // Current value
+ o.value = Value.call(e.target);
+ // Current caret position
+ o.caret = Caret.call(e.target);
+ // Mask
+ if (t = e.target.getAttribute('data-mask')) {
+ o.mask = t;
+ }
+ // Type
+ if (t = e.target.getAttribute('data-type')) {
+ o.type = t;
+ }
+ // Options
+ if (e.target.mask) {
+ if (e.target.mask.options) {
+ o.options = e.target.mask.options;
+ }
+ if (e.target.mask.locale) {
+ o.locale = e.target.mask.locale;
+ }
+ } else {
+ // Locale
+ if (t = e.target.getAttribute('data-locale')) {
+ o.locale = t;
+ if (o.mask) {
+ o.options.style = o.mask;
+ }
+ }
+ }
+ // Extra configuration
+ if (e.target.attributes && e.target.attributes.length) {
+ for (var i = 0; i < e.target.attributes.length; i++) {
+ var k = e.target.attributes[i].name;
+ var v = e.target.attributes[i].value;
+ if (k.substr(0,4) == 'data') {
+ o.options[k.substr(5)] = v;
+ }
+ }
+ }
+ } else {
+ // Options
+ if (typeof(config) == 'string') {
+ // Mask
+ o.mask = config;
+ } else {
+ // Mask
+ var k = Object.keys(config);
+ for (var i = 0; i < k.length; i++) {
+ o[k[i]] = config[k[i]];
+ }
+ }
+
+ if (typeof(e) === 'number') {
+ // Get decimal
+ getDecimal.call(o, o.mask);
+ // Replace to the correct decimal
+ e = (''+e).replace('.', o.options.decimal);
+ }
+
+ // Current
+ o.value = e;
+
+ if (o.input) {
+ // Value
+ Value.call(o.input, e);
+ // Focus
+ jSuites.focus(o.input);
+ // Caret
+ o.caret = Caret.call(o.input);
+ }
+ }
+
+ // Mask detected start the process
+ if (! isFormula(o.value) && (o.mask || o.locale)) {
+ // Compatibility fixes
+ if (o.mask) {
+ // Remove []
+ o.mask = o.mask.replace(new RegExp(/\[h]/),'|h|');
+ o.mask = o.mask.replace(new RegExp(/\[.*?\]/),'');
+ o.mask = o.mask.replace(new RegExp(/\|h\|/),'[h]');
+ if (o.mask.indexOf(';') !== -1) {
+ var t = o.mask.split(';');
+ o.mask = t[0];
+ }
+ // Excel mask TODO: Improve
+ if (o.mask.indexOf('##') !== -1) {
+ var d = o.mask.split(';');
+ if (d[0]) {
+ d[0] = d[0].replace('*', '\t');
+ d[0] = d[0].replace(new RegExp(/_-/g), ' ');
+ d[0] = d[0].replace(new RegExp(/_/g), '');
+ d[0] = d[0].replace('##0.###','##0.000');
+ d[0] = d[0].replace('##0.##','##0.00');
+ d[0] = d[0].replace('##0.#','##0.0');
+ d[0] = d[0].replace('##0,###','##0,000');
+ d[0] = d[0].replace('##0,##','##0,00');
+ d[0] = d[0].replace('##0,#','##0,0');
+ }
+ o.mask = d[0];
+ }
+ // Get type
+ if (! o.type) {
+ o.type = getType.call(o, o.mask);
+ }
+ // Get tokens
+ o.tokens = getTokens.call(o, o.mask);
+ }
+
+ // On new input
+ if (typeof(e) !== 'object' || ! e.inputType || ! e.inputType.indexOf('insert') || ! e.inputType.indexOf('delete')) {
+ // Start transformation
+ if (o.locale) {
+ if (o.input) {
+ Format.call(o, o.input, e);
+ } else {
+ var newValue = FormatValue.call(o, o.value);
+ }
+ } else {
+ // Get tokens
+ o.methods = getMethods.call(o, o.tokens);
+ o.event = e;
+
+ // Go through all tokes
+ while (o.position < o.value.length && typeof(o.tokens[o.index]) !== 'undefined') {
+ // Get the appropriate parser
+ parse.call(o);
+ }
+
+ // New value
+ var newValue = o.values.join('');
+
+ // Add tokens to the end of string only if string is not empty
+ if (isNumeric(o.type) && newValue !== '') {
+ // Complement things in the end of the mask
+ while (typeof(o.tokens[o.index]) !== 'undefined') {
+ var t = getMethod.call(o, o.tokens[o.index]);
+ if (t && t.type == 'general') {
+ o.values[o.index] = o.tokens[o.index];
+ }
+ o.index++;
+ }
+
+ var adjustNumeric = true;
+ } else {
+ var adjustNumeric = false;
+ }
+
+ // New value
+ newValue = o.values.join('');
+
+ // Reset value
+ if (o.input) {
+ t = newValue.length - o.value.length;
+ if (t > 0) {
+ var caret = o.caret + t;
+ } else {
+ var caret = o.caret;
+ }
+ Value.call(o.input, newValue, caret, adjustNumeric);
+ }
+ }
+ }
+
+ // Update raw data
+ if (o.input) {
+ var label = null;
+ if (isNumeric(o.type)) {
+ // Extract the number
+ o.number = Extract.call(o, Value.call(o.input));
+ // Keep the raw data as a property of the tag
+ if (o.type == 'percentage') {
+ label = o.number / 100;
+ } else {
+ label = o.number;
+ }
+ } else if (o.type == 'datetime') {
+ label = getDate.call(o);
+
+ if (o.date[0] && o.date[1] && o.date[2]) {
+ o.input.setAttribute('data-completed', true);
+ }
+ }
+
+ if (label) {
+ o.input.setAttribute('data-value', label);
+ }
+ }
+
+ if (newValue !== undefined) {
+ if (returnObject) {
+ return o;
+ } else {
+ return newValue;
+ }
+ }
+ }
+ }
+
+ // Get the type of the mask
+ obj.getType = getType;
+
+ // Extract the tokens from a mask
+ obj.prepare = function(str, o) {
+ if (! o) {
+ o = {};
+ }
+ return getTokens.call(o, str);
+ }
+
+ /**
+ * Apply the mask to a element (legacy)
+ */
+ obj.apply = function(e) {
+ var v = Value.call(e.target);
+ if (e.key.length == 1) {
+ v += e.key;
+ }
+ Value.call(e.target, obj(v, e.target.getAttribute('data-mask')));
+ }
+
+ /**
+ * Legacy support
+ */
+ obj.run = function(value, mask, decimal) {
+ return obj(value, { mask: mask, decimal: decimal });
+ }
+
+ /**
+ * Extract number from masked string
+ */
+ obj.extract = function(v, options, returnObject) {
+ if (isBlank(v)) {
+ return v;
+ }
+ if (typeof(options) != 'object') {
+ return value;
+ } else {
+ options = Object.assign({}, options);
+
+ if (! options.options) {
+ options.options = {};
+ }
+ }
+
+ // Compatibility
+ if (! options.mask && options.format) {
+ options.mask = options.format;
+ }
+
+ // Remove []
+ if (options.mask) {
+ if (options.mask.indexOf(';') !== -1) {
+ var t = options.mask.split(';');
+ options.mask = t[0];
+ }
+ options.mask = options.mask.replace(new RegExp(/\[h]/),'|h|');
+ options.mask = options.mask.replace(new RegExp(/\[.*?\]/),'');
+ options.mask = options.mask.replace(new RegExp(/\|h\|/),'[h]');
+ }
+
+ // Get decimal
+ getDecimal.call(options, options.mask);
+
+ var type = null;
+ if (options.type == 'percent' || options.options.style == 'percent') {
+ type = 'percentage';
+ } else if (options.mask) {
+ type = getType.call(options, options.mask);
+ }
+
+ if (type === 'general') {
+ var o = obj(v, options, true);
+
+ value = v;
+ } else if (type === 'datetime') {
+ if (v instanceof Date) {
+ var t = jSuites.calendar.getDateString(value, options.mask);
+ }
+
+ var o = obj(v, options, true);
+
+ if (jSuites.isNumeric(v)) {
+ value = v;
+ } else {
+ var value = extractDate.call(o);
+ }
+ } else {
+ var value = Extract.call(options, v);
+ // Percentage
+ if (type == 'percentage') {
+ value /= 100;
+ }
+ var o = options;
+ }
+
+ o.value = value;
+
+ if (! o.type && type) {
+ o.type = type;
+ }
+
+ if (returnObject) {
+ return o;
+ } else {
+ return value;
+ }
+ }
+
+ /**
+ * Render
+ */
+ obj.render = function(value, options, fullMask) {
+ if (isBlank(value)) {
+ return value;
+ }
+
+ if (typeof(options) != 'object') {
+ return value;
+ } else {
+ options = Object.assign({}, options);
+
+ if (! options.options) {
+ options.options = {};
+ }
+ }
+
+ // Compatibility
+ if (! options.mask && options.format) {
+ options.mask = options.format;
+ }
+
+ // Remove []
+ if (options.mask) {
+ if (options.mask.indexOf(';') !== -1) {
+ var t = options.mask.split(';');
+ options.mask = t[0];
+ }
+ options.mask = options.mask.replace(new RegExp(/\[h]/),'|h|');
+ options.mask = options.mask.replace(new RegExp(/\[.*?\]/),'');
+ options.mask = options.mask.replace(new RegExp(/\|h\|/),'[h]');
+ }
+
+ var type = null;
+ if (options.type == 'percent' || options.options.style == 'percent') {
+ type = 'percentage';
+ } else if (options.mask) {
+ type = getType.call(options, options.mask);
+ } else if (value instanceof Date) {
+ type = 'datetime';
+ }
+
+ // Fill with blanks
+ var fillWithBlanks = false;
+
+ if (type =='datetime' || options.type == 'calendar') {
+ var t = jSuites.calendar.getDateString(value, options.mask);
+ if (t) {
+ value = t;
+ }
+ if (options.mask && fullMask) {
+ fillWithBlanks = true;
+ }
+ } else {
+ // Percentage
+ if (type == 'percentage') {
+ value *= 100;
+ }
+ // Number of decimal places
+ if (typeof(value) === 'number') {
+ var t = null;
+ if (options.mask && fullMask && ((''+value).indexOf('e') === -1)) {
+ var d = getDecimal.call(options, options.mask);
+ if (options.mask.indexOf(d) !== -1) {
+ d = options.mask.split(d);
+ d = (''+d[1].match(/[0-9]+/g))
+ d = d.length;
+ t = value.toFixed(d);
+ } else {
+ t = value.toFixed(0);
+ }
+ } else if (options.locale && fullMask) {
+ // Append zeros
+ var d = (''+value).split('.');
+ if (options.options) {
+ if (typeof(d[1]) === 'undefined') {
+ d[1] = '';
+ }
+ var len = d[1].length;
+ if (options.options.minimumFractionDigits > len) {
+ for (var i = 0; i < options.options.minimumFractionDigits - len; i++) {
+ d[1] += '0';
+ }
+ }
+ }
+ if (! d[1].length) {
+ t = d[0]
+ } else {
+ t = d.join('.');
+ }
+ var len = d[1].length;
+ if (options.options && options.options.maximumFractionDigits < len) {
+ t = parseFloat(t).toFixed(options.options.maximumFractionDigits);
+ }
+ } else {
+ t = toPlainString(value);
+ }
+
+ if (t !== null) {
+ value = t;
+ // Get decimal
+ getDecimal.call(options, options.mask);
+ // Replace to the correct decimal
+ if (options.options.decimal) {
+ value = value.replace('.', options.options.decimal);
+ }
+ }
+ } else {
+ if (options.mask && fullMask) {
+ fillWithBlanks = true;
+ }
+ }
+ }
+
+ if (fillWithBlanks) {
+ var s = options.mask.length - value.length;
+ if (s > 0) {
+ for (var i = 0; i < s; i++) {
+ value += ' ';
+ }
+ }
+ }
+
+ value = obj(value, options);
+
+ // Numeric mask, number of zeros
+ if (fullMask && type === 'numeric') {
+ var maskZeros = options.mask.match(new RegExp(/^[0]+$/gm));
+ if (maskZeros && maskZeros.length === 1) {
+ var maskLength = maskZeros[0].length;
+ if (maskLength > 3) {
+ value = '' + value;
+ while (value.length < maskLength) {
+ value = '0' + value;
+ }
+ }
+ }
+ }
+
+ return value;
+ }
+
+ obj.set = function(e, m) {
+ if (m) {
+ e.setAttribute('data-mask', m);
+ // Reset the value
+ var event = new Event('input', {
+ bubbles: true,
+ cancelable: true,
+ });
+ e.dispatchEvent(event);
+ }
+ }
+
+ if (typeof document !== 'undefined') {
+ document.addEventListener('input', function(e) {
+ if (e.target.getAttribute('data-mask') || e.target.mask) {
+ obj(e);
+ }
+ });
+ }
+
+ return obj;
+})();
+
+jSuites.modal = (function(el, options) {
+ var obj = {};
+ obj.options = {};
+
+ // Default configuration
+ var defaults = {
+ url: null,
+ onopen: null,
+ onclose: null,
+ onload: null,
+ closed: false,
+ width: null,
+ height: null,
+ title: null,
+ padding: null,
+ backdrop: true,
+ icon: 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');
+ while (el.children[0]) {
+ temp.appendChild(el.children[0]);
+ }
+
+ obj.title = document.createElement('div');
+ obj.title.className = 'jmodal_title';
+ if (obj.options.icon) {
+ obj.title.setAttribute('data-icon', obj.options.icon);
+ }
+
+ obj.content = document.createElement('div');
+ obj.content.className = 'jmodal_content';
+ obj.content.innerHTML = el.innerHTML;
+
+ while (temp.children[0]) {
+ obj.content.appendChild(temp.children[0]);
+ }
+
+ obj.container = document.createElement('div');
+ obj.container.className = 'jmodal';
+ obj.container.appendChild(obj.title);
+ obj.container.appendChild(obj.content);
+
+ if (obj.options.padding) {
+ obj.content.style.padding = obj.options.padding;
+ }
+ 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) {
+ var title = document.createElement('h4');
+ title.innerText = obj.options.title;
+ obj.title.appendChild(title);
+ }
+
+ el.innerHTML = '';
+ el.style.display = 'none';
+ el.appendChild(obj.container);
+
+ // Backdrop
+ if (obj.options.backdrop) {
+ var backdrop = document.createElement('div');
+ backdrop.className = 'jmodal_backdrop';
+ backdrop.onclick = function () {
+ obj.close();
+ }
+ el.appendChild(backdrop);
+ }
+
+ obj.open = function() {
+ el.style.display = 'block';
+ // Fullscreen
+ var rect = obj.container.getBoundingClientRect();
+ if (jSuites.getWindowWidth() < rect.width) {
+ obj.container.style.top = '';
+ obj.container.style.left = '';
+ obj.container.classList.add('jmodal_fullscreen');
+ jSuites.animation.slideBottom(obj.container, 1);
+ } else {
+ if (obj.options.backdrop) {
+ backdrop.style.display = 'block';
+ }
+ }
+ // 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() {
+ if (obj.isOpen()) {
+ el.style.display = 'none';
+ if (obj.options.backdrop) {
+ // Backdrop
+ backdrop.style.display = '';
+ }
+ // 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) {
+ // Position
+ var tracker = null;
+
+ document.addEventListener('keydown', function(e) {
+ if (e.which == 27) {
+ var modals = document.querySelectorAll('.jmodal');
+ for (var i = 0; i < modals.length; i++) {
+ modals[i].parentNode.modal.close();
+ }
+ }
+ });
+
+ document.addEventListener('mouseup', function(e) {
+ var item = jSuites.findElement(e.target, 'jmodal');
+ if (item) {
+ // Get target info
+ var rect = item.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) {
+ item.parentNode.modal.close();
+ }
+ }
+
+ if (tracker) {
+ tracker.element.style.cursor = 'auto';
+ tracker = null;
+ }
+ });
+
+ document.addEventListener('mousedown', function(e) {
+ var item = jSuites.findElement(e.target, 'jmodal');
+ if (item) {
+ // Get target info
+ var rect = item.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) {
+ // Do nothing
+ } else {
+ if (y - rect.top < 50) {
+ if (document.selection) {
+ document.selection.empty();
+ } else if ( window.getSelection ) {
+ window.getSelection().removeAllRanges();
+ }
+
+ tracker = {
+ left: rect.left,
+ top: rect.top,
+ x: e.clientX,
+ y: e.clientY,
+ width: rect.width,
+ height: rect.height,
+ element: item,
+ }
+ }
+ }
+ }
+ });
+
+ document.addEventListener('mousemove', function(e) {
+ if (tracker) {
+ 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) {
+ tracker.element.style.top = (tracker.top + (e.clientY - tracker.y) + (tracker.height / 2)) + 'px';
+ tracker.element.style.left = (tracker.left + (e.clientX - tracker.x) + (tracker.width / 2)) + 'px';
+ tracker.element.style.cursor = 'move';
+ } else {
+ tracker.element.style.cursor = 'auto';
+ }
+ }
+ });
+
+ jSuites.modal.hasEvents = true;
+ }
+
+ if (obj.options.url) {
+ jSuites.ajax({
+ url: obj.options.url,
+ method: 'GET',
+ dataType: 'text/html',
+ success: function(data) {
+ obj.content.innerHTML = data;
+
+ if (! obj.options.closed) {
+ obj.open();
+ }
+
+ if (typeof(obj.options.onload) === 'function') {
+ obj.options.onload(obj);
+ }
+ }
+ });
+ } else {
+ if (! obj.options.closed) {
+ obj.open();
+ }
+
+ if (typeof(obj.options.onload) === 'function') {
+ obj.options.onload(obj);
+ }
+ }
+
+ // Keep object available from the node
+ el.modal = obj;
+
+ return obj;
+});
+
+
+jSuites.notification = (function(options) {
+ var obj = {};
+ obj.options = {};
+
+ // Default configuration
+ var defaults = {
+ icon: null,
+ name: 'Notification',
+ date: null,
+ error: 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';
+
+ if (obj.options.error) {
+ notification.classList.add('jnotification-error');
+ }
+
+ 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.animation.fadeIn(notification);
+ } else {
+ jSuites.animation.slideTop(notification, 1);
+ }
+ }
+
+ obj.hide = function() {
+ if (jSuites.getWindowWidth() > 800) {
+ jSuites.animation.fadeOut(notification, function() {
+ if (notification.parentNode) {
+ notification.parentNode.removeChild(notification);
+ if (notificationTimeout) {
+ clearTimeout(notificationTimeout);
+ }
+ }
+ });
+ } else {
+ jSuites.animation.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.notification.isVisible = function() {
+ var j = document.querySelector('.jnotification');
+ return j && j.parentNode ? true : false;
+}
+
+// More palettes https://coolors.co/ or https://gka.github.io/palettes/#/10|s|003790,005647,ffffe0|ffffe0,ff005e,93003a|1|1
+jSuites.palette = (function() {
+ /**
+ * Available palettes
+ */
+ var palette = {
+ material: [
+ [ "#ffebee", "#fce4ec", "#f3e5f5", "#e8eaf6", "#e3f2fd", "#e0f7fa", "#e0f2f1", "#e8f5e9", "#f1f8e9", "#f9fbe7", "#fffde7", "#fff8e1", "#fff3e0", "#fbe9e7", "#efebe9", "#fafafa", "#eceff1" ],
+ [ "#ffcdd2", "#f8bbd0", "#e1bee7", "#c5cae9", "#bbdefb", "#b2ebf2", "#b2dfdb", "#c8e6c9", "#dcedc8", "#f0f4c3", "#fff9c4", "#ffecb3", "#ffe0b2", "#ffccbc", "#d7ccc8", "#f5f5f5", "#cfd8dc" ],
+ [ "#ef9a9a", "#f48fb1", "#ce93d8", "#9fa8da", "#90caf9", "#80deea", "#80cbc4", "#a5d6a7", "#c5e1a5", "#e6ee9c", "#fff59d", "#ffe082", "#ffcc80", "#ffab91", "#bcaaa4", "#eeeeee", "#b0bec5" ],
+ [ "#e57373", "#f06292", "#ba68c8", "#7986cb", "#64b5f6", "#4dd0e1", "#4db6ac", "#81c784", "#aed581", "#dce775", "#fff176", "#ffd54f", "#ffb74d", "#ff8a65", "#a1887f", "#e0e0e0", "#90a4ae" ],
+ [ "#ef5350", "#ec407a", "#ab47bc", "#5c6bc0", "#42a5f5", "#26c6da", "#26a69a", "#66bb6a", "#9ccc65", "#d4e157", "#ffee58", "#ffca28", "#ffa726", "#ff7043", "#8d6e63", "#bdbdbd", "#78909c" ],
+ [ "#f44336", "#e91e63", "#9c27b0", "#3f51b5", "#2196f3", "#00bcd4", "#009688", "#4caf50", "#8bc34a", "#cddc39", "#ffeb3b", "#ffc107", "#ff9800", "#ff5722", "#795548", "#9e9e9e", "#607d8b" ],
+ [ "#e53935", "#d81b60", "#8e24aa", "#3949ab", "#1e88e5", "#00acc1", "#00897b", "#43a047", "#7cb342", "#c0ca33", "#fdd835", "#ffb300", "#fb8c00", "#f4511e", "#6d4c41", "#757575", "#546e7a" ],
+ [ "#d32f2f", "#c2185b", "#7b1fa2", "#303f9f", "#1976d2", "#0097a7", "#00796b", "#388e3c", "#689f38", "#afb42b", "#fbc02d", "#ffa000", "#f57c00", "#e64a19", "#5d4037", "#616161", "#455a64" ],
+ [ "#c62828", "#ad1457", "#6a1b9a", "#283593", "#1565c0", "#00838f", "#00695c", "#2e7d32", "#558b2f", "#9e9d24", "#f9a825", "#ff8f00", "#ef6c00", "#d84315", "#4e342e", "#424242", "#37474f" ],
+ [ "#b71c1c", "#880e4f", "#4a148c", "#1a237e", "#0d47a1", "#006064", "#004d40", "#1b5e20", "#33691e", "#827717", "#f57f17", "#ff6f00", "#e65100", "#bf360c", "#3e2723", "#212121", "#263238" ],
+ ],
+ fire: [
+ ["0b1a6d","840f38","b60718","de030b","ff0c0c","fd491c","fc7521","faa331","fbb535","ffc73a"],
+ ["071147","5f0b28","930513","be0309","ef0000","fa3403","fb670b","f9991b","faad1e","ffc123"],
+ ["03071e","370617","6a040f","9d0208","d00000","dc2f02","e85d04","f48c06","faa307","ffba08"],
+ ["020619","320615","61040d","8c0207","bc0000","c82a02","d05203","db7f06","e19405","efab00"],
+ ["020515","2d0513","58040c","7f0206","aa0000","b62602","b94903","c57205","ca8504","d89b00"],
+ ],
+ baby: [
+ ["eddcd2","fff1e6","fde2e4","fad2e1","c5dedd","dbe7e4","f0efeb","d6e2e9","bcd4e6","99c1de"],
+ ["e1c4b3","ffd5b5","fab6ba","f5a8c4","aacecd","bfd5cf","dbd9d0","baceda","9dc0db","7eb1d5"],
+ ["daa990","ffb787","f88e95","f282a9","8fc4c3","a3c8be","cec9b3","9dbcce","82acd2","649dcb"],
+ ["d69070","ff9c5e","f66770","f05f8f","74bbb9","87bfae","c5b993","83aac3","699bca","4d89c2"],
+ ["c97d5d","f58443","eb4d57","e54a7b","66a9a7","78ae9c","b5a67e","7599b1","5c88b7","4978aa"],
+ ],
+ chart: [
+ ['#C1D37F','#4C5454','#FFD275','#66586F','#D05D5B','#C96480','#95BF8F','#6EA240','#0F0F0E','#EB8258','#95A3B3','#995D81'],
+ ],
+ }
+
+ /**
+ * Get a pallete
+ */
+ var component = function(o) {
+ // Otherwise get palette value
+ if (palette[o]) {
+ return palette[o];
+ } else {
+ return palette.material;
+ }
+ }
+
+ component.get = function(o) {
+ // Otherwise get palette value
+ if (palette[o]) {
+ return palette[o];
+ } else {
+ return palette;
+ }
+ }
+
+ component.set = function(o, v) {
+ palette[o] = v;
+ }
+
+ return component;
+})();
+
+
+jSuites.picker = (function(el, options) {
+ // Already created, update options
+ if (el.picker) {
+ return el.picker.setOptions(options, true);
+ }
+
+ // New instance
+ var obj = { type: 'picker' };
+ obj.options = {};
+
+ var dropdownHeader = null;
+ var dropdownContent = null;
+
+ /**
+ * The element passed is a DOM element
+ */
+ var isDOM = function(o) {
+ return (o instanceof Element || o instanceof HTMLDocument);
+ }
+
+ /**
+ * Create the content options
+ */
+ var createContent = function() {
+ dropdownContent.innerHTML = '';
+
+ // Create items
+ var keys = Object.keys(obj.options.data);
+
+ // Go though all options
+ for (var i = 0; i < keys.length; i++) {
+ // Item
+ var dropdownItem = document.createElement('div');
+ dropdownItem.classList.add('jpicker-item');
+ dropdownItem.k = keys[i];
+ dropdownItem.v = obj.options.data[keys[i]];
+ // Label
+ var item = obj.getLabel(keys[i], dropdownItem);
+ if (isDOM(item)) {
+ dropdownItem.appendChild(item);
+ } else {
+ dropdownItem.innerHTML = item;
+ }
+ // Append
+ dropdownContent.appendChild(dropdownItem);
+ }
+ }
+
+ /**
+ * Set or reset the options for the picker
+ */
+ obj.setOptions = function(options, reset) {
+ // Default configuration
+ var defaults = {
+ value: 0,
+ data: null,
+ render: null,
+ onchange: null,
+ onmouseover: null,
+ onselect: null,
+ onopen: null,
+ onclose: null,
+ onload: null,
+ width: null,
+ header: true,
+ right: false,
+ bottom: false,
+ content: false,
+ columns: null,
+ grid: null,
+ height: null,
+ }
+
+ // Legacy purpose only
+ if (options && options.options) {
+ options.data = options.options;
+ }
+
+ // Loop through the initial configuration
+ for (var property in defaults) {
+ if (options && options.hasOwnProperty(property)) {
+ obj.options[property] = options[property];
+ } else {
+ if (typeof(obj.options[property]) == 'undefined' || reset === true) {
+ obj.options[property] = defaults[property];
+ }
+ }
+ }
+
+ // Start using the options
+ if (obj.options.header === false) {
+ dropdownHeader.style.display = 'none';
+ } else {
+ dropdownHeader.style.display = '';
+ }
+
+ // Width
+ if (obj.options.width) {
+ dropdownHeader.style.width = parseInt(obj.options.width) + 'px';
+ } else {
+ dropdownHeader.style.width = '';
+ }
+
+ // Height
+ if (obj.options.height) {
+ dropdownContent.style.maxHeight = obj.options.height + 'px';
+ dropdownContent.style.overflow = 'scroll';
+ } else {
+ dropdownContent.style.overflow = '';
+ }
+
+ if (obj.options.columns > 0) {
+ if (! obj.options.grid) {
+ dropdownContent.classList.add('jpicker-columns');
+ dropdownContent.style.width = obj.options.width ? obj.options.width : 36 * obj.options.columns + 'px';
+ } else {
+ dropdownContent.classList.add('jpicker-grid');
+ dropdownContent.style.gridTemplateColumns = 'repeat(' + obj.options.grid + ', 1fr)';
+ }
+ }
+
+ if (isNaN(obj.options.value)) {
+ obj.options.value = '0';
+ }
+
+ // Create list from data
+ createContent();
+
+ // Set value
+ obj.setValue(obj.options.value);
+
+ // Set options all returns the own instance
+ return obj;
+ }
+
+ obj.getValue = function() {
+ return obj.options.value;
+ }
+
+ obj.setValue = function(v) {
+ // Set label
+ obj.setLabel(v);
+
+ // Update value
+ obj.options.value = String(v);
+
+ // Lemonade JS
+ if (el.value != obj.options.value) {
+ el.value = obj.options.value;
+ if (typeof(el.oninput) == 'function') {
+ el.oninput({
+ type: 'input',
+ target: el,
+ value: el.value
+ });
+ }
+ }
+
+ if (dropdownContent.children[v].getAttribute('type') !== 'generic') {
+ obj.close();
+ }
+ }
+
+ obj.getLabel = function(v, item) {
+ var label = obj.options.data[v] || null;
+ if (typeof(obj.options.render) == 'function') {
+ label = obj.options.render(label, item);
+ }
+ return label;
+ }
+
+ obj.setLabel = function(v) {
+ var item;
+
+ if (obj.options.content) {
+ item = '' + obj.options.content + '';
+ } else {
+ item = obj.getLabel(v, null);
+ }
+ // Label
+ if (isDOM(item)) {
+ dropdownHeader.innerHTML = '';
+ dropdownHeader.appendChild(item);
+ } else {
+ dropdownHeader.innerHTML = item;
+ }
+ }
+
+ obj.open = function() {
+ if (! el.classList.contains('jpicker-focus')) {
+ // Start tracking the element
+ jSuites.tracking(obj, true);
+
+ // Open picker
+ el.classList.add('jpicker-focus');
+ el.focus();
+
+ var top = 0;
+ var left = 0;
+
+ dropdownContent.style.marginLeft = '';
+
+ var rectHeader = dropdownHeader.getBoundingClientRect();
+ var rectContent = dropdownContent.getBoundingClientRect();
+
+ if (window.innerHeight < rectHeader.bottom + rectContent.height || obj.options.bottom) {
+ top = -1 * (rectContent.height + 4);
+ } else {
+ top = rectHeader.height + 4;
+ }
+
+ if (obj.options.right === true) {
+ left = -1 * rectContent.width + rectHeader.width;
+ }
+
+ if (rectContent.left + left < 0) {
+ left = left + rectContent.left + 10;
+ }
+ if (rectContent.left + rectContent.width > window.innerWidth) {
+ left = -1 * (10 + rectContent.left + rectContent.width - window.innerWidth);
+ }
+
+ dropdownContent.style.marginTop = parseInt(top) + 'px';
+ dropdownContent.style.marginLeft = parseInt(left) + 'px';
+
+ //dropdownContent.style.marginTop
+ if (typeof obj.options.onopen == 'function') {
+ obj.options.onopen(el, obj);
+ }
+ }
+ }
+
+ obj.close = function() {
+ if (el.classList.contains('jpicker-focus')) {
+ el.classList.remove('jpicker-focus');
+
+ // Start tracking the element
+ jSuites.tracking(obj, false);
+
+ if (typeof obj.options.onclose == 'function') {
+ obj.options.onclose(el, obj);
+ }
+ }
+ }
+
+ /**
+ * Create floating picker
+ */
+ var init = function() {
+ // Class
+ el.classList.add('jpicker');
+ el.setAttribute('tabindex', '900');
+ el.onmousedown = function(e) {
+ if (! el.classList.contains('jpicker-focus')) {
+ obj.open();
+ }
+ }
+
+ // Dropdown Header
+ dropdownHeader = document.createElement('div');
+ dropdownHeader.classList.add('jpicker-header');
+
+ // Dropdown content
+ dropdownContent = document.createElement('div');
+ dropdownContent.classList.add('jpicker-content');
+ dropdownContent.onclick = function(e) {
+ var item = jSuites.findElement(e.target, 'jpicker-item');
+ if (item) {
+ if (item.parentNode === dropdownContent) {
+ // Update label
+ obj.setValue(item.k);
+ // Call method
+ if (typeof(obj.options.onchange) == 'function') {
+ obj.options.onchange.call(obj, el, obj, item.v, item.v, item.k, e);
+ }
+ }
+ }
+ }
+
+ // Append content and header
+ el.appendChild(dropdownHeader);
+ el.appendChild(dropdownContent);
+
+ // Default value
+ el.value = options.value || 0;
+
+ // Set options
+ obj.setOptions(options);
+
+ if (typeof(obj.options.onload) == 'function') {
+ obj.options.onload(el, obj);
+ }
+
+ // Change
+ el.change = obj.setValue;
+
+ // Global generic value handler
+ el.val = function(val) {
+ if (val === undefined) {
+ return obj.getValue();
+ } else {
+ obj.setValue(val);
+ }
+ }
+
+ // Reference
+ el.picker = obj;
+ }
+
+ init();
+
+ return obj;
+});
+
+jSuites.progressbar = (function(el, options) {
+ var obj = {};
+ obj.options = {};
+
+ // Default configuration
+ var defaults = {
+ value: 0,
+ onchange: null,
+ width: 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('jprogressbar');
+ el.setAttribute('tabindex', 1);
+ el.setAttribute('data-value', obj.options.value);
+
+ var bar = document.createElement('div');
+ bar.style.width = obj.options.value + '%';
+ bar.style.color = '#fff';
+ el.appendChild(bar);
+
+ if (obj.options.width) {
+ el.style.width = obj.options.width;
+ }
+
+ // Set value
+ obj.setValue = function(value) {
+ value = parseInt(value);
+ obj.options.value = value;
+ bar.style.width = value + '%';
+ el.setAttribute('data-value', value + '%');
+
+ if (value < 6) {
+ el.style.color = '#000';
+ } else {
+ el.style.color = '#fff';
+ }
+
+ // Update value
+ obj.options.value = value;
+
+ if (typeof(obj.options.onchange) == 'function') {
+ obj.options.onchange(el, value);
+ }
+
+ // Lemonade JS
+ if (el.value != obj.options.value) {
+ el.value = obj.options.value;
+ if (typeof(el.oninput) == 'function') {
+ el.oninput({
+ type: 'input',
+ target: el,
+ value: el.value
+ });
+ }
+ }
+ }
+
+ obj.getValue = function() {
+ return obj.options.value;
+ }
+
+ var action = function(e) {
+ if (e.which) {
+ // Get target info
+ var rect = el.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;
+ }
+
+ obj.setValue(Math.round((x - rect.left) / rect.width * 100));
+ }
+ }
+
+ // Events
+ if ('touchstart' in document.documentElement === true) {
+ el.addEventListener('touchstart', action);
+ el.addEventListener('touchend', action);
+ } else {
+ el.addEventListener('mousedown', action);
+ el.addEventListener("mousemove", action);
+ }
+
+ // Change
+ el.change = obj.setValue;
+
+ // Global generic value handler
+ el.val = function(val) {
+ if (val === undefined) {
+ return obj.getValue();
+ } else {
+ obj.setValue(val);
+ }
+ }
+
+ // Reference
+ el.progressbar = obj;
+
+ return obj;
+});
+
+jSuites.rating = (function(el, options) {
+ // Already created, update options
+ if (el.rating) {
+ return el.rating.setOptions(options, true);
+ }
+
+ // New instance
+ var obj = {};
+ obj.options = {};
+
+ obj.setOptions = function(options, reset) {
+ // 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 {
+ if (typeof(obj.options[property]) == 'undefined' || reset === true) {
+ obj.options[property] = defaults[property];
+ }
+ }
+ }
+
+ // Make sure the container is empty
+ el.innerHTML = '';
+
+ // 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);
+ }
+
+ // Selected option
+ 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');
+ }
+ }
+ }
+
+ return obj;
+ }
+
+ // 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);
+ }
+
+ // Lemonade JS
+ if (el.value != obj.options.value) {
+ el.value = obj.options.value;
+ if (typeof(el.oninput) == 'function') {
+ el.oninput({
+ type: 'input',
+ target: el,
+ value: el.value
+ });
+ }
+ }
+ }
+
+ obj.getValue = function() {
+ return obj.options.value;
+ }
+
+ var init = function() {
+ // Start plugin
+ obj.setOptions(options);
+
+ // Class
+ el.classList.add('jrating');
+
+ // 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');
+ }
+ });
+
+ // Change
+ el.change = obj.setValue;
+
+ // Global generic value handler
+ el.val = function(val) {
+ if (val === undefined) {
+ return obj.getValue();
+ } else {
+ obj.setValue(val);
+ }
+ }
+
+ // Reference
+ el.rating = obj;
+ }
+
+ init();
+
+ return obj;
+});
+
+
+jSuites.search = (function(el, options) {
+ if (el.search) {
+ return el.search;
+ }
+
+ var index = null;
+
+ var select = function(e) {
+ if (e.target.classList.contains('jsearch_item')) {
+ var element = e.target;
+ } else {
+ var element = e.target.parentNode;
+ }
+
+ obj.selectIndex(element);
+ e.preventDefault();
+ }
+
+ var createList = function(data) {
+ // Reset container
+ container.innerHTML = '';
+ // Print results
+ if (! data.length) {
+ // Show container
+ el.style.display = '';
+ } else {
+ // Show container
+ el.style.display = 'block';
+
+ // Show items (only 10)
+ var len = data.length < 11 ? data.length : 10;
+ for (var i = 0; i < len; i++) {
+ if (typeof(data[i]) == 'string') {
+ var text = data[i];
+ var value = data[i];
+ } else {
+ // Legacy
+ var text = data[i].text;
+ if (! text && data[i].name) {
+ text = data[i].name;
+ }
+ var value = data[i].value;
+ if (! value && data[i].id) {
+ value = data[i].id;
+ }
+ }
+
+ var div = document.createElement('div');
+ div.setAttribute('data-value', value);
+ div.setAttribute('data-text', text);
+ div.className = 'jsearch_item';
+
+ if (data[i].id) {
+ div.setAttribute('id', data[i].id)
+ }
+
+ if (obj.options.forceSelect && 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.innerHTML = text;
+ div.appendChild(item);
+
+ // Append item to the container
+ container.appendChild(div);
+ }
+ }
+ }
+
+ var execute = function(str) {
+ if (str != obj.terms) {
+ // New terms
+ obj.terms = str;
+ // New index
+ if (obj.options.forceSelect) {
+ index = 0;
+ } else {
+ index = null;
+ }
+ // Array or remote search
+ if (Array.isArray(obj.options.data)) {
+ var test = function(o) {
+ if (typeof(o) == 'string') {
+ if ((''+o).toLowerCase().search(str.toLowerCase()) >= 0) {
+ return true;
+ }
+ } else {
+ for (var key in o) {
+ var value = o[key];
+ if ((''+value).toLowerCase().search(str.toLowerCase()) >= 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ var results = obj.options.data.filter(function(item) {
+ return test(item);
+ });
+
+ // Show items
+ createList(results);
+ } else {
+ // Get remove results
+ jSuites.ajax({
+ url: obj.options.data + str,
+ method: 'GET',
+ dataType: 'json',
+ success: function(data) {
+ // Show items
+ createList(data);
+ }
+ });
+ }
+ }
+ }
+
+ // Search timer
+ var timer = null;
+
+ // Search methods
+ var obj = function(str) {
+ if (timer) {
+ clearTimeout(timer);
+ }
+ timer = setTimeout(function() {
+ execute(str);
+ }, 500);
+ }
+ if(options.forceSelect === null) {
+ options.forceSelect = true;
+ }
+ obj.options = {
+ data: options.data || null,
+ input: options.input || null,
+ searchByNode: options.searchByNode || null,
+ onselect: options.onselect || null,
+ forceSelect: options.forceSelect,
+ onbeforesearch: options.onbeforesearch || null,
+ };
+
+ obj.selectIndex = function(item) {
+ var id = item.getAttribute('id');
+ var text = item.getAttribute('data-text');
+ var value = item.getAttribute('data-value');
+ // Onselect
+ if (typeof(obj.options.onselect) == 'function') {
+ obj.options.onselect(obj, text, value, id);
+ }
+ // Close container
+ obj.close();
+ }
+
+ obj.open = function() {
+ el.style.display = 'block';
+ }
+
+ obj.close = function() {
+ if (timer) {
+ clearTimeout(timer);
+ }
+ // Current terms
+ obj.terms = '';
+ // Remove results
+ container.innerHTML = '';
+ // Hide
+ el.style.display = '';
+ }
+
+ obj.isOpened = function() {
+ return el.style.display ? true : false;
+ }
+
+ obj.keydown = function(e) {
+ if (obj.isOpened()) {
+ if (e.key == 'Enter') {
+ // Enter
+ if (index!==null && container.children[index]) {
+ obj.selectIndex(container.children[index]);
+ e.preventDefault();
+ } else {
+ obj.close();
+ }
+ } else if (e.key === 'ArrowUp') {
+ // Up
+ if (index!==null && container.children[0]) {
+ container.children[index].classList.remove('selected');
+ if(!obj.options.forceSelect && index === 0) {
+ index = null;
+ } else {
+ index = Math.max(0, index-1);
+ container.children[index].classList.add('selected');
+ }
+ }
+ e.preventDefault();
+ } else if (e.key === 'ArrowDown') {
+ // Down
+ if(index == null) {
+ index = -1;
+ } else {
+ container.children[index].classList.remove('selected');
+ }
+ if (index < 9 && container.children[index+1]) {
+ index++;
+ }
+ container.children[index].classList.add('selected');
+ e.preventDefault();
+ }
+ }
+ }
+
+ obj.keyup = function(e) {
+ if (! obj.options.searchByNode && obj.options.input) {
+ if (obj.options.input.tagName === 'DIV') {
+ var terms = obj.options.input.innerText;
+ } else {
+ var terms = obj.options.input.value;
+ }
+ } else {
+ // Current node
+ var node = jSuites.getNode();
+ if (node) {
+ var terms = node.innerText;
+ }
+ }
+
+ if (typeof(obj.options.onbeforesearch) == 'function') {
+ var ret = obj.options.onbeforesearch(obj, terms);
+ if (ret) {
+ terms = ret;
+ } else {
+ if (ret === false) {
+ // Ignore event
+ return;
+ }
+ }
+ }
+
+ obj(terms);
+ }
+
+ // Add events
+ if (obj.options.input) {
+ obj.options.input.addEventListener("keyup", obj.keyup);
+ obj.options.input.addEventListener("keydown", obj.keydown);
+ }
+
+ // Append element
+ var container = document.createElement('div');
+ container.classList.add('jsearch_container');
+ container.onmousedown = select;
+ el.appendChild(container);
+
+ el.classList.add('jsearch');
+ el.search = obj;
+
+ return obj;
+});
+
+
+jSuites.slider = (function(el, options) {
+ var obj = {};
+ obj.options = {};
+ obj.currentImage = null;
+
+ if (options) {
+ obj.options = options;
+ }
+
+ // Focus
+ el.setAttribute('tabindex', '900')
+
+ // Items
+ obj.options.items = [];
+
+ if (! el.classList.contains('jslider')) {
+ el.classList.add('jslider');
+ el.classList.add('unselectable');
+
+ if (obj.options.height) {
+ el.style.minHeight = obj.options.height;
+ }
+ if (obj.options.width) {
+ el.style.width = obj.options.width;
+ }
+ if (obj.options.grid) {
+ el.classList.add('jslider-grid');
+ var number = el.children.length;
+ if (number > 4) {
+ el.setAttribute('data-total', number - 4);
+ }
+ el.setAttribute('data-number', (number > 4 ? 4 : number));
+ }
+
+ // Add slider counter
+ var counter = document.createElement('div');
+ counter.classList.add('jslider-counter');
+
+ // 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]);
+
+ // counter click event
+ var item = document.createElement('div');
+ item.onclick = function() {
+ var index = Array.prototype.slice.call(counter.children).indexOf(this);
+ obj.show(obj.currentImage = obj.options.items[index]);
+ }
+ counter.appendChild(item);
+ }
+ }
+ // Add caption
+ var caption = document.createElement('div');
+ caption.className = 'jslider-caption';
+
+ // Add close buttom
+ var controls = document.createElement('div');
+ var close = document.createElement('div');
+ close.className = 'jslider-close';
+ close.innerHTML = '';
+
+ close.onclick = function() {
+ obj.close();
+ }
+ controls.appendChild(caption);
+ controls.appendChild(close);
+ }
+
+ obj.updateCounter = function(index) {
+ for (var i = 0; i < counter.children.length; i ++) {
+ if (counter.children[i].classList.contains('jslider-counter-focus')) {
+ counter.children[i].classList.remove('jslider-counter-focus');
+ break;
+ }
+ }
+ counter.children[index].classList.add('jslider-counter-focus');
+ }
+
+ obj.show = function(target) {
+ if (! target) {
+ var target = el.children[0];
+ }
+
+ // Focus element
+ el.classList.add('jslider-focus');
+ el.classList.remove('jslider-grid');
+ el.appendChild(controls);
+ el.appendChild(counter);
+
+ // Update counter
+ var index = obj.options.items.indexOf(target);
+ obj.updateCounter(index);
+
+ // Remove display
+ for (var i = 0; i < el.children.length; i++) {
+ el.children[i].style.display = '';
+ }
+ target.style.display = 'block';
+
+ // Is there any previous
+ if (target.previousElementSibling) {
+ el.classList.add('jslider-left');
+ } else {
+ el.classList.remove('jslider-left');
+ }
+
+ // Is there any next
+ if (target.nextElementSibling && target.nextElementSibling.tagName == 'IMG') {
+ el.classList.add('jslider-right');
+ } else {
+ el.classList.remove('jslider-right');
+ }
+
+ obj.currentImage = target;
+
+ // Vertical image
+ if (obj.currentImage.offsetHeight > obj.currentImage.offsetWidth) {
+ obj.currentImage.classList.add('jslider-vertical');
+ }
+
+ controls.children[0].innerText = obj.currentImage.getAttribute('title');
+ }
+
+ obj.open = function() {
+ obj.show();
+
+ // Event
+ if (typeof(obj.options.onopen) == 'function') {
+ obj.options.onopen(el);
+ }
+ }
+
+ obj.close = function() {
+ // Remove control classes
+ el.classList.remove('jslider-focus');
+ el.classList.remove('jslider-left');
+ el.classList.remove('jslider-right');
+ // Show as a grid depending on the configuration
+ if (obj.options.grid) {
+ el.classList.add('jslider-grid');
+ }
+ // Remove display
+ for (var i = 0; i < el.children.length; i++) {
+ el.children[i].style.display = '';
+ }
+ // Remove controls from the component
+ counter.remove();
+ controls.remove();
+ // Current image
+ obj.currentImage = null;
+ // Event
+ if (typeof(obj.options.onclose) == 'function') {
+ obj.options.onclose(el);
+ }
+ }
+
+ obj.reset = function() {
+ el.innerHTML = '';
+ }
+
+ obj.next = function() {
+ var nextImage = obj.currentImage.nextElementSibling;
+ if (nextImage && nextImage.tagName === 'IMG') {
+ obj.show(obj.currentImage.nextElementSibling);
+ }
+ }
+
+ obj.prev = function() {
+ if (obj.currentImage.previousElementSibling) {
+ obj.show(obj.currentImage.previousElementSibling);
+ }
+ }
+
+ var mouseUp = function(e) {
+ // Open slider
+ if (e.target.tagName == 'IMG') {
+ obj.show(e.target);
+ } else if (! e.target.classList.contains('jslider-close') && ! (e.target.parentNode.classList.contains('jslider-counter') || e.target.classList.contains('jslider-counter'))){
+ // Arrow controls
+ var offsetX = e.offsetX || e.changedTouches[0].clientX;
+ if (e.target.clientWidth - offsetX < 40) {
+ // Show next image
+ obj.next();
+ } else if (offsetX < 40) {
+ // Show previous image
+ obj.prev();
+ }
+ }
+ }
+
+ if ('ontouchend' in document.documentElement === true) {
+ el.addEventListener('touchend', mouseUp);
+ } else {
+ el.addEventListener('mouseup', 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.addEventListener('keydown', function(e) {
+ if (e.which == 27) {
+ obj.close();
+ }
+ });
+
+ el.slider = obj;
+
+ return obj;
+});
+
+jSuites.sorting = (function(el, options) {
+ var obj = {};
+ obj.options = {};
+
+ var defaults = {
+ pointer: null,
+ direction: null,
+ ondragstart: null,
+ ondragend: null,
+ ondrop: null,
+ }
+
+ var dragElement = 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];
+ }
+ }
+
+ el.classList.add('jsorting');
+
+ el.addEventListener('dragstart', function(e) {
+ var position = Array.prototype.indexOf.call(e.target.parentNode.children, e.target);
+ dragElement = {
+ element: e.target,
+ o: position,
+ d: position
+ }
+ e.target.style.opacity = '0.25';
+
+ if (typeof(obj.options.ondragstart) == 'function') {
+ obj.options.ondragstart(el, e.target, e);
+ }
+ });
+
+ el.addEventListener('dragover', function(e) {
+ e.preventDefault();
+
+ if (getElement(e.target) && dragElement) {
+ if (e.target.getAttribute('draggable') == 'true' && dragElement.element != e.target) {
+ if (! obj.options.direction) {
+ var condition = e.target.clientHeight / 2 > e.offsetY;
+ } else {
+ var condition = e.target.clientWidth / 2 > e.offsetX;
+ }
+
+ if (condition) {
+ e.target.parentNode.insertBefore(dragElement.element, e.target);
+ } else {
+ e.target.parentNode.insertBefore(dragElement.element, e.target.nextSibling);
+ }
+
+ dragElement.d = Array.prototype.indexOf.call(e.target.parentNode.children, dragElement.element);
+ }
+ }
+ });
+
+ el.addEventListener('dragleave', function(e) {
+ e.preventDefault();
+ });
+
+ el.addEventListener('dragend', function(e) {
+ e.preventDefault();
+
+ if (dragElement) {
+ if (typeof(obj.options.ondragend) == 'function') {
+ obj.options.ondragend(el, dragElement.element, e);
+ }
+
+ // Cancelled put element to the original position
+ if (dragElement.o < dragElement.d) {
+ e.target.parentNode.insertBefore(dragElement.element, e.target.parentNode.children[dragElement.o]);
+ } else {
+ e.target.parentNode.insertBefore(dragElement.element, e.target.parentNode.children[dragElement.o].nextSibling);
+ }
+
+ dragElement.element.style.opacity = '';
+ dragElement = null;
+ }
+ });
+
+ el.addEventListener('drop', function(e) {
+ e.preventDefault();
+
+ if (dragElement && (dragElement.o != dragElement.d)) {
+ if (typeof(obj.options.ondrop) == 'function') {
+ obj.options.ondrop(el, dragElement.o, dragElement.d, dragElement.element, e.target, e);
+ }
+ }
+
+ dragElement.element.style.opacity = '';
+ dragElement = null;
+ });
+
+ 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++) {
+ if (! el.children[i].hasAttribute('draggable')) {
+ el.children[i].setAttribute('draggable', 'true');
+ }
+ }
+
+ el.val = function() {
+ var id = null;
+ var data = [];
+ for (var i = 0; i < el.children.length; i++) {
+ if (id = el.children[i].getAttribute('data-id')) {
+ data.push(id);
+ }
+ }
+ return data;
+ }
+
+ return el;
+});
+
+jSuites.tabs = (function(el, options) {
+ var obj = {};
+ obj.options = {};
+
+ // Default configuration
+ var defaults = {
+ data: [],
+ position: null,
+ allowCreate: false,
+ allowChangePosition: false,
+ onclick: null,
+ onload: null,
+ onchange: null,
+ oncreate: null,
+ ondelete: null,
+ onbeforecreate: null,
+ onchangeposition: null,
+ animation: false,
+ hideHeaders: false,
+ padding: null,
+ palette: null,
+ maxWidth: 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('jtabs');
+
+ var prev = null;
+ var next = null;
+ var border = null;
+
+ // Helpers
+ var setBorder = function(index) {
+ if (obj.options.animation) {
+ setTimeout(function() {
+ var rect = obj.headers.children[index].getBoundingClientRect();
+
+ if (obj.options.palette == 'modern') {
+ border.style.width = rect.width - 4 + 'px';
+ border.style.left = obj.headers.children[index].offsetLeft + 2 + 'px';
+ } else {
+ border.style.width = rect.width + 'px';
+ border.style.left = obj.headers.children[index].offsetLeft + 'px';
+ }
+
+ if (obj.options.position == 'bottom') {
+ border.style.top = '0px';
+ } else {
+ border.style.bottom = '0px';
+ }
+ }, 150);
+ }
+ }
+
+ var updateControls = function(x) {
+ if (typeof(obj.headers.scrollTo) == 'function') {
+ obj.headers.scrollTo({
+ left: x,
+ behavior: 'smooth',
+ });
+ } else {
+ obj.headers.scrollLeft = x;
+ }
+
+ if (x <= 1) {
+ prev.classList.add('disabled');
+ } else {
+ prev.classList.remove('disabled');
+ }
+
+ if (x >= obj.headers.scrollWidth - obj.headers.offsetWidth) {
+ next.classList.add('disabled');
+ } else {
+ next.classList.remove('disabled');
+ }
+
+ if (obj.headers.scrollWidth <= obj.headers.offsetWidth) {
+ prev.style.display = 'none';
+ next.style.display = 'none';
+ } else {
+ prev.style.display = '';
+ next.style.display = '';
+ }
+ }
+
+ obj.setBorder = setBorder;
+
+ // Set value
+ obj.open = function(index) {
+ var previous = null;
+ for (var i = 0; i < obj.headers.children.length; i++) {
+ if (obj.headers.children[i].classList.contains('jtabs-selected')) {
+ // Current one
+ previous = i;
+ }
+ // Remote selected
+ obj.headers.children[i].classList.remove('jtabs-selected');
+ if (obj.content.children[i]) {
+ obj.content.children[i].classList.remove('jtabs-selected');
+ }
+ }
+
+ obj.headers.children[index].classList.add('jtabs-selected');
+ if (obj.content.children[index]) {
+ obj.content.children[index].classList.add('jtabs-selected');
+ }
+
+ if (previous != index && typeof(obj.options.onchange) == 'function') {
+ if (obj.content.children[index]) {
+ obj.options.onchange(el, obj, index, obj.headers.children[index], obj.content.children[index]);
+ }
+ }
+
+ // Hide
+ if (obj.options.hideHeaders == true && (obj.headers.children.length < 3 && obj.options.allowCreate == false)) {
+ obj.headers.parentNode.style.display = 'none';
+ } else {
+ // Set border
+ setBorder(index);
+
+ obj.headers.parentNode.style.display = '';
+
+ var x1 = obj.headers.children[index].offsetLeft;
+ var x2 = x1 + obj.headers.children[index].offsetWidth;
+ var r1 = obj.headers.scrollLeft;
+ var r2 = r1 + obj.headers.offsetWidth;
+
+ if (! (r1 <= x1 && r2 >= x2)) {
+ // Out of the viewport
+ updateControls(x1 - 1);
+ }
+ }
+ }
+
+ obj.selectIndex = function(a) {
+ var index = Array.prototype.indexOf.call(obj.headers.children, a);
+ if (index >= 0) {
+ obj.open(index);
+ }
+
+ return index;
+ }
+
+ obj.rename = function(i, title) {
+ if (! title) {
+ title = prompt('New title', obj.headers.children[i].innerText);
+ }
+ obj.headers.children[i].innerText = title;
+ obj.open(i);
+ }
+
+ obj.create = function(title, url) {
+ if (typeof(obj.options.onbeforecreate) == 'function') {
+ var ret = obj.options.onbeforecreate(el);
+ if (ret === false) {
+ return false;
+ } else {
+ title = ret;
+ }
+ }
+
+ var div = obj.appendElement(title);
+
+ if (typeof(obj.options.oncreate) == 'function') {
+ obj.options.oncreate(el, div)
+ }
+
+ setBorder();
+
+ return div;
+ }
+
+ obj.remove = function(index) {
+ return obj.deleteElement(index);
+ }
+
+ obj.nextNumber = function() {
+ var num = 0;
+ for (var i = 0; i < obj.headers.children.length; i++) {
+ var tmp = obj.headers.children[i].innerText.match(/[0-9].*/);
+ if (tmp > num) {
+ num = parseInt(tmp);
+ }
+ }
+ if (! num) {
+ num = 1;
+ } else {
+ num++;
+ }
+
+ return num;
+ }
+
+ obj.deleteElement = function(index) {
+ if (! obj.headers.children[index]) {
+ return false;
+ } else {
+ obj.headers.removeChild(obj.headers.children[index]);
+ obj.content.removeChild(obj.content.children[index]);
+ }
+
+ obj.open(0);
+
+ if (typeof(obj.options.ondelete) == 'function') {
+ obj.options.ondelete(el, index)
+ }
+ }
+
+ obj.appendElement = function(title, cb) {
+ if (! title) {
+ var title = prompt('Title?', '');
+ }
+
+ if (title) {
+ // Add content
+ var div = document.createElement('div');
+ obj.content.appendChild(div);
+
+ // Add headers
+ var h = document.createElement('div');
+ h.innerHTML = title;
+ h.content = div;
+ obj.headers.insertBefore(h, obj.headers.lastChild);
+
+ // Sortable
+ if (obj.options.allowChangePosition) {
+ h.setAttribute('draggable', 'true');
+ }
+ // Open new tab
+ obj.selectIndex(h);
+
+ // Callback
+ if (typeof(cb) == 'function') {
+ cb(div, h);
+ }
+
+ // Return element
+ return div;
+ }
+ }
+
+ obj.getActive = function() {
+ for (var i = 0; i < obj.headers.children.length; i++) {
+ if (obj.headers.children[i].classList.contains('jtabs-selected')) {
+ return i
+ }
+ }
+ return 0;
+ }
+
+ obj.updateContent = function(position, newContent) {
+ if (typeof newContent !== 'string') {
+ var contentItem = newContent;
+ } else {
+ var contentItem = document.createElement('div');
+ contentItem.innerHTML = newContent;
+ }
+
+ if (obj.content.children[position].classList.contains('jtabs-selected')) {
+ newContent.classList.add('jtabs-selected');
+ }
+
+ obj.content.replaceChild(newContent, obj.content.children[position]);
+
+ setBorder();
+ }
+
+ obj.updatePosition = function(f, t) {
+ // Ondrop update position of content
+ if (f > t) {
+ obj.content.insertBefore(obj.content.children[f], obj.content.children[t]);
+ } else {
+ obj.content.insertBefore(obj.content.children[f], obj.content.children[t].nextSibling);
+ }
+
+ // Open destination tab
+ obj.open(t);
+
+ // Call event
+ if (typeof(obj.options.onchangeposition) == 'function') {
+ obj.options.onchangeposition(obj.headers, f, t);
+ }
+ }
+
+ obj.move = function(f, t) {
+ if (f > t) {
+ obj.headers.insertBefore(obj.headers.children[f], obj.headers.children[t]);
+ } else {
+ obj.headers.insertBefore(obj.headers.children[f], obj.headers.children[t].nextSibling);
+ }
+
+ obj.updatePosition(f, t);
+ }
+
+ obj.setBorder = setBorder;
+
+ obj.init = function() {
+ el.innerHTML = '';
+
+ // Make sure the component is blank
+ obj.headers = document.createElement('div');
+ obj.content = document.createElement('div');
+ obj.headers.classList.add('jtabs-headers');
+ obj.content.classList.add('jtabs-content');
+
+ if (obj.options.palette) {
+ el.classList.add('jtabs-modern');
+ } else {
+ el.classList.remove('jtabs-modern');
+ }
+
+ // Padding
+ if (obj.options.padding) {
+ obj.content.style.padding = parseInt(obj.options.padding) + 'px';
+ }
+
+ // Header
+ var header = document.createElement('div');
+ header.className = 'jtabs-headers-container';
+ header.appendChild(obj.headers);
+ if (obj.options.maxWidth) {
+ header.style.maxWidth = parseInt(obj.options.maxWidth) + 'px';
+ }
+
+ // Controls
+ var controls = document.createElement('div');
+ controls.className = 'jtabs-controls';
+ controls.setAttribute('draggable', 'false');
+ header.appendChild(controls);
+
+ // Append DOM elements
+ if (obj.options.position == 'bottom') {
+ el.appendChild(obj.content);
+ el.appendChild(header);
+ } else {
+ el.appendChild(header);
+ el.appendChild(obj.content);
+ }
+
+ // New button
+ if (obj.options.allowCreate == true) {
+ var add = document.createElement('div');
+ add.className = 'jtabs-add';
+ add.onclick = function() {
+ obj.create();
+ }
+ controls.appendChild(add);
+ }
+
+ prev = document.createElement('div');
+ prev.className = 'jtabs-prev';
+ prev.onclick = function() {
+ updateControls(obj.headers.scrollLeft - obj.headers.offsetWidth);
+ }
+ controls.appendChild(prev);
+
+ next = document.createElement('div');
+ next.className = 'jtabs-next';
+ next.onclick = function() {
+ updateControls(obj.headers.scrollLeft + obj.headers.offsetWidth);
+ }
+ controls.appendChild(next);
+
+ // Data
+ for (var i = 0; i < obj.options.data.length; i++) {
+ // Title
+ if (obj.options.data[i].titleElement) {
+ var headerItem = obj.options.data[i].titleElement;
+ } else {
+ var headerItem = document.createElement('div');
+ }
+ // Icon
+ if (obj.options.data[i].icon) {
+ var iconContainer = document.createElement('div');
+ var icon = document.createElement('i');
+ icon.classList.add('material-icons');
+ icon.innerHTML = obj.options.data[i].icon;
+ iconContainer.appendChild(icon);
+ headerItem.appendChild(iconContainer);
+ }
+ // Title
+ if (obj.options.data[i].title) {
+ var title = document.createTextNode(obj.options.data[i].title);
+ headerItem.appendChild(title);
+ }
+ // Width
+ if (obj.options.data[i].width) {
+ headerItem.style.width = obj.options.data[i].width;
+ }
+ // Content
+ if (obj.options.data[i].contentElement) {
+ var contentItem = obj.options.data[i].contentElement;
+ } else {
+ var contentItem = document.createElement('div');
+ contentItem.innerHTML = obj.options.data[i].content;
+ }
+ obj.headers.appendChild(headerItem);
+ obj.content.appendChild(contentItem);
+ }
+
+ // Animation
+ border = document.createElement('div');
+ border.className = 'jtabs-border';
+ obj.headers.appendChild(border);
+
+ if (obj.options.animation) {
+ el.classList.add('jtabs-animation');
+ }
+
+ // Events
+ obj.headers.addEventListener("click", function(e) {
+ if (e.target.parentNode.classList.contains('jtabs-headers')) {
+ var target = e.target;
+ } else {
+ if (e.target.tagName == 'I') {
+ var target = e.target.parentNode.parentNode;
+ } else {
+ var target = e.target.parentNode;
+ }
+ }
+
+ var index = obj.selectIndex(target);
+
+ if (typeof(obj.options.onclick) == 'function') {
+ obj.options.onclick(el, obj, index, obj.headers.children[index], obj.content.children[index]);
+ }
+ });
+
+ obj.headers.addEventListener("contextmenu", function(e) {
+ obj.selectIndex(e.target);
+ });
+
+ if (obj.headers.children.length) {
+ // Open first tab
+ obj.open(0);
+ }
+
+ // Update controls
+ updateControls(0);
+
+ if (obj.options.allowChangePosition == true) {
+ jSuites.sorting(obj.headers, {
+ direction: 1,
+ ondrop: function(a,b,c) {
+ obj.updatePosition(b,c);
+ },
+ });
+ }
+
+ if (typeof(obj.options.onload) == 'function') {
+ obj.options.onload(el, obj);
+ }
+ }
+
+ // Loading existing nodes as the data
+ if (el.children[0] && el.children[0].children.length) {
+ // Create from existing elements
+ for (var i = 0; i < el.children[0].children.length; i++) {
+ var item = obj.options.data && obj.options.data[i] ? obj.options.data[i] : {};
+
+ if (el.children[1] && el.children[1].children[i]) {
+ item.titleElement = el.children[0].children[i];
+ item.contentElement = el.children[1].children[i];
+ } else {
+ item.contentElement = el.children[0].children[i];
+ }
+
+ obj.options.data[i] = item;
+ }
+ }
+
+ // Remote controller flag
+ var loadingRemoteData = false;
+
+ // Create from data
+ if (obj.options.data) {
+ // Append children
+ for (var i = 0; i < obj.options.data.length; i++) {
+ if (obj.options.data[i].url) {
+ jSuites.ajax({
+ url: obj.options.data[i].url,
+ type: 'GET',
+ dataType: 'text/html',
+ index: i,
+ success: function(result) {
+ obj.options.data[this.index].content = result;
+ },
+ complete: function() {
+ obj.init();
+ }
+ });
+
+ // Flag loading
+ loadingRemoteData = true;
+ }
+ }
+ }
+
+ if (! loadingRemoteData) {
+ obj.init();
+ }
+
+ el.tabs = obj;
+
+ return obj;
+});
+
+jSuites.tags = (function(el, options) {
+ // Redefine configuration
+ if (el.tags) {
+ return el.tags.setOptions(options, true);
+ }
+
+ var obj = { type:'tags' };
+ obj.options = {};
+
+ // Limit
+ var limit = function() {
+ return obj.options.limit && el.children.length >= obj.options.limit ? true : false;
+ }
+
+ // Search helpers
+ var search = null;
+ var searchContainer = null;
+
+ obj.setOptions = function(options, reset) {
+ /**
+ * @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: '',
+ limit: null,
+ limitMessage: null,
+ search: null,
+ placeholder: null,
+ validation: null,
+ onbeforepaste: null,
+ onbeforechange: null,
+ onlimit: 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 {
+ if (typeof(obj.options[property]) == 'undefined' || reset === true) {
+ obj.options[property] = defaults[property];
+ }
+ }
+ }
+
+ // Placeholder
+ if (obj.options.placeholder) {
+ el.setAttribute('data-placeholder', obj.options.placeholder);
+ } else {
+ el.removeAttribute('data-placeholder');
+ }
+ el.placeholder = obj.options.placeholder;
+
+ // Update value
+ obj.setValue(obj.options.value);
+
+ // Validate items
+ filter();
+
+ // Create search box
+ if (obj.options.search) {
+ if (! searchContainer) {
+ searchContainer = document.createElement('div');
+ el.parentNode.insertBefore(searchContainer, el.nextSibling);
+
+ // Create container
+ search = jSuites.search(searchContainer, {
+ data: obj.options.search,
+ onselect: function(a,b,c) {
+ obj.selectIndex(b,c);
+ }
+ });
+ }
+ } else {
+ if (searchContainer) {
+ search = null;
+ searchContainer.remove();
+ searchContainer = null;
+ }
+ }
+
+ return obj;
+ }
+
+ /**
+ * 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 ret = obj.options.onbeforechange(el, obj, obj.options.value, value);
+ if (ret === false) {
+ return false;
+ } else {
+ if (ret != null) {
+ value = ret;
+ }
+ }
+ }
+
+ // Make sure search is closed
+ if (search) {
+ search.close();
+ }
+
+ if (limit()) {
+ if (typeof(obj.options.onlimit) == 'function') {
+ obj.options.onlimit(obj, obj.options.limit);
+ } else if (obj.options.limitMessage) {
+ alert(obj.options.limitMessage + ' ' + obj.options.limit);
+ }
+ } else {
+ // Get node
+ var node = jSuites.getNode();
+
+ if (node && node.parentNode && node.parentNode.classList.contains('jtags') &&
+ node.nextSibling && (! (node.nextSibling.innerText && node.nextSibling.innerText.trim()))) {
+ div = node.nextSibling;
+ } else {
+ // Remove not used last item
+ if (el.lastChild) {
+ if (! el.lastChild.innerText.trim()) {
+ el.removeChild(el.lastChild);
+ }
+ }
+
+ // Mix argument string or array
+ if (! value || typeof(value) == 'string') {
+ var div = createElement(value, value, node);
+ } else {
+ for (var i = 0; i <= value.length; i++) {
+ if (! limit()) {
+ if (! value[i] || typeof(value[i]) == 'string') {
+ var t = value[i] || '';
+ var v = null;
+ } else {
+ var t = value[i].text;
+ var v = value[i].value;
+ }
+
+ // Add element
+ var div = createElement(t, v);
+ }
+ }
+ }
+
+ // Change
+ change();
+ }
+
+ // Place caret
+ if (focus) {
+ setFocus(div);
+ }
+ }
+ }
+
+ obj.setLimit = function(limit) {
+ obj.options.limit = limit;
+ var n = el.children.length - limit;
+ while (el.children.length > limit) {
+ el.removeChild(el.lastChild);
+ }
+ }
+
+ // Remove a item node
+ obj.remove = function(node) {
+ // Remove node
+ node.parentNode.removeChild(node);
+ // Make sure element is not blank
+ if (! el.children.length) {
+ obj.add('', true);
+ } else {
+ change();
+ }
+ }
+
+ /**
+ * 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++) {
+ // Get value
+ var text = el.children[i].innerText.replace("\n", "");
+ // Get id
+ var value = el.children[i].getAttribute('data-value');
+ if (! value) {
+ value = text;
+ }
+ // Item
+ if (text || value) {
+ data.push({ text: text, 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].getAttribute('data-value');
+ if (! 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 {mixed} value - A string or array object with values
+ */
+ obj.setValue = function(mixed) {
+ if (! mixed) {
+ obj.reset();
+ } else {
+ if (el.value != mixed) {
+ if (Array.isArray(mixed)) {
+ obj.add(mixed);
+ } else {
+ // Remove whitespaces
+ var text = (''+mixed).trim();
+ // Tags
+ var data = extractTags(text);
+ // Reset
+ el.innerHTML = '';
+ // Add tags to the element
+ obj.add(data);
+ }
+ }
+ }
+ }
+
+ /**
+ * Reset the data from the element
+ */
+ obj.reset = function() {
+ // Empty class
+ el.classList.add('jtags-empty');
+ // Empty element
+ el.innerHTML = '';
+ // Execute changes
+ change();
+ }
+
+ /**
+ * 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(text, value) {
+ var node = jSuites.getNode();
+ if (node) {
+ // Append text to the caret
+ node.innerText = text;
+ // Set node id
+ if (value) {
+ node.setAttribute('data-value', value);
+ }
+ // Remove any error
+ node.classList.remove('jtags_error');
+ if (! limit()) {
+ // Add new item
+ obj.add('', true);
+ }
+ }
+ }
+
+ /**
+ * Search for suggestions
+ * @param {object} node - Target node for any suggestions
+ */
+ obj.search = function(node) {
+ // Search for
+ var terms = node.innerText;
+ }
+
+ // 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 setFocus = function(node) {
+ if (el.children.length) {
+ var range = document.createRange();
+ var sel = window.getSelection();
+ if (! node) {
+ var node = el.childNodes[el.childNodes.length-1];
+ }
+ range.setStart(node, node.length)
+ range.collapse(true)
+ sel.removeAllRanges()
+ sel.addRange(range)
+ el.scrollLeft = el.scrollWidth;
+ }
+ }
+
+ var createElement = function(label, value, node) {
+ var div = document.createElement('div');
+ div.innerHTML = label ? label : '';
+ if (value) {
+ div.setAttribute('data-value', value);
+ }
+
+ if (node && node.parentNode.classList.contains('jtags')) {
+ el.insertBefore(div, node.nextSibling);
+ } else {
+ el.appendChild(div);
+ }
+
+ return div;
+ }
+
+ var change = function() {
+ // Value
+ var value = obj.getValue();
+
+ if (value != obj.options.value) {
+ obj.options.value = value;
+ if (typeof(obj.options.onchange) == 'function') {
+ obj.options.onchange(el, obj, obj.options.value);
+ }
+
+ // Lemonade JS
+ if (el.value != obj.options.value) {
+ el.value = obj.options.value;
+ if (typeof(el.oninput) == 'function') {
+ el.oninput({
+ type: 'input',
+ target: el,
+ value: el.value
+ });
+ }
+ }
+ }
+
+ filter();
+ }
+
+ /**
+ * Filter tags
+ */
+ var filter = function() {
+ for (var i = 0; i < el.children.length; i++) {
+ if (el.children[i].tagName === 'DIV') {
+ // 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-value'))) {
+ el.children[i].classList.add('jtags_error');
+ } else {
+ el.children[i].classList.remove('jtags_error');
+ }
+ } else {
+ el.children[i].classList.remove('jtags_error');
+ }
+ } else {
+ el.children[i].classList.remove('jtags_error');
+ }
+ }
+ }
+ }
+
+ isEmpty();
+ }
+
+ var isEmpty = function() {
+ // Can't be empty
+ if (! el.innerText.trim()) {
+ if (! el.children.length || el.children[0].tagName === 'BR') {
+ el.innerHTML = '';
+ setFocus(createElement());
+ }
+ } else {
+ el.classList.remove('jtags-empty');
+ }
+ }
+
+ /**
+ * 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] == '\n') {
+ if (word) {
+ data.push(word.trim());
+ 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 if is empty
+ isEmpty();
+
+ // Comma
+ if (e.key === 'Tab' || e.key === ';' || e.key === ',') {
+ var n = window.getSelection().anchorOffset;
+ if (n > 1) {
+ if (limit()) {
+ if (typeof(obj.options.onlimit) == 'function') {
+ obj.options.onlimit(obj, obj.options.limit)
+ }
+ } else {
+ obj.add('', true);
+ }
+ }
+ e.preventDefault();
+ } else if (e.key == 'Enter') {
+ if (! search || ! search.isOpened()) {
+ var n = window.getSelection().anchorOffset;
+ if (n > 1) {
+ if (! limit()) {
+ obj.add('', true);
+ }
+ }
+ e.preventDefault();
+ }
+ } else if (e.key == 'Backspace') {
+ // Back space - do not let last item to be removed
+ if (el.children.length == 1 && window.getSelection().anchorOffset < 1) {
+ e.preventDefault();
+ }
+ }
+
+ // Search events
+ if (search) {
+ search.keydown(e);
+ }
+
+ // Verify if is empty
+ isEmpty();
+ }
+
+ /**
+ * Processing event keyup on the element
+ * @param e {object}
+ */
+ var tagsKeyUp = function(e) {
+ if (e.which == 39) {
+ // Right arrow
+ var n = window.getSelection().anchorOffset;
+ if (n > 1 && n == anchorOffset) {
+ obj.add('', true);
+ }
+ } else if (e.which == 13 || e.which == 38 || e.which == 40) {
+ e.preventDefault();
+ } else {
+ if (search) {
+ search.keyup(e);
+ }
+ }
+
+ filter();
+ }
+
+ /**
+ * Processing event paste on the element
+ * @param e {object}
+ */
+ var tagsPaste = function(e) {
+ if (e.clipboardData || e.originalEvent.clipboardData) {
+ var text = (e.originalEvent || e).clipboardData.getData('text/plain');
+ } else if (window.clipboardData) {
+ var text = window.clipboardData.getData('Text');
+ }
+
+ var data = extractTags(text);
+
+ if (typeof(obj.options.onbeforepaste) == 'function') {
+ var ret = obj.options.onbeforepaste(el, obj, data);
+ if (ret === false) {
+ e.preventDefault();
+ return false;
+ } else {
+ if (ret) {
+ data = ret;
+ }
+ }
+ }
+
+ if (data.length > 1) {
+ obj.add(data, true);
+ e.preventDefault();
+ } else if (data[0]) {
+ document.execCommand('insertText', false, data[0])
+ 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')) {
+ var rect = e.target.getBoundingClientRect();
+ if (rect.width - (e.clientX - rect.left) < 16) {
+ obj.remove(e.target);
+ }
+ }
+ }
+
+ // Set focus in the last item
+ if (e.target == el) {
+ setFocus();
+ }
+ }
+
+ var tagsFocus = function() {
+ if (! el.classList.contains('jtags-focus')) {
+ if (! el.children.length || obj.getValue(el.children.length - 1)) {
+ if (! limit()) {
+ createElement('');
+ }
+ }
+
+ if (typeof(obj.options.onfocus) == 'function') {
+ obj.options.onfocus(el, obj, obj.getValue());
+ }
+
+ el.classList.add('jtags-focus');
+ }
+ }
+
+ var tagsBlur = function() {
+ if (el.classList.contains('jtags-focus')) {
+ if (search) {
+ search.close();
+ }
+
+ for (var i = 0; i < el.children.length - 1; i++) {
+ // Create label design
+ if (! obj.getValue(i)) {
+ el.removeChild(el.children[i]);
+ }
+ }
+
+ change();
+
+ el.classList.remove('jtags-focus');
+
+ if (typeof(obj.options.onblur) == 'function') {
+ obj.options.onblur(el, obj, obj.getValue());
+ }
+ }
+ }
+
+ var init = function() {
+ // Bind events
+ if ('touchend' in document.documentElement === true) {
+ el.addEventListener('touchend', tagsMouseUp);
+ } else {
+ el.addEventListener('mouseup', tagsMouseUp);
+ }
+
+ el.addEventListener('keydown', tagsKeyDown);
+ el.addEventListener('keyup', tagsKeyUp);
+ el.addEventListener('paste', tagsPaste);
+ el.addEventListener('focus', tagsFocus);
+ el.addEventListener('blur', tagsBlur);
+
+ // Editable
+ el.setAttribute('contenteditable', true);
+
+ // Prepare container
+ el.classList.add('jtags');
+
+ // Initial options
+ obj.setOptions(options);
+
+ if (typeof(obj.options.onload) == 'function') {
+ obj.options.onload(el, obj);
+ }
+
+ // Change methods
+ el.change = obj.setValue;
+
+ // Global generic value handler
+ el.val = function(val) {
+ if (val === undefined) {
+ return obj.getValue();
+ } else {
+ obj.setValue(val);
+ }
+ }
+
+ el.tags = obj;
+ }
+
+ init();
+
+ return obj;
+});
+
+jSuites.toolbar = (function(el, options) {
+ // New instance
+ var obj = { type:'toolbar' };
+ obj.options = {};
+
+ // Default configuration
+ var defaults = {
+ app: null,
+ container: false,
+ badge: false,
+ title: false,
+ responsive: false,
+ maxWidth: null,
+ bottom: true,
+ items: [],
+ }
+
+ // 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];
+ }
+ }
+
+ if (! el && options.app && options.app.el) {
+ el = document.createElement('div');
+ options.app.el.appendChild(el);
+ }
+
+ // Arrow
+ var toolbarArrow = document.createElement('div');
+ toolbarArrow.classList.add('jtoolbar-item');
+ toolbarArrow.classList.add('jtoolbar-arrow');
+
+ var toolbarFloating = document.createElement('div');
+ toolbarFloating.classList.add('jtoolbar-floating');
+ toolbarArrow.appendChild(toolbarFloating);
+
+ obj.selectItem = function(element) {
+ var elements = toolbarContent.children;
+ for (var i = 0; i < elements.length; i++) {
+ if (element != elements[i]) {
+ elements[i].classList.remove('jtoolbar-selected');
+ }
+ }
+ element.classList.add('jtoolbar-selected');
+ }
+
+ obj.hide = function() {
+ jSuites.animation.slideBottom(el, 0, function() {
+ el.style.display = 'none';
+ });
+ }
+
+ obj.show = function() {
+ el.style.display = '';
+ jSuites.animation.slideBottom(el, 1);
+ }
+
+ obj.get = function() {
+ return el;
+ }
+
+ obj.setBadge = function(index, value) {
+ toolbarContent.children[index].children[1].firstChild.innerHTML = value;
+ }
+
+ obj.destroy = function() {
+ toolbar.remove();
+ el.innerHTML = '';
+ }
+
+ obj.update = function(a, b) {
+ for (var i = 0; i < toolbarContent.children.length; i++) {
+ // Toolbar element
+ var toolbarItem = toolbarContent.children[i];
+ // State management
+ if (typeof(toolbarItem.updateState) == 'function') {
+ toolbarItem.updateState(el, obj, toolbarItem, a, b);
+ }
+ }
+ for (var i = 0; i < toolbarFloating.children.length; i++) {
+ // Toolbar element
+ var toolbarItem = toolbarFloating.children[i];
+ // State management
+ if (typeof(toolbarItem.updateState) == 'function') {
+ toolbarItem.updateState(el, obj, toolbarItem, a, b);
+ }
+ }
+ }
+
+ obj.create = function(items) {
+ // Reset anything in the toolbar
+ toolbarContent.innerHTML = '';
+ // Create elements in the toolbar
+ for (var i = 0; i < items.length; i++) {
+ var toolbarItem = document.createElement('div');
+ toolbarItem.classList.add('jtoolbar-item');
+
+ if (items[i].width) {
+ toolbarItem.style.width = parseInt(items[i].width) + 'px';
+ }
+
+ if (items[i].k) {
+ toolbarItem.k = items[i].k;
+ }
+
+ if (items[i].tooltip) {
+ toolbarItem.setAttribute('title', items[i].tooltip);
+ }
+
+ // Id
+ if (items[i].id) {
+ toolbarItem.setAttribute('id', items[i].id);
+ }
+
+ // Selected
+ if (items[i].updateState) {
+ toolbarItem.updateState = items[i].updateState;
+ }
+
+ if (items[i].active) {
+ toolbarItem.classList.add('jtoolbar-active');
+ }
+
+ if (items[i].disabled) {
+ toolbarItem.classList.add('jtoolbar-disabled');
+ }
+
+ if (items[i].type == 'select' || items[i].type == 'dropdown') {
+ jSuites.picker(toolbarItem, items[i]);
+ } else if (items[i].type == 'divisor') {
+ toolbarItem.classList.add('jtoolbar-divisor');
+ } else if (items[i].type == 'label') {
+ toolbarItem.classList.add('jtoolbar-label');
+ toolbarItem.innerHTML = items[i].content;
+ } else {
+ // Material icons
+ var toolbarIcon = document.createElement('i');
+ if (typeof(items[i].class) === 'undefined') {
+ toolbarIcon.classList.add('material-icons');
+ } else {
+ var c = items[i].class.split(' ');
+ for (var j = 0; j < c.length; j++) {
+ toolbarIcon.classList.add(c[j]);
+ }
+ }
+ toolbarIcon.innerHTML = items[i].content ? items[i].content : '';
+ toolbarItem.appendChild(toolbarIcon);
+
+ // Badge options
+ if (obj.options.badge == true) {
+ var toolbarBadge = document.createElement('div');
+ toolbarBadge.classList.add('jbadge');
+ var toolbarBadgeContent = document.createElement('div');
+ toolbarBadgeContent.innerHTML = items[i].badge ? items[i].badge : '';
+ toolbarBadge.appendChild(toolbarBadgeContent);
+ toolbarItem.appendChild(toolbarBadge);
+ }
+
+ // Title
+ if (items[i].title) {
+ if (obj.options.title == true) {
+ var toolbarTitle = document.createElement('span');
+ toolbarTitle.innerHTML = items[i].title;
+ toolbarItem.appendChild(toolbarTitle);
+ } else {
+ toolbarItem.setAttribute('title', items[i].title);
+ }
+ }
+
+ if (obj.options.app && items[i].route) {
+ // Route
+ toolbarItem.route = items[i].route;
+ // Onclick for route
+ toolbarItem.onclick = function() {
+ obj.options.app.pages(this.route);
+ }
+ // Create pages
+ obj.options.app.pages(items[i].route, {
+ toolbarItem: toolbarItem,
+ closed: true
+ });
+ }
+ }
+
+ if (items[i].onclick) {
+ toolbarItem.onclick = items[i].onclick.bind(items[i], el, obj, toolbarItem);
+ }
+
+ toolbarContent.appendChild(toolbarItem);
+ }
+
+ // Fits to the page
+ setTimeout(function() {
+ obj.refresh();
+ }, 0);
+ }
+
+ obj.open = function() {
+ toolbarArrow.classList.add('jtoolbar-arrow-selected');
+
+ var rectElement = el.getBoundingClientRect();
+ var rect = toolbarFloating.getBoundingClientRect();
+ if (rect.bottom > window.innerHeight || obj.options.bottom) {
+ toolbarFloating.style.bottom = '0';
+ } else {
+ toolbarFloating.style.removeProperty('bottom');
+ }
+
+ toolbarFloating.style.right = '0';
+
+ toolbarArrow.children[0].focus();
+ // Start tracking
+ jSuites.tracking(obj, true);
+ }
+
+ obj.close = function() {
+ toolbarArrow.classList.remove('jtoolbar-arrow-selected')
+ // End tracking
+ jSuites.tracking(obj, false);
+ }
+
+ obj.refresh = function() {
+ if (obj.options.responsive == true) {
+ // Width of the c
+ var rect = el.parentNode.getBoundingClientRect();
+ if (! obj.options.maxWidth) {
+ obj.options.maxWidth = rect.width;
+ }
+ // Available parent space
+ var available = parseInt(obj.options.maxWidth);
+ // Remove arrow
+ if (toolbarArrow.parentNode) {
+ toolbarArrow.parentNode.removeChild(toolbarArrow);
+ }
+ // Move all items to the toolbar
+ while (toolbarFloating.firstChild) {
+ toolbarContent.appendChild(toolbarFloating.firstChild);
+ }
+ // Toolbar is larger than the parent, move elements to the floating element
+ if (available < toolbarContent.offsetWidth) {
+ // Give space to the floating element
+ available -= 50;
+ // Move to the floating option
+ while (toolbarContent.lastChild && available < toolbarContent.offsetWidth) {
+ toolbarFloating.insertBefore(toolbarContent.lastChild, toolbarFloating.firstChild);
+ }
+ }
+ // Show arrow
+ if (toolbarFloating.children.length > 0) {
+ toolbarContent.appendChild(toolbarArrow);
+ }
+ }
+ }
+
+ obj.setReadonly = function(state) {
+ state = state ? 'add' : 'remove';
+ el.classList[state]('jtoolbar-disabled');
+ }
+
+ el.onclick = function(e) {
+ var element = jSuites.findElement(e.target, 'jtoolbar-item');
+ if (element) {
+ obj.selectItem(element);
+ }
+
+ if (e.target.classList.contains('jtoolbar-arrow')) {
+ obj.open();
+ }
+ }
+
+ window.addEventListener('resize', function() {
+ obj.refresh();
+ });
+
+ // Toolbar
+ el.classList.add('jtoolbar');
+ // Reset content
+ el.innerHTML = '';
+ // Container
+ if (obj.options.container == true) {
+ el.classList.add('jtoolbar-container');
+ }
+ // Content
+ var toolbarContent = document.createElement('div');
+ el.appendChild(toolbarContent);
+ // Special toolbar for mobile applications
+ if (obj.options.app) {
+ el.classList.add('jtoolbar-mobile');
+ }
+ // Create toolbar
+ obj.create(obj.options.items);
+ // Shortcut
+ el.toolbar = obj;
+
+ return obj;
+});
+
+jSuites.validations = (function() {
+ /**
+ * Options: Object,
+ * Properties:
+ * Constraint,
+ * Reference,
+ * Value
+ */
+
+ var isNumeric = function(num) {
+ return !isNaN(num) && num !== null && num !== '';
+ }
+
+ var numberCriterias = {
+ 'between': function(value, range) {
+ return value >= range[0] && value <= range[1];
+ },
+ 'not between': function(value, range) {
+ return value < range[0] || value > range[1];
+ },
+ '<': function(value, range) {
+ return value < range[0];
+ },
+ '<=': function(value, range) {
+ return value <= range[0];
+ },
+ '>': function(value, range) {
+ return value > range[0];
+ },
+ '>=': function(value, range) {
+ return value >= range[0];
+ },
+ '=': function(value, range) {
+ return value == range[0];
+ },
+ '!=': function(value, range) {
+ return value != range[0];
+ },
+ }
+
+ var dateCriterias = {
+ 'valid date': function() {
+ return true;
+ },
+ '=': function(value, range) {
+ return value === range[0];
+ },
+ '<': function(value, range) {
+ return value < range[0];
+ },
+ '<=': function(value, range) {
+ return value <= range[0];
+ },
+ '>': function(value, range) {
+ return value > range[0];
+ },
+ '>=': function(value, range) {
+ return value >= range[0];
+ },
+ 'between': function(value, range) {
+ return value >= range[0] && value <= range[1];
+ },
+ 'not between': function(value, range) {
+ return value < range[0] || value > range[1];
+ },
+ }
+
+ var textCriterias = {
+ 'contains': function(value, range) {
+ return value.includes(range[0]);
+ },
+ 'not contains': function(value, range) {
+ return !value.includes(range[0]);
+ },
+ 'begins with': function(value, range) {
+ return value.startsWith(range[0]);
+ },
+ 'ends with': function(value, range) {
+ return value.endsWith(range[0]);
+ },
+ '=': function(value, range) {
+ return value === range[0];
+ },
+ 'valid email': function(value) {
+ var pattern = new RegExp(/^[^\s@]+@[^\s@]+\.[^\s@]+$/);
+
+ return pattern.test(value);
+ },
+ 'valid url': function(value) {
+ var pattern = new RegExp(/(((https?:\/\/)|(www\.))[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|]+)/ig);
+
+ return pattern.test(value);
+ },
+ }
+
+ // Component router
+ var component = function(value, options) {
+ if (typeof(component[options.type]) === 'function') {
+ if (options.allowBlank && value === '') {
+ return true;
+ }
+
+ return component[options.type](value, options);
+ }
+ return null;
+ }
+
+ component.url = function() {
+ var pattern = new RegExp(/(((https?:\/\/)|(www\.))[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|]+)/ig);
+ return pattern.test(data) ? true : false;
+ }
+
+ component.email = function(data) {
+ var pattern = new RegExp(/^[^\s@]+@[^\s@]+\.[^\s@]+$/);
+ return data && pattern.test(data) ? true : false;
+ }
+
+ component.required = function(data) {
+ return data.trim() ? true : false;
+ }
+
+ component.exist = function(data, options) {
+ return !!data.toString();
+ }
+
+ component['not exist'] = function(data, options) {
+ return !data.toString();
+ }
+
+ component.empty = function(data) {
+ return !data.toString();
+ }
+
+ component.notEmpty = function(data) {
+ return !!data.toString();
+ }
+
+ component.number = function(data, options) {
+ if (! isNumeric(data)) {
+ return false;
+ }
+
+ if (!options || !options.criteria) {
+ return true;
+ }
+
+ if (!numberCriterias[options.criteria]) {
+ return false;
+ }
+
+ var values = options.value.map(function(num) {
+ return parseFloat(num);
+ })
+
+ return numberCriterias[options.criteria](data, values);
+ };
+
+ component.login = function(data) {
+ var pattern = new RegExp(/^[a-zA-Z0-9\_\-\.\s+]+$/);
+ return data && pattern.test(data) ? true : false;
+ }
+
+ component.list = function(data, options) {
+ var dataType = typeof data;
+ if (dataType !== 'string' && dataType !== 'number') {
+ return false;
+ }
+ if (typeof(options.value[0]) === 'string') {
+ var list = options.value[0].split(',');
+ } else {
+ var list = options.value[0];
+ }
+
+ var validOption = list.findIndex(function name(item) {
+ return item == data;
+ });
+
+ return validOption > -1;
+ }
+
+ component.date = function(data, options) {
+ if (new Date(data) == 'Invalid Date') {
+ return false;
+ }
+
+ if (!options || !options.criteria) {
+ return true;
+ }
+
+ if (!dateCriterias[options.criteria]) {
+ return false;
+ }
+
+ var values = options.value.map(function(date) {
+ return new Date(date).getTime();
+ });
+
+ return dateCriterias[options.criteria](new Date(data).getTime(), values);
+ }
+
+ component.text = function(data, options) {
+ if (typeof data !== 'string') {
+ return false;
+ }
+
+ if (!options || !options.criteria) {
+ return true;
+ }
+
+ if (!textCriterias[options.criteria]) {
+ return false;
+ }
+
+ return textCriterias[options.criteria](data, options.value);
+ }
+
+ component.textLength = function(data, options) {
+ data = data.toString();
+
+ return component.number(data.length, options);
+ }
+
+ return component;
+})();
+
+
+
+ return jSuites;
+
+})));
diff --git a/script/table_builder_libraries.bash b/script/table_builder_libraries.bash
new file mode 100755
index 00000000000..951dbd54d5e
--- /dev/null
+++ b/script/table_builder_libraries.bash
@@ -0,0 +1,97 @@
+#!/bin/bash
+
+# Script used for updating depencies for the table builder/editor feature.
+# Updates the JSpreadsheet and jSuites libraries to the latest available versions.
+
+# Get the directory of the script
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+
+# Construct paths relative to the script directory
+SCSS_VENDOR="$SCRIPT_DIR/../app/assets/stylesheets/common/table-builder/vendor/"
+JSPREADSHEET_VENDOR="$SCRIPT_DIR/../public/javascripts/jspreadsheet/"
+JSUITES_VENDOR="$SCRIPT_DIR/../public/javascripts/jsuites/"
+
+JSUITES_JS_URL="https://jsuites.net/v4/jsuites.js"
+JSPREADSHEET_JS_URL="https://bossanova.uk/jspreadsheet/v4/jexcel.js"
+
+JSUITES_CSS_URL="https://raw.githubusercontent.com/jsuites/jsuites/master/dist/jsuites.css"
+JSPREADSHEET_CSS_URL="https://bossanova.uk/jspreadsheet/v4/jexcel.css"
+
+JSUITES_CSS_FILE=jsuites.css
+JSUITES_SCSS_FILE=jsuites.scss
+JSUITES_SCSS_FILE_LOCATION=$SCSS_VENDOR$JSUITES_SCSS_FILE
+
+JSUITES_JS_FILE=jsuites.js
+JSUITES_NEW_JS_FILE=jsuites.js
+JSUITES_JS_FILE_LOCATION=$JSUITES_VENDOR$JSUITES_NEW_JS_FILE
+
+JSPREADSHEET_CSS_FILE=jexcel.css
+JSPREADSHEET_SCSS_FILE=jspreadsheet.scss
+JSPREADSHEET_SCSS_FILE_LOCATION=$SCSS_VENDOR$JSPREADSHEET_SCSS_FILE
+
+JSPREADSHEET_JS_FILE=jexcel.js
+JSPREADSHEET_NEW_JS_FILE=jspreadsheet.js
+JSPREADSHEET_JS_FILE_LOCATION=$JSPREADSHEET_VENDOR$JSPREADSHEET_NEW_JS_FILE
+
+
+
+
+# Remove all vendor related files:
+rm -r ${SCSS_VENDOR}*
+rm -r ${JSUITES_VENDOR}
+rm -r ${JSPREADSHEET_VENDOR}
+
+# Recreate vendor directory
+mkdir $JSUITES_VENDOR
+mkdir $JSPREADSHEET_VENDOR
+echo "Old vendor assets have been removed."
+
+
+# STYLESHEETS:
+# Add JSuite vendor file
+if test -f "$JSUITES_CSS_FILE"; then
+ echo "$JSUITES_CSS_FILE already exists."
+else
+ # Fetch jsuite stylesheet
+ wget $JSUITES_CSS_URL
+ echo "$JSUITES_CSS_FILE has been created in $(pwd)"
+ # Move jsuite stylesheet to vendor as a scss file
+ mv $JSUITES_CSS_FILE $JSUITES_SCSS_FILE_LOCATION
+ echo "$JSUITES_SCSS_FILE has been placed in the scss vendor directory"
+ # Scope styles to jexcel_container class
+ sed -i '' '1s/^/.jexcel_container {\n/' $JSUITES_SCSS_FILE_LOCATION
+ sed -i '' '$a\
+ }' $JSUITES_SCSS_FILE_LOCATION
+
+ # Remove conflicting animation classes
+ # TODO: Improve below code to handle nested code blocks
+fi
+
+# Add JSpreadsheet vendor file
+if test -f "$JSPREADSHEET_CSS_FILE"; then
+ echo "$JSPREADSHEET_CSS_FILE already exists."
+else
+ # Fetch jspreadsheet stylesheet
+ wget $JSPREADSHEET_CSS_URL
+ echo "$JSPREADSHEET_CSS_FILE has been created in $(pwd)"
+ # Move jspreadsheet stylesheet to vendor as a scss file
+ mv $JSPREADSHEET_CSS_FILE $JSPREADSHEET_SCSS_FILE_LOCATION
+fi
+
+# Apply prettier to vendor files
+yarn prettier --write $SCSS_VENDOR
+
+# JAVASCRIPTS:
+if test -f "$JSUITES_JS_FILE"; then
+ echo "$JSUITES_JS_FILE already exists."
+else
+ wget $JSUITES_JS_URL
+ mv $JSUITES_JS_FILE $JSUITES_JS_FILE_LOCATION
+fi
+
+if test -f "$JSPREADSHEET_JS_FILE"; then
+ echo "$JSPREADSHEET_JS_FILE already exists."
+else
+ wget $JSPREADSHEET_JS_URL
+ mv $JSPREADSHEET_JS_FILE $JSPREADSHEET_JS_FILE_LOCATION
+fi
diff --git a/spec/system/page_objects/modals/insert_table.rb b/spec/system/page_objects/modals/insert_table.rb
new file mode 100644
index 00000000000..9e12d462d22
--- /dev/null
+++ b/spec/system/page_objects/modals/insert_table.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+module PageObjects
+ module Modals
+ class InsertTable < PageObjects::Modals::Base
+ MODAL_SELECTOR = ".insert-table-modal"
+ SPREADSHEET_TABLE_SELECTOR = "#{MODAL_SELECTOR} .jexcel"
+
+ def click_insert_table
+ find("#{MODAL_SELECTOR} .btn-insert-table").click
+ end
+
+ def cancel
+ find("#{MODAL_SELECTOR} .d-modal-cancel").click
+ end
+
+ def click_edit_reason
+ find("#{MODAL_SELECTOR} .btn-edit-reason").click
+ end
+
+ def type_edit_reason(text)
+ find("#{MODAL_SELECTOR} .edit-reason input").send_keys(text)
+ end
+
+ def find_cell(row, col)
+ find("#{SPREADSHEET_TABLE_SELECTOR} tbody tr[data-y='#{row}'] td[data-x='#{col}']")
+ end
+
+ def select_cell(row, col)
+ find_cell(row, col).double_click
+ end
+
+ def type_in_cell(row, col, text)
+ select_cell(row, col)
+ cell = find_cell(row, col).find("textarea")
+ cell.send_keys(text, :return)
+ end
+
+ def has_content_in_cell?(row, col, content)
+ find_cell(row, col).text == content
+ end
+ end
+ end
+end
diff --git a/spec/system/table_builder_spec.rb b/spec/system/table_builder_spec.rb
new file mode 100644
index 00000000000..ff18795efd8
--- /dev/null
+++ b/spec/system/table_builder_spec.rb
@@ -0,0 +1,144 @@
+# frozen_string_literal: true
+
+describe "Table Builder", type: :system do
+ fab!(:user)
+ let(:composer) { PageObjects::Components::Composer.new }
+ let(:insert_table_modal) { PageObjects::Modals::InsertTable.new }
+ fab!(:topic) { Fabricate(:topic, user: user) }
+ fab!(:post1) { create_post(user: user, topic: topic, raw: <<~RAW) }
+ |Make | Model | Year|
+ |-------| ------- | ----|
+ |Toyota | Supra | 1998|
+ |Nissan | Skyline | 1999|
+ |Honda | S2000 | 2001|
+ RAW
+
+ let(:topic_page) { PageObjects::Pages::Topic.new }
+
+ before { sign_in(user) }
+
+ def normalize_value(content)
+ content.strip.gsub(/\s+/, " ").gsub(/\r\n/, "\n")
+ end
+
+ context "when creating a new table" do
+ it "should add table items created in spreadsheet to composer input" do
+ visit("/latest")
+ page.find("#create-topic").click
+ page.find(".toolbar-popup-menu-options").click
+ page.find(".select-kit-row[data-name='Insert Table']").click
+ insert_table_modal.type_in_cell(0, 0, "Item 1")
+ insert_table_modal.type_in_cell(0, 1, "Item 2")
+ insert_table_modal.type_in_cell(0, 2, "Item 3")
+ insert_table_modal.type_in_cell(0, 3, "Item 4")
+ insert_table_modal.click_insert_table
+
+ created_table = <<~TABLE
+ |Column 1 | Column 2 | Column 3 | Column 4|
+ |--- | --- | --- | ---|
+ |Item 1 | Item 2 | Item 3 | Item 4|
+ | | | | |
+ | | | | |
+ | | | | |
+ | | | | |
+ | | | | |
+ TABLE
+
+ expect(normalize_value(composer.composer_input.value)).to eq(normalize_value(created_table))
+ end
+
+ context "when cancelling table creation" do
+ it "should close the modal if there are no changes made" do
+ visit("/latest")
+ page.find("#create-topic").click
+ page.find(".toolbar-popup-menu-options").click
+ page.find(".select-kit-row[data-name='Insert Table']").click
+ insert_table_modal.cancel
+ expect(page).to have_no_css(".insert-table-modal")
+ end
+
+ it "should show a warning popup if there are unsaved changes" do
+ visit("/latest")
+ page.find("#create-topic").click
+ page.find(".toolbar-popup-menu-options").click
+ page.find(".select-kit-row[data-name='Insert Table']").click
+ insert_table_modal.type_in_cell(0, 0, "Item 1")
+ insert_table_modal.cancel
+ expect(page).to have_css(".dialog-container .dialog-content")
+ end
+ end
+ end
+
+ context "when editing a table" do
+ it "should prefill the spreadsheet with the markdown table items from the post" do
+ topic_page.visit_topic(topic)
+ topic_page.find(".btn-edit-table", visible: :all).click
+ expect(page).to have_selector(".insert-table-modal")
+
+ expected_table_content = [
+ %w[Toyota Supra 1998],
+ %w[Nissan Skyline 1999],
+ %w[Honda S2000 2001],
+ ]
+
+ expected_table_content.each_with_index do |row, row_index|
+ row.each_with_index do |content, col_index|
+ expect(insert_table_modal).to have_content_in_cell(row_index, col_index, content)
+ end
+ end
+ end
+
+ it "should update the post with the new table content" do
+ topic_page.visit_topic(topic)
+ topic_page.find(".btn-edit-table", visible: :all).click
+ expect(page).to have_selector(".insert-table-modal")
+ insert_table_modal.type_in_cell(1, 1, " GTR")
+ insert_table_modal.click_insert_table
+
+ updated_post = <<~RAW
+ |Make | Model | Year|
+ |-------| ------- | ----|
+ |Toyota | Supra | 1998|
+ |Nissan | Skyline | 1999|
+ |Honda | S2000 | 2001|
+ RAW
+
+ expect(normalize_value(post1.reload.raw)).to eq(normalize_value(updated_post))
+ end
+
+ context "when adding an edit reason" do
+ it "should add the edit reason to the edit history" do
+ edit_reason = "Updated Nissan model"
+
+ topic_page.visit_topic(topic)
+ topic_page.find(".btn-edit-table", visible: :all).click
+ expect(page).to have_selector(".insert-table-modal")
+ insert_table_modal.type_in_cell(1, 1, " GTR")
+ insert_table_modal.click_edit_reason
+ insert_table_modal.type_edit_reason(edit_reason)
+ insert_table_modal.click_insert_table
+ wait_for { post1.reload.edit_reason == edit_reason }
+ expect(post1.reload.edit_reason).to eq(edit_reason)
+ end
+ end
+
+ context "when cancelling table creation" do
+ it "should close the modal if there are no changes made" do
+ topic_page.visit_topic(topic)
+ topic_page.find(".btn-edit-table", visible: :all).click
+ expect(page).to have_selector(".insert-table-modal")
+ insert_table_modal.cancel
+ expect(page).to have_no_css(".insert-table-modal")
+ end
+
+ it "should show a warning popup if there are unsaved changes" do
+ topic_page.visit_topic(topic)
+ topic_page.find(".btn-edit-table", visible: :all).click
+ expect(page).to have_selector(".insert-table-modal")
+ insert_table_modal.type_in_cell(1, 1, " GTR")
+ insert_table_modal.cancel
+ expect(page).to have_css(".dialog-container .dialog-content")
+ end
+ end
+ end
+end