PageSourceSearch

https://phfd.co/core-content/js/PushNotifications.js?d017c21ceddd

js phfd.co collected 2026-09-30 13:57:33 UTC 7,008 bytes, 196 lines download raw bytes

1function urlB64ToUint8Array(base64String) {
2    const padding = '='.repeat((4 - base64String.length % 4) % 4);
3    const base64 = (base64String + padding)
4      .replace(/\-/g, '+')
5      .replace(/_/g, '/');
6  
7    const rawData = window.atob(base64);
8    const outputArray = new Uint8Array(rawData.length);
9  
10    for (let i = 0; i < rawData.length; ++i) {
11      outputArray[i] = rawData.charCodeAt(i);
12    }
13    return outputArray;
14  }
15
16class PushNotifications {
17    STATE_INITIALIZING = 0;
18    STATE_UNSUPPORTED = 1;
19    STATE_READY = 2;
20    STATE_WORKING = 3;
21    STATE_DENIED = 4;
22    STATE_FAILED_PUB_KEY = 5;
23    STATE_OTHER_ERROR = 6;
24
25    constructor() {
26        this.vapid_key_endpoint = "/api/notifications/resources/vapid-pub-key";
27        this.applicationServerPublicKey = null;
28        this.swRegistration = null;
29        this.subscription = null;
30        this.lastState = null;
31        this.disabled  = true;
32        this.innerHTML = "Initializing...";
33        this.initialize();
34    }
35
36    async initialize() {
37        await this.uiState(this.STATE_INITIALIZING);
38        if('serviceWorker' in navigator === false) return this.uiState(this.STATE_UNSUPPORTED);
39        if('PushManager' in window === false) return this.uiState(this.STATE_UNSUPPORTED);
40
41        try{ 
42            const swReg = await navigator.serviceWorker.register('/ServiceWorker.js')
43            console.log("Service Worker is registered", swReg);
44            this.swRegistration = swReg;
45            await this.uiState(this.STATE_READY);
46        } catch(error) {
47            console.error('Service Worker Error', error);
48            await this.uiState(this.STATE_UNSUPPORTED);
49        }
50    }
51
52    async uiState(status = null) {
53        if(status !== null) this.lastState = status;
54        else status = this.lastState;
55
56        switch(status) {
57            case this.STATE_INITIALIZING:
58                this.disabled = true;
59                this.innerHTML = "<loading-spinner></loading-spinner> One moment...";
60                break;
61            case this.STATE_UNSUPPORTED:
62                this.disabled = true;
63                this.innerHTML = "Unsupported Browser";
64                break; 
65            case this.STATE_READY:
66                this.disabled = false;
67                const sub = await this.swRegistration.pushManager.getSubscription();
68                this.subscription = !(sub === null);
69                this.innerHTML =  `${(this.subscription) ? "Disable" : "Enable"} Push Notifications`;
70                break;
71            case this.STATE_WORKING:
72                this.disabled = true;
73                this.innerHTML = "<loading-spinner></loading-spinner> Working...";
74                break;
75            case this.STATE_DENIED: 
76                this.disabled = true;
77                this.innerHTML = "Missing permission <help-span value='Please enable the notification permission for this site to continue.'></help-span>";
78                this.status = "denied";
79                break;
80            case this.STATE_FAILED_PUB_KEY:
81                this.disabled = false;
82                this.innerHTML = "Failed to fetch public key";
83                break;
84            case this.STATE_OTHER_ERROR:
85            default:
86                this.disabled = false;
87                this.innerHTML = "Unknown Error";
88                break;
89        }
90
91        document.body.dispatchEvent(new CustomEvent("PushNotificationStateUpdate"));
92    }
93
94    toggleSubscription() {
95        this.uiState(this.STATE_WORKING);
96        if(!this.subscription) return this.enroll();
97        return this.unsub();
98    }
99
100    async enroll() {
101        try {
102            const api = new AsyncFetch(this.vapid_key_endpoint, "GET", {});
103            this.applicationServerPublicKey = await api.get();
104        } catch (error) {
105            console.error("Failed to fetch this application's public key");
106            this.uiState(this.STATE_FAILED_PUB_KEY);
107        }
108
109        let applicationServerKey;
110        let subscription;
111        try {
112            applicationServerKey = urlB64ToUint8Array(this.applicationServerPublicKey);
113        } catch(error) {
114            console.error(error);
115            this.uiState(this.STATE_OTHER_ERROR);
116            return;
117        }
118        try {
119            subscription = await this.swRegistration.pushManager.subscribe({
120                userVisibleOnly: true,
121                applicationServerKey: applicationServerKey
122            });
123        } catch (error) {
124            console.error(error);
125            this.uiState(this.STATE_OTHER_ERROR);
126            return;
127        }
128        
129        try{
130            await this.updateSubscriptionOnServer(subscription, "subscribed");
131            this.subscription = true;
132            this.uiState(this.STATE_READY);
133        } catch (error) {
134            console.error("Failed to enroll", error);
135            this.uiState(this.STATE_OTHER_ERROR);
136        }
137    }
138
139    async unsub() {
140        const subscription = await this.swRegistration.pushManager.getSubscription();
141        try{ 
142            subscription.unsubscribe();
143        } catch (error) {
144            console.error("There was an error unsubscribing", error);
145        }
146        this.updateSubscriptionOnServer(subscription, "unsubscribed");
147        this.status = false;
148        this.uiState("ready");
149    }
150
151    updateSubscriptionOnServer(subscription, state = "enrolled", userId = null) {
152        if(!userId) userId = "me";
153        const api = new AsyncFetch(`/api/v1/user/${userId}/push/enrollment/${state}`,"POST",{});
154        return api.submit(subscription);
155    }
156}
157
158class PushNotificationEnrollmentButton extends CustomButton {
159    connectedCallback() {
160        this.innerHTML = "<loading-spinner></loading-spinner> Loading...";
161        super.connectedCallback();
162        this.disabled = true;
163
164        this.actionMenu = this.parentNode.querySelector("action-menu");
165        this.optionsTest = this.actionMenu?.querySelector("option[name='test']");
166
167        this.addEventListener("click", () => {
168            window.Cobalt.PushNotificationInstance.toggleSubscription();
169        });
170        document.body.addEventListener("PushNotificationStateUpdate", this.updateSelf.bind(this));
171        this.updateSelf();
172    }
173
174    disconnectedCallback() {
175        // document.body.removeEventListener("PushNotificationStateUpdate", this.updateSelf.bind(this));
176    }
177
178    updateSelf() {
179        this.disabled  = window.Cobalt.PushNotificationInstance.disabled;
180        this.innerHTML = window.Cobalt.PushNotificationInstance.innerHTML || "Oops!";
181        console.log(this.optionsTest);
182        this.optionsTest.disabled = this.disabled;
183    }
184
185    get disabled() {
186        return this.hasAttribute("disabled");
187    }
188
189    set disabled(val) {
190        if(val) this.setAttribute("disabled", "disabled");
191        else this.removeAttribute("disabled");
192    }
193}
194
195window.Cobalt.PushNotificationInstance = new PushNotifications();
196customElements.define("push-enrollment-button", PushNotificationEnrollmentButton);

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.