/**
 * Created by sam on 09.02.2015.
 */
import * as tslib_1 from "tslib";
import * as C from 'phovea_core/src/index';
import * as $ from 'jquery';
import lazyBootstrap from 'phovea_ui/src/_lazyBootstrap';
import * as idtypes from 'phovea_core/src/idtype';
import * as provenance from 'phovea_core/src/provenance';
import * as cmode from './mode';
import * as dialogs from 'phovea_ui/src/dialogs';
import * as d3 from 'd3';
import * as vis from 'phovea_core/src/vis';
import * as utils from './utils';
function extractTags(text) {
    var regex = /(?:^|\s)(?:#)([a-zA-Z\d]+)/gm;
    var match;
    var matches = [];
    while (match = regex.exec(text)) {
        matches.push(match[1]);
    }
    return matches;
}
var LevelOfDetail;
(function (LevelOfDetail) {
    LevelOfDetail[LevelOfDetail["ExtraSmall"] = 0] = "ExtraSmall";
    LevelOfDetail[LevelOfDetail["Small"] = 1] = "Small";
    LevelOfDetail[LevelOfDetail["Medium"] = 2] = "Medium";
    LevelOfDetail[LevelOfDetail["Large"] = 3] = "Large";
})(LevelOfDetail || (LevelOfDetail = {}));
var DOI_LARGE = 0.9;
var DOI_MEDIUM = 0.7;
var DOI_SMALL = 0.4;
function getLevelOfDetail() {
    var mode = cmode.getMode();
    //if (mode.exploration >= 0.8) {
    //  return LevelOfDetail.Small;
    //}
    if (mode.presentation > 0.3) {
        return LevelOfDetail.ExtraSmall;
    }
    if (mode.authoring >= 0.8) {
        return LevelOfDetail.Large;
    }
    return LevelOfDetail.Medium;
}
var StateRepr = (function () {
    function StateRepr(s, graph) {
        this.s = s;
        this.graph = graph;
        this.xy = [0, 0];
        this.selected = false;
        this.parent = null;
        this.children = [];
        this.a = null;
        this.doi = 0.1;
        this.a = s.creator;
    }
    Object.defineProperty(StateRepr.prototype, "thumbnail", {
        get: function () {
            return "url(" + utils.thumbnail_url(this.graph, this.s) + ")";
        },
        enumerable: true,
        configurable: true
    });
    StateRepr.prototype.build = function (lookup, line) {
        var p = this.s.previousState;
        if (p) {
            this.parent = lookup[p.id];
            if (line.indexOf(this) >= 0) {
                //ensure first
                this.parent.children.unshift(this);
            }
            else {
                this.parent.children.push(this);
            }
        }
    };
    Object.defineProperty(StateRepr.prototype, "flatChildren", {
        get: function () {
            var r = this.children.slice();
            this.children.forEach(function (c) { return r.push.apply(r, c.flatChildren); });
            return r;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(StateRepr.prototype, "cx", {
        get: function () {
            return this.xy[0] + 2;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(StateRepr.prototype, "cy", {
        get: function () {
            return this.xy[1] + 13;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(StateRepr.prototype, "lod_local", {
        get: function () {
            if (this.doi >= DOI_LARGE) {
                return LevelOfDetail.Large;
            }
            if (this.doi >= DOI_MEDIUM) {
                return LevelOfDetail.Medium;
            }
            if (this.doi >= DOI_SMALL) {
                return LevelOfDetail.Small;
            }
            return LevelOfDetail.ExtraSmall;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(StateRepr.prototype, "lod", {
        get: function () {
            var global = getLevelOfDetail();
            var local = this.lod_local;
            return global < local ? global : local;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(StateRepr.prototype, "size", {
        get: function () {
            switch (this.lod) {
                case LevelOfDetail.Large:
                    return [50, 40];
                case LevelOfDetail.Medium:
                    return [30, 18];
                case LevelOfDetail.Small:
                    return [30, 18];
                default:
                    return [10, 7];
            }
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(StateRepr.prototype, "name", {
        get: function () {
            return this.s.name;
        },
        enumerable: true,
        configurable: true
    });
    StateRepr.toRepr = function (graph, highlight, options) {
        if (options === void 0) { options = {}; }
        //assign doi
        var lookup = {};
        //mark selected
        var selected = graph.act;
        var selectedPath = selected.path.reverse();
        var lod = getLevelOfDetail();
        var size = graph.states.length;
        var toState = function (s) {
            var r = new StateRepr(s, graph);
            var a = s.creator;
            var meta = a ? a.meta : provenance.meta('No', 'none', 'none');
            var category = highlight.category[meta.category] ? 1 : 0;
            var operation = highlight.operation[meta.operation] ? 1 : 0;
            var bookmark = (s.getAttr('starred', false) ? 1 : 0);
            var tags = (highlight.tags.length > 0 ? (s.getAttr('tags', []).some(function (d) { return highlight.tags.indexOf(d) >= 0; }) ? 1 : 0) : 0);
            var isSelected = s === selected ? 3 : 0;
            var inpath = selectedPath.indexOf(s) >= 0 ? Math.max(-2.5, 6 - selectedPath.indexOf(s)) : -2.5;
            var sizePenality = Math.max(-1, -size / 10);
            //combine to a doi value
            var sum = 6 + isSelected + inpath + sizePenality;
            r.doi = d3.round(Math.max(0, Math.min(10, sum)) / 10, 1);
            if ((category + operation + bookmark + tags) > 0) {
                //boost to next level if any of the filters apply
                r.doi = Math.max(r.doi, DOI_SMALL);
            }
            if (!utils.areThumbnailsAvailable(graph) || options.thumbnails === false) {
                r.doi = Math.min(r.doi, DOI_LARGE - 0.01); //border for switching to thumbnails
            }
            r.selected = s === selected;
            lookup[s.id] = r;
            return r;
        };
        var states = (lod < LevelOfDetail.Medium ? selectedPath : graph.states).map(toState);
        //route to path = 1
        var line = selected.path.map(function (p) { return lookup[p.id]; });
        //build tree
        states.forEach(function (s) { return s.build(lookup, line); });
        this.layout(states, line);
        //boost all on the right side if they are small to medium
        return states;
    };
    StateRepr.layout = function (states, line) {
        //horizontally align the line
        var byLevel = [];
        var root = states.filter(function (s) { return s.parent === null; })[0];
        byLevel.push([root]);
        byLevel.push(root.children.slice());
        while (byLevel[byLevel.length - 1].length > 0) {
            byLevel.push([].concat.apply([], byLevel[byLevel.length - 1].map(function (c) { return c.children.slice(); })));
        }
        byLevel.forEach(function (level, i) {
            if (i < line.length) {
                //resort such that the element will be at the first place
                level.splice(level.indexOf(line[i]), 1);
                level.unshift(line[i]);
            }
        });
        var changed = false, loop = 0;
        do {
            changed = false;
            loop++;
            byLevel.forEach(function (level, i) {
                //ensure that my children have at least a >= index than me
                for (var j = 0; j < level.length; ++j) {
                    var s = level[j];
                    if (s) {
                        s.xy = [j, i];
                        if (s.children.length > 0) {
                            var start = byLevel[i + 1].indexOf(s.children[0]);
                            changed = changed || start !== j;
                            if (start < j) {
                                byLevel[i + 1].splice.apply(byLevel[i + 1], [start, 0].concat(d3.range(j - start).map(function (d) { return null; })));
                            }
                            else if (j < start && j > 0) {
                                level.splice.apply(level, [j, 0].concat(d3.range(start - j).map(function (d) { return null; })));
                                s.xy[0] = start;
                                j = start;
                            }
                        }
                    }
                }
            });
        } while (changed && loop < 5);
        byLevel = byLevel.filter(function (d) { return d.length > 0; });
        //boost all states that are on the left side to medium if they are small
        byLevel.forEach(function (level) {
            var s = level[0];
            if (s && s.lod === LevelOfDetail.Small) {
                s.doi = 0.8; //boost to medium
            }
        });
        //we have a bread first with the line at the first position
        //align all columns by their max width
        var colwidths = [], rowheights = [];
        states.forEach(function (s) {
            colwidths[s.xy[0]] = Math.max(colwidths[s.xy[0]] || 0, s.size[0]);
            rowheights[s.xy[1]] = Math.max(rowheights[s.xy[1]] || 0, s.size[1]);
        });
        //convert indices to real positions
        var acccolwidths = colwidths.reduce(function (arr, b, i) {
            arr[i + 1] = arr[arr.length - 1] + b;
            return arr;
        }, [0]), accrowheights = rowheights.reduce(function (arr, b, i) {
            arr[i + 1] = arr[arr.length - 1] + b;
            return arr;
        }, [0]);
        acccolwidths.shift();
        states.forEach(function (s) {
            var xy = s.xy;
            var x = acccolwidths[acccolwidths.length - 1] - acccolwidths[xy[0]] + 5; // + (colwidths[xy[0]]);
            var y = accrowheights[xy[1]];
            s.xy = [x, y];
        });
    };
    StateRepr.toIcon = function (repr) {
        if (!repr.a) {
            return ''; //`<i class="fa fa-circle" title="${repr.s.name}"></i>`;
        }
        var meta = repr.a.meta;
        var catIcons = {
            visual: 'bar-chart',
            data: 'database',
            logic: 'gear',
            layout: 'desktop',
            selection: 'pencil-square'
        };
        var typeIcons = {
            create: 'plus',
            update: 'refresh',
            remove: 'remove'
        };
        return "<span title=\"" + meta.name + " @ " + meta.timestamp + " (" + meta.user + ")\"><i class=\"fa fa-" + catIcons[meta.category] + "\"></i><i class=\"super fa fa-" + typeIcons[meta.operation] + "\"></i></span>";
    };
    StateRepr.render = function ($elem) {
        $elem
            .classed('doi-xs', function (d) { return d.lod === LevelOfDetail.ExtraSmall; })
            .classed('doi-sm', function (d) { return d.lod === LevelOfDetail.Small; })
            .classed('doi', function (d) { return d.lod === LevelOfDetail.Medium; })
            .classed('doi-lg', function (d) { return d.lod === LevelOfDetail.Large; })
            .classed('phovea-select-selected', function (d) { return d.selected; })
            .classed('bookmarked', function (d) { return d.s.getAttr('starred', false); })
            .attr('data-doi', function (d) { return d.doi; })
            .attr('title', function (d) { return d.name; });
        $elem.select('span.icon').html(StateRepr.toIcon);
        $elem.select('span.slabel').text(function (d) { return d.name; });
        $elem.select('i.bookmark')
            .classed('fa-bookmark-o', function (d) { return !d.s.getAttr('starred', false); })
            .classed('fa-bookmark', function (d) { return d.s.getAttr('starred', false); });
        $elem.select('div.sthumbnail')
            .style('background-image', function (d) { return d.lod === LevelOfDetail.Large ? d.thumbnail : null; });
        $elem.transition().style({
            'padding-left': function (d) { return (d.xy[0] + 4) + 'px'; },
            top: function (d) { return d.xy[1] + 'px'; }
        });
    };
    StateRepr.prototype.showDialog = function () {
        var d = this;
        var icon = StateRepr.toIcon(d);
        var title = d.s.name;
        var dia = dialogs.generateDialog("<span class=\"icon\">" + icon + "</span>" + title);
        var thumbnail = utils.thumbnail_url(d.graph, d.s, { width: 512, format: 'png' });
        var notes = d.s.getAttr('note', '');
        var starred = d.s.getAttr('starred', false);
        var $body = d3.select(dia.body);
        $body.html("\n    <form class=\"state_info\" onsubmit=\"return false\">\n      <span class=\"star fa fa-" + (starred ? 'bookmark-o' : 'bookmark-o') + "\" title=\"bookmark this state for latter use\"></span>\n      <div class=\"img\"><img src=\"" + thumbnail + "\"></div>\n      <div class=\"form-group\">\n        <label>Name</label>\n        <input type=\"text\" class=\"form-control\" placeholder=\"name\" value=\"" + title + "\">\n      </div>\n      <div class=\"form-group\">\n        <label>Notes</label>\n        <textarea class=\"form-control\" placeholder=\"place for notes... (#tags will be automatically extracted)\">" + notes + "</textarea>\n      </div>\n      <div class=\"form-group\">\n        <label>Extracted Tags</label>\n        <input type=\"text\" class=\"form-control readonly\" readonly=\"readonly\" value=\"" + extractTags(notes).join(' ') + "\">\n      </div>\n    </form>");
        $body.select('span.star').on('click', function () {
            d.s.setAttr('starred', !d.s.getAttr('starred', false));
            $(this).toggleClass('fa-bookmark-o').toggleClass('fa-bookmark');
            return false;
        });
        $body.select('textarea').on('input', function () {
            $body.select('input.readonly').property('value', extractTags(this.value).join(' '));
        });
        dia.onHide(function () {
            var name = $body.select('input').property('value');
            d.s.name = name;
            var val = $body.select('textarea').property('value');
            d.s.setAttr('tags', extractTags(val));
            d.s.setAttr('note', val);
        });
        dia.show();
    };
    return StateRepr;
}());
var LayoutedProvVis = (function (_super) {
    tslib_1.__extends(LayoutedProvVis, _super);
    function LayoutedProvVis(data, parent, options) {
        var _this = _super.call(this) || this;
        _this.data = data;
        _this.parent = parent;
        _this.options = options;
        _this.trigger = C.bind(_this.update, _this);
        _this.triggerStoryHighlight = C.bind(_this.updateStoryHighlight, _this);
        _this.onStateAdded = function (event, state) {
            state.on('setAttr', _this.trigger);
        };
        _this.onSelectionChanged = function (event, type, act) {
            var selectedStates = _this.data.selectedStates(type);
            _this.$node.selectAll('div.state').classed('phovea-select-' + type, function (d) {
                var isSelected = selectedStates.indexOf(d.s) >= 0;
                if (isSelected && type === idtypes.defaultSelectionType) {
                    this.scrollIntoView();
                }
                return isSelected;
            });
        };
        _this.line = d3.svg.line().interpolate('step-after').x(function (d) { return d.cx; }).y(function (d) { return d.cy; });
        _this.dim = [200, 100];
        _this.highlight = {
            category: {
                data: false,
                visual: false,
                selection: false,
                logic: false,
                layout: false,
                none: false
            },
            operation: {
                create: false,
                remove: false,
                update: false,
                none: false
            },
            tags: []
        };
        _this.options = C.mixin({
            thumbnails: true,
            provVisCollapsed: false,
            hideCLUEButtonsOnCollapse: false
        }, options);
        _this.options.scale = [1, 1];
        _this.options.rotate = 0;
        _this.$node = _this.build(d3.select(parent));
        C.onDOMNodeRemoved(_this.node, _this.destroy, _this);
        if (!_this.options.provVisCollapsed) {
            _this.bind();
            _this.update();
        }
        return _this;
    }
    LayoutedProvVis.prototype.bind = function () {
        var _this = this;
        this.data.on('switch_state,forked_branch,clear', this.trigger);
        this.data.on('add_slide,move_slide,remove_slide', this.triggerStoryHighlight);
        this.data.on('add_state', this.onStateAdded);
        this.data.on('select', this.onSelectionChanged);
        this.data.states.forEach(function (s) {
            s.on('setAttr', _this.trigger);
        });
        cmode.on('modeChanged', this.trigger);
    };
    LayoutedProvVis.prototype.unbind = function () {
        var _this = this;
        this.data.off('switch_state,clear', this.trigger);
        this.data.off('add_slide,move_slidey,remove_slide', this.triggerStoryHighlight);
        this.data.off('add_state', this.onStateAdded);
        this.data.off('select', this.onSelectionChanged);
        this.data.states.forEach(function (s) {
            s.off('setAttr', _this.trigger);
        });
        cmode.off('modeChanged', this.trigger);
    };
    LayoutedProvVis.prototype.toggleBinding = function (enable) {
        if (enable) {
            this.bind();
            this.update();
        }
        else {
            this.unbind();
        }
    };
    LayoutedProvVis.prototype.destroy = function () {
        _super.prototype.destroy.call(this);
        this.unbind();
    };
    Object.defineProperty(LayoutedProvVis.prototype, "rawSize", {
        get: function () {
            return this.dim;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(LayoutedProvVis.prototype, "node", {
        get: function () {
            return this.$node.node();
        },
        enumerable: true,
        configurable: true
    });
    LayoutedProvVis.prototype.option = function (name, val) {
        if (arguments.length === 1) {
            return this.options[name];
        }
        else {
            this.fire('option.' + name, val, this.options[name]);
            this.options[name] = val;
        }
    };
    LayoutedProvVis.prototype.locateImpl = function (range) {
        return Promise.resolve(null);
    };
    LayoutedProvVis.prototype.build = function ($parent) {
        var _this = this;
        //  scale = this.options.scale;
        var $p = $parent.select('aside.provenance-layout-vis');
        if ($p.empty()) {
            $p = $parent.append('aside').classed('provenance-layout-vis', true);
        }
        $p
            .classed('collapsed', this.options.provVisCollapsed)
            .style('transform', 'rotate(' + this.options.rotate + 'deg)');
        if (this.options.hideCLUEButtonsOnCollapse && this.options.provVisCollapsed) {
            d3.select('header.clue-modeselector').classed('collapsed', true);
        }
        $p.html("\n      <a href=\"#\" class=\"btn-collapse\" title=\"" + ((this.options.provVisCollapsed) ? 'Show Provenance Graph' : 'Hide Provenance Graph') + "\"><i class=\"fa " + ((this.options.provVisCollapsed) ? 'fa-arrow-circle-o-left' : 'fa-arrow-circle-o-right') + "\"></i></a>\n      <div>\n        <h2>\n          <i class=\"fa fa-code-fork fa-rotate-180\"></i> Provenance\n          <a href=\"#\" class=\"btn-filter\"><i class=\"fa fa-filter\"></i></a>\n        </h2>\n        <form class=\"form-inline toolbar\" style=\"display:none\" onsubmit=\"return false;\">\n        <div class=\"btn-group\" data-toggle=\"buttons\">\n          <label class=\"btn btn-default btn-xs\" title=\"data actions\">\n            <input type=\"checkbox\" autocomplete=\"off\" name=\"category\" value=\"data\" > <i class=\"fa fa-database\"></i>\n          </label>\n          <label class=\"btn btn-default btn-xs\" title=\"visual actions\">\n            <input type=\"checkbox\" autocomplete=\"off\" name=\"category\" value=\"visual\"> <i class=\"fa fa-bar-chart\"></i>\n          </label>\n          <label class=\"btn btn-default btn-xs\" title=\"selection actions\">\n            <input type=\"checkbox\" autocomplete=\"off\" name=\"category\" value=\"selection\"> <i class=\"fa fa-pencil-square\"></i>\n          </label>\n          <label class=\"btn btn-default btn-xs\" title=\"layout actions\">\n            <input type=\"checkbox\" autocomplete=\"off\" name=\"category\" value=\"layout\"> <i class=\"fa fa-desktop\"></i>\n          </label>\n          <label class=\"btn btn-default btn-xs\" title=\"logic actions\">\n            <input type=\"checkbox\" autocomplete=\"off\" name=\"category\" value=\"logic\"> <i class=\"fa fa-gear\"></i>\n          </label>\n        </div>\n\n        <div class=\"btn-group\" data-toggle=\"buttons\">\n          <label class=\"btn btn-default btn-xs\" title=\"create actions\">\n            <input type=\"checkbox\" autocomplete=\"off\" name=\"operation\" value=\"create\"> <i class=\"fa fa-plus\"></i>\n          </label>\n          <label class=\"btn btn-default btn-xs\" title=\"update actions\">\n            <input type=\"checkbox\" autocomplete=\"off\" name=\"operation\" value=\"update\"> <i class=\"fa fa-refresh\"></i>\n          </label>\n          <label class=\"btn btn-default btn-xs\" title=\"remove actions\">\n            <input type=\"checkbox\" autocomplete=\"off\" name=\"operation\" value=\"remove\"> <i class=\"fa fa-remove\"></i>\n          </label>\n        </div>\n\n        <div class=\"btn-group\" data-toggle=\"buttons\">\n          <div class=\"form-group btn-group\">\n            <div class=\"btn-group btn-group-xs\" role=\"group\">\n              <button type=\"button\" class=\"btn btn-default dropdown-toggle\" data-toggle=\"dropdown\" aria-haspopup=\"true\"\n                      aria-expanded=\"false\">\n                <i class=\"fa fa-tags\"></i><span class=\"caret\"></span>\n              </button>\n              <div class=\"dropdown-menu dropdown-menu-right\">\n                <div class=\"input-group input-group-sm\">\n                  <span class=\"input-group-addon\" title=\"tagged states\"><i class=\"fa fa-tags\"></i></span>\n                  <input name=\"tags\" type=\"text\" class=\"form-control input-sm\" placeholder=\"tags\">\n                </div>\n              </div>\n            </div>\n          </div>\n         </div>\n        </form>\n      </div>\n      <div style=\"position: relative\">\n        <svg>\n          <g transform=\"translate(1,1)\" class=\"edges\"></g>\n          <g transform=\"translate(1,1)\" class=\"storyhighlights\" style=\"display:none\">\n            <path class=\"story\"></path>\n          </g>\n        </svg>\n        <div class=\"states\"></div>\n      </div>\n      <div class=\"legend\">\n        <div class=\"btn-group-vertical\" data-toggle=\"buttons\">\n          <label class=\"btn btn-default btn-xs\" title=\"data actions\">\n            <input type=\"checkbox\" autocomplete=\"off\" name=\"category\" value=\"data\"> <i class=\"fa fa-database\"></i> Data\n          </label>\n          <label class=\"btn btn-default btn-xs\" title=\"visual actions\">\n            <input type=\"checkbox\" autocomplete=\"off\" name=\"category\" value=\"visual\"> <i class=\"fa fa-bar-chart\"></i> Visual\n          </label>\n          <label class=\"btn btn-default btn-xs\" title=\"selection actions\">\n            <input type=\"checkbox\" autocomplete=\"off\" name=\"category\" value=\"selection\" > <i class=\"fa fa-pencil-square\"></i> Selections\n          </label>\n          <label class=\"btn btn-default btn-xs\" title=\"layout actions\">\n            <input type=\"checkbox\" autocomplete=\"off\" name=\"category\" value=\"layout\"> <i class=\"fa fa-desktop\"></i> Layout\n          </label>\n          <label class=\"btn btn-default btn-xs\" title=\"logic actions\">\n            <input type=\"checkbox\" autocomplete=\"off\" name=\"category\" value=\"logic\"> <i class=\"fa fa-gear\"></i> Analysis\n          </label>\n        </div>\n      </div>\n    ");
        //init the toolbar filter options
        var jp = $($p.node());
        var that = this;
        //must use bootstrap since they are manually triggered
        jp.find('form.toolbar input, .legend input').on('change', function () {
            if (this.type === 'text') {
                that.highlight.tags = this.value.split(' ');
                jp.find('button[data-toggle="dropdown"]').toggleClass('active', that.highlight.tags.length > 0);
            }
            else {
                that.highlight[this.name][this.value] = this.checked;
            }
            that.update();
        });
        //initialize bootstrap
        lazyBootstrap().then(function ($$) {
            $$($p.node()).find('*[data-toggle="buttons"],.btn[data-toggle="button"]').button();
        });
        jp.find('.btn-filter').on('click', function () {
            jp.find('form.toolbar').toggle('fast');
            return false;
        });
        jp.find('.btn-collapse').on('click', function (evt) {
            evt.preventDefault();
            var collapsed = !$p.classed('collapsed');
            _this.toggleBinding(!collapsed);
            $p.select('.btn-collapse').attr('title', collapsed ? 'Show Provenance Graph' : 'Hide Provenance Graph');
            $p.select('.btn-collapse > i').classed('fa-arrow-circle-o-right', !collapsed).classed('fa-arrow-circle-o-left', collapsed);
            $p.classed('collapsed', collapsed);
            if (_this.options.hideCLUEButtonsOnCollapse) {
                d3.select('header.clue-modeselector').classed('collapsed', collapsed);
            }
        });
        return $p;
    };
    LayoutedProvVis.prototype.onStateClick = function (d) {
        d3.event.stopPropagation();
        this.data.selectState(d.s, idtypes.toSelectOperation(d3.event));
        this.data.jumpTo(d.s);
    };
    LayoutedProvVis.prototype.update = function () {
        var _this = this;
        var that = this;
        var graph = this.data;
        var lod = getLevelOfDetail();
        this.$node.classed('large', lod === LevelOfDetail.Large);
        this.$node.classed('medium', lod === LevelOfDetail.Medium);
        this.$node.classed('small', lod === LevelOfDetail.Small);
        this.$node.classed('xsmall', lod === LevelOfDetail.ExtraSmall);
        var states = StateRepr.toRepr(graph, this.highlight, { thumbnails: this.options.thumbnails });
        var $states = this.$node.select('div.states').selectAll('div.state').data(states, function (d) { return '' + d.s.id; });
        var $statesEnter = $states.enter().append('div')
            .classed('state', true)
            .attr('data-id', function (d) { return d.s.id; })
            .append('div')
            .on('click', this.onStateClick.bind(this))
            .on('mouseenter', function (d) {
            graph.selectState(d.s, idtypes.SelectOperation.SET, idtypes.hoverSelectionType);
        })
            .on('mouseleave', function (d) {
            graph.selectState(d.s, idtypes.SelectOperation.REMOVE, idtypes.hoverSelectionType);
        })
            .attr('draggable', true)
            .on('dragstart', function (d) {
            var e = d3.event;
            e.dataTransfer.effectAllowed = 'copy'; //none, copy, copyLink, copyMove, link, linkMove, move, all
            e.dataTransfer.setData('text/plain', d.s.name);
            e.dataTransfer.setData('application/phovea-prov-state', String(d.s.id));
        })
            .on('dragenter', function () {
            if (C.hasDnDType(d3.event, 'application/phovea-prov-state')) {
                d3.select(this).classed('hover', true);
                return false;
            }
        }).on('dragover', function () {
            if (C.hasDnDType(d3.event, 'application/phovea-prov-state')) {
                d3.event.preventDefault();
                C.updateDropEffect(d3.event);
                return false;
            }
        }).on('dragleave', function () {
            d3.select(this).classed('hover', false);
        }).on('drop', function (d) {
            d3.select(this).classed('hover', false);
            var e = d3.event;
            e.preventDefault();
            var state = that.data.getStateById(parseInt(e.dataTransfer.getData('application/phovea-prov-state'), 10));
            that.data.fork(state.creator, d.s);
            return false;
        });
        var $inner = $statesEnter;
        $inner.append('i').attr('class', 'fa fa-circle glyph');
        $inner.append('span').classed('icon', true);
        /*$states_enter.append('span').attr('class','star fa').on('click', (d) => {
          d.s.setAttr('starred',!d.s.getAttr('starred',false));
          d3.event.stopPropagation();
          d3.event.preventDefault();
        });
        $states_enter.append('span').attr('class','fa fa-tags').on('click', (d) => {
          var tags = d.s.getAttr('tags',[]).join(' ');
          dialogs.prompt(tags, 'Tags').then((new_) => {
            d.s.setAttr('tags', new_.split(' '));
          });
          d3.event.stopPropagation();
          d3.event.preventDefault();
        });*/
        $inner.append('span').classed('slabel', true);
        $inner.append('div').classed('sthumbnail', true);
        var $toolbarEnter = $statesEnter.append('div').classed('toolbar', true);
        $toolbarEnter.append('i').attr('class', 'fa bookmark fa-bookmark-o').on('click', function (d) {
            var v = !d.s.getAttr('starred', false);
            var e = d3.event;
            d.s.setAttr('starred', v);
            d3.select(this).classed('fa-bookmark', v).classed('fa-bookmark-o', !v);
            e.stopPropagation();
            e.preventDefault();
        });
        $toolbarEnter.append('i').attr('class', 'fa fa-edit').on('click', function (d) {
            var e = d3.event;
            d.showDialog();
            e.stopPropagation();
            e.preventDefault();
        });
        $states.call(StateRepr.render);
        $states.exit().remove();
        //just for the entered ones
        //(<any>$($states_enter[0][0]).find('> span.icon')).popover(StateRepr.popover)
        //  .parent().on({
        //    mouseenter: function () {
        //      var $icon = $(this).find('> span.icon');
        //      $icon.addClass('popping');
        //      $icon.data('popup', setTimeout(function () {
        //        (<any>$icon).popover('show');
        //      }, 200));
        //    },
        //    mouseleave: function () {
        //      var $icon = $(this).find('> span.icon');
        //      const id = +$icon.data('popoup');
        //      clearTimeout(id);
        //      $icon.removeData('popup');
        //      const d:StateRepr = d3.select(this).datum();
        //      if (d && $icon.has('textarea')) {
        //        const val = $(this).find('textarea').val();
        //        d.s.setAttr('tags', extractTags(val));
        //        d.s.setAttr('note', val);
        //      }
        //      (<any>$icon).popover('hide');
        //    }
        //  });
        var edges = [];
        states.forEach(function (s) {
            edges.push.apply(edges, s.children.map(function (c) { return ({ s: s, t: c }); }));
        });
        this.dim = [
            d3.max(states, function (s) { return s.xy[0] + s.size[0]; }) + (lod >= LevelOfDetail.Medium ? 200 : 0),
            d3.max(states, function (s) { return s.xy[1] + s.size[1]; })
        ];
        this.$node.select('svg')
            .attr('width', this.dim[0])
            .attr('height', this.dim[1]);
        var $edges = this.$node.select('svg g.edges').selectAll('path').data(edges, function (d) { return d.s.s.id + '-' + d.t.s.id; });
        $edges.enter().append('path');
        $edges.transition().attr('d', function (d) { return _this.line([d.s, d.t]); });
        $edges.exit().remove();
        this.updateStoryHighlight();
    };
    LayoutedProvVis.prototype.updateStoryHighlight = function () {
        //TODO hide if not needed
        var $g = this.$node.select('svg g.storyhighlights');
        var $states = this.$node.select('div.states').selectAll('div.state');
        var states = $states.data();
        var lookup = {};
        states.forEach(function (s) { return lookup[s.s.id] = s; });
        var firstSlide = this.data.selectedSlides()[0] || this.data.getSlideChains()[0];
        if (firstSlide) {
            $g.style('display', null);
            while (firstSlide.previous) {
                firstSlide = firstSlide.previous;
            }
            var line_1 = provenance.toSlidePath(firstSlide).map(function (s) { return s.state ? lookup[s.state.id] : null; }).filter(function (d) { return !!d; });
            $states.classed('story_member', function (d) { return line_1.indexOf(d) >= 0; });
            $g.select('path').attr('d', this.line.interpolate('linear')(line_1));
            this.line.interpolate('step-after');
        }
        else {
            $g.style('display', 'none');
        }
    };
    return LayoutedProvVis;
}(vis.AVisInstance));
export { LayoutedProvVis };
export function create(data, parent, options) {
    if (options === void 0) { options = {}; }
    return new LayoutedProvVis(data, parent, options);
}
//# sourceMappingURL=provvis.js.map