4 Likes
Where is the source for users visiting the chrome addon?
Do you mean source code ?
(() => {
βuse strictβ;
// ββ Constants ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
const BUTTON_ID = "zerodha-expand-collapse-btn";
const SELECTORS = {
section : "section.open-positions",
header : "header.data-table-header",
icons : "span.expand-icon",
expanded: "expanded",
};
const COLORS = {
expand : "#2e7d32", // green
collapse: "#c62828", // red
};
const LABELS = {
expand : "Expand All",
collapse: "Collapse All",
};
// ββ Helpers ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
/**
* Applies a plain object of style properties to an element.
* Avoids inline style strings; keeps styling declarative and easy to diff.
*/
function applyStyles(el, styles) {
Object.assign(el.style, styles);
}
/** Creates the button with all attributes set via safe DOM APIs (no innerHTML). */
function createButton() {
const btn = document.createElement("button");
btn.id = BUTTON_ID;
btn.textContent = LABELS.expand; // textContent β XSS-safe
btn.setAttribute("aria-pressed", "false");
btn.setAttribute("title", LABELS.expand);
applyStyles(btn, {
marginLeft : "10px",
padding : "3px 8px",
cursor : "pointer",
borderRadius: "4px",
border : "none",
fontWeight : "500",
fontSize : "12px",
color : "white",
background : COLORS.expand,
transition : "background 0.2s ease",
});
return btn;
}
/** Syncs button label, colour, and ARIA state to the current expanded flag. */
function syncButton(btn, isExpanded) {
const label = isExpanded ? LABELS.collapse : LABELS.expand;
btn.textContent = label;
btn.setAttribute("aria-pressed", String(isExpanded));
btn.setAttribute("title", label);
applyStyles(btn, { background: isExpanded ? COLORS.collapse : COLORS.expand });
}
/**
* Derives the true expanded state from the DOM.
* Returns true only if every icon is currently expanded.
*/
function areAllExpanded() {
const icons = document.querySelectorAll(SELECTORS.icons);
if (!icons.length) return false;
return [...icons].every(icon => icon.classList.contains(SELECTORS.expanded));
}
// ββ Core logic βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
function injectButton() {
// Idempotency β bail if already injected (handles SPA re-renders)
if (document.getElementById(BUTTON_ID)) return;
const section = document.querySelector(SELECTORS.section);
if (!section) return;
const header = section.querySelector(SELECTORS.header);
if (!header) return;
const btn = createButton();
btn.addEventListener("click", () => {
try {
const icons = document.querySelectorAll(SELECTORS.icons);
if (!icons.length) return;
// Read actual DOM state β not an internal flag
const currentlyExpanded = areAllExpanded();
icons.forEach(icon => {
const isExpanded = icon.classList.contains(SELECTORS.expanded);
if (!currentlyExpanded && !isExpanded) icon.click();
if ( currentlyExpanded && isExpanded) icon.click();
});
// Disable during settle window β prevents double-click race condition
btn.disabled = true;
// Defer DOM read β Zerodha updates classes asynchronously after icon.click()
setTimeout(() => {
// Guard β button may have been removed if user navigated away
if (!document.getElementById(BUTTON_ID)) return;
syncButton(btn, areAllExpanded());
btn.disabled = false;
}, 50);
} catch (err) {
// Absorb errors so a broken icon never bricks the button
btn.disabled = false;
console.warn("[Zerodha Ext] Error toggling positions:", err);
}
});
header.appendChild(btn);
// Defer injection sync β Zerodha's classes may not have settled on page load
setTimeout(() => {
if (!document.getElementById(BUTTON_ID)) return;
syncButton(btn, areAllExpanded());
}, 50);
// Button is live β stop watching for it
domObserver.disconnect();
// Watch only the header for button removal (SPA navigation away)
// When removed, restart the main observer to wait for it again
removalObserver.observe(header, { childList: true });
}
// ββ Observers ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// Watches for the button being removed from the DOM (user navigated away)
const removalObserver = new MutationObserver((mutations) => {
const removed = mutations.some(m =>
[...m.removedNodes].some(node => node.id === BUTTON_ID)
);
if (removed) {
removalObserver.disconnect();
// Resume watching for the section to reappear
domObserver.observe(document.body, { childList: true, subtree: true });
}
});
// Watches for the positions section to appear, then injects the button
const domObserver = new MutationObserver(() => {
if (!window.location.pathname.includes("positions")) return;
injectButton();
});
domObserver.observe(document.body, {
childList: true,
subtree : true,
});
// Also attempt immediately in case the DOM is already ready
injectButton();
})();
Yeah⦠You said you can examine every line of source. Right? How do end users do that? Did you put the code in Github?
