From c4265ff94c4faad7907c48ed7d25bcd689e2ff76 Mon Sep 17 00:00:00 2001 From: Spencer Alger Date: Sun, 15 Dec 2013 13:17:34 -0700 Subject: [PATCH] flat merge of both notification and filter support --- README.md | 1 + background.js | 89 ++++++++++++++++++++++++++++---- options.html | 38 +++++++++++++- options.js | 140 +++++++++++++++++++++++++++++++++++++++++++------- popup.js | 8 +-- 5 files changed, 244 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 686d25c..cc04571 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ You can [install](https://chrome.google.com/extensions/detail/lnalnbkkohdcnaapee ### Version 0.3.2 * Merged Pull Request #7 from Nick Doyle (Manifest v2 & updated styling) +* add desktop notifications ### Version 0.3.1 diff --git a/background.js b/background.js index 25504aa..ba4f9c6 100644 --- a/background.js +++ b/background.js @@ -1,6 +1,7 @@ // Prefs var refreshTime; var requestUrl; +var filterPattern; var auth; var desc; var green; @@ -9,27 +10,35 @@ var green; var abortTimer; var refreshTimer; var jobs; +var previousJobsByName; var xhr; +var showNotification; function init() { desc = localStorage.desc == 'true'; green = localStorage.green == 'true'; + showNotification = localStorage.showNotification == 'true'; + if (!localStorage.url) { updateStatus(-1); jobs = null; return; } else requestUrl = localStorage.url; - + refreshTime = localStorage.refreshTime || REFRESH_DEFAULT; refreshTime *= 60 * 1000; // minutes in ms - + if (typeof localStorage.username == 'string') { auth = window.btoa((localStorage.username || '') + ':' + (localStorage.password || '')); } else { auth = null; } - + + if (typeof localStorage.filterText === 'string') { + filterPattern = new RegExp(localStorage.filterText, localStorage.filterCaseSensitive ? '' : 'i'); + } + doRequest(); } @@ -40,7 +49,7 @@ function doRequest() { xhr = new XMLHttpRequest(); window.clearTimeout(abortTimer); abortTimer = window.setTimeout(xhr.abort, REQUEST_TIMEOUT); - + try { xhr.onreadystatechange = checkResponse; xhr.onerror = handleError; @@ -54,21 +63,83 @@ function doRequest() { } } + +// Displays a notification popup +function notify(message) { + try { + var notification = webkitNotifications.createNotification("./images/icon48.png", "Hudson", message); + var timeout = 15; + + notification.show(); + if( localStorage.showNotificationTime ) { + setTimeout(function () { notification.cancel(); }, localStorage.showNotificationTime * 1000); + } + + } catch (e) { + alert("error displaying notification"); + } +} + + function checkResponse() { if (xhr.readyState != 4) return; - + if (xhr.status == 200 && xhr.responseText) { var response = JSON.parse(xhr.responseText); var topStatus = -1; if (response.jobs) { - jobs = response.jobs; + jobs = response.jobs.filter(function (job) { + if (filterPattern) { + return !!filterPattern.test(job.name); + } else { + return true; + } + }); + + if (previousJobsByName) { + var jobsByName = {}; + var msgs = jobs + // find the jobs with status changes and reindex the jobs + .map(function (job) { + var previousJob = previousJobsByName[job.name]; + jobsByName[job.name] = job; + if (!previousJob) { + return job.name + ' was added'; + } else if (job.color !== previousJob.color) { + switch(job.color) { + case 'green_anime': + case 'blue_anime': + case 'green': + case 'blue': + return job.name + ' is now passing!'; + case 'red_anime': + case 'red': + return job.name + ' is now failing!'; + } + } + }) + // filter out empty messages + .filter(Boolean); + + if(msgs.length) { + notify(msgs.join('\n')); + } + previousJobsByName = jobsByName; + } else { + previousJobsByName = {}; + jobs.forEach(function (job) { + previousJobsByName[job.name] = job; + }); + } + if (localStorage.sorting == 'status') { jobs.sort(sortByStatus); } else { jobs.sort(sortByName); } - for (var i in response.jobs) - topStatus = Math.max(topStatus, STATUSES[response.jobs[i].color]); + jobs.forEach(function (job) { + topStatus = Math.max(topStatus, STATUSES[job.color]); + }); } handleSuccess(topStatus); return; @@ -97,7 +168,7 @@ function handleError(error) { function sortByName(a, b) { var o1 = a.name.toLowerCase(); var o2 = b.name.toLowerCase(); - + if (o1 < o2) return desc ? 1 : -1; else if (o2 < o1) return desc ? -1 : 1; else return 0; diff --git a/options.html b/options.html index 9de753b..9403a57 100644 --- a/options.html +++ b/options.html @@ -19,7 +19,6 @@ -

Hudson Monitor

@@ -52,6 +51,24 @@

Hudson Monitor

+ +
+ Desktop Notifications + + + + + +
+ +
@@ -95,6 +112,24 @@

Hudson Monitor

+
+ Filter by job name + + + + + + + + +
+

+ The filter text is converted into a regular expression. Only things that match + the expression are displayed in the popup and considered for the main status + image. +

+
+
License @@ -102,4 +137,3 @@

Hudson Monitor

- diff --git a/options.js b/options.js index 50ae5df..474bb05 100644 --- a/options.js +++ b/options.js @@ -1,3 +1,5 @@ +/* jshint eqeqeq:false, -W041: false, undef: true, browser:true */ +/* global REFRESH_DEFAULT, REQUEST_TIMEOUT, STATUSES, chrome, webkitNotifications */ /* Copyright 2010 Henning Hoefer @@ -14,27 +16,37 @@ limitations under the License. */ var saveButton; +var cancelButton; var urlInput; var errorImage; var refreshDropdown; var authCheckbox; var usernameInput; var passwordInput; +var filterCheckbox; +var filterText; +var filterCaseSensitive; var sortByName; var sortByStatus; var sortDesc; +var greenBalls; +var notificationCheckbox; +var notificationTimeout; function init() { + var inputs; + var i; + // All text inputs onKeyup = makeDirty - var inputs = document.querySelectorAll('input[type=text]'); - for (var i = 0; i < inputs.length; i++) { + inputs = document.querySelectorAll('input[type=text]'); + for (i = 0; i < inputs.length; i++) { inputs[i].addEventListener('keyup', markDirty); } // All other inputs onChange = makeDirty - var inputs = document.querySelectorAll('input[type=password], input[type=checkbox], input[type=radio], select'); - for (var i = 0; i < inputs.length; i++) { + inputs = document.querySelectorAll('input[type=password], input[type=checkbox], input[type=radio], select'); + for (i = 0; i < inputs.length; i++) { inputs[i].addEventListener('change', markDirty); } @@ -44,7 +56,7 @@ function init() { saveButton.addEventListener('click', save); cancelButton.addEventListener('click', init); - + urlInput = document.getElementById('url'); errorImage = document.getElementById('error'); urlInput.value = localStorage.url || 'http://'; @@ -53,16 +65,33 @@ function init() { } else { errorImage.style.visibility = 'hidden'; } - + refreshDropdown = document.getElementById('refresh'); var refreshTime = localStorage.refreshTime || REFRESH_DEFAULT; - for (var i = 0; i < refreshDropdown.options.length; i++) { + for (i = 0; i < refreshDropdown.options.length; i++) { if (refreshDropdown.options[i].value == refreshTime) { refreshDropdown.selectedIndex = i; break; } } - + + notificationCheckbox = document.getElementById('showNotifications'); + notificationTimeout = document.getElementById('notificationTimeout'); + if (localStorage.showNotification == 'true') { + notificationCheckbox.checked = true; + notificationTimeout.disabled = false; + } else { + notificationCheckbox.checked = false; + notificationTimeout.disabled = true; + } + if (localStorage.notificationTimeout) { + notificationTimeout.options.forEach(function (option, i) { + if (option.value == localStorage.notificationTimeout) { + notificationTimeout.selectedIndex = i; + } + }); + } + authCheckbox = document.getElementById('auth'); usernameInput = document.getElementById('username'); passwordInput = document.getElementById('password'); @@ -77,7 +106,22 @@ function init() { usernameInput.disabled = true; passwordInput.disabled = true; } - + + filterCheckbox = document.getElementById('enableFilter'); + filterText = document.getElementById('filterText'); + filterCaseSensitive = document.getElementById('filterCaseSensitive'); + if (typeof localStorage.filterText == 'string') { + filterCheckbox.checked = true; + filterText.value = localStorage.filterText || ''; + filterCaseSensitive.checked = !!localStorage.filterCaseSensitive; + } else { + filterCheckbox.checked = false; + filterText.value = ''; + filterCaseSensitive.checked = false; + filterText.disabled = true; + filterCaseSensitive.disabled = true; + } + sortByName = document.getElementById('sortByName'); sortByStatus = document.getElementById('sortByStatus'); sortDesc = document.getElementById('sortDesc'); @@ -88,11 +132,11 @@ function init() { } if (typeof localStorage.desc == 'string') sortDesc.checked = true; - + greenBalls = document.getElementById('greenBalls'); if (typeof localStorage.green == 'string') greenBalls.checked = true; - + markClean(); } @@ -102,13 +146,19 @@ function save() { } else { delete localStorage.url; } - + + if (notificationTimeout.value != "0") { + localStorage.notificationTimeout = notificationTimeout.value; + } else { + delete localStorage.notificationTimeout; + } + if (refreshDropdown.value != REFRESH_DEFAULT) { localStorage.refreshTime = refreshDropdown.value; } else { delete localStorage.refreshTime; } - + if (authCheckbox.checked) { localStorage.username = usernameInput.value; localStorage.password = passwordInput.value; @@ -116,7 +166,15 @@ function save() { delete localStorage.username; delete localStorage.password; } - + + if (filterCheckbox.checked) { + localStorage.filterText = filterText.value; + localStorage.filterCaseSensitive = filterCaseSensitive.value ? 'true' : ''; + } else { + delete localStorage.filterText; + delete localStorage.filterCaseSensitive; + } + if (sortByStatus.checked == true) { localStorage.sorting = 'status'; } else { @@ -127,13 +185,19 @@ function save() { } else { delete localStorage.desc; } - + if (greenBalls.checked == true) { localStorage.green = 'true'; } else { delete localStorage.green; } - + + if (notificationCheckbox.checked == true) { + localStorage.showNotification = 'true'; + } else { + delete localStorage.showNotification; + } + init(); chrome.extension.getBackgroundPage()["init"](); } @@ -144,7 +208,7 @@ function markDirty() { } else { errorImage.style.visibility = 'hidden'; } - + if (authCheckbox.checked == true) { usernameInput.disabled = false; passwordInput.disabled = false; @@ -152,7 +216,22 @@ function markDirty() { usernameInput.disabled = true; passwordInput.disabled = true; } - + + if (notificationCheckbox.checked == true) { + requestUserPermission(); + notificationTimeout.disabled = false; + } else { + notificationTimeout.disabled = true; + } + + if (filterCheckbox.checked == true) { + filterText.disabled = false; + filterCaseSensitive.disabled = false; + } else { + filterText.disabled = true; + filterCaseSensitive.disabled = true; + } + saveButton.disabled = false; } @@ -160,4 +239,29 @@ function markClean() { saveButton.disabled = true; } +function requestUserPermission() { + try { + if (notificationCheckbox.checked) { + if (checkUserPermission()) + return; + + if (typeof webkitNotifications != "undefined") { + webkitNotifications.requestPermission(function () { + notificationCheckbox.checked = checkUserPermission(); + }); + } + } + } catch (e) { + notificationCheckbox.checked = false; + } +} + +function checkUserPermission() { + try { + return (webkitNotifications.checkPermission() === 0); + } catch (e) { + return false; + } +} + document.addEventListener('DOMContentLoaded', init); diff --git a/popup.js b/popup.js index efc3bd4..912fd61 100644 --- a/popup.js +++ b/popup.js @@ -23,15 +23,15 @@ function init() { document.getElementById('refresh').addEventListener('click', function() {console.log('ok');bg['doRequest']();window.location.reload();}); document.getElementById('monitor').addEventListener('click', function() {bg['openTab']();window.close();}); - + if (bg.jobs) { - for (var i in bg.jobs) { + bg.jobs.forEach(function (job, i) { var tr = table.insertRow(i); var sym = tr.insertCell(0); sym.innerHTML = getImage(STATUSES[bg.jobs[i].color]); var lnk = tr.insertCell(1); lnk.innerHTML = bg.jobs[i].name.link(bg.jobs[i].url); - } + }); } else { var p = document.createElement('p'); p.innerHTML = 'Please check the configuration.'; @@ -57,4 +57,6 @@ function getImage(status) { } } + + document.addEventListener('DOMContentLoaded', init);