'use strict';

exports.__esModule = true;
exports.GraphistryJS = undefined;

var _extends2 = require('babel-runtime/helpers/extends');

var _extends3 = _interopRequireDefault(_extends2);

var _create = require('babel-runtime/core-js/object/create');

var _create2 = _interopRequireDefault(_create);

var _from = require('babel-runtime/core-js/array/from');

var _from2 = _interopRequireDefault(_from);

var _typeof2 = require('babel-runtime/helpers/typeof');

var _typeof3 = _interopRequireDefault(_typeof2);

var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');

var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);

var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');

var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);

var _inherits2 = require('babel-runtime/helpers/inherits');

var _inherits3 = _interopRequireDefault(_inherits2);

var _shallowEqual = require('recompose/shallowEqual');

var _shallowEqual2 = _interopRequireDefault(_shallowEqual);

var _falcorModelRxjs = require('@graphistry/falcor-model-rxjs');

var _falcorSocketDatasource = require('@graphistry/falcor-socket-datasource');

var _falcorJsonGraph = require('@graphistry/falcor-json-graph');

var _rxjs = require('./rxjs');

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/**
 * @class Graphistry
 * @classdesc This object wraps a HTML IFrame of a Graphistry Visualization in order
 * to provide an API for interacting with the graph.
 * @extends Observable
 * @see {@link https://github.com/ReactiveX/rxjs/blob/master/doc/observable.md}
 */
