From a8cdc6c3ea21fdf1673a6f00711333dedf4ceabe Mon Sep 17 00:00:00 2001 From: Satyam Seth Date: Sun, 22 Dec 2024 18:51:47 +0530 Subject: [PATCH 1/2] restartucture --- Notification API/Levelup Blog/README.md | 5 +++ Notification API/{ => Levelup Blog}/demo.html | 0 Notification API/{ => Levelup Blog}/script.js | 0 Notification API/{ => Levelup Blog}/style.css | 0 Notification API/README.md | 7 --- Notification API/index.html | 43 +++++++++++++++++++ index.html | 2 +- 7 files changed, 49 insertions(+), 8 deletions(-) create mode 100644 Notification API/Levelup Blog/README.md rename Notification API/{ => Levelup Blog}/demo.html (100%) rename Notification API/{ => Levelup Blog}/script.js (100%) rename Notification API/{ => Levelup Blog}/style.css (100%) delete mode 100644 Notification API/README.md create mode 100644 Notification API/index.html diff --git a/Notification API/Levelup Blog/README.md b/Notification API/Levelup Blog/README.md new file mode 100644 index 0000000..c77ee11 --- /dev/null +++ b/Notification API/Levelup Blog/README.md @@ -0,0 +1,5 @@ +# Reference Links + +- MDN: [Notification API](https://developer.mozilla.org/en-US/docs/Web/API/Notifications_API/Using_the_Notifications_API) + +- [medium.com (levelup) Blog](https://levelup.gitconnected.com/creating-browser-notification-in-javascript-79e91bfb76c8) diff --git a/Notification API/demo.html b/Notification API/Levelup Blog/demo.html similarity index 100% rename from Notification API/demo.html rename to Notification API/Levelup Blog/demo.html diff --git a/Notification API/script.js b/Notification API/Levelup Blog/script.js similarity index 100% rename from Notification API/script.js rename to Notification API/Levelup Blog/script.js diff --git a/Notification API/style.css b/Notification API/Levelup Blog/style.css similarity index 100% rename from Notification API/style.css rename to Notification API/Levelup Blog/style.css diff --git a/Notification API/README.md b/Notification API/README.md deleted file mode 100644 index 57b074d..0000000 --- a/Notification API/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Reference Links - -- MDN: [Notification API](https://developer.mozilla.org/en-US/docs/Web/API/Notifications_API/Using_the_Notifications_API) - -- Blogs: - 1. [educative.io](https://www.educative.io/answers/how-to-show-notifications-in-javascript) - 2. [medium.com (levelup)](https://levelup.gitconnected.com/creating-browser-notification-in-javascript-79e91bfb76c8) diff --git a/Notification API/index.html b/Notification API/index.html new file mode 100644 index 0000000..b987218 --- /dev/null +++ b/Notification API/index.html @@ -0,0 +1,43 @@ + + + + + + + Notification API + + + + + Levelup Blog + + diff --git a/index.html b/index.html index 3e721e4..181e896 100644 --- a/index.html +++ b/index.html @@ -52,7 +52,7 @@ Get select2 selected value on change using jquery - Notification API + Notification API Debouncing Long Press Event Date: Sun, 22 Dec 2024 21:06:01 +0530 Subject: [PATCH 2/2] Add Notification API Example --- Notification API/MDN Doc/demo.html | 120 ++++++++ Notification API/MDN Doc/img/icon-128.png | Bin 0 -> 3316 bytes Notification API/MDN Doc/scripts.js | 360 ++++++++++++++++++++++ Notification API/MDN Doc/style.css | 247 +++++++++++++++ Notification API/index.html | 1 + 5 files changed, 728 insertions(+) create mode 100644 Notification API/MDN Doc/demo.html create mode 100644 Notification API/MDN Doc/img/icon-128.png create mode 100644 Notification API/MDN Doc/scripts.js create mode 100644 Notification API/MDN Doc/style.css diff --git a/Notification API/MDN Doc/demo.html b/Notification API/MDN Doc/demo.html new file mode 100644 index 0000000..102f124 --- /dev/null +++ b/Notification API/MDN Doc/demo.html @@ -0,0 +1,120 @@ + + + + + + + To-do list with Notifications + + + + + + +

To-do list

