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
JavaScript
; (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