Purpose
Retrieve bid-plan documents from Excelerator Takeoff/Online Plan Service instead of Cal eProcure, using a project name, search phrase, or known project identifier and returning document paths, URLs, sizes, page counts, and likely instruction/pre-bid document candidates.
When to Use
Use when the caller needs plans, specifications, notices, instructions, addenda, or other bid documents for an Online Plan Service project. The caller may provide a project name or search phrase without the site's opaque project identifier, or may already provide the identifier.
Workflow
- Open
https://login.onlineplanservice.com/Login.aspx. If the consent dialog appears, accept it, then authenticate with credentials supplied by the caller or the existing browser session; never hard-code credentials. - If only a project name or search phrase is available, resolve the opaque project ID with one authenticated request to:
https://login.onlineplanservice.com/ajax_grid_datasource.aspx?mode=biddingprojects&search={URL-encoded query}&page=1&sort=biddate&dir=ASCParse the JSON response, select the record whose project name/title best matches{project-name}, and read its project identifier, such as aVOPS...code. If more pages are indicated, request subsequentpage={n}values until the intended match is found. Do not guess the identifier. - Prefer the direct embedded-data document page when the project ID is known:
https://login.onlineplanservice.com/DocumentViewer.aspx?project={project-id}Navigate there in the authenticated context and run the evaluator below once after the page loads. This page embedswindow.data, including project metadata and the hierarchicalFancyNodeDatadocument tree. - If the embedded-data page is unavailable or lacks the document tree, navigate directly to:
https://exceleratortakeoff.com/Viewer/{project-id}/indexWait for#treeto populate and run the Fancytree evaluator below. Preserve the authenticated browser session across the login and viewer origins. - To find mandatory job-walk language, inspect or download the returned candidate documents whose titles or paths match terms such as
instruction,notice,bidder,prebid,pre-bid,mandatory,conference,walk,general condition, or Division 00. Treat these as candidates rather than assuming a matching filename proves the language is mandatory; extract the actual wording from the document when requested.
Embedded document-page evaluator (run on the loaded DocumentViewer.aspx page):
(() => {
const clean = v => String(v ?? '').replace(/<!--!-->/g, '').trim();
const html = document.documentElement.outerHTML;
const match = html.match(/window\\.data\\s*=\\s*(\\{[\\s\\S]+?\\});/);
if (!match) return { projectId: new URL(location.href).searchParams.get('project'), documents: [], error: 'Embedded window.data was not found' };
let data;
try { data = JSON.parse(match[1]); } catch (e) { return { projectId: new URL(location.href).searchParams.get('project'), documents: [], error: 'Unable to parse embedded project data: ' + e.message }; }
const documents = [];
const walk = (node, parents) => {
if (!node) return;
const title = clean(node.Title);
const path = title ? parents.concat(title) : parents;
const item = node.Data || node.data || {};
if (node.Folder === false && item.Url) {
const folder = path.slice(0, -1).join('/');
const titleText = title;
documents.push({ path: folder, title: titleText, url: new URL(item.Url, location.href).href, size: item.Size ?? null, pages: item.Pages ?? null, likelyRelevant: /instruction|notice|bidder|prebid|pre-bid|mandatory|conference|walk|general condition|division\\s*0/i.test(folder + ' ' + titleText) });
}
for (const child of (node.Children || [])) walk(child, path);
};
walk(data.FancyNodeData, []);
return { projectId: data.Opsplannum || new URL(location.href).searchParams.get('project'), projectName: data.ProjectName || null, projectDetails: data.ProjectDetails || null, documents };
})()Fancytree fallback evaluator (run on the loaded /Viewer/{project-id}/index 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) {
const path = pathOf(node), title = clean(node.title);
documents.push({ path, title, url: new URL(content.Url, location.href).href, size: content.Size ?? null, pages: content.Pages ?? null, likelyRelevant: /instruction|notice|bidder|prebid|pre-bid|mandatory|conference|walk|general condition|division\\s*0/i.test(path + ' ' + title) });
}
});
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. DocumentViewer.aspx?project={project-id}embedswindow.datacontainingOpsplannum, project metadata, and the recursiveFancyNodeDatatree; this is a faster extraction path than clicking through the project grid.- In embedded data, leaf documents have
Folder === falseandData.Url; folder nodes generally lack a document URL and must be excluded. - In the Excelerator viewer, the tree is mounted at
#treeand may require a short load wait before evaluation. Its useful metadata is in each Fancytree node'sdata.content, especiallyUrl,Size, andPages. - Filename/path matching only identifies likely documents. Mandatory job-walk requirements must be verified from the document text itself, not inferred solely from a title.
Expected Output
Return the resolved project ID and a documents array. Each document contains its folder path, displayed title, downloadable URL, size, page count, and a likelyRelevant flag for instruction, notice, bidder, pre-bid, mandatory-walk, conference, or related documents. Include project name/details when available. Return an empty array only when the authenticated source has no document nodes.