"use strict";
var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var Emitter_1 = __importDefault(require("./Emitter"));
var Helpers_1 = __importDefault(require("./Helpers"));
var Utils_1 = __importDefault(require("./Utils"));
var Clipboard = /** @class */ (function (_super) {
    __extends(Clipboard, _super);
    function Clipboard(element, doc) {
        var _this = _super.call(this) || this;
        _this.editableAreaId = 'pages-editable';
        _this.focus = false;
        _this.clipboardTextPlain = '';
        _this.key = '';
        _this.doc = false;
        _this.validStyles = [
            'color',
            'background',
            'background-color',
            'border',
            'border-left',
            'border-right',
            'border-top',
            'border-bottom',
            'width',
            'height',
            'text-align',
            'vertical-align',
            'font-family',
            'font-size',
            'font-weight',
            'font-style',
            'text-wrap',
            'text-decoration',
            'number-format',
            'word-wrap',
            'white-space'
        ];
        /**
         * Linking names of excel/json styles to css styles
         */
        _this.excelStyles = ['tb', 'rb', 'bb', 'lb'];
        /**
         * Map excel border styles to css border styles (with some compromise)
         */
        _this.excelBorderStyles = {
            solid: 'solid',
            dashed: 'dashed',
            dashdotdot: 'dotted',
            double: 'double'
        };
        /**
         * Map excel border weights to css border weights (with some compromise)
         */
        _this.excelBorderWeights = {
            '1px': 'thin',
            '2px': 'medium',
            '3px': 'thick'
        };
        _this.borderSides = ['top', 'right', 'bottom', 'left'];
        _this.borderSyles = ['width', 'style', 'color'];
        _this.onEditFocus = function () {
            _this.focus = true;
        };
        _this.onEditBlur = function () {
            _this.focus = false;
        };
        _this.onPasteDocument = function (e) {
            if (_this.focus) {
                return;
            }
            _this.emit(_this.ON_PASTED);
            var clipboard = _this.getClipboardText(e);
            _this.clipboardTextPlain = clipboard.text;
            var div = _this.createPastedElement(clipboard.html || clipboard.text);
            _this.getHtml(div);
            console.log('onPasteDocument', clipboard);
        };
        _this.onPaste = function (e) {
            console.log('onPaste', e);
            _this.emit(_this.ON_PASTED);
            // try and get plain text input
            var clipboard = _this.getClipboardText(e);
            _this.clipboardTextPlain = clipboard.text;
            // try and get html
            try {
                var div = _this.createPastedElement(clipboard.html);
                _this.getHtml(div);
                e.preventDefault();
            }
            catch (exception) {
                _this.getHtml(e.target);
            }
            console.log('onPaste', clipboard);
        };
        _this.onKeyDown = function (e) {
            _this.key = e.key;
            if (_this.key === 'Control' && e.key === 'v') {
                _this.emit(_this.ON_PASTED);
            }
            else {
                // e.preventDefault();
            }
        };
        _this.helpers = new Helpers_1.default();
        _this.utils = new Utils_1.default();
        _this.init(element, doc);
        return _this;
    }
    Object.defineProperty(Clipboard.prototype, "ON_DATA", {
        // public static $inject: string[] = [
        //     "$timeout",
        //     "Helpers",
        // ];
        // events
        get: function () {
            return 'data';
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Clipboard.prototype, "ON_PASTED", {
        get: function () {
            return 'pasted';
        },
        enumerable: true,
        configurable: true
    });
    Clipboard.prototype.init = function (element, doc) {
        this.destroy();
        if (typeof element === 'string') {
            this.editableAreaEl = document.getElementById(element);
        }
        else {
            this.editableAreaEl = element;
        }
        if (this.editableAreaEl) {
            this.helpers.addEvent(this.editableAreaEl, 'paste', this.onPaste);
            this.helpers.addEvent(this.editableAreaEl, 'focus', this.onEditFocus);
            this.helpers.addEvent(this.editableAreaEl, 'blur', this.onEditBlur);
            this.helpers.addEvent(this.editableAreaEl, 'keypress', this.onKeyDown);
            this.helpers.addEvent(this.editableAreaEl, 'drop', this.onPaste);
            this.editableAreaEl.focus();
        }
        if (doc) {
            this.helpers.addEvent(document, 'paste', this.onPasteDocument);
            this.doc = true;
        }
        else {
            this.doc = false;
        }
        return true;
    };
    Clipboard.prototype.destroy = function () {
        this.focus = false;
        if (this.editableAreaEl) {
            this.helpers.removeEvent(this.editableAreaEl, 'paste', this.onPaste);
            this.helpers.removeEvent(this.editableAreaEl, 'focus', this.onEditFocus);
            this.helpers.removeEvent(this.editableAreaEl, 'blur', this.onEditBlur);
            this.helpers.removeEvent(this.editableAreaEl, 'keypress', this.onKeyDown);
            this.helpers.removeEvent(this.editableAreaEl, 'drop', this.onPaste);
        }
        if (this.doc) {
            this.helpers.removeEvent(document, 'paste', this.onPasteDocument);
        }
        var pasted = document.getElementById('pasted');
        if (pasted) {
            pasted.parentElement.removeChild(pasted);
        }
    };
    Clipboard.prototype.copyTextToClipboard = function (text, el) {
        if (el === void 0) { el = 'table'; }
        var div = document.createElement('div');
        var custom = el ? true : false;
        div.classList.add('clipboard');
        div.style.position = 'fixed';
        div.style.bottom = '100vh';
        div.style.right = '100vw';
        if (el) {
            div.innerHTML = text;
        }
        else {
            el = 'textarea';
            div.innerHTML = "<textarea>" + text + "</textarea>";
            // input = document.createElement('textarea');
            // input.value = text;
            // div.appendChild(input);
        }
        document.body.appendChild(div);
        var success = true;
        try {
            var firstEl = div.getElementsByTagName(el)[0];
            if (custom) {
                this.selectElContents(firstEl);
            }
            else {
                firstEl.select();
                firstEl.setSelectionRange(0, 99999);
            }
            success = document.execCommand('copy');
        }
        catch (err) {
            success = false;
        }
        document.body.removeChild(div);
        return success;
    };
    // @todo Should probably not be here
    // @note Taken from http://stackoverflow.com/a/2044793/820942
    Clipboard.prototype.selectElContents = function (el) {
        var body = document.body, range, sel;
        if (document.createRange && window.getSelection) {
            range = document.createRange();
            sel = window.getSelection();
            if (!sel) {
                throw 'Error';
            }
            try {
                sel.removeAllRanges();
                range.selectNodeContents(el);
                sel.addRange(range);
            }
            catch (e) {
                try {
                    range.selectNode(el);
                    sel.addRange(range);
                }
                catch (e) {
                    throw e;
                }
            }
        }
        else if (body.createTextRange) {
            range = body.createTextRange();
            range.moveToElementText(el);
            range.select();
        }
    };
    Clipboard.prototype.getClipboardText = function (e) {
        var clipboard = {
            html: '',
            text: ''
        };
        try {
            // Other
            clipboard.text = e.clipboardData.getData('text/plain');
            clipboard.html = e.clipboardData.getData('text/html');
        }
        catch (exception) { }
        // try {
        //   // IE
        //   if (!clipboard.text) {
        //     clipboard.text = window.clipboardData.getData('Text');
        //   }
        // } catch (exception) {}
        return clipboard;
    };
    Clipboard.prototype.createPastedElement = function (html) {
        var div = document.getElementById('pasted') || document.createElement('div');
        div.id = 'pasted';
        if (html.indexOf('<img') > -1 || html.indexOf('<script') > -1) {
            html = '';
        }
        div.innerHTML = html;
        div.style.left = '-10000px';
        div.style.top = '-10000px';
        div.style.position = 'absolute';
        div.style['z-index'] = -1;
        document.getElementsByTagName('body')[0].appendChild(div);
        return div;
    };
    Clipboard.prototype.getHtml = function (el) {
        var _this = this;
        setTimeout(function () {
            var tables = el.getElementsByTagName('table');
            // check for table
            if (!tables.length) {
                el.innerHTML = '';
                if (!_this.clipboardTextPlain) {
                    _this.emit(_this.ON_DATA, false);
                }
                else {
                    // just use what we got
                    console.log(_this.clipboardTextPlain);
                    var data = _this.parseText(_this.clipboardTextPlain);
                    _this.emit(_this.ON_DATA, data);
                }
                return;
            }
            _this.parseTable(tables[0]);
            el.style.visibility = 'hidden';
        }, 10);
    };
    Clipboard.prototype.parseText = function (text) {
        var _this = this;
        var data = [];
        var lines;
        var colWidths = [];
        var numOfCols = 0;
        var rows = [];
        try {
            var jsonData = JSON.parse(text);
            if (!jsonData.length) {
                throw 'Not an array';
            }
            var firstRow = jsonData[0];
            var cells_1 = [];
            Object.keys(firstRow).forEach(function (key) {
                if (typeof key !== 'string' && typeof key !== 'number') {
                    key = JSON.stringify(key);
                }
                cells_1.push(key);
            });
            numOfCols = cells_1.length;
            rows.push(cells_1);
            jsonData.forEach(function (row) {
                var cells = [];
                Object.keys(row).forEach(function (key, c) {
                    var value = row[key];
                    if (typeof value !== 'string' && typeof value !== 'number') {
                        value = JSON.stringify(value);
                    }
                    cells.push(value);
                    var width = Math.ceil(_this.getTextWidth(row[key], 'normal 12pt Arial'));
                    if (!colWidths[c] || colWidths[c] < width) {
                        colWidths[c] = width;
                    }
                });
                rows.push(cells);
            });
        }
        catch (e) {
            // TOOD: use a proper library to do this
            lines = text.split('\n');
            var tabs = lines[0].match(/\t/gi) ? lines[0].match(/\t/gi).length : 0;
            var commas = lines[0].match(/,/gi) ? lines[0].match(/,/gi).length : 0;
            for (var i = 0; i < lines.length; i++) {
                var line = lines[i]; // .replace(/\t([^\s])/ig, ",$1");
                var cells = void 0;
                if (tabs >= commas) {
                    cells = line.split(/\t(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))/);
                }
                else {
                    cells = line.split(/,(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))/);
                }
                if (cells.length < 2) {
                    line = line.replace(/( {2,9})/gi, ',');
                    cells = line.split(/,(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))/);
                }
                rows[i] = cells;
                if (cells.length > numOfCols) {
                    numOfCols = cells.length;
                }
                for (var c = 0; c < cells.length; c++) {
                    var width = Math.ceil(this.getTextWidth(cells[c], 'normal 12pt Arial'));
                    if (!colWidths[c] || colWidths[c] < width) {
                        colWidths[c] = width;
                    }
                }
            }
        }
        for (var i = 0; i < rows.length; i++) {
            var cells = rows[i];
            var cols = [];
            for (var c = 0; c < numOfCols; c++) {
                var value = cells[c] ? cells[c].replace(/['"]+/g, '') : '';
                var textAlign = /^\d+$/.test(value) ? 'right' : 'left';
                var style = this.utils.getDefaultCellStyle();
                style.width = colWidths[c] + "px";
                style["text-align"] = textAlign;
                cols.push({
                    style: style,
                    formatted_value: value,
                    value: value,
                    index: {
                        row: i,
                        col: c
                    }
                });
            }
            data.push(cols);
        }
        return data;
    };
    Clipboard.prototype.parseTable = function (table) {
        var data = this.collectClipData(table);
        this.emit(this.ON_DATA, data);
    };
    Clipboard.prototype.collectClipData = function (table) {
        var data = [];
        var colWidths = this.tableColWidths(table);
        var rows = table.rows;
        var maxColumns = (colWidths && colWidths.length) || 0;
        var rowCount = 0;
        var rowSpans = [];
        // get max columns
        if (!colWidths || !colWidths.length) {
            colWidths = [];
            var rowIndex = -1;
            for (var i = 0; i < rows.length; i++) {
                var cells = rows[i].cells;
                if (!cells.length) {
                    continue;
                }
                var cellCount = 0;
                for (var j = 0; j < cells.length; j++) {
                    cellCount += cells[j].colSpan;
                }
                if (cellCount > maxColumns) {
                    maxColumns = cellCount;
                    rowIndex = i;
                }
            }
            for (var j = 0; j < rows[rowIndex].cells.length; j++) {
                var style = this.cssToStyles(window.getComputedStyle(rows[rowIndex].cells[j]).cssText);
                colWidths[j] = parseFloat(style.width) != NaN ? parseFloat(style.width) : 120;
            }
        }
        console.log('colwidths', colWidths);
        for (var i = 0; i < rows.length; i++) {
            var cells = rows[i].cells;
            // just ignore the row if no cells found
            if (!cells.length) {
                continue;
            }
            data[rowCount] = [];
            var cellCount = 0;
            var rowHeight = '';
            // add row spaning cells first
            if (rowSpans[rowCount]) {
                for (var rs = 0; rs < rowSpans[rowCount].length; rs++) {
                    var c = rowSpans[rowCount][rs];
                    if (!c)
                        continue;
                    data[rowCount][c.index.col] = c;
                    // cellCount++;
                    if (!rowHeight)
                        rowHeight = c.style.height;
                }
            }
            // main cell creation loop
            for (var j = 0; j < maxColumns; j++) {
                // check for row span cell and move cell count pointer
                if (rowSpans[rowCount] && rowSpans[rowCount][cellCount]) {
                    // cellCount++;
                    for (var s = cellCount; s < maxColumns; s++) {
                        if (!rowSpans[rowCount][s])
                            break;
                        cellCount++;
                    }
                }
                if (!cells[j]) {
                    continue;
                }
                var rowSpan = cells[j].rowSpan;
                var colSpan = cells[j].colSpan;
                var links = cells[j].getElementsByTagName('a');
                // get value
                var val = cells[j].textContent;
                if (links && links.length) {
                    val = links[0].textContent;
                }
                val = val
                    // .replace(/<(?!br\s*\/?)[^>]+>/g, '')
                    .replace(/\n/g, '')
                    .replace(/<br>/g, '\n')
                    .replace(/<br \/>/g, '\n');
                // .replace(/&amp;/g, '&')
                // .replace(/&lt;/g, '<')
                // .replace(/&nbsp;/g, ' ')
                val = val.split('\n').map(function (v) { return v.trim(); }).join('\n');
                // cell data
                var cellData = {
                    index: {
                        row: rowCount,
                        col: cellCount
                    },
                    value: this.helpers.isNumber(val) ? parseFloat(val) : val,
                    formatted_value: val,
                    style: {}
                };
                if (links && links.length) {
                    cellData.link = {
                        external: true,
                        address: links[0].href
                    };
                }
                // cell style
                cellData.style = this.cssToStyles(window.getComputedStyle(cells[j]).cssText); // @todo: We should do this only for new cells - very expensive
                if (cells[j].dataset.format) {
                    cellData.style['number-format'] = cells[j].dataset.format;
                }
                if (!rowHeight) {
                    rowHeight = cellData.style.height;
                }
                else {
                    cellData.style.height = rowHeight;
                }
                if (colWidths && colWidths[j]) {
                    cellData.style.width = colWidths[cellCount] + "px";
                }
                else {
                    if (cellData.style.width === '0px') {
                        cellData.style.width = '80px';
                    }
                }
                if (cells[j].getElementsByTagName('br').length) {
                    cellData.style['text-wrap'] = 'wrap';
                }
                // check if color is valid @todo: bit hacky
                var color = cellData.style.color;
                if (color && color.indexOf('#') === -1 && color.indexOf('rgb') === -1) {
                    color = "#" + color;
                }
                if (!this.helpers.validHex(color)) {
                    cellData.style.color = '000000';
                }
                // add cell to row
                data[rowCount][cellCount] = cellData;
                // merged rows. ow!
                if (rowSpan > 1) {
                    for (var s = 1; s < rowSpan; s++) {
                        if (!rowSpans[rowCount + s])
                            rowSpans[rowCount + s] = [];
                        var copyCellData = JSON.parse(JSON.stringify(cellData));
                        copyCellData.value = '';
                        copyCellData.formatted_value = '';
                        copyCellData.index.row = rowCount + s;
                        rowSpans[rowCount + s][cellCount] = copyCellData;
                    }
                }
                cellCount++;
                // merged cells TODO: what about vertically merged cells?
                if (colSpan > 1) {
                    for (var k = 1; k < colSpan; k++) {
                        var copyCellData = JSON.parse(JSON.stringify(cellData));
                        copyCellData.value = '';
                        copyCellData.formatted_value = '';
                        // copyCellData.index = merge({}, cellData.index);
                        if (colWidths && colWidths[cellCount]) {
                            copyCellData.style.width = colWidths[cellCount] + "px";
                        }
                        copyCellData.index.col = cellCount;
                        data[rowCount][cellCount] = copyCellData;
                        if (rowSpan > 1) {
                            for (var s = 1; s < rowSpan; s++) {
                                copyCellData = rowSpans[rowCount + s][cellCount] || JSON.parse(JSON.stringify(copyCellData));
                                copyCellData.index.row = rowCount + s;
                                rowSpans[rowCount + s][cellCount] = copyCellData;
                            }
                        }
                        cellCount++;
                    }
                }
            }
            rowCount++;
        }
        for (var i = 0; i < data.length; i++) {
            if (data[i].length >= maxColumns) {
                continue;
            }
            var cell = data[i][data[i].length - 1];
            cell.value = '';
            cell.formatted_value = '';
            for (var k = data[i].length; k < maxColumns; k++) {
                data[i].push(cell);
            }
        }
        console.log(data);
        return data;
    };
    Clipboard.prototype.tableColWidths = function (table) {
        var colGroup = table.getElementsByTagName('colgroup');
        if (!colGroup.length) {
            return undefined;
        }
        var colWidths = [];
        var cols = colGroup[0].getElementsByTagName('col');
        for (var i = 0; i < cols.length; i++) {
            colWidths.push(parseInt(cols[i].width, 10));
            if (cols[i].span && cols[i].span > 1) {
                for (var k = 0; k < cols[i].span - 1; k++) {
                    colWidths.push(parseInt(cols[i].width, 10));
                }
            }
        }
        return colWidths;
    };
    Clipboard.prototype.getRawValue = function (val, format) {
        if (typeof format === 'undefined') {
            format = '@';
        }
        if (format === '@') {
            return val;
        } // Plain text
        return val;
    };
    Clipboard.prototype.cssToStyles = function (cssText) {
        var parts = cssText.split(';'), style = {};
        for (var i = 0; i < parts.length; i++) {
            var styleParts = parts[i].split(':');
            if (styleParts.length < 2) {
                continue;
            }
            var styleName = styleParts[0].trim();
            var styleVal = styleParts[1].trim();
            // Ignore styles we dont need
            var hasBorder = false;
            if (styleName.indexOf('border-') > -1) {
                var nameParts = styleName.split('-');
                if (nameParts.length === 3 &&
                    this.borderSides.indexOf(nameParts[1]) !== -1 &&
                    this.borderSyles.indexOf(nameParts[2]) !== -1) {
                    hasBorder = true;
                }
            }
            if (this.validStyles.indexOf(styleName) === -1 && !hasBorder) {
                continue;
            }
            // Strip !important, #
            styleVal = styleVal.split('!')[0];
            // Font family
            if (styleName === 'font-family') {
                styleVal = styleVal.split(',')[0].replace(/"/gi, '');
            }
            // Colors
            if ((styleName === 'color' || styleName === 'background-color' || styleName.indexOf('-color') !== -1) &&
                styleVal.indexOf('rgb') >= 0) {
                styleVal = "" + this.helpers.rgbToHex(styleVal);
            }
            // Font size
            if (styleName === 'font-size' && styleVal.indexOf('px')) {
                styleVal = Math.round(parseFloat(styleVal) * 0.73) + "pt";
            }
            // text wrap (for excel)
            if (styleName === 'word-wrap') {
                style['text-wrap'] = styleVal.trim();
            }
            // workaround for ng-ipushpull
            if (styleName === 'white-space' && styleVal === 'normal') {
                styleVal = 'inherit';
            }
            if (styleName === 'text-align') {
                if (styleVal.indexOf('right') > -1) {
                    styleVal = 'right';
                }
                else if (styleVal.indexOf('justify') > -1 || styleVal.indexOf('left') > -1) {
                    styleVal = 'left';
                }
                else if (styleVal.indexOf('center') > -1) {
                    styleVal = 'center';
                }
            }
            style[styleName] = styleVal.trim();
            if (hasBorder) {
                var map = this.mapExcelBorder(styleName, styleVal);
                if (map) {
                    style[map.name] = map.value;
                }
            }
        }
        return style;
    };
    Clipboard.prototype.mapExcelBorder = function (key, val) {
        var parts = key.split('-');
        if (parts.length !== 3) {
            return undefined;
        }
        var name = [parts[1].substr(0, 1), parts[0].substr(0, 1)];
        if (this.excelStyles.indexOf(name.join('')) === -1 || this.borderSyles.indexOf(parts[2]) === -1) {
            return undefined;
        }
        var index = this.borderSyles.indexOf(parts[2]);
        var style = this.borderSyles[index];
        name.push(style.substr(0, 1));
        var value = '';
        switch (style) {
            case 'width':
                value = this.excelBorderWeights[Math.round(parseFloat(val)) + "px"] || 'none';
                break;
            case 'style':
                value = this.excelBorderStyles[val] || 'none';
                break;
            default:
                value = val;
                break;
        }
        var map = {
            name: name.join(''),
            value: value
        };
        return map;
    };
    Clipboard.prototype.getTextWidth = function (text, font) {
        // re-use canvas object for better performance
        var canvas = this.canvas || (this.canvas = document.createElement('canvas'));
        var context = canvas.getContext('2d');
        context.font = font;
        var metrics = context.measureText(text);
        return metrics.width;
    };
    return Clipboard;
}(Emitter_1.default));
exports.Clipboard = Clipboard;
//# sourceMappingURL=Clipboard.js.map