@deephaven/golden-layout
Version:
A multi-screen javascript Layout manager
250 lines (238 loc) • 10.7 kB
JavaScript
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
import $ from 'jquery';
import { getUniqueId, minifyConfig, EventEmitter } from "../utils/index.js";
/**
* Pops a content item out into a new browser window.
* This is achieved by
*
* - Creating a new configuration with the content item as root element
* - Serializing and minifying the configuration
* - Opening the current window's URL with the configuration as a GET parameter
* - GoldenLayout when opened in the new window will look for the GET parameter
* and use it instead of the provided configuration
*
* @param config GoldenLayout item config
* @param dimensions A map with width, height, top and left
* @param parentId The id of the element the item will be appended to on popIn
* @param indexInParent The position of this element within its parent
* @param layoutManager
*/
export default class BrowserPopout extends EventEmitter {
constructor(config, dimensions, parentId, indexInParent, layoutManager) {
super();
_defineProperty(this, "isInitialised", false);
_defineProperty(this, "_config", void 0);
_defineProperty(this, "_dimensions", void 0);
_defineProperty(this, "_parentId", void 0);
_defineProperty(this, "_indexInParent", void 0);
_defineProperty(this, "_layoutManager", void 0);
_defineProperty(this, "_popoutWindow", null);
_defineProperty(this, "_id", null);
this._config = config;
this._dimensions = dimensions;
this._parentId = parentId;
this._indexInParent = indexInParent;
this._layoutManager = layoutManager;
this._createWindow();
}
toConfig() {
var _this$getGlInstance, _this$getGlInstance2, _ref, _this$_popoutWindow$s, _this$_popoutWindow, _this$_popoutWindow2, _ref2, _this$_popoutWindow$s2, _this$_popoutWindow3, _this$_popoutWindow4, _this$getGlInstance3;
if (this.isInitialised === false) {
throw new Error("Can't create config, layout not yet initialised");
}
return {
dimensions: {
width: (_this$getGlInstance = this.getGlInstance()) === null || _this$getGlInstance === void 0 ? void 0 : _this$getGlInstance.width,
height: (_this$getGlInstance2 = this.getGlInstance()) === null || _this$getGlInstance2 === void 0 ? void 0 : _this$getGlInstance2.height,
left: (_ref = (_this$_popoutWindow$s = (_this$_popoutWindow = this._popoutWindow) === null || _this$_popoutWindow === void 0 ? void 0 : _this$_popoutWindow.screenX) !== null && _this$_popoutWindow$s !== void 0 ? _this$_popoutWindow$s : (_this$_popoutWindow2 = this._popoutWindow) === null || _this$_popoutWindow2 === void 0 ? void 0 : _this$_popoutWindow2.screenLeft) !== null && _ref !== void 0 ? _ref : 0,
top: (_ref2 = (_this$_popoutWindow$s2 = (_this$_popoutWindow3 = this._popoutWindow) === null || _this$_popoutWindow3 === void 0 ? void 0 : _this$_popoutWindow3.screenY) !== null && _this$_popoutWindow$s2 !== void 0 ? _this$_popoutWindow$s2 : (_this$_popoutWindow4 = this._popoutWindow) === null || _this$_popoutWindow4 === void 0 ? void 0 : _this$_popoutWindow4.screenTop) !== null && _ref2 !== void 0 ? _ref2 : 0
},
content: (_this$getGlInstance3 = this.getGlInstance()) === null || _this$getGlInstance3 === void 0 ? void 0 : _this$getGlInstance3.toConfig().content,
parentId: this._parentId,
indexInParent: this._indexInParent
};
}
getGlInstance() {
var _this$_popoutWindow5;
return (_this$_popoutWindow5 = this._popoutWindow) === null || _this$_popoutWindow5 === void 0 ? void 0 : _this$_popoutWindow5.__glInstance;
}
getWindow() {
return this._popoutWindow;
}
close() {
if (this.getGlInstance()) {
var _this$getGlInstance4;
(_this$getGlInstance4 = this.getGlInstance()) === null || _this$getGlInstance4 === void 0 || _this$getGlInstance4._$closeWindow();
} else {
try {
var _this$getWindow;
(_this$getWindow = this.getWindow()) === null || _this$getWindow === void 0 || _this$getWindow.close();
} catch (e) {}
}
}
/**
* Returns the popped out item to its original position. If the original
* parent isn't available anymore it falls back to the layout's topmost element
*/
popIn() {
var _parentItem;
var index = this._indexInParent;
var childConfig = null;
var parentItem = null;
if (this._parentId) {
var _this$getGlInstance5;
/*
* The $.extend call seems a bit pointless, but it's crucial to
* copy the config returned by this.getGlInstance().toConfig()
* onto a new object. Internet Explorer keeps the references
* to objects on the child window, resulting in the following error
* once the child window is closed:
*
* The callee (server [not server application]) is not available and disappeared
*/
childConfig = $.extend(true, {}, (_this$getGlInstance5 = this.getGlInstance()) === null || _this$getGlInstance5 === void 0 ? void 0 : _this$getGlInstance5.toConfig()).content[0];
parentItem = this._layoutManager.root.getItemsById(this._parentId)[0];
/*
* Fallback if parentItem is not available. Either add it to the topmost
* item or make it the topmost item if the layout is empty
*/
if (!parentItem) {
var _this$_layoutManager$;
if (((_this$_layoutManager$ = this._layoutManager.root.contentItems.length) !== null && _this$_layoutManager$ !== void 0 ? _this$_layoutManager$ : 0) > 0) {
parentItem = this._layoutManager.root.contentItems[0];
} else {
parentItem = this._layoutManager.root;
}
index = 0;
}
}
if (!childConfig) {
return;
}
(_parentItem = parentItem) === null || _parentItem === void 0 || _parentItem.addChild(childConfig, this._indexInParent);
this.close();
}
/**
* Creates the URL and window parameter
* and opens a new window
*/
_createWindow() {
var url = this._createUrl();
/**
* Bogus title to prevent re-usage of existing window with the
* same title. The actual title will be set by the new window's
* GoldenLayout instance if it detects that it is in subWindowMode
*/
var title = Math.floor(Math.random() * 1000000).toString(36);
/**
* The options as used in the window.open string
*/
var options = this._serializeWindowOptions({
width: this._dimensions.width,
height: this._dimensions.height,
innerWidth: this._dimensions.width,
innerHeight: this._dimensions.height,
menubar: 'no',
toolbar: 'no',
location: 'no',
personalbar: 'no',
resizable: 'yes',
scrollbars: 'no',
status: 'no'
});
// I'm not entirely sure how __glInstance is mounted to the popout window
this._popoutWindow = window.open(url, title, options);
if (!this._popoutWindow) {
if (this._layoutManager.config.settings.blockedPopoutsThrowError === true) {
var error = new Error('Popout blocked');
error.type = 'popoutBlocked';
throw error;
} else {
return;
}
}
$(this._popoutWindow).on('load', this._positionWindow.bind(this)).on('unload beforeunload', this._onClose.bind(this));
/**
* Polling the childwindow to find out if GoldenLayout has been initialised
* doesn't seem optimal, but the alternatives - adding a callback to the parent
* window or raising an event on the window object - both would introduce knowledge
* about the parent to the child window which we'd rather avoid
*/
var checkReadyInterval = window.setInterval(() => {
var _this$_popoutWindow6;
if ((_this$_popoutWindow6 = this._popoutWindow) !== null && _this$_popoutWindow6 !== void 0 && _this$_popoutWindow6.__glInstance && this._popoutWindow.__glInstance.isInitialised) {
this._onInitialised();
window.clearInterval(checkReadyInterval);
}
}, 10);
}
/**
* Serialises a map of key:values to a window options string
*
* @param windowOptions
*
* @returns serialised window options
*/
_serializeWindowOptions(windowOptions) {
var windowOptionsString = [],
key;
for (key in windowOptions) {
windowOptionsString.push(key + '=' + windowOptions[key]);
}
return windowOptionsString.join(',');
}
/**
* Creates the URL for the new window, including the
* config GET parameter
*
* @returns URL
*/
_createUrl() {
var config = {
content: this._config
};
var storageKey = 'gl-window-config-' + getUniqueId();
config = minifyConfig(config);
try {
localStorage.setItem(storageKey, JSON.stringify(config));
} catch (e) {
throw new Error('Error while writing to localStorage ' + e.toString());
}
var urlParts = document.location.href.split('?');
// URL doesn't contain GET-parameters
if (urlParts.length === 1) {
return urlParts[0] + '?gl-window=' + storageKey;
// URL contains GET-parameters
} else {
return document.location.href + '&gl-window=' + storageKey;
}
}
/**
* Move the newly created window roughly to
* where the component used to be.
*/
_positionWindow() {
var _this$_popoutWindow7, _this$_popoutWindow8;
(_this$_popoutWindow7 = this._popoutWindow) === null || _this$_popoutWindow7 === void 0 || _this$_popoutWindow7.moveTo(this._dimensions.left, this._dimensions.top);
(_this$_popoutWindow8 = this._popoutWindow) === null || _this$_popoutWindow8 === void 0 || _this$_popoutWindow8.focus();
}
/**
* Callback when the new window is opened and the GoldenLayout instance
* within it is initialised
*/
_onInitialised() {
var _this$getGlInstance6;
this.isInitialised = true;
(_this$getGlInstance6 = this.getGlInstance()) === null || _this$getGlInstance6 === void 0 || _this$getGlInstance6.on('popIn', this.popIn, this);
this.emit('initialised');
}
/**
* Invoked 50ms after the window unload event
*/
_onClose() {
setTimeout(this.emit.bind(this, 'closed'), 50);
}
}
//# sourceMappingURL=BrowserPopout.js.map