+ +
+
    +
    + +
    +

    Add new to-do item.

    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    +
    +
    +
    + +
    +
      + + +
      + + diff --git a/Notification API/MDN Doc/img/icon-128.png b/Notification API/MDN Doc/img/icon-128.png new file mode 100644 index 0000000000000000000000000000000000000000..dd5b5dca4dde60e20517fa6bf2811a9b44ffdaa6 GIT binary patch literal 3316 zcmds3`8O2&_kYifee9GylO^H_Q9@y?Wl4h}Nf;Sq$ufi?CMNrm#DmEGRo1aZk|j^c zo_H)34JBmCzC^q~o`2)}I_KWkIroQq?!D)A@44|fi;G+wq8tDKE|W`!R)`a2~(XcKZ*3>{+A*_vtG*?(n`c#(>E9_xu5iCi9B|fn9>O{q(%@Qgs3WG&9 z2f6+*&kAO{%{>W%=~18aqSrG0VQMOTK!WL);X{019u=k%H`3gu9tG?tJHRAUNDb@A zNbE_Eg0ZIXzH4pPLr5ikOZ0^IZTiy4<4Ivti9TER9kR2tVU%H2deT(Pohhm(3^#ih zN3KbUhQXxrl=y$*-C>v>46)k|H*1S0_20j_OY(%In^WP$`k1@hftD~(KRG$MI6eZB zF3gjzK<|rFfrRc5ALy;w7V4D|9nlx)N~Kb1Wd9XU6X>P}U97%&U4jJX+;BhWqB-Sp zy*|JzhwR<%djsCkq4~P!kvzk~!YcgjlS2HVz49j)-FI$g5N}oCWBHJDGsnjxDlh
      Tj6Bi?W`co={&48k|gW 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 M7+Dxr>pMsN4|EESQ2+n{ literal 0 HcmV?d00001 diff --git a/Notification API/MDN Doc/scripts.js b/Notification API/MDN Doc/scripts.js new file mode 100644 index 0000000..4ba41b6 --- /dev/null +++ b/Notification API/MDN Doc/scripts.js @@ -0,0 +1,360 @@ +window.onload = () => { + const MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; + + // Hold an instance of a db object for us to store the IndexedDB data in + let db; + + // Create a reference to the notifications list in the bottom of the app; we will write database messages into this list by + // appending list items as children of this element + const note = document.getElementById('notifications'); + + // All other UI elements we need for the app + const taskList = document.getElementById('task-list'); + const taskForm = document.getElementById('task-form'); + const title = document.getElementById('title'); + const hours = document.getElementById('deadline-hours'); + const minutes = document.getElementById('deadline-minutes'); + const day = document.getElementById('deadline-day'); + const month = document.getElementById('deadline-month'); + const year = document.getElementById('deadline-year'); + const notificationBtn = document.getElementById('enable'); + + // Do an initial check to see what the notification permission state is + if (Notification.permission === 'denied' || Notification.permission === 'default') { + notificationBtn.style.display = 'block'; + } else { + notificationBtn.style.display = 'none'; + } + + note.appendChild(createListItem('App initialised.')); + + // Let us open our database + const DBOpenRequest = window.indexedDB.open('toDoList', 4); + + // Register two event handlers to act on the database being opened successfully, or not + DBOpenRequest.onerror = (event) => { + note.appendChild(createListItem('Error loading database.')); + }; + + DBOpenRequest.onsuccess = (event) => { + note.appendChild(createListItem('Database initialised.')); + + // Store the result of opening the database in the db variable. This is used a lot below + db = DBOpenRequest.result; + + // Run the displayData() function to populate the task list with all the to-do list data already in the IndexedDB + displayData(); + }; + + // This event handles the event whereby a new version of the database needs to be created + // Either one has not been created before, or a new version number has been submitted via the + // window.indexedDB.open line above + //it is only implemented in recent browsers + DBOpenRequest.onupgradeneeded = (event) => { + db = event.target.result; + + db.onerror = (event) => { + note.appendChild(createListItem('Error loading database.')); + }; + + // Create an objectStore for this database + const objectStore = db.createObjectStore('toDoList', { keyPath: 'taskTitle' }); + + // Define what data items the objectStore will contain + objectStore.createIndex('hours', 'hours', { unique: false }); + objectStore.createIndex('minutes', 'minutes', { unique: false }); + objectStore.createIndex('day', 'day', { unique: false }); + objectStore.createIndex('month', 'month', { unique: false }); + objectStore.createIndex('year', 'year', { unique: false }); + + objectStore.createIndex('notified', 'notified', { unique: false }); + + note.appendChild(createListItem('Object store created.')); + }; + + function displayData() { + // First clear the content of the task list so that you don't get a huge long list of duplicate stuff each time + // the display is updated. + while (taskList.firstChild) { + taskList.removeChild(taskList.lastChild); + } + + // Open our object store and then get a cursor list of all the different data items in the IDB to iterate through + const objectStore = db.transaction('toDoList').objectStore('toDoList'); + objectStore.openCursor().onsuccess = (event) => { + const cursor = event.target.result; + // Check if there are no (more) cursor items to iterate through + if (!cursor) { + // No more items to iterate through, we quit. + note.appendChild(createListItem('Entries all displayed.')); + return; + } + + // Check which suffix the deadline day of the month needs + const { hours, minutes, day, month, year, notified, taskTitle } = cursor.value; + const ordDay = ordinal(day); + + // Build the to-do list entry and put it into the list item. + const toDoText = `${taskTitle} — ${hours}:${minutes}, ${month} ${ordDay} ${year}.`; + const listItem = createListItem(toDoText); + + if (notified === 'yes') { + listItem.style.textDecoration = 'line-through'; + listItem.style.color = 'rgba(255, 0, 0, 0.5)'; + } + + // Put the item item inside the task list + taskList.appendChild(listItem); + + // Create a delete button inside each list item, + const deleteButton = document.createElement('button'); + listItem.appendChild(deleteButton); + deleteButton.textContent = 'X'; + + // Set a data attribute on our delete button to associate the task it relates to. + deleteButton.setAttribute('data-task', taskTitle); + + // Associate action (deletion) when clicked + deleteButton.onclick = (event) => { + deleteItem(event); + }; + + // continue on to the next item in the cursor + cursor.continue(); + }; + }; + + // Add listener for clicking the submit button + taskForm.addEventListener('submit', addData, false); + + function addData(e) { + // Prevent default, as we don't want the form to submit in the conventional way + e.preventDefault(); + + // Stop the form submitting if any values are left empty. + // This should never happen as there is the required attribute + if (title.value === '' || hours.value === null || minutes.value === null || day.value === '' || month.value === '' || year.value === null) { + note.appendChild(createListItem('Data not submitted — form incomplete.')); + return; + } + + // Grab the values entered into the form fields and store them in an object ready for being inserted into the IndexedDB + const newItem = [ + { taskTitle: title.value, hours: hours.value, minutes: minutes.value, day: day.value, month: month.value, year: year.value, notified: 'no' }, + ]; + + // Open a read/write DB transaction, ready for adding the data + const transaction = db.transaction(['toDoList'], 'readwrite'); + + // Report on the success of the transaction completing, when everything is done + transaction.oncomplete = () => { + note.appendChild(createListItem('Transaction completed: database modification finished.')); + + // Update the display of data to show the newly added item, by running displayData() again. + displayData(); + }; + + // Handler for any unexpected error + transaction.onerror = () => { + note.appendChild(createListItem(`Transaction not opened due to error: ${transaction.error}`)); + }; + + // Call an object store that's already been added to the database + const objectStore = transaction.objectStore('toDoList'); + console.log(objectStore.indexNames); + console.log(objectStore.keyPath); + console.log(objectStore.name); + console.log(objectStore.transaction); + console.log(objectStore.autoIncrement); + + // Make a request to add our newItem object to the object store + const objectStoreRequest = objectStore.add(newItem[0]); + objectStoreRequest.onsuccess = (event) => { + + // Report the success of our request + // (to detect whether it has been succesfully + // added to the database, you'd look at transaction.oncomplete) + note.appendChild(createListItem('Request successful.')); + + // Clear the form, ready for adding the next entry + title.value = ''; + hours.value = null; + minutes.value = null; + day.value = 01; + month.value = 'January'; + year.value = 2020; + }; + }; + + function deleteItem(event) { + // Retrieve the name of the task we want to delete + const dataTask = event.target.getAttribute('data-task'); + + // Open a database transaction and delete the task, finding it by the name we retrieved above + const transaction = db.transaction(['toDoList'], 'readwrite'); + transaction.objectStore('toDoList').delete(dataTask); + + // Report that the data item has been deleted + transaction.oncomplete = () => { + // Delete the parent of the button, which is the list item, so it is no longer displayed + event.target.parentNode.parentNode.removeChild(event.target.parentNode); + note.appendChild(createListItem(`Task "${dataTask}" deleted.`)); + }; + }; + + // Check whether the deadline for each task is up or not, and responds appropriately + function checkDeadlines() { + // First of all check whether notifications are enabled or denied + if (Notification.permission === 'denied' || Notification.permission === 'default') { + notificationBtn.style.display = 'block'; + } else { + notificationBtn.style.display = 'none'; + } + + // Grab the current time and date + const now = new Date(); + + // From the now variable, store the current minutes, hours, day of the month, month, year and seconds + const minuteCheck = now.getMinutes(); + const hourCheck = now.getHours(); + const dayCheck = now.getDate(); // Do not use getDay() that returns the day of the week, 1 to 7 + const monthCheck = now.getMonth(); + const yearCheck = now.getFullYear(); // Do not use getYear() that is deprecated. + + // Open a new transaction + const objectStore = db.transaction(['toDoList'], 'readwrite').objectStore('toDoList'); + + // Open a cursor to iterate through all the data items in the IndexedDB + objectStore.openCursor().onsuccess = (event) => { + const cursor = event.target.result; + if (!cursor) return; + const { hours, minutes, day, month, year, notified, taskTitle } = cursor.value; + + // convert the month names we have installed in the IDB into a month number that JavaScript will understand. + // The JavaScript date object creates month values as a number between 0 and 11. + const monthNumber = MONTHS.indexOf(month); + if (monthNumber === -1) throw new Error('Incorrect month entered in database.'); + + // Check if the current hours, minutes, day, month and year values match the stored values for each task. + // The parseInt() function transforms the value from a string to a number for comparison + // (taking care of leading zeros, and removing spaces and underscores from the string). + let matched = parseInt(hours) === hourCheck; + matched &&= parseInt(minutes) === minuteCheck; + matched &&= parseInt(day) === dayCheck; + matched &&= parseInt(monthNumber) === monthCheck; + matched &&= parseInt(year) === yearCheck; + if (matched && notified === 'no') { + // If the numbers all do match, run the createNotification() function to create a system notification + // but only if the permission is set + if (Notification.permission === 'granted') { + createNotification(taskTitle); + } + } + + // Move on to the next cursor item + cursor.continue(); + }; + }; + + // Ask for permission when the 'Enable notifications' button is clicked + function askNotificationPermission() { + // Function to actually ask the permissions + function handlePermission(permission) { + // Whatever the user answers, we make sure Chrome stores the information + if (!Reflect.has(Notification, 'permission')) { + Notification.permission = permission; + } + + // Set the button to shown or hidden, depending on what the user answers + if (Notification.permission === 'denied' || Notification.permission === 'default') { + notificationBtn.style.display = 'block'; + } else { + notificationBtn.style.display = 'none'; + } + }; + + // Check if the browser supports notifications + if (!Reflect.has(window, 'Notification')) { + console.log('This browser does not support notifications.'); + } else { + if (checkNotificationPromise()) { + Notification.requestPermission().then(handlePermission); + } else { + Notification.requestPermission(handlePermission); + } + } + }; + + // Check whether browser supports the promise version of requestPermission() + // Safari only supports the old callback-based version + function checkNotificationPromise() { + try { + Notification.requestPermission().then(); + } catch (e) { + return false; + } + + return true; + }; + + // Wire up notification permission functionality to 'Enable notifications' button + notificationBtn.addEventListener('click', askNotificationPermission); + + function createListItem(contents) { + const listItem = document.createElement('li'); + listItem.textContent = contents; + return listItem; + }; + + // Create a notification with the given title + function createNotification(title) { + // Create and show the notification + const img = '/Notification API/MDN Doc/img/icon-128.png'; + const text = `HEY! Your task "${title}" is now overdue.`; + const notification = new Notification('To do list', { body: text, icon: img }); + + // Add event listeners + notification.addEventListener('show', (e) => console.log('Notification Show Event', e)); + notification.addEventListener('click', (e) => console.log('Notification Click Event', e)); + notification.addEventListener('close', (e) => console.log('Notification Close Event', e)); + notification.addEventListener('error', (e) => console.log('Notification Error Event', e)); + + // We need to update the value of notified to 'yes' in this particular data object, so the + // notification won't be set off on it again + + // First open up a transaction + const objectStore = db.transaction(['toDoList'], 'readwrite').objectStore('toDoList'); + + // Get the to-do list object that has this title as its title + const objectStoreTitleRequest = objectStore.get(title); + + objectStoreTitleRequest.onsuccess = () => { + // Grab the data object returned as the result + const data = objectStoreTitleRequest.result; + + // Update the notified value in the object to 'yes' + data.notified = 'yes'; + + // Create another request that inserts the item back into the database + const updateTitleRequest = objectStore.put(data); + + // When this new request succeeds, run the displayData() function again to update the display + updateTitleRequest.onsuccess = () => { + displayData(); + }; + }; + }; + + // Using a setInterval to run the checkDeadlines() function every second + setInterval(checkDeadlines, 1000); +} + +// Helper function returning the day of the month followed by an ordinal (st, nd, or rd) +function ordinal(day) { + const n = day.toString(); + const last = n.slice(-1); + if (last === '1' && n !== '11') return `${n}st`; + if (last === '2' && n !== '12') return `${n}nd`; + if (last === '3' && n !== '13') return `${n}rd`; + return `${n}th`; +}; \ No newline at end of file diff --git a/Notification API/MDN Doc/style.css b/Notification API/MDN Doc/style.css new file mode 100644 index 0000000..ffaca88 --- /dev/null +++ b/Notification API/MDN Doc/style.css @@ -0,0 +1,247 @@ +/* Basic set up + sizing for containers */ + +html, +body { + margin: 0; +} + +html { + width: 100%; + height: 100%; + font-size: 10px; + font-family: Georgia, "Times New Roman", Times, serif; + background: #111; +} + +body { + width: 50rem; + position: relative; + background: #d88; + margin: 0 auto; + border-left: 2px solid #d33; + border-right: 2px solid #d33; +} + +h1, +h2 { + text-align: center; + background: #d88; + font-family: Arial, Helvetica, sans-serif; +} + +h1 { + font-size: 6rem; + margin: 0; + background: #d66; +} + +h2 { + font-size: 2.4rem; +} + +/* Bottom toolbar styling */ + +#toolbar { + position: relative; + height: 6rem; + width: 100%; + background: #d66; + border-top: 2px solid #d33; + border-bottom: 2px solid #d33; +} + +#enable, +input[type="submit"] { + line-height: 1.8; + font-size: 1.3rem; + border-radius: 5px; + border: 1px solid black; + color: black; + text-shadow: 1px 1px 1px black; + border: 1px solid rgba(0, 0, 0, 0.1); + box-shadow: inset 0px 5px 3px rgba(255, 255, 255, 0.2), + inset 0px -5px 3px rgba(0, 0, 0, 0.2); +} + +#enable { + position: absolute; + bottom: 0.3rem; + right: 0.3rem; +} + +#notifications { + margin: 0; + position: relative; + padding: 0.3rem; + background: #ddd; + position: absolute; + top: 0rem; + left: 0rem; + height: 5.4rem; + width: 50%; + overflow: auto; + line-height: 1.2; +} + +#notifications li { + margin-left: 1.5rem; +} + +/* New item form styling */ + +.form-box { + background: #d66; + width: 85%; + padding: 1rem; + margin: 2rem auto; + box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.7); +} + +form div { + margin-bottom: 1rem; +} + +form .full-width { + margin: 1rem auto 2rem; + width: 100%; +} + +form .half-width { + width: 50%; + float: left; +} + +form .third-width { + width: 33%; + float: left; +} + +form div label { + width: 10rem; + float: left; + padding-right: 1rem; + font-size: 1.6rem; + line-height: 1.6; +} + +form .full-width input { + width: 30rem; +} + +form .half-width input { + width: 8.75rem; +} + +form .third-width select { + width: 13.5rem; +} + +form div input[type="submit"] { + clear: both; + width: 20rem; + display: block; + height: 3rem; + margin: 0 auto; + position: relative; + top: 0.5rem; +} + +/* || tasks box */ + +.task-box { + width: 85%; + padding: 1rem; + margin: 2rem auto; + font-size: 1.8rem; +} + +.task-box ul { + margin: 0; + padding: 0; +} + +.task-box li { + list-style-type: none; + padding: 1rem; + border-bottom: 2px solid #d33; +} + +.task-box li:last-child { + border-bottom: none; +} + +.task-box li:last-child { + margin-bottom: 0rem; +} + +.task-box button { + margin-left: 2rem; + font-size: 1.6rem; + border: 1px solid #eee; + border-radius: 5px; + box-shadow: inset 0 -2px 5px rgba(0, 0, 0, 0.5) 1px 1px 1px black; +} + +/* setting cursor for interactive controls */ + +button, +input[type="submit"], +select { + cursor: pointer; +} + +/* media query for small screens */ + +@media (max-width: 32rem) { + body { + width: 100%; + border-left: none; + border-right: none; + } + + form div { + clear: both; + } + + form .full-width { + margin: 1rem auto; + } + + form .half-width { + width: 100%; + float: none; + } + + form .third-width { + width: 100%; + float: none; + } + + form div label { + width: 36%; + padding-left: 1rem; + } + + form input, + form select, + form label { + line-height: 2.5rem; + font-size: 2rem; + } + + form .full-width input { + width: 50%; + } + + form .half-width input { + width: 50%; + } + + form .third-width select { + width: 50%; + } + + #enable { + right: 1rem; + } +} diff --git a/Notification API/index.html b/Notification API/index.html index b987218..0bcd5a0 100644 --- a/Notification API/index.html +++ b/Notification API/index.html @@ -39,5 +39,6 @@ Levelup Blog + MDN Docs