var Graphistry = function (_Observable) {
    (0, _inherits3.default)(Graphistry, _Observable);

    /**
     * Create Graphistry Observable by extending observable's methods
     * @param {Object} source - The source observable.
     */
    function Graphistry(source) {
        (0, _classCallCheck3.default)(this, Graphistry);

        if (!source || typeof source === 'function' || (typeof source === 'undefined' ? 'undefined' : (0, _typeof3.default)(source)) !== 'object') {
            var _this = (0, _possibleConstructorReturn3.default)(this, _Observable.call(this, source));
        } else {
            var _this = (0, _possibleConstructorReturn3.default)(this, _Observable.call(this));

            if (typeof source[_rxjs.$$observable] === 'function') {
                _this.source = source[_rxjs.$$observable]();
            } else {
                _this.source = _this.constructor.from(source);
            }
        }
        return (0, _possibleConstructorReturn3.default)(_this);
    }

    /**
     * Creates a new Observable with this as the source, and the passed
     * operator as the new Observable's operator.
     * @method Graphistry~lift
     * @param {Operator} operator - the operator defining the operation to take on the
     * observable
     * @return {Observable} a new observable with the operator applied
     */


    Graphistry.prototype.lift = function lift(operator) {
        var observable = new Graphistry(this);
        observable.operator = operator;
        return observable;
    };

    Graphistry._getIds = function _getIds(componentType, name, dataType) {
        var values = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : [];
        var view = this.view;

        return new this(view.call('componentsByType[\'' + componentType + '\'].rows.filter', [name, dataType, values], ['_index']).takeLast(1).map(function (_ref) {
            var _ref$json = _ref.json,
                json = _ref$json === undefined ? {} : _ref$json;
            var _json$componentsByTyp = json.componentsByType,
                componentsByType = _json$componentsByTyp === undefined ? {} : _json$componentsByTyp;
            var _componentsByType$com = componentsByType[componentType],
                componentsForType = _componentsByType$com === undefined ? {} : _componentsByType$com;
            var _componentsForType$ro = componentsForType.rows,
                rows = _componentsForType$ro === undefined ? {} : _componentsForType$ro;

            return (0, _from2.default)(rows.filter || []).filter(Boolean).map(function (_ref2) {
                var _index = _ref2._index;
                return _index;
            });
        }).toPromise());
    };

    /**
     * Add columns to the current graph visuzliation's dataset
     * @method Graphistry.addColumns
     * @params {...Arrays} columns - One of more columns to be appended to the dataset
     * @return {Promise} A promise to return the result of the callback
     * @example
     * GraphistryJS(document.getElementById('viz'))
     *     .flatMap(function(g) {
     *         window.g = g;
     *         const columns = [
     *             ['edge', 'highways', [66, 101, 280], 'number'],
     *             ['point', 'theme parks', ['six flags', 'disney world', 'great america'], 'string']
     *         ];
     *         console.log('adding columns', columns);
     *         return g.addColumns.apply(g, columns);
     *     })
     *     .subscribe();
     */


    Graphistry.addColumns = function addColumns() {
        for (var _len = arguments.length, columns = Array(_len), _key = 0; _key < _len; _key++) {
            columns[_key] = arguments[_key];
        }

        var view = this.view;

        return new this(this.from(columns).concatMap(function (column) {
            return view.call('columns.add', column);
        }).map(function (_ref3) {
            var columns = _ref3.json.columns;
            return columns;
        }).filter(Boolean).map(function (columns) {
            return columns[columns.length - 1].toJSON();
        }).toArray().toPromise());
    };

    /**
    * Change colors based on an attribute
    * @method Graphistry.encodeColor
    * @param {GraphType} [graphType] - 'point' or 'edge'
    * @param {Attribute} [attribute] - name of data column, e.g., 'degree'
    * @param {Variant} [variation] - If there are more bins than colors, use 'categorical' to repeat colors and 'continuous' to interpolate
    * @param {Array} [colors] - array of color name or hex codes
    * @return {Promise} The result of the callback
    * @example
    *  GraphistryJS(document.getElementById('viz'))
    *     .flatMap(function (g) {
    *         window.g = g;
    *         return g.encodeColor('point', 'degree', 'categorical', ['black', 'white'])
    *     })
    *     .subscribe();
    */


    Graphistry.encodeColor = function encodeColor(graphType, attribute, variation, colors) {
        var view = this.view;

        return new this(view.set((0, _falcorJsonGraph.$value)('histograms.encodings.' + graphType + '.color', { reset: false, variation: variation, name: 'user_' + Math.random(),
            encodingType: 'color', colors: colors, graphType: graphType, attribute: attribute })).map(function (_ref4) {
            var json = _ref4.json;
            return json.toJSON();
        }).toPromise());
    };

    /**
    * Reset color to value at page load
    * @method Graphistry.resetColor
    * @param {GraphType} [graphType] - 'point' or 'edge'
    * @return {Promise} The result of the callback
    * @example
    *  GraphistryJS(document.getElementById('viz'))
    *     .flatMap(function (g) {
    *         window.g = g;
    *         return g.encodeColor('point', 'degree', 'categorical', ['black', 'white'])
    *         return g.resetColor('point')
    *     })
    *     .subscribe();
    */


    Graphistry.resetColor = function resetColor(graphType) {
        var view = this.view;

        return new this(view.set((0, _falcorJsonGraph.$value)('histograms.encodings.' + graphType + '.color', { reset: true, encodingType: 'color' })).map(function (_ref5) {
            var json = _ref5.json;
            return json.toJSON();
        }).toPromise());
    };

    //=========


    /**
    * Change icons based on an attribute
    * @method Graphistry.encodeIcons
    * @param {GraphType} [graphType] - 'point' or 'edge'
    * @param {Attribute} [attribute] - name of data column, e.g., 'icon'
    * @return {Promise} The result of the callback
    * @example
    *  GraphistryJS(document.getElementById('viz'))
    *     .flatMap(function (g) {
    *         window.g = g;
    *         return g.encodeIcons('point', 'icon')
    *     })
    *     .subscribe();
    */


    Graphistry.encodeIcons = function encodeIcons(graphType, attribute) {
        var view = this.view;

        return new this(view.set((0, _falcorJsonGraph.$value)('histograms.encodings.' + graphType + '.icon', { reset: false, name: 'user_' + Math.random(),
            encodingType: 'icon', graphType: graphType, attribute: attribute })).map(function (_ref6) {
            var json = _ref6.json;
            return json.toJSON();
        }).toPromise());
    };

    /**
    * Reset icons to value at page load
    * @method Graphistry.resetIcons
    * @param {GraphType} [graphType] - 'point' or 'edge'
    * @return {Promise} The result of the callback
    * @example
    *  GraphistryJS(document.getElementById('viz'))
    *     .flatMap(function (g) {
    *         window.g = g;
    *         return g.encodeIcons('point', 'icon')
    *         return g.resetIcons('point')
    *     })
    *     .subscribe();
    */


    Graphistry.resetIcons = function resetIcons(graphType) {
        var view = this.view;

        return new this(view.set((0, _falcorJsonGraph.$value)('histograms.encodings.' + graphType + '.icon', { reset: true, encodingType: 'icon' })).map(function (_ref7) {
            var json = _ref7.json;
            return json.toJSON();
        }).toPromise());
    };

    //=========


    /**
     * Change size based on an attribute
     * @method Graphistry.encodeSize
     * @param {GraphType} [graphType] - 'point'
     * @param {Attribute} [attribute] - name of data column, e.g., 'degree'
     * @return {Promise} The result of the callback
     * @example
     *  GraphistryJS(document.getElementById('viz'))
     *     .flatMap(function (g) {
     *         window.g = g;
     *         return g.encodeSize('point', 'community_infomap')
     *     })
     *     .subscribe();
     */


    Graphistry.encodeSize = function encodeSize(graphType, attribute) {
        var view = this.view;

        return new this(view.set((0, _falcorJsonGraph.$value)('histograms.encodings.' + graphType + '.size', { reset: false, name: 'user_' + Math.random(),
            encodingType: 'size', graphType: graphType, attribute: attribute })).map(function (_ref8) {
            var json = _ref8.json;
            return json.toJSON();
        }).toPromise());
    };

    /**
     * Reset size to value at page load
     * @method Graphistry.resetSize
     * @param {GraphType} [graphType] - 'point'
     * @return {Promise} The result of the callback
     * @example
     *  GraphistryJS(document.getElementById('viz'))
     *     .flatMap(function (g) {
     *         window.g = g;
     *         return g.encodeSize('point', 'community_infomap')
     *         return g.resetSize('point')
     *     })
     *     .subscribe();
     */


    Graphistry.resetSize = function resetSize(graphType) {
        var view = this.view;

        return new this(view.set((0, _falcorJsonGraph.$value)('histograms.encodings.' + graphType + '.size', { reset: true, encodingType: 'size' })).map(function (_ref9) {
            var json = _ref9.json;
            return json.toJSON();
        }).toPromise());
    };

    /**
    * Toggle a leftside panel
    * @method Graphistry.toggleLeftPanel
    * @param {string} [panel] - Name of panel: filters, exclusions, scene, labels, layout
    * @param {boolean} [turnOn] - Whether to make panel visible, or turn all off
    * @return {Promise} The result of the callback
    * @example
    *  GraphistryJS(document.getElementById('viz'))
    *     .flatMap(function (g) {
    *         window.g = g;
    *         console.log('opening filters');
    *         return g.toggleLeftPanel('filters', true);
    *     })
    *     .subscribe();
    */


    Graphistry.toggleLeftPanel = function toggleLeftPanel(panel, turnOn) {
        var view = this.view;

        if (turnOn) {
            return new this(view.set((0, _falcorJsonGraph.$value)('filters.controls[0].selected', panel === 'filters'), (0, _falcorJsonGraph.$value)('scene.controls[1].selected', panel === 'scene'), (0, _falcorJsonGraph.$value)('labels.controls[0].selected', panel === 'labels'), (0, _falcorJsonGraph.$value)('layout.controls[0].selected', panel === 'layout'), (0, _falcorJsonGraph.$value)('exclusions.controls[0].selected', panel === 'exclusions'), (0, _falcorJsonGraph.$value)('panels.left', panel === 'filters' ? (0, _falcorJsonGraph.$ref)(view._path.concat('filters')) : panel === 'scene' ? (0, _falcorJsonGraph.$ref)(view._path.concat('scene')) : panel === 'labels' ? (0, _falcorJsonGraph.$ref)(view._path.concat('labels')) : panel === 'layout' ? (0, _falcorJsonGraph.$ref)(view._path.concat('layout')) : (0, _falcorJsonGraph.$ref)(view._path.concat('exclusions')))).map(function (_ref10) {
                var json = _ref10.json;
                return json.toJSON();
            }).toPromise());
        } else {
            return new this(view.set((0, _falcorJsonGraph.$value)('panels.left', undefined), (0, _falcorJsonGraph.$value)('filters.controls[0].selected', false), (0, _falcorJsonGraph.$value)('scene.controls[1].selected', false), (0, _falcorJsonGraph.$value)('labels.controls[0].selected', false), (0, _falcorJsonGraph.$value)('layout.controls[0].selected', false), (0, _falcorJsonGraph.$value)('exclusions.controls[0].selected', false)).map(function (_ref11) {
                var json = _ref11.json;
                return json.toJSON();
            }).toPromise());
        }
    };

    /**
     * Toggle inspector panel
     * @method Graphistry.toggleInspector
     * @param {boolean} [turnOn] - Whether to make panel visible
     * @return {Promise} The result of the callback
     * @example
     *  GraphistryJS(document.getElementById('viz'))
     *     .flatMap(function (g) {
     *         window.g = g;
     *         console.log('opening inspector panel');
     *         return g.toggleInspector(true);
     *     })
     *     .subscribe();
     */


    Graphistry.toggleInspector = function toggleInspector(turnOn) {
        var view = this.view;

        if (!turnOn) {
            return new this(view.set((0, _falcorJsonGraph.$value)('panels.bottom', undefined), (0, _falcorJsonGraph.$value)('inspector.controls[0].selected', false)).map(function (_ref12) {
                var json = _ref12.json;
                return json.toJSON();
            }).toPromise());
        } else {
            return new this(view.set((0, _falcorJsonGraph.$value)('inspector.controls[0].selected', true), (0, _falcorJsonGraph.$value)('timebar.controls[0].selected', false), (0, _falcorJsonGraph.$value)('panels.bottom', (0, _falcorJsonGraph.$ref)(view._path.concat('inspector')))).map(function (_ref13) {
                var json = _ref13.json;
                return json.toJSON();
            }).toPromise());
        }
    };

    /**
     * Toggle histogram panel
     * @method Graphistry.toggleHistograms
     * @param {boolean} [turnOn] - Whether to make panel visible
     * @return {Promise} The result of the callback
     * @example
     *  GraphistryJS(document.getElementById('viz'))
     *     .flatMap(function (g) {
     *         window.g = g;
     *         console.log('opening histogram panel');
     *         return g.toggleHistograms(true);
     *     })
     *     .subscribe();
     */


    Graphistry.toggleHistograms = function toggleHistograms(turnOn) {
        var view = this.view;

        if (!turnOn) {
            return new this(view.set((0, _falcorJsonGraph.$value)('panels.right', undefined), (0, _falcorJsonGraph.$value)('histograms.controls[0].selected', false)).map(function (_ref14) {
                var json = _ref14.json;
                return json.toJSON();
            }).toPromise());
        } else {
            return new this(view.set((0, _falcorJsonGraph.$value)('histograms.controls[0].selected', true), (0, _falcorJsonGraph.$value)('panels.right', (0, _falcorJsonGraph.$ref)(view._path.concat('histograms')))).map(function (_ref15) {
                var json = _ref15.json;
                return json.toJSON();
            }).toPromise());
        }
    };

    /**
     * Close the filters panel
     * @method Graphistry.closeFilters
     * @return {Promise} The result of the callback
     * @example
     * GraphistryJS(document.getElementById('viz'))
     *     .flatMap(function (g) {
     *         window.g = g;
     *         console.log('closing filters');
     *         return g.closeFilters();
     *     })
     *     .subscribe();
     */


    Graphistry.closeFilters = function closeFilters() {
        var view = this.view;

        return new this(view.set((0, _falcorJsonGraph.$value)('panels.left', undefined), (0, _falcorJsonGraph.$value)('filters.controls[0].selected', false)).map(function (_ref16) {
            var json = _ref16.json;
            return json.toJSON();
        }).toPromise());
    };

    /**
     * Run a number of steps of Graphistry's clustering algorithm
     * @method Graphistry.tickClustering
     * @static
     * @param {number} ticks - The number of ticks to run
     * @return {Promise} The result of the callback
     * @example
     * GraphistryJS(document.getElementById('viz'))
     *     .flatMap(function (g) {
     *         window.g = g;
     *         console.log('starting to cluster');
     *         return g.tickClustering();
     *     })
     *     .subscribe();
     */


    Graphistry.tickClustering = function tickClustering() {
        var ticks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;


        var obs = void 0;
        var view = this.view;


        if (typeof ticks !== 'number') {
            obs = _rxjs.Observable.of({});
        } else {
            obs = _rxjs.Observable.timer(0, 40).take(Math.abs(ticks) || 1).concatMap(function () {
                return view.call('tick', []);
            }).takeLast(1);
        }

        return new this(obs.toPromise());
    };

    /**
     * Center the view of the graph
     * @method Graphistry.autocenter
     * @static
     * @param {number} percentile - Controls sensitivity to outliers
     * @param {function} [cb] - Callback function of type callback(error, result)
     * @return {Promise} The result of the callback
     * @example
     * GraphistryJS(document.getElementById('viz'))
     *     .flatMap(function (g) {
     *         window.g = g;
     *         console.log('centering');
     *         return g.autocenter(.90);
     *     })
     *     .subscribe();
     */


    Graphistry.autocenter = function autocenter(percentile, cb) {};

    /**
     * Read the workbook ID
     * @method Graphistry.getCurrentWorkbook
     * @static
     * @param {function} [cb] - Callback function of type callback(error, result)
     * @return {Promise} The result of the callback
     * @example
     * GraphistryJS(document.getElementById('viz'))
     *     .flatMap(function (g) {
     *         window.g = g;
     *         console.log('getting workbook id');
     *         return g.getCurrentWorkbook();
     *     })
     *     .subscribe(function (workbook) {
     *         alert('id: ' + workbook.id)
     *     });
     */


    Graphistry.getCurrentWorkbook = function getCurrentWorkbook() {
        var workbook = this.workbook;

        return new this(workbook.get('id').map(function (_ref17) {
            var json = _ref17.json;
            return json.toJSON();
        }).toPromise());
    };

    /**
     * Save the current workbook. A saved workbook will persist the analytics state
     * of the visualization, including active filters and exclusions
     * @method Graphistry.saveWorkbook
     * @static
     * @return {Promise} The result of the callback
     * @example
     * GraphistryJS(document.getElementById('viz'))
     *     .flatMap(function (g) {
     *         window.g = g;
     *         return g.saveWorkbook();
     *     })
     *     .subscribe();
     */


    Graphistry.saveWorkbook = function saveWorkbook() {
        var workbook = this.workbook;


        return new this(workbook.call('save', []).map(function (_ref18) {
            var json = _ref18.json;
            return json.toJSON();
        }).toPromise());
    };

    /**
     * Hide or Show Toolbar UI
     * @method Graphistry.toogleToolbar
     * @static
     * @param {boolean} show - Set to true to show toolbar, and false to hide toolbar.
     * @return {Promise} The result of the callback
     * @example
     *
     * <button onclick="window.graphistry.toggleToolbar(false)">Hide toolbar</button>
     * <button onclick="window.graphistry.toggleToolbar(true)">Show toolbar</button>
     *
     */


    Graphistry.toggleToolbar = function toggleToolbar(show) {
        return this.updateSetting('showToolbar', !!show);
    };

    /**
     * Add a filter to the visualization with the given expression
     * @method Graphistry.addFilter
     * @static
     * @param {string} expr - An expression using the same language as our in-tool
     * exclusion and filter panel
     * @return {Promise} The result of the callback
     * @example
     * GraphistryJS(document.getElementById('viz'))
     *     .flatMap(function (g) {
     *         window.g = g;
     *         console.log('Adding filter for "point:degree > 0"');
     *         return g.addFilter('point:degree > 0');
     *     })
     *     .subscribe();
     */


    Graphistry.addFilter = function addFilter(expr) {
        var view = this.view;


        return new this(view.call('filters.add', [expr]).map(function (_ref19) {
            var json = _ref19.json;
            return json.toJSON();
        }).toPromise());
    };

    /**
     * Add an exclusion to the visualization with the given expression
     * @method Graphistry.addExclusion
     * @static
     * @param {string} expr - An expression using the same language as our in-tool
     * exclusion and filter panel
     * @return {Promise} The result of the callback
     * @example
     * GraphistryJS(document.getElementById('viz'))
     *     .flatMap(function (g) {
     *         window.g = g;
     *         console.log('Adding exclusion for "point:degree > 0"');
     *         return g.addExclusion('point:degree > 0');
     *     })
     *     .subscribe();
     */


    Graphistry.addExclusion = function addExclusion(expr) {
        var view = this.view;


        return new this(view.call('exclusions.add', [expr]).map(function (_ref20) {
            var json = _ref20.json;
            return json.toJSON();
        }).toPromise());
    };

    /**
     * Modify a settings value in the visualization
     * Key settings: showArrows, pruneOrphans, edgeOpacity, edgeSize, pointOpacity,
     * pointSize, labelOpacity, labelEnabled, labelPOI, labelHighlightEnabled, zoom
     * @method Graphistry.updateSetting
     * @static
     * @param {string} name - the name of the setting to change
     * @param {string} val - the value to set the setting to.
     * @return {Promise} The result of the callback
     */


    Graphistry.updateSetting = function updateSetting(name, val) {

        var lookup = {

            //models/toolbar.js
            'showToolbar': ['view', 'toolbar.visible'],

            //models/scene/scene.js
            'pruneOrphans': ['view', 'pruneOrphans'],
            'showArrows': ['view', 'scene.renderer.showArrows'],
            'background': ['view', 'scene.renderer.background.color'],
            'edgeOpacity': ['view', 'scene.renderer.edges.opacity'],
            'edgeSize': ['view', 'scene.renderer.edges.scaling'],
            'pointOpacity': ['view', 'scene.renderer.points.opacity'],
            'pointSize': ['view', 'scene.renderer.points.scaling'],

            //models/camera.js
            'zoom': ['view', 'scene.camera.zoom'],
            'center': ['view', 'scene.camera.center["x", "y", "z"]'],

            //models/label.js
            'labelOpacity': ['view', 'scene.labels.opacity'],
            'labelEnabled': ['view', 'scene.labels.enabled'],
            'labelPOI': ['view', 'scene.labels.poiEnabled'],
            'labelHighlightEnabled': ['view', 'scene.labels.highlightEnabled'],
            'labelColor': ['view', 'scene.labels.foreground.color'],
            'labelBackground': ['view', 'scene.labels.background.color'],

            //models/layout.js => viz-worker/simulator/layout.config.js:
            'precisionVsSpeed': ['view', 'layout.options.tau']

        };

        var _lookup$name = lookup[name],
            model = _lookup$name[0],
            path = _lookup$name[1];


        return new this(this[model].set((0, _falcorJsonGraph.$value)(path, (0, _falcorJsonGraph.$atom)(val, { $timestamp: Date.now() }))).map(function (_ref21) {
            var json = _ref21.json;
            return json.toJSON();
        }).toPromise());
    };

    /**
     * Update the camera zoom level
     * @method Graphistry.updateZoom
     * @static
     * @param {string} level - Controls how far to zoom in or out.
     * @param {string} val - the value to set the setting to.
     * @return {Promise} The result of the callback
     */


    Graphistry.updateZoom = function updateZoom(level) {
        return this.updateSetting('zoom', level);
    };

    /**
     * Get or create an Observable stream of all label updates from the visualization.
     * <p>
     * The Observable returned by this method emits inner Observables, where each
     * inner Observable is tied to the lifetime of the label for which it was created.
     * </p><p>
     * For each label rendered in the visualization, the Observable returned by this
     * method will create and emit a new inner Observable. The inner Observable will
     * emit events when the label changes. For example, if the user clicks on the label,
     * or the label changes position because of a pan/zoom, the inner Observable will
     * emit an event.
     * </p><p>
     * The inner Observable for a label will complete if the label is removed from the screen.
     * </p><p>
     * @method Graphistry.labelUpdates
     * @static
     * @return {Observable<Observable<LabelEvent>>} An Observable of inner Observables, where each
     * inner Observable represents the lifetime of a label in the visualization.
     */


    Graphistry.labelUpdates = function labelUpdates() {
        return this.labelsStream || (this.labelsStream = this.fromEvent(window, 'message').pluck('data').filter(function (data) {
            return data && data.type === 'labels-update';
        }).multicast(function () {
            return new _rxjs.ReplaySubject(1);
        }).let(function (connectable) {
            return connectable.connect() && connectable.refCount();
        }).scan(function (memo, _ref22) {
            var labels = _ref22.labels,
                simulating = _ref22.simulating,
                semanticZoomLevel = _ref22.semanticZoomLevel;


            labels = labels || [];
            var updates = [],
                newSources = [];
            var labelsById = (0, _create2.default)(null);
            var nextSources = (0, _create2.default)(null);
            var sources = memo.sources,
                prevLabelsById = memo.prevLabelsById;

            var idx = -1,
                len = labels.length,
                label = void 0;

            while (++idx < len) {
                var source = void 0;
                label = labels[idx];
                var _label = label,
                    id = _label.id;


                if (id in sources) {
                    source = sources[id];
                    delete sources[id];
                    if (memo.simulating !== simulating || memo.semanticZoomLevel !== semanticZoomLevel || !(0, _shallowEqual2.default)(prevLabelsById[id], label)) {
                        updates.push((0, _extends3.default)({}, label, { simulating: simulating, semanticZoomLevel: semanticZoomLevel, tag: 'updated' }));
                    }
                } else {
                    newSources.push(source = new _rxjs.ReplaySubject(1));
                    updates.push((0, _extends3.default)({}, label, { simulating: simulating, semanticZoomLevel: semanticZoomLevel, tag: 'added' }));
                    source.key = id;
                }

                labelsById[id] = label;
                nextSources[id] = source;
            }

            for (var id in sources) {
                sources[id].complete();
            }

            idx = -1;
            len = updates.length;
            while (++idx < len) {
                label = updates[idx];
                nextSources[label.id].next(label);
            }

            return {
                newSources: newSources,
                simulating: simulating,
                semanticZoomLevel: semanticZoomLevel,
                sources: nextSources,
                prevLabelsById: labelsById
            };
        }, {
            newSources: [],
            sources: (0, _create2.default)(null),
            prevLabelsById: (0, _create2.default)(null)
        }).mergeMap(function (_ref23) {
            var newSources = _ref23.newSources;
            return newSources;
        }));
    };

    /**
     * Subscribe to label change and exit events
     * @method Graphistry.subscribeLabels
     * @static
     * @param {subscriptions} subscriptions - A list of subscriptions that
     * will subscribe to any label updates
     * @return {Subscription} The result of the callback
     */


    Graphistry.subscribeLabels = function subscribeLabels(_ref24) {
        var onChange = _ref24.onChange,
            onExit = _ref24.onExit;

        return this.labelUpdates().mergeMap(function (group) {
            return group.do(function (event) {
                return onChange && onChange(event);
            }).takeLast(1).do(function (event) {
                return onExit && onExit(event);
            });
        }).subscribe();
    };

    return Graphistry;
}(_rxjs.Observable);

