UNPKG

pdb-lite-mol

Version:

Web-component implementation of LiteMol 3D structure viewer. LiteMol is a streamlined structure viewer which enables a PDB structure to be explored within a browser rather than requiring pre-installed molecular graphics software. It is a PDB Component dev

1,108 lines (1,081 loc) 100 kB
; (function () { angular.module('pdb.litemol', []) .directive('pdbLiteMol', ['$document', function($document, $http, $q){ return { restrict: 'EAC', scope: { pdbId : '=', loadEdMaps: '@', validationAnnotation: '@', domainAnnotation: '@', hideControls: '@', showLogs: '@', customQuery: '@', customRender: '@', treeMenu: '@', isExpanded: '@', subscribeEvents: '@', sourceUrl: '@', sourceFormat: '@', displayFullMapOnload: '@' }, template: '', link: function (scope, element, attrs) { //Method to create custom events var createNewEvent = function(eventTypeArr){ var eventObj = {}; angular.forEach(eventTypeArr, function(eventType, index){ var event; if (typeof MouseEvent == 'function') { // current standard event = new MouseEvent(eventType, { 'view': window, 'bubbles': true, 'cancelable': true }); } else if (typeof document.createEvent == 'function') { // older standard event = document.createEvent('MouseEvents'); event.initEvent(eventType, true /*bubbles*/, true /*cancelable*/); } else if (typeof document.createEventObject == 'function') { // IE 8- event = document.createEventObject(); } eventObj[eventType] = event; }); return eventObj; } //default events scope.pdbevents = createNewEvent(['PDB.litemol.click','PDB.litemol.mouseover','PDB.litemol.mouseout']); //Set default scope values if(typeof scope.hideControls !== 'undefined' && scope.hideControls == 'true'){ scope.hideControls = true; }else{ scope.hideControls = false; } if(typeof scope.treeMenu !== 'undefined' && scope.treeMenu == 'true'){ scope.treeMenu = true; }else{ scope.treeMenu = false; } if(typeof scope.isExpanded !== 'undefined' && scope.isExpanded == 'true'){ scope.isExpanded = true; }else{ scope.isExpanded = false; } //Set subscribe event to true by default if(typeof scope.subscribeEvents == 'undefined'){ scope.subscribeEvents = 'true'; } //Method for inheritance support var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; //Setting DataSources var Viewer; (function (Viewer) { var DataSources; (function (DataSources) { var Bootstrap = LiteMol.Bootstrap; var Entity = Bootstrap.Entity; DataSources.DownloadMolecule = Entity.Transformer.Molecule.downloadMoleculeSource({ sourceId: 'url-molecule', name: 'URL', description: 'Download a molecule from the specified Url (if the host server supports cross domain requests).', defaultId: '//www.ebi.ac.uk/pdbe/coordinates/1tqn/cartoon', urlTemplate: function (id) { return id; }, isFullUrl: true }); })(DataSources = Viewer.DataSources || (Viewer.DataSources = {})); })(Viewer = LiteMol.Viewer || (LiteMol.Viewer = {})); //Setting PDBe DataSources var Viewer; (function (Viewer) { var PDBe; (function (PDBe) { var Data; (function (Data) { var Bootstrap = LiteMol.Bootstrap; var Entity = Bootstrap.Entity; var Transformer = Bootstrap.Entity.Transformer; var Visualization = Bootstrap.Visualization; // straigtforward Data.DownloadMolecule = Transformer.Molecule.downloadMoleculeSource({ sourceId: 'pdbe-molecule', name: 'PDBe', description: 'Download a molecule from PDBe.', defaultId: '1cbs', specificFormat: LiteMol.Core.Formats.Molecule.SupportedFormats.mmCIF, urlTemplate: function (id) { return ("//www.ebi.ac.uk/pdbe/static/entry/" + id.toLowerCase() + "_updated.cif"); } }); Data.DownloadBinaryCIFFromCoordinateServer = Bootstrap.Tree.Transformer.action({ id: 'molecule-download-bcif-from-coordinate-server', name: 'Molecule (BinaryCIF)', description: 'Download full or cartoon representation of a PDB entry from the CoordinateServer.', from: [Entity.Root], to: [Entity.Action], defaultParams: function (ctx) { return ({ id: '5iv5', type: 'Cartoon', lowPrecisionCoords: true, serverUrl: ctx.settings.get('molecule.downloadBinaryCIFFromCoordinateServer.server') ? ctx.settings.get('molecule.downloadBinaryCIFFromCoordinateServer.server') : 'http://www.ebi.ac.uk/pdbe/coordinates/' }); }, validateParams: function (p) { return (!p.id || !p.id.trim().length) ? ['Enter Id'] : (!p.serverUrl || !p.serverUrl.trim().length) ? ['Enter CoordinateServer base URL'] : void 0; }, }, function (context, a, t) { var query = t.params.type === 'Cartoon' ? 'cartoon' : 'full'; var id = t.params.id.toLowerCase().trim(); var url = "" + t.params.serverUrl + (t.params.serverUrl[t.params.serverUrl.length - 1] === '/' ? '' : '/') + id + "/" + query + "?encoding=bcif&lowPrecisionCoords=" + (t.params.lowPrecisionCoords ? '1' : '2'); return Bootstrap.Tree.Transform.build() .add(a, Entity.Transformer.Data.Download, { url: url, type: 'Binary', id: id }) .then(Entity.Transformer.Molecule.CreateFromData, { format: LiteMol.Core.Formats.Molecule.SupportedFormats.mmBCIF }, { isBinding: true }) .then(Entity.Transformer.Molecule.CreateModel, { modelIndex: 0 }, { isBinding: false }); }); })(Data = PDBe.Data || (PDBe.Data = {})); })(PDBe = Viewer.PDBe || (Viewer.PDBe = {})); })(Viewer = Viewer || (Viewer = {})); var Viewer; (function (Viewer) { var PDBe; (function (PDBe) { var Data; (function (Data) { var Bootstrap = LiteMol.Bootstrap; var Entity = Bootstrap.Entity; var Transformer = Bootstrap.Entity.Transformer; var Tree = Bootstrap.Tree; var Visualization = Bootstrap.Visualization; Data.DensityDownloader = Entity.create({ name: 'PDBe Density Downloader', typeClass: 'Behaviour', shortName: 'DD', description: 'Represents PDBe Density Downloader.' }); var CreateDensityDownloader = LiteMol.Bootstrap.Tree.Transformer.create({ id: 'pdbe-density-download-create', name: 'PDBe Density Downloader', description: 'Create the PDBe Density Downloader.', from: [Entity.Root], to: [Data.DensityDownloader], defaultParams: function () { return ({}); } }, function (context, a, t) { return LiteMol.Bootstrap.Task.create("Density Downloader", 'Background', function (ctx) { /*ctx.update('Parsing...'); ctx.schedule(function () { var data = JSON.parse(a.props.data); var model = data[t.params.id]; var report = Api.createReport(model || {}); ctx.resolve(Validation.Report.create(t, { label: 'Validation Report', behaviour: new Interactivity.Behaviour(context, report), ref: 'validation-visual' })); });*/ ctx.resolve(Data.DensityDownloader.create(t, { ref: 'densityDownloader-transformer' })); }).setReportTime(true); }); Data.DensityDownloaderAction = LiteMol.Bootstrap.Tree.Transformer.action({ id: 'pdbe-density-download-create-action', name: 'PDBe Density Downloader Action', description: 'PDBe Density Downloader Action', from: [Data.DensityDownloader], to: [Entity.Action], defaultParams: function () { return ({}); }//, //customController: function (ctx, t, e) { return new LiteMol.Bootstrap.Components.Transform.Controller(ctx, t, e, false); } }, function (context, a, t) { var action = LiteMol.Bootstrap.Tree.Transform.build() //.add(a, Transformer.Basic.CreateGroup, { label: 'Ligand Group'+fi, description: 'Ligand Group'+fi }, { ref: groupRef }) .add(a, CreateDensityDownloader, { id: 'densityDwnldrAction' }, { isBinding: true, ref: 'densityDownloader-model' }); return action; }, "Density Loader Loaded"); Data.DensitySources = ['electron-density', 'emdb-pdbid', 'emdb-id']; Data.DensitySourceLabels = { 'electron-density': 'Electron Density', 'emdb-pdbid': 'EMDB (from PDB ID)', 'emdb-id': 'EMDB' }; function doElectron(a, t, id) { var action = Bootstrap.Tree.Transform.build(); id = id.trim().toLowerCase(); var groupRef = t.props.ref ? t.props.ref : Bootstrap.Utils.generateUUID(); var group = action.add(a, Transformer.Basic.CreateGroup, { label: id, description: 'Density' }, { ref: groupRef }); var diffRef = Bootstrap.Utils.generateUUID(); var mainRef = Bootstrap.Utils.generateUUID(); var diff = group .then(Transformer.Data.Download, { url: "//www.ebi.ac.uk/pdbe/coordinates/files/" + id + "_diff.ccp4", type: 'Binary', id: id, description: 'Fo-Fc', title: 'Density' }) .then(Transformer.Density.ParseData, { format: LiteMol.Core.Formats.Density.SupportedFormats.CCP4, id: 'Fo-Fc', normalize: false }, { isBinding: true, ref: diffRef }); diff .then(Transformer.Density.CreateVisualBehaviour, { id: 'Fo-Fc(-ve)', isoSigmaMin: -5, isoSigmaMax: 0, minRadius: 0, maxRadius: 10, radius: 5, showFull: typeof scope.displayFullMapOnload !== 'undefined' && scope.displayFullMapOnload == 'true' ? true : false, style: Visualization.Density.Style.create({ isoValue: -3, isoValueType: Bootstrap.Visualization.Density.IsoValueType.Sigma, color: LiteMol.Visualization.Color.fromHex(0xBB3333), isWireframe: t.params.isWireframe, transparency: { alpha: 0.75 } }) }, { ref: 'density-fofc_minus' }); diff .then(Transformer.Density.CreateVisualBehaviour, { id: 'Fo-Fc(+ve)', isoSigmaMin: 0, isoSigmaMax: 5, minRadius: 0, maxRadius: 10, radius: 5, showFull: typeof scope.displayFullMapOnload !== 'undefined' && scope.displayFullMapOnload == 'true' ? true : false, style: Visualization.Density.Style.create({ isoValue: 3, isoValueType: Bootstrap.Visualization.Density.IsoValueType.Sigma, color: LiteMol.Visualization.Color.fromHex(0x33BB33), isWireframe: t.params.isWireframe, transparency: { alpha: 0.75 } }) }, { ref: 'density-fofc_plus' }); var base = group .then(Transformer.Data.Download, { url: "//www.ebi.ac.uk/pdbe/coordinates/files/" + id + ".ccp4", type: 'Binary', id: id, description: '2Fo-Fc', title: 'Density' }) .then(Transformer.Density.ParseData, { format: LiteMol.Core.Formats.Density.SupportedFormats.CCP4, id: '2Fo-Fc', normalize: false }, { isBinding: true, ref: mainRef }) .then(Transformer.Density.CreateVisualBehaviour, { id: '2Fo-Fc', isoSigmaMin: 0, isoSigmaMax: 2, minRadius: 0, maxRadius: 10, radius: 5, showFull: typeof scope.displayFullMapOnload !== 'undefined' && scope.displayFullMapOnload == 'true' ? true : false, style: Visualization.Density.Style.create({ isoValue: 1.5, isoValueType: Bootstrap.Visualization.Density.IsoValueType.Sigma, color: LiteMol.Visualization.Color.fromHex(0x3362B2), isWireframe: t.params.isWireframe, transparency: { alpha: 0.4 } }) }, { ref: 'density-2fofc' }); return { action: action, context: { id: id, refs: [mainRef, diffRef], groupRef: groupRef } }; } function doEmdb(a, t, id, contourLevel, sigmaRange) { var action = Bootstrap.Tree.Transform.build(); var mainRef = Bootstrap.Utils.generateUUID(); var labelId = 'EMD-' + id; action .add(a, Transformer.Data.Download, { url: "//www.ebi.ac.uk/pdbe/static/files/em/maps/emd_" + id + ".map.gz", type: 'Binary', id: labelId, description: 'EMDB Density', responseCompression: Bootstrap.Utils.DataCompressionMethod.Gzip, title: 'Density' }) .then(Transformer.Density.ParseData, { format: LiteMol.Core.Formats.Density.SupportedFormats.CCP4, id: labelId, normalize: false }, { isBinding: true, ref: mainRef }) .then(Transformer.Density.CreateVisualBehaviour, { id: 'Density', isoSigmaMin: sigmaRange.minVal !== void 0 ? sigmaRange.minVal : 1.5, isoSigmaMax: sigmaRange.maxVal !== void 0 ? sigmaRange.maxVal : 5, minRadius: 0, maxRadius: 50, radius: 5, showFull: true, //typeof scope.displayFullMapOnload !== 'undefined' && scope.displayFullMapOnload == 'true' ? true : false, style: Visualization.Density.Style.create({ isoValue: contourLevel !== void 0 ? contourLevel : 1.5, isoValueType: contourLevel !== void 0 ? Bootstrap.Visualization.Density.IsoValueType.Absolute : Bootstrap.Visualization.Density.IsoValueType.Sigma, color: LiteMol.Visualization.Color.fromHex(0x638F8F), isWireframe: t.params.isWireframe, transparency: { alpha: 0.3 } }) }, { ref: 'em-density-map' }); return { action: action, context: { id: id, refs: [mainRef] } }; } function fail(a, message) { return { action: Bootstrap.Tree.Transform.build() .add(a, Transformer.Basic.Fail, { title: 'Density', message: message }), context: void 0 }; } function abortMapDownlaod(a, mapSize) { if(scope.treeMenu == true || scope.treeMenu == 'true'){ return { action: void 0, context: { id: 'abortMaps', refs: ['abortMapDownload'], mapSize: mapSize } }; }else{ return { action: Bootstrap.Tree.Transform.build() .add(a, Transformer.Basic.CreateGroup, { id: 'abortMapsgroup', label: 'Density Dummy Group', description: 'Density Dummy Group' }, {ref: 'abortMapDownload'}), context: { id: 'abortMaps', refs: ['abortMapDownload'], mapSize: mapSize } }; } } function doEmdbPdbId(ctx, a, t, id) { return new LiteMol.Core.Promise(function (res, rej) { id = id.trim().toLowerCase(); Bootstrap.Utils.ajaxGetString("https://www.ebi.ac.uk/pdbe/api/pdb/entry/summary/" + id, 'PDB API') .run(ctx) .then(function (s) { try { var json = JSON.parse(s); var emdbId = void 0; var e = json[id]; if (e && e[0] && e[0].related_structures) { var emdb = e[0].related_structures.filter(function (s) { return s.resource === 'EMDB'; }); if (!emdb.length) { res(fail(a, "No related EMDB entry found for '" + id + "'.")); return; } emdbId = emdb[0].accession.split('-')[1]; } else { res(fail(a, "No related EMDB entry found for '" + id + "'.")); return; } if(typeof t.params.checkMapFileSize !== 'undefined' && t.params.checkMapFileSize == true){ res(validateEMMapFileSize(ctx, a, t, emdbId, 25)); }else{ res(doEmdbId(ctx, a, t, emdbId)); } } catch (e) { res(fail(a, 'PDB API call failed.')); } }) .catch(function (e) { return res(fail(a, 'PDB API call failed.')); }); }); } function doEmdbId(ctx, a, t, id) { return new LiteMol.Core.Promise(function (res, rej) { id = id.trim(); Bootstrap.Utils.ajaxGetString("https://www.ebi.ac.uk/pdbe/api/emdb/entry/map/EMD-" + id, 'EMDB API') .run(ctx) .then(function (s) { try { var json = JSON.parse(s); var contour = void 0; var sigmaRange = { 'minVal': void 0, 'maxVal': void 0}; var e = json['EMD-' + id]; if (e && e[0] && e[0].map && e[0].map.contour_level && e[0].map.contour_level.value !== void 0) { contour = +e[0].map.contour_level.value; } if (e && e[0] && e[0].map && e[0].map.statistics && e[0].map.statistics.minimum !== void 0) { sigmaRange.minVal = +e[0].map.statistics.minimum; } if (e && e[0] && e[0].map && e[0].map.statistics && e[0].map.statistics.maximum !== void 0) { sigmaRange.maxVal = +e[0].map.statistics.maximum; } res(doEmdb(a, t, id, contour, sigmaRange)); } catch (e) { res(fail(a, 'EMDB API call failed.')); } }) .catch(function (e) { return res(fail(a, 'EMDB API call failed.')); }); }); } function getExperimentType(context) { var exptlMethodVal; //Search Parsed CIF Dictionary for Experimental Method /*var cifDictionary = context.plugin.context.select('cifDict')[0]; if(typeof cifDictionary !== 'undefined'){ var dict = cifDictionary.props.dictionary; var cat = dict.dataBlocks[0].getCategory('_exptl'); if(typeof cat !== 'undefined') var exptlMethod = cat.getColumn('method').getString(0); }*/ try{ var moleculeRef = context.plugin.context.select('molecule')[0]; if(typeof moleculeRef !== 'undefined'){ var exptlProp = moleculeRef.props.molecule.properties.experimentMethod; if(typeof exptlProp !== 'undefined') var exptlMethod = exptlProp; } } catch(e) {} if(typeof exptlMethod !== 'undefined' && exptlMethod == 'Electron Microscopy'){ exptlMethodVal = 'emdb-pdbid'; }else if(typeof exptlMethod !== 'undefined' && exptlMethod == 'X-ray diffraction'){ exptlMethodVal = 'electron-density'; }else{ exptlMethodVal = exptlMethod; } return exptlMethodVal; } function validateEMMapFileSize(ctx, a, t, entryId, maxFileSize) { return new LiteMol.Core.Promise(function (res, rej) { $http.head("http://ftp.ebi.ac.uk/pub/databases/emdb/structures/EMD-" + entryId + "/map/emd_" + entryId + ".map.gz") .then(function(response) { var mapByteSize = response.headers('Content-Length'); var mapMBSize = typeof mapByteSize !== 'undefined' ? (mapByteSize / 1024) / 1024 : undefined; //console.log(mapMBSize+' MB'); if(typeof mapMBSize !== 'undefined' && mapMBSize <= maxFileSize){ res(doEmdbId(ctx, a, t, entryId)); }else{ res(abortMapDownlaod(a, mapMBSize)); } }, function(response) { res(fail(a, 'File size issue.')); }); }); } // this creates the electron density based on the spec you sent me Data.DownloadDensity = Bootstrap.Tree.Transformer.actionWithContext({ id: 'pdbe-density-download-data', name: 'Density Data from PDBe', description: 'Download density data from PDBe.', from: [Entity.Root], to: [Entity.Action], defaultParams: function (context) { var exptType = getExperimentType(context); return ({ sourceId: typeof exptType !== 'undefined' ? exptType : 'electron-density', id: { 'electron-density': scope.pdbId, 'emdb-id': '8003', 'emdb-pdbid': scope.pdbId } }); }, validateParams: function (p) { var source = p.sourceId ? p.sourceId : 'electron-density'; if (!p.id) return ['Enter Id']; var id = typeof p.id === 'string' ? p.id : p.id[source]; return !id.trim().length ? ['Enter Id'] : void 0; } }, function (context, a, t) { var id; var exptlMethod = typeof t.params.sourceId !== 'undefined' ? t.params.sourceId : getExperimentType(context); if (typeof t.params.id === 'string') id = t.params.id; else id = t.params.id[t.params.sourceId]; switch (exptlMethod) { case 'electron-density': return doElectron(a, t, id); case 'emdb-id': return doEmdbId(context, a, t, id); case 'emdb-pdbid': return doEmdbPdbId(context, a, t, id); default: return doElectron(a, t, id); //default: return fail(a, 'Unknown source.'); } }, function (ctx, actionCtx) { if (!actionCtx) return; var _a = actionCtx, id = _a.id, refs = _a.refs, groupRef = _a.groupRef; if(id == "abortMaps"){ var densityAbortMsg = 'You can download the Density using Download option in Controls'; if(typeof _a.mapSize !== 'undefined') densityAbortMsg = 'Density file size is '+ parseInt(_a.mapSize) +' MB. You can download it using the <strong>\'Download Density\'</strong> option in Controls.'; Bootstrap.Command.Toast.Show.dispatch(ctx, { key: 'density-abort-toast', title: 'Density', message: densityAbortMsg }); Bootstrap.Event.Visual.VisualSelectElement.getStream(ctx).subscribe(function (e) { Bootstrap.Command.Toast.Hide.dispatch(ctx, { key: 'density-abort-toast' }); }); //scope.LiteMolComponent.showControls(); }else{ var sel = ctx.select((_b = Tree.Selection).byRef.apply(_b, refs)); if (sel.length === refs.length) { ctx.logger.message('Density loaded, click on a residue or an atom to view the data.'); } else if (sel.length > 0) { ctx.logger.message('Density partially loaded, click on a residue or an atom to view the data.'); } else { ctx.logger.error("Density for ID '" + id + "' failed to load."); if (groupRef) { Bootstrap.Command.Tree.RemoveNode.dispatch(ctx, groupRef); } } var _b; //Remove 'RemoveNode' to hide Download density control Bootstrap.Command.Toast.Hide.dispatch(ctx, { key: 'density-abort-toast' }); if(scope.treeMenu == false || scope.treeMenu == 'false'){ var abortMapDownloadRef = ctx.select('abortMapDownload')[0]; if(typeof abortMapDownloadRef !== 'undefined') Bootstrap.Command.Tree.RemoveNode.dispatch(ctx, 'abortMapDownload'); } } }); })(Data = PDBe.Data || (PDBe.Data = {})); })(PDBe = Viewer.PDBe || (Viewer.PDBe = {})); })(Viewer = Viewer || (Viewer = {})); //Validation data annotation var Viewer; (function (Viewer) { var PDBe; (function (PDBe) { var Validation; (function (Validation) { var Entity = LiteMol.Bootstrap.Entity; var Transformer = LiteMol.Bootstrap.Entity.Transformer; Validation.Report = Entity.create({ name: 'PDBe Molecule Validation Report', typeClass: 'Behaviour', shortName: 'VR', description: 'Represents PDBe validation report.' }); var Api; (function (Api) { function getResidueId(seqNumber, insCode) { var id = seqNumber.toString(); if ((insCode || "").length !== 0) id += " " + insCode; return id; } Api.getResidueId = getResidueId; function getEntry(report, modelId, entity, asymId, residueId) { var e = report[entity]; if (!e) return void 0; e = e[asymId]; if (!e) return void 0; e = e[modelId]; if (!e) return void 0; return e[residueId]; } Api.getEntry = getEntry; function createReport(data) { var report = {}; if (!data.molecules) return report; for (var _i = 0, _a = data.molecules; _i < _a.length; _i++) { var entity = _a[_i]; var chains = {}; for (var _c = 0, _d = entity.chains; _c < _d.length; _c++) { var chain = _d[_c]; var models = {}; for (var _e = 0, _f = chain.models; _e < _f.length; _e++) { var model = _f[_e]; var residues = {}; for (var _g = 0, _h = model.residues; _g < _h.length; _g++) { var residue = _h[_g]; var id = getResidueId(residue.residue_number, residue.author_insertion_code), entry = residues[id]; if (entry) { entry.residues.push(residue); entry.numIssues = Math.max(entry.numIssues, residue.outlier_types.length); } else { residues[id] = { residues: [residue], numIssues: residue.outlier_types.length }; } } models[model.model_id.toString()] = residues; } chains[chain.struct_asym_id] = models; } report[entity.entity_id.toString()] = chains; } return report; } Api.createReport = createReport; })(Api || (Api = {})); var Interactivity; (function (Interactivity) { var Behaviour = (function () { function Behaviour(context, report) { var _this = this; this.context = context; this.report = report; this.provider = function (info) { try { return _this.processInfo(info); } catch (e) { console.error('Error showing validation label', e); return void 0; } }; } Behaviour.prototype.dispose = function () { this.context.highlight.removeProvider(this.provider); }; Behaviour.prototype.register = function (behaviour) { this.context.highlight.addProvider(this.provider); }; Behaviour.prototype.processInfo = function (info) { var i = LiteMol.Bootstrap.Interactivity.Molecule.transformInteraction(info); if (!i || i.residues.length > 1) return void 0; var r = i.residues[0]; var e = Api.getEntry(this.report, i.modelId, r.chain.entity.entityId, r.chain.asymId, Api.getResidueId(r.seqNumber, r.insCode)); if (!e) return void 0; var label; if (e.residues.length === 1) { var vr = e.residues[0]; label = 'Validation: '; if (!vr.outlier_types.length) label += 'no issue'; else label += "<b>" + e.residues[0].outlier_types.join(", ") + "</b>"; return label; } else { label = ''; var index = 0; for (var _i = 0, _a = e.residues; _i < _a.length; _i++) { var v = _a[_i]; if (index > 0) label += ', '; label += "Validation (altLoc " + v.alt_code + "): <b>" + v.outlier_types.join(", ") + "</b>"; index++; } return label; } }; return Behaviour; }()); Interactivity.Behaviour = Behaviour; })(Interactivity || (Interactivity = {})); var Theme; (function (Theme) { var colorMap = (function () { var colors = new Map(); colors.set(0, { r: 0, g: 1, b: 0 }); colors.set(1, { r: 1, g: 1, b: 0 }); colors.set(2, { r: 1, g: 0.5, b: 0 }); colors.set(3, { r: 1, g: 0, b: 0 }); return colors; })(); var defaultColor = { r: 0.6, g: 0.6, b: 0.6 }; var selectionColor = { r: 0, g: 0, b: 1 }; var highlightColor = { r: 1, g: 0, b: 1 }; function createResidueMapNormal(model, report) { var map = new Uint8Array(model.residues.count); var mId = model.modelId; var _a = model.residues, asymId = _a.asymId, entityId = _a.entityId, seqNumber = _a.seqNumber, insCode = _a.insCode; for (var i = 0, _b = model.residues.count; i < _b; i++) { var e = Api.getEntry(report, mId, entityId[i], asymId[i], Api.getResidueId(seqNumber[i], insCode[i])); if (e) { map[i] = Math.min(e.numIssues, 3); } } return map; } function createResidueMapComputed(model, report) { var map = new Uint8Array(model.residues.count); var mId = model.modelId; var parent = model.parent; var _a = model.residues, entityId = _a.entityId, seqNumber = _a.seqNumber, insCode = _a.insCode, chainIndex = _a.chainIndex; var sourceChainIndex = model.chains.sourceChainIndex; var asymId = parent.chains.asymId; for (var i = 0, _b = model.residues.count; i < _b; i++) { var aId = asymId[sourceChainIndex[chainIndex[i]]]; var e = Api.getEntry(report, mId, entityId[i], aId, Api.getResidueId(seqNumber[i], insCode[i])); if (e) { map[i] = Math.min(e.numIssues, 3); } } return map; } function create(entity, report) { var model = entity.props.model; var map = model.source === LiteMol.Core.Structure.MoleculeModelSource.File ? createResidueMapNormal(model, report) : createResidueMapComputed(model, report); var colors = new Map(); colors.set('Uniform', defaultColor); colors.set('Selection', selectionColor); colors.set('Highlight', highlightColor); var residueIndex = model.atoms.residueIndex; var mapping = LiteMol.Visualization.Theme.createColorMapMapping(function (i) { return map[residueIndex[i]]; }, colorMap, defaultColor); return LiteMol.Visualization.Theme.createMapping(mapping, { colors: colors, interactive: true, transparency: { alpha: 1.0 } }); } Theme.create = create; })(Theme || (Theme = {})); var Create = LiteMol.Bootstrap.Tree.Transformer.create({ id: 'pdbe-validation-create', name: 'PDBe Validation', description: 'Create the validation report from a string.', from: [Entity.Data.String], to: [Validation.Report], defaultParams: function () { return ({}); } }, function (context, a, t) { return LiteMol.Bootstrap.Task.create("Validation Report (" + t.params.id + ")", 'Normal', function (ctx) { ctx.update('Parsing...'); ctx.schedule(function () { var data = JSON.parse(a.props.data); var model = data[t.params.id]; var report = Api.createReport(model || {}); ctx.resolve(Validation.Report.create(t, { label: 'Validation Report', behaviour: new Interactivity.Behaviour(context, report), ref: 'validation-visual' })); }); }).setReportTime(true); }); Validation.CustomDownloadAndCreate = LiteMol.Bootstrap.Tree.Transformer.action({ id: 'pdbe-validation-download-and-create', name: 'PDBe Validation Report', description: 'Download Validation Report from PDBe', from: [Entity.Root], to: [Entity.Action], defaultParams: function () { return ({}); }, customController: function (ctx, t, e) { return new LiteMol.Bootstrap.Components.Transform.Controller(ctx, t, e, false); } }, function (context, a, t) { var id = t.params.id.trim().toLocaleLowerCase(); var action = LiteMol.Bootstrap.Tree.Transform.build() .add(a, Transformer.Data.Download, { url: "//www.ebi.ac.uk/pdbe/api/validation/residuewise_outlier_summary/entry/" + id, type: 'String', id: id, description: 'Validation Data' }) .then(Create, { id: id }, { isBinding: true, ref: 'validation-model' }); return action; }, "Validation report loaded. Hovering over residue will now contain validation info. To apply validation coloring, select the entity in the tree and apply it the right panel."); Validation.DownloadAndCreate = LiteMol.Bootstrap.Tree.Transformer.action({ id: 'pdbe-validation-download-and-create', name: 'PDBe Validation Report', description: 'Download Validation Report from PDBe', from: [Entity.Molecule.Molecule], to: [Entity.Action], defaultParams: function () { return ({}); }, customController: function (ctx, t, e) { return new LiteMol.Bootstrap.Components.Transform.Controller(ctx, t, e, false); } }, function (context, a, t) { var id = a.props.molecule.id.trim().toLocaleLowerCase(); var action = LiteMol.Bootstrap.Tree.Transform.build() .add(a, Transformer.Data.Download, { url: "//www.ebi.ac.uk/pdbe/api/validation/residuewise_outlier_summary/entry/" + id, type: 'String', id: id, description: 'Validation Data' }) .then(Create, { id: id }, { isBinding: true }); return action; }, "Validation report loaded. Hovering over residue will now contain validation info. To apply validation coloring, select the entity in the tree and apply it the right panel."); Validation.ApplyTheme = LiteMol.Bootstrap.Tree.Transformer.create({ id: 'pdbe-validation-apply-theme', name: 'Apply Coloring', description: 'Colors all visuals using the validation report.', from: [Validation.Report], to: [Entity.Action], defaultParams: function () { return ({}); } }, function (context, a, t) { return LiteMol.Bootstrap.Task.create('Validation Coloring', 'Background', function (ctx) { var molecule = LiteMol.Bootstrap.Tree.Node.findAncestor(context.select('polymer-visual')[0], LiteMol.Bootstrap.Entity.Molecule.Molecule); if (!molecule) { ctx.reject('No suitable parent found.'); return; } var themes = new Map(); var visuals = context.select(LiteMol.Bootstrap.Tree.Selection.byValue(molecule).subtree().ofType(LiteMol.Bootstrap.Entity.Molecule.Visual)); for (var _i = 0, visuals_1 = visuals; _i < visuals_1.length; _i++) { var v = visuals_1[_i]; var model = LiteMol.Bootstrap.Utils.Molecule.findModel(v); if (!model) continue; var theme = themes.get(model.id); if (!theme) { theme = Theme.create(model, a.props.behaviour.report); themes.set(model.id, theme); } LiteMol.Bootstrap.Command.Visual.UpdateBasicTheme.dispatch(context, { visual: v, theme: theme }); } context.logger.message('Validation coloring applied.'); ctx.resolve(LiteMol.Bootstrap.Tree.Node.Null); }); }); })(Validation = PDBe.Validation || (PDBe.Validation = {})); })(PDBe = Viewer.PDBe || (Viewer.PDBe = {})); })(Viewer = Viewer || (Viewer = {})); //Sequence Annotation var Viewer; (function (Viewer) { var PDBe; (function (PDBe) { var SequenceAnnotation; (function (SequenceAnnotation) { var Entity = LiteMol.Bootstrap.Entity; var Transformer = LiteMol.Bootstrap.Entity.Transformer; var Query = LiteMol.Core.Structure.Query; SequenceAnnotation.Annotations = Entity.create({ name: 'PDBe Sequence Annotations', typeClass: 'Data', shortName: 'SA', description: 'Represents PDBe sequence annotation data.' }); SequenceAnnotation.Annotation = Entity.create({ name: 'PDBe Sequence Annotation', typeClass: 'Object', shortName: 'SA', description: 'Represents PDBe sequence annotation.' }, { isSilent: true, isFocusable: true }); SequenceAnnotation.Behaviour = Entity.create({ name: 'PDBe Sequence Annotation Behaviour', typeClass: 'Behaviour', shortName: 'SA', description: 'Represents PDBe sequence annoation behaviour.' }); var Interactivity; (function (Interactivity) { var Behaviour = (function () { function Behaviour(context) { var _this = this; this.context = context; this.node = void 0; this.current = void 0; this.subs = []; this.toHighlight = void 0; this.isHighlightOn = false; this.__highlight = LiteMol.Core.Utils.debounce(function () { return _this.highlight(); }, 33); } Behaviour.prototype.dispose = function () { this.resetTheme(); for (var _i = 0, _a = this.subs; _i < _a.length; _i++) { var sub = _a[_i]; sub.dispose(); } this.subs = []; this.node = void 0; }; Behaviour.prototype.register = function (behaviour) { var _this = this; this.node = behaviour; this.subs.push(this.context.behaviours.currentEntity.subscribe(function (e) { return _this.update(e); })); this.subs.push(LiteMol.Bootstrap.Command.Entity.Highlight.getStream(this.context).subscribe(function (e) { if (e.data.entities.length === 1) { var a = e.data.entities[0]; if (a.type !== SequenceAnnotation.Annotation) return; _this.toHighlight = a; _this.isHighlightOn = e.data.isOn; _this.__highlight(); } })); this.subs.push(LiteMol.Bootstrap.Command.Entity.Focus.getStream(this.context).subscribe(function (e) { if (e.data.length === 1) { var a = e.data[0]; if (a.type !== SequenceAnnotation.Annotation) return; _this.focus(a); } })); }; Object.defineProperty(Behaviour.prototype, "molecule", { get: function () { return LiteMol.Bootstrap.Utils.Molecule.findMolecule(this.node); }, enumerable: true, configurable: true }); Behaviour.prototype.resetTheme = function () { var molecule = this.molecule; if (molecule) { LiteMol.Bootstrap.Command.Visual.ResetTheme.dispatch(this.context, { selection: LiteMol.Bootstrap.Tree.Selection.byValue(molecule).subtree() }); } }; Behaviour.prototype.getCached = function (a, model) { return this.context.entityCache.get(a, "theme-" + model.id); }; Behaviour.prototype.setCached = function (a, model, theme) { var e = this.context.entityCache.set(a, "theme-" + model.id, theme); }; Behaviour.prototype.highlight = function () { var e = this.toHighlight; this.toHighlight = void 0; if (!e || e.type !== SequenceAnnotation.Annotation) return; var a = e; if (!this.isHighlightOn) { if (this.current) { this.update(this.current); } else { this.resetTheme(); } } else { this.apply(a); } }; Behaviour.prototype.focus = function (a) { var molecule = this.molecule; if (!molecule) return; var model = this.context.select(LiteMol.Bootstrap.Tree.Selection.byValue(molecule).subtree().ofType(LiteMol.Bootstrap.Entity.Molecule.Model))[0]; if (!model) return; LiteMol.Bootstrap.Command.Molecule.FocusQuery.dispatch(this.context, { model: model, query: a.props.query }); LiteMol.Bootstrap.Command.Entity.SetCurrent.dispatch(this.context, a); }; Behaviour.prototype.apply = function (a) { var molecule = this.molecule; if (!molecule) return; var visuals = this.context.select(LiteMol.Bootstrap.Tree.Selection.byValue(molecule).subtree().ofType(LiteMol.Bootstrap.Entity.Molecule.Visual)); for (var _i = 0, visuals_2 = visuals; _i < visuals_2.length; _i++) { var v = visuals_2[_i]; var model = LiteMol.Bootstrap.Utils.Molecule.findModel(v); if (!model) continue; var theme = this.getCached(a, model); if (!theme) { theme = Theme.create(model, a.props.query, a.props.color); this.setCached(a, model, theme); } LiteMol.Bootstrap.Command.Visual.UpdateBasicTheme.dispatch(this.context, { visual: v, theme: theme }); } }; Behaviour.prototype.update = function (e) { if (!e || e.type !== SequenceAnnotation.Annotation) { if (this.current) this.resetTheme(); this.current = void 0; return; } this.current = e; this.apply(this.current); }; return Behaviour; }()); Interactivity.Behaviour = Behaviour; })(Interactivity || (Interactivity = {})); var Theme; (function (Theme) { var defaultColor = { r: 1, g: 1, b: 1 }; var selectionColor = LiteMol.Visualization.Theme.Default.SelectionColor; var highlightColor = LiteMol.Visualization.Theme.Default.HighlightColor; function createResidueMap(model, fs) { var map = new Uint8Array(model.residues.count); var mId = model.modelId; var residueIndex = model.atoms.residueIndex; var _a = model.residues, asymId = _a.asymId, entityId = _a.entityId, seqNumber = _a.seqNumber, insCode = _a.insCode; for (var _i = 0, _b = fs.fragments; _i < _b.length; _i++) { var f = _b[_i]; for (var _c = 0, _d = f.atomIndices; _c < _d.length; _c++) { var i = _d[_c]; map[residueIndex[i]] = 1; } } return map; } function create(entity, query, color) { var model = entity.props.model; var q = Query.Builder.toQuery(query); var fs = q(model.queryContext); var map = createResidueMap(model, fs); var colors = new Map(); colors.set('Uniform', defaultColor); colors.set('Bond', defaultColor); colors.set('Selection', selectionColor); colors.set('Highlight', highlightColor); var colorMap = new Map(); colorMap.set(1, color); var residueIndex = model.atoms.residueIndex; var mapping = LiteMol.Visualization.Theme.createColorMapMapping(function (i) { return map[residueIndex[i]]; }, colorMap, defaultColor); return LiteMol.Visualization.Theme.createMapping(mapping, { colors: colors, interactive: true, transparency: { alpha: 1.0 } }); } Theme.create = create; })(Theme || (Theme = {})); SequenceAnnotation.Theme = Theme; function buildAnnotations(parent, id, data) { var action = LiteMol.Bootstrap.Tree.Transform.build(); if (!data) { return action; } var baseColor = LiteMol.Visualization.Color.fromHex(0xFA6900); var _loop_1 = function(g) { var ans = data[g]; if (!ans) return "continue"; var entries = Object.keys(ans).filter(function (a) { return Object.prototype.hasOwnProperty.call(ans, a); }); if (!entries.length) return "continue"; var group = action.add(parent, Transformer.Basic.CreateGroup, { label: g, isCollapsed: true }, { isBinding: true, ref: g }); for (var _i = 0, entries_1 = entries; _i < entries_1.length; _i++) { var a = entries_1[_i]; group.then(SequenceAnnotation.CreateSingle, { data: ans[a], id: a, color: baseColor }); } }; for (var _a = 0, _b = ["Pfam", "InterPro", "CATH", "SCOP", "UniProt"]; _a < _b.length; _a++) { var g = _b[_a]; var state_1 = _loop_1(g); if (state_1 === "continue") continue; } action.add(parent, CreateBehaviour, {}, { isHidden: true, ref: 'domain-annotation-model' }); return action; } function getInsCode(v) { if (v.length === 0) return null; return v; } SequenceAnnotation.CreateSingle = LiteMol.Bootstrap.Tree.Transformer.create({ id: 'pdbe-sequence-annotations-create-single', name: 'PDBe Sequence Annotation', description: 'Create a sequence annotation object.', from: [], to: [SequenceAnnotation.Annotation], defaultParams: function () { return ({}); }, isUpdatable: true }, function (context, a, t) { return LiteMol.Bootstrap.Task.create("Sequence Annotation", 'Background', function (ctx) { var data = t.params.data; var query = Query.or.apply(null, data.mappings.map(function (m) { return Query.sequence(m.entity_id.toString(), m.struct_asym_id, { seqNumber: m.start.residue_number, insCode: getInsCode(m.start.author_insertion_code) }, { seqNumber: m.end.residue_number, insCode: getInsCode(m.end.author_insertion_code) }); })) .union(); ctx.resolve(SequenceAnnotation.Annotation.create(t, { label: data.identifier, description: t.params.id, query: query, color: t.params.color }, {ref: data.identifier})); }); }); var Parse = LiteMol.Bootstrap.Tree.Transformer.create({ id: 'pdbe-sequence-annotations-parse', name: 'PDBe Sequence Annotations', description: 'Parse sequence annotaions JSON.', from: [Entity.Data.String], to: [SequenceAnnotation.Annotations], defaultParams: function () { return ({}); } }, function (context, a, t) { return LiteMol.Bootstrap.Task.create("Sequence Annotations", 'Normal', function (ctx) { ctx.update('Parsing...'); ctx.schedule(function () { var data = JSON.parse(a.props.data); ctx.resolve(SequenceAnnotation.Annotations.create(t, { label: 'Sequence Annotations', data: data })); }); }).setReportTime(true); }); var CreateBehaviour = LiteMol.Bootstrap.Tree.Transformer.create({ id: 'pdbe-sequence-annotations-create-behaviour', name: 'PDBe Sequence Annotation Behaviour', description: 'Create sequence annotation behaviour.', from: [SequenceAnnotation.Annotations], to: [SequenceAnnotation.Behaviour], defaultParams: function () { return ({}); } }, function (context, a, t) { return LiteMol.Bootstrap.Task.resolve("Sequence Annotations", 'Background', SequenceAnnotation.Behaviour.create(t, { label: 'Sequence Annotations', behaviour: new Interactivity.Behaviour(context) })); }); var Build = LiteMol.Bootstrap.Tree.Transformer.action({ id: 'pdbe-sequence-annotations-build', name: 'PDBe Sequence Annotations', description: 'Build sequence validations behaviour.', from: [SequenceAnnotation.Annotations], to: [Entity.Action], defaultParams: function () { return ({}); } }, function (context, a, t) { var data = a.props.data; var keys = Object.keys(data); return buildAnnotations(a, keys[0], data[keys[0]]); }, "Sequence annotations downloaded. Selecting or hovering an annotation in the tree will color the visuals."); SequenceAnnotation.DownloadAndCreate = LiteMol.Bootstrap.Tree.Transformer.action({ id: 'pdbe-sequence-annotations-download-and-create', name: 'PDBe Sequence Annotations', description: 'Download Sequence Annotations from PDBe', from: [Entity.Molecule.Molecule], to: [Entity.Action], defaultParams: function () { return ({}); }, customController: function (ctx, t, e) { return new LiteMol.Bootstrap.Components.Transform.Controller(ctx, t, e, false); } }, function (context, a, t) { var id = a.props.molecule.id.trim().toLocaleLowerCase(); return LiteMol.Bootstrap.Tree.Transform.build() .add(a, Transformer.Data.Download, { url: "//www.ebi.ac.uk/pdbe/api/mappings/" + id, type: 'String', id: id, description: 'Annotation Data' }) .then(Parse, {}, { isBinding: true }) .then(Build, {}, { isBinding: true }); }); SequenceAnnotation.CustomDownloadAndCreate = LiteMol.Bootstrap.Tree.Transformer.action({ id: 'pdbe-sequence-annotations-download-and-create', name: 'PDBe Sequence Annotations', description: 'Download Sequence Annotations from PDBe', from: [Entity.Root], to: [Entity.Action], defaultParams: function () { return ({}); }, customController: function (ctx, t, e) { return new LiteMol.Bootstrap.Components.Transform.Controller(ctx, t, e, false); } }, function (context, a, t) { var id = t.params.id.trim().toLocaleLowerCase(); return LiteMol.Bootstrap.Tree.Transform.build() .add(a, Transformer.Data.Download, { url: "//www.ebi.ac.uk/pdbe/api/mappings/" + id, type: 'String', id: id, description: 'Annotation Data' }) .then(Parse, {}, { isBinding: true }) .then(Build, {}, { isBinding: true }); }); })(SequenceAnnotation = PDBe.SequenceAnnotation || (PDBe.SequenceAnnotation = {})); })(PDBe = Viewer.PDBe || (Viewer.PDBe = {})); })(Viewer = Viewer || (Viewer = {})); //HTML like syntax implementation for adding sequence annotaions var Viewer; (function (Viewer) { var PDBe; (function (PDBe) { var Views; (function (Views) { var React = LiteMol.Plugin.React; // this is to enable the HTML-like syntax var Controls = LiteMol.Plugin.Controls; var CreateSequenceAnnotationView = (function (_super) { __extends(CreateSequenceAnnotationView, _super); function CreateSequenceAnnotationView() { _super.apply(this, arguments); } CreateSequenceAnnotationView.prototype.renderControls = function () { var _this = this; var params = this.params; return React.createElement("div", null, React.createElement(Controls.ToggleColorPicker, {label: 'Color', color: params.color, onChange: function (c) { return _this.controller.autoUpdateParams({ color: c }); }, position: 'below'})); }; return CreateSequenceAnnotationView; }(LiteMol.Plugin.Views.Transform.ControllerBase)); Views.CreateSequenceAnnotationView = CreateSequenceAnnotationView; var DownloadBinaryCIFFromCoordinateServerView = (function (_super) { __extends(DownloadBinaryCIFFromCoordinateServerView, _super); function DownloadBinaryCIFFromCoordinateServerView() { _super.apply(this, arguments); } DownloadBinaryCIFFromCoordinateServerView.prototype.renderControls = function () { var _this = this; var params = this.params; return React.createElement("div", null, React.createElement(Controls.TextBoxGroup, {value: params.id, onChange: function (v) { return _this.updateParams({ id: v }); }, label: 'Id', onEnter: function (e) { return _this.applyEnter(e); }, placeholder: 'Enter pdb id...'}), React.createElement(Controls.OptionsGroup, {options: ['Cartoon', 'Full'], caption: function (s) { return s; }, current: params.type, onChange: function (o) { return _this.updateParams({ type: o }); }, label: 'Type', title: 'Determines whether to send all atoms or just atoms that are needed for the Cartoon representation.'}), React.createElement(Controls.Toggle, {onChange: function (v) { return _this.updateParams({ lowPrecisionCoords: v }); }, value: params.lowPrecisionCoords, label: 'Low Precicion', title: 'If on, sends coordinates with 1 digit precision instead of 3. This saves up to 50% of data that need to be sent.'}), React.createElement(Controls.TextBoxGroup, {value: params.serverUrl, onChange: function (v) { return _this.updateParams({ serverUrl: v }); }, label: 'Server', title: 'The base URL of the CoordinateServer.', onEnter: function (e) { return _this.applyEnter(e); }, placeholder: 'Enter server URL...'})); }; return DownloadBinaryCIFFromCoordinat