UNPKG

3dmol

Version:

JavaScript/TypeScript molecular visualization library

285 lines (254 loc) 11.9 kB
//auto-initialization import { GLViewer, createViewer } from "./GLViewer"; import { SurfaceType } from "./ProteinSurface4"; import { get, getbin, makeFunction, specStringToObject } from "./utilities"; import { CC } from "./colors"; export var autoinit = false; export var processing_autoinit = false; /** * Contains a dictionary of embedded viewers created from HTML elements * with a the viewer_3Dmoljs css class indexed by their id (or numerically * if they do not have an id). */ export var viewers: any = {}; //Create embedded viewer from HTML attributes if true //viewer and callback are used by the testing harness export function autoload(viewer?: any, callback?: (arg0: any) => void) { var i: string | number, dataname: string, type: string; if (document.querySelector(".viewer_3Dmoljs") != null) autoinit = true; if (autoinit) { processing_autoinit = true; viewer = (viewer != undefined) ? viewer : null; var nviewers = 0; document.querySelectorAll<HTMLInputElement>(".viewer_3Dmoljs").forEach(viewerdiv => { var datauri = []; var datatypes = []; var uri = ''; if (viewerdiv.style.position == 'static') { //slight hack - canvas needs this element to be positioned viewerdiv.style.position = 'relative'; } var UI:any = null; type = null; if (viewerdiv.dataset.pdb) { datauri.push("https://files.rcsb.org/view/" + viewerdiv.dataset.pdb + ".pdb"); datatypes.push("pdb"); } else if (viewerdiv.dataset.cid) { //this doesn't actually work since pubchem does have CORS enabled datatypes.push("sdf"); datauri.push("https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/cid/" + viewerdiv.dataset.cid + "/SDF?record_type=3d"); } else if (viewerdiv.dataset.href || viewerdiv.dataset.url) { if (viewerdiv.dataset.href) uri = viewerdiv.dataset.href; else uri = viewerdiv.dataset.url; datauri.push(uri); type = uri.substring(uri.lastIndexOf('.') + 1); if(type == 'gz') { let pos = uri.substring(0,uri.lastIndexOf('.')).lastIndexOf('.'); type = uri.substring(pos+1); } datatypes.push(type); var molName = uri.substring(uri.lastIndexOf('/') + 1, uri.lastIndexOf('.')); if (molName == '/') molName = uri.substring(uri.lastIndexOf('/') + 1); viewerdiv.dataset[datatypes[datatypes.length - 1]] = molName; } let divdata = viewerdiv.dataset; for (i in divdata) { if ((i.substring(0, 3) === "pdb" && (i !== "pdb"))) { datauri.push("https://files.rcsb.org/view/" + divdata[i] + ".pdb"); datatypes.push('pdb'); } else if (i.substring(0, 4) === "href" && (i !== "href")) { uri = divdata[i]; datauri.push(uri); datatypes.push(uri.substring(uri.lastIndexOf('.') + 1)); } else if (i.substring(0, 3) === "cid" && (i !== "cid")) { datauri.push("https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/cid/" + divdata[i] + "/SDF?record_type=3d"); datatypes.push('sdf'); } } var options = {}; if (viewerdiv.dataset.options) options = specStringToObject(viewerdiv.dataset.options); //note that data tags must be lowercase var bgcolor = CC.color(viewerdiv.dataset.backgroundcolor); var bgalpha: string | number = viewerdiv.dataset.backgroundalpha; bgalpha = (bgalpha == undefined) ? 1.0 : parseFloat(bgalpha); var style = { line: {} }; if (viewerdiv.dataset.style) style = specStringToObject(viewerdiv.dataset.style); var select = {}; if (viewerdiv.dataset.select) select = specStringToObject(viewerdiv.dataset.select); var selectstylelist = []; var surfaces = []; var labels = []; var zoomto = {}; var spin = null; var d = viewerdiv.dataset; //let users specify individual but matching select/style/surface tags, eg. //data-select1 data-style1 var stylere = /style(.+)/; var surfre = /surface(.*)/; var reslabre = /labelres(.*)/; var keys = []; for (dataname in d) { if (Object.prototype.hasOwnProperty.call(d, dataname)) { keys.push(dataname); } } keys.sort(); for (i = 0; i < keys.length; i++) { dataname = keys[i]; var m = stylere.exec(dataname); var selname: string, newsel: any, styleobj: any; if (m) { selname = "select" + m[1]; newsel = specStringToObject(d[selname]); styleobj = specStringToObject(d[dataname]); selectstylelist.push([newsel, styleobj]); } m = surfre.exec(dataname); if (m) { selname = "select" + m[1]; newsel = specStringToObject(d[selname]); styleobj = specStringToObject(d[dataname]); surfaces.push([newsel, styleobj]); } m = reslabre.exec(dataname); if (m) { selname = "select" + m[1]; newsel = specStringToObject(d[selname]); styleobj = specStringToObject(d[dataname]); labels.push([newsel, styleobj]); } if (dataname == "zoomto") { zoomto = specStringToObject(d[dataname]); } if (dataname == "spin") { spin = specStringToObject(d[dataname]); } } //apply all the selections/styles parsed out above to the passed viewer var applyStyles = function (glviewer: GLViewer) { glviewer.setStyle(select, style); if (UI) { UI.createSelectionAndStyle(select, style); } for (i = 0; i < selectstylelist.length; i++) { let sel = selectstylelist[i][0] || {}; let sty = selectstylelist[i][1] || { "line": {} }; glviewer.setStyle(sel, sty); if (UI) { UI.createSelectionAndStyle(select, style); } } for (i = 0; i < surfaces.length; i++) { let sel = surfaces[i][0] || {}; let sty = surfaces[i][1] || {}; let viewer = glviewer; if (UI) { viewer.addSurface(SurfaceType.VDW, sty, sel, sel).then((surfid: any) => { UI.loadSurface("VDW", sel, sty, surfid); }); } else { glviewer.addSurface(SurfaceType.VDW, sty, sel, sel); } } for (i = 0; i < labels.length; i++) { let sel = labels[i][0] || {}; let sty = labels[i][1] || {}; glviewer.addResLabels(sel, sty); } glviewer.render(); glviewer.zoomTo(zoomto); if (spin) { glviewer.spin(spin.axis, spin.speed); } }; let glviewer = viewer; try { var config: any = specStringToObject(viewerdiv.dataset.config) || {}; if (config.backgroundColor === undefined) config.backgroundColor = bgcolor; if (config.backgroundAlpha === undefined) config.backgroundAlpha = bgalpha; if (glviewer == null) { glviewer = viewers[viewerdiv.id || nviewers++] = createViewer(viewerdiv, config); } else { glviewer.setBackgroundColor(bgcolor, bgalpha); glviewer.setConfig(config); if (UI) UI.initiateUI(); } if(viewerdiv.dataset.ui && $3Dmol.StateManager) { UI = new $3Dmol.StateManager(glviewer); // Creates the UI state management tool } } catch (error) { console.log(error); //for autoload, provide a useful error message viewerdiv.textContent = "WebGL appears to be disabled."; } if (datauri.length != 0) { //load multiple data elements in serial let i = 0; let process = ((viewerdiv, glviewer) => function (moldata: any) { //add moldata to viewer and load next model uri = datauri[i]; //this is where the moldata came from var type = viewerdiv.dataset.type || viewerdiv.dataset.datatype || datatypes[i]; glviewer.addModel(moldata, type, options); if (UI) { var modelName = viewerdiv.dataset[datatypes[i]]; UI.setModelTitle(modelName); } i +=1; if (i < datauri.length) { get(datauri[i]).then(process); } else { // or finalize if this is the last model applyStyles(glviewer); if (viewerdiv.dataset.callback) { //evaluate javascript in the string, if it resolves to a function, //call it with the viewer let runres = makeFunction(viewerdiv.dataset.callback); runres(glviewer); } processing_autoinit = false; if (callback) callback(glviewer); } })(viewerdiv,glviewer); if(type && type.endsWith('gz')) { getbin(datauri[0]).then(process); } else { get(datauri[0]).then(process); } } else { if (viewerdiv.dataset.element) { var moldataid = "#" + viewerdiv.dataset.element; var molelem = document.querySelector(moldataid); var moldata = molelem ? molelem.textContent : ""; type = viewerdiv.dataset.type || viewerdiv.dataset.datatype; glviewer.addModel(moldata, type, options); } applyStyles(glviewer); if (viewerdiv.dataset.callback) { //evaluate javascript in the string, if it resolves to a function, //call it with the viewer let runres = makeFunction(viewerdiv.dataset.callback); runres(glviewer); } processing_autoinit = false; if (callback) callback(glviewer); } }); } }; document.onreadystatechange = () => { if (document.readyState === "complete") { autoload(); } };