UNPKG

@progress/kendo-ui

Version:

This package is part of the [Kendo UI for jQuery](http://www.telerik.com/kendo-ui) suite.

5,965 lines 253 kB
'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

require('./mixins-RbnksPfz.js');
require('./kendo.ooxml.js');
require('./kendo.progressbar.js');
require('./kendo.core.js');
require('./html-sdnHcjkh.js');
require('./mixins-D385O08Q.js');
require('./kendo.toolbar.js');
require('./kendo.list.js');
require('./kendo.spreadsheet.common.js');
require('./kendo.binder.js');
require('./kendo.validator.js');
require('./kendo.icons.js');
require('./kendo.window.js');
require('./kendo.colorpicker.js');
require('./kendo.combobox.js');
require('./kendo.dropdownlist.js');
require('./kendo.dropdownbutton.js');
require('./kendo.popup.js');
require('./kendo.togglebutton.js');
require('./kendo.calendar.js');
require('./kendo.listview.js');
require('./kendo.dom.js');
require('./kendo.sortable.js');
require('./kendo.treeview.js');
require('./kendo.numerictextbox.js');
require('./kendo.datepicker.js');
require('./kendo.datetimepicker.js');
require('./kendo.data.js');
require('./kendo.data.odata.js');
require('./kendo.licensing.js');
require('@progress/kendo-licensing');
require('./kendo.data.xml.js');
require('@progress/kendo-ooxml');
require('@progress/kendo-drawing');
require('./kendo.color.js');
require('./kendo.splitbutton.js');
require('./kendo.html.button.js');
require('./kendo.html.base.js');
require('./kendo.html.icon.js');
require('@progress/kendo-svg-icons');
require('./kendo.button.menu.js');
require('./kendo.buttongroup.js');
require('./kendo.button.js');
require('./kendo.badge.js');
require('./kendo.menu.js');
require('./kendo.label.js');
require('./kendo.floatinglabel.js');
require('./kendo.actionsheet.js');
require('./kendo.actionsheet.view.js');
require('./dropdowns-loader-Bc4IELFi.js');
require('@progress/kendo-spreadsheet-common');
require('./kendo.draganddrop.js');
require('./kendo.userevents.js');
require('./kendo.slider.js');
require('./kendo.textbox.js');
require('./prefix-suffix-containers-BmDm564f.js');
require('./kendo.mobile.scroller.js');
require('./kendo.fx.js');
require('./kendo.virtuallist.js');
require('./valueMapper-q_OtZ-Tj.js');
require('./kendo.selectable.js');
require('./kendo.editable.js');
require('./kendo.checkbox.js');
require('./kendo.toggleinputbase.js');
require('./kendo.html.input.js');
require('./kendo.otpinput.js');
require('./kendo.upload.js');
require('./kendo.dateinput.js');
require('@progress/kendo-dateinputs-common');
require('./kendo.pager.js');
require('./kendo.treeview.draganddrop.js');
require('./kendo.timepicker.js');

(function(kendo) {
    var $ = kendo.jQuery;
    var ObservableObject = kendo.data.ObservableObject;

    var MESSAGES = kendo.spreadsheet.messages.dialogs = {
        apply: "Apply",
        save: "Save",
        cancel: "Cancel",
        remove: "Remove",
        retry: "Retry",
        revert: "Revert",
        okText: "OK",
        formatCellsDialog: {
            title: "Format",
            categories: {
                number: "Number",
                currency: "Currency",
                date: "Date"
            }
        },
        fontFamilyDialog: {
            title: "Font"
        },
        fontSizeDialog: {
            title: "Font size"
        },
        bordersDialog: {
            title: "Borders"
        },
        alignmentDialog: {
            title: "Alignment",
            buttons: {
                justifyLeft: "Align left",
                justifyCenter: "Center",
                justifyRight: "Align right",
                justifyFull: "Justify",
                alignTop: "Align top",
                alignMiddle: "Align middle",
                alignBottom: "Align bottom"
            }
        },
        mergeDialog: {
            title: "Merge cells",
            buttons: {
                mergeCells: "Merge all",
                mergeHorizontally: "Merge horizontally",
                mergeVertically: "Merge vertically",
                unmerge: "Unmerge"
            }
        },
        freezeDialog: {
            title: "Freeze panes",
            buttons: {
                freezePanes: "Freeze panes",
                freezeRows: "Freeze rows",
                freezeColumns: "Freeze columns",
                unfreeze: "Unfreeze panes"
            }
        },
        confirmationDialog: {
            text: "Are you sure you want to remove this sheet?",
            title: "Sheet remove"
        },
        validationDialog: {
            title: "Data Validation",
            hintMessage: "Please enter a valid {0} value {1}.",
            hintTitle: "Validation {0}",
            criteria: {
                any: "Any value",
                number: "Number",
                text: "Text",
                date: "Date",
                custom: "Custom Formula",
                list: "List"
            },
            comparers: {
                greaterThan: "greater than",
                lessThan: "less than",
                between: "between",
                notBetween: "not between",
                equalTo: "equal to",
                notEqualTo: "not equal to",
                greaterThanOrEqualTo: "greater than or equal to",
                lessThanOrEqualTo: "less than or equal to"
            },
            comparerMessages: {
                greaterThan: "greater than {0}",
                lessThan: "less than {0}",
                between: "between {0} and {1}",
                notBetween: "not between {0} and {1}",
                equalTo: "equal to {0}",
                notEqualTo: "not equal to {0}",
                greaterThanOrEqualTo: "greater than or equal to {0}",
                lessThanOrEqualTo: "less than or equal to {0}",
                custom: "that satisfies the formula: {0}"
            },
            labels: {
                criteria: "Criteria",
                comparer: "Comparer",
                min: "Min",
                max: "Max",
                value: "Value",
                start: "Start",
                end: "End",
                onInvalidData: "On invalid data",
                rejectInput: "Reject input",
                showWarning: "Show warning",
                showHint: "Show hint",
                hintTitle: "Hint title",
                hintMessage: "Hint message",
                ignoreBlank: "Ignore blank",
                showListButton: "Display button to show list",
                showCalendarButton: "Display button to show calendar"
            },
            placeholders: {
                typeTitle: "Type title",
                typeMessage: "Type message"
            }
        },
        exportAsDialog: {
            title: "Export...",
            defaultFileName: "Workbook",
            xlsx: {
                description: "Excel Workbook (.xlsx)"
            },
            pdf: {
                description: "Portable Document Format (.pdf)",
                area: {
                    workbook: "Entire Workbook",
                    sheet: "Active Sheet",
                    selection: "Selection"
                },
                paper: {
                    a2        : "A2 (420 mm × 594 mm)",
                    a3        : "A3 (297 mm x 420 mm)",
                    a4        : "A4 (210 mm x 297 mm)",
                    a5        : "A5 (148 mm x 210 mm)",
                    b3        : "B3 (353 mm × 500 mm)",
                    b4        : "B4 (250 mm x 353 mm)",
                    b5        : "B5 (176 mm x 250 mm)",
                    folio     : 'Folio (8.5" x 13")',
                    legal     : 'Legal (8.5" x 14")',
                    letter    : 'Letter (8.5" x 11")',
                    tabloid   : 'Tabloid (11" x 17")',
                    executive : 'Executive (7.25" x 10.5")'
                },
                margin: {
                    normal: "Normal",
                    narrow: "Narrow",
                    wide: "Wide"
                }
            },
            labels: {
                scale: "Scale",
                fit: "Fit to page",
                fileName: "File name",
                saveAsType: "Save as type",
                exportArea: "Export",
                paperSize: "Paper size",
                margins: "Margins",
                orientation: "Orientation",
                print: "Print",
                guidelines: "Guidelines",
                center: "Center",
                horizontally: "Horizontally",
                vertically: "Vertically"
            }
        },
        modifyMergedDialog: {
            errorMessage: "Cannot change part of a merged cell."
        },
        rangeDisabledDialog: {
            errorMessage: "Destination range contains disabled cells."
        },
        intersectsArrayDialog: {
            errorMessage: "You cannot alter part of an array"
        },
        incompatibleRangesDialog: {
            errorMessage: "Incompatible ranges"
        },
        noFillDirectionDialog: {
            errorMessage: "Cannot determine fill direction"
        },
        duplicateSheetNameDialog: {
            errorMessage: "Duplicate sheet name"
        },
        overflowDialog: {
            errorMessage: "Cannot paste, because the copy area and the paste area are not the same size and shape."
        },
        useKeyboardDialog: {
            title: "Copying and pasting",
            errorMessage: "These actions cannot be invoked through the menu. Please use the keyboard shortcuts instead:",
            labels: {
                forCopy: "for copy",
                forCut: "for cut",
                forPaste: "for paste"
            }
        },
        unsupportedSelectionDialog: {
            errorMessage: "That action cannot be performed on multiple selection."
        },
        linkDialog: {
            title: "Hyperlink",
            labels: {
                text: "Text",
                url: "Address",
                removeLink: "Remove link"
            }
        },
        sheetRenameDialog: {
            title: "Rename Sheet",
            labels: {
                text: "Rename Sheet",
                rename: "Rename"
            }
        },
        insertCommentDialog: {
            title: "Insert comment",
            labels: {
                comment: "Comment",
                removeComment: "Remove comment"
            }
        },
        insertImageDialog: {
            title: "Insert image",
            info: "Drag an image here, or click to select",
            typeError: "Please select a JPEG, PNG or GIF image"
        }
    };

    var registry = {};
    kendo.spreadsheet.dialogs = {
        register: function(name, dialogClass) {
            registry[name] = dialogClass;
        },
        registered: function(name) {
            return !!registry[name];
        },
        create: function(name, options) {
            var dialogClass = registry[name];

            if (dialogClass) {
                return new dialogClass(options);
            }
        }
    };

    var SpreadsheetDialog = kendo.spreadsheet.SpreadsheetDialog = kendo.Observable.extend({
        init: function(options) {
            kendo.Observable.fn.init.call(this, options);

            this.options = translate($.extend(true, {}, this.options, options));

            this.bind(this.events, options);
        },
        events: [
            "close",
            "activate"
        ],
        options: {
            autoFocus: true
        },
        dialog: function() {
            if (!this._dialog) {
                var options = {
                    autoFocus: false,
                    scrollable: false,
                    resizable: false,
                    modal: true,
                    visible: false,
                    width: this.options.width || 320,
                    title: this.options.title,
                    open: function() {
                        this.center();
                    },
                    close: this._onDialogClose.bind(this),
                    activate: this._onDialogActivate.bind(this),
                    deactivate: this._onDialogDeactivate.bind(this)
                };
                this._dialog = $("<div class='k-spreadsheet-window k-action-window k-popup-edit-form' />")
                    .addClass(this.options.className || "")
                    .append(kendo.template(this.options.template)({
                        messages: kendo.spreadsheet.messages.dialogs || MESSAGES,
                        ns: kendo.ns,

                        // this is for ImportErrorDialog.  ugly that
                        // we need this line here, but I couldn't
                        // figure out a better way
                        errors: this.options.errors
                    }))
                    .kendoWindow(options)
                    .data("kendoWindow");
            }

            return this._dialog;
        },
        _onDialogClose: function() {
            this.trigger("close", {
                action: this._action
            });
        },
        _onDialogActivate: function() {
            this.trigger("activate");
        },
        _onDialogDeactivate: function() {
            this.trigger("deactivate");
            this.destroy();
        },
        destroy: function() {
            if (this._dialog) {
                this._dialog.destroy();
                this._dialog = null;
            }
        },
        open: function() {
            this.dialog().open();
            this.dialog().element.find(".k-button-solid-primary").trigger("focus");
        },
        apply: function() {
            this.close();
        },
        close: function() {
            this._action = "close";

            this.dialog().close();
        }
    });

    function formattedValue(value, format) {
        return kendo.spreadsheet.formatting.text(value, format);
    }

    var FormatCellsViewModel = kendo.spreadsheet.FormatCellsViewModel = ObservableObject.extend({
        init: function(options) {
            ObservableObject.fn.init.call(this, options);

            this.useCategory(this.category);
        },
        useCategory: function(category) {
            var type = category && category.type || "number";
            var formatCurrency = type == "currency";

            this.category = category;

            this.set("showCurrencyFilter", formatCurrency && this.currencies.length > 1);

            if (!formatCurrency) {
                this.set("formats", this.allFormats[type + "Formats"]);
            } else {
                this.currency(this.currencies[0]);
            }

            this.useFirstFormat();
        },
        useFirstFormat: function() {
            if (this.formats.length) {
                this.set("format", this.formats[0].value);
            }
        },
        currency: function(currency) {
            if (currency !== undefined) {
                this._currency = currency;

                var info = currency.value;
                var formats = [
                    { currency: info, decimals: true },
                    { currency: info, decimals: true, iso: true },
                    { currency: info, decimals: false }
                ];

                formats = formats.map(function(format) {
                    format = FormatCellsViewModel.convert.currency(format);

                    return { value: format, name: formattedValue(1000, format) };
                });

                this.set("formats", formats);

                this.useFirstFormat();
            }

            return this._currency || this.currencies[0];
        },
        categoryFilter: function(category) {
            if (category !== undefined) {
                this.useCategory(category);
            }

            return this.category;
        },
        preview: function() {
            var format = this.get("format");
            var value = this.value || 0;

            if (format && format.length) {
                return formattedValue(value, format);
            } else {
                return value;
            }
        }
    });

    FormatCellsViewModel.convert = {
        currency: function(options) {
            function repeat(token, n) {
                return new Array(n+1).join(token);
            }

            // convert culture info to spreadsheet format
            var info = options.currency;
            var format = info.pattern[1];

            if (options.decimals) {
                format = format.replace(/n/g, "n" + info["."] + repeat("0", info.decimals));
            }

            if (options.iso) {
                format = '"' + info.abbr + '" ' + format.replace(/\s*\$\s*/g, "");
            } else {
                format = format.replace(/\$/g, JSON.stringify(info.symbol));
            }

            format = format.replace(/n/g, "?");

            return format;
        },
        date: function(format) {
            if ((/T|Z/).test(format)) {
                return "";
            }

            return format.toLowerCase().replace(/tt/g, "AM/PM").replace(/'/g, '"');
        }
    };

    function uniqueBy(field, array) {
        var result = [];
        var values = [];

        for (var i = 0; i < array.length; i++) {
            if ($.inArray(array[i][field], values) == -1) {
                result.push(array[i]);
                values.push(array[i][field]);
            }
        }

        return result;
    }

    var FormatCellsDialog = SpreadsheetDialog.extend({
        init: function(options) {
            var messages = kendo.spreadsheet.messages.dialogs.formatCellsDialog || MESSAGES;
            var defaultOptions = {
                title: messages.title,
                categories: [
                    { type: "number", name: messages.categories.number },
                    { type: "currency", name: messages.categories.currency },
                    { type: "date", name: messages.categories.date }
                ]
            };

            SpreadsheetDialog.fn.init.call(this, $.extend(defaultOptions, options));

            this._generateFormats();
        },
        options: {
            className: "k-spreadsheet-format-cells",
            template: (data) => {
                let $kendoOutput;
                $kendoOutput = `<div class='k-edit-form-container'><div class='k-root-tabs' data-${kendo.htmlEncode(data.ns)}role='tabstrip' data-${kendo.htmlEncode(data.ns)}text-field='name' data-${kendo.htmlEncode(data.ns)}bind='source: categories, value: categoryFilter' data-${kendo.htmlEncode(data.ns)}animation='false'></div>
    <div class='k-spreadsheet-preview' data-${kendo.htmlEncode(data.ns)}bind='text: preview'></div><select data-${kendo.htmlEncode(data.ns)}role='dropdownlist' class='k-format-filter' data-${kendo.htmlEncode(data.ns)}text-field='description' data-${kendo.htmlEncode(data.ns)}value-field='value.name' data-${kendo.htmlEncode(data.ns)}bind='visible: showCurrencyFilter, value: currency, source: currencies'></select><ul data-${kendo.htmlEncode(data.ns)}role='staticlist' tabindex='0' id='formats-list' aria-label='formats list' class='k-list k-reset' bind:data-${kendo.htmlEncode(data.ns)}template='formatItemTemplate' data-${kendo.htmlEncode(data.ns)}value-primitive='true' data-${kendo.htmlEncode(data.ns)}value-field='value' data-${kendo.htmlEncode(data.ns)}bind='source: formats, value: format'></ul>
    <div class='k-actions'><button class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-primary' data-${kendo.htmlEncode(data.ns)}bind='click: apply'><span class='k-button-text'>${kendo.htmlEncode(data.messages.apply)}</span></button><button class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-base' data-${kendo.htmlEncode(data.ns)}bind='click: close'><span class='k-button-text'>${kendo.htmlEncode(data.messages.cancel)}</span></button></div>
</div>`;
                return $kendoOutput;
            }
        },
        _generateFormats: function() {
            var options = this.options;

            if (!options.currencies) {
                options.currencies = FormatCellsDialog.currenciesFrom(kendo.cultures);
            }

            if (!options.numberFormats) {
                options.numberFormats = [
                    { value: "#.00%", name: "100.00%" },
                    { value: "#%", name: "100%" },
                    { value: "#.00", name: "1024.00" },
                    { value: "#,###.00", name: "1,024.00" }
                ];
            }

            if (!options.dateFormats) {
                var calendarPatterns = kendo.cultures.current.calendars.standard.patterns;

                options.dateFormats = uniqueBy("value", $.map(calendarPatterns, function(format) {
                    format = FormatCellsViewModel.convert.date(format);

                    if (!format) {
                        return;
                    }

                    return { value: format, name: formattedValue(34567.7678, format) };
                }));
            }
        },
        open: function(range) {
            var options = this.options;
            var value = range.value();
            var categories = options.categories.slice(0);
            var element;

            this.viewModel = new FormatCellsViewModel({
                currencies: options.currencies.slice(0),
                allFormats: {
                    numberFormats: options.numberFormats.slice(0),
                    dateFormats: options.dateFormats.slice(0)
                },
                categories: categories,
                format: range.format(),
                category: value instanceof Date ? categories[2] : categories[0],
                apply: this.apply.bind(this),
                close: this.close.bind(this),
                value: value,
                formatItemTemplate: (data) => kendo.htmlEncode(data.name)
            });

            SpreadsheetDialog.fn.open.call(this);

            element = this.dialog().element;

            kendo.bind(element, this.viewModel);

            var currencyFilter = element.find("select.k-format-filter").data("kendoDropDownList");

            if (options.currencies.length > 10) {
                currencyFilter.setOptions({ filter: "contains" });
            }

            element.find(kendo.roleSelector("staticlist")).parent().addClass("k-list-wrapper");

            element.find(".k-tabstrip-item").attr("aria-controls", "formats-list");
        },
        apply: function() {
            var format = this.viewModel.format;

            SpreadsheetDialog.fn.apply.call(this);

            this.trigger("action", {
                command: "PropertyChangeCommand",
                options: {
                    property: "format",
                    value: format
                }
            });
        }
    });

    FormatCellsDialog.currenciesFrom = function (cultures) {
        return uniqueBy("description", $.map(cultures, function(culture, name) {
            if (!(/-/).test(name)) {
                return;
            }

            var currency = culture.numberFormat.currency;
            var description = kendo.format(
                "{0} ({1}, {2})",
                currency.name,
                currency.abbr,
                currency.symbol
            );

            return { description: description, value: currency };
        }));
    };

    kendo.spreadsheet.dialogs.register("formatCells", FormatCellsDialog);

    kendo.spreadsheet.dialogs.FormatCellsDialog = FormatCellsDialog;

    var MessageDialog = SpreadsheetDialog.extend({
        options: {
            className: "k-spreadsheet-message",
            title: "",
            messageId: "",
            text: "",
            template: (data) => {
                let $kendoOutput;
                $kendoOutput = `<div class='k-spreadsheet-message-content' data-${kendo.htmlEncode(data.ns)}bind='text: text'></div>
<div class='k-actions'><button class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-primary'
        data-${kendo.htmlEncode(data.ns)}bind='click: close'><span
            class='k-button-text'>${data.messages.okText}</span></button></div>`;
                return $kendoOutput;
            }
        },
        open: function() {
            SpreadsheetDialog.fn.open.call(this);

            var options = this.options;
            var text = options.text;

            if (options.messageId) {
                text = kendo.getter(options.messageId, true)(kendo.spreadsheet.messages.dialogs);
            }

            kendo.bind(this.dialog().element, {
                text: text,
                close: this.close.bind(this)
            });
        }
    });

    kendo.spreadsheet.dialogs.register("message", MessageDialog);

    var ConfirmationDialog = SpreadsheetDialog.extend({
        init: function(options) {
            var messages = kendo.spreadsheet.messages.dialogs.confirmationDialog || MESSAGES;
            var defaultOptions = {
                title: messages.title,
                text: messages.text
            };

            SpreadsheetDialog.fn.init.call(this, $.extend(defaultOptions, options));
        },
        options: {
            className: "k-spreadsheet-message",
            messageId: "",
            template: (data) => {
                let $kendoOutput;
                $kendoOutput = `<div class='k-spreadsheet-message-content' data-${kendo.htmlEncode(data.ns)}bind='text: text'></div>
<div class='k-actions'><button class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-primary'
        data-${kendo.htmlEncode(data.ns)}bind='click: confirm'><span
            class='k-button-text'>${data.messages.okText}</span></button><button
        class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-base'
        data-${kendo.htmlEncode(data.ns)}bind='click: cancel'><span
            class='k-button-text'>${data.messages.cancel}</span></button></div>`;
                return $kendoOutput;
            }
        },
        open: function() {
            SpreadsheetDialog.fn.open.call(this);

            var options = this.options;
            var text = options.text;

            if (options.messageId) {
                text = kendo.getter(options.messageId, true)(kendo.spreadsheet.messages.dialogs);
            }

            kendo.bind(this.dialog().element, {
                text: text,
                confirm: this.confirm.bind(this),
                cancel: this.close.bind(this)
            });
        },
        isConfirmed: function() {
            return this._confirmed;
        },
        confirm: function() {
            this._confirmed = true;
            this.close();
        }
    });

    kendo.spreadsheet.dialogs.register("confirmation", ConfirmationDialog);

    var ValidationErrorDialog = SpreadsheetDialog.extend({
        options: {
            className: "k-spreadsheet-message",
            title: "",
            messageId: "",
            text: "",
            template: (data) => {
                let $kendoOutput;
                $kendoOutput = `<div class='k-spreadsheet-message-content' data-${kendo.htmlEncode(data.ns)}bind='text: text'></div><div class='k-actions'><button class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-primary' data-${kendo.htmlEncode(data.ns)}bind='click: retry'><span class='k-button-text'>${data.messages.retry}</span></button><button class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-base' data-${kendo.htmlEncode(data.ns)}bind='click: cancel'><span class='k-button-text'>${data.messages.cancel}</span></button></div>`;
                return $kendoOutput;
            }
        },
        open: function() {
            SpreadsheetDialog.fn.open.call(this);

            var options = this.options;
            var text = options.text;

            if (options.messageId) {
                text = kendo.getter(options.messageId, true)(kendo.spreadsheet.messages.dialogs);
            }

            kendo.bind(this.dialog().element, {
                text: text,
                retry: this.retry.bind(this),
                cancel: this.close.bind(this)
            });
        },
        retry: function() {
            this._retry = true;
            this.close();
        }
    });

    kendo.spreadsheet.dialogs.register("validationError", ValidationErrorDialog);

    var FontFamilyDialog = SpreadsheetDialog.extend({
        init: function(options) {
            var messages = kendo.spreadsheet.messages.dialogs.fontFamilyDialog || MESSAGES;

            SpreadsheetDialog.fn.init.call(this, $.extend({ title: messages.title }, options));

            this._list();
        },
        options: {
            template: () => "<ul class='k-list k-reset'></ul>"
        },
        _list: function() {
            var ul = this.dialog().element.find("ul");
            var fonts = this.options.options;
            var defaultFont = this.options.default;

            this.list = new kendo.ui.StaticList(ul, {
                dataSource: new kendo.data.DataSource({ data: fonts }),
                template: (data) => kendo.htmlEncode(data),
                value: defaultFont,
                change: this.apply.bind(this)
            });

            this.list.dataSource.fetch();
        },
        apply: function(e) {
            SpreadsheetDialog.fn.apply.call(this);

            this.trigger("action", {
                command: "PropertyChangeCommand",
                options: {
                    property: "fontFamily",
                    value: e.sender.value()[0]
                }
            });
        }
    });

    kendo.spreadsheet.dialogs.register("fontFamily", FontFamilyDialog);

    var FontSizeDialog = SpreadsheetDialog.extend({
        init: function(options) {
            var messages = kendo.spreadsheet.messages.dialogs.fontSizeDialog || MESSAGES;

            SpreadsheetDialog.fn.init.call(this, $.extend({ title: messages.title }, options));

            this._list();
        },
        options: {
            template: () => "<ul class='k-list k-reset'></ul>"
        },
        _list: function() {
            var ul = this.dialog().element.find("ul");
            var sizes = this.options.options;
            var defaultSize = this.options.default;

            this.list = new kendo.ui.StaticList(ul, {
                dataSource: new kendo.data.DataSource({ data: sizes }),
                template: (data) => kendo.htmlEncode(data),
                value: defaultSize,
                change: this.apply.bind(this)
            });

            this.list.dataSource.fetch();
        },
        apply: function(e) {
            SpreadsheetDialog.fn.apply.call(this);

            this.trigger("action", {
                command: "PropertyChangeCommand",
                options: {
                    property: "fontSize",
                    value: kendo.parseInt(e.sender.value()[0])
                }
            });
        }
    });

    kendo.spreadsheet.dialogs.register("fontSize", FontSizeDialog);

    var BordersDialog = SpreadsheetDialog.extend({
        init: function(options) {
            var messages = kendo.spreadsheet.messages.dialogs.bordersDialog || MESSAGES;

            SpreadsheetDialog.fn.init.call(this, $.extend({ title: messages.title }, options));

            this.element = this.dialog().element;
            this._borderPalette();

            this.viewModel = kendo.observable({
                apply: this.apply.bind(this),
                close: this.close.bind(this)
            });

            kendo.bind(this.element.find(".k-actions"), this.viewModel);
        },
        options: {
            template: (data) => {
                let $kendoOutput;
                $kendoOutput = `<div></div>
<div class='k-actions'><button class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-primary'
        data-${kendo.htmlEncode(data.ns)}bind='click: apply'><span
            class='k-button-text'>${kendo.htmlEncode(data.messages.apply)}</span></button><button
        class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-base'
        data-${kendo.htmlEncode(data.ns)}bind='click: close'><span
            class='k-button-text'>${kendo.htmlEncode(data.messages.cancel)}</span></button></div>`;
                return $kendoOutput;
            }
        },
        apply: function() {
            SpreadsheetDialog.fn.apply.call(this);

            var state = this.borderPalette.value();

            if (state.color && state.type) {
                this.trigger("action", {
                    command: "BorderChangeCommand",
                    options: {
                        border: state.type,
                        style: { size: 1, color: state.color }
                    }
                });
            }
        },
        _borderPalette: function() {
            var element = this.dialog().element.find("div").first();

            this.borderPalette = new kendo.spreadsheet.BorderPalette(element, {
                change: this.value.bind(this)
            });
        },
        value: function(state) {
            if (state === undefined) {
                return this._state;
            } else {
                this._state = state;
            }
        }
    });

    kendo.spreadsheet.dialogs.register("borders", BordersDialog);

    var ColorChooser = SpreadsheetDialog.extend({
        init: function(options) {
            SpreadsheetDialog.fn.init.call(this, options);

            this.element = this.dialog().element;
            this.property = options.property;
            this.options.title = options.title;

            this.viewModel = kendo.observable({
                apply: this.apply.bind(this),
                close: this.close.bind(this)
            });

            kendo.bind(this.element.find(".k-actions"), this.viewModel);
        },
        options: {
            template: (data) => {
                let $kendoOutput;
                $kendoOutput = `<div></div>
<div class='k-actions'><button class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-primary'
        data-${kendo.htmlEncode(data.ns)}bind='click: apply'><span
            class='k-button-text'>${kendo.htmlEncode(data.messages.apply)}</span></button><button
        class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-base'
        data-${kendo.htmlEncode(data.ns)}bind='click: close'><span
            class='k-button-text'>${kendo.htmlEncode(data.messages.cancel)}</span></button></div>`;
                return $kendoOutput;
            }
        },
        apply: function() {
            SpreadsheetDialog.fn.apply.call(this);

            this.trigger("action", {
                command: "PropertyChangeCommand",
                options: {
                    property: this.property,
                    value: this.value()
                }
            });
        },
        value: function(e) {
            if (e === undefined) {
                return this._value;
            } else {
                this._value = e.value;
            }
        }
    });

    var ColorPickerDialog = ColorChooser.extend({
        init: function(options) {
            options.width = 177;
            ColorChooser.fn.init.call(this, options);
            this._colorPalette();
        },
        _colorPalette: function() {
            var element = this.dialog().element.find("div").first();
            this.colorPalette = element.kendoColorPalette({
                palette: [ //metro palette
                    "#ffffff", "#000000", "#d6ecff", "#4e5b6f", "#7fd13b", "#ea157a", "#feb80a", "#00addc", "#738ac8", "#1ab39f",
                    "#f2f2f2", "#7f7f7f", "#a7d6ff", "#d9dde4", "#e5f5d7", "#fad0e4", "#fef0cd", "#c5f2ff", "#e2e7f4", "#c9f7f1",
                    "#d8d8d8", "#595959", "#60b5ff", "#b3bcca", "#cbecb0", "#f6a1c9", "#fee29c", "#8be6ff", "#c7d0e9", "#94efe3",
                    "#bfbfbf", "#3f3f3f", "#007dea", "#8d9baf", "#b2e389", "#f272af", "#fed46b", "#51d9ff", "#aab8de", "#5fe7d5",
                    "#a5a5a5", "#262626", "#003e75", "#3a4453", "#5ea226", "#af0f5b", "#c58c00", "#0081a5", "#425ea9", "#138677",
                    "#7f7f7f", "#0c0c0c", "#00192e", "#272d37", "#3f6c19", "#750a3d", "#835d00", "#00566e", "#2c3f71", "#0c594f"
                ],
                change: this.value.bind(this)
            }).data("kendoColorPalette");
        }
    });

    kendo.spreadsheet.dialogs.register("colorPicker", ColorPickerDialog);

    var CustomColorDialog = ColorChooser.extend({
        init: function(options) {
            options.width = 268;
            ColorChooser.fn.init.call(this, options);
            this.dialog().setOptions({ animation: false });
            this.dialog().one("activate", this._colorPicker.bind(this));
        },
        _colorPicker: function() {
            var element = this.dialog().element.find("div").first();
            this.colorPicker = element.kendoFlatColorPicker({
                change: this.value.bind(this)
            }).data("kendoFlatColorPicker");
        }
    });

    kendo.spreadsheet.dialogs.register("customColor", CustomColorDialog);

    var AlignmentDialog = SpreadsheetDialog.extend({
        init: function(options) {
            var messages = kendo.spreadsheet.messages.dialogs.alignmentDialog || MESSAGES;
            var defaultOptions = {
                title: messages.title,
                buttons: [
                    { property: "textAlign",     value: "left",    iconClass: "align-left",   text: messages.buttons.justifyLeft },
                    { property: "textAlign",     value: "center",  iconClass: "align-center", text: messages.buttons.justifyCenter },
                    { property: "textAlign",     value: "right",   iconClass: "align-right",  text: messages.buttons.justifyRight },
                    { property: "textAlign",     value: "justify", iconClass: "align-justify",   text: messages.buttons.justifyFull },
                    { property: "verticalAlign", value: "top",     iconClass: "align-top",      text: messages.buttons.alignTop },
                    { property: "verticalAlign", value: "center",  iconClass: "align-middle",   text: messages.buttons.alignMiddle },
                    { property: "verticalAlign", value: "bottom",  iconClass: "align-bottom",   text: messages.buttons.alignBottom }
                ]
            };

            SpreadsheetDialog.fn.init.call(this, $.extend(defaultOptions, options));

            this._list();
        },
        options: {
            template: () => "<ul class='k-list k-reset'></ul>"
        },
        _list: function() {
            var ul = this.dialog().element.find("ul");

            this.list = new kendo.ui.StaticList(ul, {
                dataSource: new kendo.data.DataSource({ data: this.options.buttons }),
                template: (data) => `<a role='button' title='${data.text}' data-property='${data.property}' data-value='${data.value}'>
                                ${kendo.ui.icon(data.iconClass)} ${data.text}
                           </a>`,
                change: this.apply.bind(this)
            });

            this.list.dataSource.fetch();
        },
        apply: function(e) {
            var dataItem = e.sender.value()[0];
            SpreadsheetDialog.fn.apply.call(this);

            this.trigger("action", {
                command: "PropertyChangeCommand",
                options: {
                    property: dataItem.property,
                    value: dataItem.value
                }
            });
        }
    });

    kendo.spreadsheet.dialogs.register("alignment", AlignmentDialog);

    var MergeDialog = SpreadsheetDialog.extend({
        init: function(options) {
            var messages = kendo.spreadsheet.messages.dialogs.mergeDialog || MESSAGES;
            var defaultOptions = {
                title: messages.title,
                buttons: [
                    { value: "cells",        iconClass: "cells-merge",        text: messages.buttons.mergeCells },
                    { value: "horizontally", iconClass: "cells-merge-horizontally", text: messages.buttons.mergeHorizontally },
                    { value: "vertically",   iconClass: "cells-merge-vertically",   text: messages.buttons.mergeVertically },
                    { value: "unmerge",      iconClass: "table-unmerge",      text: messages.buttons.unmerge }
                ]
            };

            SpreadsheetDialog.fn.init.call(this, $.extend(defaultOptions, options));

            this._list();
        },
        options: {
            template: () => "<ul class='k-list k-reset'></ul>"
        },
        _list: function() {
            var ul = this.dialog().element.find("ul");

            this.list = new kendo.ui.StaticList(ul, {
                dataSource: new kendo.data.DataSource({ data: this.options.buttons }),
                template: (data) => `<a role='button' title='${data.text}' data-value='${data.value}'>` +
                            `${kendo.ui.icon(data.iconClass)}${data.text}` +
                          "</a>",
                change: this.apply.bind(this)
            });

            this.list.dataSource.fetch();
        },
        apply: function(e) {
            var dataItem = e.sender.value()[0];
            SpreadsheetDialog.fn.apply.call(this);

            this.trigger("action", {
                command: "MergeCellCommand",
                options: {
                    value: dataItem.value
                }
            });
        }
    });

    kendo.spreadsheet.dialogs.register("merge", MergeDialog);

    var FreezeDialog = SpreadsheetDialog.extend({
        init: function(options) {
            var messages = kendo.spreadsheet.messages.dialogs.freezeDialog || MESSAGES;
            var defaultOptions = {
                title: messages.title,
                buttons: [
                    { value: "panes",    iconClass: "pane-freeze",  text: messages.buttons.freezePanes },
                    { value: "rows",     iconClass: "row-freeze",    text: messages.buttons.freezeRows },
                    { value: "columns",  iconClass: "column-freeze",    text: messages.buttons.freezeColumns },
                    { value: "unfreeze", iconClass: "table-unmerge", text: messages.buttons.unfreeze }
                ]
            };

            SpreadsheetDialog.fn.init.call(this, $.extend(defaultOptions, options));

            this._list();
        },
        options: {
            template: () => "<ul class='k-list k-reset'></ul>"
        },
        _list: function() {
            var ul = this.dialog().element.find("ul");

            this.list = new kendo.ui.StaticList(ul, {
                dataSource: new kendo.data.DataSource({ data: this.options.buttons }),
                template: (data) => `<a role='button' title='${data.text}' data-value='${data.value}'>` +
                            `${kendo.ui.icon(data.iconClass)}${data.text}` +
                          "</a>",
                change: this.apply.bind(this)
            });

            this.list.dataSource.fetch();
        },
        apply: function(e) {
            var dataItem = e.sender.value()[0];
            SpreadsheetDialog.fn.apply.call(this);

            this.trigger("action", {
                command: "FreezePanesCommand",
                options: {
                    value: dataItem.value
                }
            });
        }
    });

    kendo.spreadsheet.dialogs.register("freeze", FreezeDialog);

    var ValidationViewModel = kendo.spreadsheet.ValidationCellsViewModel = ObservableObject.extend({
        init: function(options) {
            ObservableObject.fn.init.call(this, options);

            this.bind("change", (function(e) {

                if (e.field === "criterion") {
                    this.reset();

                    if (this.criterion === "custom" || this.criterion === "list") {
                        this.setHintMessageTemplate();
                    }
                }

                if (e.field === "comparer") {
                    this.setHintMessageTemplate();
                }

                if ((e.field == "hintMessage" || e.field == "hintTitle") && !this._mute) {
                    this.shouldBuild = false;
                }

                if ((e.field == "from" || e.field == "to" || e.field == "hintMessageTemplate" || e.field == "type") && this.shouldBuild) {
                    this.buildMessages();
                }
            }).bind(this));

            this.reset();
        },
        buildMessages: function() {
            this._mute = true;
            this.set("hintTitle", this.hintTitleTemplate ? kendo.format(this.hintTitleTemplate, this.type) : "");
            this.set("hintMessage", this.hintMessageTemplate ? kendo.format(this.hintMessageTemplate, this.from, this.to) : "");
            this._mute = false;
        },
        reset: function() {
            this.setComparers();
            this.set("comparer", this.comparers[0].type);
            this.set("from", null);
            this.set("to", null);

            this.set("useCustomMessages", false);

            this.shouldBuild = true;

            this.hintTitleTemplate = this.defaultHintTitle;
            this.buildMessages();
        },
        //TODO: refactor
        setComparers: function() {
            var all = this.defaultComparers;
            var comparers = [];

            if (this.criterion === "text") {
                var text_comparers = ["equalTo", "notEqualTo"];
                for (var idx = 0; idx < all.length; idx++) {
                    if (text_comparers[0] == all[idx].type) {
                        comparers.push(all[idx]);
                        text_comparers.shift();
                    }
                }
            } else {
                comparers = all.slice();
            }

            this.set("comparers", comparers);
        },
        setHintMessageTemplate: function() {
           if (this.criterion !== "custom" && this.criterion !== "list") {
               this.set("hintMessageTemplate", kendo.format(this.defaultHintMessage, this.criterion, this.comparerMessages[this.comparer]));
           } else {
               this.set("hintMessageTemplate", "");
               this.set("hintMessage", "");
           }
        },
        isAny: function() {
            return this.get("criterion") === "any";
        },
        isNumber: function() {
            return this.get("criterion") === "number";
        },
        showToForNumber: function() {
            return this.showTo() && this.isNumber();
        },
        showToForDate: function() {
            return this.showTo() && this.isDate();
        },
        isText: function() {
            return this.get("criterion") === "text";
        },
        isDate: function() {
            return this.get("criterion") === "date";
        },
        isList: function() {
            return this.get("criterion") === "list";
        },
        isCustom: function() {
            return this.get("criterion") === "custom";
        },
        showRemove: function() {
            return this.get("hasValidation");
        },
        showTo: function() {
            return this.get("comparer") == "between" || this.get("comparer") == "notBetween";
        },
        update: function(validation) {
            this.set("hasValidation", !!validation);

            if (validation) {
                this.fromValidationObject(validation);
            }
        },
        fromValidationObject: function(validation) {
            this.set("criterion", validation.dataType);
            this.set("comparer", validation.comparerType);
            this.set("from", validation.from);
            this.set("to", validation.to);
            this.set("type", validation.type);
            this.set("ignoreBlank", validation.allowNulls);
            this.set("showButton", validation.showButton);

            if (validation.messageTemplate || validation.titleTemplate) {
                this.hintMessageTemplate = validation.messageTemplate;
                this.hintMessage = validation.messageTemplate;
                this.hintTitleTemplate = validation.titleTemplate;
                this.hintTitle = validation.titleTemplate;
                this.useCustomMessages = true;
                this.buildMessages();
            } else {
                this.useCustomMessages = false;
            }
        },
        toValidationObject: function() {
            if (this.criterion === "any") {
                return null;
            }

            var options = {
                type: this.type,
                dataType: this.criterion,
                comparerType: this.comparer,
                from: this.from,
                to: this.to,
                allowNulls: this.ignoreBlank,
                showButton: this.showButton
            };

            if (this.useCustomMessages) {
                options.messageTemplate = this.shouldBuild ? this.hintMessageTemplate : this.hintMessage;
                options.titleTemplate = this.hintTitle;
            }

            return options;
        }
    });

    var ValidationDialog = SpreadsheetDialog.extend({
        init: function(options) {
            var messages = kendo.spreadsheet.messages.dialogs.validationDialog || MESSAGES;
            var defaultOptions = {
                title: messages.title,
                hintMessage: messages.hintMessage,
                hintTitle: messages.hintTitle,
                criteria: [
                    { type: "any", name: messages.criteria.any },
                    { type: "number", name: messages.criteria.number },
                    { type: "text", name: messages.criteria.text },
                    { type: "date", name: messages.criteria.date },
                    { type: "custom", name: messages.criteria.custom },
                    { type: "list", name: messages.criteria.list }
                ],
                comparers: [
                    { type: "greaterThan", name: messages.comparers.greaterThan },
                    { type: "lessThan",    name: messages.comparers.lessThan },
                    { type: "between",     name: messages.comparers.between },
                    { type: "notBetween",  name: messages.comparers.notBetween },
                    { type: "equalTo",     name: messages.comparers.equalTo },
                    { type: "notEqualTo",  name: messages.comparers.notEqualTo },
                    { type: "greaterThanOrEqualTo", name: messages.comparers.greaterThanOrEqualTo },
                    { type: "lessThanOrEqualTo",    name: messages.comparers.lessThanOrEqualTo }
                ],
                comparerMessages: messages.comparerMessages
            };

            SpreadsheetDialog.fn.init.call(this, $.extend(defaultOptions, options));
        },
        options: {
            width: 450,
            criterion: "any",
            type: "reject",
            ignoreBlank: true,
            showButton: true,
            useCustomMessages: false,
            errorTemplate: (data) =>
                `<div class="k-tooltip k-tooltip-error k-validator-tooltip">
                    ${kendo.ui.icon({ icon: "exclamation-circle", iconClass: "k-tooltip-icon" })}
                    <span class="k-tooltip-content">${data.message}</span>
                    <span class="k-callout k-callout-n"></span>
                </div>`,
            template: (data) => {
                let $kendoOutput;
                $kendoOutput = `<div class="k-edit-form-container">
                    <div class="k-edit-label"><label for="criteria">${kendo.htmlEncode(data.messages.validationDialog.labels.criteria)}:</label></div>
                    <div class="k-edit-field"><select id="criteria" data-${kendo.htmlEncode(data.ns)}role="dropdownlist" title="${kendo.htmlEncode(data.messages.validationDialog.labels.criteria)}" data-${kendo.htmlEncode(data.ns)}text-field="name" data-${kendo.htmlEncode(data.ns)}value-field="type" data-${kendo.htmlEncode(data.ns)}bind="value: criterion, source: criteria"></select></div>
                    <div data-${kendo.htmlEncode(data.ns)}bind="visible: isNumber">
                    <div class="k-edit-label"><label for="number-comparer">${kendo.htmlEncode(data.messages.validationDialog.labels.comparer)}:</label></div>
                    <div class="k-edit-field"><select id="number-comparer" data-${kendo.htmlEncode(data.ns)}role="dropdownlist" title="${kendo.htmlEncode(data.messages.validationDialog.labels.comparer)}" data-${kendo.htmlEncode(data.ns)}text-field="name" data-${kendo.htmlEncode(data.ns)}value-field="type" data-${kendo.htmlEncode(data.ns)}bind="value: comparer, source: comparers"></select></div>
                    <div class="k-edit-label"><label for="comparer-min">${kendo.htmlEncode(data.messages.validationDialog.labels.min)}:</label></div>
                    <div class="k-edit-field"><span class="k-textbox k-input k-input-md k-rounded-md k-input-solid"><input id="comparer-min" name="${kendo.htmlEncode(data.messages.validationDialog.labels.min)}" title="${kendo.htmlEncode(data.messages.validationDialog.labels.min)}" placeholder="e.g. 10" class="k-input-inner" data-${kendo.htmlEncode(data.ns)}bind="value: from, enabled: isNumber" required="required" /></span></div>
                    <div data-${kendo.htmlEncode(data.ns)}bind="visible: showTo">
                    <div class="k-edit-label"><label for="comparer-max">${kendo.htmlEncode(data.messages.validationDialog.labels.max)}:</label></div>
                    <div class="k-edit-field"><span class="k-textbox k-input k-input-md k-rounded-md k-input-solid"><input id="comparer-max" name="${kendo.htmlEncode(data.messages.validationDialog.labels.max)}" title="${kendo.htmlEncode(data.messages.validationDialog.labels.max)}" placeholder="e.g. 100" class="k-input-inner" data-${kendo.htmlEncode(data.ns)}bind="value: to, enabled: showToForNumber" required="required" /></span></div>
                    </div>
                    </div><div data-${kendo.htmlEncode(data.ns)}bind="visible: isText">
                    <div class="k-edit-label"><label for="text-comparer">${kendo.htmlEncode(data.messages.validationDialog.labels.comparer)}:</label></div>
                    <div class="k-edit-field"><select id="text-comparer" data-${kendo.htmlEncode(data.ns)}role="dropdownlist" title="${kendo.htmlEncode(data.messages.validationDialog.labels.comparer)}" data-${kendo.htmlEncode(data.ns)}text-field="name" data-${kendo.htmlEncode(data.ns)}value-field="type" data-${kendo.htmlEncode(data.ns)}bind="value: comparer, source: comparers"></select></div>
                    <div class="k-edit-label"><label for="text-comparer-value">${kendo.htmlEncode(data.messages.validationDialog.labels.value)}:</label></div>
                    <div class="k-edit-field"><span class="k-textbox k-input k-input-md k-rounded-md k-input-solid"><input id="text-comparer-value" name="${kendo.htmlEncode(data.messages.validationDialog.labels.value)}" title="${kendo.htmlEncode(data.messages.validationDialog.labels.value)}" class="k-input-inner" data-${kendo.htmlEncode(data.ns)}bind="value: from, enabled: isText" required="required" /></span></div>
                    </div><div data-${kendo.htmlEncode(data.ns)}bind="visible: isDate">
                    <div class="k-edit-label"><label for="date-comparer">${kendo.htmlEncode(data.messages.validationDialog.labels.comparer)}:</label></div>
                    <div class="k-edit-field"><select id="date-comparer" data-${kendo.htmlEncode(data.ns)}role="dropdownlist" title="${kendo.htmlEncode(data.messages.validationDialog.labels.comparer)}" data-${kendo.htmlEncode(data.ns)}text-field="name" data-${kendo.htmlEncode(data.ns)}value-field="type" data-${kendo.htmlEncode(data.ns)}bind="value: comparer, source: comparers"></select></div>
                    <div class="k-edit-label"><label for="date-comparer-start">${kendo.htmlEncode(data.messages.validationDialog.labels.start)}:</label></div>
                    <div class="k-edit-field"><span class="k-textbox k-input k-input-md k-rounded-md k-input-solid"><input id="date-comparer-start" name="${kendo.htmlEncode(data.messages.validationDialog.labels.start)}" title="${kendo.htmlEncode(data.messages.validationDialog.labels.start)}" class="k-input-inner" data-${kendo.htmlEncode(data.ns)}bind="value: from, enabled: isDate" required="required" /></span></div>
                    <div data-${kendo.htmlEncode(data.ns)}bind="visible: showTo">
                    <div class="k-edit-label"><label for="date-comparer-end">${kendo.htmlEncode(data.messages.validationDialog.labels.end)}:</label></div>
                    <div class="k-edit-field"><span class="k-textbox k-input k-input-md k-rounded-md k-input-solid"><input id="date-comparer-end" name="${kendo.htmlEncode(data.messages.validationDialog.labels.end)}" title="${kendo.htmlEncode(data.messages.validationDialog.labels.end)}" class="k-input-inner" data-${kendo.htmlEncode(data.ns)}bind="value: to, enabled: showToForDate" required="required" /></span></div>
                    </div></div><div data-${kendo.htmlEncode(data.ns)}bind="visible: isCustom">
                    <div class="k-edit-label"><label for="custom-comparer-value">${kendo.htmlEncode(data.messages.validationDialog.labels.value)}:</label></div>
                    <div class="k-edit-field"><span class="k-textbox k-input k-input-md k-rounded-md k-input-solid"><input id="custom-comparer-value" name="${kendo.htmlEncode(data.messages.validationDialog.labels.value)}" title="${kendo.htmlEncode(data.messages.validationDialog.labels.value)}" class="k-input-inner" data-${kendo.htmlEncode(data.ns)}bind="value: from, enabled: isCustom" required="required" /></span></div>
                    </div><div data-${kendo.htmlEncode(data.ns)}bind="visible: isList">
                    <div class="k-edit-label"><label for="list-comparer-value">${kendo.htmlEncode(data.messages.validationDialog.labels.value)}:</label></div>
                    <div class="k-edit-field"><span class="k-textbox k-input k-input-md k-rounded-md k-input-solid"><input id="list-comparer-value" name="${kendo.htmlEncode(data.messages.validationDialog.labels.value)}" title="${kendo.htmlEncode(data.messages.validationDialog.labels.value)}" class="k-input-inner" data-${kendo.htmlEncode(data.ns)}bind="value: from, enabled: isList" required="required" /></span></div>
                    </div><div data-${kendo.htmlEncode(data.ns)}bind="visible: isList">
                    <div class="k-edit-field"><input type="checkbox" name="showButton" id="listShowButton" class="k-checkbox k-checkbox-md k-rounded-md" data-${kendo.htmlEncode(data.ns)}bind="checked: showButton" /><label for="listShowButton" class="k-checkbox-label"> ${kendo.htmlEncode(data.messages.validationDialog.labels.showListButton)}</label></div>
                    </div><div data-${kendo.htmlEncode(data.ns)}bind="visible: isDate">
                    <div class="k-edit-field"><input type="checkbox" name="showButton" id="dateShowButton" class="k-checkbox k-checkbox-md k-rounded-md" data-${kendo.htmlEncode(data.ns)}bind="checked: showButton" /><label for="dateShowButton" class="k-checkbox-label"> ${kendo.htmlEncode(data.messages.validationDialog.labels.showCalendarButton)}</label></div>
                    </div><div data-${kendo.htmlEncode(data.ns)}bind="invisible: isAny">
                    <div class="k-edit-field"><input type="checkbox" title="${kendo.htmlEncode(data.messages.validationDialog.labels.ignoreBlank)}" name="ignoreBlank" id="ignoreBlank" class="k-checkbox k-checkbox-md k-rounded-md" data-${kendo.htmlEncode(data.ns)}bind="checked: ignoreBlank" /><label for="ignoreBlank" class="k-checkbox-label"> ${kendo.htmlEncode(data.messages.validationDialog.labels.ignoreBlank)}</label></div>
                    </div><div data-${kendo.htmlEncode(data.ns)}bind="invisible: isAny">
                    <div class="k-hr"></div>
                    <div class="k-edit-label"><label>${kendo.htmlEncode(data.messages.validationDialog.labels.onInvalidData)}:</label></div>
                    <div class="k-edit-field"><input type="radio" title="${kendo.htmlEncode(data.messages.validationDialog.labels.rejectInput)}" id="validationTypeReject" name="validationType" value="reject" data-${kendo.htmlEncode(data.ns)}bind="checked: type" class="k-radio k-radio-md" /><label for="validationTypeReject" class="k-radio-label">${kendo.htmlEncode(data.messages.validationDialog.labels.rejectInput)}</label> <input type="radio" title="${kendo.htmlEncode(data.messages.validationDialog.labels.showWarning)}" id="validationTypeWarning" name="validationType" value="warning" data-${kendo.htmlEncode(data.ns)}bind="checked: type" class="k-radio k-radio-md" /><label for="validationTypeWarning" class="k-radio-label">${kendo.htmlEncode(data.messages.validationDialog.labels.showWarning)}</label></div>
                    </div><div data-${kendo.htmlEncode(data.ns)}bind="invisible: isAny" class="hint-wrapper">
                    <div class="k-edit-field"><input type="checkbox" title="${kendo.htmlEncode(data.messages.validationDialog.labels.showHint)}" name="useCustomMessages" id="useCustomMessages" class="k-checkbox k-checkbox-md k-rounded-md" data-${kendo.htmlEncode(data.ns)}bind="checked: useCustomMessages" /><label class="k-checkbox-label" for="useCustomMessages"> ${kendo.htmlEncode(data.messages.validationDialog.labels.showHint)}</label></div>
                    <div data-${kendo.htmlEncode(data.ns)}bind="visible: useCustomMessages">
                    <div class="k-edit-label"><label for="hint-title">${kendo.htmlEncode(data.messages.validationDialog.labels.hintTitle)}:</label></div>
                    <div class="k-edit-field"><span class="k-textbox k-input k-input-md k-rounded-md k-input-solid"><input id="hint-title" name="hint-title" class="k-input-inner" title="${kendo.htmlEncode(data.messages.validationDialog.labels.hintTitle)}" placeholder="${kendo.htmlEncode(data.messages.validationDialog.placeholders.typeTitle)}" data-${kendo.htmlEncode(data.ns)}bind="value: hintTitle" /></span></div>
                    <div class="k-edit-label"><label for="hint-message">${kendo.htmlEncode(data.messages.validationDialog.labels.hintMessage)}:</label></div>
                    <div class="k-edit-field"><span class="k-textbox k-input k-input-md k-rounded-md k-input-solid"><input id="hint-message" class="k-input-inner" title="${kendo.htmlEncode(data.messages.validationDialog.labels.hintMessage)}" placeholder="${kendo.htmlEncode(data.messages.validationDialog.placeholders.typeMessage)}" data-${kendo.htmlEncode(data.ns)}bind="value: hintMessage" /></span></div>
                    </div></div>
                    <div class="k-actions"><button class="k-button k-button-md k-rounded-md k-button-solid k-button-solid-base" data-${kendo.htmlEncode(data.ns)}bind="visible: showRemove, click: remove"><span class="k-button-text">${kendo.htmlEncode(data.messages.remove)}</span></button><button class="k-button k-button-md k-rounded-md k-button-solid k-button-solid-primary" data-${kendo.htmlEncode(data.ns)}bind="click: apply"><span class="k-button-text">${kendo.htmlEncode(data.messages.apply)}</span></button><button class="k-button k-button-md k-rounded-md k-button-solid k-button-solid-base" data-${kendo.htmlEncode(data.ns)}bind="click: close"><span class="k-button-text">${kendo.htmlEncode(data.messages.cancel)}</span></button></div>
                    </div>`;
                return $kendoOutput;
            }
        },
        open: function(range) {
            var options = this.options;
            var element;

            this.viewModel = new ValidationViewModel({
                type: options.type,
                defaultHintMessage: options.hintMessage,
                defaultHintTitle: options.hintTitle,
                defaultComparers: options.comparers.slice(0),
                comparerMessages: options.comparerMessages,
                criteria: options.criteria.slice(0),
                criterion: options.criterion,
                ignoreBlank: options.ignoreBlank,
                showButton: options.showButton,
                apply: this.apply.bind(this),
                close: this.close.bind(this),
                remove: this.remove.bind(this)
            });

            this.viewModel.update(range.validation());

            SpreadsheetDialog.fn.open.call(this);

            element = this.dialog().element;

            if (this.validatable) {
                this.validatable.destroy();
            }

            kendo.bind(element, this.viewModel);

            this.validatable = new kendo.ui.Validator(element.find(".k-edit-form-container"), {
                validateOnBlur: false,
                errorTemplate: this.options.errorTemplate || undefined
            });
        },
        apply: function() {

            if (this.validatable.validate()) {
                SpreadsheetDialog.fn.apply.call(this);

                this.trigger("action", {
                    command: "EditValidationCommand",
                    options: {
                        value: this.viewModel.toValidationObject()
                    }
                });
            }
        },
        remove: function() {
            this.viewModel.set("criterion", "any");
            this.apply();
        }
    });

    kendo.spreadsheet.dialogs.register("validation", ValidationDialog);
    kendo.spreadsheet.dialogs.ValidationDialog = ValidationDialog;

    function PDF_PAPER_SIZE(size) {
        return {
            value: size,
            text: TEXT("exportAsDialog.pdf.paper." + size)
        };
    }

    var ExportAsDialog = SpreadsheetDialog.extend({
        init: function(options) {
            SpreadsheetDialog.fn.init.call(this, options);
            options = this.options;

            this.viewModel = kendo.observable({
                title: options.title,
                name: options.name,
                extension: options.extension,
                fileFormats:options.fileFormats,
                excel: options.excelExport,
                pdf: {
                    proxyURL: options.pdfExport.proxyURL,
                    forceProxy: options.pdfExport.forceProxy,
                    title: options.pdfExport.title,
                    author: options.pdfExport.author,
                    autoPrint: options.pdfExport.autoPrint,
                    subject: options.pdfExport.subject,
                    keywords: options.pdfExport.keywords,
                    creator: options.pdfExport.creator,
                    date: options.pdfExport.date,

                    fitWidth: options.pdf.fitWidth,
                    area: options.pdf.area,
                    areas: options.pdf.areas,
                    paperSize: options.pdf.paperSize,
                    paperSizes: options.pdf.paperSizes,
                    margin: options.pdf.margin,
                    margins: options.pdf.margins,
                    landscape: options.pdf.landscape,
                    guidelines: options.pdf.guidelines,
                    hCenter: options.pdf.hCenter,
                    vCenter: options.pdf.vCenter
                },
                apply: this.apply.bind(this),
                close: this.close.bind(this)
            });

            var dialog = this.dialog();
            this.viewModel.bind("change", function(e) {
                if (e.field === "extension") {
                    this.set("showPdfOptions", this.extension === ".pdf" ? true : false);
                    dialog.center();
                }
            });

            kendo.bind(dialog.element, this.viewModel);
        },
        options: {
            title: TEXT("exportAsDialog.title", "Export..."),
            name: TEXT("exportAsDialog.defaultFileName", "Workbook"),
            extension: ".xlsx",
            fileFormats: [{
                description: TEXT("exportAsDialog.xlsx.description", "Excel Workbook (.xlsx)"),
                extension: ".xlsx"
            }, {
                description: TEXT("exportAsDialog.pdf.description", "Portable Document Format (.pdf)"),
                extension: ".pdf"
            }],
            pdf: {
                fitWidth: true,
                area: "workbook",
                areas: [{
                    area: "workbook",
                    text: TEXT("exportAsDialog.pdf.area.workbook", "Entire Workbook")
                },{
                    area: "sheet",
                    text: TEXT("exportAsDialog.pdf.area.sheet", "Active Sheet")
                },{
                    area: "selection",
                    text: TEXT("exportAsDialog.pdf.area.selection", "Selection")
                }],
                paperSize: "a4",
                paperSizes: [
                    "a2", "a3", "a4", "a5", "b3", "b4", "b5", "folio", "legal", "letter", "tabloid", "executive"
                ].map(PDF_PAPER_SIZE),
                margin: {bottom: "0.75in", left: "0.7in", right: "0.7in", top: "0.75in"},
                margins: [
                    { value: { bottom: "0.75in", left: "0.7in", right: "0.7in", top: "0.75in" },
                      text: TEXT("exportAsDialog.pdf.margin.normal", "Normal")
                    },
                    { value: { bottom: "0.75in", left: "0.25in", right: "0.25in", top: "0.75in" },
                      text: TEXT("exportAsDialog.pdf.margin.narrow", "Narrow")
                    },
                    { value: { bottom: "1in", left: "1in", right: "1in", top: "1in" },
                      text: TEXT("exportAsDialog.pdf.margin.wide", "Wide")
                    }
                ],
                landscape: true,
                guidelines: true,
                hCenter: true,
                vCenter: true
            },
            width: 520,
            template: (data) => {
                let $kendoOutput;
                $kendoOutput = `<div class='k-edit-label'><label id='name-label'>${kendo.htmlEncode(data.messages.exportAsDialog.labels.fileName)}:</label></div>
                    <div class='k-edit-field'><span class='k-textbox k-input k-input-md k-rounded-md k-input-solid'><input aria-labelledby='name-label' class='k-input-inner' data-${kendo.htmlEncode(data.ns)}bind='value: name' /></span></div>
                    <div >
                       <div class='k-edit-label'><label id='file-format-label'>${kendo.htmlEncode(data.messages.exportAsDialog.labels.saveAsType)}:</label></div>
                       <div class='k-edit-field'><select aria-labelledby='file-format-label' data-${kendo.htmlEncode(data.ns)}role='dropdownlist' class='k-file-format' data-${kendo.htmlEncode(data.ns)}text-field='description' data-${kendo.htmlEncode(data.ns)}value-field='extension' data-${kendo.htmlEncode(data.ns)}bind='value: extension, source: fileFormats'></select></div>
                    </div>
                    <div class='k-export-config' data-${kendo.htmlEncode(data.ns)}bind='visible: showPdfOptions'>
                    <hr class='k-hr' />
                    <div class='k-edit-label'><label id='export-area-label'>${kendo.htmlEncode(data.messages.exportAsDialog.labels.exportArea)}:</label></div>
                    <div class='k-edit-field'><select aria-labelledby='export-area-label' data-${kendo.htmlEncode(data.ns)}role='dropdownlist' class='k-file-format' data-${kendo.htmlEncode(data.ns)}text-field='text' data-${kendo.htmlEncode(data.ns)}value-field='area' data-${kendo.htmlEncode(data.ns)}bind='value: pdf.area, source: pdf.areas'></select></div>
                    <div class='k-edit-label'><label id='paper-size-label'>${kendo.htmlEncode(data.messages.exportAsDialog.labels.paperSize)}:</label></div>
                    <div class='k-edit-field'><select aria-labelledby='paper-size-label' data-${kendo.htmlEncode(data.ns)}role='dropdownlist' class='k-file-format' data-${kendo.htmlEncode(data.ns)}text-field='text' data-${kendo.htmlEncode(data.ns)}value-field='value' data-${kendo.htmlEncode(data.ns)}bind='value: pdf.paperSize, source: pdf.paperSizes'></select></div>
                    <div class='k-edit-label'><label id='margins-label'>${kendo.htmlEncode(data.messages.exportAsDialog.labels.margins)}:</label></div>
                    <div class='k-edit-field'><select aria-labelledby='margins-label' data-${kendo.htmlEncode(data.ns)}role='dropdownlist' class='k-file-format' data-${kendo.htmlEncode(data.ns)}value-primitive='true'data-${kendo.htmlEncode(data.ns)}text-field='text' data-${kendo.htmlEncode(data.ns)}value-field='value' data-${kendo.htmlEncode(data.ns)}bind='value: pdf.margin, source: pdf.margins'></select></div>
                    <div class='k-edit-label'><label>${kendo.htmlEncode(data.messages.exportAsDialog.labels.orientation)}:</label></div>
                    <div class='k-edit-field'>
                       <div class='k-button-group'>
                          <input type='radio' id='k-orientation-portrait' aria-label='orientation portrait' name='orientation' data-${kendo.htmlEncode(data.ns)}type='boolean' data-${kendo.htmlEncode(data.ns)}bind='checked: pdf.landscape' value='false' />
                          <label class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-base k-icon-button k-group-start k-orientation-button' for='k-orientation-portrait'>
                          ${kendo.ui.icon({ icon: "file", iconClass: "k-button-icon" })}
                          </label>
                          <input type='radio' id='k-orientation-landscape' aria-label='orientation landscape' name='orientation' data-${kendo.htmlEncode(data.ns)}type='boolean' data-${kendo.htmlEncode(data.ns)}bind='checked: pdf.landscape' value='true' />
                          <label class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-base k-icon-button k-group-end k-orientation-button' for='k-orientation-landscape'>
                          ${kendo.ui.icon({ icon: "file-horizontal", iconClass: "k-button-icon" })}
                          </label>
                       </div>
                    </div>
                    <div class='k-edit-label'><label>${kendo.htmlEncode(data.messages.exportAsDialog.labels.print)}:</label></div>
                    <div class='k-edit-field'><input class='k-checkbox k-checkbox-md k-rounded-md' id='guidelines' type='checkbox' data-${kendo.htmlEncode(data.ns)}bind='checked: pdf.guidelines'/><label class='k-checkbox-label' for='guidelines'>${kendo.htmlEncode(data.messages.exportAsDialog.labels.guidelines)}</label></div>
                    <div class='k-edit-label'><label>${kendo.htmlEncode(data.messages.exportAsDialog.labels.scale)}:</label></div>
                    <div class='k-edit-field'><input class='k-checkbox k-checkbox-md k-rounded-md' id='fitWidth' type='checkbox' data-${kendo.htmlEncode(data.ns)}bind='checked: pdf.fitWidth'/><label class='k-checkbox-label' for='fitWidth'>${kendo.htmlEncode(data.messages.exportAsDialog.labels.fit)}</label></div>
                    <div class='k-edit-label'><label>${kendo.htmlEncode(data.messages.exportAsDialog.labels.center)}:</label></div>
                    <div class='k-edit-field'><input class='k-checkbox k-checkbox-md k-rounded-md' id='hCenter' type='checkbox' data-${kendo.htmlEncode(data.ns)}bind='checked: pdf.hCenter'/><label class='k-checkbox-label' for='hCenter'>${kendo.htmlEncode(data.messages.exportAsDialog.labels.horizontally)}</label><input class='k-checkbox k-checkbox-md k-rounded-md' id='vCenter' type='checkbox' data-${kendo.htmlEncode(data.ns)}bind='checked: pdf.vCenter'/><label class='k-checkbox-label' for='vCenter'>${kendo.htmlEncode(data.messages.exportAsDialog.labels.vertically)}</label></div>
                    <div class='k-page-orientation'>
                       ${kendo.ui.icon($(`<span data-${data.ns}bind='invisible: pdf.landscape'></span>`), { icon: "file" })}
                       ${kendo.ui.icon($(`<span data-${data.ns}bind='visible: pdf.landscape'></span>`), { icon: "file-horizontal" })}
                    </div>
                    </div>
                    <div class='k-actions'><button class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-primary' data-${kendo.htmlEncode(data.ns)}bind='click: apply'><span class='k-button-text'>${kendo.htmlEncode(data.messages.save)}</span></button><button class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-base' data-${kendo.htmlEncode(data.ns)}bind='click: close'><span class='k-button-text'>${kendo.htmlEncode(data.messages.cancel)}</span></button></div>`;
                return $kendoOutput;
            }
        },
        apply: function() {
            SpreadsheetDialog.fn.apply.call(this);

            this.trigger("action", {
                command: "SaveAsCommand",
                options: this.viewModel
            });
        }
    });
    kendo.spreadsheet.dialogs.register("exportAs", ExportAsDialog);

    function basicErrorDialog(id, msg) {
        kendo.spreadsheet.dialogs.register(
            id,
            MessageDialog.extend({
                options: { messageId: msg }
            })
        );
    }

    basicErrorDialog("modifyMerged", "modifyMergedDialog.errorMessage");
    basicErrorDialog("rangeDisabled", "rangeDisabledDialog.errorMessage");
    basicErrorDialog("intersectsArray", "intersectsArrayDialog.errorMessage");
    basicErrorDialog("overflow", "overflowDialog.errorMessage");
    basicErrorDialog("unsupportedSelection", "unsupportedSelectionDialog.errorMessage");
    basicErrorDialog("incompatibleRanges", "incompatibleRangesDialog.errorMessage");
    basicErrorDialog("noFillDirection", "noFillDirectionDialog.errorMessage");
    basicErrorDialog("duplicateSheetName", "duplicateSheetNameDialog.errorMessage");

    var ImportErrorDialog = MessageDialog.extend({
        options: {
            width: 640,
            title: "Errors in import",
            template: (data) => {
                let $kendoOutput;
                $kendoOutput = `<div class='k-spreadsheet-message-content k-spreadsheet-import-errors'><div class='k--header-message'>We encountered ${data.errors.length} errors while reading this file.  Please be aware that some formulas might be missing, or contain invalid results.</div><div class='k--errors'><table><thead><tr><th>Context</th><th>Error message</th></tr></thead>`;
                for (let i = 0; i < errors.length; ++i) {
                    $kendoOutput += ``; let err = errors[i];                    $kendoOutput += `<tr><td>${kendo.htmlEncode(err.context)}</td><td>${kendo.htmlEncode(err.error)}</td></tr>`;
                }
                $kendoOutput += `</table></div></div><div class='k-actions'><button class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-primary' data-${kendo.htmlEncode(data.ns)}bind='click: close'><span class='k-button-text'>${kendo.htmlEncode(data.messages.okText)}</span></button></div>`;
                return $kendoOutput;
            }
        }
    });

    kendo.spreadsheet.dialogs.register("importError", ImportErrorDialog);

    var UseKeyboardDialog = MessageDialog.extend({
        options: {
            title: TEXT("useKeyboardDialog.title", "Copying and pasting"),
            template: (data) => {
                let $kendoOutput;
                $kendoOutput = `${kendo.htmlEncode(data.messages.useKeyboardDialog.errorMessage)}
                <div>Ctrl+C ${kendo.htmlEncode(data.messages.useKeyboardDialog.labels.forCopy)}</div>
                <div>Ctrl+X ${kendo.htmlEncode(data.messages.useKeyboardDialog.labels.forCut)}</div>
                <div>Ctrl+V ${kendo.htmlEncode(data.messages.useKeyboardDialog.labels.forPaste)}</div>
                <div class="k-actions"><button class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-primary'
                        data-${kendo.htmlEncode(data.ns)}bind='click: close'><span
                            class='k-button-text'>${data.messages.okText}</span></button></div>`;
                return $kendoOutput;
            }
        }
    });

    kendo.spreadsheet.dialogs.register("useKeyboard", UseKeyboardDialog);

    var HyperlinkDialog = SpreadsheetDialog.extend({
        options: {
            title: TEXT("linkDialog.title", "Hyperlink"),
            template: (data) => {
                let $kendoOutput;
                $kendoOutput = `<div class='k-edit-label'><label id='url-label'>${kendo.htmlEncode(data.messages.linkDialog.labels.url)}:</label></div>
<div class='k-edit-field'><span class='k-textbox k-input k-input-md k-rounded-md k-input-solid'><input
            class='k-input-inner' data-${kendo.htmlEncode(data.ns)}bind='value: url'
            title='${kendo.htmlEncode(data.messages.linkDialog.labels.url)}' aria-labelledby='url-label' /></span></div>
<div class='k-actions'><button class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-base k-left'
        data-${kendo.htmlEncode(data.ns)}bind='click: remove'><span
            class='k-button-text'>${data.messages.linkDialog.labels.removeLink}</span></button><button
        class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-primary'
        data-${kendo.htmlEncode(data.ns)}bind='click: apply'><span
            class='k-button-text'>${data.messages.okText}</span></button><button
        class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-base'
        data-${kendo.htmlEncode(data.ns)}bind='click: cancel'><span
            class='k-button-text'>${data.messages.cancel}</span></button></div>`;
                return $kendoOutput;
            },
            autoFocus: false
        },
        open: function(range) {
            var self = this;
            SpreadsheetDialog.fn.open.apply(self, arguments);
            var element = self.dialog().element;
            var model = kendo.observable({
                url: range.link(),
                apply: function() {
                    if (!/\S/.test(model.url)) {
                        model.url = null;
                    }
                    self.trigger("action", {
                        command: "HyperlinkCommand",
                        options: {
                            link: model.url
                        }
                    });
                    self.close();
                },
                remove: function() {
                    model.url = null;
                    model.apply();
                },
                cancel: self.close.bind(self)
            });
            kendo.bind(element, model);

            // it would be nice if we could easily handle that in one
            // place for all dialogs, but it doesn't seem easily
            // doable.
            element.find("input")
                .trigger("focus")
                .on("keydown", function(ev){
                    if (ev.keyCode == 13 /*ENTER*/) {
                        model.url = $(this).val(); // there won't be a "change" event and the model wouldn't update :-\
                        ev.stopPropagation();
                        ev.preventDefault();
                        model.apply();
                    } else if (ev.keyCode == 27 /*ESC*/) {
                        ev.stopPropagation();
                        ev.preventDefault();
                        model.cancel();
                    }
                });
        }
    });

    kendo.spreadsheet.dialogs.register("hyperlink", HyperlinkDialog);

    var SheetRenameDialog = SpreadsheetDialog.extend({
        options: {
            title: TEXT("sheetRenameDialog.title", "Rename Sheet"),
            template: (data) => {
                let $kendoOutput;
                $kendoOutput = `<div class='k-edit-label'><label id='sheet-name-label'>${kendo.htmlEncode(data.messages.sheetRenameDialog.labels.text)}:</label></div>
<div class='k-edit-field'><span class='k-textbox k-input k-input-md k-rounded-md k-input-solid'><input
            class='k-input-inner' data-${kendo.htmlEncode(data.ns)}bind='value: sheetName'
            title='${kendo.htmlEncode(data.messages.sheetRenameDialog.labels.text)}' aria-labelledby='sheet-name-label' /></span></div>
<div class='k-actions'><button class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-primary'
        data-${kendo.htmlEncode(data.ns)}bind='click: apply'><span
            class='k-button-text'>${data.messages.sheetRenameDialog.labels.rename}</span></button><button
        class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-base'
        data-${kendo.htmlEncode(data.ns)}bind='click: cancel'><span
            class='k-button-text'>${data.messages.cancel}</span></button></div>`;
                return $kendoOutput;
            },
            autoFocus: false
        },
        open: function(range, args) {
            let sheet = range.sheet();
            let self = this;
            SpreadsheetDialog.fn.open.apply(self, arguments);
            let element = self.dialog().element;
            let model = kendo.observable({
                sheetName: args?._oldSheetName || sheet.name(),
                apply: function() {
                    self._newSheetName = model.sheetName;
                    self.close();
                },
                cancel: self.close.bind(self)
            });
            kendo.bind(element, model);

            element.find("input")
                .trigger("focus")
                .on("keydown", function(ev){
                    if (ev.keyCode == 13 /*ENTER*/) {
                        model.sheetName = $(this).val();
                        ev.stopPropagation();
                        ev.preventDefault();
                        model.apply();
                    } else if (ev.keyCode == 27 /*ESC*/) {
                        ev.stopPropagation();
                        ev.preventDefault();
                        model.cancel();
                    }
                });
        }
    });

    kendo.spreadsheet.dialogs.register("renameSheet", SheetRenameDialog);

    var InsertCommentDialog = SpreadsheetDialog.extend({
        options: {
            className: "k-spreadsheet-insert-comment",
            template: (data) => {
                let $kendoOutput;
                $kendoOutput = `<div class='k-edit-label'><label id='comment-label'>${kendo.htmlEncode(data.messages.insertCommentDialog.labels.comment)}:</label></div>
                <div class='k-edit-field'><span class='k-input k-textarea k-input-solid k-input-md k-rounded-md'><textarea
                    aria-labelledby='comment-label' rows='5' class='k-input-inner'
                    data-${kendo.htmlEncode(data.ns)}bind='value: comment'></textarea></span></div>
                <div class='k-actions'> <button class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-base k-left'
                    data-${kendo.htmlEncode(data.ns)}bind='click: remove'><span
                    class='k-button-text'>${kendo.htmlEncode(data.messages.insertCommentDialog.labels.removeComment)}</span></button>
                <button class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-primary'
                    data-${kendo.htmlEncode(data.ns)}bind='click: apply'><span
                    class='k-button-text'>${kendo.htmlEncode(data.messages.okText)}</span></button> <button
                    class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-base'
                    data-${kendo.htmlEncode(data.ns)}bind='click: cancel'><span
                    class='k-button-text'>${data.messages.cancel}</span></button></div>`;
                return $kendoOutput;
            },
            title: TEXT("insertCommentDialog.title", "Insert comment"),
            autoFocus: false,
            width: 450
        },
        open: function(range) {
            var self = this;
            SpreadsheetDialog.fn.open.apply(self, arguments);
            var element = self.dialog().element;
            var model = kendo.observable({
                comment: range.comment(),
                apply: function() {
                    if (!/\S/.test(model.comment)) {
                        model.comment = null;
                    }
                    self.trigger("action", {
                        command: "InsertCommentCommand",
                        options: {
                            value: model.comment
                        }
                    });
                    self.close();
                },
                remove: function() {
                    model.comment = null;
                    model.apply();
                },
                cancel: self.close.bind(self)
            });
            kendo.bind(element, model);

            element.find("textarea").trigger("focus");
        }
    });

    kendo.spreadsheet.dialogs.register("insertComment", InsertCommentDialog);

    var InsertImageDialog = SpreadsheetDialog.extend({
        options: {
            template: (data) => {
                let $kendoOutput;
                $kendoOutput = `<div class='k-spreadsheet-insert-image-dialog'> <label
        data-${kendo.htmlEncode(data.ns)}bind='style: { background-image: imageUrl }, css: { k-spreadsheet-has-image: hasImage, k-hover: isHovered }, events: { dragenter: dragEnter, dragover: stopEvent, dragleave: dragLeave, drop: drop }'>
        <div data-${kendo.htmlEncode(data.ns)}bind='text: info'></div>
        <input type='file' data-${kendo.htmlEncode(data.ns)}bind='events: { change: change }'
            accept='image/png, image/jpeg, image/gif' />
    </label></div>
<div class='k-actions'> <button class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-primary'
        data-${kendo.htmlEncode(data.ns)}bind='enabled: okEnabled, click: apply'><span
            class='k-button-text'>${kendo.htmlEncode(data.messages.okText)}</span></button> <button
        class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-base'
        data-${kendo.htmlEncode(data.ns)}bind='click: cancel'><span
            class='k-button-text'>${data.messages.cancel}</span></button></div>`;
                return $kendoOutput;
            },
            title: TEXT("insertImageDialog.title", "Insert image"),
            width: "auto"
        },
        open: function() {
            var self = this;
            SpreadsheetDialog.fn.open.apply(self, arguments);
            var element = self.dialog().element;
            var model = kendo.observable({
                okEnabled: false,
                info: kendo.spreadsheet.messages.dialogs.insertImageDialog.info,
                imageUrl: "",
                hasImage: false,
                isHovered: false,
                _url: null,
                _image: null,
                apply: function() {
                    window.URL.revokeObjectURL(model._url);
                    self.trigger("action", {
                        command: "InsertImageCommand",
                        options: {
                            blob   : model._image,
                            width  : model._width,
                            height : model._height
                        }
                    });
                    self.close();
                },
                cancel: self.close.bind(self),
                stopEvent: function(ev) {
                    ev.stopPropagation();
                    ev.preventDefault();
                },
                drop: function(ev) {
                    model.stopEvent(ev);
                    model.selectFile(ev.originalEvent.dataTransfer.files);
                    model.set("isHovered", false);
                },
                dragEnter: function(ev) {
                    model.stopEvent(ev);
                    model.set("isHovered", true);
                },
                dragLeave: function(ev) {
                    model.stopEvent(ev);
                    model.set("isHovered", false);
                },
                change: function(ev) {
                    model.selectFile(ev.target.files);
                },
                selectFile: function(files) {
                    var image;
                    for (var i = 0; i < files.length; ++i) {
                        if (/^image\//i.test(files[i].type)) {
                            image = files[i];
                            break;
                        }
                    }
                    if (model._url) {
                        window.URL.revokeObjectURL(model._url);
                    }
                    if (image) {
                        model._image = image;
                        model._url = window.URL.createObjectURL(image);
                        var img = new Image();
                        img.src = model._url;
                        img.onload = function() {
                            model._width = img.width;
                            model._height = img.height;
                            model.set("info", kendo.spreadsheet.messages.dialogs.insertImageDialog.info);
                            model.set("okEnabled", true);
                            model.set("imageUrl", "url('" + model._url + "')");
                            model.set("hasImage", true);
                        };
                    } else {
                        model._image = null;
                        model.set("info", kendo.spreadsheet.messages.dialogs.insertImageDialog.typeError);
                        model.set("okEnabled", false);
                        model.set("imageUrl", "");
                        model.set("hasImage", false);
                    }
                }
            });
            kendo.bind(element, model);
        }
    });

    kendo.spreadsheet.dialogs.register("insertImage", InsertImageDialog);

    function Localizable(path, def) {
        this.path = path.split(".");
        this.def = def;
    }

    Localizable.prototype.trans = function() {
        var msg = kendo.spreadsheet.messages.dialogs;
        for (var i = 0; i < this.path.length; ++i) {
            msg = msg[this.path[i]];
            if (!msg) {
                return this.def;
            }
        }
        return msg;
    };

    function TEXT(path, def) {
        return new Localizable(path, def);
    }

    function translate(thing) {
        if (thing instanceof Localizable) {
            return thing.trans();
        }
        else if (Array.isArray(thing)) {
            return thing.map(translate);
        }
        else if (thing != null && typeof thing == "object") {
            return Object.keys(thing).reduce(function(ret, key){
                ret[key] = translate(thing[key]);
                return ret;
            }, {});
        }
        return thing;
    }

})(window.kendo);

(function(kendo) {

        var $ = kendo.jQuery;
        var BORDER_TYPES = [
            "allBorders",
            "insideBorders",
            "insideHorizontalBorders",
            "insideVerticalBorders",
            "outsideBorders",
            "leftBorder",
            "topBorder",
            "rightBorder",
            "bottomBorder",
            "noBorders"
        ];
        var BORDER_TYPES_ICONS = {
           allBorders: "bordersAll",
           insideBorders: "bordersInside",
           insideHorizontalBorders: "bordersInsideHorizontal",
           insideVerticalBorders: "bordersInsideVertical",
           outsideBorders: "bordersOutside",
           leftBorder: "borderLeft",
           topBorder: "borderTop",
           rightBorder: "borderRight",
           bottomBorder: "borderBottom",
           noBorders: "bordersNone"
        };

        var BORDER_PALETTE_MESSAGES = kendo.spreadsheet.messages.borderPalette = {
            allBorders: "All borders",
            insideBorders: "Inside borders",
            insideHorizontalBorders: "Inside horizontal borders",
            insideVerticalBorders: "Inside vertical borders",
            outsideBorders: "Outside borders",
            leftBorder: "Left border",
            topBorder: "Top border",
            rightBorder: "Right border",
            bottomBorder: "Bottom border",
            noBorders: "No border"
        };

        var colorPickerPalette = [ //metro palette
            "#ffffff", "#000000", "#d6ecff", "#4e5b6f", "#7fd13b", "#ea157a", "#feb80a", "#00addc", "#738ac8", "#1ab39f",
            "#f2f2f2", "#7f7f7f", "#a7d6ff", "#d9dde4", "#e5f5d7", "#fad0e4", "#fef0cd", "#c5f2ff", "#e2e7f4", "#c9f7f1",
            "#d8d8d8", "#595959", "#60b5ff", "#b3bcca", "#cbecb0", "#f6a1c9", "#fee29c", "#8be6ff", "#c7d0e9", "#94efe3",
            "#bfbfbf", "#3f3f3f", "#007dea", "#8d9baf", "#b2e389", "#f272af", "#fed46b", "#51d9ff", "#aab8de", "#5fe7d5",
            "#a5a5a5", "#262626", "#003e75", "#3a4453", "#5ea226", "#af0f5b", "#c58c00", "#0081a5", "#425ea9", "#138677",
            "#7f7f7f", "#0c0c0c", "#00192e", "#272d37", "#3f6c19", "#750a3d", "#835d00", "#00566e", "#2c3f71", "#0c594f"
        ];

        var BorderPalette = kendo.ui.Widget.extend({
            init: function(element, options) {
                kendo.ui.Widget.call(this, element, options);

                this.element = element;
                this.color = "#000";

                this.element.addClass("k-spreadsheet-popup");

                this._borderTypePalette();
                this._borderColorPalette();
            },

            options: {
                name: "BorderPalette"
            },

            events: [
                "change"
            ],

            destroy: function() {
                this.colorChooser.destroy();
                kendo.destroy(this.element.find(".k-spreadsheet-border-type-palette"));
            },

            value: function() {
                return { type: this.type, color: this.color };
            },

            _borderTypePalette: function() {
                var that = this;
                var messages = BORDER_PALETTE_MESSAGES;

                var element = $("<div />", {
                    "class": "k-spreadsheet-border-type-palette"
                });

                $('<span class="k-column-menu-group-header"><span class="k-column-menu-group-header-text">Border type</span></span>').appendTo(this.element);

                element.appendTo(this.element);

                BORDER_TYPES.map(function(type) {
                    $('<button title="' + messages[type] + '" aria-label="' + messages[type] + '" data-border-type="' + type + '">')
                        .appendTo(element)
                        .kendoToggleButton({
                            icon: kendo.toHyphens(BORDER_TYPES_ICONS[type]),
                            toggle: that._toggle.bind(that)
                        });
                });
            },

            _borderColorPalette: function() {
                var element = $("<div />", {
                    "class": "k-spreadsheet-border-color-palette"
                });

                $('<span class="k-column-menu-group-header"><span class="k-column-menu-group-header-text">Border color</span></span>').appendTo(this.element);
                element.appendTo(this.element);

                this.colorChooser = new kendo.ui.FlatColorPicker(element, {
                    buttons: !this.options.change,
                    color: this.color,
                    view: "palette",
                    palette: colorPickerPalette,
                    input: false,
                    change: this._change.bind(this)
                });

                this.colorChooser.wrapper.find(".k-coloreditor-apply").on("click", this._apply.bind(this));
                this.colorChooser.wrapper.find(".k-coloreditor-cancel").on("click", this._cancel.bind(this));
            },

            _change: function() {
                this.color = this.colorChooser.value();
            },

            _toggle: function(e) {
                var type = e.target.data("borderType"),
                    previous = e.target.siblings(".k-selected").data("kendoToggleButton");

                if (e.checked === true) {
                    if (previous) {
                        previous.toggle(false);
                    }

                    this.type = type;
                } else {
                    this.type = null;
                }
            },

            _apply: function() {
                this.trigger("change", { type: this.type, color: this.color });
            },

            _cancel: function() {
                this.trigger("change", { type: null, color: null });
            }
        });

        kendo.spreadsheet.BorderPalette = BorderPalette;

    })(window.kendo);

(function(kendo) {
    var $ = kendo.jQuery;

    function getDefaultToolElement(firstIconName) {
        return `<button role="button">${kendo.ui.icon({ icon: firstIconName, iconClass: "k-button-icon" })}<span class="k-button-text">${kendo.ui.icon("caret-alt-down")}</span></button>`
    }
    var ToolBar = kendo.ui.ToolBar;

    var MESSAGES = kendo.spreadsheet.messages.toolbar = {
        addColumnLeft: "Add column left",
        addColumnRight: "Add column right",
        addRowAbove: "Add row above",
        addRowBelow: "Add row below",
        alignment: "Alignment",
        alignmentButtons: {
            justifyLeft: "Align left",
            justifyCenter: "Center",
            justifyRight: "Align right",
            justifyFull: "Justify",
            alignTop: "Align top",
            alignMiddle: "Align middle",
            alignBottom: "Align bottom"
        },
        backgroundColor: "Background",
        increaseFontSize: "Increase font size",
        decreaseFontSize: "Decrease font size",
        bold: "Bold",
        borders: "Borders",
        copy: "Copy",
        cut: "Cut",
        deleteColumn: "Delete column",
        deleteRow: "Delete row",
        filter: "Filter",
        fontFamily: "Font",
        fontSize: "Font size",
        format: "Custom format...",
        formatTypes: {
            automatic: "Automatic",
            text: "Text",
            number: "Number",
            percent: "Percent",
            financial: "Financial",
            currency: "Currency",
            date: "Date",
            time: "Time",
            dateTime: "Date time",
            duration: "Duration",
            moreFormats: "More formats..."
        },
        formatDecreaseDecimal: "Decrease decimal",
        formatIncreaseDecimal: "Increase decimal",
        freeze: "Freeze panes",
        freezeButtons: {
            freezePanes: "Freeze panes",
            freezeRows: "Freeze rows",
            freezeColumns: "Freeze columns",
            unfreeze: "Unfreeze panes"
        },
        insertComment: "Insert comment",
        insertImage: "Insert image",
        italic: "Italic",
        merge: "Merge cells",
        mergeButtons: {
            mergeCells: "Merge all",
            mergeHorizontally: "Merge horizontally",
            mergeVertically: "Merge vertically",
            unmerge: "Unmerge"
        },
        open: "Open...",
        paste: "Paste",
        redo: "Redo",
        undo: "Undo",
        exportAs: "Export...",
        toggleGridlines: "Toggle gridlines",
        sort: "Sort",
        sortButtons: {
            // sortSheetAsc: "Sort sheet A to Z",
            // sortSheetDesc: "Sort sheet Z to A",
            sortRangeAsc: "Sort range A to Z",
            sortRangeDesc: "Sort range Z to A"
        },
        textColor: "Text Color",
        textWrap: "Wrap text",
        underline: "Underline",
        validation: "Data validation...",
        hyperlink: "Link"
    };

    var defaultTools = {
        file: [
            "open",
            "exportAs",
        ],
        format: [
            "format",
            "formatDecreaseDecimal",
            "formatIncreaseDecimal"
        ],
        home: [
            ["undo", "redo"],
            "separator",
            ["cut", "copy", "paste"],
            "separator",
            "fontFamily", "fontSize", "increaseFontSize", "decreaseFontSize",
            "separator",
            ["bold", "italic", "underline"],
            "separator",
            "textColor",
            "separator",
            "backgroundColor",
            "borders",
            "separator",
            "alignment",
            "textWrap",
        ],
        insert: [
            "insertComment",
            "separator",
            "hyperlink",
            "separator",
            "insertImage",
            "separator",
            ["addColumnLeft", "addColumnRight", "addRowBelow", "addRowAbove"],
            "separator",
            ["deleteColumn", "deleteRow"]
        ],
        data: [
            "sort",
            "separator",
            "filter",
            "separator",
            "validation"
        ],
        view: [
            "freeze",
            "merge",
            "toggleGridlines"
        ]
    };

    var defaultFormats = kendo.spreadsheet.formats = {
        automatic: null,
        text: "@",
        number: "#,0.00",
        percent: "0.00%",
        financial: '_("$"* #,##0.00_);_("$"* (#,##0.00);_("$"* "-"??_);_(@_)',
        currency: "$#,##0.00;[Red]$#,##0.00",
        date: "m/d/yyyy",
        time: "h:mm:ss AM/PM",
        dateTime: "m/d/yyyy h:mm",
        duration: "[h]:mm:ss"
    };

    var colorPickerPalette = [ //metro palette
        "#ffffff", "#000000", "#d6ecff", "#4e5b6f", "#7fd13b", "#ea157a", "#feb80a", "#00addc", "#738ac8", "#1ab39f",
        "#f2f2f2", "#7f7f7f", "#a7d6ff", "#d9dde4", "#e5f5d7", "#fad0e4", "#fef0cd", "#c5f2ff", "#e2e7f4", "#c9f7f1",
        "#d8d8d8", "#595959", "#60b5ff", "#b3bcca", "#cbecb0", "#f6a1c9", "#fee29c", "#8be6ff", "#c7d0e9", "#94efe3",
        "#bfbfbf", "#3f3f3f", "#007dea", "#8d9baf", "#b2e389", "#f272af", "#fed46b", "#51d9ff", "#aab8de", "#5fe7d5",
        "#a5a5a5", "#262626", "#003e75", "#3a4453", "#5ea226", "#af0f5b", "#c58c00", "#0081a5", "#425ea9", "#138677",
        "#7f7f7f", "#0c0c0c", "#00192e", "#272d37", "#3f6c19", "#750a3d", "#835d00", "#00566e", "#2c3f71", "#0c594f"
    ];

    var COLOR_PICKER_MESSAGES = kendo.spreadsheet.messages.colorPicker = {
        reset: "Reset color",
        customColor: "Custom color...",
        apply: "Apply",
        cancel: "Cancel"
    };

    var toolDefaults = {
        undo: {
           type: "button",
           name: "undo",
           icon: "undo",
           command: "undo"
        },
        redo: {
           type: "button",
           name: "redo",
           icon: "redo",
           command: "redo"
        },
        separator: { type: "separator" },
        //home tab
        open: {
            type: "open",
            name: "open",
            icon: "folder-open",
            extensions: ".xlsx",
            command: "OpenCommand"
        },
        exportAs: {
            type: "button",
            name: "exportAs",
            dialog: "exportAs",
            overflow: "never",
            icon: "download"
        },
        bold: {
            type: "button",
            command: "PropertyChangeCommand",
            property: "bold",
            value: true,
            icon: "bold",
            togglable: true
        },
        italic: {
            type: "button",
            command: "PropertyChangeCommand",
            property: "italic",
            value: true,
            icon: "italic",
            togglable: true
        },
        underline: {
            type: "button",
            command: "PropertyChangeCommand",
            property: "underline",
            value: true,
            icon: "underline",
            togglable: true
        },
        formatDecreaseDecimal: {
            type: "button",
            name: "formatDecreaseDecimal",
            command: "AdjustDecimalsCommand",
            value: -1,
            icon: "decimal-decrease"
        },
        formatIncreaseDecimal: {
            type: "button",
            name: "formatIncreaseDecimal",
            command: "AdjustDecimalsCommand",
            value: 1,
            icon: "decimal-increase"
        },
        textWrap: {
            type: "button",
            name: "textWrap",
            command: "TextWrapCommand",
            property: "wrap",
            value: true,
            icon: "text-wrap",
            togglable: true
        },
        cut: {
            type: "button",
            name: "cut",
            command: "ToolbarCutCommand",
            icon: "cut"
        },
        copy: {
            type: "button",
            name: "copy",
            command: "ToolbarCopyCommand",
            icon: "copy"
        },
        paste: {
            type: "button",
            name: "paste",
            command: "ToolbarPasteCommand",
            icon: "clipboard"
        },
        alignment: {
            type: "component",
            name: "alignment",
            property: "alignment",
            component: "DropDownButton",
            element: getDefaultToolElement("align-left"),
            overflowComponent: {
                type: "button",
                dialog: "alignment",
                icon: "align-left"
            },
            componentOptions: {
                fillMode: "flat",
                items: [
                    { attributes: { "data-value": "left", "data-property": "textAlign", "data-command": "PropertyChangeCommand" }, icon: "align-left", text: MESSAGES.alignmentButtons.justifyLeft },
                    { attributes: { "data-value": "center", "data-property": "textAlign", "data-command": "PropertyChangeCommand" }, icon: "align-center", text: MESSAGES.alignmentButtons.justifyCenter },
                    { attributes: { "data-value": "right", "data-property": "textAlign", "data-command": "PropertyChangeCommand" }, icon: "align-right", text: MESSAGES.alignmentButtons.justifyRight },
                    { attributes: { "data-value": "justify", "data-property": "textAlign", "data-command": "PropertyChangeCommand" }, icon: "align-justify", text: MESSAGES.alignmentButtons.justifyFull },
                    { attributes: { class: "k-separator" } },
                    { attributes: { "data-value": "top", "data-property": "verticalAlign", "data-command": "PropertyChangeCommand" }, icon: "align-top", text: MESSAGES.alignmentButtons.alignTop },
                    { attributes: { "data-value": "center", "data-property": "verticalAlign", "data-command": "PropertyChangeCommand" }, icon: "align-middle", text: MESSAGES.alignmentButtons.alignMiddle },
                    { attributes: { "data-value": "bottom", "data-property": "verticalAlign", "data-command": "PropertyChangeCommand" }, icon: "align-bottom", text: MESSAGES.alignmentButtons.alignBottom }
                ],
                commandOn: "click"
            }
        },
        backgroundColor: {
            type: "component",
            name: "backgroundColor",
            commandOn: "change",
            command: "PropertyChangeCommand",
            property: "background",
            component: "ColorPicker",
            componentOptions: {
                view: "palette",
                toolIcon: "droplet",
                fillMode: "flat",
                palette: colorPickerPalette,
                clearButton: true,
                messages: COLOR_PICKER_MESSAGES,
                input: false,
                commandOn: "change"
            },
            overflowComponent: {
                type: "button",
                dialog: "colorPicker",
                icon: "droplet",
                property: "background"
            }
        },
        textColor: {
            type: "component",
            name: "textColor",
            commandOn: "change",
            command: "PropertyChangeCommand",
            property: "color",
            component: "ColorPicker",
            componentOptions: {
                view: "palette",
                fillMode: "flat",
                toolIcon: "foreground-color",
                palette: colorPickerPalette,
                clearButton: true,
                messages: COLOR_PICKER_MESSAGES,
                input: false,
                commandOn: "change"
            },
            overflowComponent: {
                type: "button",
                dialog: "colorPicker",
                icon: "foreground-color",
                property: "color"
            }
        },
        fontFamily: {
            type: "component",
            name: "fontFamily",
            command: "PropertyChangeCommand",
            property: "fontFamily",
            component: "DropDownList",
            overflowComponent: {
                type: "button",
                dialog: "fontFamily",
                icon: "font-family"
            },
            componentOptions: {
                dataSource: ["Arial", "Courier New", "Georgia", "Times New Roman", "Trebuchet MS", "Verdana"],
                value: "Arial",
                fillMode: "flat",
                commandOn: "change"
            }
        },
        fontSize: {
            type: "component",
            name: "fontSize",
            command: "PropertyChangeCommand",
            property: "fontSize",
            component: "ComboBox",
            overflowComponent: {
                type: "button",
                dialog: "fontSize",
                icon: "font-size"
            },
            componentOptions: {
                dataSource: [8, 9, 10, 11, 12, 13, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72],
                value: 12,
                fillMode: "flat",
                commandOn: "change"
            }
        },
        increaseFontSize: {
            type: "button",
            name: "increaseFontSize",
            command: "IncreaseFontSize",
            property: "fontSize",
            icon: "font-grow"
        },
        decreaseFontSize: {
            type: "button",
            name: "decreaseFontSize",
            command: "DecreaseFontSize",
            property: "fontSize",
            icon: "font-shrink"
        },
        format: {
            type: "component",
            name: "format",
            component: "DropDownButton",
            element: getDefaultToolElement("custom-format"),
            overflowComponent: {
                type: "button",
                dialog: "formatCells",
                icon: "custom-format"
            },
            componentOptions: {
                fillMode: "flat",
                items: [
                    { attributes: { "data-value": defaultFormats.automatic, "data-command": "PropertyChangeCommand", "data-property": "format" }, text: MESSAGES.formatTypes.automatic },
                    { attributes: { "data-value": defaultFormats.text, "data-command": "PropertyChangeCommand", "data-property": "format" }, text: MESSAGES.formatTypes.text },
                    { attributes: { "data-value": defaultFormats.number, "data-command": "PropertyChangeCommand", "data-property": "format" }, text: MESSAGES.formatTypes.number, sample: "1,499.99" },
                    { attributes: { "data-value": defaultFormats.percent, "data-command": "PropertyChangeCommand", "data-property": "format" }, text: MESSAGES.formatTypes.percent, sample: "14.50%" },
                    { attributes: { "data-value": defaultFormats.financial, "data-command": "PropertyChangeCommand", "data-property": "format" }, text: MESSAGES.formatTypes.financial, sample: "(1,000.12)" },
                    { attributes: { "data-value": defaultFormats.currency, "data-command": "PropertyChangeCommand", "data-property": "format" }, text: MESSAGES.formatTypes.currency, sample: "$1,499.99" },
                    { attributes: { "data-value": defaultFormats.date, "data-command": "PropertyChangeCommand", "data-property": "format" }, text: MESSAGES.formatTypes.date, sample: "4/21/2012" },
                    { attributes: { "data-value": defaultFormats.time, "data-command": "PropertyChangeCommand", "data-property": "format" }, text: MESSAGES.formatTypes.time, sample: "5:49:00 PM" },
                    { attributes: { "data-value": defaultFormats.dateTime, "data-command": "PropertyChangeCommand", "data-property": "format" }, text: MESSAGES.formatTypes.dateTime, sample: "4/21/2012 5:49:00" },
                    { attributes: { "data-value": defaultFormats.duration, "data-command": "PropertyChangeCommand", "data-property": "format" }, text: MESSAGES.formatTypes.duration, sample: "168:05:00" },
                    { attributes: { "data-value": "popup", "data-popup": "formatCells" }, text: MESSAGES.formatTypes.moreFormats },
                ],
                itemTemplate: (data) => {
                    let $kendoOutput;
                    $kendoOutput = `<span class="k-link k-menu-link"><span class="k-menu-link-text">`; if (data.sample) {                    $kendoOutput += `<span class='k-spreadsheet-sample'>${kendo.htmlEncode(data.sample)}</span> `; }                    $kendoOutput += `${kendo.htmlEncode(data.text)}</span></span>`;
                    return $kendoOutput;
                },
                commandOn: "click"
            }
        },
        filter: {
            type: "button",
            name: "filter",
            property: "hasFilter",
            icon: "filter",
            command: "FilterCommand",
            togglable: true,
            enable: false
        },
        merge: {
            type: "component",
            name: "merge",
            component: "DropDownButton",
            element: getDefaultToolElement("cells-merge"),
            overflowComponent: {
                type: "button",
                dialog: "merge",
                icon: "cells-merge"
            },
            componentOptions: {
                fillMode: "flat",
                items: [
                    { attributes: { "data-value": "cells", "data-command": "MergeCellCommand" }, icon: "cells-merge", text: MESSAGES.mergeButtons.mergeCells },
                    { attributes: { "data-value": "horizontally", "data-command": "MergeCellCommand" }, icon: "cells-merge-horizontally", text: MESSAGES.mergeButtons.mergeHorizontally },
                    { attributes: { "data-value": "vertically", "data-command": "MergeCellCommand" }, icon: "cells-merge-vertically", text: MESSAGES.mergeButtons.mergeVertically },
                    { attributes: { "data-value": "unmerge", "data-command": "MergeCellCommand" }, icon: "table-unmerge", text: MESSAGES.mergeButtons.unmerge }
                ],
                commandOn: "click"
            }
        },
        freeze: {
            type: "component",
            name: "freeze",
            component: "DropDownButton",
            element: getDefaultToolElement("pane-freeze"),
            overflowComponent: {
                type: "button",
                dialog: "freeze",
                icon: "pane-freeze"
            },
            componentOptions: {
                fillMode: "flat",
                items: [
                    { attributes: { "data-value": "panes", "data-command": "FreezePanesCommand" }, icon: "pane-freeze", text: MESSAGES.freezeButtons.freezePanes },
                    { attributes: { "data-value": "rows", "data-command": "FreezePanesCommand" }, icon: "row-freeze", text: MESSAGES.freezeButtons.freezeRows },
                    { attributes: { "data-value": "columns", "data-command": "FreezePanesCommand" }, icon: "column-freeze", text: MESSAGES.freezeButtons.freezeColumns },
                    { attributes: { "data-value": "unfreeze", "data-command": "FreezePanesCommand" }, icon: "table-unmerge", text: MESSAGES.freezeButtons.unfreeze }
                ],
                commandOn: "click"
            }
        },
        borders: {
            type: "popupButton",
            name: "borders",
            icon: "borders-all",
            popupComponent: kendo.spreadsheet.BorderPalette,
            commandOn: "change",
            command: "BorderChangeCommand",
            overflowComponent: {
                type: "button",
                dialog: "borders"
            }
        },
        formatCells: {
            type: "button",
            dialog: "formatCells",
            overflow: "never"
        },
        hyperlink: {
            type: "button",
            name: "hyperlink",
            dialog: "hyperlink",
            icon: "link"
        },
        toggleGridlines: {
            type: "button",
            name: "toggleGridlines",
            command: "GridLinesChangeCommand",
            property: "gridLines",
            value: true,
            icon: "borders-none",
            togglable: true,
            selected: true
        },
        insertComment: {
            type: "button",
            name: "insertComment",
            dialog: "insertComment",
            property: "comment",
            togglable: true,
            icon: "comment"
        },
        insertImage: {
            type: "button",
            name: "insertImage",
            dialog: "insertImage",
            icon: "image",
        },

        //insert tab
        addColumnLeft: {
            type: "button",
            name: "addColumnLeft",
            command: "AddColumnCommand",
            value: "left",
            icon: "table-column-insert-left"
        },
        addColumnRight: {
            type: "button",
            name: "addColumnRight",
            command: "AddColumnCommand",
            value: "right",
            icon: "table-column-insert-right"
        },
        addRowBelow: {
            type: "button",
            name: "addRowBelow",
            command: "AddRowCommand",
            value: "below",
            icon: "table-row-insert-below"
        },
        addRowAbove: {
            type: "button",
            name: "addRowAbove",
            command: "AddRowCommand",
            value: "above",
            icon: "table-row-insert-above"
        },
        deleteColumn: {
            type: "button",
            name: "deleteColumn",
            command: "DeleteColumnCommand",
            icon: "table-column-delete"
        },
        deleteRow: {
            type: "button",
            name: "deleteRow",
            command: "DeleteRowCommand",
            icon: "table-row-delete"
        },

        //data tab
        sort: {
            type: "component",
            name: "sort",
            component: "DropDownButton",
            element: getDefaultToolElement("sort-desc"),
            overflowComponent: {
                type: "button",
                dialog: "sort",
                icon: "sort-desc"
            },
            componentOptions: {
                fillMode: "flat",
                items: [
                    { attributes: { "data-value": "asc", "data-command": "SortCommand" }, sheet: false, text: MESSAGES.sortButtons.sortRangeAsc, icon: "sort-asc" },
                    { attributes: { "data-value": "desc", "data-command": "SortCommand" }, sheet: false, text: MESSAGES.sortButtons.sortRangeDesc, icon: "sort-desc" },
                ],
                commandOn: "click"
            }
        },
        validation: {
            type: "button",
            name: "validation",
            dialog: "validation",
            icon: "exclamation-circle"
        }
    };

    var SpreadsheetToolBar = ToolBar.extend({
        init: function(element, options) {
            Object.keys(toolDefaults).forEach((t) => {
                if (t !== "validation") {
                    toolDefaults[t].showText = "overflow";
                }
            });

            options.tools = options.tools || SpreadsheetToolBar.prototype.options.tools[options.toolbarName];
            options.parentMessages = MESSAGES;
            options.defaultTools = toolDefaults;

            ToolBar.fn.init.call(this, element, options);
            var handleClick = this._click.bind(this);

            this.element.addClass("k-spreadsheet-toolbar k-toolbar-md");

            this.bind({
                click: handleClick,
                toggle: handleClick,
                change: handleClick
            });
        },
        _click: function(e) {
            var target = e.target,
                property = target.data("property"),
                value = e.value || target.data("value") || e.target.val(),
                commandType = target.data("command"),
                dialog = target.data("dialog"),
                options = target.data("options") || {};

            if (value === "popup") {
                // Special case to open custom format dialog from option of the format DDL
                dialog = target.data("popup");
            }

            if (commandType === "IncreaseFontSize") {
                commandType = "PropertyChangeCommand";
                value++;
            }

            if (commandType === "DecreaseFontSize") {
                commandType = "PropertyChangeCommand";
                value--;
            }

            options.property = property || null;
            options.value = value || null;

            if (dialog) {
                this.dialog({
                    name: dialog,
                    options: options
                });
                return;
            }

            if (!commandType) {
                return;
            }

            var args = {
                command: commandType,
                options: options
            };

            if (typeof args.options.value === "boolean") {
                args.options.value = e.checked ? true : null;
            }

            this.action(args);
        },
        events: [
            "action",
            "dialog"
        ],
        options: {
            name: "SpreadsheetToolBar",
            resizable: true,
            tools: defaultTools,
            fillMode: "flat"
        },
        action: function(args) {
            this.trigger("action", args);
        },
        dialog: function(args) {
            this.trigger("dialog", args);
        },
        refresh: function(activeCell) {
            var range = activeCell,
                tools = this._tools();

            for (var i = 0; i < tools.length; i++) {
                var property = tools[i].property,
                    tool = tools[i].tool,
                    value = kendo.isFunction(range[property]) ? range[property]() : range;

                if (property == "gridLines") {
                    // this isn't really a property of the range, it's per-sheet.
                    value = range.sheet().showGridLines();
                }

                this._updateTool(tool, value, property, range);
            }

            this.resize(true);
        },
        _tools: function() {
            return this.element.find("[data-property]").toArray().map(function(element) {
                element = $(element);
                return {
                    property: element.attr("data-property"),
                    tool: this._getItem(element)
                };
            }.bind(this));
        },
        _updateTool: function(tool, value, property, range) {
            var component = tool.component,
                toolbarEl = tool.toolbarEl,
                widget = kendo.widgetInstance(toolbarEl.find("[data-role]")),
                menuItem = tool.menuItem,
                toggle = false,
                vertical, text, menu, selection, enabled, label;

            if (property === "hasFilter") {
                selection = range.sheet().selection();

                if (selection && selection._ref && selection._ref.height) {
                    enabled = value || selection._ref.height() > 1;

                    this.enable(toolbarEl, enabled);
                }
            } else if (property === "alignment") {
                vertical = value.verticalAlign();
                text = value.textAlign();
                menu = component.menu.element;

                menu.find(".k-item .k-link").removeClass("k-selected");
                menu.find(".k-item[data-property=textAlign][data-value=" + text + "] .k-link").addClass("k-selected");
                menu.find(".k-item[data-property=verticalAlign][data-value=" + vertical + "] .k-link").addClass("k-selected");
            } else if (property === "background" || property === "color") {
                widget.value(value);
            } else if (property === "fontFamily" || property === "fontSize") {
                if (toolbarEl.is("button")) {
                    toolbarEl.attr("data-value", value);
                    toolbarEl.data("value", value);
                    return;
                }
                label = menuItem.find(".k-menu-link-text").text().split("(")[0].trim();
                menuItem.find(".k-menu-link-text").text(label + " (" + value + ") ...");
                widget.value(value);
            }

            if (component && component.toggle) {
                if (typeof value === "boolean") {
                    toggle = !!value;
                } else if (typeof value === "string") {
                    if (toolbarEl.data("value")) {
                        toggle = toolbarEl.data("value") === value;
                    } else {
                        // if no value is specified in the tool options,
                        // assume it should be ON if the range value is not null, and OFF otherwise.
                        toggle = value != null;
                    }
                }

                this.toggle(toolbarEl, toggle);
            }
        }
    });

    kendo.cssProperties.propertyDictionary["SpreadsheetToolBar"] = kendo.cssProperties.propertyDictionary["ToolBar"];

    kendo.spreadsheet.ToolBar = SpreadsheetToolBar;

    kendo.spreadsheet.Menu = kendo.ui.Menu;

})(window.kendo);

function clone(hash, target) {
        if (!target) {
            target = {};
        }
        if (Object.assign) {
            return Object.assign(target, hash);
        }
        return Object.keys(hash).reduce(function(copy, key) {
            copy[key] = hash[key];
            return copy;
        }, target);
    }

    function drawTabularData(options) {
        var progress = new $.Deferred();
        var promise = progress.promise();

        options = clone(options, {
            dataSource       : null,
            guidelines       : true,
            guideColor       : "#000",
            columns          : null,
            headerBackground : "#999",
            headerColor      : "#000",
            oddBackground    : null,
            evenBackground   : null,
            fontFamily       : "Arial",
            fontSize         : 12,
            paperSize        : "A4",
            margin           : "1cm",
            landscape        : true,
            fitWidth         : false,
            scale            : 1,
            rowHeight        : 20,
            maxEmpty         : 1,
            useGridFormat    : true
        });

        // retrieve fonts; custom fonts should be already loaded
        kendo.drawing.pdf.defineFont(
            kendo.drawing.drawDOM.getFontFaces(document)
        );

        var charWidth = charWidthFunction(options.fontFamily, options.fontSize);

        function textWidth(value) {
            if (value != null) {
                var width = 12;         // magic numbers :-/
                for (var i = value.length; --i >= 0;) {
                    width += charWidth(value.charAt(i));
                }
                return width;
            }
            return 0;
        }

        var border = options.guidelines ? { size: 1, color: options.guideColor } : null;

        function mkCell(data) {
            if (!border) {
                return data;
            }
            return clone(data, {
                borderLeft: border,
                borderTop: border,
                borderRight: border,
                borderBottom: border
            });
        }

        options.dataSource.fetch(function(){
            var data = options.dataSource.data();
            if (!data.length) {
                return progress.reject("Empty dataset");
            }

            // this really must be present
            var columns = options.columns.map(function(col){
                if (typeof col == "string") {
                    return { title: col, field: col };
                } else {
                    return col;
                }
            });
            var columnTitles = columns.map(function(col){
                return col.title || col.field;
            });
            var columnWidths = columnTitles.map(textWidth);

            // prepare data for a Sheet object's fromJSON method
            var rows = data.map(function(row, rowIndex){
                return {
                    cells: columns.map(function(col, colIndex){
                        var value = row[col.field];

                        // NOTE: value might not be string.  I added option useGridFormat (default
                        // true), which will use a column's format, if present, to convert the value
                        // to a string, so that we can measure the width right now.
                        if (options.useGridFormat) {
                            if (value != null) {
                                if (col.format) {
                                    value = kendo.format(col.format, value);
                                } else {
                                    value += "";
                                }
                            }
                            // adjust the column widths while we're at it
                            columnWidths[colIndex] = Math.max(
                                textWidth(value),
                                columnWidths[colIndex]
                            );
                        }

                        // if options.useGridFormat is false and col.format is present, pass it over
                        // to the spreadsheet.  In that case we should calculate the widths after
                        // the spreadsheet is created (XXX to be implemented when someone needs it).
                        return mkCell({
                            value: value,
                            format: options.useGridFormat ? null : col.format,
                            background: rowIndex % 2 ? options.evenBackground : options.oddBackground
                        });
                    })
                };
            });

            // insert header line
            rows.unshift({
                cells: columnTitles.map(function(label){
                    return mkCell({
                        value: label,
                        background: options.headerBackground,
                        color: options.headerColor
                    });
                })
            });

            // init a Sheet object.  Note that we have to add one
            // extra-row and column, because the very last ones can't
            // have right/bottom borders (known limitation).
            var sheet = new kendo.spreadsheet.Sheet(
                rows.length + 1,        // rows
                columns.length + 1,     // columns
                options.rowHeight,      // row height
                50,                     // column width
                20,                     // header height
                20,                     // header width,
                {                       // default cell style
                    fontFamily: options.fontFamily,
                    fontSize: options.fontSize,
                    verticalAlign: "center"
                }
            );

            // load data
            sheet.fromJSON({
                name: "Sheet1",
                rows: rows,
                columns: columnWidths.map(function(w, i){
                    return { index: i, width: w };
                })
            });

            sheet.draw({
                paperSize  : options.paperSize,
                landscape  : options.landscape,
                margin     : options.margin,
                guidelines : false, // using borders instead (better contrast)
                scale      : options.scale,
                fitWidth   : options.fitWidth,
                maxEmpty   : options.maxEmpty,
                headerRows : 1
            }, progress.resolve.bind(progress));
        });

        return promise;
    }

    var CACHE_CHAR_WIDTH = {};

    var charWidthFunction = function(fontFamily, fontSize) {
        var id = fontSize + ":" + fontFamily;
        var func = CACHE_CHAR_WIDTH[id];
        if (!func) {
            var span, div = document.createElement("div");
            div.style.position = "fixed";
            div.style.left = "-10000px";
            div.style.top = "-10000px";
            div.style.fontFamily = fontFamily;
            div.style.fontSize = fontSize + "px";
            div.style.whiteSpace = "pre";
            for (var i = 32; i < 128; ++i) {
                span = document.createElement("span");
                span.appendChild(document.createTextNode(String.fromCharCode(i)));
                div.appendChild(span);
            }
            document.body.appendChild(div);
            var widths = {};
            for (i = 32, span = div.firstChild; i < 128 && span; ++i, span = span.nextSibling) {
                widths[i] = span.offsetWidth;
            }
            while ((span = div.firstChild)) {
                div.removeChild(span);
            }
            func = CACHE_CHAR_WIDTH[id] = function(ch) {
                var code = ch.charCodeAt(0);
                var width = widths[code];
                if (width == null) {
                    // probably not an ASCII character, let's cache its width as well
                    span = document.createElement("span");
                    span.appendChild(document.createTextNode(String.fromCharCode(code)));
                    div.appendChild(span);
                    width = widths[code] = span.offsetWidth;
                    div.removeChild(span);
                }
                return width;
            };
        }
        return func;
    };

    kendo.spreadsheet.drawTabularData = drawTabularData;

(function(kendo) {


    let registerEditor = kendo.spreadsheet.registerEditor;
    var $ = kendo.jQuery;

    registerEditor("_validation_date", function(){
        var context, calendar, popup;

        function create() {
            if (!calendar) {
                calendar = $("<div>").kendoCalendar();
                popup = $("<div>").kendoPopup();
                calendar.appendTo(popup);
                calendar = calendar.getKendoCalendar();
                popup = popup.getKendoPopup();

                calendar.bind("change", function(){
                    popup.close();
                    var date = calendar.value();
                    if (!context.range.format()) {
                        context.range.format("yyyy-mm-dd");
                    }
                    context.callback(kendo.spreadsheet.dateToNumber(date));
                });
            }
            popup.setOptions({
                anchor: context.view.element.querySelector(".k-spreadsheet-editor-button"),
                origin: context.alignLeft ? "bottom right" : "bottom left",
                position: context.alignLeft ? "top right" : "top left"
            });
        }

        function open() {
            create();

            var date = context.range.value();
            var sheet = context.range.sheet();
            if (date != null) {
                calendar.value(kendo.spreadsheet.numberToDate(date));
            } else {
                calendar.value(null);
            }
            var val = context.validation;
            if (val) {
                var min = kendo.ui.Calendar.fn.options.min;
                var max = kendo.ui.Calendar.fn.options.max;
                var fromValidation = val.from;
                var toValidation = val.to;
                var formula = kendo.spreadsheet.calc.runtime.Formula;

                if (/^(?:greaterThan|between)/.test(val.comparerType)) {
                    if(fromValidation instanceof formula && _rowAndColPresent(fromValidation.value)) {
                        min = kendo.spreadsheet.numberToDate(sheet.range(fromValidation.value.row, fromValidation.value.col).value());
                    } else {
                        min = kendo.spreadsheet.numberToDate(fromValidation.value);
                    }
                }
                if (val.comparerType == "between") {
                    if(toValidation instanceof formula && _rowAndColPresent(toValidation.value)) {
                        max = kendo.spreadsheet.numberToDate(sheet.range(toValidation.value.row, toValidation.value.col).value());
                    } else {
                        max = kendo.spreadsheet.numberToDate(val.to.value);
                    }
                }
                if (val.comparerType == "lessThan" || val.comparerType == "lessThanOrEqualTo") {
                    if(fromValidation instanceof formula && _rowAndColPresent(fromValidation.value)) {
                        max = kendo.spreadsheet.numberToDate(sheet.range(fromValidation.value.row, fromValidation.value.col).value());
                    } else {
                        max = kendo.spreadsheet.numberToDate(val.from.value);
                    }
                }
                calendar.setOptions({
                    disableDates: function(date) {
                        var from, to;

                        if(fromValidation && fromValidation instanceof formula && _rowAndColPresent(fromValidation.value)) {
                            from = sheet.range(fromValidation.value.row, fromValidation.value.col).value();
                        } else {
                            from = fromValidation ? fromValidation.value|0 : 0;
                        }

                        if(toValidation && toValidation instanceof formula && _rowAndColPresent(toValidation.value)) {
                            to = sheet.range(toValidation.value.row, toValidation.value.col).value();
                        } else {
                            to = toValidation ? toValidation.value|0 : 0;
                        }

                        date = kendo.spreadsheet.dateToNumber(date) | 0;
                        return !kendo.spreadsheet.validation
                            .validationComparers[val.comparerType](date, from, to);
                    },
                    min: min,
                    max: max
                });
            } else {
                calendar.setOptions({ disableDates: null, min: null, max: null });
            }
            popup.open();
        }

        function _rowAndColPresent(value) {
            return (value && value.row !== null && value.col !== null && value.row > -1 && value.col > -1);
        }

        return {
            edit: function(options) {
                context = options;
                open();
            },
            icon: "calendar"
        };
    });

    registerEditor("_validation_list", function(){
        var context, list, popup;
        function create() {
            if (!list) {
                list = $("<ul class='k-list k-reset'/>").kendoStaticList({
                    template   : "#:value#",
                    selectable : true,
                    autoBind   : false
                });
                popup = $("<div class='k-spreadsheet-list-popup'>").kendoPopup();
                list.appendTo(popup);
                popup = popup.getKendoPopup();
                list = list.getKendoStaticList();

                list.bind("change", function(){
                    popup.close();
                    var item = list.value()[0];
                    if (item) {
                        context.callback(item.value);
                    }
                });
            }
            popup.setOptions({
                anchor: context.view.element.querySelector(".k-spreadsheet-editor-button"),
                origin: context.alignLeft ? "bottom right" : "bottom left",
                position: context.alignLeft ? "top right" : "top left"
            });
        }
        function open() {
            create();
            var items = context.validation.from.value;
            var data = [], add = function(el){ data.push({ value: el }); };
            if (items instanceof kendo.spreadsheet.calc.runtime.Matrix) {
                items.each(add);
            } else {
                // actually Excel expects a simple string for list
                // validation (comma-separated labels).
                (items+"").split(/\s*,\s*/).forEach(add);
            }
            var dataSource = new kendo.data.DataSource({ data: data });
            list.setDataSource(dataSource);
            dataSource.read();
            popup.open();
        }
        return {
            edit: function(options) {
                context = options;
                open();
            },
            icon: "caret-alt-down"
        };
    });

})(window.kendo);

(function(kendo) {

        var $ = kendo.jQuery;
        var outerWidth = kendo._outerWidth;
        var DOT = ".";
        var EMPTYCHAR = " ";
        var sheetsBarClassNames = {
            sheetsBarWrapper: "k-spreadsheet-sheets-bar",
            sheetsBarSheetsWrapper: "k-tabstrip k-tabstrip-bottom",
            sheetsBarAdd: "k-spreadsheet-sheet-add",
            sheetsBarSheetsMenu: "k-spreadsheet-sheets-menu",
            sheetsBarRemove: "k-spreadsheet-sheets-remove",
            sheetsBarItems: "k-spreadsheet-sheets",
            sheetsBarEditor: "k-spreadsheet-sheets-editor",
            sheetsBarScrollable: "k-tabstrip-scrollable",
            sheetsBarNext: "k-tabstrip-next",
            sheetsBarPrev: "k-tabstrip-prev",
            sheetsBarKItem: "k-item",
            sheetsBarKTabstripItem: "k-tabstrip-item",
            sheetsBarKActive: "k-active",
            sheetsBarKTextbox: "k-textbox",
            sheetsBarKLink: "k-link",
            sheetsBarKLinkText: "k-link-text",
            sheetsBarKIcon: "k-icon",
            sheetsBarKFontIcon: "k-icon",
            sheetsBarKButton: "k-button k-icon-button",
            sheetsBarKButtonDefaults: "k-button-md k-rounded-md k-button-solid k-button-solid-base",
            sheetsBarKButtonBare: "k-button-md k-rounded-md k-button-flat k-button-flat-base",
            sheetsBarArrowWIcon: "caret-alt-left",
            sheetsBarArrowEIcon: "caret-alt-right",
            sheetsBarKReset: "k-reset k-tabstrip-items k-tabstrip-items-start",
            sheetsBarXIcon: "x",
            sheetsBarMoreIcon: "caret-alt-down",
            sheetsBarKSprite: "k-sprite",
            sheetsBarPlusIcon: "plus",
            sheetsBarMenuIcon: "menu",
            sheetsBarHintWrapper: "k-widget k-tabstrip k-tabstrip-bottom k-spreadsheet-sheets-items-hint",
            sheetsBarKResetItems: "k-reset k-tabstrip-items k-tabstrip-items-start"
        };

        var SheetsBar = kendo.ui.Widget.extend({
            init: function(element, options) {
                var classNames = SheetsBar.classNames;

                kendo.ui.Widget.call(this, element, options);

                element = this.element;

                element.addClass(classNames.sheetsBarWrapper);

                this._openDialog = options.openDialog;

                this._addButton();
                this._menuButton();
                this._tree = new kendo.dom.Tree(element[0]);

                this._tree.render([this._createSheetsWrapper([])]);
                this._initSheetDropDownButtons();

                this._toggleScrollEvents(true);

                this._createSortable();

                this._sortable.bind("start", this._onSheetReorderStart.bind(this));

                this._sortable.bind("end", this._onSheetReorderEnd.bind(this));

                element.on("click", "[ref-sheetsbar-more-button]", this._onSheetContextMenu.bind(this));

                element.on("click", "li", this._onSheetSelect.bind(this));

                element.on("dblclick", "li" + DOT + classNames.sheetsBarKItem + DOT + classNames.sheetsBarKItem, this._createEditor.bind(this));
            },

            options: {
                name: "SheetsBar",
                scrollable: {
                    distance: 200
                }
            },

            events: [
                "select",
                "reorder",
                "rename"
            ],

            _createEditor: function() {
                if (this._editor) {
                    return;
                }

                this._renderSheets(this._sheets, this._selectedIndex, true);
                this._editor = this.element
                    .find(kendo.format("input{0}{1}", DOT, SheetsBar.classNames.sheetsBarEditor))
                    .trigger("focus")
                    .on("keydown", this._onEditorKeydown.bind(this))
                    .on("blur", this._onEditorBlur.bind(this));
            },

            _destroyEditor: function(canceled) {
                var newSheetName = canceled ? null : this._editor.val();
                this._editor.off();
                this._editor = null;
                this._renderSheets(this._sheets, this._selectedIndex, false);
                this._onSheetRename(newSheetName);
            },

            renderSheets: function(sheets, selectedIndex) {
                if (!sheets || selectedIndex < 0) {
                    return;
                }

                this._renderSheets(sheets, selectedIndex, false);
            },

            _renderSheets: function(sheets, selectedIndex, isInEditMode) {
                var that = this;
                var classNames = SheetsBar.classNames;

                that._isRtl = kendo.support.isRtl(that.element);
                that._sheets = sheets;
                that._selectedIndex = selectedIndex;

                that._renderHtml(isInEditMode, true);

                if (!that._scrollableAllowed()) {
                    return;
                }

                var sheetsWrapper = that._sheetsWrapper();

                sheetsWrapper.addClass(classNames.sheetsBarScrollable + EMPTYCHAR + classNames.sheetsBarSheetsWrapper);

                that._toggleScrollButtons();
            },

            _toggleScrollButtons: function(toggle) {
                var that = this;
                var ul = that._sheetsGroup();
                var wrapper = that._sheetsWrapper();
                var scrollLeft = kendo.scrollLeft(ul);
                var prev = wrapper.find(DOT + SheetsBar.classNames.sheetsBarPrev);
                var next = wrapper.find(DOT + SheetsBar.classNames.sheetsBarNext);

                if (toggle === false) {
                    prev.addClass('k-disabled');
                    next.addClass('k-disabled');
                } else {
                    prev.toggleClass('k-disabled', !(that._isRtl ? scrollLeft < ul[0].scrollWidth - ul[0].offsetWidth - 1 : scrollLeft !== 0));
                    next.toggleClass('k-disabled', !(that._isRtl ? scrollLeft !== 0 : scrollLeft < ul[0].scrollWidth - ul[0].offsetWidth - 1));
                }

            },

            _toggleScrollEvents: function(toggle) {
                var that = this;
                var classNames = SheetsBar.classNames;
                var options = that.options;
                var scrollPrevButton;
                var scrollNextButton;
                var sheetsWrapper = that._sheetsWrapper();
                scrollPrevButton = sheetsWrapper.find(DOT + classNames.sheetsBarPrev);
                scrollNextButton = sheetsWrapper.find(DOT + classNames.sheetsBarNext);

                if (toggle) {
                    scrollPrevButton.on("mousedown", function(event) {
                        event.preventDefault();
                        event.stopPropagation();
                        that._nowScrollingSheets = true;
                        that._scrollSheetsByDelta(options.scrollable.distance * (that._isRtl ? 1 : -1));
                    });

                    scrollNextButton.on("mousedown", function(event) {
                        event.preventDefault();
                        event.stopPropagation();
                        that._nowScrollingSheets = true;
                        that._scrollSheetsByDelta(options.scrollable.distance * (that._isRtl ? -1 : 1));
                    });

                    scrollPrevButton.add(scrollNextButton).on("mouseup", function() {
                        that._nowScrollingSheets = false;
                    });
                } else {
                    scrollPrevButton.off();
                    scrollNextButton.off();
                }
            },

            _renderHtml: function(isInEditMode, renderScrollButtons) {
                var idx;
                var sheetElements = [];
                var dom = kendo.dom;
                var element = dom.element;
                var sheets = this._sheets;
                var selectedIndex = this._selectedIndex;
                var classNames = SheetsBar.classNames;

                for (idx = 0; idx < sheets.length; idx++) {
                    var sheet = sheets[idx];

                    var isSelectedSheet = (idx === selectedIndex);
                    var attr = { className: classNames.sheetsBarKItem + EMPTYCHAR + classNames.sheetsBarKTabstripItem + EMPTYCHAR, role: "tab" };
                    var elementContent = [];
                    if (sheet.state() !== 'visible') {
                        attr.className += "k-hidden ";
                    }

                    if (isSelectedSheet) {
                        attr.className += classNames.sheetsBarKActive;
                    }

                    if (isSelectedSheet && isInEditMode) {
                        elementContent.push(element("input", {
                            type: "text",
                            value: sheet.name(),
                            className: classNames.sheetsBarKTextbox + EMPTYCHAR + classNames.sheetsBarEditor,
                            maxlength: 50
                        }, []));
                    } else {
                        elementContent.push(element("span", {
                            className: classNames.sheetsBarKLink,
                            title: sheet.name()
                        }, [element("span", { className: classNames.sheetsBarKLinkText }, [dom.text(sheet.name())])]));

                        let contextMenuButton = element($(kendo.html.renderButton($(`<button ref-sheetsbar-more-button data-sheet-name="${sheet.name()}" class="k-menu-button"></button>`), {
                            icon: classNames.sheetsBarMoreIcon,
                            fillMode: "flat"
                        }))[0]);

                        elementContent.push(element("span", {
                            className: "k-item-actions",
                            'data-type': 'context-menu',
                        }, [contextMenuButton]));
                    }

                    sheetElements.push(element("li", attr, elementContent));
                }

                kendo.destroy(this._sheetsWrapper());
                this._addButton();
                this._menuButton();
                this._tree.render([ this._createSheetsWrapper(sheetElements, renderScrollButtons)]);
                this._initSheetDropDownButtons();
            },
            _initSheetDropDownButtons: function() {
                let that = this;
                this.element.find("[ref-sheetsbar-more-button]").each(function(ind, btnEl){
                    let el = $(btnEl);
                    let allSheets = that._sheets || [];
                    let visibleSheets = allSheets.filter(sheet => sheet.state() === 'visible');
                    let isSingleVisibleSheet = !that._sheets || that._sheets && visibleSheets.length < 2;
                    let sheetName = el.data("sheetName");
                    let shouldAllowMoveRight = !(isSingleVisibleSheet || (that._sheets && ind == allSheets.length - 1));
                    let shouldAllowMoveLeft = !(isSingleVisibleSheet || (that._sheets && ind === 0));
                    el.kendoDropDownButton({
                        icon: SheetsBar.classNames.sheetsBarMoreIcon,
                        fillMode: "flat",
                        items: [
                            { text: "Delete", icon: "trash", attributes: { "data-command": "delete", "data-sheet-name": sheetName }, enabled: !isSingleVisibleSheet },
                            { text: "Duplicate", icon: "copy", attributes: { "data-command": "duplicate", "data-sheet-name": sheetName } },
                            { text: "Rename", icon: "pencil", attributes: { "data-command": "rename", "data-sheet-name": sheetName } },
                            { text: "Hide", icon: "eye-slash", attributes: { "data-command": "hide", "data-sheet-name": sheetName }, enabled: !isSingleVisibleSheet },
                            { text: "Move Right", icon: "arrow-right", attributes: { "data-command": "move-right", "data-sheet-name": sheetName }, enabled: shouldAllowMoveRight },
                            { text: "Move Left", icon: "arrow-left", attributes: { "data-command": "move-left", "data-sheet-name": sheetName }, enabled: shouldAllowMoveLeft },
                        ],
                        click: that._onSheetContextMenu.bind(that)
                    });
                });
            },
            _createSheetsWrapper: function(sheetElements, renderScrollButtons) {
                var element = kendo.dom.element;
                var classNames = SheetsBar.classNames;
                var itemsWrapper = element('div', { className: 'k-tabstrip-items-wrapper k-hstack' });
                var childrenElements = [element("ul", {
                    className: classNames.sheetsBarKReset,
                    role: "tablist"
                }, sheetElements), null, null];

                renderScrollButtons = true;

                if (renderScrollButtons) {
                    var baseButtonClass = classNames.sheetsBarKButton + EMPTYCHAR + classNames.sheetsBarKButtonBare + EMPTYCHAR;

                    childrenElements[1] = (element("span", { ariaHidden: "true", tabIndex: -1, className: baseButtonClass + classNames.sheetsBarPrev }, [
                        element($(kendo.ui.icon({ icon: classNames.sheetsBarArrowWIcon, iconClass: "k-button-icon" }))[0])
                    ]));

                    childrenElements[2] = (element("span", { ariaHidden: "true", tabIndex: -1, className: baseButtonClass + classNames.sheetsBarNext }, [
                        element($(kendo.ui.icon({ icon: classNames.sheetsBarArrowEIcon, iconClass: "k-button-icon" }))[0])
                    ]));
                }

                itemsWrapper.children = childrenElements;

                return element("div", { className: classNames.sheetsBarItems }, [itemsWrapper]);
            },

            _createSortable: function() {
                var classNames = SheetsBar.classNames;
                this._sortable = new kendo.ui.Sortable(this.element, {
                    filter: `ul li.${classNames.sheetsBarKItem}`,
                    container: DOT + classNames.sheetsBarItems,
                    axis: "x",
                    animation: false,
                    ignore: "input",
                    end: function() {
                        if (this.draggable.hint) {
                            this.draggable.hint.remove();
                        }
                    },
                    hint: function(element) {
                        var hint = $(element).clone().attr("ref-sheetsbar-sortable-hint", "");
                        return hint.wrap("<div class='" + classNames.sheetsBarHintWrapper + "'><ul class='" + classNames.sheetsBarKResetItems + "'></ul></div>").closest("div");
                    }
                });
            },

            _onEditorKeydown: function(e) {
                if (this._editor) {
                    if (e.which === 13) {
                        this._destroyEditor();
                    }

                    if (e.which === 27) {
                        this._destroyEditor(true);
                    }
                }
            },

            _onEditorBlur: function() {
                if (this._editor) {
                    this._destroyEditor();
                }
            },

            _onSheetReorderEnd: function(e) {
                e.preventDefault();
                this.trigger("reorder", { oldIndex: e.oldIndex, newIndex: e.newIndex });
            },

            _onSheetReorderStart: function(e) {
                if (this._editor) {
                    e.preventDefault();
                }
            },
            _onSheetContextMenu: function(e) {
                let sheetName = $(e.target).closest("li").data("sheetName");
                let command = $(e.target).closest("li").data("command");

                if (this._editor) {
                    this._destroyEditor();
                }

                if (sheetName && command) {
                    switch(command) {
                        case "delete":
                            this._onSheetRemove(e);
                            break;
                        case "duplicate":
                            this._onSheetDuplicate(sheetName);
                            break;
                        case "rename":
                            this._renamePrompt(sheetName);
                            break;
                        case "hide":
                            this._hideSheet(sheetName);
                            break;
                        case "move-right":
                            this._moveSheet(sheetName, 1);
                            break;
                        case "move-left":
                            this._moveSheet(sheetName, -1);
                            break;
                    }
                }
            },
            _onSheetDuplicate: function(sheetName) {
                this.trigger("duplicate", { name: sheetName });
            },
            _renamePrompt: function(sheetName) {
                let renameSheetIndex = this._sheets.findIndex(sh => sh.name() == sheetName);
                let closeCallback = function(e) {
                    let dlg = e.sender;
                    if (dlg._newSheetName && dlg._newSheetName !== sheetName) {
                        this.trigger("rename", { name: dlg._newSheetName, sheetIndex: renameSheetIndex });
                    }
                }.bind(this);

                this._openDialog("renameSheet", {
                    close: closeCallback,
                    _oldSheetName: sheetName
                });
            },
            _hideSheet: function(sheetName) {
                this.trigger("hide", { name: sheetName });
            },
            _moveSheet: function(sheetName, direction) {
                let sheetIndex = this._sheets.findIndex(sheet => sheet.name() === sheetName);
                let newSheetIndex = sheetIndex + direction;
                if (newSheetIndex < 0 || newSheetIndex >= this._sheets.length) {
                    return;
                }

                this.trigger("reorder", { oldIndex: sheetIndex, newIndex: newSheetIndex });
            },
            _onSheetRemove: function(e) {
                var removedSheetName = $(e.target).closest("li").data("sheetName");

                if (this._editor) {
                    this._destroyEditor();
                }

                var closeCallback = function(e) {
                    var dlg = e.sender;

                    if (dlg.isConfirmed()) {
                        this.trigger("remove", { name: removedSheetName, confirmation: true });
                    }
                }.bind(this);

                this._openDialog("confirmation", {
                    close: closeCallback
                });
            },

            _onSheetSelect: function(e) {
                var selectedSheetText = $(e.target).text();

                if ($(e.target).is(DOT + SheetsBar.classNames.sheetsBarEditor) || !selectedSheetText) {
                    e.preventDefault();
                    return;
                }

                if (this._editor) {
                    this._destroyEditor();
                }

                this._scrollSheetsToItem($(e.target).closest("li"));
                this.trigger("select", { name: selectedSheetText, isAddButton: false });
            },

            _onSheetRename: function(newSheetName) {
                if (this._sheets[this._selectedIndex].name() === newSheetName || newSheetName === null) {
                    return;
                }

                this.trigger("rename", { name: newSheetName, sheetIndex: this._selectedIndex });
            },

            _onAddSelect: function(ev) {
                ev.sender.element.removeClass("k-focus");
                this.trigger("select", { isAddButton: true });
            },

            _onMenuSelect: function(ev) {
                let sheetName = $(ev.target).closest("li").data("sheetName");
                if (sheetName) {
                    this.trigger("show", { name: sheetName });
                }
            },

            _addButton: function() {
                var classNames = SheetsBar.classNames;
                let addButton = this.element.find("." + classNames.sheetsBarAdd);
                if (!addButton.length) {
                    addButton = $(`<button class="${classNames.sheetsBarAdd}" aria-label="Add new sheet"></button>`).appendTo(this.element);
                } else {
                    kendo.destroy(addButton);
                }

                addButton.kendoButton({
                    icon: classNames.sheetsBarPlusIcon,
                    fillMode: "flat",
                    click: this._onAddSelect.bind(this)
                });
            },
            _menuButton: function() {
                var classNames = SheetsBar.classNames;
                let menuButton = this.element.find("." + classNames.sheetsBarSheetsMenu);
                if (!menuButton.length) {
                    menuButton = $(`<button class="${classNames.sheetsBarSheetsMenu}"></button>`).appendTo(this.element);
                } else {
                    kendo.destroy(menuButton);
                }

                menuButton.kendoDropDownButton({
                    icon: classNames.sheetsBarMenuIcon,
                    fillMode: "flat",
                    items: (this._sheets || []).map(sheet => ({
                        text: sheet.name(),
                        attributes: { 'data-sheet-name': sheet.name() },
                        icon: sheet.state() === 'visible' ? 'eye' : 'eye-slash',
                        cssClass: classNames
                    })),
                    click: this._onMenuSelect.bind(this)
                });
            },

            destroy: function() {
                this._sortable.destroy();
            },

            _scrollableAllowed: function() {
                var options = this.options;
                return options.scrollable && !isNaN(options.scrollable.distance);
            },

            _scrollSheetsToItem: function(item) {
                var that = this;
                var sheetsGroup = that._sheetsGroup();
                var currentScrollOffset = kendo.scrollLeft(sheetsGroup);
                var itemWidth = outerWidth(item);
                var itemOffset = that._isRtl ? item.position().left : item.position().left - sheetsGroup.children().first().position().left;
                var sheetsGroupWidth = sheetsGroup[0].offsetWidth;
                var itemPosition;

                if (that._isRtl) {
                    if (itemOffset < 0) {
                        itemPosition = currentScrollOffset + itemOffset - (sheetsGroupWidth - currentScrollOffset);
                    } else if (itemOffset + itemWidth > sheetsGroupWidth) {
                        itemPosition = currentScrollOffset + itemOffset - itemWidth;
                    }
                } else {
                    if (currentScrollOffset + sheetsGroupWidth < itemOffset + itemWidth) {
                        itemPosition = itemOffset + itemWidth - sheetsGroupWidth;
                    } else if (currentScrollOffset > itemOffset) {
                        itemPosition = itemOffset;
                    }
                }

                sheetsGroup.finish().animate({ "scrollLeft": itemPosition }, "fast", "linear", function() {
                    that._toggleScrollButtons();
                });
            },

            _sheetsGroup: function() {
                return this._sheetsWrapper().find("ul");
            },

            _sheetsWrapper: function() {
                return this.element.find(DOT + SheetsBar.classNames.sheetsBarItems);
            },

            _scrollSheetsByDelta: function(delta) {
                var that = this;
                var sheetsGroup = that._sheetsGroup();
                var scrLeft = kendo.scrollLeft(sheetsGroup);

                sheetsGroup.finish().animate({ "scrollLeft": scrLeft + delta }, "fast", "linear", function() {
                    if (that._nowScrollingSheets) {
                        that._scrollSheetsByDelta(delta);
                    } else {
                        that._toggleScrollButtons();
                    }
                });
            }
        });

        kendo.spreadsheet.SheetsBar = SheetsBar;
        $.extend(true, SheetsBar, { classNames: sheetsBarClassNames });
    })(window.kendo);

(function(kendo) {

        var $ = kendo.jQuery;
        var Widget = kendo.ui.Widget;
        var classNames = {
            details: "k-details",
            button: "k-button",
            detailsSummary: "k-details-summary",
            detailsContent: "k-details-content",
            icon: "k-icon",
            iconCollapseName: "caret-br",
            iconExpandName: "caret-alt-right",
            iconSearchName: "search",
            textbox: "k-textbox",
            wrapper: "k-spreadsheet-filter-menu",
            filterByCondition: "k-spreadsheet-condition-filter",
            filterByValue: "k-spreadsheet-value-filter",
            valuesTreeViewWrapper: "k-spreadsheet-value-treeview-wrapper",
            actionButtons: "k-actions"
        };

        kendo.spreadsheet.messages.filterMenu = {
            all: "All",
            sortAscending: "Sort range A to Z",
            sortDescending: "Sort range Z to A",
            filterByValue: "Filter by value",
            filterByCondition: "Filter by condition",
            apply: "Apply",
            search: "Search",
            addToCurrent: "Add to current selection",
            clear: "Clear",
            blanks: "(Blanks)",
            operatorNone: "None",
            and: "AND",
            or: "OR",
            operators: {
                string: {
                    contains: "Text contains",
                    doesnotcontain: "Text does not contain",
                    startswith: "Text starts with",
                    endswith: "Text ends with",
                    matches: "Text matches",
                    doesnotmatch: "Text does not match"
                },
                date: {
                    eq:  "Date is",
                    neq: "Date is not",
                    lt:  "Date is before",
                    gt:  "Date is after"
                },
                number: {
                    eq: "Is equal to",
                    neq: "Is not equal to",
                    gte: "Is greater than or equal to",
                    gt: "Is greater than",
                    lte: "Is less than or equal to",
                    lt: "Is less than"
                }
            }
        };

        var Details = Widget.extend({
            init: function(element, options) {
                Widget.fn.init.call(this, element, options);

                this.element.addClass(FilterMenu.classNames.details);

                this._summary = this.element.find("." + FilterMenu.classNames.detailsSummary)
                    .on("click", this._toggle.bind(this));

                var iconName = options.expanded ? FilterMenu.classNames.iconCollapseName : FilterMenu.classNames.iconExpandName;
                this._icon = $(kendo.ui.icon(iconName))
                    .prependTo(this._summary);

                this._container = kendo.wrap(this._summary.next(), true);

                if (!options.expanded) {
                    this._container.hide();
                }
            },
            options: {
                name: "Details"
            },
            events: [ "toggle" ],
            visible: function() {
                return this.options.expanded;
            },
            toggle: function(show) {
                var animation = kendo.fx(this._container).expand("vertical");

                animation.stop()[show ? "reverse" : "play"]();
                kendo.ui.icon(this._icon, { icon: show ? FilterMenu.classNames.iconExpandName : FilterMenu.classNames.iconCollapseName });

                this.options.expanded = !show;
            },
            _toggle: function() {
                var show = this.visible();
                this.toggle(show);
                this.trigger("toggle", { show: show });
            }
        });

        kendo.data.binders.spreadsheetFilterValue = kendo.data.Binder.extend({
            init: function(element, bindings, options) {
                kendo.data.Binder.fn.init.call(this, element, bindings, options);

                this._change = this.change.bind(this);
                $(this.element).on("change", this._change);
            },
            refresh: function() {
                var that = this,
                    value = that.bindings.spreadsheetFilterValue.get(); //get the value from the View-Model

                $(that.element).val(value instanceof Date ? "" : value);
            },
            change: function() {
                var value = this.element.value;
                this.bindings.spreadsheetFilterValue.set(value); //update the View-Model
            }
        });

        kendo.data.binders.widget.spreadsheetFilterValue = kendo.data.Binder.extend({
            init: function(widget, bindings, options) {
                kendo.data.Binder.fn.init.call(this, widget.element[0], bindings, options);

                this.widget = widget;
                this._change = this.change.bind(this);
                this.widget.first("change", this._change);
            },
            refresh: function() {
                var binding = this.bindings.spreadsheetFilterValue,
                    value = binding.get(),
                    type = $(this.widget.element).data("filterType");

                if ((type === "date" && value instanceof Date) || (type === "number" && !isNaN(value))) {
                    this.widget.value(value);
                } else {
                    this.widget.value(null);
                }
            },
            change: function() {
                var value = this.widget.value(),
                    binding = this.bindings.spreadsheetFilterValue;

                binding.set(value);
            }
        });

        var templates = {
            filterByValue: (data) =>
                `<div class='${classNames.detailsSummary}'>${data.messages.filterByValue}</div>` +
                `<div class='${classNames.detailsContent}'>` +
                    "<div class='k-searchbox k-input k-input-md k-rounded-md k-input-solid'>" +
                        kendo.ui.icon({ icon: "search", iconClass: "k-input-icon" }) +
                        `<input class='k-input-inner' autocomplete='off' placeholder='${data.messages.search}' data-${data.ns}bind='events: { input: filterValues }' />` +
                    "</div>" +
                    `<div data-${data.ns}bind='visible: hasActiveSearch'><input class='k-checkbox k-checkbox-md k-rounded-md' type='checkbox' data-${data.ns}bind='checked: appendToSearch' id='_${data.guid}'/><label class='k-checkbox-label' for='_${data.guid}'>${data.messages.addToCurrent}</label></div>` +
                    `<div class='${classNames.valuesTreeViewWrapper}'>` +
                        `<div data-${data.ns}role='treeview' ` +
                            `bind:data-${data.ns}checkboxes='checkboxesConfig' `+
                            `data-${data.ns}bind='source: valuesDataSource, events: { check: valuesChange, select: valueSelect }' `+
                            "></div>" +
                    "</div>" +
                "</div>",
            filterByCondition: (data) =>
                `<div class='${classNames.detailsSummary}'>${data.messages.filterByCondition}</div>` +
                `<div class='${classNames.detailsContent}'>` +
                    '<div>' +
                        '<select ' +
                            `aria-label="${data.messages.filterByCondition}" ` +
                            `data-${data.ns}role="dropdownlist"` +
                            `data-${data.ns}bind="value: operator, source: operators, events: { change: operatorChange } "` +
                            `data-${data.ns}value-primitive="false"` +
                            `data-${data.ns}option-label="${data.messages.operatorNone}"` +
                            `data-${data.ns}height="auto"` +
                            `data-${data.ns}text-field="text"` +
                            `data-${data.ns}value-field="unique">`+
                        '</select>'+
                    '</div>' +

                    `<div data-${data.ns}bind="visible: isString">` +
                        `<input aria-label="string-value" data-filter-type="string" data-${data.ns}bind="spreadsheetFilterValue: customFilter.criteria[0].value" class="k-textbox" />`+
                    '</div>' +

                    `<div data-${data.ns}bind="visible: isNumber">` +
                        `<input aria-label="number-value" data-filter-type="number" data-${data.ns}role="numerictextbox" data-${data.ns}bind="spreadsheetFilterValue: customFilter.criteria[0].value" />`+
                    '</div>' +

                    `<div data-${data.ns}bind="visible: isDate">` +
                        `<input aria-label="date-value" data-filter-type="date" data-${data.ns}role="datepicker" data-${data.ns}bind="spreadsheetFilterValue: customFilter.criteria[0].value" />`+
                    '</div>' +
                "</div>",
            menuItem: (data) =>
                `<li data-command='${data.command}' data-dir='${data.dir}'>` +
                    `${kendo.ui.icon(data.iconClass)}${data.text}` +
                "</li>",
            actionButtons: (data) =>
                `<button data-${data.ns}bind='click: apply' class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-primary'><span class='k-button-text'>${data.messages.apply}</span></button>` +
                `<button data-${data.ns}bind='click: clear' class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-base'><span class='k-button-text'>${data.messages.clear}</span></button>`
        };

        function distinctValues(values) {
            var hash = {};
            var result = [];

            for (var i = 0; i < values.length; i++) {
                if (!hash[values[i].value]) {
                    hash[values[i].value] = values[i];
                    result.push(values[i]);
                } else if (!hash[values[i].value].checked && values[i].checked) {
                    hash[values[i].value].checked = true;
                }
            }

            return result;
        }

        function filter(dataSource, query) {
            var hasVisibleChildren = false;
            var data = dataSource instanceof kendo.data.HierarchicalDataSource && dataSource.data();
            var valuesFilter = this;
            var values = this.values;
            for (var i = 0; i < data.length; i++) {
                var item = data[i];
                var text = item.text.toString().toLocaleLowerCase(kendo.culture().name);
                var itemVisible = query === true || query === "" || text.indexOf(query) >= 0;
                var filterSpread = filter.bind(valuesFilter);
                var anyVisibleChildren = filterSpread(item.children, query); // pass true if parent matches

                hasVisibleChildren = hasVisibleChildren || anyVisibleChildren || itemVisible;
                item.hidden = !itemVisible && !anyVisibleChildren;

                if (query.length || (values && !values.length)) {
                    item.checked = !item.hidden;
                } else if (values && values.indexOf(item.text) != -1){
                    item.checked = true;
                }
            }

            if (data) {
                // re-apply filter on children
                dataSource.filter({ field: "hidden", operator: "neq", value: true });
            }

            return hasVisibleChildren;
        }

        function uncheckAll(dataSource) {
            var data = dataSource instanceof kendo.data.HierarchicalDataSource && dataSource.data();

            for (var i = 0; i < data.length; i++) {
                var item = data[i];
                item.checked = false;

                if (item.hasChildren) {
                    uncheckAll(item.children);
                }
            }
        }

        var FilterMenuViewModel = kendo.spreadsheet.FilterMenuViewModel = kendo.data.ObservableObject.extend({
            valuesChange: function(e) {
                var dataSource = e ? e.sender.dataSource : this.valuesDataSource;
                var checked = function(item) {
                    return item.checked;
                };
                var value = function(item) {
                    return item.dataType === "date" ? kendo.spreadsheet.dateToNumber(item.value) : item.value;
                };
                var unique = function(value, index, array) {
                    return array.lastIndexOf(value) === index;
                };
                var data = dataSource.data();
                var values = data.toJSON()[0].items;
                var blanks = values.filter(function(item) {
                    return item.dataType === "blank";
                });

                blanks = blanks.length ? blanks[0].checked : false;
                values = values.filter(checked).map(value);

                if (this.appendToSearch && this.valueFilter && this.valueFilter.values.length) {
                    values = values.concat(this.valueFilter.values.toJSON()).sort().filter(unique);
                }

                this.set("valueFilter", {
                    values: values,
                    blanks: blanks
                });
            },
            valueSelect: function(e) {
                e.preventDefault();

                var node = e.sender.dataItem(e.node);
                node.set("checked", !node.checked);
            },
            hasActiveSearch: false,
            appendToSearch: false,
            filterValues: function(e) {
                var query = typeof e == "string" ? e : $(e.target).val().toLocaleLowerCase(kendo.culture().name);
                var dataSource = this.valuesDataSource;

                this.set("hasActiveSearch", !!query);

                var filterSpread = filter.bind(this.valueFilter);
                uncheckAll(dataSource);
                filterSpread(dataSource, query);
            },
            reset: function() {
                this.set("customFilter", { logic: "and", criteria: [ { operator: null, value: null } ] });
                this.set("valueFilter", { values: [] });
            },
            operatorChange: function(e) {
                var dataItem = e.sender.dataItem();
                this.set("operatorType", dataItem.type);

                // https://github.com/telerik/kendo-ui-core/issues/3317 - when switching from Value
                // filter to “filter by condition”, the customFilter property is lost.
                if (!this.get("customFilter")) {
                    this.reset();
                }

                this.set("customFilter.criteria[0].operator", dataItem.value);
            },
            isNone: function() {
                return this.get("operatorType") === undefined;
            },
            isString: function() {
                return this.get("operatorType") === "string";
            },
            isNumber: function() {
                return this.get("operatorType") === "number";
            },
            isDate: function() {
                return this.get("operatorType") === "date";
            }
        });

        function flattenOperators(operators) {
            var messages = kendo.spreadsheet.messages.filterMenu.operators;
            var result = [];
            for (var type in operators) {
                if (!operators.hasOwnProperty(type)) {
                    continue;
                }

                for (var operator in operators[type]) {
                    if (!operators[type].hasOwnProperty(operator)) {
                        continue;
                    }

                    result.push({
                        text: messages[type][operator],
                        value: operator,
                        unique: type + "_" + operator,
                        type: type
                    });
                }
            }
            return result;
        }

        var FilterMenuController = kendo.spreadsheet.FilterMenuController = {
            valuesTree: function(range, column) {
                return [{
                    text: kendo.spreadsheet.messages.filterMenu.all,
                    expanded: true,
                    checked: false,
                    items: this.values(range.resize({ top: 1 }), column)
                }];
            },
            values: function(range, column) {
                var values = [];
                var messages = kendo.spreadsheet.messages.filterMenu;
                var columnRange = range.column(column);
                var sheet = range.sheet();

                columnRange.forEachCell(function(row, col, cell) {
                    var checked = true;
                    if (sheet.isHiddenRow(row)) {
                        checked = false;
                    }

                    var value = cell.value;
                    var dataType = cell.dataType;
                    var text = cell.text;

                    if (value === undefined) {
                        dataType = "blank";
                    } else if (cell.format) {
                        dataType = kendo.spreadsheet.formatting.type(value, cell.format);
                    } else {
                        dataType = typeof value;
                    }

                    if (value !== null && cell.format) {
                        text = kendo.spreadsheet.formatting.text(value, cell.format);
                    } else {
                        text = dataType == "blank" ? messages.blanks : value;
                    }

                    if (dataType === "percent" || dataType === "currency") { //treat percent as number
                        dataType = "number";
                    }

                    if (dataType === "date") {
                        value = kendo.spreadsheet.numberToDate(value);
                    }

                    values.push({
                        dataType: dataType,
                        value: value,
                        text: text,
                        checked: checked
                    });
                });

                values = distinctValues(values);

                values.sort(function(a, b) {
                    if (a.dataType === b.dataType) {
                        return 0;
                    }

                    if (a.dataType === "blank" || b.dataType === "blank") {
                        return a.dataType === "blank" ? -1 : 1;
                    }

                    if (a.dataType === "number" || b.dataType === "number") {
                        return a.dataType === "number" ? -1 : 1;
                    }

                    if (a.dataType === "date" || b.dataType === "date") {
                        return a.dataType === "date" ? -1 : 1;
                    }

                    return 0;
                });

                return values;
            },

            filterType: function(range, column) {
                // 1. try to infer type from current filter
                var sheet = range.sheet();
                var filter = this.filterForColumn(column, sheet);
                var type;

                filter = filter && filter.filter.toJSON();

                if (filter && filter.filter == "custom") {
                    var value = filter.criteria[0].value;

                    if (value instanceof Date) {
                        type = "date";
                    } else if (typeof value == "string") {
                        type = "string";
                    } else if (typeof value == "number") {
                        type = "number";
                    }
                }

                if (!type) {
                    // 2. try to infer type from column data
                    var topValue = this.values(range.row(1), column)[0];
                    type = topValue && topValue.dataType;

                    if (type == "blank") {
                        type = null;
                    }
                }

                return type;
            },

            filterForColumn: function(column, sheet) {
                var allFilters = sheet.filter();
                var filters;

                if (allFilters) {
                    filters =  allFilters.columns.filter(function(item) {
                        return item.index === column;
                    })[0];
                }

                return filters;
            },

            filter: function(column, sheet) {
                var columnFilters = this.filterForColumn(column, sheet);

                if (!columnFilters) {
                    return;
                }

                var options = columnFilters.filter.toJSON();
                var type = options.filter;

                delete options.filter;

                var result = {
                    type: type,
                    options: options
                };

                var criteria = options.criteria;
                if (criteria && criteria.length) {
                    result.operator = criteria[0].operator;
                }

                return result;
            }
        };

        var FilterMenu = Widget.extend({
            init: function(element, options) {
                Widget.call(this, element, options);

                this.element.addClass(FilterMenu.classNames.wrapper);

                this.viewModel = new FilterMenuViewModel({
                    active: "value",
                    checkboxesConfig: { checkChildren: true },
                    operator: null,
                    operators: flattenOperators(this.options.operators),
                    clear: this.clear.bind(this),
                    apply: this.apply.bind(this)
                });

                this._filterInit();
                this._popup();
                this._sort();
                this._filterByCondition();
                this._filterByValue();
                this._actionButtons();
            },

            options: {
                name: "FilterMenu",
                column: 0,
                range: null,
                operators: {
                    string: {
                        contains: "Text contains",
                        doesnotcontain: "Text does not contain",
                        startswith: "Text starts with",
                        endswith: "Text ends with",
                        matches: "Text matches",
                        doesnotmatch: "Text does not match"
                    },
                    date: {
                        eq:  "Date is",
                        neq: "Date is not",
                        lt:  "Date is before",
                        gt:  "Date is after"
                    },
                    number: {
                        eq: "Is equal to",
                        neq: "Is not equal to",
                        gte: "Is greater than or equal to",
                        gt: "Is greater than",
                        lte: "Is less than or equal to",
                        lt: "Is less than"
                    }
                }
            },

            events: [
                "action"
            ],

            destroy: function() {
                Widget.fn.destroy.call(this);

                this.menu.destroy();
                this.valuesTreeView.destroy();
                this.popup.destroy();
            },

            openFor: function(anchor, event) {
                if ($(anchor).hasClass("k-spreadsheet-view-size")) {
                    // on touch devices this element will sit above to
                    // enable scrolling; it's not useful as an anchor,
                    // use event coords instead.
                    this.popup.open(event.pageX, event.pageY);
                } else {
                    this.popup.setOptions({ anchor: anchor });
                    this.popup.open();
                }
            },

            close: function() {
                this.popup.close();
            },

            clear: function() {
                this.action({
                    command: "ClearFilterCommand",
                    options: {
                        column: this.options.column
                    }
                });
                this.viewModel.reset();
                this.close();
            },

            apply: function() {
                this._active();

                var options = {
                    operatingRange: this.options.range,
                    column: this.options.column
                };

                var valueFilter;
                var customFilter;

                if (this.viewModel.active === "value") {
                    this.viewModel.valuesChange({ sender: this.valuesTreeView });
                    valueFilter = this.viewModel.valueFilter.toJSON();

                    if (valueFilter.blanks || (valueFilter.values && valueFilter.values.length)) {
                        options.valueFilter = valueFilter;
                    }
                } else if (this.viewModel.active === "custom") {
                    customFilter = this.viewModel.customFilter.toJSON();

                    if (customFilter.criteria.length && customFilter.criteria[0].value !== null) {
                        options.customFilter = customFilter;
                    }
                }

                if (options.valueFilter || options.customFilter) {
                    this.action({ command: "ApplyFilterCommand", options: options });
                }
            },

            action: function(options) {
                this.trigger("action", $.extend({ }, options));
            },

            _filterInit: function() {
                var column = this.options.column;
                var range = this.options.range;
                var sheet = range.sheet();
                var activeFilter = FilterMenuController.filter(column, sheet);

                if (activeFilter) {
                    var filterType = FilterMenuController.filterType(range, column);

                    this.viewModel.set("active", activeFilter.type);
                    this.viewModel.set(activeFilter.type + "Filter", activeFilter.options);

                    if (activeFilter.type == "custom") {
                        this.viewModel.set("operator", filterType + "_" + activeFilter.operator);
                        this.viewModel.set("operatorType", filterType);
                    }
                } else {
                    this.viewModel.reset();
                }
            },

            _popup: function() {
                this.popup = this.element.kendoPopup({
                    copyAnchorStyles: false
                }).data("kendoPopup");
            },

            _sort: function() {
                var template = kendo.template(FilterMenu.templates.menuItem);
                var messages = kendo.spreadsheet.messages.filterMenu;
                var items = [
                    { command: "sort", dir: "asc", text: messages.sortAscending, iconClass: "sort-asc" },
                    { command: "sort", dir: "desc", text: messages.sortDescending, iconClass: "sort-desc" }
                ];

                var ul = $("<ul></ul>", {
                    "html": kendo.render(template, items)
                }).appendTo(this.element);

                this.menu = ul.kendoMenu({
                    orientation: "vertical",
                    select: function(e) {
                        var dir = $(e.item).data("dir");
                        var range = this.options.range.resize({ top: 1 });
                        var options = {
                            value: dir,
                            sheet: false,
                            operatingRange: range,
                            column: this.options.column
                        };

                        this.action({ command: "SortCommand", options: options });
                    }.bind(this)
                }).data("kendoMenu");
            },

            _appendTemplate: function(template, className, details, expanded) {
                var compiledTemplate = kendo.template(template);
                var wrapper = $("<div class='" + className + "'/>").html(compiledTemplate({
                    messages: kendo.spreadsheet.messages.filterMenu,
                    guid: kendo.guid(),
                    ns: kendo.ns
                }));

                this.element.append(wrapper);

                if (details) {
                    details = new Details(wrapper, { expanded: expanded, toggle: this._detailToggle.bind(this) });
                }

                kendo.bind(wrapper, this.viewModel);

                return wrapper;
            },

            _detailToggle: function(e) {
                this.element
                    .find("[" + kendo.attr("role") + "=details]")
                    .not(e.sender.element)
                    .data("kendoDetails")
                    .toggle(!e.show);
            },

            _filterByCondition: function() {
                var isExpanded = this.viewModel.active === "custom";
                this._appendTemplate(FilterMenu.templates.filterByCondition, FilterMenu.classNames.filterByCondition, true, isExpanded);
            },

            _filterByValue: function() {
                var isExpanded = this.viewModel.active === "value";
                var wrapper = this._appendTemplate(FilterMenu.templates.filterByValue, FilterMenu.classNames.filterByValue, true, isExpanded);

                this.valuesTreeView = wrapper.find("[" + kendo.attr("role") + "=treeview]").data("kendoTreeView");

                var values = FilterMenuController.valuesTree(this.options.range, this.options.column);

                this.viewModel.set("valuesDataSource", new kendo.data.HierarchicalDataSource({ data: values, accentFoldingFiltering: kendo.culture().name }));
            },

            _actionButtons: function() {
                this._appendTemplate(FilterMenu.templates.actionButtons, FilterMenu.classNames.actionButtons, false);
            },

            _active: function() {
                var activeContainer = this.element.find("[" + kendo.attr("role") + "=details]").filter(function(index, element) {
                    return $(element).data("kendoDetails").visible();
                });

                if (activeContainer.hasClass(FilterMenu.classNames.filterByValue)) {
                    this.viewModel.set("active", "value");
                } else if (activeContainer.hasClass(FilterMenu.classNames.filterByCondition)) {
                    this.viewModel.set("active", "custom");
                }
            }
        });

        kendo.spreadsheet.FilterMenu = FilterMenu;
        $.extend(true, FilterMenu, { classNames: classNames, templates: templates });

    })(window.kendo);

(function(kendo) {

    kendo.spreadsheet.messages.workbook = {
        defaultSheetName: "Sheet"
    };

    kendo.spreadsheet.messages.view = {
        nameBox: "Name Box",
        formulaInput: "Formula Input",
        errors: {
            openUnsupported: "Unsupported format. Please select an .xlsx file.",
            shiftingNonblankCells: "Cannot insert cells due to data loss possibility. Select another insert location or delete the data from the end of your worksheet.",
            insertColumnWhenRowIsSelected: "Cannot insert column when all columns are selected.",
            insertRowWhenColumnIsSelected: "Cannot insert row when all rows are selected.",
            filterRangeContainingMerges: "Cannot create a filter within a range containing merges",
            sortRangeContainingMerges: "Cannot sort a range containing merges",
            cantSortMultipleSelection: "Cannot sort multiple selection",
            cantSortNullRef: "Cannot sort empty selection",
            cantSortMixedCells: "Cannot sort range containing cells of mixed shapes",
            validationError: "The value that you entered violates the validation rules set on the cell.",
            cannotModifyDisabled: "Cannot modify disabled cells.",
            insertRowBelowLastRow: "Cannot insert row below the last row.",
            insertColAfterLastCol: "Cannot insert column to the right of the last column."
        },
        tabs: {
            file: "File",
            home: "Home",
            insert: "Insert",
            format: "Format",
            data: "Data",
            view: "View"
        }
    };

    kendo.spreadsheet.messages.menus = {
        "cut": "Cut",
        "copy": "Copy",
        "paste": "Paste",
        "merge": "Merge",
        "unmerge": "Unmerge",
        "delete": "Delete",
        "hide": "Hide",
        "unhide": "Unhide",
        "bringToFront": "Bring to front",
        "sendToBack": "Send to back"
    };
})(window.kendo);

(function(kendo) {
    let $ = kendo.jQuery;

    if (kendo.PDFMixin) {
        let override_drawPDF = function(options) {
            var result = new $.Deferred();
            var callback = function(group) {
                result.resolve(group);
            };
            switch(options.area) {
            case "workbook":
                options.workbook.draw(options, callback);
                break;
            case "sheet":
                options.workbook.activeSheet().draw(options, callback);
                break;
            case "selection":
                options.workbook.activeSheet().selection().draw(options, callback);
                break;
            }

            return result.promise();
        };

         let overrideSaveAsPDF = function(options) {
            if (this.events.indexOf("pdfExport") === -1) {
                kendo.PDFMixin.extend(this);
                this.saveAsPDF = overrideSaveAsPDF;
                this._drawPDF = override_drawPDF;
            }

            var progress = new $.Deferred();
            var promise = progress.promise();
            var args = { promise: promise };
            if (this.trigger("pdfExport", args)) {
                return;
            }

            this._drawPDF(options, progress)
            .then(function(root) {
                let pdfResultPromise = options.forceProxy
                    ? kendo.pdf.exportPDF(root) // produce data URI for proxy
                    : kendo.pdf.exportPDFToBlob(root);
                return kendo.convertPromiseToDeferred(pdfResultPromise);
            })
            .done(function(dataURI) {
                kendo.saveAs({
                    dataURI: dataURI,
                    fileName: options.fileName,
                    proxyURL: options.proxyURL,
                    forceProxy: options.forceProxy,
                    proxyTarget: options.proxyTarget
                });

                progress.resolve();
            })
            .fail(function(err) {
                progress.reject(err);
            });

            return promise;
        };

        kendo.spreadsheet.Workbook.prototype.saveAsPDF = overrideSaveAsPDF;

        kendo.spreadsheet.Workbook.prototype._drawPDF = override_drawPDF;
    }
})(kendo);

const __meta__ = {
    id: "spreadsheet",
    name: "Spreadsheet",
    category: "web",
    description: "Spreadsheet component",
    depends: [
        "core", "binder", "colorpicker", "combobox", "data", "dom", "dropdownlist",
        "menu", "ooxml", "popup", "sortable", "toolbar", "treeview",
        "window", "validator", "excel", "pdf", "drawing", "list", "spreadsheet.common"]
};

(function(kendo, undefined$1) {
    if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
        return;
    }

    const K_ACTIVE = "k-active";
    let $ = kendo.jQuery;
    let Widget = kendo.ui.Widget;
    let NS = ".kendoSpreadsheet";
    let DOT = ".";
    let commonEngine = kendo.spreadsheet.commonEngine;
    let FormulaInput = kendo.spreadsheet.FormulaInput;
    let SheetsBar = kendo.spreadsheet.SheetsBar;

    function toActionSelector(selectors) {
        return selectors.map(function(action) {
            return '[data-action="' + action + '"]';
        }).join(",");
    }

    function convertSpreadsheetDeferredToJQueryDeferred(spreadsheetDeferred) {
        const deferred = $.Deferred();

        spreadsheetDeferred.then(
            function(value) { deferred.resolve(value); },
            function(reason) { deferred.reject(reason); }
        );

        if (typeof spreadsheetDeferred.progress === "function") {
            spreadsheetDeferred.progress(function(val) {
                deferred.notify(val);
            });
        }

        const promise = deferred.promise();
        promise.progress = function(fn) {
            deferred.progress(fn);
            return promise;
        };
        return promise;
    }

    function removeEventsFromOptions(options, eventNames) {
        const sanitized = {};
        for (let key in options) {
            if (options.hasOwnProperty(key) && eventNames.indexOf(key) === -1) {
                sanitized[key] = options[key];
            }
        }
        return sanitized;
    }

    let COMPOSITE_UNAVAILABLE_ACTION_SELECTORS = toActionSelector(['cut', 'copy', 'paste', 'insert-left', 'insert-right', 'insert-above', 'insert-below']);
    let UNHIDE_ACTION_SELECTORS = toActionSelector(['unhide-row', 'unhide-column']);
    let classNames = {
        wrapper: "k-spreadsheet"
    };

    let viewClassNames = commonEngine.View.classNames;

    const SPREADSHEET_CONTENT_HTML_TEMPLATE = ({ messages }) => `
                            <div ref-header-container class="k-spreadsheet-header"><div ref-header-menu></div><div ref-header-toolbar></div></div>
                            <div class="k-spreadsheet-action-bar">
                                <div class="k-spreadsheet-name-editor">
                                    <input ref-namebox-input title="${messages.nameBox || "Name Box"}" aria-label="${messages.nameBox || "Name Box"}"></input>
                                </div>
                                <div class="k-spreadsheet-formula-bar">
                                    ${kendo.ui.icon("formula-fx")}
                                    <div class="k-spreadsheet-formula-input"
                                        role="combobox"
                                        title="${messages.view.formulaInput}"
                                        aria-haspopup="menu"
                                        aria-expanded="false"
                                        contentEditable="true"
                                        spellCheck="false">
                                    </div>
                                </div>
                            </div>
                            <div class="k-spreadsheet-view">
                                <div class="k-spreadsheet-fixed-container"></div>
                                <div class="k-spreadsheet-scroller">
                                    <div class="k-spreadsheet-view-size"></div>
                                </div>
                                <div tabIndex="0" class="k-spreadsheet-clipboard" contentEditable="true" ></div>
                                <div contentEditable="true"
                                    spellCheck="false"
                                    role="combobox"
                                    title="${messages.view.formulaInput}"
                                    aria-haspopup="menu"
                                    aria-expanded="false"
                                    class="k-spreadsheet-formula-input k-spreadsheet-cell-editor">
                                </div>
                            </div>
                            <div ref-sheets-bar></div>`;

    const CELL_CONTEXT_MENU = ({ messages }) => `
                    <ul class="${viewClassNames.cellContextMenu}">
                        <li data-action="cut">${messages.menus.cut}</li>
                        <li data-action="copy">${messages.menus.copy}</li>
                        <li data-action="paste">${messages.menus.paste}</li>
                        <li class="k-separator"></li>
                        <li data-action="merge">${messages.menus.merge}</li>
                        <li data-action="unmerge">${messages.menus.unmerge}</li>
                    </ul>`;

    const ROW_HEADER_CONTEXT_MENU = ({ messages }) => `
                    <ul class="${viewClassNames.rowHeaderContextMenu}">
                        <li data-action="cut">${messages.menus.cut}</li>
                        <li data-action="copy">${messages.menus.copy}</li>
                        <li data-action="paste">${messages.menus.paste}</li>
                        <li class="k-separator"></li>
                        <li data-action="delete-row">${messages.menus.delete}</li>
                        <li data-action="hide-row">${messages.menus.hide}</li>
                        <li data-action="unhide-row">${messages.menus.unhide}</li>
                    </ul>`;

    const COL_HEADER_CONTEXT_MENU = ({ messages }) => `
                    <ul class="${viewClassNames.colHeaderContextMenu}">
                        <li data-action="cut">${messages.menus.cut}</li>
                        <li data-action="copy">${messages.menus.copy}</li>
                        <li data-action="paste">${messages.menus.paste}</li>
                        <li class="k-separator"></li>
                        <li data-action="delete-column">${messages.menus.delete}</li>
                        <li data-action="hide-column">${messages.menus.hide}</li>
                        <li data-action="unhide-column">${messages.menus.unhide}</li>
                    </ul>`;

    const DRAWING_CONTEXT_MENU = ({ messages }) => `
                    <ul class="${viewClassNames.drawingContextMenu}">
                        <li data-action="bring-to-front">${messages.menus.bringToFront}</li>
                        <li data-action="send-to-back">${messages.menus.sendToBack}</li>
                        <li class="k-separator"></li>
                        <li data-action="delete-drawing">${messages.menus.delete}</li>
                    </ul>`;

    kendo.spreadsheet.ContextMenu = kendo.ui.ContextMenu;

    let FormulaInputStaticList = kendo.ui.StaticList.extend({
        data: function(d) {
            return this.dataSource.data(d);
        },
        itemClick: function(handler) {
            this.bind("change", function(ev) { handler(this.value()[0]); });
        }
    });

    class FormulaInputRefArgs {
        constructor({ list, popup }) {
            this.current = { list, popup };
            this.clone = function clone() {
                return this;
            };
        }
    }

    class NameBoxRefArgs {
        constructor({ nameEditor, container }) {
            let combo = this.initializeComboBox(container, nameEditor);
            this.current = {
                value: (val) => {
                    if (val === undefined$1) {
                        const item = combo.value();
                        return item ? (item.name || item) : item;
                    } else {
                        combo.value(val || '');
                    }
                }
            };

            this.nameEditor = nameEditor;

            this.clone = function clone() {
                return this;
            };
        }

        initializeComboBox(container) {
            let that = this;
            let dataSource = new kendo.data.DataSource({
                transport: {
                    read: function(options) {
                        let data = that.nameEditor()?.readData() || [];
                        options.success(data);
                    },
                    cache: false
                }
            });

            let combo = $(container).kendoComboBox({
                clearButton: false,
                dataTextField: "name",
                dataValueField: "name",
                template: ({ name }) => `${kendo.htmlEncode(name)}<a role='button' class='k-button-delete' href='\\#'>${kendo.ui.icon("x")}</a>`,
                dataSource: dataSource,
                autoBind: false,
                ignoreCase: true,
                change: that.onChange.bind(that),
                noDataTemplate: () => "<div></div>",
                open: function() {
                    dataSource.read();
                }
            }).data("kendoComboBox");

            combo.input
                .on("keydown", that.onKeyDown.bind(this));
            combo.popup.element
                .addClass("k-spreadsheet-names-popup")

                .on("mousemove", function(ev) {
                    // XXX: should remove this when we find a better
                    // solution for the popup closing as we hover the
                    // resize handles.
                    ev.stopPropagation();
                })

                .on("click", ".k-button-delete", function(ev) {
                    ev.preventDefault();
                    ev.stopPropagation();
                    let item = $(ev.target).closest(".k-list-item");
                    item = combo.dataItem(item);
                    that.onDelete(item.name);
                });

            return combo;
        }

        // Event: Handle change in ComboBox
        onChange(event) {
            const editor = this.nameEditor();
            if (editor && event.sender.value()) {
                editor.trigger("select", { name: event.sender.value() });
            }
        }

        // Event: Delete button click within item
        onDelete(name) {
            this.nameEditor()?.trigger("delete", { name });
        }

        // Handling keyboard events like Enter, Escape
        onKeyDown(event) {
            const editor = this.nameEditor();
            if (editor) {
                if (event.key === 'Enter') {
                    const name = event.target.value;
                    editor.trigger("enter", { value: name });
                } else if (event.key === 'Escape') {
                    editor.trigger("cancel");
                }
            }
        }
    }

    let Spreadsheet = kendo.ui.Widget.extend({
        _renderInnerContent: function(element) {
            $(element).append(SPREADSHEET_CONTENT_HTML_TEMPLATE(this.options));
        },
        init: function(element, options) {
            let that = this;
            Widget.fn.init.call(this, element, options);

            this.options.messages = $.extend(true, {
                view: kendo.spreadsheet.messages.view,
                tabs: kendo.spreadsheet.messages.view.tabs,
                menus: kendo.spreadsheet.messages.menus,
                workbook: {
                    defaultSheetName: "Sheet",
                }
            }, this.options.messages);

            this.element.addClass(Spreadsheet.classNames.wrapper);
            this.element.attr("role", "application");
            this._renderInnerContent(this.element);

            this._createFormulaInputs();

            that.spreadsheetRef = this._initSpreadsheetWidget();

            this._bindWorkbookEvents();

            const activeSheet = this.activeSheet();
            this._onUpdateTools({ range: activeSheet.range(activeSheet.activeCell()) });

            this._resizeHandler = function() { that.resize(); };
            $(window).on("resize" + NS, this._resizeHandler);

            if (this._showWatermarkOverlay) {
                this._showWatermarkOverlay(this.element[0]);
            }
        },
        _initSpreadsheetWidget: function() {
            const that = this;
            const options = removeEventsFromOptions(that.options, that.events);

            let widgetOptions = $.extend({},
                options, {
                sheetsbar: that.options.sheetsbar != false ? that.options.sheetsbar : {},
                formulaBarInputRef: new FormulaInputRefArgs({
                    list: this._formulaBarInputRefList,
                    popup: this._formulaBarInputRefPopup
                }),
                formulaCellInputRef: new FormulaInputRefArgs({
                    list: this._formulaCellInputRefList,
                    popup: this._formulaCellInputRefPopup
                }),
                nameBoxRef: new NameBoxRefArgs({
                    container: that.element.find('.k-spreadsheet-name-editor [ref-namebox-input]'),
                    nameEditor: function() {
                        return that.spreadsheetRef?.view.nameEditor;
                    }
                }),
                createFilterMenu: function(options) {
                    let element = $("<div></div>").appendTo(that.element);
                    return new kendo.spreadsheet.FilterMenu(element, options);
                },
                createTabStrip: function(options) {
                    if (that.options.toolbar) {
                        that._initHeader();

                        return {
                            focus: function() { that.menu.wrapper.trigger("focus"); },
                            select: function(name) { that.menu.element.find(`li[ref-tab-name="${name}"]`).click(); },
                            destroy: function() { that.menu.destroy(); }
                        };
                    }
                },
                createSheetEditor: function(options) {
                    that.sheetsBar._createEditor();
                },
                createSheetBar: function(openDialogCallback) {
                    that._sheetsBar(openDialogCallback);
                },
                createContextMenus: function(options) {
                    that._dialogs = [];
                    that._initContextMenus();
                },
                createSheetDataSource: function(dataSource) {
                    return kendo.data.DataSource.create(dataSource);
                },
                getWorkbookCommand: function(commandName, commandOptions) {
                    return new kendo.spreadsheet[commandName](commandOptions);
                },
                getIconHTMLString: function(iconName) {
                    return $(kendo.ui.icon(iconName))[0];
                },
                update: function(e) {
                    that._onUpdateTools(e);
                    if (e.reason.sheetSelection && that.sheetsBar) {
                        that.sheetsBar.renderSheets(e.sender.sheets(), e.sender.sheetIndex(e.sheet));
                    }
                },
                message: (args) => {
                    // exclude these properties from the options
                    let { sender, name, ref, range, ...dialogOptions } = args;
                    let opts = $.extend({}, { pdfExport: that.options.pdf, excelExport: that.options.excel }, dialogOptions);
                    let dialog = kendo.spreadsheet.dialogs.create(args.name, opts);
                    const saveAsCallback = function(data, fileName, exportOptions) {
                        kendo.saveAs({ dataURI: data, fileName, ...exportOptions });
                    };


                    if (dialog) {
                        dialog.bind("action", function(args) {
                            if (args.command == "SaveAsCommand") {
                                let { sender, ...commandArgs } = args;
                                let fileName = args.options.name + args.options.extension;
                                if (args.options.extension === ".xlsx") {
                                    return that.spreadsheetRef.saveAsExcel($.extend(commandArgs, {
                                        fileName: fileName,
                                        Workbook: kendo.ooxml.Workbook,
                                        saveAs: saveAsCallback
                                    }));
                                } else if (this.options.extension === ".pdf") {
                                    return that.spreadsheetRef.saveAsPDF($.extend(args.options.pdf, { workbook: this.options.workbook, fileName: fileName }));
                                }
                            }

                            that.spreadsheetRef.executeCommand(args);
                        });

                        dialog.bind("deactivate", () => {
                            dialog.destroy();
                            that._dialogs.pop();
                        });

                        that._dialogs.push(dialog);

                        dialog.open(args.range, args);
                        args.dialog = dialog;
                    }
                },
                contextmenu: (e) => {
                    const { objectRef, targetType, showUnhide, showUnmerge, originalEvent, isComposite } = e;
                    const selection = e.sender.activeSheet().select();
                    const { topLeft, bottomRight } = selection;

                    this._cellContextMenu.close();
                    this._colHeaderContextMenu.close();
                    this._rowHeaderContextMenu.close();
                    this._drawingContextMenu.close();

                    let menu;

                    if (targetType == "columnheader") {
                        menu = this._colHeaderContextMenu;
                    } else if (targetType == "rowheader") {
                        menu = this._rowHeaderContextMenu;
                    } else if (targetType == "drawing") {
                        menu = this._drawingContextMenu;
                    } else {
                        menu = this._cellContextMenu;
                    }

                    menu.element.find(COMPOSITE_UNAVAILABLE_ACTION_SELECTORS).toggle(!isComposite);
                    menu.element.find(UNHIDE_ACTION_SELECTORS).toggle(showUnhide);
                    menu.element.find('[data-action=unmerge]').toggle(showUnmerge);

                    // avoid the immediate close
                    setTimeout(function() {
                        menu.open(e.originalEvent.pageX, e.originalEvent.pageY);
                    });
                },
                locale: kendo.cultures.current.name,
                intl: kendo.kendoCultureToIntl(),
            });

            return new kendo.spreadsheet.commonEngine.SpreadsheetWidget(this.element[0], widgetOptions);
        },
        _createFormulaInputs: function() {
            let formulaBarInputRefElement = this.element.find('.k-spreadsheet-action-bar .k-spreadsheet-formula-input');
            let formulaBarInputRefListId = kendo.guid();
            formulaBarInputRefElement.attr("aria-controls", formulaBarInputRefListId);

            this._formulaBarInputRefList = new FormulaInputStaticList($(`<ul id="${formulaBarInputRefListId}" />`)
                .addClass(FormulaInput.classNames.listWrapper)
                .insertAfter(formulaBarInputRefElement), {
                aria: {
                    role: 'menu',
                    itemRole: 'menuitem'
                },
                autoBind: false,
                selectable: true,
                dataTextField: "text",
                dataValueField: "value",
                template: ({ text, value }) => `${text || value}`
            });

            this._formulaBarInputRefList.element.on("mousedown", function(e) {
                e.preventDefault();
            });

            this._formulaBarInputRefPopup = new kendo.ui.Popup(this._formulaBarInputRefList.element, {
                anchor: this.element.find(".k-spreadsheet-formula-input:not(.k-spreadsheet-cell-editor)"),
                open: function(ev) { formulaBarInputRefElement.attr("aria-expanded", true); },
                close: function(ev) { formulaBarInputRefElement.attr("aria-expanded", false); }
            });

            let formulaCellInputRefElement = this.element.find('.k-spreadsheet-cell-editor');
            let formulaCellInputRefListId = kendo.guid();
            formulaCellInputRefElement.attr("aria-controls", formulaCellInputRefListId);

            this._formulaCellInputRefList = new FormulaInputStaticList($(`<ul id="${formulaCellInputRefListId}" />`)
                .addClass(FormulaInput.classNames.listWrapper)
                .insertAfter(formulaCellInputRefElement), {
                aria: {
                    role: 'menu',
                    itemRole: 'menuitem'
                },
                autoBind: false,
                selectable: true,
                dataValueField: "value",
                dataTextField: "text",
                template: ({ text, value }) => `${text || value}`

            });

            this._formulaCellInputRefList.element.on("mousedown", function(e) {
                e.preventDefault();
            });

            this._formulaCellInputRefPopup = new kendo.ui.Popup(this._formulaCellInputRefList.element, {
                anchor: this.element.find(DOT + commonEngine.View.classNames.cellEditor),
                open: function(ev) { formulaCellInputRefElement.attr("aria-expanded", true); },
                close: function(ev) { formulaCellInputRefElement.attr("aria-expanded", false); }
            });
        },
        _initContextMenus: function() {
            let that = this;

            let contextMenuConfig = {
                target: this.element,
                animation: false,
                showOn: "never" // this is just an invalid event name to prevent the show
            };

            this._cellContextMenu = new kendo.spreadsheet.ContextMenu($(CELL_CONTEXT_MENU(that.options)).appendTo(that.element), contextMenuConfig);

            this._colHeaderContextMenu = new kendo.spreadsheet.ContextMenu($(COL_HEADER_CONTEXT_MENU(that.options)).appendTo(that.element), contextMenuConfig);

            this._rowHeaderContextMenu = new kendo.spreadsheet.ContextMenu($(ROW_HEADER_CONTEXT_MENU(that.options)).appendTo(that.element), contextMenuConfig);

            this._drawingContextMenu = new kendo.spreadsheet.ContextMenu($(DRAWING_CONTEXT_MENU(that.options)).appendTo(that.element), contextMenuConfig);

            this._cellContextMenu.bind("select", this.onContextMenuSelect.bind(this));
            this._rowHeaderContextMenu.bind("select", this.onContextMenuSelect.bind(this));
            this._colHeaderContextMenu.bind("select", this.onContextMenuSelect.bind(this));
            this._drawingContextMenu.bind("select", this.onContextMenuSelect.bind(this));
        },
        onContextMenuSelect: function(e) {
            let that = this;
            let action = $(e.item).data("action");
            let command;
            switch (action) {
                case "cut":
                    command = { command: "ToolbarCutCommand", options: { workbook: this.spreadsheetRef.workbook } };
                    break;
                case "copy":
                    command = { command: "ToolbarCopyCommand", options: { workbook: this.spreadsheetRef.workbook } };
                    break;
                case "paste":
                    command = { command: "ToolbarPasteCommand", options: { workbook: this.spreadsheetRef.workbook } };
                    break;
                case "delete-drawing":
                    command = { command: "DeleteDrawingCommand", options: { drawing: this.spreadsheetRef._controller.navigator._sheet._activeDrawing } };
                    break;
                case "bring-to-front":
                    command = { command: "BringToFrontCommand", options: { drawing: this.spreadsheetRef._controller.navigator._sheet._activeDrawing } };
                    break;
                case "send-to-back":
                    command = { command: "SendToBackCommand", options: { drawing: this.spreadsheetRef._controller.navigator._sheet._activeDrawing } };
                    break;
                case "unmerge":
                    command = { command: "MergeCellCommand", options: { value: "unmerge" } };
                    break;
                case "merge":
                    this.spreadsheetRef.openDialog("merge");
                    break;
                case "hide-row":
                    command = { command: "HideLineCommand", options: { axis: "row" } };
                    break;
                case "hide-column":
                    command = { command: "HideLineCommand", options: { axis: "column" } };
                    break;
                case "unhide-row":
                    command = { command: "UnHideLineCommand", options: { axis: "row" } };
                    break;
                case "unhide-column":
                    command = { command: "UnHideLineCommand", options: { axis: "column" } };
                    break;
                case "delete-row":
                    command = { command: "DeleteRowCommand" };
                    break;
                case "delete-column":
                    command = { command: "DeleteColumnCommand" };
                    break;
            }

            if (command) {
                that.spreadsheetRef.executeCommand(command);
            }
        },
        _onUpdateTools: function(e) {
            let reason = e.reason;
            if (reason && (reason.overElement || reason.comment)) {
                return;
            }
            this.toolbar?.refresh(e.range);
        },
        _getCopyRegex: function(sheetName) {
            const newName = sheetName.replaceAll('(', '\\(').replaceAll(')', '\\)');
            const st = `(${newName})\\s?\\(`;
            return new RegExp(st, 's');
        },
        _sheetsBar: function(openDialogCallback) {
            let that = this;

            if (that.options.sheetsbar) {
                let sheetsbarOptions = $.extend(true, {
                    openDialog: openDialogCallback,
                }, that.options.sheetsbar);

                that.sheetsBar = new SheetsBar(that.element[0].querySelector('[ref-sheets-bar]'), sheetsbarOptions);

                that.sheetsBar.bind("select", function onSheetSelect(ev) {
                    if (ev.isAddButton) {
                        that.spreadsheetRef.view.sheetsbar.onAddSelect();
                    } else {
                        that.spreadsheetRef.view.sheetsbar.onSheetSelect(ev.name);
                    }
                });

                that.sheetsBar.bind("reorder", function onSheetReorderEnd(ev) {
                    that.spreadsheetRef.view.sheetsbar.onSheetReorderEnd(ev);
                });

                that.sheetsBar.bind("rename", function onSheetRename(ev) {
                    that.spreadsheetRef.view.sheetsbar.onSheetRename(ev.name, ev.sheetIndex);
                });

                that.sheetsBar.bind("duplicate", function onSheetDuplicate(ev) {
                    let name = ev.name;
                    let sheetToCopy = that.sheetByName(name);
                    let sheetIndex = that.sheetIndex(sheetToCopy);
                    let copies = 0;
                    const regex = that._getCopyRegex(name);
                    that.sheets().forEach(sheet => {
                        const isPresent = regex.test(sheet.name());
                        if (isPresent) {
                            copies += 1;
                        }
                    });

                    const newName = `${name} (${copies + 1})`;
                    that.insertSheet({ data: { ...sheetToCopy.toJSON(), name: newName }, index: sheetIndex + 1 });
                    that.spreadsheetRef.view.sheetsbar.onSheetSelect(newName);
                });

                that.sheetsBar.bind("remove", function onSheetBarRemove(ev) {
                    that.spreadsheetRef.view.sheetsbar.onSheetRemove(ev.name);
                });

                that.sheetsBar.bind("hide", function onSheetBarHide(ev) {
                    let sheetName = ev.name;
                    let sheetToHide = that.sheetByName(sheetName);
                    let visibleSheets = that.sheets().filter(x=> x.state() === "visible");
                    let visibleSheetIndex = visibleSheets.findIndex(x=> x.name() === sheetName);
                    let nextVisibleSheet = visibleSheets[visibleSheetIndex + 1] || visibleSheets[visibleSheetIndex - 1];
                    sheetToHide._state('hidden');

                    that.spreadsheetRef.workbook._sheetsSearchCache = {};
                    that.spreadsheetRef.view.sheetsbar.onSheetSelect(nextVisibleSheet.name());
                });

                that.sheetsBar.bind("show", function onSheetBarShow(ev) {
                    let sheetName = ev.name;
                    that.sheetByName(sheetName)._state('visible');
                    that.spreadsheetRef.workbook._sheetsSearchCache = {};
                    that.spreadsheetRef.view.sheetsbar.onSheetSelect(ev.name);
                });
            }
        },
        _createToolBar: function(options, tabs, toolbarId) {
            let that = this;

            if (that.toolbar) {
                that.toolbar.destroy();
                that.toolbar.element.empty();
            }

            let activeTabIndex = tabs.findIndex((item) => item.id === toolbarId);
            let activeToolbar = tabs[activeTabIndex];
            let tools = options[activeToolbar.id];

            that._activeToolBar = activeToolbar.text;

            that.toolbar = new kendo.spreadsheet.ToolBar(this.element.find("[ref-header-toolbar]"), {
                tools: typeof tools === "boolean" ? undefined$1 : tools,
                overflow: $.isPlainObject(options.overflow) ? options.overflow : undefined$1,
                toolbarName: activeToolbar.id,
                fillMode: "flat",
                action: function(args) {
                    if (args.command === 'undo' || args.command === 'redo') {
                        that.spreadsheetRef.workbook.undoRedoStack[args.command]();
                        return;
                    }

                    if (args.command == "OpenCommand") {
                        args.options.file = args.options.value;
                    } else if (args.command === "BorderChangeCommand") {
                        args.options.border = args.options.border || args.options.value.type;
                        args.options.style = args.options.style || args.options.value.style || { color: args.options.value.color, size: 1 };
                    }

                    that.spreadsheetRef.executeCommand(args);
                },
                dialog: function(args) {
                    that.spreadsheetRef.openDialog(args.name, args.options);
                }
            });
            that.toolbar.element.attr("aria-label", activeToolbar.text);
        },

        _initHeader: function() {
            let that = this;
            let messages = this.options.messages.tabs;
            let options = $.extend(true, { file: true, home: true, insert: true, format: true, data: true, view: true }, this.options.toolbar);
            let tabs = [];

            if (this.menu) {
                this.menu.destroy();
                this.menu.empty();
            }

            if (this.toolbar) {
                this.toolbar.destroy();
                this.toolbar.empty();
            }

            for (let name in options) {
                if (options[name] === true || options[name] instanceof Array) {
                    tabs.push({ id: name, text: messages[name], content: "", attr: { "ref-tab-name": name } });
                }
            }

            this.menu = new kendo.spreadsheet.Menu(this.element.find("[ref-header-menu]"), {
                dataSource: tabs,
                toolbarOptions: options,
                select: function(e) {
                    let text = $(e.item).text();
                    if (that._activeToolBar !== text) {
                        $(e.item).addClass(K_ACTIVE)
                            .siblings("li").removeClass(K_ACTIVE);

                        that._createToolBar(options, tabs, tabs.find((item) => item.text === text).id);
                        const activeSheet = that.activeSheet();
                        that._onUpdateTools({ range: activeSheet.range(activeSheet.activeCell()) });
                    }
                }
            });

            if (tabs.length) {
                let defaultTab = tabs.find((item)=> item.id === "home");
                this.menu.element.find(`li[ref-tab-name="home"]`).addClass(K_ACTIVE);
                that._createToolBar(options, tabs, defaultTab ? defaultTab.id : tabs[0].id);
            }
        },

        executeCommand(options) {
            return this.spreadsheetRef.executeCommand(options);
        },

        _resize: function() {
            this.refresh({ layout: true });
        },

        _workbookChanging: function(e) {
            if (this.trigger("changing", e)) {
                e.preventDefault();
            }
        },

        _workbookChange: function(e) {
            if (this.trigger("change", { range: e.range })) {
                e.preventDefault();
            }
        },

        _workbookCut: function(e) {
            this.trigger("cut", e);
        },

        _workbookCopy: function(e) {
            this.trigger("copy", e);
        },

        _workbookPaste: function(e) {
            this.trigger("paste", e);
        },

        activeSheet: function(sheet) {
            return this.spreadsheetRef.activeSheet(sheet);
        },

        moveSheetToIndex: function(sheet, index) {
            return this.spreadsheetRef.moveSheetToIndex(sheet, index);
        },

        insertSheet: function(options) {
            return this.spreadsheetRef.insertSheet(options);
        },

        sheets: function() {
            return this.spreadsheetRef.sheets();
        },

        removeSheet: function(sheet) {
            return this.spreadsheetRef.removeSheet(sheet);
        },

        sheetByName: function(sheetName) {
            return this.spreadsheetRef.sheetByName(sheetName);
        },

        sheetIndex: function(sheet) {
            return this.spreadsheetRef.sheetIndex(sheet);
        },

        sheetByIndex: function(index) {
            return this.spreadsheetRef.sheetByIndex(index);
        },

        renameSheet: function(sheet, newSheetName) {
            return this.spreadsheetRef.renameSheet(sheet, newSheetName);
        },

        refresh: function(reason) {
            this.spreadsheetRef.refresh(reason);

            return this;
        },

        openDialog: function(name, options) {
            return this.spreadsheetRef.openDialog(name, options);
        },

        autoRefresh: function(value) {
            if (value !== undefined$1) {
                this._autoRefresh = value;

                if (value === true) {
                    this.refresh();
                }

                return this;
            }

            return this._autoRefresh;
        },

        toJSON: function() {
            return this.spreadsheetRef.toJSON();
        },

        fromJSON: function(json) {
            this.spreadsheetRef.fromJSON(json);
        },

        saveJSON: function() {
            return this.spreadsheetRef.saveJSON();
        },

        fromFile: function(blob, name) {
            return this.spreadsheetRef.fromFile(blob, name);
        },

        saveAsPDF: function(options) {
            this.spreadsheetRef.saveAsPDF(
                $.extend({}, this.options.pdf, options, { workbook: this.spreadsheetRef._workbook })
            );
        },

        saveAsExcel: function(options) {
            const exportOps = $.extend({}, this.options.excel, options, {
                Workbook: kendo.ooxml.Workbook,
                saveAs: function(data, fileName, exportOptions) {
                    kendo.saveAs({ dataURI: data, fileName, ...exportOptions });
                }
            });
            this.spreadsheetRef.saveAsExcel(exportOps);
        },

        draw: function(options, callback) {
            this.spreadsheetRef.draw(options, callback);
        },

        _workbookExcelExport: function(e) {
            if (this.trigger("excelExport", e)) {
                e.preventDefault();
            }
        },

        _workbookExcelImport: function(e) {
            const eventData = {
                file: e.file,
                sender: e.sender,
                preventDefault: e.preventDefault,
                isDefaultPrevented: e.isDefaultPrevented,
                promise: convertSpreadsheetDeferredToJQueryDeferred(e.deferred),
                _defaultPrevented: e._defaultPrevented,
            };

            if (this.trigger("excelImport", eventData)) {
                e.preventDefault();
            } else {
                this._initProgress(e.deferred);
            }
        },

        _initProgress: function(deferred) {
            let loading =
                $("<div class='k-loading-mask' " +
                    "style='width: 100%; height: 100%; top: 0;'>" +
                    "<div class='k-loading-color'></div>" +
                    "</div>")
                    .appendTo(this.element);

            let pb = $("<div class='k-loading-progress'>")
                .appendTo(loading)
                .kendoProgressBar({
                    type: "chunk", chunkCount: 10,
                    min: 0, max: 1, value: 0
                }).data("kendoProgressBar");

            deferred.progress(function(e) {
                pb.value(e.progress);
            })
                .promise.finally(function() {
                    kendo.destroy(loading);
                    loading.remove();
                });
        },

        _workbookPdfExport: function(e) {
            if (this.trigger("pdfExport", e)) {
                e.preventDefault();
            }
        },

        _workbookInsertSheet: function(e) {
            if (this.trigger("insertSheet", e)) {
                e.preventDefault();
            }
        },

        _workbookRemoveSheet: function(e) {
            if (this.trigger("removeSheet", e)) {
                e.preventDefault();
            }
        },

        _workbookSelectSheet: function(e) {
            if (this.trigger("selectSheet", e)) {
                e.preventDefault();
            }
        },

        _workbookRenameSheet: function(e) {
            if (this.trigger("renameSheet", e)) {
                e.preventDefault();
            }
        },

        _workbookInsertRow: function(e) {
            if (this.trigger("insertRow", e)) {
                e.preventDefault();
            }
        },

        _workbookInsertColumn: function(e) {
            if (this.trigger("insertColumn", e)) {
                e.preventDefault();
            }
        },

        _workbookDeleteRow: function(e) {
            if (this.trigger("deleteRow", e)) {
                e.preventDefault();
            }
        },

        _workbookDeleteColumn: function(e) {
            if (this.trigger("deleteColumn", e)) {
                e.preventDefault();
            }
        },

        _workbookHideRow: function(e) {
            if (this.trigger("hideRow", e)) {
                e.preventDefault();
            }
        },

        _workbookHideColumn: function(e) {
            if (this.trigger("hideColumn", e)) {
                e.preventDefault();
            }
        },

        _workbookUnhideRow: function(e) {
            if (this.trigger("unhideRow", e)) {
                e.preventDefault();
            }
        },

        _workbookUnhideColumn: function(e) {
            if (this.trigger("unhideColumn", e)) {
                e.preventDefault();
            }
        },

        _workbookSelect: function(e) {
            this.trigger("select", e);
        },

        _workbookChangeFormat: function(e) {
            this.trigger("changeFormat", e);
        },

        _workbookDataBinding: function(e) {
            if (this.trigger("dataBinding", e)) {
                e.preventDefault();
            }
        },

        _workbookDataBound: function(e) {
            this.trigger("dataBound", e);
        },

        _workbookProgress: function(e) {
            kendo.ui.progress(this.element, e.toggle);
        },

        _bindWorkbookEvents: function() {
            this.spreadsheetRef.bind("cut", this._workbookCut.bind(this));
            this.spreadsheetRef.bind("copy", this._workbookCopy.bind(this));
            this.spreadsheetRef.bind("paste", this._workbookPaste.bind(this));
            this.spreadsheetRef.bind("changing", this._workbookChanging.bind(this));
            this.spreadsheetRef.bind("change", this._workbookChange.bind(this));
            this.spreadsheetRef.bind("excelExport", this._workbookExcelExport.bind(this));
            this.spreadsheetRef.bind("excelImport", this._workbookExcelImport.bind(this));
            this.spreadsheetRef.bind("pdfExport", this._workbookPdfExport.bind(this));
            this.spreadsheetRef.bind("insertSheet", this._workbookInsertSheet.bind(this));
            this.spreadsheetRef.bind("removeSheet", this._workbookRemoveSheet.bind(this));
            this.spreadsheetRef.bind("selectSheet", this._workbookSelectSheet.bind(this));
            this.spreadsheetRef.bind("renameSheet", this._workbookRenameSheet.bind(this));
            this.spreadsheetRef.bind("insertRow", this._workbookInsertRow.bind(this));
            this.spreadsheetRef.bind("insertColumn", this._workbookInsertColumn.bind(this));
            this.spreadsheetRef.bind("deleteRow", this._workbookDeleteRow.bind(this));
            this.spreadsheetRef.bind("deleteColumn", this._workbookDeleteColumn.bind(this));
            this.spreadsheetRef.bind("hideRow", this._workbookHideRow.bind(this));
            this.spreadsheetRef.bind("hideColumn", this._workbookHideColumn.bind(this));
            this.spreadsheetRef.bind("unhideRow", this._workbookUnhideRow.bind(this));
            this.spreadsheetRef.bind("unhideColumn", this._workbookUnhideColumn.bind(this));
            this.spreadsheetRef.bind("select", this._workbookSelect.bind(this));
            this.spreadsheetRef.bind("changeFormat", this._workbookChangeFormat.bind(this));
            this.spreadsheetRef.bind("dataBinding", this._workbookDataBinding.bind(this));
            this.spreadsheetRef.bind("dataBound", this._workbookDataBound.bind(this));
            this.spreadsheetRef.bind("progress", this._workbookProgress.bind(this));
        },

        destroy: function() {
            kendo.ui.Widget.fn.destroy.call(this);

            this._formulaBarInputRefPopup?.destroy();
            this._formulaCellInputRefPopup?.destroy();
            this._formulaBarInputRefList?.destroy();
            this._formulaCellInputRefList?.destroy();

            this._formulaBarInputRefList = this._formulaCellInputRefList = null;
            this._formulaBarInputRefPopup = this._formulaCellInputRefPopup = null;

            if (this.menu) {
                this.menu.destroy();
            }

            if (this.toolbar) {
                this.toolbar.destroy();
            }

            if (this.sheetsBar) {
                this.sheetsBar.destroy();
            }

            this._dialogs?.forEach(function(dialog) {
                dialog?.destroy();
            });

            this.spreadsheetRef?.destroy();

            this._cellContextMenu?.destroy();

            this._rowHeaderContextMenu?.destroy();
            this._colHeaderContextMenu?.destroy();
            this._drawingContextMenu?.destroy();

            this._cellContextMenu = this._rowHeaderContextMenu =
                this._colHeaderContextMenu =
                this._drawingContextMenu = null;

            if (this._resizeHandler) {
                $(window).off("resize" + NS, this._resizeHandler);
            }

            kendo.destroy(this.element);
        },

        options: {
            name: "Spreadsheet",
            toolbar: true,
            sheetsbar: true,
            rows: 200,
            columns: 50,
            rowHeight: 30,
            columnWidth: 64,
            headerHeight: 30,
            headerWidth: 32,
            excel: {
                proxyURL: "",
                fileName: "Workbook.xlsx"
            },
            messages: {},
            pdf: {
                // which part of the workbook to be exported
                area: "workbook",
                fileName: "Workbook.pdf",
                proxyURL: "",
                // paperSize can be an usual name, i.e. "A4", or an array of two Number-s specifying the
                // width/height in points (1pt = 1/72in), or strings including unit, i.e. "10mm".  Supported
                // units are "mm", "cm", "in" and "pt".  The default "auto" means paper size is determined
                // by content.
                paperSize: "a4",
                // True to reverse the paper dimensions if needed such that width is the larger edge.
                landscape: true,
                // An object containing { left, top, bottom, right } margins with units.
                margin: null,
                // Optional information for the PDF Info dictionary; all strings except for the date.
                title: null,
                author: null,
                subject: null,
                keywords: null,
                creator: "Kendo UI PDF Generator v." + kendo.version,
                // Creation Date; defaults to new Date()
                date: null
            },
            defaultCellStyle: {
                fontFamily: "Arial",
                fontSize: 12
            },
            useCultureDecimals: false
        },

        defineName: function(name, value, hidden) {
            return this.spreadsheetRef.defineName(name, value, hidden);
        },

        undefineName: function(name) {
            return this.spreadsheetRef.undefineName(name);
        },

        nameValue: function(name) {
            return this.spreadsheetRef.nameValue(name);
        },

        forEachName: function(func) {
            return this.spreadsheetRef.forEachName(func);
        },

        cellContextMenu: function() {
            return this._cellContextMenu;
        },

        rowHeaderContextMenu: function() {
            return this._rowHeaderContextMenu;
        },

        colHeaderContextMenu: function() {
            return this._colHeaderContextMenu;
        },

        addImage: function(image) {
            return this.spreadsheetRef.addImage(image);
        },

        cleanupImages: function() {
            return this.spreadsheetRef.cleanupImages();
        },

        events: [
            "cut",
            "copy",
            "paste",
            "pdfExport",
            "excelExport",
            "excelImport",
            "changing",
            "change",
            "render",
            "removeSheet",
            "selectSheet",
            "renameSheet",
            "insertRow",
            "insertColumn",
            "deleteRow",
            "insertSheet",
            "deleteColumn",
            "hideRow",
            "hideColumn",
            "unhideRow",
            "unhideColumn",
            "select",
            "changeFormat",
            "dataBinding",
            "dataBound"
        ]
    });

    kendo.ui.plugin(Spreadsheet);
    $.extend(true, Spreadsheet, { classNames: classNames });
})(window.kendo);
var kendo$1 = kendo;

exports.__meta__ = __meta__;
exports.default = kendo$1;