Purpose
Retrieve bid-plan documents from Excelerator Takeoff/Online Plan Service instead of Cal eProcure, using a project name or search phrase and returning the document paths, URLs, sizes, and page counts.
When to Use
Use when the caller has a bid/project name but not the site's opaque project identifier, and needs the associated plans, specifications, or other bid documents from an authenticated Online Plan Service account.
Workflow
- Open
https://login.onlineplanservice.com/Login. If the consent dialog appears, accept it, then authenticate with credentials supplied by the caller or the existing browser session; never hard-code credentials. - While authenticated on the login origin, resolve the project ID with one request to:
https://login.onlineplanservice.com/ajax_grid_datasource.aspx?mode=biddingprojects&search={URL-encoded query}&page=1&sort=biddate&dir=ASCParse the returned records, select the project whose name/title matches{project-name}(or the most specific intended result), and read its opaque project identifier, such as aNOPS...code. If the endpoint indicates more pages, request subsequentpage={n}values until the matching project is found. - Construct the viewer URL directly—without visiting the project grid or clicking through the result:
https://exceleratortakeoff.com/Viewer/{project-id}/indexNavigate there in the same authenticated browser context and wait until the document tree has populated. - Run this evaluator once on the loaded viewer page:
(() => { const clean = (v) => String(v ?? "") .replace(/<!--!-->/g, "") .trim(); const treeEl = document.querySelector("#tree"); if (!treeEl || !window.jQuery || !window.jQuery.fn.fancytree) { return { projectId: location.pathname.match(/\/Viewer\/([^/]+)/i)?.[1] || null, documents: [], error: "Fancytree document tree is not available", }; } const tree = window.jQuery(treeEl).fancytree("getTree"); const documents = []; const pathOf = (node) => { const parts = []; for (let p = node.parent; p && p.title; p = p.parent) parts.unshift(clean(p.title)); return parts.join("/"); }; tree.visit((node) => { const content = node.data && node.data.content; if (content && content.Url) { documents.push({ path: pathOf(node), title: clean(node.title), url: new URL(content.Url, location.href).href, size: content.Size ?? null, pages: content.Pages ?? null, }); } }); return { projectId: location.pathname.match(/\/Viewer\/([^/]+)/i)?.[1] || null, documents, }; })();
Site-Specific Gotchas
- The project identifier is opaque; do not guess it from the project name. Resolve it through the authenticated
ajax_grid_datasource.aspxsearch endpoint first. - Authentication and consent occur on
login.onlineplanservice.com, while the final viewer is onexceleratortakeoff.com; preserve the same browser session when navigating between them. - The useful document metadata is stored in the Fancytree node's
data.contentobject, especiallyUrl,Size, andPages; folder nodes generally lackcontent.Urland must be excluded. - The tree is mounted at
#treeand may require a short load wait before evaluation. The direct/Viewer/{project-id}/indexURL avoids the slower project-grid and document-viewer click sequence.
Expected Output
Return the resolved project ID and a documents array. Each document contains its folder path, displayed title, downloadable URL, size, and page count; return an empty array only when the authenticated tree has no document nodes.