| OLD | NEW |
| 1 /* | 1 /* |
| 2 * This file is part of Adblock Plus <https://adblockplus.org/>, | 2 * This file is part of Adblock Plus <https://adblockplus.org/>, |
| 3 * Copyright (C) 2006-2016 Eyeo GmbH | 3 * Copyright (C) 2006-2016 Eyeo GmbH |
| 4 * | 4 * |
| 5 * Adblock Plus is free software: you can redistribute it and/or modify | 5 * Adblock Plus is free software: you can redistribute it and/or modify |
| 6 * it under the terms of the GNU General Public License version 3 as | 6 * it under the terms of the GNU General Public License version 3 as |
| 7 * published by the Free Software Foundation. | 7 * published by the Free Software Foundation. |
| 8 * | 8 * |
| 9 * Adblock Plus is distributed in the hope that it will be useful, | 9 * Adblock Plus is distributed in the hope that it will be useful, |
| 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 * GNU General Public License for more details. | 12 * GNU General Public License for more details. |
| 13 * | 13 * |
| 14 * You should have received a copy of the GNU General Public License | 14 * You should have received a copy of the GNU General Public License |
| 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. | 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
| 16 */ | 16 */ |
| 17 | 17 |
| 18 "use strict"; |
| 19 |
| 18 /** | 20 /** |
| 19 * @fileOverview Handles notifications. | 21 * @fileOverview Handles notifications. |
| 20 */ | 22 */ |
| 21 | 23 |
| 22 Cu.import("resource://gre/modules/Services.jsm"); | 24 Cu.import("resource://gre/modules/Services.jsm"); |
| 23 | 25 |
| 24 var {Prefs} = require("prefs"); | 26 const {Prefs} = require("prefs"); |
| 25 var {Downloader, Downloadable, MILLIS_IN_MINUTE, MILLIS_IN_HOUR, MILLIS_IN_DAY}
= require("downloader"); | 27 const {Downloader, Downloadable, |
| 26 var {Utils} = require("utils"); | 28 MILLIS_IN_MINUTE, MILLIS_IN_HOUR, MILLIS_IN_DAY} = require("downloader"); |
| 27 var {Matcher, defaultMatcher} = require("matcher"); | 29 const {Utils} = require("utils"); |
| 28 var {Filter, RegExpFilter, WhitelistFilter} = require("filterClasses"); | 30 const {Matcher, defaultMatcher} = require("matcher"); |
| 31 const {Filter, RegExpFilter, WhitelistFilter} = require("filterClasses"); |
| 29 | 32 |
| 30 var INITIAL_DELAY = 1 * MILLIS_IN_MINUTE; | 33 let INITIAL_DELAY = 1 * MILLIS_IN_MINUTE; |
| 31 var CHECK_INTERVAL = 1 * MILLIS_IN_HOUR; | 34 let CHECK_INTERVAL = 1 * MILLIS_IN_HOUR; |
| 32 var EXPIRATION_INTERVAL = 1 * MILLIS_IN_DAY; | 35 let EXPIRATION_INTERVAL = 1 * MILLIS_IN_DAY; |
| 33 var TYPE = { | 36 let TYPE = { |
| 34 information: 0, | 37 information: 0, |
| 35 question: 1, | 38 question: 1, |
| 36 relentless: 2, | 39 relentless: 2, |
| 37 critical: 3 | 40 critical: 3 |
| 38 }; | 41 }; |
| 39 | 42 |
| 40 var showListeners = []; | 43 let showListeners = []; |
| 41 var questionListeners = {}; | 44 let questionListeners = {}; |
| 42 | 45 |
| 43 function getNumericalSeverity(notification) | 46 function getNumericalSeverity(notification) |
| 44 { | 47 { |
| 45 return (notification.type in TYPE ? TYPE[notification.type] : TYPE.information
); | 48 if (notification.type in TYPE) |
| 49 return TYPE[notification.type]; |
| 50 return TYPE.information; |
| 46 } | 51 } |
| 47 | 52 |
| 48 function saveNotificationData() | 53 function saveNotificationData() |
| 49 { | 54 { |
| 50 // HACK: JSON values aren't saved unless they are assigned a different object. | 55 // HACK: JSON values aren't saved unless they are assigned a different object. |
| 51 Prefs.notificationdata = JSON.parse(JSON.stringify(Prefs.notificationdata)); | 56 Prefs.notificationdata = JSON.parse(JSON.stringify(Prefs.notificationdata)); |
| 52 } | 57 } |
| 53 | 58 |
| 54 function localize(translations, locale) | 59 function localize(translations, locale) |
| 55 { | 60 { |
| 56 if (locale in translations) | 61 if (locale in translations) |
| 57 return translations[locale]; | 62 return translations[locale]; |
| 58 | 63 |
| 59 let languagePart = locale.substring(0, locale.indexOf("-")); | 64 let languagePart = locale.substring(0, locale.indexOf("-")); |
| 60 if (languagePart && languagePart in translations) | 65 if (languagePart && languagePart in translations) |
| 61 return translations[languagePart]; | 66 return translations[languagePart]; |
| 62 | 67 |
| 63 let defaultLocale = "en-US"; | 68 let defaultLocale = "en-US"; |
| 64 return translations[defaultLocale]; | 69 return translations[defaultLocale]; |
| 65 } | 70 } |
| 66 | 71 |
| 67 /** | 72 /** |
| 68 * The object providing actual downloading functionality. | 73 * The object providing actual downloading functionality. |
| 69 * @type Downloader | 74 * @type {Downloader} |
| 70 */ | 75 */ |
| 71 var downloader = null; | 76 let downloader = null; |
| 72 var localData = []; | 77 let localData = []; |
| 73 | 78 |
| 74 /** | 79 /** |
| 75 * Regularly fetches notifications and decides which to show. | 80 * Regularly fetches notifications and decides which to show. |
| 76 * @class | 81 * @class |
| 77 */ | 82 */ |
| 78 var Notification = exports.Notification = | 83 let Notification = exports.Notification = |
| 79 { | 84 { |
| 80 /** | 85 /** |
| 81 * Called on module startup. | 86 * Called on module startup. |
| 82 */ | 87 */ |
| 83 init: function() | 88 init() |
| 84 { | 89 { |
| 85 downloader = new Downloader(this._getDownloadables.bind(this), INITIAL_DELAY
, CHECK_INTERVAL); | 90 downloader = new Downloader(this._getDownloadables.bind(this), |
| 91 INITIAL_DELAY, CHECK_INTERVAL); |
| 86 downloader.onExpirationChange = this._onExpirationChange.bind(this); | 92 downloader.onExpirationChange = this._onExpirationChange.bind(this); |
| 87 downloader.onDownloadSuccess = this._onDownloadSuccess.bind(this); | 93 downloader.onDownloadSuccess = this._onDownloadSuccess.bind(this); |
| 88 downloader.onDownloadError = this._onDownloadError.bind(this); | 94 downloader.onDownloadError = this._onDownloadError.bind(this); |
| 89 onShutdown.add(() => downloader.cancel()); | 95 onShutdown.add(() => downloader.cancel()); |
| 90 }, | 96 }, |
| 91 | 97 |
| 92 /** | 98 /** |
| 93 * Yields a Downloadable instances for the notifications download. | 99 * Yields a Downloadable instances for the notifications download. |
| 94 */ | 100 */ |
| 95 _getDownloadables: function*() | 101 *_getDownloadables() |
| 96 { | 102 { |
| 97 let downloadable = new Downloadable(Prefs.notificationurl); | 103 let downloadable = new Downloadable(Prefs.notificationurl); |
| 98 if (typeof Prefs.notificationdata.lastError === "number") | 104 if (typeof Prefs.notificationdata.lastError === "number") |
| 99 downloadable.lastError = Prefs.notificationdata.lastError; | 105 downloadable.lastError = Prefs.notificationdata.lastError; |
| 100 if (typeof Prefs.notificationdata.lastCheck === "number") | 106 if (typeof Prefs.notificationdata.lastCheck === "number") |
| 101 downloadable.lastCheck = Prefs.notificationdata.lastCheck; | 107 downloadable.lastCheck = Prefs.notificationdata.lastCheck; |
| 102 if (typeof Prefs.notificationdata.data === "object" && "version" in Prefs.no
tificationdata.data) | 108 if (typeof Prefs.notificationdata.data === "object" && |
| 109 "version" in Prefs.notificationdata.data) |
| 103 downloadable.lastVersion = Prefs.notificationdata.data.version; | 110 downloadable.lastVersion = Prefs.notificationdata.data.version; |
| 104 if (typeof Prefs.notificationdata.softExpiration === "number") | 111 if (typeof Prefs.notificationdata.softExpiration === "number") |
| 105 downloadable.softExpiration = Prefs.notificationdata.softExpiration; | 112 downloadable.softExpiration = Prefs.notificationdata.softExpiration; |
| 106 if (typeof Prefs.notificationdata.hardExpiration === "number") | 113 if (typeof Prefs.notificationdata.hardExpiration === "number") |
| 107 downloadable.hardExpiration = Prefs.notificationdata.hardExpiration; | 114 downloadable.hardExpiration = Prefs.notificationdata.hardExpiration; |
| 108 if (typeof Prefs.notificationdata.downloadCount === "number") | 115 if (typeof Prefs.notificationdata.downloadCount === "number") |
| 109 downloadable.downloadCount = Prefs.notificationdata.downloadCount; | 116 downloadable.downloadCount = Prefs.notificationdata.downloadCount; |
| 110 yield downloadable; | 117 yield downloadable; |
| 111 }, | 118 }, |
| 112 | 119 |
| 113 _onExpirationChange: function(downloadable) | 120 _onExpirationChange(downloadable) |
| 114 { | 121 { |
| 115 Prefs.notificationdata.lastCheck = downloadable.lastCheck; | 122 Prefs.notificationdata.lastCheck = downloadable.lastCheck; |
| 116 Prefs.notificationdata.softExpiration = downloadable.softExpiration; | 123 Prefs.notificationdata.softExpiration = downloadable.softExpiration; |
| 117 Prefs.notificationdata.hardExpiration = downloadable.hardExpiration; | 124 Prefs.notificationdata.hardExpiration = downloadable.hardExpiration; |
| 118 saveNotificationData(); | 125 saveNotificationData(); |
| 119 }, | 126 }, |
| 120 | 127 |
| 121 _onDownloadSuccess: function(downloadable, responseText, errorCallback, redire
ctCallback) | 128 _onDownloadSuccess(downloadable, responseText, errorCallback, |
| 129 redirectCallback) |
| 122 { | 130 { |
| 123 try | 131 try |
| 124 { | 132 { |
| 125 let data = JSON.parse(responseText); | 133 let data = JSON.parse(responseText); |
| 126 for (let notification of data.notifications) | 134 for (let notification of data.notifications) |
| 127 { | 135 { |
| 128 if ("severity" in notification) | 136 if ("severity" in notification) |
| 129 { | 137 { |
| 130 if (!("type" in notification)) | 138 if (!("type" in notification)) |
| 131 notification.type = notification.severity; | 139 notification.type = notification.severity; |
| 132 delete notification.severity; | 140 delete notification.severity; |
| 133 } | 141 } |
| 134 } | 142 } |
| 135 Prefs.notificationdata.data = data; | 143 Prefs.notificationdata.data = data; |
| 136 } | 144 } |
| 137 catch (e) | 145 catch (e) |
| 138 { | 146 { |
| 139 Cu.reportError(e); | 147 Cu.reportError(e); |
| 140 errorCallback("synchronize_invalid_data"); | 148 errorCallback("synchronize_invalid_data"); |
| 141 return; | 149 return; |
| 142 } | 150 } |
| 143 | 151 |
| 144 Prefs.notificationdata.lastError = 0; | 152 Prefs.notificationdata.lastError = 0; |
| 145 Prefs.notificationdata.downloadStatus = "synchronize_ok"; | 153 Prefs.notificationdata.downloadStatus = "synchronize_ok"; |
| 146 [Prefs.notificationdata.softExpiration, Prefs.notificationdata.hardExpiratio
n] = downloader.processExpirationInterval(EXPIRATION_INTERVAL); | 154 [Prefs.notificationdata.softExpiration, |
| 155 Prefs.notificationdata.hardExpiration] = |
| 156 downloader.processExpirationInterval(EXPIRATION_INTERVAL); |
| 147 Prefs.notificationdata.downloadCount = downloadable.downloadCount; | 157 Prefs.notificationdata.downloadCount = downloadable.downloadCount; |
| 148 saveNotificationData(); | 158 saveNotificationData(); |
| 149 | 159 |
| 150 Notification.showNext(); | 160 Notification.showNext(); |
| 151 }, | 161 }, |
| 152 | 162 |
| 153 _onDownloadError: function(downloadable, downloadURL, error, channelStatus, re
sponseStatus, redirectCallback) | 163 _onDownloadError(downloadable, downloadURL, error, channelStatus, |
| 164 responseStatus, redirectCallback) |
| 154 { | 165 { |
| 155 Prefs.notificationdata.lastError = Date.now(); | 166 Prefs.notificationdata.lastError = Date.now(); |
| 156 Prefs.notificationdata.downloadStatus = error; | 167 Prefs.notificationdata.downloadStatus = error; |
| 157 saveNotificationData(); | 168 saveNotificationData(); |
| 158 }, | 169 }, |
| 159 | 170 |
| 160 /** | 171 /** |
| 161 * Adds a listener for notifications to be shown. | 172 * Adds a listener for notifications to be shown. |
| 162 * @param {Function} listener Listener to be invoked when a notification is | 173 * @param {Function} listener Listener to be invoked when a notification is |
| 163 * to be shown | 174 * to be shown |
| 164 */ | 175 */ |
| 165 addShowListener: function(listener) | 176 addShowListener(listener) |
| 166 { | 177 { |
| 167 if (showListeners.indexOf(listener) == -1) | 178 if (showListeners.indexOf(listener) == -1) |
| 168 showListeners.push(listener); | 179 showListeners.push(listener); |
| 169 }, | 180 }, |
| 170 | 181 |
| 171 /** | 182 /** |
| 172 * Removes the supplied listener. | 183 * Removes the supplied listener. |
| 173 * @param {Function} listener Listener that was added via addShowListener() | 184 * @param {Function} listener Listener that was added via addShowListener() |
| 174 */ | 185 */ |
| 175 removeShowListener: function(listener) | 186 removeShowListener(listener) |
| 176 { | 187 { |
| 177 let index = showListeners.indexOf(listener); | 188 let index = showListeners.indexOf(listener); |
| 178 if (index != -1) | 189 if (index != -1) |
| 179 showListeners.splice(index, 1); | 190 showListeners.splice(index, 1); |
| 180 }, | 191 }, |
| 181 | 192 |
| 182 /** | 193 /** |
| 183 * Determines which notification is to be shown next. | 194 * Determines which notification is to be shown next. |
| 184 * @param {String} url URL to match notifications to (optional) | 195 * @param {string} url URL to match notifications to (optional) |
| 185 * @return {Object} notification to be shown, or null if there is none | 196 * @return {Object} notification to be shown, or null if there is none |
| 186 */ | 197 */ |
| 187 _getNextToShow: function(url) | 198 _getNextToShow(url) |
| 188 { | 199 { |
| 189 function checkTarget(target, parameter, name, version) | 200 function checkTarget(target, parameter, name, version) |
| 190 { | 201 { |
| 191 let minVersionKey = parameter + "MinVersion"; | 202 let minVersionKey = parameter + "MinVersion"; |
| 192 let maxVersionKey = parameter + "MaxVersion"; | 203 let maxVersionKey = parameter + "MaxVersion"; |
| 193 return !((parameter in target && target[parameter] != name) || | 204 return !((parameter in target && target[parameter] != name) || |
| 194 (minVersionKey in target && Services.vc.compare(version, target[m
inVersionKey]) < 0) || | 205 (minVersionKey in target && |
| 195 (maxVersionKey in target && Services.vc.compare(version, target[m
axVersionKey]) > 0)); | 206 Services.vc.compare(version, target[minVersionKey]) < 0) || |
| 207 (maxVersionKey in target && |
| 208 Services.vc.compare(version, target[maxVersionKey]) > 0)); |
| 196 } | 209 } |
| 197 | 210 |
| 198 let remoteData = []; | 211 let remoteData = []; |
| 199 if (typeof Prefs.notificationdata.data == "object" && Prefs.notificationdata
.data.notifications instanceof Array) | 212 if (typeof Prefs.notificationdata.data == "object" && |
| 213 Prefs.notificationdata.data.notifications instanceof Array) |
| 200 remoteData = Prefs.notificationdata.data.notifications; | 214 remoteData = Prefs.notificationdata.data.notifications; |
| 201 | 215 |
| 202 let notifications = localData.concat(remoteData); | 216 let notifications = localData.concat(remoteData); |
| 203 if (notifications.length === 0) | 217 if (notifications.length === 0) |
| 204 return null; | 218 return null; |
| 205 | 219 |
| 206 let {addonName, addonVersion, application, applicationVersion, platform, pla
tformVersion} = require("info"); | 220 const {addonName, addonVersion, application, |
| 221 applicationVersion, platform, platformVersion} = require("info"); |
| 207 let notificationToShow = null; | 222 let notificationToShow = null; |
| 208 for (let notification of notifications) | 223 for (let notification of notifications) |
| 209 { | 224 { |
| 210 if (typeof notification.type === "undefined" || notification.type !== "cri
tical") | 225 if (typeof notification.type === "undefined" || |
| 226 notification.type !== "critical") |
| 211 { | 227 { |
| 212 let shown; | 228 let shown; |
| 213 if (typeof Prefs.notificationdata.shown == "object") | 229 if (typeof Prefs.notificationdata.shown == "object") |
| 214 shown = Prefs.notificationdata.shown[notification.id]; | 230 shown = Prefs.notificationdata.shown[notification.id]; |
| 215 | 231 |
| 216 if (typeof shown != "undefined") | 232 if (typeof shown != "undefined") |
| 217 { | 233 { |
| 218 if (typeof notification.interval == "number") | 234 if (typeof notification.interval == "number") |
| 219 { | 235 { |
| 220 if (shown + notification.interval > Date.now()) | 236 if (shown + notification.interval > Date.now()) |
| 221 continue; | 237 continue; |
| 222 } | 238 } |
| 223 else if (shown) | 239 else if (shown) |
| 224 continue; | 240 continue; |
| 225 } | 241 } |
| 226 | 242 |
| 227 if (notification.type !== "relentless" && Prefs.notifications_ignoredcat
egories.indexOf("*") != -1) | 243 if (notification.type !== "relentless" && |
| 244 Prefs.notifications_ignoredcategories.indexOf("*") != -1) |
| 228 continue; | 245 continue; |
| 229 } | 246 } |
| 230 | 247 |
| 231 if (typeof url === "string" || notification.urlFilters instanceof Array) | 248 if (typeof url === "string" || notification.urlFilters instanceof Array) |
| 232 { | 249 { |
| 233 if (Prefs.enabled && typeof url === "string" && notification.urlFilters
instanceof Array) | 250 if (Prefs.enabled && typeof url === "string" && |
| 251 notification.urlFilters instanceof Array) |
| 234 { | 252 { |
| 235 let host; | 253 let host; |
| 236 try | 254 try |
| 237 { | 255 { |
| 238 host = new URL(url).hostname; | 256 host = new URL(url).hostname; |
| 239 } | 257 } |
| 240 catch (e) | 258 catch (e) |
| 241 { | 259 { |
| 242 host = ""; | 260 host = ""; |
| 243 } | 261 } |
| 244 | 262 |
| 245 let exception = defaultMatcher.matchesAny(url, RegExpFilter.typeMap.DO
CUMENT, host, false, null); | 263 let exception = defaultMatcher.matchesAny( |
| 264 url, RegExpFilter.typeMap.DOCUMENT, host, false, null |
| 265 ); |
| 246 if (exception instanceof WhitelistFilter) | 266 if (exception instanceof WhitelistFilter) |
| 247 continue; | 267 continue; |
| 248 | 268 |
| 249 let matcher = new Matcher(); | 269 let matcher = new Matcher(); |
| 250 for (let urlFilter of notification.urlFilters) | 270 for (let urlFilter of notification.urlFilters) |
| 251 matcher.add(Filter.fromText(urlFilter)); | 271 matcher.add(Filter.fromText(urlFilter)); |
| 252 if (!matcher.matchesAny(url, RegExpFilter.typeMap.DOCUMENT, host, fals
e, null)) | 272 if (!matcher.matchesAny(url, RegExpFilter.typeMap.DOCUMENT, host, |
| 273 false, null)) |
| 253 continue; | 274 continue; |
| 254 } | 275 } |
| 255 else | 276 else |
| 256 continue; | 277 continue; |
| 257 } | 278 } |
| 258 | 279 |
| 259 if (notification.targets instanceof Array) | 280 if (notification.targets instanceof Array) |
| 260 { | 281 { |
| 261 let match = false; | 282 let match = false; |
| 262 for (let target of notification.targets) | 283 for (let target of notification.targets) |
| 263 { | 284 { |
| 264 if (checkTarget(target, "extension", addonName, addonVersion) && | 285 if (checkTarget(target, "extension", addonName, addonVersion) && |
| 265 checkTarget(target, "application", application, applicationVersion
) && | 286 checkTarget(target, "application", application, |
| 287 applicationVersion) && |
| 266 checkTarget(target, "platform", platform, platformVersion)) | 288 checkTarget(target, "platform", platform, platformVersion)) |
| 267 { | 289 { |
| 268 match = true; | 290 match = true; |
| 269 break; | 291 break; |
| 270 } | 292 } |
| 271 } | 293 } |
| 272 if (!match) | 294 if (!match) |
| 273 continue; | 295 continue; |
| 274 } | 296 } |
| 275 | 297 |
| 276 if (!notificationToShow | 298 if (!notificationToShow || |
| 277 || getNumericalSeverity(notification) > getNumericalSeverity(notificat
ionToShow)) | 299 getNumericalSeverity(notification) > |
| 300 getNumericalSeverity(notificationToShow)) |
| 278 notificationToShow = notification; | 301 notificationToShow = notification; |
| 279 } | 302 } |
| 280 | 303 |
| 281 return notificationToShow; | 304 return notificationToShow; |
| 282 }, | 305 }, |
| 283 | 306 |
| 284 /** | 307 /** |
| 285 * Invokes the listeners added via addShowListener() with the next | 308 * Invokes the listeners added via addShowListener() with the next |
| 286 * notification to be shown. | 309 * notification to be shown. |
| 287 * @param {String} url URL to match notifications to (optional) | 310 * @param {string} url URL to match notifications to (optional) |
| 288 */ | 311 */ |
| 289 showNext: function(url) | 312 showNext(url) |
| 290 { | 313 { |
| 291 let notification = Notification._getNextToShow(url); | 314 let notification = Notification._getNextToShow(url); |
| 292 if (notification) | 315 if (notification) |
| 316 { |
| 293 for (let showListener of showListeners) | 317 for (let showListener of showListeners) |
| 294 showListener(notification); | 318 showListener(notification); |
| 319 } |
| 295 }, | 320 }, |
| 296 | 321 |
| 297 /** | 322 /** |
| 298 * Marks a notification as shown. | 323 * Marks a notification as shown. |
| 299 * @param {String} id ID of the notification to be marked as shown | 324 * @param {string} id ID of the notification to be marked as shown |
| 300 */ | 325 */ |
| 301 markAsShown: function(id) | 326 markAsShown(id) |
| 302 { | 327 { |
| 303 let now = Date.now(); | 328 let now = Date.now(); |
| 304 let data = Prefs.notificationdata; | 329 let data = Prefs.notificationdata; |
| 305 | 330 |
| 306 if (data.shown instanceof Array) | 331 if (data.shown instanceof Array) |
| 307 { | 332 { |
| 308 let newShown = {}; | 333 let newShown = {}; |
| 309 for (let oldId of data.shown) | 334 for (let oldId of data.shown) |
| 310 newShown[oldId] = now; | 335 newShown[oldId] = now; |
| 311 data.shown = newShown; | 336 data.shown = newShown; |
| 312 } | 337 } |
| 313 | 338 |
| 314 if (typeof data.shown != "object") | 339 if (typeof data.shown != "object") |
| 315 data.shown = {}; | 340 data.shown = {}; |
| 316 | 341 |
| 317 data.shown[id] = now; | 342 data.shown[id] = now; |
| 318 | 343 |
| 319 saveNotificationData(); | 344 saveNotificationData(); |
| 320 }, | 345 }, |
| 321 | 346 |
| 322 /** | 347 /** |
| 323 * Localizes the texts of the supplied notification. | 348 * Localizes the texts of the supplied notification. |
| 324 * @param {Object} notification notification to translate | 349 * @param {Object} notification notification to translate |
| 325 * @param {String} locale the target locale (optional, defaults to the | 350 * @param {string} locale the target locale (optional, defaults to the |
| 326 * application locale) | 351 * application locale) |
| 327 * @return {Object} the translated texts | 352 * @return {Object} the translated texts |
| 328 */ | 353 */ |
| 329 getLocalizedTexts: function(notification, locale) | 354 getLocalizedTexts(notification, locale) |
| 330 { | 355 { |
| 331 locale = locale || Utils.appLocale; | 356 locale = locale || Utils.appLocale; |
| 332 let textKeys = ["title", "message"]; | 357 let textKeys = ["title", "message"]; |
| 333 let localizedTexts = []; | 358 let localizedTexts = []; |
| 334 for (let key of textKeys) | 359 for (let key of textKeys) |
| 335 { | 360 { |
| 336 if (key in notification) | 361 if (key in notification) |
| 337 { | 362 { |
| 338 if (typeof notification[key] == "string") | 363 if (typeof notification[key] == "string") |
| 339 localizedTexts[key] = notification[key]; | 364 localizedTexts[key] = notification[key]; |
| 340 else | 365 else |
| 341 localizedTexts[key] = localize(notification[key], locale); | 366 localizedTexts[key] = localize(notification[key], locale); |
| 342 } | 367 } |
| 343 } | 368 } |
| 344 return localizedTexts; | 369 return localizedTexts; |
| 345 }, | 370 }, |
| 346 | 371 |
| 347 /** | 372 /** |
| 348 * Adds a local notification. | 373 * Adds a local notification. |
| 349 * @param {Object} notification notification to add | 374 * @param {Object} notification notification to add |
| 350 */ | 375 */ |
| 351 addNotification: function(notification) | 376 addNotification(notification) |
| 352 { | 377 { |
| 353 if (localData.indexOf(notification) == -1) | 378 if (localData.indexOf(notification) == -1) |
| 354 localData.push(notification); | 379 localData.push(notification); |
| 355 }, | 380 }, |
| 356 | 381 |
| 357 /** | 382 /** |
| 358 * Removes an existing local notification. | 383 * Removes an existing local notification. |
| 359 * @param {Object} notification notification to remove | 384 * @param {Object} notification notification to remove |
| 360 */ | 385 */ |
| 361 removeNotification: function(notification) | 386 removeNotification(notification) |
| 362 { | 387 { |
| 363 let index = localData.indexOf(notification); | 388 let index = localData.indexOf(notification); |
| 364 if (index > -1) | 389 if (index > -1) |
| 365 localData.splice(index, 1); | 390 localData.splice(index, 1); |
| 366 }, | 391 }, |
| 367 | 392 |
| 368 /** | 393 /** |
| 394 * A callback function which listens to see if notifications were approved. |
| 395 * |
| 396 * @callback approvedListener |
| 397 * @param {boolean} approved |
| 398 */ |
| 399 |
| 400 /** |
| 369 * Adds a listener for question-type notifications | 401 * Adds a listener for question-type notifications |
| 402 * @param {string} id |
| 403 * @param {approvedListener} listener |
| 370 */ | 404 */ |
| 371 addQuestionListener: function(/**string*/ id, /**function(approved)*/ listener
) | 405 addQuestionListener(id, listener) |
| 372 { | 406 { |
| 373 if (!(id in questionListeners)) | 407 if (!(id in questionListeners)) |
| 374 questionListeners[id] = []; | 408 questionListeners[id] = []; |
| 375 if (questionListeners[id].indexOf(listener) === -1) | 409 if (questionListeners[id].indexOf(listener) === -1) |
| 376 questionListeners[id].push(listener); | 410 questionListeners[id].push(listener); |
| 377 }, | 411 }, |
| 378 | 412 |
| 379 /** | 413 /** |
| 380 * Removes a listener that was previously added via addQuestionListener | 414 * Removes a listener that was previously added via addQuestionListener |
| 415 * @param {string} id |
| 416 * @param {approvedListener} listener |
| 381 */ | 417 */ |
| 382 removeQuestionListener: function(/**string*/ id, /**function(approved)*/ liste
ner) | 418 removeQuestionListener(id, listener) |
| 383 { | 419 { |
| 384 if (!(id in questionListeners)) | 420 if (!(id in questionListeners)) |
| 385 return; | 421 return; |
| 386 let index = questionListeners[id].indexOf(listener); | 422 let index = questionListeners[id].indexOf(listener); |
| 387 if (index > -1) | 423 if (index > -1) |
| 388 questionListeners[id].splice(index, 1); | 424 questionListeners[id].splice(index, 1); |
| 389 if (questionListeners[id].length === 0) | 425 if (questionListeners[id].length === 0) |
| 390 delete questionListeners[id]; | 426 delete questionListeners[id]; |
| 391 }, | 427 }, |
| 392 | 428 |
| 393 /** | 429 /** |
| 394 * Notifies question listeners about interactions with a notification | 430 * Notifies question listeners about interactions with a notification |
| 395 * @param {String} id notification ID | 431 * @param {string} id notification ID |
| 396 * @param {Boolean} approved indicator whether notification has been approved
or not | 432 * @param {boolean} approved indicator whether notification has been approved |
| 397 */ | 433 */ |
| 398 triggerQuestionListeners: function(id, approved) | 434 triggerQuestionListeners(id, approved) |
| 399 { | 435 { |
| 400 if (!(id in questionListeners)) | 436 if (!(id in questionListeners)) |
| 401 return; | 437 return; |
| 402 let listeners = questionListeners[id]; | 438 let listeners = questionListeners[id]; |
| 403 for (let listener of listeners) | 439 for (let listener of listeners) |
| 404 listener(approved); | 440 listener(approved); |
| 405 }, | 441 }, |
| 406 | 442 |
| 407 /** | 443 /** |
| 408 * Toggles whether notifications of a specific category should be ignored | 444 * Toggles whether notifications of a specific category should be ignored |
| 409 * @param {String} category notification category identifier | 445 * @param {string} category notification category identifier |
| 410 * @param {Boolean} [forceValue] force specified value | 446 * @param {boolean} [forceValue] force specified value |
| 411 */ | 447 */ |
| 412 toggleIgnoreCategory: function(category, forceValue) | 448 toggleIgnoreCategory(category, forceValue) |
| 413 { | 449 { |
| 414 let categories = Prefs.notifications_ignoredcategories; | 450 let categories = Prefs.notifications_ignoredcategories; |
| 415 let index = categories.indexOf(category); | 451 let index = categories.indexOf(category); |
| 416 if (index == -1 && forceValue !== false) | 452 if (index == -1 && forceValue !== false) |
| 417 { | 453 { |
| 418 categories.push(category); | 454 categories.push(category); |
| 419 Prefs.notifications_showui = true; | 455 Prefs.notifications_showui = true; |
| 420 } | 456 } |
| 421 else if (index != -1 && forceValue !== true) | 457 else if (index != -1 && forceValue !== true) |
| 422 categories.splice(index, 1); | 458 categories.splice(index, 1); |
| 423 | 459 |
| 424 // HACK: JSON values aren't saved unless they are assigned a different objec
t. | 460 // HACK: JSON values aren't saved unless they are assigned a |
| 425 Prefs.notifications_ignoredcategories = JSON.parse(JSON.stringify(categories
)); | 461 // different object. |
| 462 Prefs.notifications_ignoredcategories = |
| 463 JSON.parse(JSON.stringify(categories)); |
| 426 } | 464 } |
| 427 }; | 465 }; |
| 428 Notification.init(); | 466 Notification.init(); |
| OLD | NEW |