/**
 * Function that creates a Graphistry Wrapped IFrame
 * @func GraphistryJS
 * @param {Object} IFrame - An IFrame that incudes a Graphistry visualization.
 * @return {Graphistry}
 * @example
 *
 * <iframe id="viz" src="http://127.0.0.1:10000/graph/graph.html?dataset=Miserables" />
 * <script>
 * document.addEventListener("DOMContentLoaded", function () {
 *
 *     GraphistryJS(document.getElementById('viz'))
 *         .flatMap(function (g) {
 *             window.g = g;
 *             document.getElementById('controls').style.opacity=1.0;
 *             console.log('opening filters');
 *             return g.openFilters();
 *         })
 *         .delay(5000)
 *         .flatMap(function() {
 *             console.log('filters opened');
 *             const columns = [
 *                 ['edge', 'highways', [66, 101, 280], 'number'],
 *                 ['point', 'theme parks', ['six flags', 'disney world', 'great america'], 'string']
 *             ];
 *             console.log('adding columns', columns);
 *             return g.addColumns.apply(g, columns);
 *        })
 *         .subscribe(function (result) {
 *             console.log('all columns: ', result);
 *         });
 * });
 * </script>
 *
 */


Graphistry.view = null;
Graphistry.model = null;
Graphistry.workbook = null;
Graphistry.iFrame = null;
function GraphistryJS(iFrame) {

    if (!iFrame) {
        throw new Error('No iframe provided to Graphistry');
    }

    var model = new _falcorModelRxjs.Model({
        recycleJSON: true,
        scheduler: _rxjs.Scheduler.async,
        allowFromWhenceYouCame: true
    });

    model._source = new _falcorSocketDatasource.PostMessageDataSource(window, iFrame.contentWindow, model);

    var InstalledGraphistry = function (_Graphistry) {
        (0, _inherits3.default)(InstalledGraphistry, _Graphistry);

        function InstalledGraphistry() {
            (0, _classCallCheck3.default)(this, InstalledGraphistry);
            return (0, _possibleConstructorReturn3.default)(this, _Graphistry.apply(this, arguments));
        }

        InstalledGraphistry.prototype.lift = function lift(operator) {
            var observable = new InstalledGraphistry(this);
            observable.operator = operator;
            return observable;
        };

        return InstalledGraphistry;
    }(Graphistry);

    InstalledGraphistry.model = model;
    InstalledGraphistry.iFrame = iFrame;


    InstalledGraphistry = wrapStaticObservableMethods(_rxjs.Observable, InstalledGraphistry);

    return InstalledGraphistry.defer(function () {

        var initEvent = _rxjs.Observable.fromEvent(window, 'message').filter(function (_ref25) {
            var data = _ref25.data;
            return data && data.type === 'init';
        }).do(function (_ref26) {
            var data = _ref26.data;
            return model.setCache(data.cache);
        }).mergeMap(function (_ref27) {
            var data = _ref27.data;
            return model.get('workbooks.open.views.current.id');
        }, function (_ref28, _ref29) {
            var data = _ref28.data,
                source = _ref28.source;
            var json = _ref29.json;


            var workbook = json.workbooks.open;
            var view = workbook.views.current;

            InstalledGraphistry.workbook = model.deref(workbook);
            InstalledGraphistry.view = model.deref(view);

            console.log('initialized with view \'' + view.id + '\'');
            console.log('parent sending initialized message');

            source.postMessage({
                type: 'initialized',
                agent: 'graphistryjs',
                version: '2.11.218'
            }, '*');

            return InstalledGraphistry;
        }).take(1).multicast(new _rxjs.AsyncSubject());

        initEvent.connect();

        console.log('parent sending ready message');

        // trigger hello if missed initial one
        iFrame.contentWindow.postMessage({
            type: 'ready',
            agent: 'graphistryjs',
            version: '2.11.218'
        }, '*');

        return initEvent;
    });
}

