PageSourceSearch

https://01intern.com/js/keep-service.js?ver=202608061

js 01intern.com collected 2026-10-02 14:59:24 UTC 7,313 bytes, 169 lines download raw bytes

1/**
2 * KeepService — framework-agnostic job "keep" (favorite) logic.
3 * Mirrors the Angular User.addFavorite/favoriteDelete + LocalKeepService +
4 * NotificationsService behaviour so Vue pages behave identically.
5 *
6 * window.KeepService.toggle(jobId, currentlyKept) -> Promise<boolean> (new state)
7 */
8(function () {
9    'use strict';
10
11    var AUTH_COOKIE_KEY = 'zosid';
12    var LOCAL_KEEP_STORE_KEY = 'keepstore';
13
14    function isLoggedIn() {
15        var checkKey = AUTH_COOKIE_KEY + '=';
16        var cookies = decodeURIComponent(document.cookie).split('; ');
17        return cookies.some(function (cookie) {
18            return cookie.indexOf(checkKey) === 0;
19        });
20    }
21
22    // --- guest keep store (same shape as Angular LocalKeepService) ---
23    function readStore() {
24        var data = window.localStorage.getItem(LOCAL_KEEP_STORE_KEY);
25        return data ? JSON.parse(data) : [];
26    }
27    function writeStore(keeps) {
28        window.localStorage.setItem(LOCAL_KEEP_STORE_KEY, JSON.stringify(keeps));
29    }
30    function localIds() {
31        return readStore().map(function (k) { return k.jobId; });
32    }
33    function localAdd(jobId) {
34        var keeps = readStore();
35        if (!keeps.some(function (k) { return k.jobId === jobId; })) {
36            var now = new Date();
37            keeps.push({
38                added_at: [now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), now.getMinutes(), now.getSeconds()],
39                jobId: jobId
40            });
41            writeStore(keeps);
42        }
43    }
44    function localRemove(jobId) {
45        writeStore(readStore().filter(function (k) { return k.jobId !== jobId; }));
46    }
47
48    // --- header keep-count badge (ported from Angular NotificationsService) ---
49    function incrementHeaderCount(loggedIn) {
50        var keepCountPc = document.getElementById('keep-count-pc');
51        var keepCountSpNav = document.getElementById('keep-link-sp-nav');
52        if (keepCountPc == null) {
53            var href = loggedIn ? '/user/me/favorite/form' : '/keeps';
54            var keepNotificationPc = '<a target="_self" href="' + href + '" id="keep-count-pc" class="i-btn i-btn--small i-btn-border--yellow i-btn-icon--star i-text-yellow">1</a>';
55            var keepNotificationSp = '<a target="_self" href="' + href + '" id="keep-count-sp" class="i-btn i-btn--small i-btn-border--yellow i-btn-icon--star i-text-yellow">1</a>';
56            var unreadCountPc = document.getElementById('unread-count-pc');
57            if (unreadCountPc == null) {
58                var naviPc = document.getElementById('header-navi-pc');
59                var naviSp = document.getElementById('header-navi-sp');
60                if (naviPc) { naviPc.children[0].insertAdjacentHTML('afterend', keepNotificationPc); }
61                if (naviSp) { naviSp.insertAdjacentHTML('afterbegin', keepNotificationSp); }
62            } else {
63                unreadCountPc.insertAdjacentHTML('afterend', keepNotificationPc);
64                var unreadCountSp = document.getElementById('unread-count-sp');
65                if (unreadCountSp) { unreadCountSp.insertAdjacentHTML('afterend', keepNotificationSp); }
66            }
67            if (keepCountSpNav) {
68                keepCountSpNav.children[1].insertAdjacentHTML('afterend', '<span id="keep-count-sp-nav" class="i-nav__link-alert i-save">1</span>');
69            }
70        } else {
71            var next = parseInt(keepCountPc.innerText, 10) + 1;
72            keepCountPc.innerText = next.toString();
73            var sp = document.getElementById('keep-count-sp');
74            if (sp) { sp.innerText = next.toString(); }
75            var spNav = document.getElementById('keep-count-sp-nav');
76            if (spNav) { spNav.innerText = next.toString(); }
77        }
78    }
79    function decrementHeaderCount() {
80        var keepCountPc = document.getElementById('keep-count-pc');
81        var keepCountSp = document.getElementById('keep-count-sp');
82        var keepCountSpNav = document.getElementById('keep-count-sp-nav');
83        if (keepCountPc != null) {
84            var next = parseInt(keepCountPc.innerText, 10) - 1;
85            if (next === 0) {
86                keepCountPc.remove();
87                if (keepCountSp) { keepCountSp.remove(); }
88                if (keepCountSpNav) { keepCountSpNav.remove(); }
89            } else {
90                keepCountPc.innerText = next.toString();
91                if (keepCountSp) { keepCountSp.innerText = next.toString(); }
92                if (keepCountSpNav) { keepCountSpNav.innerText = next.toString(); }
93            }
94        }
95    }
96
97    // --- server calls (same endpoints as Angular User resource) ---
98    // POST /user/me/favorite reads jobId from the QUERY string (@QueryParam), matching
99    // the Angular $resource's `jobId:'@jobId'` param mapping — not the request body.
100    function addServer(jobId) {
101        return fetch('/user/me/favorite?jobId=' + encodeURIComponent(jobId), {
102            method: 'POST',
103            credentials: 'same-origin'
104        }).then(function (res) {
105            return res.json().catch(function () { return {}; }).then(function (body) {
106                return { ok: res.ok, body: body };
107            });
108        });
109    }
110    function removeServer(jobId) {
111        return fetch('/user/me/favorite/delete/' + jobId, {
112            method: 'DELETE',
113            credentials: 'same-origin'
114        }).then(function (res) {
115            return res.json().catch(function () { return {}; }).then(function (body) {
116                return { ok: res.ok, body: body };
117            });
118        });
119    }
120
121    function redirectIfUnauthenticated(body) {
122        if (body && body.code === 9) {
123            window.location.href = body.redirect || '/user/login/form';
124            return true;
125        }
126        return false;
127    }
128
129    /**
130     * Toggle keep state for a job. Returns a Promise resolving to the new
131     * kept-state (true/false). On failure, rejects (caller keeps old state).
132     * `loggedIn` should be the server's authoritative login state; if omitted,
133     * falls back to the (less reliable) cookie check.
134     */
135    function toggle(jobId, currentlyKept, loggedIn) {
136        if (loggedIn === undefined) { loggedIn = isLoggedIn(); }
137        if (loggedIn) {
138            if (!currentlyKept) {
139                return addServer(jobId).then(function (r) {
140                    if (redirectIfUnauthenticated(r.body)) { return currentlyKept; }
141                    if (!r.ok) { throw new Error('addFavorite failed'); }
142                    incrementHeaderCount(true);
143                    return true;
144                });
145            }
146            return removeServer(jobId).then(function (r) {
147                if (redirectIfUnauthenticated(r.body)) { return currentlyKept; }
148                if (!r.ok) { throw new Error('favoriteDelete failed'); }
149                decrementHeaderCount();
150                return false;
151            });
152        }
153        // guest
154        if (!currentlyKept) {
155            localAdd(jobId);
156            incrementHeaderCount(false);
157            return Promise.resolve(true);
158        }
159        localRemove(jobId);
160        decrementHeaderCount();
161        return Promise.resolve(false);
162    }
163
164    window.KeepService = {
165        isLoggedIn: isLoggedIn,
166        localIds: localIds,
167        toggle: toggle
168    };
169})();

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.