CSS = """ /* HTML:
*/ .loader { width: 60px; aspect-ratio: 1.154; color: #25b09b; display: grid; animation: l38-0 6s infinite linear; } .loader:before, .loader:after { content: ""; grid-area: 1/1; } .loader:before { clip-path: polygon(25% 0,75% 0,100% 50%,75% 100%,25% 100%,0 50%); background: conic-gradient(from -30deg,#0000 60deg,currentColor 0); } .loader:after { background: conic-gradient(from -30deg,currentColor 60deg,#0000 0); animation: l38 0.5s infinite alternate; } @keyframes l38-0 { 0%,16.66% {transform: rotate(0deg)} 16.67%,33.33% {transform: rotate(60deg)} 33.34%,50% {transform: rotate(120deg)} 50.1%,66.66% {transform: rotate(180deg)} 66.67%,83.33% {transform: rotate(240deg)} 83.34%,100% {transform: rotate(300deg)} } @keyframes l38 { 100% {transform: translateY(-20px)} } .loader.first-frame { animation: none; /* Stop the main animation */ } .loader.first-frame:before { transform: rotate(0deg); /* Explicitly set the rotation to the first frame */ } .loader.first-frame:after { animation: none; /* Stop the after animation */ transform: translateY(0px); /*reset the translate Y*/ } .help-tip { position: absolute; display: inline-block; top: 16px; right: 0px; text-align: center; border-radius: 40%; /* border: 2px solid darkred; background-color: #8B0000;*/ width: 24px; height: 24px; font-size: 16px; line-height: 26px; cursor: default; transition: all 0.5s cubic-bezier(0.55, 0, 0.1, 1); z-index: 100 !important; } .help-tip:hover { cursor: pointer; /*background-color: #ccc;*/ } .help-tip:before { content: '?'; font-weight: 700; color: #8B0000; z-index: 100 !important; } .help-tip p { visibility: hidden; opacity: 0; text-align: left; background-color: #EFDDE3; padding: 20px; width: 300px; position: absolute; border-radius: 4px; right: -4px; color: #494F5A; font-size: 13px; line-height: normal; transform: scale(0.7); transform-origin: 100% 0%; transition: all 0.5s cubic-bezier(0.55, 0, 0.1, 1); z-index: 100; } .help-tip:hover p { cursor: default; visibility: visible; opacity: 1; transform: scale(1.0); } .help-tip p:before { position: absolute; content: ''; width: 0; height: 0; border: 6px solid transparent; border-bottom-color: #EFDDE3; right: 10px; top: -12px; } .help-tip p:after { width: 100%; height: 40px; content: ''; position: absolute; top: -5px; left: 0; z-index: 101; } .upload_button { background-color: #008000; } .absolute { position: absolute; } .example { padding: 0; background: none; border: none; text-decoration: underline; box-shadow: none; text-align: left !important; display: inline-block !important; } footer { visibility: hidden } .html-container { padding: 0px; } .block .table-wrap { width: 100% !important; aspect-ratio: 1.618 / 1 !important; height: auto !important; } .mol-container { width: 100%; aspect-ratio: 1.618 / 1; } .mol-container > canvas { position: relative ! important; } .gr-btn-grp { display: flex; flex-wrap: wrap; gap: var(--layout-gap); width: var(--size-full); position: relative border-radius: 0 border-radius: var(--container-radius); background: var(--background-fill-secondary); padding: var(--size-2) align-items: stretch } .gr-btn-grp > button { flex: 1 1 0; display: inline-flex; justify-content: center; align-items: center; transition: var(--button-transition); padding: var(--size-0-5) var(--size-2); text-align: center; border: var(--button-border-width) solid var(--button-secondary-border-color); background: var(--button-secondary-background-fill); color: var(--button-secondary-text-color); box-shadow: var(--button-secondary-shadow) border-radius: var(--button-large-radius); padding: calc(var(--button-large-padding) - 1px); font-family: monospace; font-weight: var(--button-large-text-weight); font-size: calc(var(--button-large-text-size) - 1px); } """ IFRAME_TEMPLATE = """ """ SETUP_JS = """ () => { const scripts = [ "https://code.jquery.com/jquery-3.7.1.min.js", "https://3dmol.org/build/3Dmol.js" ]; scripts.forEach((script) => { const scriptElement = document.createElement("script"); scriptElement.src = script; scriptElement.async = true; document.head.appendChild(scriptElement); }); viewerConfig = { backgroundColor: "white" }; ligandStyle = { stick: { colorscheme: "greenCarbon" } }; proteinStyle = { cartoon: { colorscheme: "Jmol" } }; pocketStyle = { clicksphere: { radius: 1.5 } }; pocketSurfaceStyle = { opacity: 0.854, color: "aquamarine" }; selectedElements = { "atoms": [], "ligand": {name: null, resn: null, chain: null, resi: null}, "pocket": {name: null, id: null} } } """ # function handleMessage(event) { # if (event.data.name == "atom_selection") { # console.log("New message: ", event.data) # let atom = event.data.data["atom"]; # let add = event.data.data["add"]; # console.log("add: ", add, " atom: ", atom); # window.selected_elements["atom_selection"][atom] = add; # } # if (event.data.name == "ligand_selection") { # console.log("New message: ", event.data) # let residue_info = event.data.data["residue"]; # let add = event.data.data["add"]; # residue = residue_info.resi + ":" + residue_info.resn + ":" + residue_info.chain; # console.log("add: ", add, " residue: ", residue); # window.selected_elements["ligand_selection"][residue] = add; # } # if (event.data.name == "pocket_selection") { # let pocket = event.data.data["pocket"]; # console.log("add: ", add, " pocket: ", pocket); # window.selected_elements["pocket_selection"][pocket] = add; # } # } # # window.addEventListener("message", handleMessage); # console.log("Listener Added"); # console.log(window.selected_elements); RETURN_LIGAND_SELECTION_JS = """ (prot_file, selected_ligand) => { const selectedElements = window.selected_elements || {}; // Handle potential undefined if ("ligand_selection" in selectedElements) { ligandElements = selectedElements["ligand_selection"]; for (const [residue, add] of Object.entries(ligandElements)) { if (add) { selectedLigand = residue console.log("Selecting ligand ", selectedLigand); } } } console.log("Finished parsing selection."); window.selected_elements["ligand_selection"] = {} return [prot_file, selectedLigand]; } """ RETURN_SELECTION = """ (selected_ligand, selected_pocket) => { selectedAtoms = selectedElements.atoms; selectedLigand = selectedElements.ligand.name; console.log("Selected Ligand:", selectedLigand); selectedPocket = selectedElements.pocket.name; console.log("Selected Pocket:", selectedPocket); return [selectedLigand, selectedPocket]; } """ RETURN_POCKET_SELECTION_JS = """ (prot_file, selected_pocket) => { const selectedElements = window.selected_elements || {}; // Handle potential undefined if ("pocket_selection" in selectedElements) { pocketElements = selectedElements["pocket_selection"]; for (const [pocket, add] of Object.entries(pocketElements)) { if (add) { console.log("Selecting pocket ", pocket); selected_pocket = pocket; } } } console.log("Finished parsing selection."); window.selected_elements["pocket_selection"] = {} return [prot_file, selected_pocket]; } """ RETURN_ATOM_SELECTION_JS = """ (input_file, selected_atoms) => { let selectedAtoms = []; if ("atom_selection" in selectedElements) { atomElements = selectedElements["atom_selection"]; for (const [atom, add] of Object.entries(atomElements)) { if (add) { console.log("Adding atom ", atom); selectedAtoms.push(String(atom)); } } } const selectedAtomsString = selectedAtoms.join(","); console.log("Finished parsing selection."); window.selected_elements["atom_selection"] = {} return [input_file, selectedAtomsString]; } """ CREATE_INPUT_MOL_VIEW = """ (mol_file, view_html) => { try { let viewer; // Get element id of the view_html string with error handling const idMatch = view_html.match(/id="(\w+)"/); if (!idMatch || !idMatch[1]) { console.error("Invalid view_html: No ID found."); return; // Exit the function if no ID is found } const element_id = idMatch[1]; const element = document.getElementById(element_id); if (!element.querySelector('canvas')) { viewer = $3Dmol.createViewer(element, viewerConfig); } else { viewer = element.querySelector('canvas')._3dmol_viewer; viewer.clear(); selectedElements = { "atoms": [], "ligand": {name: null, resn: null, chain: null, resi: null}, "pocket": {name: null, id: null} } } if (mol_file == null) { return; } $.get(mol_file.url, function(molContent) { fmt = mol_file.path.split('.').pop(); model = viewer.addModel(molContent, fmt); model.setStyle({ hetflag: false }, proteinStyle); model.setStyle({ hetflag: true }, ligandStyle); selectedLigand = selectedElements.ligand.name; model.setClickable( { hetflag: true, byres: true }, true, function (_atom, _viewer, _event, _container) { let selectedLigand = selectedElements.ligand; let currentLigand = { resn: _atom.resn, chain: _atom.chain, resi: _atom.resi }; currentLigand.name = currentLigand.resi + ":" + currentLigand.resn + ":" + currentLigand.chain; if (selectedLigand.name == currentLigand.name) { // Deselect ligand _viewer.setStyle( { resn: currentLigand.resn, chain: currentLigand.chain, resi: currentLigand.resi }, ligandStyle ); console.log("Deselected Ligand:", currentLigand); selectedElements.ligand = {name: null, resn: null, chain: null, resi: null}; } else { // Select ligand and deselect previous if (selectedLigand.name) { _viewer.setStyle( { resn: selectedLigand.resn, chain: selectedLigand.chain, resi: selectedLigand.resi }, ligandStyle ); } selectedElements.ligand = currentLigand; _viewer.setStyle( { resn: currentLigand.resn, chain: currentLigand.chain, resi: currentLigand.resi }, { stick: { color: "red", radius: "0.4"} } ); console.log("Selected Ligand:", currentLigand); } _viewer.render(); } ); viewer.zoomTo(); viewer.render(); }).fail(function(error) { console.error("Error loading molecule:", error); }); } catch (error) { console.error("An error occurred:", error); } } """ UPDATE_MOL_VIEW = """ (mol_files, view_html) => { try { let viewer; // Get element id of the view_html string with error handling const idMatch = view_html.match(/id="(\w+)"/); if (!idMatch || !idMatch[1]) { console.error("Invalid view_html: No ID found."); return; // Exit the function if no ID is found } const element_id = idMatch[1]; const element = document.getElementById(element_id); if (element.querySelector('canvas')) { viewer = element.querySelector('canvas')._3dmol_viewer; for (let i = 1; i < viewer.models.length; i++) { viewer.removeModel(i); } viewer.removeAllSurfaces(); } else { console.error("Invalid view_html: No canvas found."); return; } for (var mol_file of mol_files) { // Get the file format const fmt = mol_file.orig_name.split('.').pop(); const filename = mol_file.orig_name; console.log("File:", mol_file.orig_name, "Format:", fmt); // Check values $.get(mol_file.url, function(molContent) { model = viewer.addModel(molContent, fmt); if (fmt == "pqr") { model.setStyle(pocketStyle); surface = viewer.addSurface('VDW', pocketSurfaceStyle, {model: model}, {model: model}); const surface_id = surface.surfid; const pocket_name = filename.replace("_vert.pqr", ""); model.setClickable( { byres: true }, true, function (_atom, _viewer, _event, _container) { let selectedPocket = selectedElements.pocket; let currentPocket = { name: pocket_name, id: _atom.model, surface: surface_id }; if (currentPocket.name == selectedPocket.name) { // Deselect pocket // _viewer.setStyle({model: currentPocket.id}, pocketStyle); _viewer.setSurfaceMaterialStyle(currentPocket.surface, pocketSurfaceStyle); console.log("Deselected Pocket:", currentPocket); selectedElements.pocket = {name: null, id: null}; } else { // Select pocket and deselect previous if (selectedPocket.name) { console.log("Deselected Pocket:", selectedPocket); //_viewer.setStyle({model: selectedPocket.id}, pocketStyle); _viewer.setSurfaceMaterialStyle(selectedPocket.surface, pocketSurfaceStyle); } selectedElements.pocket = currentPocket; //_viewer.setStyle({model: currentPocket.id}, {sphere: {color: "red", opacity: 0.944} }); _viewer.setSurfaceMaterialStyle(currentPocket.surface, { opacity: 0.944, color: "red" }); console.log("Selected Pocket:", currentPocket); } _viewer.render(); } ); } else { model.setStyle({ hetflag: false }, proteinStyle); model.setStyle({ hetflag: true }, ligandStyle); } viewer.render(); }).fail(function(error) { console.error("Error loading molecule:", error); }) } console.log("Rendering protein view."); viewer.zoomTo(); viewer.render(); selectedElements = { "atoms": [], "ligand": {name: null, resn: null, chain: null, resi: null}, "pocket": {name: null, id: null} } } catch (error) { console.error("An error occurred:", error); } } """ CREATE_OUTPUT_MOL_VIEW = """ (mol_file, view_html) => { try { let viewer; const idMatch = view_html.match(/id="(\w+)"/); if (!idMatch || !idMatch[1]) { console.error("Invalid view_html: No ID found."); return; } const element_id = idMatch[1]; const element = document.getElementById(element_id); fmt = mol_file.path.split('.').pop(); $.get(mol_file.url, function(molContent) { if (!element.querySelector('canvas')) { viewer = $3Dmol.createViewer(element, viewerConfig); } else { viewer = element.querySelector('canvas')._3dmol_viewer; viewer.clear(); } model = viewer.addModel(molContent, fmt); model.setStyle({ hetflag: false }, proteinStyle); model.setStyle({ hetflag: true }, ligandStyle); viewer.zoomTo(); viewer.render(); let container = element.parentElement.querySelector(".gr-btn-grp"); if (!container) { container = document.createElement("div"); container.classList.add("gr-btn-grp"); element.parentElement.appendChild(container); } // Molecule Button let toggleMoleculeButton = container.querySelector("#toggleMoleculeButton"); if (!toggleMoleculeButton) { toggleMoleculeButton = document.createElement("button"); toggleMoleculeButton.id = "toggleMoleculeButton"; // Add an ID toggleMoleculeButton.textContent = "Hide Generated Molecule"; let moleculeIsHidden = false; toggleMoleculeButton.onclick = function() { if (viewer.models.length === 2) { moleculeIsHidden = !moleculeIsHidden; viewer.addStyle({model: 1}, {stick: {hidden: moleculeIsHidden} }); toggleMoleculeButton.textContent = moleculeIsHidden ? "Show Generated Molecule" : "Hide Generated Molecule"; viewer.render(); } }; container.appendChild(toggleMoleculeButton); } // Ligand Button const ligandButtonId = "toggleLigandButton"; let toggleLigandButton = container.querySelector("#" + ligandButtonId); // Check for ligands and existing button const hasLigands = viewer.getAtomsFromSel({model: 0, hetflag : true}).length > 0; if (hasLigands && !toggleLigandButton) { // Create button if ligands exist and it doesn't exist yet toggleLigandButton = document.createElement("button"); toggleLigandButton.id = ligandButtonId; toggleLigandButton.textContent = "Hide Co-Crystallized Ligand"; let ligandIsHidden = false; toggleLigandButton.onclick = function() { ligandIsHidden = !ligandIsHidden; viewer.addStyle({model: 0, hetflag: true}, {stick: {hidden: ligandIsHidden} }); toggleLigandButton.textContent = ligandIsHidden ? "Show Co-Crystallized Ligand" : "Hide Co-Crystallized Ligand"; viewer.render(); }; container.appendChild(toggleLigandButton); } else if (!hasLigands && toggleLigandButton) { // Remove button if no ligands exist and it already exists container.removeChild(toggleLigandButton); } // Protein Button let toggleProteinButton = container.querySelector("#toggleProteinButton"); if (!toggleProteinButton) { toggleProteinButton = document.createElement("button"); toggleProteinButton.id = "toggleProteinButton"; toggleProteinButton.textContent = "Hide Target Protein"; let proteinIsHidden = false; toggleProteinButton.onclick = function() { proteinIsHidden = !proteinIsHidden; viewer.addStyle({model: 0, hetflag: false}, {cartoon: {hidden: proteinIsHidden} }); toggleProteinButton.textContent = proteinIsHidden ? "Show Target Protein" : "Hide Target Protein"; viewer.render(); }; container.appendChild(toggleProteinButton); } }).fail(function(error) { console.error("Error loading molecule:", error); }); } catch (error) { console.error("An error occurred:", error); } } """