diff --git a/js/src/widget_ngl.ts b/js/src/widget_ngl.ts index 0c2dac0a..117b0611 100644 --- a/js/src/widget_ngl.ts +++ b/js/src/widget_ngl.ts @@ -15,7 +15,7 @@ import 'jquery-ui/themes/base/all.css' // FIXME: remove? import { StageWidget } from "./gui" import { FullscreenModel, FullscreenView } from "./fullscreen" import { ColormakerRegistryModel, ColormakerRegistryView } from "./color" -import { ThemeManagerModel, ThemeManagerView} from "./theme" +import { ThemeManagerModel, ThemeManagerView } from "./theme" NGL.nglview_debug = false @@ -24,28 +24,28 @@ NGL.nglview_debug = false const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('') const uuid = new Array(36) -function generateUUID () { - let rnd = 0 - let r - - for (let i = 0; i < 36; i++) { - if (i === 8 || i === 13 || i === 18 || i === 23) { - uuid[ i ] = '-' - } else if (i === 14) { - uuid[ i ] = '4' - } else { - if (rnd <= 0x02) rnd = 0x2000000 + (Math.random() * 0x1000000) | 0 - r = rnd & 0xf - rnd = rnd >> 4 - uuid[ i ] = chars[ (i === 19) ? (r & 0x3) | 0x8 : r ] +function generateUUID() { + let rnd = 0 + let r + + for (let i = 0; i < 36; i++) { + if (i === 8 || i === 13 || i === 18 || i === 23) { + uuid[i] = '-' + } else if (i === 14) { + uuid[i] = '4' + } else { + if (rnd <= 0x02) rnd = 0x2000000 + (Math.random() * 0x1000000) | 0 + r = rnd & 0xf + rnd = rnd >> 4 + uuid[i] = chars[(i === 19) ? (r & 0x3) | 0x8 : r] + } } - } - return uuid.join('') + return uuid.join('') } -async function createView(that, trait_name){ +async function createView(that, trait_name) { // Create a view for the model with given `trait_name` // e.g: in backend, 'view.` console.log("Creating view for model " + trait_name); @@ -56,8 +56,8 @@ async function createView(that, trait_name){ export -class NGLModel extends widgets.DOMWidgetModel{ - defaults(){ + class NGLModel extends widgets.DOMWidgetModel { + defaults() { return _.extend(widgets.DOMWidgetModel.prototype.defaults(), { _model_name: 'NGLModel', _model_module: 'nglview-js-widgets', @@ -70,10 +70,10 @@ class NGLModel extends widgets.DOMWidgetModel{ } export -class NGLView extends widgets.DOMWidgetView{ - render(){ + class NGLView extends widgets.DOMWidgetView { + render() { this.beforeDisplay() - this.displayed.then(function() { + this.displayed.then(function () { // move all below code inside 'displayed' // to make sure the NGLView and NGLModel are created this.createStage() @@ -85,7 +85,7 @@ class NGLView extends widgets.DOMWidgetView{ } - beforeDisplay(){ + beforeDisplay() { this.model.on("change:_parameters", this.parametersChanged, this); this.model.on("change:gui_style", this.GUIStyleChanged, this); this.model.set('_ngl_version', NGL.Version); @@ -96,19 +96,19 @@ class NGLView extends widgets.DOMWidgetView{ this._synced_model_ids = this.model.get("_synced_model_ids"); this._synced_repr_model_ids = this.model.get("_synced_repr_model_ids") - if (this.isEmbeded()){ + if (this.isEmbeded()) { // embed mode this._handleEmbedBeforeStage() } } - createStage(){ + createStage() { // init NGL stage var stage_params = { // Shallow copy so that _ngl_full_stage_parameters is not updated yet ...this.model.get("_ngl_full_stage_parameters") }; - if (!("backgroundColor" in stage_params)){ + if (!("backgroundColor" in stage_params)) { stage_params["backgroundColor"] = "white" } NGL.useWorker = false; @@ -132,30 +132,30 @@ class NGLView extends widgets.DOMWidgetView{ this.$container.resizable( "option", "maxWidth", this.$el.parent().width() ); - if (this.isEmbeded()){ + if (this.isEmbeded()) { console.log("Embed mode for NGLView") that.handleEmbed(); - }else{ + } else { this.requestUpdateStageParameters(); - if (this.model.views.length == 1){ + if (this.model.views.length == 1) { this.serialize_camera_orientation(); - }else{ + } else { this.set_camera_orientation(that.model.get("_camera_orientation")); } } } - isEmbeded(){ + isEmbeded() { return (this.model.get("_ngl_serialize") || (this.model.comm == undefined)) } - handleMessage(){ - this.model.on("msg:custom", function(msg){ - this.on_msg(msg); + handleMessage() { + this.model.on("msg:custom", function (msg) { + this.on_msg(msg); }, this); if (this.model.comm) { - this.model.comm.on_msg(function(msg) { + this.model.comm.on_msg(function (msg) { var buffers = msg.buffers; var content = msg.content.data.content; if (buffers.length && content) { @@ -167,197 +167,197 @@ class NGLView extends widgets.DOMWidgetView{ } - finalizeDisplay(){ - // for callbacks from Python - // must be after initializing NGL.Stage - this.send({ - 'type': 'request_loaded', - 'data': true - }) - var state_params = this.stage.getParameters(); - this.model.set('_ngl_original_stage_parameters', state_params); - this.touch(); - if (!this.isEmbeded() && this.stage.compList.length < this.model.get("n_components")) { - // only call this in notebook to avoid calling handleEmbed twice in embeded mode. - this.handleEmbed() - } - var ngl_view_ids = this.model.get("_ngl_view_id") - ngl_view_ids.push(this.ngl_view_id) - this.send({"type": "updateIDs", "data": ngl_view_ids}) - - // FIXME: Why below doesn't update _ngl_view_id in backend? - // this.model.set("_ngl_view_id", ngl_view_ids) - // this.touch() - } - - handleSignals(){ - var container = this.stage.viewer.container; - var that = this; - container.addEventListener('mouseover', function(e) { - that._ngl_focused = 1; - e; // linter - that.mouseOverDisplay('block') - }, false); - - container.addEventListener('mouseout', function(e) { - that._ngl_focused = 0; - e; // linter - that.mouseOverDisplay('none') - }, false); - - container.addEventListener('contextmenu', function(e) { - e.stopPropagation(); - e.preventDefault(); - }, true); - - this.stage.signals.componentAdded.add(function(component) { - this.comp_uuids.push(component.uuid) - var len = this.stage.compList.length; - this.model.set("n_components", len); - this.touch(); - var comp = this.stage.compList[len - 1]; - comp.signals.representationRemoved.add(function() { - that.request_repr_dict(); - }); - comp.signals.representationAdded.add(function(repr) { - that.request_repr_dict(); - repr.signals.parametersChanged.add(function(){ - console.log("repr.parametersChanged") - that.request_repr_dict(); - }) - }); - }, this); - - this.stage.signals.componentRemoved.add(async function(component) { - var that = this - var cindex = this.comp_uuids.indexOf(component.uuid) - this.comp_uuids.splice(cindex, 1) - var n_components = this.stage.compList.length - this.model.set("n_components", n_components) - this.touch() - console.log('componentRemoved', component, component.uuid) - - var pviews = [] - for (var k in this.model.views){ - pviews.push(this.model.views[k]) - } - - var views = await Promise.all(pviews) - console.log(views) - var update_backend = false - for (var k in views){ - var view = views[k] - if ((view.uuid != that.uuid) && (view.stage.compList.length > n_components)){ - // remove component from NGL's GUI - // pass - view.stage.removeComponent(view.stage.compList[cindex]) - update_backend = true - } - } - if (update_backend){ - console.log("should update backend") - that.send({"type": "removeComponent", "data": cindex}) - } - }, this); - - this.stage.signals.parametersChanged.add(function(){ - this.requestUpdateStageParameters(); - }, this); - - this.stage.viewerControls.signals.changed.add(function() { - this.serialize_camera_orientation(); - var m = this.stage.viewerControls.getOrientation(); - if (that._synced_model_ids.length > 0 && that._ngl_focused == 1){ - that._synced_model_ids.forEach(async function(mid){ - var model = await that.model.widget_manager.get_model(mid) - for (var k in model.views){ - var pview = model.views[k]; - var view = await model.views[k] - if (view.uuid != that.uuid){ - view.stage.viewerControls.orient(m); - } - } - }) - } - }.bind(this)); - - } - - handlePicking(){ - this.$pickingInfo = $("
") - .css("position", "absolute") - .css("top", "5%") - .css("left", "3%") - .css("background-color", "white") - .css("padding", "2px 5px 2px 5px") - .css("opacity", "0.7") - .appendTo(this.$container); - - var that = this; - this.stage.signals.clicked.add(function (pd) { - if (pd) { - this.model.set('picked', {}); //refresh signal - this.touch(); - - var pd2 = {} as any; - var pickingText = ""; - if (pd.atom) { - pd2.atom1 = pd.atom.toObject(); - pd2.atom1.name = pd.atom.qualifiedName(); - pickingText = "Atom: " + pd2.atom1.name; - } else if (pd.bond) { - pd2.bond = pd.bond.toObject(); - pd2.atom1 = pd.bond.atom1.toObject(); - pd2.atom1.name = pd.bond.atom1.qualifiedName(); - pd2.atom2 = pd.bond.atom2.toObject(); - pd2.atom2.name = pd.bond.atom2.qualifiedName(); - pickingText = "Bond: " + pd2.atom1.name + " - " + pd2.atom2.name; - } - if (pd.instance) pd2.instance = pd.instance; - - var n_components = this.stage.compList.length; - for (var i = 0; i < n_components; i++) { - var comp = this.stage.compList[i]; - if (comp.uuid == pd.component.uuid) { - pd2.component = i; - } - } - - this.model.set('picked', pd2); - this.touch(); - - this.$pickingInfo.text(pickingText); - } - }, this); - } - - async mouseOverDisplay(type){ + finalizeDisplay() { + // for callbacks from Python + // must be after initializing NGL.Stage + this.send({ + 'type': 'request_loaded', + 'data': true + }) + var state_params = this.stage.getParameters(); + this.model.set('_ngl_original_stage_parameters', state_params); + this.touch(); + if (!this.isEmbeded() && this.stage.compList.length < this.model.get("n_components")) { + // only call this in notebook to avoid calling handleEmbed twice in embeded mode. + this.handleEmbed() + } + var ngl_view_ids = this.model.get("_ngl_view_id") + ngl_view_ids.push(this.ngl_view_id) + this.send({ "type": "updateIDs", "data": ngl_view_ids }) + + // FIXME: Why below doesn't update _ngl_view_id in backend? + // this.model.set("_ngl_view_id", ngl_view_ids) + // this.touch() + } + + handleSignals() { + var container = this.stage.viewer.container; + var that = this; + container.addEventListener('mouseover', function (e) { + that._ngl_focused = 1; + e; // linter + that.mouseOverDisplay('block') + }, false); + + container.addEventListener('mouseout', function (e) { + that._ngl_focused = 0; + e; // linter + that.mouseOverDisplay('none') + }, false); + + container.addEventListener('contextmenu', function (e) { + e.stopPropagation(); + e.preventDefault(); + }, true); + + this.stage.signals.componentAdded.add(function (component) { + this.comp_uuids.push(component.uuid) + var len = this.stage.compList.length; + this.model.set("n_components", len); + this.touch(); + var comp = this.stage.compList[len - 1]; + comp.signals.representationRemoved.add(function () { + that.request_repr_dict(); + }); + comp.signals.representationAdded.add(function (repr) { + that.request_repr_dict(); + repr.signals.parametersChanged.add(function () { + console.log("repr.parametersChanged") + that.request_repr_dict(); + }) + }); + }, this); + + this.stage.signals.componentRemoved.add(async function (component) { + var that = this + var cindex = this.comp_uuids.indexOf(component.uuid) + this.comp_uuids.splice(cindex, 1) + var n_components = this.stage.compList.length + this.model.set("n_components", n_components) + this.touch() + console.log('componentRemoved', component, component.uuid) + + var pviews = [] + for (var k in this.model.views) { + pviews.push(this.model.views[k]) + } + + var views = await Promise.all(pviews) + console.log(views) + var update_backend = false + for (var k in views) { + var view = views[k] + if ((view.uuid != that.uuid) && (view.stage.compList.length > n_components)) { + // remove component from NGL's GUI + // pass + view.stage.removeComponent(view.stage.compList[cindex]) + update_backend = true + } + } + if (update_backend) { + console.log("should update backend") + that.send({ "type": "removeComponent", "data": cindex }) + } + }, this); + + this.stage.signals.parametersChanged.add(function () { + this.requestUpdateStageParameters(); + }, this); + + this.stage.viewerControls.signals.changed.add(function () { + this.serialize_camera_orientation(); + var m = this.stage.viewerControls.getOrientation(); + if (that._synced_model_ids.length > 0 && that._ngl_focused == 1) { + that._synced_model_ids.forEach(async function (mid) { + var model = await that.model.widget_manager.get_model(mid) + for (var k in model.views) { + var pview = model.views[k]; + var view = await model.views[k] + if (view.uuid != that.uuid) { + view.stage.viewerControls.orient(m); + } + } + }) + } + }.bind(this)); + + } + + handlePicking() { + this.$pickingInfo = $("
") + .css("position", "absolute") + .css("top", "5%") + .css("left", "3%") + .css("background-color", "white") + .css("padding", "2px 5px 2px 5px") + .css("opacity", "0.7") + .appendTo(this.$container); + + var that = this; + this.stage.signals.clicked.add(function (pd) { + if (pd) { + this.model.set('picked', {}); //refresh signal + this.touch(); + + var pd2 = {} as any; + var pickingText = ""; + if (pd.atom) { + pd2.atom1 = pd.atom.toObject(); + pd2.atom1.name = pd.atom.qualifiedName(); + pickingText = "Atom: " + pd2.atom1.name; + } else if (pd.bond) { + pd2.bond = pd.bond.toObject(); + pd2.atom1 = pd.bond.atom1.toObject(); + pd2.atom1.name = pd.bond.atom1.qualifiedName(); + pd2.atom2 = pd.bond.atom2.toObject(); + pd2.atom2.name = pd.bond.atom2.qualifiedName(); + pickingText = "Bond: " + pd2.atom1.name + " - " + pd2.atom2.name; + } + if (pd.instance) pd2.instance = pd.instance; + + var n_components = this.stage.compList.length; + for (var i = 0; i < n_components; i++) { + var comp = this.stage.compList[i]; + if (comp.uuid == pd.component.uuid) { + pd2.component = i; + } + } + + this.model.set('picked', pd2); + this.touch(); + + this.$pickingInfo.text(pickingText); + } + }, this); + } + + async mouseOverDisplay(type) { var that = this; - if (this.btn_pview_fullscreen){ + if (this.btn_pview_fullscreen) { var btn = await this.btn_pview_fullscreen btn.el.style.display = type - if (that.stage_widget){ + if (that.stage_widget) { // If NGL's GUI exists, use its fullscreen button. btn.el.style.display = 'none' } } var that = this; - if (this.player_pview){ + if (this.player_pview) { var v = await this.player_pview - v.el.style.display = type - // Need to check if max_frame is available (otherwise NaN) - // https://github.com/jupyter-widgets/ipywidgets/issues/2485 - if (!that.model.get("max_frame") || (that.model.get("max_frame") == 0)){ - // always hide if there's no trajectory. - v.el.style.display = 'none' + v.el.style.display = type + // Need to check if max_frame is available (otherwise NaN) + // https://github.com/jupyter-widgets/ipywidgets/issues/2485 + if (!that.model.get("max_frame") || (that.model.get("max_frame") == 0)) { + // always hide if there's no trajectory. + v.el.style.display = 'none' } } } - updateNGLTheme(css_content){ + updateNGLTheme(css_content) { var ele = document.getElementById("nglview_style") - if (ele != undefined){ + if (ele != undefined) { document.head.removeChild(ele) } var style = document.createElement("style") @@ -367,38 +367,38 @@ class NGLView extends widgets.DOMWidgetView{ document.head.appendChild(style) } - serialize_camera_orientation(){ - var m = this.stage.viewerControls.getOrientation(); - this.model.set('_camera_orientation', m.elements); - this.touch(); + serialize_camera_orientation() { + var m = this.stage.viewerControls.getOrientation(); + this.model.set('_camera_orientation', m.elements); + this.touch(); } - set_camera_orientation(orientation){ - if (orientation.length > 0){ + set_camera_orientation(orientation) { + if (orientation.length > 0) { this.stage.viewerControls.orient(orientation); this.serialize_camera_orientation(); } } - executeCode(code){ + executeCode(code) { eval(code); } - _handleEmbedBeforeStage(){ + _handleEmbedBeforeStage() { // Only need to reconstruct colors in embeding mode (outside notebook) // FIXME: remove this function var that = this var ngl_color_dict = that.model.get("_ngl_color_dict"); var label - // Old API (_ColorScheme) - for (label in ngl_color_dict){ - if (!NGL.ColormakerRegistry.hasScheme(label)){ + // Old API (_ColorScheme) + for (label in ngl_color_dict) { + if (!NGL.ColormakerRegistry.hasScheme(label)) { that.addColorScheme(ngl_color_dict[label], label); } } } - async handleEmbed(){ + async handleEmbed() { var that = this; var ngl_msg_archive = that.model.get("_ngl_msg_archive"); var ngl_stage_params = that.model.get('_ngl_full_stage_parameters'); @@ -417,13 +417,13 @@ class NGLView extends widgets.DOMWidgetView{ var loadfile_list = []; - _.each(ngl_msg_archive, function(msg: any){ - if (msg.methodName == 'loadFile'){ + _.each(ngl_msg_archive, function (msg: any) { + if (msg.methodName == 'loadFile') { if (msg.kwargs && msg.kwargs.defaultRepresentation) { // no need to add default representation as all representations // are serialized separately, also it unwantedly sets the orientation msg.kwargs.defaultRepresentation = false - } + } loadfile_list.push(that._getLoadFilePromise(msg)); } }); @@ -435,23 +435,24 @@ class NGLView extends widgets.DOMWidgetView{ that.touch(); // Outside notebook - if (that.model.comm === undefined){ + if (that.model.comm === undefined) { var ngl_coordinate_resource = that.model.get("_ngl_coordinate_resource"); var n_frames = ngl_coordinate_resource['n_frames'] || 1 - that.model.set("max_frame", n_frames-1); // trigger updating slider and player's max + that.model.set("max_frame", n_frames - 1); // trigger updating slider and player's max that.touch() var model = await that.getPlayerModel() var pmodel = model.get("children")[0]; that.listenTo(pmodel, - "change:value", function(){ + "change:value", function () { that.updateCoordinatesFromDict(ngl_coordinate_resource, - pmodel.get("value"))}) + pmodel.get("value")) + }) } // fire any msg with "fire_embed" for (const msg of that.model.get("_ngl_msg_archive")) { - if (msg.fire_embed){ + if (msg.fire_embed) { await that.on_msg(msg); } } @@ -464,7 +465,7 @@ class NGLView extends widgets.DOMWidgetView{ that.handleResize() // FIXME: really need this? } - updateCoordinatesFromDict(cdict, frame_index){ + updateCoordinatesFromDict(cdict, frame_index) { // update coordinates for given "index" // cdict = Dict[int, List[base64]] var keys = Object.keys(cdict).filter(k => (k !== 'n_frames')); @@ -485,7 +486,7 @@ class NGLView extends widgets.DOMWidgetView{ }); } - requestUpdateStageParameters(){ + requestUpdateStageParameters() { var updated_params = this.stage.getParameters(); this.model.set('_ngl_full_stage_parameters', updated_params); this.touch(); @@ -505,7 +506,7 @@ class NGLView extends widgets.DOMWidgetView{ } } - request_repr_dict(){ + request_repr_dict() { var repr_dict = this.getReprDictFrontEnd() this.send({ // make sure we are using "request_repr_dict" name @@ -514,21 +515,21 @@ class NGLView extends widgets.DOMWidgetView{ 'data': repr_dict, }); var that = this - if (that._synced_repr_model_ids.length > 0){ - that._synced_repr_model_ids.forEach(async function(mid){ + if (that._synced_repr_model_ids.length > 0) { + that._synced_repr_model_ids.forEach(async function (mid) { var model = await that.model.widget_manager.get_model(mid) - for (var k in model.views){ - var view = await model.views[k]; - // not sync with itself - if (view.uuid != that.uuid){ - view._set_representation_from_repr_dict(repr_dict) - } + for (var k in model.views) { + var view = await model.views[k]; + // not sync with itself + if (view.uuid != that.uuid) { + view._set_representation_from_repr_dict(repr_dict) } + } }) } } - getReprDictFrontEnd(){ + getReprDictFrontEnd() { var repr_dict = {}; var n_components = this.stage.compList.length; for (var i = 0; i < n_components; i++) { @@ -545,57 +546,57 @@ class NGLView extends widgets.DOMWidgetView{ return repr_dict } - syncReprForAllViews(){ + syncReprForAllViews() { var repr_dict_backend = this.model.get("_ngl_repr_dict") var repr_dict_frontend = this.getReprDictFrontEnd() - if (JSON.stringify(repr_dict_frontend) !== JSON.stringify(repr_dict_backend)){ + if (JSON.stringify(repr_dict_frontend) !== JSON.stringify(repr_dict_backend)) { this._set_representation_from_repr_dict(repr_dict_backend) } } - async syncReprWithMe(){ + async syncReprWithMe() { // Make sure views of the same model has the same representations // Only needed if we use Sidebar that connects to specific view. var that = this var repr_dict = this.getReprDictFrontEnd() - for (var k in this.model.views){ + for (var k in this.model.views) { var v = await this.model.views[k] - if (v.uuid != that.uuid){ + if (v.uuid != that.uuid) { v._set_representation_from_repr_dict(repr_dict) } } this.request_repr_dict() } - setSyncRepr(model_ids){ + setSyncRepr(model_ids) { this._synced_repr_model_ids = model_ids } - setSyncCamera(model_ids){ + setSyncCamera(model_ids) { this._synced_model_ids = model_ids } - viewXZPlane(){ - var m = new NGL.Matrix4().makeRotationX( Math.PI / 2 ); - var q = new NGL.Quaternion().setFromRotationMatrix( m ); - this.stage.viewerControls.rotate( q ); + viewXZPlane() { + var m = new NGL.Matrix4().makeRotationX(Math.PI / 2); + var q = new NGL.Quaternion().setFromRotationMatrix(m); + this.stage.viewerControls.rotate(q); } - set_representation_from_backend(){ + set_representation_from_backend() { var repr_dict = this.model.get('_ngl_repr_dict') this._set_representation_from_repr_dict(repr_dict) } - _set_representation_from_repr_dict(repr_dict){ + _set_representation_from_repr_dict(repr_dict) { var compList = this.stage.compList - if (compList.length > 0){ - for (var index in repr_dict){ + if (compList.length > 0) { + for (var index in repr_dict) { var comp = compList[index]; comp.removeAllRepresentations(); var reprlist = repr_dict[index]; - for (var j in reprlist){ + for (var j in reprlist) { var repr = reprlist[j]; - if (repr){ + if (repr) { comp.addRepresentation(repr.type, repr.params); } } @@ -603,7 +604,7 @@ class NGLView extends widgets.DOMWidgetView{ } } - async createView(trait_name){ + async createView(trait_name) { // Create a view for the model with given `trait_name` // e.g: in backend, 'view.` console.log("Creating view for model " + trait_name); @@ -612,13 +613,13 @@ class NGLView extends widgets.DOMWidgetView{ return await manager.create_view(await manager.get_model(model_id)) } - getPlayerModel(){ + getPlayerModel() { // return a Promise var model_id = this.model.get("_iplayer").replace("IPY_MODEL_", ""); return this.model.widget_manager.get_model(model_id) } - async createIPlayer(){ + async createIPlayer() { this.player_pview = this.createView("_iplayer"); var view = await this.player_pview var that = this; @@ -632,7 +633,7 @@ class NGLView extends widgets.DOMWidgetView{ pe.style.display = 'none' } - async createImageBtn(){ + async createImageBtn() { this.image_btn_pview = this.createView("_ibtn_image"); var view = await this.image_btn_pview var pe = view.el @@ -645,7 +646,7 @@ class NGLView extends widgets.DOMWidgetView{ this.stage.viewer.container.append(view.el); } - async createFullscreenBtn(){ + async createFullscreenBtn() { this.btn_pview_fullscreen = this.createView("_ibtn_fullscreen"); var view = await this.btn_pview_fullscreen var stage = this.stage; @@ -660,21 +661,21 @@ class NGLView extends widgets.DOMWidgetView{ pe.style.background = 'white' pe.style.opacity = '0.3' pe.style.display = 'none' - pe.onclick = function(){ + pe.onclick = function () { this.stage.toggleFullscreen(); }.bind(this) stage.viewer.container.append(view.el); stage.signals.fullscreenChanged.add(function (isFullscreen) { - if (isFullscreen) { - view.model.set("icon", "compress") - } else { - view.model.set("icon", "expand") - } + if (isFullscreen) { + view.model.set("icon", "compress") + } else { + view.model.set("icon", "expand") + } }) } - async createGUI(){ + async createGUI() { this.pgui_view = this.createView("_igui"); var view = await this.pgui_view var pe = view.el @@ -687,8 +688,8 @@ class NGLView extends widgets.DOMWidgetView{ } - createNglGUI(){ - this.stage_widget = new StageWidget(this) + createNglGUI() { + this.stage_widget = new StageWidget(this) } @@ -715,7 +716,7 @@ class NGLView extends widgets.DOMWidgetView{ var component = this.stage.compList[component_index]; if (component) { - component.reprList.forEach(function(repr) { + component.reprList.forEach(function (repr) { if (repr.name == repr_name) { component.removeRepresentation(repr); } @@ -737,7 +738,7 @@ class NGLView extends widgets.DOMWidgetView{ var that = this; if (component) { - component.reprList.forEach(function(repr) { + component.reprList.forEach(function (repr) { if (repr.name == repr_name) { repr.setParameters(params); that.request_repr_dict(); @@ -764,12 +765,12 @@ class NGLView extends widgets.DOMWidgetView{ } } - setColorByResidue(colors, component_index, repr_index){ + setColorByResidue(colors, component_index, repr_index) { var repr = this.stage.compList[component_index].reprList[repr_index]; - var schemeId = NGL.ColormakerRegistry.addScheme(function(params){ - this.atomColor = function(atom){ - var color = colors[atom.residueIndex]; - return color + var schemeId = NGL.ColormakerRegistry.addScheme(function (params) { + this.atomColor = function (atom) { + var color = colors[atom.residueIndex]; + return color }; params; // to pass eslint; ack; }); @@ -807,7 +808,7 @@ class NGLView extends widgets.DOMWidgetView{ shapeComp.addRepresentation("buffer"); } - addBuffer(name, kwargs){ + addBuffer(name, kwargs) { var class_dict = { "arrow": NGL.ArrowBuffer, "box": NGL.BoXbuffer, @@ -822,7 +823,7 @@ class NGLView extends widgets.DOMWidgetView{ }; var params = {}; - for (var key in kwargs){ + for (var key in kwargs) { params[key] = new Float32Array(kwargs[key]); } var shape = new NGL.Shape("shape"); @@ -833,26 +834,26 @@ class NGLView extends widgets.DOMWidgetView{ shapeComp.addRepresentation("buffer"); } - async replaceStructure(structure){ - var blob = new Blob([structure.data], {type: "text/plain"}); - var stage = this.stage - var params = structure.params || {}; - params.ext = structure.ext; - params.defaultRepresentation = false; - var comp = this.stage.compList[0]; - var representations = comp.reprList.slice(); - var old_orientation = this.stage.viewerControls.getOrientation(); - var component = await this.stage.loadFile(blob, params) - stage.viewerControls.orient(old_orientation); - representations.forEach(function(repr) { - var repr_name = repr.name; - var repr_params = repr.repr.getParameters(); - // Note: not using repr.repr.type, repr.repr.params - // since seems to me that repr.repr.params won't return correct "sele" - component.addRepresentation(repr_name, repr_params); - }); - stage.removeComponent(comp); - this._handleLoadFileFinished(); + async replaceStructure(structure) { + var blob = new Blob([structure.data], { type: "text/plain" }); + var stage = this.stage + var params = structure.params || {}; + params.ext = structure.ext; + params.defaultRepresentation = false; + var comp = this.stage.compList[0]; + var representations = comp.reprList.slice(); + var old_orientation = this.stage.viewerControls.getOrientation(); + var component = await this.stage.loadFile(blob, params) + stage.viewerControls.orient(old_orientation); + representations.forEach(function (repr) { + var repr_name = repr.name; + var repr_params = repr.repr.getParameters(); + // Note: not using repr.repr.type, repr.repr.params + // since seems to me that repr.repr.params won't return correct "sele" + component.addRepresentation(repr_name, repr_params); + }); + stage.removeComponent(comp); + this._handleLoadFileFinished(); } superpose(cindex0, cindex1, align, sele0, sele1) { @@ -917,16 +918,16 @@ class NGLView extends widgets.DOMWidgetView{ handleResizable() { this.$container.resizable({ - resize: function(event, ui) { + resize: function (event, ui) { this.setSize(ui.size.width + "px", ui.size.height + "px"); }.bind(this) }) } - handleResize(){ + handleResize() { var width = this.$el.width() var height = this.$el.height() + "px" - if (this.stage_widget){ + if (this.stage_widget) { width = width - $(this.stage_widget.sidebar.dom).width() } width = width + "px" @@ -939,12 +940,12 @@ class NGLView extends widgets.DOMWidgetView{ this.stage.handleResize(); } - GUIStyleChanged(){ + GUIStyleChanged() { var style = this.model.get("gui_style"); - if (style === 'ngl'){ + if (style === 'ngl') { this.createNglGUI(); - }else{ - if (this.stage_widget){ + } else { + if (this.stage_widget) { this.stage_widget.dispose() this.stage_widget = undefined this.$container.resizable("enable") @@ -974,47 +975,48 @@ class NGLView extends widgets.DOMWidgetView{ } async _downloadImage(filename, params) { - if (this.ngl_view_id == this.get_last_child_id()){ + if (this.ngl_view_id == this.get_last_child_id()) { var blob = await this.stage.makeImage(params) NGL.download(blob, filename); } } async _exportImage(wid, params) { - if (this.ngl_view_id == this.get_last_child_id()){ + if (this.ngl_view_id == this.get_last_child_id()) { var blob = await this.stage.makeImage(params) - var reader = new FileReader(); - var arr_str; - reader.onload = function() { - arr_str = (reader.result as string).replace("data:image/png;base64,", ""); - // this.model.set("_image_data", arr_str); - // this.touch(); - this.send({ - "data": arr_str, - "type": "image_data", - "ID": wid, - }); - this.send({'type': 'async_message', 'data': 'ok'}); - }.bind(this); - reader.readAsDataURL(blob); - }} + var reader = new FileReader(); + var arr_str; + reader.onload = function () { + arr_str = (reader.result as string).replace("data:image/png;base64,", ""); + // this.model.set("_image_data", arr_str); + // this.touch(); + this.send({ + "data": arr_str, + "type": "image_data", + "ID": wid, + }); + this.send({ 'type': 'async_message', 'data': 'ok' }); + }.bind(this); + reader.readAsDataURL(blob); + } + } async handleMovieMaking(render_params) { console.log('handleMovieMaking: render_params', render_params) - if (this.ngl_view_id == this.get_last_child_id()){ + if (this.ngl_view_id == this.get_last_child_id()) { var blob = await this.stage.makeImage(render_params) var reader = new FileReader(); var arr_str; - reader.onload = function() { + reader.onload = function () { arr_str = (reader.result as string).replace("data:image/png;base64,", ""); // this.model.set("_image_data", arr_str); // this.touch(); this.send({ "data": arr_str, "type": "movie_image_data", - }); // tell backend that image render is finished, - // backend will send next frame's coordinates. - this.send({'type': 'async_message', 'data': 'ok'}); + }); // tell backend that image render is finished, + // backend will send next frame's coordinates. + this.send({ 'type': 'async_message', 'data': 'ok' }); }.bind(this); reader.readAsDataURL(blob); } @@ -1022,71 +1024,71 @@ class NGLView extends widgets.DOMWidgetView{ _handleLoadFileFinished() { - this.send({'type': 'async_message', 'data': 'ok'}); - } - - _getLoadFilePromise(msg){ - // args = [{'type': ..., 'data': ...}] - var args0 = msg.args[0]; - if (args0.type == 'blob') { - var blob; - if (args0.binary) { - var decoded_data = this.decode_base64(args0.data); - blob = new Blob([decoded_data], { - type: "application/octet-binary" - }); - } else { - blob = new Blob([args0.data], { - type: "text/plain" - }); - } - return this.stage.loadFile(blob, msg.kwargs) - } else { - var file = new File([""], args0.data); - // FIXME: if not "any", typescipt complains there is no - // "exists" method. - var path = ""; - // FIXME: uncomment ("exists" does not exist) - // if (args0.type == 'path' && ! file.exists){ - // hacky fix for jupyterlab - // https://github.com/arose/nglview/issues/783 - // https://github.com/jupyterlab/jupyterlab/issues/6218 - // e.g: http://localhost:8888/files/ala3.pdb - // path = 'files/' + args0.data; - // } else { - // path = args0.data; - //} - path = args0.data; - return this.stage.loadFile(path, msg.kwargs) - } - } - - get_last_child_id(){ + this.send({ 'type': 'async_message', 'data': 'ok' }); + } + + _getLoadFilePromise(msg) { + // args = [{'type': ..., 'data': ...}] + var args0 = msg.args[0]; + if (args0.type == 'blob') { + var blob; + if (args0.binary) { + var decoded_data = this.decode_base64(args0.data); + blob = new Blob([decoded_data], { + type: "application/octet-binary" + }); + } else { + blob = new Blob([args0.data], { + type: "text/plain" + }); + } + return this.stage.loadFile(blob, msg.kwargs) + } else { + var file = new File([""], args0.data); + // FIXME: if not "any", typescipt complains there is no + // "exists" method. + var path = ""; + // FIXME: uncomment ("exists" does not exist) + // if (args0.type == 'path' && ! file.exists){ + // hacky fix for jupyterlab + // https://github.com/arose/nglview/issues/783 + // https://github.com/jupyterlab/jupyterlab/issues/6218 + // e.g: http://localhost:8888/files/ala3.pdb + // path = 'files/' + args0.data; + // } else { + // path = args0.data; + //} + path = args0.data; + return this.stage.loadFile(path, msg.kwargs) + } + } + + get_last_child_id() { var keys = this.model.get('_ngl_view_id') - return keys[keys.length-1] + return keys[keys.length - 1] } - async _handleStageLoadFile(msg){ + async _handleStageLoadFile(msg) { // args = [{'type': ..., 'data': ...}] - if (this.ngl_view_id != this.get_last_child_id() && msg.last_child){ + if (this.ngl_view_id != this.get_last_child_id() && msg.last_child) { return } var o = await this._getLoadFilePromise(msg) this._handleLoadFileFinished(); } - addColorScheme(args, label){ + addColorScheme(args, label) { var id = NGL.ColormakerRegistry.addSelectionScheme(args, label); var scheme = NGL.ColormakerRegistry.userSchemes[id]; NGL.ColormakerRegistry.removeScheme(id); // hard code the scheme ID NGL.ColormakerRegistry.add(label, scheme); return label - } + } async on_msg(msg) { // TODO: re-organize - if (('ngl_view_id' in msg) && (msg.ngl_view_id !== this.ngl_view_id)){ + if (('ngl_view_id' in msg) && (msg.ngl_view_id !== this.ngl_view_id)) { return } if (msg.type == 'call_method') { @@ -1096,10 +1098,10 @@ class NGLView extends widgets.DOMWidgetView{ // handle color if (msg.methodName == 'addRepresentation' && - msg.reconstruc_color_scheme){ + msg.reconstruc_color_scheme) { msg.kwargs.color = this.addColorScheme(msg.kwargs.color, msg.kwargs.color_label); } - if ("colorVolume" in msg.kwargs){ + if ("colorVolume" in msg.kwargs) { // backend only send component index // so we need to convert to 'volume' data index = msg.kwargs["colorVolume"]; @@ -1189,7 +1191,7 @@ class NGLView extends widgets.DOMWidgetView{ this.updateCoordinates(coordinates, traj_index); } } - if (msg.movie_making){ + if (msg.movie_making) { this.handleMovieMaking(msg.render_params) } } else if (msg.type == 'get') {