PageSourceSearch

https://dr.eka.care/shipyard.js

js eka.care collected 2026-10-01 15:08:25 UTC 13,688 bytes, 352 lines download raw bytes

1// Sticky showversion: once enabled via ?showversion=true it is persisted to localStorage, so the
2// version label survives the host app's history.replace (which strips the query string) and later
3// full reloads. ?showversion=false turns it back off. (A build may still force it on by pre-setting
4// window.shipyard.showversion — that override wins via the spread below.)
5const resolveShowversion = () => {
6    const sv = new URLSearchParams(window.location.search).get('showversion');
7    if (sv === 'true') localStorage.setItem('shipyard_showversion', 'true');
8    else if (sv === 'false') localStorage.removeItem('shipyard_showversion');
9    return localStorage.getItem('shipyard_showversion') === 'true';
10};
11
12window.shipyard = {
13    showversion: resolveShowversion(),
14    defaultVersion: 'main',
15    ...(window.shipyard || {})
16};
17
18const getLsKey = (key) => `shipyard_${key}`
19
20const getQueryParamKey = (key = "version") => {
21    return key.replace("shipyard_", "");
22}
23
24const getCurrentVersion = (key = "version") => {
25    // get from query params if present
26    const queryParamVersion = getQueryParamKey(getLsKey(key)) && new URLSearchParams(window.location.search).get(getQueryParamKey(getLsKey(key)));
27
28    if (queryParamVersion) {
29        setVersion(key, queryParamVersion, true);
30        return queryParamVersion;
31    }
32    
33    return localStorage.getItem(getLsKey(key)) || window.shipyard.defaultVersion;
34}
35
36window.freezeVersion = (key = "version") => {
37    setVersion(key, getCurrentVersion(key), true);
38    injectVersionLabel();
39}
40
41// Helper function to get all shipyard keys from localStorage
42window.getAllShipyardVersions = () => {
43    const versions = {};
44    for (let i = 0; i < localStorage.length; i++) {
45        const key = localStorage.key(i);
46        if (key && key.startsWith('shipyard_')) {
47            const displayKey = key.replace('shipyard_', '');
48            versions[displayKey] = localStorage.getItem(key);
49        }
50    }
51    return versions;
52}
53
54window.setVersion = (key = "version", version, avoidRedirect) => {
55    // Ensure we always use the localStorage key format
56    const storageKey = key.startsWith('shipyard_') ? key : getLsKey(key);
57    localStorage.setItem(storageKey, version);
58    if (!avoidRedirect && window.shipyard.redirectUrl) {
59        // Get all shipyard versions from localStorage
60        const allVersions = window.getAllShipyardVersions();
61        
62        // Build query parameters for all versions
63        const queryParams = Object.entries(allVersions)
64            .map(([versionKey, versionValue]) => `${versionKey}=${encodeURIComponent(versionValue)}`)
65            .join('&');
66        
67        // Construct the redirect URL with all version parameters
68        const separator = window.shipyard.redirectUrl.includes('?') ? '&' : '?';
69        const targetUrl = window.shipyard.redirectUrl + (queryParams ? `${separator}${queryParams}` : '');
70        
71        // Prevent redirect loops by checking if we're already on the target URL
72        // Extract the path and query from both current and target URLs
73        const currentPath = window.location.pathname;
74        const currentParams = new URLSearchParams(window.location.search);
75        
76        const targetUrlObj = new URL(targetUrl, window.location.origin);
77        const targetPath = targetUrlObj.pathname;
78        const targetParams = new URLSearchParams(targetUrlObj.search);
79        
80        // Check if we're redirecting to the same path with identical version parameters
81        let sameDestination = (currentPath === targetPath);
82        if (sameDestination) {
83            // Compare all version parameters
84            Object.keys(allVersions).forEach(versionKey => {
85                if (currentParams.get(versionKey) !== targetParams.get(versionKey)) {
86                    sameDestination = false;
87                }
88            });
89        }
90        
91        if (!sameDestination) {
92            console.log('Shipyard redirecting to:', targetUrl);
93            window.location.href = targetUrl;
94        } else {
95            console.log('Shipyard redirect loop prevented - already at target destination');
96        }
97    }
98}
99
100window.getVersion = (key = "version") => {
101    const queryParamVersion = getQueryParamKey(getLsKey(key)) && new URLSearchParams(window.location.search).get(getQueryParamKey(getLsKey(key)));
102
103    if (queryParamVersion) {
104        setVersion(key, queryParamVersion, true);
105        return queryParamVersion;
106    }
107    return localStorage.getItem(getLsKey(key)) || window.shipyard.defaultVersion;
108}
109
110let currentVersion = window.getVersion();
111
112window.injectVersionLabel = () => {
113    // Only inject if showversion is true
114    if (!window.shipyard.showversion) {
115        return;
116    }
117
118    // Check if label already exists to avoid duplicates
119    if (document.getElementById('version-label')) {
120        // remove from DOM
121        document.getElementById('version-label').remove();
122        return;
123    }
124
125    const versionLabel = document.createElement('div');
126    versionLabel.id = 'version-label';
127    
128    // Show all versions in the label
129    const allVersions = window.getAllShipyardVersions();
130    const versionText = Object.keys(allVersions).length > 0 
131        ? Object.entries(allVersions).map(([key, value]) => `[${getQueryParamKey(key)}]: ${value}`).join(', ')
132        : `[version]: ${window.getVersion()}`;
133    
134    versionLabel.innerHTML = `<span style="font-size: 10px;">v</span>${versionText}`;
135
136    // Styling for bottom left positioning
137    versionLabel.style.cssText = `
138        position: fixed;
139        bottom: 0px;
140        left: 0px;
141        background-color: rgba(0, 0, 0, 0.7);
142        color: white;
143        padding: 4px 8px;
144        border-radius: 4px;
145        font-family: monospace;
146        font-size: 12px;
147        z-index: 9999;
148        pointer-events: auto;
149        user-select: none;
150        cursor: pointer;
151        transition: background-color 0.2s;
152        max-width: 80vw;
153        word-wrap: break-word;
154    `;
155
156    // Add hover effect
157    versionLabel.addEventListener('mouseenter', () => {
158        versionLabel.style.backgroundColor = 'rgba(0, 0, 0, 0.9)';
159    });
160
161    versionLabel.addEventListener('mouseleave', () => {
162        versionLabel.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
163    });
164
165    // Add click handler to open version change popup
166    versionLabel.addEventListener('click', () => {
167        const allVersions = window.getAllShipyardVersions();
168        
169        // Create custom dialog
170        const dialog = document.createElement('div');
171        dialog.id = 'version-dialog';
172        dialog.style.cssText = `
173            position: fixed;
174            top: 0;
175            left: 0;
176            width: 100%;
177            height: 100%;
178            background-color: rgba(0, 0, 0, 0.5);
179            display: flex;
180            justify-content: center;
181            align-items: center;
182            z-index: 10000;
183        `;
184
185        const dialogContent = document.createElement('div');
186        dialogContent.style.cssText = `
187            background-color: white;
188            padding: 20px;
189            border-radius: 8px;
190            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
191            min-width: 400px;
192            max-width: 80vw;
193            max-height: 80vh;
194            overflow-y: auto;
195            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
196        `;
197
198        // Build the dialog content with multiple version inputs
199        let dialogHTML = `
200            <h3 style="margin: 0 0 15px 0; font-size: 16px; color: #333;">Change Versions</h3>
201        `;
202
203        // If no versions exist, create default version entry
204        if (Object.keys(allVersions).length === 0) {
205            allVersions['version'] = window.getVersion();
206        }
207
208        let versionInputsHTML = '';
209        Object.entries(allVersions).forEach(([key, value]) => {
210            versionInputsHTML += `
211                <div style="margin-bottom: 15px; padding: 15px; border: 1px solid #e0e0e0; border-radius: 6px; background-color: #f9f9f9;">
212                    <div style="display: flex; align-items: center; gap: 12px;">
213                        <label style="font-weight: 600; color: #333; min-width: 80px; font-size: 14px;">${getQueryParamKey(key)}:</label>
214                        <input type="text" id="version-input-${key}" value="${value}" style="
215                            flex: 1;
216                            padding: 8px 12px;
217                            border: 1px solid #ddd;
218                            border-radius: 4px;
219                            font-size: 14px;
220                            margin-right: 8px;
221                        " placeholder="Enter version">
222                        <button class="apply-single-btn" data-key="${key}" style="
223                            background-color: #28a745;
224                            color: white;
225                            border: none;
226                            padding: 8px 16px;
227                            border-radius: 4px;
228                            cursor: pointer;
229                            font-size: 13px;
230                            font-weight: 500;
231                            margin-right: 6px;
232                            transition: background-color 0.2s;
233                        ">Apply</button>
234                        <button class="use-latest-single-btn" data-key="${key}" style="
235                            background-color: #007bff;
236                            color: white;
237                            border: none;
238                            padding: 8px 16px;
239                            border-radius: 4px;
240                            cursor: pointer;
241                            font-size: 13px;
242                            font-weight: 500;
243                            transition: background-color 0.2s;
244                        ">Use Latest</button>
245                    </div>
246                </div>
247            `;
248        });
249
250        dialogHTML += versionInputsHTML;
251
252        dialogHTML += `
253            <div style="display: flex; gap: 10px; justify-content: flex-end; margin-top: 20px; padding-top: 15px; border-top: 1px solid #e0e0e0;">
254                <button id="cancel-btn" style="
255                    background-color: #f0f0f0;
256                    color: #333;
257                    border: none;
258                    padding: 10px 20px;
259                    border-radius: 4px;
260                    cursor: pointer;
261                    font-size: 14px;
262                ">Cancel</button>
263                <button id="use-all-latest-btn" style="
264                    background-color: #007acc;
265                    color: white;
266                    border: none;
267                    padding: 10px 20px;
268                    border-radius: 4px;
269                    cursor: pointer;
270                    font-size: 14px;
271                ">Use All Latest</button>
272            </div>
273        `;
274
275        dialogContent.innerHTML = dialogHTML;
276        dialog.appendChild(dialogContent);
277        document.body.appendChild(dialog);
278
279        const cancelBtn = document.getElementById('cancel-btn');
280        const useAllLatestBtn = document.getElementById('use-all-latest-btn');
281
282        const closeDialog = () => {
283            document.body.removeChild(dialog);
284        };
285
286        const updateVersionLabel = () => {
287            const updatedVersions = window.getAllShipyardVersions();
288            const versionText = Object.keys(updatedVersions).length > 0 
289                ? Object.entries(updatedVersions).map(([key, value]) => `[${key}]: ${value}`).join(', ')
290                : `[version]: ${window.getVersion()}`;
291            versionLabel.innerHTML = `<span style="font-size: 10px;">v</span>${versionText}`;
292        };
293
294        // Handle individual apply buttons
295        document.querySelectorAll('.apply-single-btn').forEach(btn => {
296            btn.addEventListener('click', () => {
297                const key = btn.dataset.key;
298                const input = document.getElementById(`version-input-${key}`);
299                const newVersion = input.value.trim();
300                if (newVersion !== '') {
301                    window.setVersion(key, newVersion, false);
302                    updateVersionLabel();
303                }
304            });
305        });
306
307        // Handle individual use latest buttons
308        document.querySelectorAll('.use-latest-single-btn').forEach(btn => {
309            btn.addEventListener('click', () => {
310                const key = btn.dataset.key;
311                const input = document.getElementById(`version-input-${key}`);
312                input.value = 'main';
313                window.setVersion(key, 'main', false);
314                updateVersionLabel();
315            });
316        });
317
318        // Handle use all latest button
319        useAllLatestBtn.addEventListener('click', () => {
320            Object.keys(allVersions).forEach(key => {
321                window.setVersion(key, 'main', false);
322            });
323            updateVersionLabel();
324            closeDialog();
325        });
326
327        cancelBtn.addEventListener('click', closeDialog);
328
329        // Close on escape key
330        dialog.addEventListener('keydown', (e) => {
331            if (e.key === 'Escape') {
332                closeDialog();
333            }
334        });
335
336        // Close on backdrop click
337        dialog.addEventListener('click', (e) => {
338            if (e.target === dialog) {
339                closeDialog();
340            }
341        });
342    });
343
344    document.body.appendChild(versionLabel);
345}
346
347// Auto-inject when DOM is ready
348if (document.readyState === 'loading') {
349    document.addEventListener('DOMContentLoaded', window.injectVersionLabel);
350} else {
351    window.injectVersionLabel();
352}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.