Graphistry = wrapStaticObservableMethods(_rxjs.Observable, Graphistry);

exports.GraphistryJS = GraphistryJS;
// export default GraphistryJS;

function wrapStaticObservableMethods(Observable, Graphistry) {
    function createStaticWrapper(staticMethodName) {
        return function () {
            return new Graphistry(Observable[staticMethodName].apply(Observable, arguments));
        };
    }
    for (var staticMethodName in Observable) {
        Graphistry[staticMethodName] = createStaticWrapper(staticMethodName);
    }
    Graphistry.bindCallback = function () {
        for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
            args[_key2] = arguments[_key2];
        }

        return function () {
            return new Graphistry(Observable.bindCallback.apply(Observable, args).apply(undefined, arguments));
        };
    };
    Graphistry.bindNodeCallback = function () {
        for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
            args[_key3] = arguments[_key3];
        }

        return function () {
            return new Graphistry(Observable.bindNodeCallback.apply(Observable, args).apply(undefined, arguments));
        };
    };
    return Graphistry;
}

/**
 * A LabelEvent is dispatched by the inner Observables emitted by the labelUpdates() Observable.
 * A LabelEvent is generated for each label update in the visualization.
 * @typedef {Object} LabelEvent
 * @property {number} id - the integer ID for the element the label describes
 * @property {String} tag - a string that describes the update kind, either 'added' or 'updated'
 * @property {String} type - the graph component type for the element the label describes, either 'edge' or 'point'
 * @property {number} size - the size in pixels of the element the label describes. This is 0 for edges, and the diameter for points
 * @property {number} pageX - the pageX of the element the label describes
 * @property {number} pageY - the pageY of the element the label describes
 * @property {boolean} selected - a boolean that describes whether element the label describes is selected
 * @property {boolean} highlight - a boolean that describes whether element the label describes is highlighted
 * @property {boolean} simulating - a boolean that indicates the visualization is running clustering
 * @property {number} semanticZoomLevel - the semantic zoom level of the visualization
 */

