NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name Cello Extract with One click Copy and Scrollable Table
// @namespace http://tampermonkey.net/
// @version 5.1
// @description Display all rows with a star (*) on the second page in a scrollable table format. Copy functionality ensures data from the Short Form column is pasted into a single Excel cell per index. Scan the entire page for Gene Name after SeqID:, and place each Gene Name in its respective row, adding ">" before each Gene Name without space. Adds notification on successful copy and fixed title on the table.
// @author Faisal Ahmed
// @license MIT
// @match http://cello.life.nctu.edu.tw/
// @match http://cello.life.nctu.edu.tw/cgi/main.cgi
// @grant none
// ==/UserScript==
(function () {
'use strict';
// Function to show a brief notification for 1 second
function showNotification(message) {
let notification = document.createElement('div');
notification.innerText = message;
notification.style.position = 'fixed';
notification.style.top = '50px';
notification.style.right = '10px';
notification.style.backgroundColor = 'green';
notification.style.color = 'white';
notification.style.padding = '10px';
notification.style.borderRadius = '5px';
notification.style.zIndex = '9999';
document.body.appendChild(notification);
// Remove the notification after 1 second
setTimeout(() => {
notification.remove();
}, 1000);
}
// Mapping of full forms to short forms with Unicode superscript "a"
const fullToShortForm = {
"Extracellular": "Exᵃ",
"Cytoplasmic": "Cyᵃ",
"Lysosomal": "Lyᵃ",
"Vacuole": "Vᵃ",
"Chloroplast": "Chᵃ",
"Nuclear": "Nuᵃ",
"Golgi": "Gᵃ",
"ER": "ERᵃ",
"Mitochondrial": "Mᵃ",
"InnerMembrane": "IMᵃ",
"OuterMembrane": "OMᵃ",
"Periplasmic": "Periᵃ",
"PlasmaMembrane": "Pmᵃ"
};
// Function to scan the entire page and extract all Gene Names after "SeqID:"
function extractAllGeneNames() {
let pageText = document.body.innerText.split('\n'); // Split the entire page text into lines
console.log("Page text for scanning Gene Names:", pageText);
let geneNames = [];
// Look for all lines that contain "SeqID:" and extract the gene names
for (let line of pageText) {
if (line.includes("SeqID:")) {
let geneName = line.split("SeqID:")[1].split("|")[0].trim(); // Extract Gene Name after SeqID: and before |
console.log("Extracted Gene Name:", geneName);
geneNames.push(geneName); // Store each extracted Gene Name
}
}
return geneNames; // Return the list of all extracted Gene Names
}
function handleSecondPage() {
let geneNames = extractAllGeneNames(); // Extract all Gene Names from the page
let lines = document.body.innerText.split('\n'); // Split the page text into individual lines
let sequences = []; // Array to store sequences
let currentSequence = []; // Store the current sequence being processed
// Search for all lines, grouping them by sequence (assuming empty lines separate sequences)
lines.forEach((line) => {
if (line.trim() === "") {
if (currentSequence.length > 0) {
sequences.push(currentSequence); // Push current sequence to the sequences array
currentSequence = []; // Reset current sequence
}
} else {
currentSequence.push(line.trim()); // Add line to current sequence
}
});
// Handle last sequence if not empty
if (currentSequence.length > 0) {
sequences.push(currentSequence);
}
// Prepare data with star (*) containing lines for each sequence
let sequenceData = sequences.map((sequence) => {
return sequence.filter(line => {
let starCount = (line.match(/\*/g) || []).length; // Count the number of * symbols in the line
return starCount > 0 && starCount <= 3; // Include lines with 1 to 3 stars only
});
}).filter(data => data.length > 0); // Keep only sequences with star-containing lines
if (sequenceData.length > 0) {
// Create a scrollable table element
let tableContainer = document.createElement('div');
tableContainer.style.position = 'fixed';
tableContainer.style.top = '10px';
tableContainer.style.right = '10px';
tableContainer.style.width = '400px';
tableContainer.style.height = '300px'; // Limit the table height to 300px
tableContainer.style.overflowY = 'auto'; // Add vertical scrolling
tableContainer.style.border = '1px solid #ccc'; // Softer border color
tableContainer.style.backgroundColor = '#B0B0B0'; // Ash background color
tableContainer.style.borderRadius = '10px'; // Rounded corners
tableContainer.style.boxShadow = '0px 4px 12px rgba(0, 0, 0, 0.1)'; // Subtle shadow for depth
tableContainer.style.zIndex = '9999';
tableContainer.style.transition = 'all 0.3s ease'; // Smooth transition for interactions
// Create the title element with rainbow animation
let title = document.createElement('div');
title.innerText = 'Made By Faisal Ahmed';
title.style.position = 'sticky';
title.style.top = '0';
title.style.backgroundImage = 'linear-gradient(90deg, red, orange, yellow, green, blue, indigo, violet)';
title.style.backgroundSize = '200%'; // Make the gradient larger for movement
title.style.backgroundClip = 'text'; // Apply gradient only to text
title.style.color = 'transparent'; // Hide the background
title.style.textAlign = 'center';
title.style.padding = '5px';
title.style.fontWeight = 'bold';
title.style.zIndex = '1000'; // Ensure it stays on top
title.style.borderBottom = '1px solid #ccc';
// Add the animation to the title
title.style.animation = 'rainbowAnimation 5s linear infinite'; // Slower and right-to-left animation
// Append the title to the table container
tableContainer.appendChild(title);
// Create the table inside the scrollable container
let table = document.createElement('table');
table.style.width = '100%'; // Make the table take up full width of the container
// Create table headers with copy buttons
let headerRow = document.createElement('tr');
let th1 = document.createElement('th');
let th2 = document.createElement('th');
let th3 = document.createElement('th');
// Create Copy Buttons for each column
let copyGeneNameButton = document.createElement('button');
copyGeneNameButton.innerText = 'Copy Gene Name';
copyGeneNameButton.style.margin = '5px';
copyGeneNameButton.addEventListener('click', function () {
copyColumnData(0); // Copy the first column (Gene Name)
});
let copyShortFormButton = document.createElement('button');
copyShortFormButton.innerText = 'Copy Short Form';
copyShortFormButton.style.margin = '5px';
copyShortFormButton.addEventListener('click', function () {
copyColumnData(1); // Copy the second column (Short Form)
});
let copyDataButton = document.createElement('button');
copyDataButton.innerText = 'Copy Data';
copyDataButton.style.margin = '5px';
copyDataButton.addEventListener('click', function () {
copyColumnData(2); // Copy the third column (Data)
});
th1.appendChild(copyGeneNameButton);
th2.appendChild(copyShortFormButton);
th3.appendChild(copyDataButton);
headerRow.appendChild(th1);
headerRow.appendChild(th2);
headerRow.appendChild(th3);
table.appendChild(headerRow);
// Populate the table with sequence-related star data
sequenceData.forEach((data, index) => {
let row = document.createElement('tr');
let geneNameCell = document.createElement('td');
// Assign each gene name to the respective row (1st gene name -> 1st row, 2nd gene name -> 2nd row)
let geneName = geneNames[index] || 'N/A'; // Handle cases where there are fewer gene names than rows
geneNameCell.innerText = `>${geneName}`; // Add ">" directly before the Gene Name without any space
geneNameCell.style.border = '1px solid black';
let shortFormCell = document.createElement('td');
shortFormCell.innerText = generateShortForm(data); // Create the correct short form for each line in the sequence
shortFormCell.style.border = '1px solid black';
let dataCell = document.createElement('td');
dataCell.innerText = data.join('\n'); // Join multiple lines into a single cell
dataCell.style.border = '1px solid black';
row.appendChild(geneNameCell); // Append Gene Name to the first column
row.appendChild(shortFormCell);
row.appendChild(dataCell);
table.appendChild(row);
});
// Append the table to the container
tableContainer.appendChild(table);
// Append the container to the body
document.body.appendChild(tableContainer);
// Function to copy column data to the clipboard
function copyColumnData(columnIndex) {
let tableRows = table.querySelectorAll('tr');
let columnData = Array.from(tableRows).slice(1) // Skip the header row
.map(row => row.cells[columnIndex].innerText.replace(/\n/g, ' ')) // Replace newlines with spaces for Excel single-cell entries
.join('\n');
// Create a temporary textarea element to copy the content
let tempTextarea = document.createElement('textarea');
tempTextarea.value = columnData;
document.body.appendChild(tempTextarea);
tempTextarea.select();
document.execCommand('copy');
document.body.removeChild(tempTextarea);
// Show a brief notification when copied
showNotification(`${['Gene Name', 'Short Form', 'Data'][columnIndex]} column copied!`);
}
// Function to generate a short form using Unicode superscript "a" for each line in the sequence
function generateShortForm(sequence) {
return sequence.map(line => {
let fullForm = Object.keys(fullToShortForm).find(form => line.includes(form));
if (fullForm) {
return fullToShortForm[fullForm]; // Return the short form with Unicode superscript
}
return ''; // If no match is found, return an empty string
}).join(', '); // Join multiple entries with commas to keep them in a single Excel cell
}
} else {
console.log("No lines with a valid number of stars (*) found.");
}
}
// Handle the second page
handleSecondPage();
// CSS for rainbow animation
const style = document.createElement('style');
style.innerHTML = `
@keyframes rainbowAnimation {
0% { background-position: 200%; }
100% { background-position: 0%; }
}
`;
document.head.appendChild(style);
})();