/**
 * @constructor Observable
 * @see {@link https://github.com/ReactiveX/rxjs/blob/master/doc/observable.md}
 */

/**
* The subscribe method triggers the execution of the Observable, causing the values within to be pushed to a callback. An Observable is like a pipe of water that is closed. When subscribe is called, we open the valve and the values within are pushed at us.  These values can be received using either callbacks or an {@link Observer} object.
* @name subscribe
* @memberof Observable.prototype
* @function
* @arg {?Observable~nextCallback} next a callback that accepts the next value in the stream of values
* @arg {?Observable~errorCallback} error a callback that accepts an error that occurred while evaluating the operation underlying the {@link Observable} stream
* @arg {?Observable~completeCallback} completed a callback that is invoked when the {@link Observable} stream has ended, and the {@link Observable~nextCallback} will not receive any more values
* @return {Subscription}
*/

/**
 * This callback accepts a value that was emitted while evaluating the operation underlying the {@link Observable} stream.
 * @callback Observable~nextCallback
 * @param {Object} value the value that was emitted while evaluating the operation underlying the {@link Observable}
 */

/**
 * This callback accepts an error that occurred while evaluating the operation underlying the {@link Observable} stream. When this callback is invoked, the {@link Observable} stream ends and no more values will be received by the {@link Observable~nextCallback}.
 * @callback Observable~errorCallback
 * @param {Error} error the error that occurred while evaluating the operation underlying the {@link Observable}
 */

/**
* This callback is invoked when the {@link Observable} stream ends. When this callback is invoked the {@link Observable} stream has ended, and therefore the {@link Observable~nextCallback} will not receive any more values.
* @callback Observable~completeCallback
*/

/**
 * @constructor Subscription
 * @see {@link https://github.com/ReactiveX/rxjs/blob/master/doc/subscription.md}
 */

/**
 * When this method is called on the Subscription, the Observable that created the Subscription will stop sending values to the callbacks passed when the Subscription was created.
 * @name unsubscribe
 * @method
 * @memberof Subscription.prototype
 */